Linux内核驱动初探(二) TI声卡

目录

0. 前言

1. menuconfig

2. 超时问题

3. 稳定性问题


0. 前言

通过查询该设备原理图可知,该声卡的Codec芯片型号为德州仪器 TLV320DAC3101。


1. menuconfig

我们在 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放入设备启动,结果在内核启动的时候报错了。


2. 超时问题

内核启动时上报的跟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文件了。


3. 稳定性问题

就在笔者认为声卡驱动大功告成的时候,以下问题再次出现:

[   55.618137] tlv320aic31xx-codec 0-0018: aic31xx_wait_bits: Failed! 0x25 was 0x82f3a0a8 expected 0x1 (0, 0x1, 500000 us)

笔者做了稳定性测试,发现:

  • 平均把板子断电重启50次就会出现1次这样的错误
  • 一旦出现这样的错误,不断电重启是无法恢复的
  • 如果开机没出现这样的错误,则在接下来的使用过程中声卡都是OK的

截止编辑这个帖子的时候,这个稳定性问题依旧存在。希望接下来的工作中可以解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值