rk3399 simple-card alsa架构分析

一、 概念

  1. CPU DAI:主控端的 Audio Data Interface,比如 I²S,Spdif,Pdm,Tdm (通常所说的platform层)
  2. CODEC DAI:即 Codec将数字信号转换为模拟音频输出的新品(通常所说的codec层)
  3. DAI_LINK:绑定 Cpu_Dai 和 Codec_Dai 为一个声卡,等同于 Machine Driver。(通常所说的platform层)
  4. DMAENGINE:用于 Cpu 和 I²S/Spdif 等 Dai 之间的 Dma 传输引擎,实际是通过 Dma
    来进行数据的搬运。(其实是包含在platform层中)
  5. DAPM:动态音频电源管理,用于动态管理 Codec 等的电源管理,根据通路的开启配置开
    关,以达到保证功能的前提下功耗尽量小。
  6. JACK:耳机的接口检测,大部分使用 Codec 自身的检测机制,小部分使用 IO 来进行模拟。

主要理解核心思想是alsa将底层硬件的不同做了抽象分别是cpu digital audio interface的不同,声卡codec的不同,以及如何将这两者进行关联的中介machine driver :dai link,通常名字是chipName_codecName.c;
simple_card架构即是简单通用的machine driver即dai_link

二、代码结构

项目 功能 路径
Sound soc 主要包含公共部分代码,包括dapm 控制, jack, dmaengine,core 等等 sound/soc/
rockchip platform Rockchip 平台的 cpu dai 的驱动,比如 I²S, spdif 等以及自定义声卡machine driver sound/soc/rockchip
generic platform simple card framework sound/soc/generic
codec driver 所有的 codec driver 存放位置 sound/soc/codecs

开发步骤

一个声卡包含 cpu_dai, codec_dai, 以及 dai_link 组成,分别对应 cpu dai 的 dirver,比如I²S driver, spdif driver; codec driver, 比如 rk817 codec driver; dai_link driver,也就是 machine driver, 比如 sound/soc/rockchip/rockchip_rt5640.c。 4.4 的内核中支持两种方式创建声卡,一种是通用的 simple-card framework,一种是传统的编写自定义的 machinedriver 来创建。 本文以 rk817 为例。

  1. 添加 codec driver,比如添加: sound/soc/codec/rk817_codec.c
  2. 修改 sound/soc/codec/Kconfig 以及 Makefile 加入驱动编译。
sound/soc/codec/Kconfig:
        tristate "Rockchip RK817 CODEC"
        depends on MFD_RK808
        select REGMAP_I2C
sound/soc/codec/Makefile:  
		snd-soc-rk817-objs := rk817_codec.o
		obj-$(CONFIG_SND_SOC_RK817)     += snd-soc-rk817.o      
  1. menuconfig 中 enable simple card 以及 codec
make menuconfig
Device Drivers --->
[*] Sound card support --->
[*] Advanced Linux Sound Architecture --->
[*] ALSA for SoC audio support --->
	[*] ASoC support for Rockchip
	[*] Rockchip I2S Device Driver
		CODEC drivers --->
			[*] Rockchip RK817 CODEC
[*] ASoC Simple sound card support
  1. 产品的 DTS 中添加 Simple Card Node
rk809-sound {
                compatible = "simple-audio-card";
                simple-audio-card,format = "i2s";
                simple-audio-card,name = "rockchip,rk809-codec";
                simple-audio-card,mclk-fs = <256>;
                simple-audio-card,widgets =
                        "Microphone", "Mic Jack",
                        "Headphone", "Headphone Jack";
                simple-audio-card,routing =
                        "Mic Jack", "MICBIAS1",
                        "IN1P", "Mic Jack",
                        "Headphone Jack", "HPOL",
                        "Headphone Jack", "HPOR";
                simple-audio-card,cpu {
                        sound-dai = <&i2s1>;
                };
                simple-audio-card,codec {
                        sound-dai = <&rk809_codec>;
                };
        };
