wm8960修改MCLK时钟
问题出现的背景:
最近两天遇到了一个iMX6的项目定制WM8960音频芯片播放声音出现砰砰声的问题。
此问题出现在客户的批量产品上,一共检验280台,喇叭噪音大的有10多台,噪音小的有50多台。砰砰声比较大的板子更换音频芯片后测试正常,但换下来的音频芯片在公司自己的6Q-C底板上面无杂音。
前期判断:
以上述信息来看的话,就是这个现象不是必出的,而且和硬件有一定的关联性,由于客户定制做好的板子比较多,单靠硬件去排查解决的话不现实,还是希望能在软件上通过一些手段将这个问题解决或者规避掉。
因为自己对音频这块不是很熟悉,完全不知道如何下手。
刚开始排查的时候,同事提醒有可能是音频芯片的DAPM部分的问题,所谓DAPM,也就是Dynamic Audio Power Management动态音频电源管理,就是通过音频使用过程中用户所使用的音频路径bybass,动态控制路径上的门开关来实现诸如如果使用MIC录音,Speaker放音的时候,就关闭掉Line in通道的输入和HeadPhone放音的路径。这样才来实现节能的功能。
初期猜想是DAPM控制的时候,有一些控制没有做好,导致客户的定制版使用speaker放音的时候,别的通路开关串进去了杂音信号。
因为在6Q-C的Linux4.1.15的系统上测试speaker也有轻微可以接受的砰砰声,而3.0.35系统上没有。李哥说这两个系统的区别就是当时调Linux3.0.35的音频的时候关掉了DAPM的功能,所以理所当然的想到如果把Linux4.1.15系统上音频的DAPM也关掉。
前期验证结果:
依照判断在Linux4.1.15文件系统中通过amixer命令查看到有很多的amixer contents,这些都是可以控制的ALSA音频的API接口,可以控制音频芯片上的各种组件的开关,增益,或者MUX通路选择。这些内容以后有机会还会进行详细说明。
由于对音频这一部分还不很熟悉,所以我将这些全部打开进行测试,结果砰砰声问题仍然存在。
不知道是测试方法问题是否使此次验证生效,不过这次尝试也就失败了。
后期李哥排查:
之后让李哥协助排查,李哥拿到板子之后发现砰砰声不止出现在音频的播放开始和结尾,播放过程中也有出现。所以开始怀疑是音频芯片工作不正常导致,芯片工作不正常的原因有两个,一是电源不正常,二是时钟不正常。所以先开始排查时钟问题。
首先介绍一下WM8960的时钟分频:
具体的一些名词解释和关系可以参照上一篇文章《音频的IIS引脚的理解》一起来看。
通过上面那个图,我们知道系统需要的SYSCLK为11.2896MHz,而设定的MCLK是与其关联的。
我们硬件上给了MCLK时钟之后,然后在驱动里面告诉我们设定的值,驱动自己会通过设定PLL和分频的值使其达到或者接近SYSCLK,来保证音频部分的时钟正常工作。
首先我们先排查砰砰声的问题是否是PLL锁相环的问题。
修改音频平台驱动文件:linux-4.1.15/sound/soc/fsl/imx-wm8960.c 第660行
static int imx_wm8960_probe(struct platform_device *pdev)
{
...
...
codec_clk = devm_clk_get(&codec_dev->dev, NULL);
if (IS_ERR(codec_clk)) {
ret = PTR_ERR(codec_clk);
dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
goto fail;
}
data->clk_frequency = clk_get_rate(codec_clk);
...
...
将
data->clk_frequency = clk_get_rate(codec_clk);
里面的 codec_clk 改为11.2896MHz*2=22579200Hz
data->clk_frequency = clk_get_rate(22579200);
此值是MCLK的时钟,原本通过devm_clk_get函数从设备树中动态获取,但是我们现在手动在代码中指定,这样系统就会认为给定的MCLK时钟为22579200,是SYSCLK需要的11.2896MHz的2倍,可以通过SYSCLKDIV[1:0]的2分频获得,这样就不会选择PLL锁相环的路径。
绕过锁相环验证结果:
经过这个方法验证,发现砰砰声依然存在,不是PLL锁相环的问题。
再次验证:
这次,我们将时钟的怀疑点放在了时钟的源头上,也就是MCLK提供的时钟上。
首先我们通过以上内容知道,当LRC需要44.1KHz频率时钟的时候,SYSCLK为11.2896MHz,这次我们让LRC工作在22.05KHz频率时钟模式下,这个时候的SYSCLK则对应减半,变为5.6448MHz,然后我们可以将MCLK时钟设置为24M的四分数即6MHz模式下,这样5.6448MHz和6MHz较为接近也可以正常工作。
首先修改文件系统的/etc/asound.conf 第239行
将
pcm.asymed{
type asym
playback.pcm "dmix_44100"
capture.pcm "dsnoop_44100"
}
改为
pcm.asymed{
type asym
playback.pcm "dmix_22050"
capture.pcm "dsnoop_44100"
}
这样就让LRC工作在22.05KHz频率时钟模式下了。
然后修改时钟,通过修改时钟文件:linux-4.1.15/arch/arm/mach-imx/clk-imx6q.c 第868行
添加一行内容:
imx_clk_set_rate(clk[IMX6QDL_CLK_CKO2_PODF], 6000000);
这样就将MCLK设置为了6MHz,然后修改上文提到的linux-4.1.15/sound/soc/fsl/imx-wm8960.c 第660行,修改为
data->clk_frequency = clk_get_rate(5644800);
这样修改的结果是,既降低了MCLK的时钟,又避免使用了PLL锁相环。
降低MCLK时钟验证结果:
经过这个方法验证,发现砰砰声没有了,确认了是MCLK的时钟问题,不知道是MCLK时钟不稳还是WM8960芯片接受24MHz时钟的时候无法处理。
后续:
问题已经找到,就是MCLK时钟太高的问题,但是为了做测试将时钟设置为6MHz也难免过低,于是我们测试将其设置为12HMz情况下再次测试
此时就可以将LRC工作在44.1KHz频率时钟下了,不用再次修改文件系统的/etc/asound.conf 了,这时的SYSCLK为11.2896MHz,我们可以将MCLK修改为24MHz的二分数12MHz。
同样通过修改时钟文件:linux-4.1.15/arch/arm/mach-imx/clk-imx6q.c 第868行
添加一行内容:
imx_clk_set_rate(clk[IMX6QDL_CLK_CKO2_PODF], 12000000);
这样就将MCLK设置为了12MHz,因为我们排除了PLL锁相环的问题,所以可以不用在驱动代码中指定时钟频率,可以让锁相环自由去分频倍频去将12MHz变成SYSCLK需要的更准确的11.2896MHz
然后修改上文提到的linux-4.1.15/sound/soc/fsl/imx-wm8960.c 第660行,修改为原来的值
data->clk_frequency = clk_get_rate(codec_clk);
经过这个方法验证,砰砰声变得很轻微可以接受了,暂时通过这个方法解决。