现在的位置: 首页 > 综合 > 正文

WM8994调试要点记录

2012年04月22日 ⁄ 综合 ⁄ 共 4780字 ⁄ 字号 评论关闭

这两个礼拜调试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);
	else
		ret = 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);
	else
		ret = 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:

【上篇】
【下篇】

抱歉!评论已关闭.