這兩個禮拜調試WM8994,還算順利,除了開始LDO不大穩定造成CODEC經常性異常外。不得不感歎,有時候一個電阻阻值的大小都影響了整個系統,做硬體的同學要注意哇。
Wolfson的工程師也很nice,對於我反饋的問題,都能及時得以解答,感謝他們。
下面記錄幾個大的問題點,至於一些瑣碎的,如缺少一些mixer controls (MIC Bias)、回放錄音通路設定等就略過了。
1、線上查看/更改CODEC寄存器
mount -t debugfs none /d //掛載debugfs檔案系統cd /d/asoc/WMT_WM8994/wm8994-codec //進入wm8994調試目錄cat codec_reg //查看wm8994寄存器echo 6 0010 > codec_reg //寫0x0010到reg[0006h]
這樣調試非常方便,有時候mixer controls還沒寫好,直接寫寄存器也能達到目的。
2、AIF模式(Master/Slave)選擇
WM8994支援Master/Slave模式,而每種模式都可使用MCLK或FLL作為AIFnCLK,所以這一塊根據自己實際情況配置好。比如我們使用如下模式:
則AIF1的hw_params這樣寫:
/* Set codec DAI configuration */ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S);if (ret< 0)return ret;/* Set cpu DAI configuration for I2S */ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S);if (ret < 0)return ret;/* Set the codec system clock for DAC and ADC */if (!(params_rate(params) % 11025))ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, 11289600, SND_SOC_CLOCK_IN);elseret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, 12288000, SND_SOC_CLOCK_IN);
3、Voice DAI
在調試Voice DAI中遇到一些問題,頗為耗時的問題。
我們通話模組的設計基本就是WM8994的典型設計:
使用AIF2與MODEM的PCM介面相連。這個在Linux-3.0.8裡有個不錯的例子可以參考,見sound/soc/samsung/goni_wm8994.c。不過我們配置modem為Master,CODEC為Slave,資料格式是PCM,所以AIF2的hw_params有點變動:
static int wmt_voice_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params){struct snd_soc_pcm_runtime *rtd = substream->private_data;struct snd_soc_dai *codec_dai = rtd->codec_dai;int ret = 0;if (params_rate(params) != 8000)return -EINVAL;/* Set codec DAI configuration */ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS);if (ret < 0)return ret;/* Set the codec system clock for DAC and ADC */if (!(params_rate(params) % 11025))ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, 11289600,SND_SOC_CLOCK_IN);elseret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, 12288000,SND_SOC_CLOCK_IN);return ret;}
改好了這個後,接下去馬上發現一個很大的問題,Android啟動後打電話,無法開啟AIF2,表現在open /dev/snd/pcmC0D1(or pcmC0D1p) failed (Invalid argument)。這個問題跟了一天,通過audio_hw->tinyalsa->alsa-core一步步加列印最終定位問題所在,出錯的地方:
snd_pcm_open_substream --> snd_pcm_attach_substream --> substream->ops->open(substream) --> err = snd_pcm_hw_constraints_complete(substream); --> err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
我們看看snd_pcm_hw_constraints_complete函數:
int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream){struct snd_pcm_runtime *runtime = substream->runtime;struct snd_pcm_hardware *hw = &runtime->hw;int err;unsigned int mask = 0;if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;if (hw->info & SNDRV_PCM_INFO_MMAP) {if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;if (hw->info & SNDRV_PCM_INFO_COMPLEX)mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;}err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);if (err < 0)return err;
這樣基本可以確定hw->info是空的,順藤摸瓜我們會留意struct snd_pcm_hardware這個結構體,這個東東一般是在DMA(一般是sound/soc/XXX/XXX_pcm.c)裡面設定的,如:
static const struct snd_pcm_hardware wmt_pcm_hardware = {.info= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,.formats= SNDRV_PCM_FMTBIT_S16_LE,.rate_min= 8000,.rate_max= 96000,.period_bytes_min= 32,.period_bytes_max= 4 * 1024,.periods_min= 2,.periods_max= 128,.buffer_bytes_max= 64 * 1024,//.fifo_size= 32,};static int wmt_pcm_open(struct snd_pcm_substream *substream){// ...snd_soc_set_runtime_hwparams(substream, &wmt_pcm_hardware);// ...}
這樣就很容易理解了,因為在goni_wm8994.c中,它根本沒有設定runtime hwparams,而我們照瓜畫瓢,自然就出錯了。因為AIF2是與MODEM PCM直連的,並不需要為它寫一個DMA驅動,因此我們在startup時設定runtime hwparams:
static const struct snd_pcm_hardware wmt_voice_hardware = {.info= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,.formats= SNDRV_PCM_FMTBIT_S16_LE,.rate_min= 8000,.rate_max= 16000,.period_bytes_min= 16,.period_bytes_max= 1024,.periods_min= 2,.periods_max= 16,.buffer_bytes_max= 4*1024,//.fifo_size= 32,};static int wmt_voice_startup(struct snd_pcm_substream *substream){struct snd_pcm_runtime *runtime = substream->runtime;int ret;snd_soc_set_runtime_hwparams(substream, &wmt_voice_hardware);/* Ensure that buffer size is a multiple of period size */ret = snd_pcm_hw_constraint_integer(runtime,SNDRV_PCM_HW_PARAM_PERIODS);return ret;}static struct snd_soc_ops wmt_voice_ops = {.startup = wmt_voice_startup,.hw_params = wmt_voice_hw_params,};
這樣上層就能成功開啟AIF2,通話通路也正常了。但我還是有點困惑:三星這個方案它如何run起來的呢?我本來很信任它的,經曆了這一遭,還是要對任何權威抱有懷疑態度才行。
4、BT DAI
-------------------------------------------------------------------------------------------
2013/3/26
不想多寫了,我只想說一句:如果你以後不想被這個項目搞得心力憔悴,各種奇怪問題層出不窮,騷擾得你上天無路入地無門求生不得求死不能的話,那麼最好讓codec作主,modem作從。
在做硬體設計前,請認真閱讀WM8994規格書DIGITAL AUDIO INTERFACE CLOCKING CONFIGURATIONS: