再战声卡无声难题(中)

吃过晚饭,继续写文章,求解声卡无声的问题。上次说到发现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, &reg);    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那样,不熟练,一想到芯片上密集的管脚就有点恐惧。欲知后来情况如何,请看《再战声卡无声难题》最后一集。

文末插播一条广告,本文作者主讲的《纳秒级优化训练营》北京站正在招生中,对低延迟设计感兴趣,或者极致追求性能的同行不容错过。

(写文章很辛苦,恳请各位读者点击“在看”,欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

Image

也欢迎关注格友公众号

Image

管理后台HTML页面是Web开发中一种常见的实践,主要用于构建企业或组织内部的管理界面,具备数据监控、用户管理、内容编辑等功能。本文将探讨一套美观易用的二级菜单目录设计,帮助开发者创建高效且直观的后台管理系统。 HTML5:作为超文本标记语言的最新版本,HTML5增强了网页的互动性和可访问性,提供了更多语义元素,如<header>、<nav>、<section>、<article>等,有助于清晰地定义网页结构。在管理后台中,HTML5可用于构建页面布局,划分功能区域,并集成多媒体内容,如图像、音频和视频。 界面设计:良好的管理后台界面应具备清晰的导航、一致的布局和易于理解的图标。二级菜单目录设计能够有效组织信息,主菜单涵盖大类功能,次级菜单则提供更具体的操作选项,通过展开和折叠实现层次感,降低用户认知负担。 CSS:CSS是用于控制网页外观和布局的语言,可对HTML元素进行样式设置,包括颜色、字体、布局等。在管理后台中,CSS能够实现响应式设计,使页面在不同设备上具有良好的显示效果。借助CSS预处理器(如Sass或Less),可以编写更高效、模块化的样式代码,便于维护。 文件结构: guanli.html:可能是管理页面的主入口,包含后台的主要功能和布局。 xitong.html:可能是系统设置或配置页面,用于管理员调整系统参数。 denglu.html:登录页面,通常包含用户名和密码输入框、登录按钮,以及注册或忘记密码的链接。 image文件夹:存放页面使用的图片资源,如图标、背景图等。 css文件夹:包含后台系统的样式文件,如全局样式表style.css或按模块划分的样式文件。 响应式设计:在移动设备普及的背景下,管理后台需要支持多种屏幕尺寸。通过媒体查询(Media Queries)和流式布局(Fluid Grids),可以确保后台在桌面、平板和手机上都能良好展示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值