Linux向けAK449x Codec Driverの紹介

 旭化成エレクトロニクス製 AK4490/AK4495/AK4497/AK4493向けLinux Driverの紹介です。NanoPi NEO2でAK449xシリーズを鳴らすために作成しました。

f:id:tkztkztkz:20190124231738j:plain
 ALSA SoC Codecドライバとして実装しています。NanoPi NEO2/Raspberry Pi Zeroで動作確認済み。そのほかのSBCでもDAI LINKドライバとdevice-treeを適切に用意すれば使用可能だと思います。Raspberry Pi Zeroで鳴らす手順はそのうちブログに書く予定です。

 ライセンスはLinux Kernel ModuleなのでGPLv2です。自由に組み込んで使用してください。何か面白いブツができたら教えて頂けるとうれしいです。

できること

  • 標準機能
    • PDN制御(DAC IC PDN信号のGPIO出力)
    • 外部MUTE制御(MUTE制御信号のGPIO出力)
    • ソフトMUTE制御(AKレジスタを叩いてのMUTE)
    • 複数Codec同時制御(I2Cバスあたり最大4個まで)
  • Alsa Mixer設定可能項目
    • ディエンファシス設定
    • 電子ボリューム設定
    • デジタルフィルタ設定
    • Sound Control設定
    • HLOAD ON/OFF設定(AK4497のみ)
    • Gain Control設定(AK4493/AK4497のみ)
    • ATT遷移時間設定(AK4493/AK4497のみ)
  • 出力制御機能(device-treeで設定)
    • STEREO出力の左右入れ替え
    • MONOモード設定
    • 出力位相の変更

ドライバのダウンロード

この辺にソースがあります。
https://raw.githubusercontent.com/tkztkztkz/linux/npi-audio-4.11.y/sound/soc/codecs/ak449x.c https://raw.githubusercontent.com/tkztkztkz/linux/npi-audio-4.11.y/sound/soc/codecs/ak449x.h

NanoPi NEO2用のイメージだとこの辺に導入済みです。
NanoPi Neo2用 Volumio2 - _tkz_ memo

dts設定方法

NanoPi NEO2

dtsの設定例 通常のステレオ仕様(I2C ADDR 0x10)
&i2c0 {
        status = "okay";

        ak449x: ak449x-codec@10 {
                #sound-dai-cells = <0>;
                compatible = "asahi-kasei,ak449x";
                reg = <0x10>; /* I2C addr */
                status = "okay";

                chip = "AK4495"; /* AK4490/AK4495/AK4497/AK4493 */
                reset-gpios = <&pio 6 7 GPIO_ACTIVE_LOW>; /* PG7 */ /* pdn pin */
                mute-gpios = <&pio 6 8 GPIO_ACTIVE_LOW>; /* PG8 */ /* external mute */
        };

&i2s0 {
        sound-dai = <&ak449x>; /* dai link target */
        status = "okay";
};
dtsの設定例 DualMonoバランス出力仕様(I2C ADDR 0x10/0x11)
&i2c0 {
        ak449x1: ak449x-codec@10 {
                #sound-dai-cells = <0>;
                compatible = "asahi-kasei,ak449x";
                reg = <0x10>; /* I2C addr */
                status = "okay";

                chip = "AK4495"; /* AK4490/AK4495/AK4497/AK4493 */
                chmode = "MONO_LCH"; /* STEREO/STEREO_INVERT/MONO_LCH/MONO_RCH */
                phase = "LIRN";  /* LNRN LNRI LIRN LIRI - N=non-invert I=invert*/
                reset-gpios = <&pio 6 7 GPIO_ACTIVE_LOW>; /* PG7 */ /* pdn pin */
                mute-gpios = <&pio 6 8 GPIO_ACTIVE_LOW>; /* PG8 */ /* external mute */
        };
        ak449x2: ak449x-codec@11 {
                #sound-dai-cells = <0>;
                compatible = "asahi-kasei,ak449x";
                reg = <0x11>; /* I2C addr */
                status = "okay";

                chip = "AK4495"; /* AK4490/AK4495/AK4497/AK4493 */
                chmode = "MONO_RCH"; /* STEREO/STEREO_INVERT/MONO_LCH/MONO_RCH */
                phase = "LIRN"; /* LNRN LNRI LIRN LIRI - N=non-invert I=invert*/
       };
};

&i2s0 {
        sound-dai = <&ak449x1 &ak449x2>; /* dai link target */
        status = "okay";
};

Raspberry Pi Zero

dt-overlayの設定例 通常のステレオ仕様(I2C ADDR 0x10)
// Definitions for HiFiBerry DAC
/dts-v1/;
/plugin/;

/ {
        compatible = "brcm,bcm2708";

        fragment@0 {
                target = <&i2s>;
                __overlay__ {
                        status = "okay";
                };
        };
        fragment@1 {
                target = <&i2c1>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <0>;
                        status = "okay";

                        ak449x: ak449x-codec@10 {
                                #sound-dai-cells = <0>;
                                compatible = "asahi-kasei,ak449x";
                                reg = <0x10>; /* I2C addr */
                                status = "okay";

                                chip = "AK4495"; /* AK4490/AK4495/AK4497/AK4493 */
                                chmode = "STEREO"; /* STEREO/STEREO_INVERT/MONO_LCH/MONO_RCH */
                                reset-gpios = <&gpio 15 1>; /* GPIO15 */ /* pdn pin */
                                mute-gpios = <&gpio 23 1>;  /* GPIO23 */ /* external mute */
                        };
                };
        };

        fragment@2 {
                target = <&sound>;
                __overlay__ {
                        compatible = "hifiberry,hifiberry-dac";
                        i2s-controller = <&i2s>;
                        i2s-codec = <&ak449x>;
                        status = "okay";
                };
        };
};

