NanoPi-NEO2 の I2SでMCLK出力 & 384kHz, 768kHz, 1536kHz, 8kHz対応

# 概要

NanoPi-NEO2およびNanoPi-NEO Plus2 のI2S出力において下記を対応。

- I2S MCLK の出力
- PCM-384kHz, 768kHz, 1536kHz 出力
- 24bit再生時のバグ(音小さいし割れる)対応
- 8kHz再生時のバグ(16kHzで再生されてしまう)対応

大雑把な手順は、LinuxのKernelソース弄ってNanoPi-NEO2上でコンパイル。

成功するとPA6のピンからMCLKが出力されます。そして、768kHz 対応します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/e2bcc52c-b684-5e87-d27e-e708562b3d1b.png)

NanoPi-NEO(Allwinner H3系)の場合は、H3のDatasheet上出力できません。
(が、もしかしたらこっそり繋がっている可能性は0では無いハズ)

### 関連シリーズ紹介

- [NanoPi-NEOとMPDとOLEDで音楽再生サーバ](https://qiita.com/blue-7/items/e58bcfa5ecf93cc8838b)
- [NanoPi-NEOとMPDとLCDとI2S-DACとTDA7297で音楽再生サーバ(完全体)](https://qiita.com/blue-7/items/07ad5661607799053d44)

### 参考にさせて頂いたサイト

- [Patchwork ALSA: Add rate defines for 352k8 and 384k](https://patchwork.kernel.org/patch/9160203/)
- [NanoPi NEO2 I2S AUDIO でMCLKを出すメモ](http://tkz.hateblo.jp/entry/2017/10/04/230819)
- [NanoPi NEO2 I2S AUDIO 24bit再生でNG メモ](http://tkz.hateblo.jp/entry/2017/10/04/230628)
- [added: 384k playback support](https://github.com/tkztkztkz/linux/commit/a6398c0ec58755223137423eaabdd1675fe15d58)
- [ASoC: bcm2835: Support additional samplerates up to 384kHz](https://github.com/raspberrypi/linux/commit/e621a35cf91a6167d8b594b44f83c2aba0bbb5f7)

# 前提

|||
|:--|:--|
|対象マシーン|NanoPi-NEO2 / NanoPi-NEO Plus2 / NanoPi-NEO Core2|
|クロスコンパイル環境|不要|
|Official Rom Image|[nanopi-neo2_FriendlyCore-Xenial_4.14.0_20171208.img.zip](https://drive.google.com/open?id=1uKLMqeOzB63dVj6j6A1JCmd_oGqVeND0)|
|Linux Kernel ver|[Linux kernel source 4.14.0](https://drive.google.com/open?id=1NsYXCbyz3yjW-rX4GDmfN0bYNr2jtZCM)|

## nanopi-neo2_FriendlyCore-Xenial_4.14.0_20171208.img.zip で無効になったI2S

たぶん、4.11.2 → 4.14.0 の際のマージミスが原因です。。(汗
menuconfigで見ると、A10 I2S が有効になっていて、sun8i　I2Sが無効になっています。
それと同時にPCM5102aも無効になっていると思われますので、DeviceTreeとのUnmatchでI2Sだめなのかも。。。

ソースは
[arch/arm64/configs/sunxi_arm64_defconfig](https://github.com/blue777/NanoPi-NEO2-I2S_MCLK/blob/master/linux_4.14.0_20171212/arch/arm64/configs/sunxi_arm64_defconfig)
の3551行目。

sound/soc/sunxi/Kconfig
をみると
	select SND_SOC_GENERIC_DMAENGINE_PCM
	select SND_SOC_PCM5102A
とあるので、合わせて修正すれば良いのですが未検証のため素直にmenuconfigします。。。

menuconfig での違いは以下の通り。

■ linux_4.11.2_20170707

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/6078b037-4e3b-a312-c6f3-d8230dd7ce4b.png)

■ linux_4.14.0_20171212

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/bc27dbf8-e831-0a6b-128f-28f9c233b11c.png)

# 手順

LinuxのカーネルコンパイルまでをNanoPi上でやってしまいます。
面倒なCrossCompile環境構築が不要ですが、その代わり時間かかります。
大体1時間～2時間くらい掛かりますが、一度コンパイルすれば、その後の差分コンパイルは最短2分程度からスタートです。

## 初期セットアップ

Official ROM Image を取得
[nanopi-neo2_FriendlyCore-Xenial_4.14.0_20171208.img.zip](https://drive.google.com/open?id=1uKLMqeOzB63dVj6j6A1JCmd_oGqVeND0)

MicroSDに書き込み。NanoPi-NEO2起動して初期セットアップ

```console:NanoPi-NEO2
timedatectl set-timezone Asia/Tokyo
apt update
apt upgrade
```
npi-config はまだ実行しない。(どうせDeviceTree弄るので)

## カーネルソースの準備

KernelSourceがwgetだと取得困難になったので、Sambaで共有フォルダ作り転送する。

### Sambaの準備

```console:NanoPi-NEO2
apt install samba
nano /etc/samba/smb.conf
----------------------------------------------------
    ・
    ・
    ・
    ・
    ↓末尾に追加

[Home]
   comment = share folder
   path = /root
   public = yes
   writable = yes
   guest ok = yes
   guest only = yes
   create mode = 0777
   directory mode = 0777
----------------------------------------------------
chmod 777 /root
systemctl enable smbd
systemctl start smbd
```

### LinuxKernelのソース取得＆転送

[linux_4.14.0_20171212.tar.gz](https://drive.google.com/open?id=1NsYXCbyz3yjW-rX4GDmfN0bYNr2jtZCM)

PCかなんかでダウンロードして、Samba経由でNanoPi-NEO2に転送し、解凍する。

```console:NanoPi-NEO2
cd ~

tar xf linux_4.14.0_20171212.tar.gz
mv linux linux_4.14.0_20171212
```

https://github.com/friendlyarm/linux.git
からgit cloneの方法もあるが、上記のほうが確実かな。

### 差分ファイル上書き

https://github.com/blue777/NanoPi-NEO2-I2S_MCLK/tree/master/linux_4.14.0_20171212

に差分ファイルだけをまとめたので、上記で展開したKernel sourceに上書き。
パッチ適用でも、Samba経由でもなんでもやりやすい方法で上書きします。

```console:NanoPi-NEO2
git clone https://github.com/blue777/NanoPi-NEO2-I2S_MCLK/
cp NanoPi-NEO2-I2S_MCLK/linux_4.14.0_20171212/* linux_4.14.0_20171212 -r
rm NanoPi-NEO2-I2S_MCLK -r
```


## Linuxカーネルのコンパイル

```console:NanoPi-NEO2
apt-get install libncurses5-dev libncursesw5-dev bc

cd linux_4.14.0_20171212

touch .scmversion
make sunxi_arm64_defconfig
make menuconfig
```

なぜかI2Sが無効になったのでmenuconfigで
Allwinner A10 I2S Support → 無効化
Allwinner sun8i I2S Support → 有効化
したうえでコンパイル。

ついでに、[iSCSI Initiator over TCP/IP](https://qiita.com/blue-7/items/361050dfaca7063322e9)も有功にしておく(笑

```console:menuconfig
Device Drivers ---> [enter]
    Sound card support ---> [enter]
        Advanced Linux Sound Archtecture --> [enter]
            ALSA for SoC audio support [enter]
                Allwinner SoC Audio support ---> [enter]

< > Allwinner A10 I2S Support [n]
<*> Allwinner sun8i I2S Support [y]

-----

Device Drivers ---> [enter]
    SCSI device support ---> [enter]
        SCSI low-level drivers --> [y][enter]

<*> iSCSI Initiator over TCP/IP [y]
```

```console:NanoPi-NEO2
make -j4

mkdir /boot/old
mv /boot/Image /boot/old
mv /boot/*.dtb /boot/old
cp arch/arm64/boot/Image /boot/
cp arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi*.dtb /boot/
reboot
```

DeviceTree関係が更新されているので、再起動後あらためて、
npi-config で I2Sを有効化します。

# 解説

オリジナルソース(4.14.0)からの修正差分は以下の通り。
ほぼほぼ参考にさせて頂いた内容のままです。

### MCLK出力対応と384kHz対応

https://github.com/blue777/NanoPi-NEO2-I2S_MCLK/commit/8ea2682c9a2d7d567c5549f148d51852e2a666d4#diff-20b69ed4c03bb7900580c3cc2c8e1deb

ファイル毎に変更概要を書くと以下の感じ。
なお、I2Sの有効化は、MenuConfigで実施のためソース修正無し。

|File|変更|
|:--|:--|
|arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi.dtsi|MCLK出力対応(DeviceTreeでPin変更)|
|arch/arm64/boot/dts/allwinner/sunxi-h3-h5.dtsi|MCLK出力対応(DeviceTreeでPin変更)|
|drivers/pinctrl/sunxi/pinctrl-sun50i-h5.c|MCLKピンファンクション定義追加|
|include/sound/pcm.h|384kHz追加(ALSA側)|
|sound/core/pcm_native.c|384kHz追加(ALSA側)|
|sound/soc/codecs/pcm5102a.c|384kHz追加(DAC側)|
|sound/soc/sunxi/sun8i-i2s.c|384kHz追加(I2S出力部側)、24bit時にRightFitされる問題修正|

DeviceTree周りの対応において、PA6が[2.8 inch resistive touch screen LCD 240x320 for Pi](http://www.friendlyarm.com/index.php?route=product/product&path=81&product_id=102)のタッチパネルのChipSelect用に使用されていますので、同時に有効化はできません。

DeviceTreeは大元で修正しているため、NanoPi-NEO2 / NanoPi-NEO Plus2 / NanoPi-NEO Core2 にまとめて対応できます。

### 768kHz対応

https://github.com/blue777/NanoPi-NEO2-I2S_MCLK/commit/6ca18e9d7dce4134bff71f6824bf7b8b4e77f9b6#diff-20b69ed4c03bb7900580c3cc2c8e1deb

ファイル毎に変更概要を書くと以下の通りです。

|File|変更|
|:--|:--|
|include/sound/pcm.h|768kHz追加(ALSA側)|
|sound/core/pcm_native.c|768kHz追加(ALSA側)|
|sound/soc/codecs/pcm5102a.c|768kHz追加(DAC側)|
|sound/soc/sunxi/sun8i-i2s.c|768kHz追加(I2S出力部側)、MCLKを49MHz設定追加|

基本的に768kHz系の定義を追加と、
MCLKが24MHzクラスだと、768kHz再生時に32fsとなってしまうため、16bit再生までしかできなくなります。
32bit再生したいので、768kHz 64fsとなる49MHzクラスとなるように修正します。

また、pcm5102aは、768kHz対応していませんが、とりあえず NanoPi-NEO2のI2S出力として768kHz出ればよいので pcm5102aのソースを弄っちゃいます。悪しからず。

### 1536 kHz, 8kHz 対応

8kHzは、MLCKの分周の問題で16kHz出力されていたのでついでに直します。

https://github.com/blue777/NanoPi-NEO2-I2S_MCLK/commit/c0154061ec30554bbf10ddc084d923e5ec9adf65#diff-20b69ed4c03bb7900580c3cc2c8e1deb

ファイル毎に変更概要を書くと以下の通りです。

|File|変更|
|:--|:--|
|include/sound/pcm.h|オリジナル戻した(ALSA側)|
|sound/core/pcm_native.c|オリジナルに戻した(ALSA側)|
|sound/soc/codecs/pcm5102a.c|1536kHz追加(DAC側)|
|sound/soc/sunxi/sun8i-i2s.c|1536kHz追加(I2S出力部側)、MCLK計算部をちゃんと実装|

include/sound/pcm.hとsound/core/pcm_native.cは、ALSA開発チームが192kHzを超える周波数への対応をしないことを表明しているようですので、SNDRV_PCM_RATE_CONTINUOUS で対応するようにし、元の定義に戻しています。

再生周波数とMCLKの目標周波数は以下になっています。

|Sampling freq.|MLCK target freq.|
|--:|--:|
|44.1k, 88.2k, 176.4k, 352.8k|22,579,200|
|48k, 96k, 192k, 384k|24,576,000|
|705.6k|45,158,400|
|768k|49,152,000|
|1411.2k|90,316,800|
|1536k|98,304,000|

# 結果

## MCLK

### NanoPi-NEO Plus2

#### 44.1kHz系

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/bdcecd0f-6a51-dd19-e02a-37f4b87e6ddf.png)

正直、思ったより綺麗です。
Rise Time も良好では無いでしょうか。
ジッタも画像レベルではOKです(笑

#### 48.0kHz系

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/4a416568-ad7b-73f4-bf15-7a38f791ea2f.png)

画像上のジッタは 0.5ns くらいでしょうか。。。そこまでひどいクロックでは無さそうです。
ただし、オシロスコープ自体は 1Gsps ですので、観測限界を超えてます。

### NanoPi-NEO2

#### 44.1kHz系

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/8aca3677-f878-bc2a-13ce-a428583e8904.png)

#### 48.0kHz系

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/aa1f3fa7-fd3d-1466-6ecb-88d93572692f.png)


## 352.8 kHz再生時

### NanoPi-NEO2

#### LRCK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/66c236e9-ead6-d1b1-4184-44bca7402e15.png)

## 384kHz 再生時

### NanoPi-NEO2

#### LRCK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/da7ca2b9-2073-3a12-db29-434d36ffae14.png)

## 768kHz再生時

### NanoPi-NEO Plus2

#### MCLK

768kHz * 64fs = 49,152,000 [Hz] に対し、49.1453 [MHz]なので、0.0137%ほどのズレ。
オシロもついてきていないようで、波形の細かな部分は落ちてきています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/737919fa-e393-d68b-6d3e-0a51189dd302.png)

#### LRCKとBCK

ちゃんと、64fsで出ています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/491f33ff-a8a4-ecd1-31ec-dd4d4f263bd3.png)

#### LRCKとDATA

ちゃんと32bitぎっちり詰まっています♪♪
LRCK 768kHz は良いね！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/d9b7597d-6fd1-3ddb-321f-e1757959a732.png)

## 1536kHz 再生時

### NanoPi-NEO2

#### MCLK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/7774120a-74fa-3f42-89db-f952381e4e68.png)

オシロついていかず、完全にSine波です。。。

#### LRCK と BCK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/1d0ec8cd-9947-ff07-a1cc-e375c64660e0.png)

#### LRCK と DATA

![image.png](https://qiita-image-store.s3.amazonaws.com/0/187531/e47449b6-db7c-2c4e-a22d-5fe60708ed33.png)

再生データは24bitなので、後半8bitは出てません。

## hw_params

### 352.8 kHz再生時

```console:NanoPi-NEO2
root@NanoPi-NEO2:~# cat /proc/asound/card0/pcm0p/sub0/hw_params

access: RW_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 352800 (352800/1)
period_size: 44100
buffer_size: 176400
```

### 384kHz再生時

```console:NanoPi-NEO2
root@NanoPi-NEO2:~# cat /proc/asound/card0/pcm0p/sub0/hw_params

access: RW_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 384000 (384000/1)
period_size: 48000
buffer_size: 192000
```

### 768 kHz再生時

```console:NanoPi-NEO_Plus2
root@NanoPi-NEO-Plus2:~# cat /proc/asound/card1/pcm0p/sub0/hw_params

access: RW_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 768000 (768000/1)
period_size: 32768
buffer_size: 131072
```

bufferサイズが384kHz時より小さくなっているのは気になりますが、音飛びしていないようなので保留。

### 1536kHz再生時

```console:NanoPi-NEO_Plus2
root@NanoPi-NEO-Plus2:~# cat /proc/asound/card1/pcm0p/sub0/hw_params
access: RW_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 1536000 (1536000/1)
period_size: 32768
buffer_size: 131072
```

ここまでくるとDSDかと錯覚させる領域。。。
でもPCMなんです！

## mpd.conf

24bitが正しく処理できるようになったので、暫定対策として入れていた32bitへの強制アップサンプリング指定が不要になりました。

■今まで

```console:/etc/mpd.conf
audio_output {
        type            "alsa"
        name            "HanoHat PCM5102a"
        device          "hw:sndsun8ii2sdac,0"
        mixer_type      "software"
        format          "*:32:2"
        replay_gain_handler "software"
}
```

■これから

```console:/etc/mpd.conf
audio_output {
        type            "alsa"
        name            "HanoHat PCM5102a"
        device          "hw:sndsun8ii2sdac,0"
        mixer_type      "none"
}
```

# 結果(DAC系)

## PCM5102のMCLK入力

PCM5102はMCLKが入力されていない場合、内部でMCLKを自動生成しますが、あえて外部から入力してみました。
結果、わずかに分解能向上するようです。
ただし、これに関してはプラシーボの可能性もあります（笑

また、768kHzの信号入力に関しては、MCLKの外部入力・内部生成に関らず、いずれも再生不可(音でない)でした。

## AK4495SEQ を使う

eBayで購入したAK4495SEQのI2S DACボード。
MCLKが必要で、BLCKと同期している必要があるため、ようやく使えるようになります。
使えるDACが増えるのは、良いね！

![_72D3251s.jpg](https://qiita-image-store.s3.amazonaws.com/0/187531/adde9245-5977-7d7a-968a-aada71b70a0b.jpeg)

スペックは、PCM：768kHz 32bit、DSD: 2.8MHz, 5.6M になっています。

PCM 768kHzに関しては、すんなり音出ました。
AK4495SEQのDatasheet上、MCLKの49MHzにおいては対応できない場合(モード)もあると記載がありましたが、普通に音出てくれました。

DSDに関しては、Nanopi-NEOが素のDSD(AK4495がそのままで受け付けてくれる状態での)出力は無理なので、
DoPでI2Sして、I2SからDSDの変換基盤(LRCKに応じてデータを分配)を作れば大丈夫に思えます。

そしてなんと！PCM-1536kHzも音出るようです。
ただ、曲の冒頭でプチプチ言いますので、DAC側でのクロック検出に問題が生じているのかもしれません。
もちろん、正式サポート外ですので、自己責任で。
あと、8kHzは音出ませんでした。(笑

AK4495のサポート範囲は、あくまでも32kHz～768kHzです。


# 最後に

H5のDatasheetを見ると、

- AUDIO_PLLは、20MHz~200MHz
- I2Sは100MHz
 
~~と言っているので、実は768kHzも行けそうです。~~
~~この場合、~~
~~- LRCK = 768kHz~~
~~- BCK = LRCK * 32 * 2 = 49,152,000 Hz~~
~~- MCLK = BCK~~
~~が基準になります。~~

768kHz対応したので、書き直しです(笑

と言っているので、実は 1536kHzも行けそうです。
この場合、

- LRCK = 1536 kHz
- BCK = LRCK * 32 [bit] * 2[ch] = 98,304,000 Hz
- MCLK = BCK

が基準になります。
これが、NanoPi-NEOの対応限界となります。

## おまけ

各ファイルフォーマットの仕様
(主にWikiより)

|Format|Max frequency|Max bit depth|
|:--|--:|--:|
|FLAC|655.3 kHz|32 bit|
|ALAC|384 kHz|32 bit|
|AAC|96 kHz|?|
|mp3|48 kHz|?|
|.wav|4.2 GHz|65535 bit|

FLACで対応していないとなると、768 kHzは配信用としてはあまりなさそうですね。
完全にマスタリング用でしょうかね。

しかし、wavの対応範囲がすごいですね(笑
ただしファイルサイズ上限4GBが付きまといますが。