BCLK/LRCLK出力しかないRaspberry Piで旭化成のDAC IC(AK449xシリーズ)を鳴らす実験

 Raspberry PiでDAI/ASRC/CPLDなどのクロック生成周辺回路を用いず、旭化成DAC AK449Xシリーズを使用する方法についてメモ。
 Raspberry PiDACにAK449xシリーズの採用が少ない原因のひとつとして、AK449xの要求するMCLKをRaspberry Piから供給できない点が挙げられます。
 今回は、周辺回路の追加無しで、なんとかAK449xシリーズを鳴らせないかな?という実験となります。

 実験には@blue__sevenさん製作のChiKoiDac-AK4495Sを使用しました。ChiKoiDac-AK4495SはNanoPi NEO2向けの小型DACボードで、NEO2サイズの基板にAK4495S・OPAMP LPF・電源回路を搭載したお手軽高音質DACです。
 NanoPi NEO2では専用品だけあってそのまま乗せるだけですが、Raspberry Pi Zeroでは互換のあるピン部分をそのまま差し込み、不足するI2SのDATA/LRCLK/BCLKをジャンパケーブルで接続することで使用できます。

f:id:tkztkztkz:20190205231002j:plain
Raspberry PI Zero with AK4495S DAC

方法1

  • @blue__sevenさんに教えて頂いた方法です。
  • AK449xは、MCLKにBCLKを入力するだけで音は出ます。仕様範囲外の動作となります。
    • 44.1kHz/32bit時にはBCLK = MCLK = 64fs = 2.8224MHz
  • MCLKが規定のクロックに達していないため、デジフィル(デジタルフィルタ)は正常に動作しません。
  • オシロで確認する限りNOS(Non-Oversampling)出力ではないようで、何らかの簡易フィルタは掛かっているように見えます。
  • デジフィルにショートディレイやNOSを選んでも、出力波形に変化はありません。
    f:id:tkztkztkz:20190205231256p:plain
    Raspberry Pi Zero Internel PLL MCLK=BCLK=64fs (AK4495S) 1kHz Sine Zoom
    f:id:tkztkztkz:20190205231354p:plain
    Raspberry Pi Zero Internal PLL MCLK=BCLK=64fs (AK4495S) 1kHz Square

方法2

  • MCLKとBCLKを同じクロックで入力するところまでは方法1と同様
  • BCLKを標準の32bit再生時64fsではなく、MCLK相当の512fsまでクロックを上げます。
    • 44.1kHz/32bit時にはBCLK = MCLK = 512fs = 22.5792MHz
  • I2Sドライバの改造が必要です。修正内容は下記の感じ
    • BCLKを大幅に上昇させる 64fs->512fs
    • fslenを適切に設定する
  • オシロで確認する限り、デジフィルはちゃんと動いているように見えます。

    f:id:tkztkztkz:20190205231555p:plain
    Raspberry Pi Zero Internel PLL MCLK=BCLK=512fs (AK4495S) 1kHz Sine Zoom DF SSD
    f:id:tkztkztkz:20190205232013p:plain
    Raspberry Pi Zero Internel PLL MCLK=BCLK=512fs (AK4495S) 1kHz Square DF SSD
    f:id:tkztkztkz:20190205231706p:plain
    Raspberry Pi Zero Internel PLL MCLK=BCLK=512fs (AK4495S) 1kHz Sine Zoom DF NOS

  • AK449XシリーズではBCLKをデータの取り込みタイミングだけに使っているから実現できているようです。LRCLKのエッジから少しだけデータを送出していることがわかります。通常64fsのところ8倍の512fsでBCLKを駆動しているため、エッジから1/8くらいデータが出ています。

    f:id:tkztkztkz:20190205232105p:plain
    Raspberry Pi Zero Internel PLL MCLK=BCLK=512fs (AK4495S) LRCLK & DATA

方法2 検証ソース修正例

  • 割とひどいパッチですが実験時のコードを例示します
    • bclk_rateは24.576MHzをfsで割り切れたら24.576MHz。22.5792MHzをfsで割り切れたら22.5792MHz固定出力にします
    • frame_lengthを変更したbclk ratioで再算出
    • tx_ch2_posもそのままでは正しく計算できないので、framesync_length(frame_length/2)から再算出
  • いろいろ改造しているのでdiffのoffsetは狂ってます。参考まで
*** sound/soc/bcm/bcm2835-i2s.c 2019-01-22 01:56:36.880202010 +0000
--- sound/soc/bcm/bcm2835-i2s_old.c     2019-01-22 01:25:58.276303397 +0000
***************
*** 31,37 ****
   * General Public License for more details.
   */

  #include <linux/bitops.h>
  #include <linux/clk.h>
--- 31,39 ----
   * General Public License for more details.
   */

+ #define AKFS

  #include <linux/bitops.h>
  #include <linux/clk.h>
***************
*** 393,398 ****
--- 394,424 ----
                if (bclk_rate < 0)
                        return bclk_rate;
        }
+ #ifdef AKFS
+       if ( !(24576000 % params_rate(params))) {
+               bclk_rate = 24576000;
+       }else
+       if ( !(22579200 % params_rate(params))) {
+               bclk_rate = 22579200;
+       }else{
+               dev_info(dev->dev, "unknownrate = %d", params_rate(params));
+       }
+       frame_length = bclk_rate / params_rate(params);
+       dev_info(dev->dev, "bclk = %d, flen = %d", bclk_rate, frame_length);
+ #endif

        /* Check if data fits into slots */
        if (data_length > slot_width)
