目录
0. 前言
2. 超时问题
3. 稳定性问题
通过查询该设备原理图可知,该声卡的Codec芯片型号为德州仪器 TLV320DAC3101。
我们在 linux-menuconfig 里面如下设置:进入 Device Drivers ---> Sound card support ---> Advanced Linux Sound Architecture ---> ALSA for SoC audio support
进入 SoC Audio for Freescale CPUs:
注意,不必选择Eukrea TLV320.
返回上一层,进入 CODEC drivers:
这里就遇到了第一个问题,选项中并没有正正好好的TLV320DAC3101。
外设开发商一般很难做到,将自己历史上所有设备的驱动都做成独立条目,加入Linux内核。所以,在没有完全对应的选项时,首先把最接近的条目给选中。上图的选项是笔者几次尝试后的选择,亲测可用。
保存选项,退出menuconfig,编译一次通过,无报错。把zImage放入设备启动,结果在内核启动的时候报错了。
内核启动时上报的跟CODEC有关的错误有若干个,其中比较典型的当属这个:
[ 55.618137] tlv320aic31xx-codec 0-0018: aic31xx_wait_bits: Failed! 0x25 was 0x82f3a0a8 expected 0x1 (0, 0x1, 500000 us)
......
[ 64.214435] tlv320aic31xx-codec 0-0018: aic31xx_wait_bits: Failed! 0x25 was 0x82f3a0a8 expected 0x10 (0, 0x10, 500000 us)
内核报上面这个问题的时候,出现了明显的卡顿(尽管不影响最后的rootfs引导)。我们使用命令“grep -rl” 可以找到报错的来源:./sound/soc/codecs/tlv320aic31xx.c
static int aic31xx_wait_bits(struct aic31xx_priv *aic31xx, unsigned int reg,
unsigned int mask, unsigned int wbits, int sleep,
int count)
{
unsigned int bits;
int counter = count;
int ret = regmap_read(aic31xx->regmap, reg, &bits);
while ((bits & mask) != wbits && counter && !ret) // wait for counter times
{
usleep_range(sleep, sleep * 2);
ret = regmap_read(aic31xx->regmap, reg, &bits);
counter--;
}
if ((bits & mask) != wbits)
{
dev_err(aic31xx->dev,
"%s: Failed! 0x%x was 0x%x expected 0x%x (%d, 0x%x, %d us)\n",
__func__, reg, bits, wbits, ret, mask,
(count - counter) * sleep);
ret = -1;
}
return ret;
}
字面上讲,就是内核在等待 CODEC芯片 的 0x25 寄存器变成 0x01 或者 0x10,但该寄存器一直是其他乱七八糟的值。理论上,内核会等待最多counter次 ret == 0 的情况。
我们看看 TLV320DAC3101 的器件手册:
0x10 或者 0x01 分别是为了等到左通道或者右通道 class-D driver 启动,可见接力棒在CODEC芯片那边,它不递过来也就没办法。
笔者采取的补救措施:
......
unsigned int bits;
int counter = count * 3;
int ret = regmap_read(aic31xx->regmap, reg, &bits);
while ((bits & mask) != wbits && counter && !ret)
{
usleep_range(sleep, sleep * 2);
ret = regmap_read(aic31xx->regmap, reg, &bits);
counter--;
}
......
让 counter 增加2倍,regmap_read(...)读寄存器的失败重复次数更多。
其他报错也主要是类似的原因,笔者也采用类似的方法:
如在 ./sound/soc/soc-dapm.c 中
......
pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n",
w->name, ev_name);
soc_dapm_async_complete(w->dapm);
trace_snd_soc_dapm_widget_event_start(w, event);
ret = w->event(w, NULL, event);
trace_snd_soc_dapm_widget_event_done(w, event);
if (ret < 0)
dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n",ev_name, w->name, ret);
......
也要等到 ret 返回1 为止,通过while循环读取 event 5次。
......
pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n",
w->name, ev_name);
soc_dapm_async_complete(w->dapm);
trace_snd_soc_dapm_widget_event_start(w, event);
int i = 5;
while( ret = w->event(w, NULL, event) && i-- )
trace_snd_soc_dapm_widget_event_done(w, event);
if (ret < 0)
dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n",ev_name, w->name, ret);
......
如此这般修改以后,再编译,再启动内核。声卡确实好用了。可以 aplay 播放wav文件了。
就在笔者认为声卡驱动大功告成的时候,以下问题再次出现:
[ 55.618137] tlv320aic31xx-codec 0-0018: aic31xx_wait_bits: Failed! 0x25 was 0x82f3a0a8 expected 0x1 (0, 0x1, 500000 us)
笔者做了稳定性测试,发现:
- 平均把板子断电重启50次就会出现1次这样的错误
- 一旦出现这样的错误,不断电重启是无法恢复的
- 如果开机没出现这样的错误,则在接下来的使用过程中声卡都是OK的
截止编辑这个帖子的时候,这个稳定性问题依旧存在。希望接下来的工作中可以解决。