&i2c0 {
        rk809: pmic@20 {
                rk809_codec: codec {
                        #sound-dai-cells = <0>;
                        compatible = "rockchip,rk809-codec", "rockchip,rk817-codec";
                        clocks = <&cru SCLK_I2S_8CH_OUT>;
                        clock-names = "mclk";
                        pinctrl-names = "default","spk_gpio";
                        pinctrl-0 = <&i2s_8ch_mclk>;
                        hp-volume = <20>;
                        spk-volume = <3>;
                        io-channels = <&saradc 4>;
                        hp-det-adc-value = <930>;
                        capture-volume = <0>;
                        spk-ctl = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
                        pinctrl-1 = <&spk_ctl_gpio>;
                        status = "okay";
                };
        };
};

5、分析simple-audio-card实现

首先通过打印大致看看:


[    2.527416] asoc-simple-card rk809-sound: New simple-card: rockchip,rk809-codec
[    2.527458] asoc-simple-card rk809-sound: Revert to legacy daifmt parsing
[    2.527681] asoc-simple-card rk809-sound:    name : ff890000.i2s-rk817-hifi
[    2.527702] asoc-simple-card rk809-sound:    format : 4001
[    2.527721] asoc-simple-card rk809-sound:    cpu : ff890000.i2s / 0
[    2.527739] asoc-simple-card rk809-sound:    codec : rk817-hifi / 0
[    2.527761] of_get_named_gpiod_flags: can't parse 'simple-audio-card,hp-det-gpio' property of node '/rk809-sound[0]'
[    2.527782] of_get_named_gpiod_flags: can't parse 'simple-audio-card,mic-det-gpio' property of node '/rk809-sound[0]'
[    2.536256] asoc-simple-card rk809-sound: rk817-hifi <-> ff890000.i2s mapping ok
[    2.536953] asoc-simple-card rk809-sound: ASoC: no source widget found for MICBIAS1
[    2.537668] asoc-simple-card rk809-sound: ASoC: Failed to add route MICBIAS1 -> direct -> Mic Jack
[    2.538485] asoc-simple-card rk809-sound: ASoC: no sink widget found for IN1P
[    2.539150] asoc-simple-card rk809-sound: ASoC: Failed to add route Mic Jack -> direct -> IN1P
[    2.539924] asoc-simple-card rk809-sound: ASoC: no source widget found for HPOL
[    2.540594] asoc-simple-card rk809-sound: ASoC: Failed to add route HPOL -> direct -> Headphone Jack
[    2.541425] asoc-simple-card rk809-sound: ASoC: no source widget found for HPOR
[    2.542098] asoc-simple-card rk809-sound: ASoC: Failed to add route HPOR -> direct -> Headphone Jack
[    2.544603] input: rockchip,rk809-codec Headphone Jack as /devices/platform/rk809-sound/sound/card1/input4

kernel/sound/soc/generic/simple-card.c
函数调用顺序大致如下:

static const struct of_device_id asoc_simple_of_match[] = {
        { .compatible = "simple-audio-card", },
        {},
};

通过driver platform框架我们可以知道下面会调用probe函数

asoc_simple_card_probe
	asoc_simple_card_parse_of
		snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");//拿到卡的名字即dts中rockchip,rk809-codec
		/* For single DAI link & old style of DT node */
		asoc_simple_card_dai_link_of //连接cpu dai与codec dai
			asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
                                            &dai_link->cpu_of_node,
                                            &dai_link->cpu_dai_name,
                                            &cpu_args); //拿到cpu dai
            asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
                                            &dai_link->codec_of_node,
                                            &dai_link->codec_dai_name, 
                                            NULL;//拿到codec dai
             
           	//soc/soc-core.c +3606
           	of_parse_phandle_with_args(of_node, "sound-dai",
                                       "#sound-dai-cells", 0, &args);//#sound-dai-cells 在dts中是0,即rk817_codec.c中的struct snd_soc_dai_driver rk817_dai[0]节点 即rk817-hifi。
           snd_soc_of_get_dai_name(np, name); //拿到dai name
 //注册声卡          
 snd_soc_card_set_drvdata(&priv->snd_card, priv);
 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);                       

这个达到的效果跟machine driver是一样的,只不过这个属于读取dts然后批量注册声卡,更加简单高效。

你可能感兴趣的:(alsa,rk3399,alsa)