パラメータについて

  • chip (AK4490/AK4495/AK4497/AK4493)
    • 制御対象チップを設定します
    • デフォルトは4495となります
    • 下記項目の動作が変わります
      • HLOAD(4497のみ)
      • Gain Control(4493/4497のみ)
      • ソフトMUTEの遷移時間
  • chmode (STEREO/STEREO_INVERT/MONO_LCH/MONO_RCH)
    • 動作モードを設定します
    • デフォルトはSTEREOとなります
    • Dual MonoやSTEREOの左右を入れ替える場合に指定します
  • phase (LNRN LNRI LIRN LIRI - N=non-invert I=invert)
    • 出力位相を設定します
    • デフォルトはLNRNとなります。L/R CHとも正相出力。
    • LIRIにするとL/R CHとも逆相出力となります。
    • 出力位相を変えたり、Dual Mono設定時、バランス出力する場合などに指定します
  • reset-gpios
    • PDN制御出力を行うピンを指定します
    • デフォルトは無効です
    • 出力論理はGPIOドライバの指定に従います(出力論理変更ができない場合はActive Highになるはず)
  • mute-gpios
    • 外部MUTE制御出力を行うピンを指定します
    • デフォルトは無効です
    • 出力論理はGPIOドライバの指定に従います(出力論理変更ができない場合はActive Highになるはず)
    • MUTEの制御順は下記となります
      • Power On -> 外部MUTE assert
      • 再生開始 MCLK/LRCLK設定 -> 外部MUTE negate -> ソフトMUTE negate -> wait
      • 再生停止 ソフトMUTE assert -> wait -> 外部MUTE assert -> MCLK/LRCLK設定

備考

  • ポップノイズ回避について
    • AK449xシリーズはMCLK/LRCLKの開始・停止でポップノイズが発生します
    • ソフトMUTE制御だけでは回避できません
    • 外部MUTE制御を使用して、リレーかFETでMUTE回路を組むことをオススメします
  • 複数のAK449Xを制御する場合には、最初に指定するcodecにのみGPIO設定を記述してください
  • PDN制御について
    • 旭化成DAC ICはPDN制御が鬼門だったりします。規定のタイミングを守らないと動かないケースがあります。
    • POWER ONから一定時間LowにしHighを入れる必要があります。
    • PDN信号をCRで鈍らせてプルアップを入れても動かなくはありませんが、場合によっては不安定になるため外部からきっちりリセットパルスを入れたほうが良いです。