Linux ASoC音频驱动架构 及 Machine驱动代码分析

news/2024/12/2 10:37:33/

【软件框架】

    在对要做的事情一无所知的时候,从全局看看系统的拓扑图对我们认识新事物有很大的帮助。Audio 部分的驱动程序框架如下图所示:

    这幅图明显地分为 3 级。

    上方蓝色系的 ALSA Kernel 整体属于Linux Kernel,是原生Linux 操作系统的一部分,其中又分出 ASoC Core 和 PCM Core 两级,和她们相关的代码都可以直接在 Linux 源码中找到。

    中间淡红色的部分看名字就知道和驱动相关,分为左右 2 条支线。需要注意的是左侧支线由 ASoC 派生而来,而 ASoC 虽本质上属于 ALSA,但在代码上将各部分驱动进行分离设计,也就是这里看到的 Platform Driver、Machine Driver、Codec Driver,分别对应 CPU 驱动、板驱动、编解码芯片驱动。这种架构增强了 CPU 芯片驱动和编解码芯片驱动的可移植性,让我们在开发音频驱动时只需要重新编写电路板相关的板驱动即可。进一步分析,紧接 Platform Driver 后面是 SST Driver,这个 SST 即 Smart Sound Technology,是 Intel 自研的技术,所以这部分结构不一定适用于其它 CPU 芯片,但一旁的 DMA Driver 却是所有 CPU 驱动都必须包含的,因为音频的实质是数据;Codec Driver 之后是 I2C 驱动,对于编解码芯片寄存器的读写都是通过 I2C 总线实现。右侧支线从 PCM Core 引出,这部分我目前还不熟悉,从框图和字面意思上看是和高清音频接口相关的,直接通过 ALSA 的 PCM 机制进行操作并且在硬件上使用专门的 HDMI 控制器而非编解码芯片。

    下方灰色一级是最底层的硬件部分,依次为 CPU 内嵌的 DSP 模块、独立的编解码芯片、独立的 HDMI 控制器。在 DSP 与 Codec 之间存在音频数据传输,虽然在图中没有注明,但我们知道这是通过 I2S 总线实现的。在我的实际项目中,DSP 模块集成在编解码芯片中。

    无疑,编解码芯片驱动,也即 Codec Driver 部分是我们编写音频驱动的核心。负责对内驱动芯片实现设备/驱动正常注册,对外沟通 DSP 进行音频数据交换。所以下文以此为中心进行代码分析。

 

【源码文件架构】

    按照 ASoC 框架的设计理念,源码文件应该分为 3 个部分,分别是 Platform Driver、Machine Driver、Codec Driver,这 3 者为并行关系,各对应一份源码。其中,Platform Driver 相关的源码主要实现 DMA 功能和 DAI,即 DSP 模块的 I2S 数据传输功能,并导出相应变量或操作函数接口;Codec Driver 相关的源码主要实现 I/O 控制、DAPM 和 PCM 配置,并导出相应变量或操作函数接口;Machine Driver 相关的源码则将前 2 个文件中导出的接口绑定在一起。内核启动后,以模块的形式加载 3 个驱动。

    在我的实际项目中,文件 rt5677.c 中是 Codec Driver 部分的源码,文件 cht_rt5677.c 中则为 Machine Driver 部分的源码。前者实现编解码芯片的寄存器配置、设备/驱动创建与注册,后者实现将 Machine 与 Platform 绑定。

 

【Machine Driver 代码分析】

    既然驱动是以模块的形式载入的,那么我们就从模块初始化函数开始阅读代码。

    模块初始化函数部分的代码为 late_initcall(snd_cht_driver_init);

 查看 snd_cht_driver_init() 函数:

static int __init snd_cht_driver_init(void)
{…return platform_driver_register(&snd_cht_mc_driver);
}

    原来是注册 platform 驱动,接下来的流程应该就比较熟悉了。

    结构体snd_cht_mc_driver 的定义如下:

static struct platform_driver snd_cht_mc_driver = {.driver = {.owner = THIS_MODULE,.name = "cht_rt5677",.pm = &snd_cht_mc_pm_ops,},.probe = snd_cht_mc_probe,    // 绑定 probe() 函数.remove = snd_cht_mc_remove,.shutdown = snd_cht_mc_shutdown,
};

        这里的 probe 函数 snd_cht_mc_probe() 会在平台驱动注册过程中被调用。查看该函数代码:

static int snd_cht_mc_probe(struct platform_device *pdev)
{…/* register the soc card */snd_soc_card_cht.dev = &pdev->dev;ret_val = snd_soc_register_card(&snd_soc_card_cht);    // 注册声卡…platform_set_drvdata(pdev,&snd_soc_card_cht);    // 设定声卡信息codec = cht_get_codec(&snd_soc_card_cht);// use gpio GPIO_SPK_EN to enable/disable ext boost pagpio_request(GPIO_SPK_EN, "speaker boost pa ctl");gpio_direction_output(GPIO_SPK_EN, 0);…return ret_val;
}

        其中 snd_soc_register_card() 是ASoC Core 中的函数,由Linux 内核实现,功能是创建和注册一个声卡设备。而函数 platform_set_drvdata() 的功能是将预设的声卡结构体值配置到 machine driver。追踪 snd_soc_card_cht 这个结构体,其定义如下:

