作りかけだったコードを使えるようにしてみました。
当初Onion Omega(AR9331 MIPS 24K)で試していたのですが、割り込みの処理が重くアンダーランしていました。dev/sound/pcmの問題と思われ放置していました。
サウンドをいろいろ試していて、どうもFreeBSDのサウンド処理はMIPS 24Kと相性が悪い可能性が濃厚になってきました。
24Kはi386などに比べて十分早いと思われ、にわかに信じがたいのですが、FreeBSDのコードがMIPSの悪いところをついてしまっているような気がします。MIPS社も気がついていて74Kでは改良したような気がします。
たまたま手元にHardOffで108円で手に入れた74KのAR9341なモジュールがあり、これで試してみるとこにしました。
AR9331で動かなかったので、いろいろごにょごにょしたコードを一度整理しました。
FreeBSDのpcmドライバーはデフォルトではダブルバッファなのですが、2つより多くする事も可能で、私のドライバーでは8段にしてあります。おそらくダブルバッファでも大丈夫ではないかと思われまが、ここは面倒なのでそのままにしました。
MIPS SOCはRalinkとAtherosが双璧をなしていました。両方ともI2Sのサポートがありますが、SPDIFはAtherosにしかありません。I2Sは4本信号線が必要ですが、SPDIFは1本ですむのでお手軽です。
AR9341にはPINマルチプレクサーという新機能があり、任意のGPIOに任意の機能をアサインできます。この機能を使って、モジュールでLEDが実装されていないGPIO12にSPDIFを設定しました。
# SPDIF_OUT
hint.gpio.0.func.12.gpiofunc=25
hint.gpio.0.func.12.gpiomode=1
sys/mips/atherosはhintsベースなので下記を設定します。
hint.pcm.0.at="apb0"
hint.pcm.0.maddr=0x180b0000
hint.pcm.0.msize=0x1000
hint.pcm.0.irq=7
mkでは以下を追加します。
KERNCONF_DEVICES+=sound ar71xx_pcm
AR9331とAR9341はPLLの設定が違っています。
Atherosの出したと思われるオープンソースを見るとDPLLというレジスタの設定をしているのですが、データシートにDPLLはありません。
どうもAR934xで追加されたレジスタのようです。FreeBSDのコードにも一部定義がありましたがAudio用の定義がなかったので追加しました。
Linuxのコードを見ながらDPLLの処理を追加してみました。AthrosのオープンソースはBSD/GPLのデュアルライセンスなので安心です。
# cat /dev/sndstat
Installed devices:
pcm0: <Atheros AR71XX I2S/SPDIF-out Audio Interface> (play) default
No devices installed from userspace.
SPDIFをONKYOのミニコンポで受けてデジタル信号が認識できるか確認して試しました。SPDIFの信号を正しく受けられてる場合は赤いDIGTALが点灯し、正しく受けられてないときは点滅します。
STEREO_CONFIGのPOSEDGEがLinuxのソースを見ると2で固定なのですが、データーシートを見るとなにやら呪文が書いてあり、意味が良く分からないので、トライアンドエラーしたところ3だとミニコンポがデジタルデータを認識します。
ZRouterのtargetに以下のサイトにあったプログラムをちょっといじってsinというテストプログラムを入れてあります。これにテスト用に無音やエンディアンのチェックができる機能を追加して試しました。
無音で試すとレベルメーターが触れない状態になりましたが、サイン波はほとんど雑音が再生されます。般若心経の教えで、すべては無から始まります。
大きな雑音はエンディアンなどの問題でした。
テストで鳴らしていてサイン波が聞こえるのですが、まだ雑音がのります。これはサウンドバッファの大きさによっていて、DMAのディスクリプタのバッファのサイズが12Bitなので16bitx2chのバウンダリーにあわせると4k-4byteが最大なので、これにしたところだめで、3Kにしたところ、正しくサイン波が聞こえるようになりました。後から気がついたのですが、3KというのはSPDIFの1ブロックの192フレームのLRデーター(64bit=8Byte)の2ブロック分(192x8x2)でした。
不思議なのはAtherosのLinuxnのコードは1ディスクリプタのバッファがかなり小さく、割り込みが多くなりパフォーマンス的に不利になるので、なぜなのでしょうか?またSPDIFの192フレームとも違っていて、SPDIFでまともに動くか疑問です。
VUCをどのように送ってるのか、いつかFPGAでモニターを作って調べてみたいです。
一度startして止めて、再度startすると再生できない問題がありました。これはstopの時にMBOX_DMA_RX_CONTROLをstopではなくresumeにしたところ問題が解消しました。
作ったコードはsys/mips/atheros/ar71xx_pcm*です。
ターゲットにしたモジュールにはLANのインジケータとしてLEDが4付いていて、それを外してI2SにしてDACにつないでみてもよいかもしれません。
と思ったのですが、実はまだ終わってません。POSEDGEが3だと32Kになってpcm側は48Kと思って送り出すので周波数が2/3になってしまっていました。440Hzのはずが下のように290Hzになっています。とりあえずpcmを32Kにして正しく再生できるようにしてありますが、ぼちぼち調べてみたいと思います。
トスリンクのコネクタの接続を作り直したりしていたところ48Kでも再生できるようになっていました。何が効いたのかは不明なのですが、
なぜかmpg123が二回失敗した後に正常に動くようになる。失敗したときにはdmesgに以下の行が出ている。
pcm0: chn_write(): pcm0:virtual:dsp0.vp0: play interrupt timeout, channel dead
必ず三回目で成功する。またUSB Audioでは起きないし、USB Audioで再生した後は、問題は起きない。
これ以外の問題はなく、再生中に落ちたとかまったくない。音もいい。
intrの処理見直して直りました。
何度も書いていますが、30年前にOMRONでLUNA 88KとカノープスのCバスカードのサウンドマスターを使ってCDクオリティのサウンド再生システムを作らせてもらいました。このドライバはそのときのプロジェクトの成果物とも思えてきます。
Kernel/VM探検隊でこのドライバーの話をしましたので、興味があったら見てみてください。