Linux 内核 ASoC 框架,在概念上将嵌入式音频系统拆分为多个可复用的组件驱动程序,包括 Codec 类驱动程序、平台类驱动程序和机器类驱动程序。在实现上,机器类驱动程序用 struct snd_soc_card
和 struct snd_soc_dai_link
结构描述,属于平台类驱动程序的 DMA 引擎驱动程序由 struct snd_soc_component_driver
结构描述,codec 类驱动程序和 I2S 等驱动程序,由 struct snd_soc_component_driver
、struct snd_soc_dai_driver
和 struct snd_soc_dai_ops
等结构描述。除平台类驱动程序外的各种驱动程序都通过 component 抽象组织在一起,即这些驱动程序都作为 struct snd_soc_component_driver
注册给 Linux 内核 ASoC 框架,Linux 内核 ASoC 框架为它们各自创建 struct snd_soc_component
结构对象,并保存在 sound/soc/soc-core.c
文件中定义的全局链表 component_list
中。
一个 DMA 驱动程序的示例为 soc/pxa/pxa2xx-pcm.c
:
static const struct snd_soc_component_driver pxa2xx_soc_platform = {
.pcm_construct = pxa2xx_soc_pcm_new,
.pcm_destruct = pxa2xx_soc_pcm_free,
.open = pxa2xx_soc_pcm_open,
.close = pxa2xx_soc_pcm_close,
.hw_params = pxa2xx_soc_pcm_hw_params,
.hw_free = pxa2xx_soc_pcm_hw_free,
.prepare = pxa2xx_soc_pcm_prepare,
.trigger = pxa2xx_soc_pcm_trigger,
.pointer = pxa2xx_soc_pcm_pointer,
.mmap = pxa2xx_soc_pcm_mmap,
};
static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
{
return devm_snd_soc_register_component(&pdev->dev, &pxa2xx_soc_platform,
NULL, 0);
}
static struct platform_driver pxa_pcm_driver = {
.driver = {
.name = "pxa-pcm-audio",
},
.probe = pxa2xx_soc_platform_probe,
};
module_platform_driver(pxa_pcm_driver);
DMA 引擎驱动程序和 I2S 或 Codec 驱动程序一样,通过 devm_snd_soc_register_component()
函数以 struct snd_soc_component_driver
的形式注册给 Linux 内核 ASoC 框架,但比较特别的地方在于,它的 dai driver 参数为空。
机器类驱动程序定义的 struct snd_soc_dai_link
结构对象通过 struct snd_soc_dai_link_component
描述它引用的其它类型的驱动程序,如机器类驱动程序 sound/soc/pxa/e800_wm9712.c
有如下的代码片段:
SND_SOC_DAILINK_DEFS(ac97,
DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
SND_SOC_DAILINK_DEFS(ac97_aux,
DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
static struct snd_soc_dai_link e800_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 HiFi",
SND_SOC_DAILINK_REG(ac97),
},
{
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
SND_SOC_DAILINK_REG(ac97_aux),
},
};
static struct snd_soc_card e800 = {
.name = "Toshiba e800",
.owner = THIS_MODULE,
.dai_link = e800_dai,
.num_links = ARRAY_SIZE(e800_dai),
.dapm_widgets = e800_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(e800_dapm_widgets),
.dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map),
};
SND_SOC_DAILINK_DEFS()
宏用于为 struct snd_soc_dai_link
方便地定义引用的 cpus
、codecs
和 platforms
等 struct snd_soc_dai_link_component
数组,其中用于定义 platforms
的 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))
引用了上面我们看到的 DMA 引擎驱动。
Linux 内核 ASoC 框架提供了一个通用的 DMA 引擎驱动程序,位于文件 sound/soc/soc-generic-dmaengine-pcm
中。这个驱动程序本身不会主动向 Linux 内核 ASoC 框架注册自己,需要使用 DMA 引擎在设备和内存之间传数据的驱动程序要在 probe
时注册它,如 sound/soc/rockchip/rockchip_pcm.c
:
static const struct snd_pcm_hardware snd_rockchip_hardware = {
.inf