/* SoCcard */
static struct snd_soc_card snd_soc_card_cht = {.name = "cherrytrailaud",.dai_link = cht_dailink,    // 绑定 dai_link.num_links = ARRAY_SIZE(cht_dailink),.set_bias_level = cht_set_bias_level,.dapm_widgets = cht_dapm_widgets,    // 绑定 dapm_widgets.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),.dapm_routes = cht_audio_map,.num_dapm_routes = ARRAY_SIZE(cht_audio_map),.controls = cht_mc_controls,    // 绑定 controls.num_controls = ARRAY_SIZE(cht_mc_controls),
};

        在这里我们看到了 cht_dailink 数组、cht_dapm_widgets() 数组、cht_mc_controls 数组。后 2 者主要实现 DAPM 相关操作,我们重点查看 cht_dailink,该数组核心部分代码如下:

static struct snd_soc_dai_link cht_dailink[] = {[CHT_DPCM_AUDIO] = {.name = "Cherrytrail Audio Port",.stream_name = "Cherrytrail Audio",.cpu_dai_name = "Headset-cpu-dai",.codec_name = "snd-soc-dummy",.codec_dai_name = "snd-soc-dummy-dai",.platform_name = "sst-platform",.init = cht_audio_init,    // 绑定 init() 函数.ignore_suspend = 1,.dynamic = 1,.ops = &cht_aif1_ops,.dpcm_playback = 1,},…
};

        初始化函数 cht_audio_init() 在第 1 个 cht_dailink 元素中被绑定。至此,Machine Driver 相关代码的分析就完成了。



http://www.ppmy.cn/news/341918.html

相关文章

USB Type-C接口——硬件/Lenovo

1.eMaker 考虑到USB-C的性能与功能大幅提升,那么对线材的要求也会相对提升。但是问题来了,怎么判断这个线材的优劣呢?? 因此就出现了EMarker芯片,定义各种工作模式。响应 USB PD 发出的标识命令返回有关电缆的信息&am…

随机数组归并问题

1 问题 生成两个任意的随机数组,并将这两个数组按照数字大小按顺序归并到一个新数组中。 2 方法 思路:定义三个数组,两个数组自己输入值,第三个数组用来作归并后的数组,先将两个数组的值全部赋给第三个数组&#xff0c…

USB声卡芯片DP108,完美替代CM108,升级版DP108T

DP108是一款完全替代CM108的高度集成的单芯片USB音频解决方案芯片。方便的USB即插即用的兼容性,用户可以快速创建易用性,高质量和便携式USB音频产品基于高度集成的单芯片解决方案。所有重要的模拟模块嵌入DP108,包括双DAC和耳机放大器,ADC和麦克风助力器&#xff0c…

联想同传系统安装即使用心得

2018.03.12 这几天,维护机房电脑,出现了各种问题,基本解决。这里进行一下总结,避免以后出现同样的错误。 一、安装联想同传系统 首先联想同传系统edu7.5.3的安装,非常简单。开机bios里面设置cd启动,插入…

码分多址CDMA通信

码分复用CDM或码分多址CDMA是一种共享信道的方法,每一个用户可以在同样的时间使用同样的频带进行通信, CDMA系统的一个重要特点就是这种体制给每一个站分配的码片序列不仅必须各不相同,并且还需要互相正交。 正交结果为1,说明发送…

C语言疑难进阶手册(2)

目录 预定义宏取消宏宏参数转换逗号运算符整数类型的8种组合进制表示浮点类型三种读写字符预定义宏 预定义宏是C语言中标准编译器预先定义的宏,在ANSI标准中C程序有5个预定义宏可以直接使用 ANSI标准中说明了以下5个宏替换名,可以直接使用 LINE:当前编译的代码的行号 FIL…

2020310

Selenium: 基于WebDriver协议的 Web应用程序测试的工具 精简版: selenium脚本通过http请求发送命令和参数给httpserver httpserver通过json wire protocol格式转发命令和参数给webdriver webdriver通过原生API或者JavaScript代码执行操作给浏览器 浏览器通过json wi…

赋能矿山 | KaiwuDB 智慧矿山解决方案

行业背景 随着勘探和矿产开发技术的提高以及能源需求量的大幅增加,矿山开发速度持续加快。随之而来的诸多弊端,如矿山资源综合利用率低、管理方式粗放、安全和环境污染等问题日益突出,使得矿业智能化建设迫在眉睫。 近年来,政府…