吃过晚饭,继续写文章,求解声卡无声的问题。上次说到发现es8326.c的电源处理函数内部有/*TBD*/,怀疑它们有问题。
如何改进呢?一种简单的方案是把它们的内部实现注释掉,什么都不做。但是,我大脑中那些喜欢“追求完美”的细胞们站出来反对:“没有suspend会费电啊,不使用设备时也白白耗电,会减少电池待机时间,不利于节能和环保...”我深以为然,是啊,不能图一时简单,还是要选周全的方案。
切到主线代码
另一种方案是使用内核6.1本来包含的es8326.c,它的suspend和resume函数不再有TBD,看起来很完善。
static int es8326_suspend(struct snd_soc_component *component){ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); cancel_delayed_work_sync(&es8326->jack_detect_work); es8326_disable_micbias(component); regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF); regcache_cache_only(es8326->regmap, true); regcache_mark_dirty(es8326->regmap); return 0;}
static int es8326_resume(struct snd_soc_component *component){ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); unsigned int reg; regcache_cache_only(es8326->regmap, false); regcache_sync(es8326->regmap); regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); /* Two channel ADC */ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x02); regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x1F); regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xC8); regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x88); regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x20); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08); regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x22); regmap_write(es8326->regmap, ES8326_ADC1_SRC, es8326->mic1_src); regmap_write(es8326->regmap, ES8326_ADC2_SRC, es8326->mic2_src); regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); regmap_write(es8326->regmap, ES8326_HP_DET, ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol); regmap_write(es8326->regmap, ES8326_INT_SOURCE, es8326->interrupt_src); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); snd_soc_component_update_bits(component, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL); regmap_read(es8326->regmap, ES8326_CHIP_VERSION, ®); if ((reg & ES8326_VERSION_B) == 1) { regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xDD); regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x0F); /* enable button detect */ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xA0); } es8326_irq(es8326->irq, es8326); return 0;}
于是我准备切换回内核6.1自带的es8326.c,在真正修改文件名之前,我又想起来一个人——David,他是es8326.c驱动的维护者。我有他的微信,还通过电话。我把6.1版本的源文件发给他,“麻烦看一下,这个是最新版本的么?”
David很快回复:“不是,你去看6.15内核的”
我打开kernel.org,找到最新的6.15内核代码,粗略比较,确实比6.1的多了很多行代码,差异挺大,可能改掉了不少问题。多亏问了一下。
把es8326.c和它的头文件切到最新的6.15版本后,有个编译错误。
仔细看一下,是probe函数原型变化了。6.1版本使用两个参数:
static int es8326_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
而6.15版本改为一个参数:
static int es8326_i2c_probe(struct i2c_client *i2c)
打心里说,我很讨厌Linux内核中这种折腾参数的做法。今天减掉一个,明天可能又加回来。从软件工程的角度来讲,像probe这样具有接口性质的函数原型,应该尽可能保持稳定,本来设计两个参数,具有更大的灵活性,按说可以不删。
那么这个修改是哪位大侠干的呢?使用github的Blame功能很容易找到。
Commit 5eb1e6eBrowse filesUwe Kleine-Königauthored and wsakernel committedon Sep 13, 2023i2c: Drop legacy callback .probe_new()Now that all drivers are converted to the (new) .probe() callback, thetemporary .probe_new() can go away. \o/Link: https://lore.kernel.org/linux-i2c/20230626094548.559542-1-u.kleine-koenig@pengutronix.deReviewed-by: Javier Martinez Canillas <javierm@redhat.com>Reviewed-by: Jean Delvare <jdelvare@suse.de>Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>Signed-off-by: Wolfram Sang <wsa@kernel.org>master·v6.15-rc6v6.6-rc2
https://github.com/torvalds/linux/commit/5eb1e6e459cfa025f79c43014f66ff62a55542f1
看起来是好大个工程,先是加了个probe_new,后来又把probe_new废弃了。
书归正传,改掉这个编译错误的方法倒也简单,加个参数就行了。编译通过后,更新到幽兰上,还是没有声音。检查一番,确认是新的代码,使用i2cdump观察寄存器,差异更多了。
时间已经到了周一下午4点,还是没有看到深藏着的那个虫子(bug)。
GPIO静音开关
声音子系统的特点是琐碎的东西很多,多声道,各种音频格式,传输接口,加上音量调节,去除噪声等等,当然还有静音(Mute)。
切到新的6.15代码还是没有声音后,我一边思考新的解题思路,一边比较新旧代码。大约十几分钟后,发现了一个新线索。
在老的代码里,有处理GPIO的逻辑,定义了两个指针:
struct gpio_desc *hp_ctl_gpio; struct gpio_desc *spk_ctl_gpio;
两个GPIO,一个叫spk_ctl_gpio,是用来控制扬声器,实现静音功能的;另一个叫hp_ctl_gpio,是用来检测耳机的(Headset,或者叫head phone)。
看新的代码,使用了新的机制来实现静音和检测耳机,因此不再有GPIO的代码了。我和David确认,的确如此。
而对于幽兰,spk_ctl_gpio可能把扬声器静音,所以代码里还是需要处理的。于是我着手修改代码,把spk_ctl_gpio的代码加了进去,hp_ctl_gpio的没有加,使用新的逻辑。
加上代码后,有个编译错误,缺少函数原型。加上头文件就解决了。
更新编译好的新镜像,通过gpioinfo工具观察,可以看到spk-con在工作了。
gpiochip2 - 32 lines: line 0: unnamed unused input active-high line 1: unnamed unused input active-high line 2: unnamed unused input active-high line 3: unnamed unused input active-high line 4: unnamed unused input active-high line 5: unnamed unused input active-high line 6: unnamed unused input active-high line 7: unnamed unused input active-high line 8: unnamed unused input active-high line 9: unnamed unused input active-high line 10: unnamed unused input active-high line 11: unnamed unused input active-high line 12: unnamed unused input active-high line 13: unnamed "usb2-enable" output active-high [used] line 14: unnamed unused output active-high line 15: unnamed "spk-con" output active-high [used] line 16: unnamed unused input active-high line 17: unnamed unused input active-high line 18: unnamed unused input active-high line 19: unnamed unused input active-high line 20: unnamed unused input active-high line 21: unnamed unused input active-high line 22: unnamed unused input active-high line 23: unnamed unused input active-high line 24: unnamed unused input active-high line 25: unnamed unused input active-high line 26: unnamed unused input active-high line 27: unnamed unused input active-high line 28: unnamed unused input active-high line 29: unnamed unused input active-high line 30: unnamed unused input active-high line 31: unnamed unused input active-high
但是根本的问题还是没有解决,8326声卡依然没有声音。
es8326芯片来自苏州顺芯,David便在顺芯工作。看我的问题切到主线代码后还没有解决,他很热心的拉了个微信群,请他的一个同事E来帮忙,并把我们讨论过的各种信息发到了群里。
不知道e工是否看了之前的讨论,他在群里说的的第一句话便“不同凡响”,后来的结果也证明了他的话切中要害:“麻烦量一下时钟频率是多少”。我看设备树和内核消息里都是12288000,便回答:“看起来是12288000”,其实就是12.288M赫兹。
接下来,E工提出了一个后来证明非常合理,但当时却让我有点为难的建议:“实际测量一下吧”。
为什么为难呢?主要是我使用示波器的水平就像很多人使用gdb那样,不熟练,一想到芯片上密集的管脚就有点恐惧。欲知后来情况如何,请看《再战声卡无声难题》最后一集。
文末插播一条广告,本文作者主讲的《纳秒级优化训练营》北京站正在招生中,对低延迟设计感兴趣,或者极致追求性能的同行不容错过。
(写文章很辛苦,恳请各位读者点击“在看”,欢迎转发)
*************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生
扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物
也欢迎关注格友公众号