***************
*** 520,527 ****
                rx_mask, slot_width, data_delay, odd_slot_offset);
        bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos,
                tx_mask, slot_width, data_delay, odd_slot_offset);

        /*
         * Transmitting data immediately after frame start, eg
         * in left-justified or DSP mode A, only works stable
--- 546,567 ----
                rx_mask, slot_width, data_delay, odd_slot_offset);
        bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos,
                tx_mask, slot_width, data_delay, odd_slot_offset);

+
+ #if defined(AKFS)
+       tx_ch2_pos = framesync_length + 1;
+       dev_info(dev->dev, "tx_ch1_pos = %d, tx_ch2_pos = %d", tx_ch1_pos, tx_ch2_pos);
+ #endif
        /*
         * Transmitting data immediately after frame start, eg
         * in left-justified or DSP mode A, only works stable

考察とかなんとか

  • AK449Xの動作とか
    • AK449x系はMCLK/LRCLKの比を見てデジフィルの動作を決めてるっぽい。BCLKは関与していない?
    • 実際仕様書にもMCLKとLRCLKは同期する必要があると記載。BCLK/DATAについては触れていない
    • BCLKはDATAの取り込みタイミングだけに使われてると予想
  • MCLKが低いときのデジフィル動作について
    • 768kHz再生時のデジフィル動作に似ているとのこと
    • ハイレート再生時の簡易フィルタと同じ動きになるのかな?
    • 実際に768kHz/32bit=64fs再生時には、MCLK=BCLK=49.152MHzで再生している
  • 音質は?
    • NEO2のAUDIO PLLに比べると、なんかいまいちっぽい
    • ラズパイのAudio PLLはAccumlated Jitterが発生するのでそれの影響?

コードについて

 今回は検証用にI2Sドライバを無理やり改造したが、dai linkドライバ(hifiberry-dac.cに代わるもの)を新規に起こして、I2Sドライバはオリジナルのままで動作させる方向で開発中です。

おまけ:前提のお話

  • I2Sとは何か?
    • BCLK/LRCLK/DATAの3本の信号線を用いてIC間でオーディオ信号を転送する規格
    • Inter-IC Soundの略でI2S
  • LRCLK(LR Clock)とは何か?
    • I2S I/Fでデータを転送するとき左右どちらのチャネルを転送しているか示す信号
    • I2SではLRCLK信号がLOW区間でLチャネルのデータ、HIGH区間でRチャネルのデータ転送を行なっていることを示しています。
  • クロックのfs表記
    • DAC ICの仕様書でよく出てくる単位。BCLKやMCLKの周波数表記に良く使われる。MCLK=256,512fsなど
    • LRCLK一周期 = 1fsとする。LRCLKの別名がFrame Syncというだけの話
    • 1fsの周波数は、サンプリング周波数と同値になります。44.1kHzのとき1fs=44.1kHz。48kHzのとき1fs=48kHz
    • I2Sドライバ内ではBCLKとLRCLKの比(bclk_ratio)を表すため重要な単位となります。
  • BCLK(Bit Clock)とは何か?
    • I2S I/Fでデータを転送する基準クロック
    • BCLKは多くの場合32/64fsの二択(それぞれ量子化ビット数 16/32bit時の最小転送サイズになるため)
      • I2S 44.1kHz/16bit転送の場合、BCLK = 32fs = 16 * 2 * 44.1kHz = 1.4112MHz
      • I2S 44.1kHz/32bit転送の場合、BCLK = 64fs = 32 * 2 * 44.1kHz = 2.8224MHz
      • 24bit/48fsも時々使うが、クロックの生成が面倒なので古いDAC ICくらいでしか使わない。
  • MCLK(Master Clock)とは何か?
    • I2S I/Fでは必須ではない信号 (I2S自体はBCLK/LRCLK/DATAの3本あれば良い)
    • DAC ICが内部でオーバーサンプリングなどのデジタル信号処理を行う基準クロックとして要求している。別名SCLK(System Clock)
      • 多くの場合BCLKの2/4/8倍くらいを要求します。
      • LRCLK 44.1/48kHz 32bit時にBCLKの8倍だと64fs * 8 = 512fsとなり、MCLKはそれぞれ22.5792MHz/24.576MHz
      • この二つのクロックの最小公倍数は3.6GHzになるため一つの発振器から生成するのは困難
      • よって2系統のクロック発振器を使うか、PLLにて生成する必要がある
    • DAC IC次第だが、MCLKとBCLK and/or LRCLKの同期が必要なケースが多い
      • MCLKから分周してBCLK/LRCLKを生成するか、BCLK/LRCLKを逓倍してMCLKを生成する必要がある
      • 例外としてESSのDACは非同期でもOKなようです。
    • 要するに用意するのが面倒で、量産を考えるとコストが掛かる。
  • Raspberry PiはMCLK入出力機能を持っていない
    • Raspberry Piシリーズでは、内蔵PLLによるBCLK/LRCLKの生成・出力できるが、MCLKの出力機能はない。
    • Allwinner H5(NanoPi NEO2など)は内蔵AUDIO PLLにてMCLKを生成し出力可能。入力はなし。
    • TI AM335X(BBB/PocketBeagleなど)はMCLK入力が可能。出力はなし。入力したMCLKを基にBCLK/LRCLKを生成・出力が可能