ASOC_Machine简要分析
2026/7/9 8:08:27 网站建设 项目流程

ASOC Machine 驱动简要分析

平台:RK3562 | 电源管理与 Codec:RK809


目录

  • 1. 找到 Machine 驱动的过程
  • 2. Machine 与 Codec 驱动分析
    • 2.1 整体流程(Mermaid 图)
    • 2.2 Codec 驱动注册
    • 2.3 Machine 驱动匹配 DAI
    • 2.4 DAI Name 的来源
    • 2.5 Machine 绑定与注册
  • 3. 总结

1. 找到 Machine 驱动的过程

从硬件上知道使用RK809作为电源管理和 Codec。

步骤

  1. 进入设备树节点目录:

    root@RK3562-Tronlong:/proc/device-tree/rk809-sound#
  2. 查看 compatible 属性:

    catcompatible

    输出:rockchip,multicodecs-card—— 这就是匹配驱动的内容。

  3. 反编译 DTB,找到最终生成的设备树:

    dtc-Idtb-Odts tl3562-minievm.dtb-otmp.dis
  4. 查看tmp.dis,找到对应的设备树文件rk3562-evb2-ddr4-v10.dtsi

  5. 在 SDK 中搜索rockchip,multicodecs-card,找到对应的 Machine 驱动:

    文件路径:kernel-5.10/sound/soc/rockchip/rockchip_multicodecs.c

    注意:该文件不在driver目录下,而是在sound/soc/rockchip/下。


2. Machine 与 Codec 驱动分析

Machine 驱动负责解析设备树中的关键属性:

  • rockchip,cpu = <&sai0>→ 获取 Platform 的 DAI(即 I2S 控制器)
  • rockchip,codec = <&rk809_codec>→ 获取 Codec 的 DAI

2.1 整体流程(Mermaid 图)

Machine 驱动端: rk809-sound

Codec 驱动端: rk817_platform_probe

是 两者都是 Codec 子节点

框架匹配时读取

传入的是

设备树 Device Tree 静态结构

包含子节点

rockchip,codec phandle 引用

PMIC 父节点: rk809

Codec 子节点: rk809_codec
带标签,含 spk-volume 等属性

Machine 声卡节点: rk809-sound

驱动 Probe 开始

MFD 框架创建设备时
已将 dev->of_node
指向 Codec 子节点

读取私有属性
如 spk-volume

走备用路径:
dev->parent->of_node
+ of_get_child_by_name

最终读到 Codec 子节点属性

调用 devm_snd_soc_register_component

内部执行 snd_soc_add_component

将 component 加入全局链表
component_list

此时 component->dev->of_node
保持指向 Codec 子节点

驱动 Probe 开始

of_parse_phandle
解析 rockchip,codec

获取 args.np
指向 Codec 子节点

调用 snd_soc_get_dai_name
传入 args

遍历 component_list

对每个 component 执行
soc_component_to_node

优先返回
component->dev->of_node

比较返回的 of_node
是否等于 args.np?

匹配成功!
返回 dai_name

核心结论

Machine 框架匹配:
直接用 dev->of_node Codec 子节点
Codec 驱动读属性:
绕道 dev->parent 曲线救国
殊途同归,终点都是 Codec 子节点

核心结论:Machine 端通过soc_component_to_node获取component->dev->of_node(即 Codec 子节点),与of_parse_phandle解析出的args.np(也是 Codec 子节点)进行匹配——两者指向同一节点,匹配成功。


2.2 Codec 驱动注册

设备树定义

设备树中定义rk809_codec节点:

rk809_codec:codec{#sound-dai-cells=<1>;compatible="rockchip,rk809-codec","rockchip,rk817-codec";clocks=<&mclkout_sai0>;clock-names="mclk";assigned-clocks=<&mclkout_sai0>;assigned-clock-rates=<12288000>;pinctrl-names="default";pinctrl-0=<&i2s0m0_mclk>;hp-volume=<20>;spk-volume=<3>;mic-in-differential;status="okay";};

在 SDK 中搜索rockchip,rk809-codec是搜不到的,但可以搜索到rockchip,rk817-codec。这说明一个驱动程序兼容两个 Codec

Probe 函数分析
staticintrk817_platform_probe(structplatform_device*pdev){// 解析设备树属性rk817_codec_parse_dt_property(&pdev->dev,rk817_codec_data);structdevice_node*node=dev->parent->of_node;/* * dev->parent->of_node 是谁? * 从设备树可知是 rk809: pmic@20,挂在 I2C0 上 */node=of_get_child_by_name(dev->parent->of_node,"codec");// 此时 node 对应 rk809_codec 节点// 解析各种 DTB 属性(举一个例子,其余类似)of_property_read_u32(node,"spk-volume",&rk817->spk_volume);// 获取 mclk 时钟rk817_codec_data->mclk=devm_clk_get(&pdev->dev,"mclk");// 注册 component(这个就是 component->driver 的来源)devm_snd_soc_register_component(&pdev->dev,&soc_codec_dev_rk817,rk817_dai,ARRAY_SIZE(rk817_dai));}

注册 component 的调用链

devm_snd_soc_register_component() → snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai) → snd_soc_component_initialize(component, component_driver, dev); component->dev = dev; component->driver = driver; → snd_soc_add_component(component, dai_drv, num_dai); list_add(&component->list, &component_list);

从上面的代码可以看到:在rk817_platform_probe中,component 被添加到全局链表component_list,且component->dev= 传入的dev


2.3 Machine 驱动匹配 DAI

Machine 驱动通过snd_soc_get_dai_name获取 Codec 的 DAI Name:

intsnd_soc_get_dai_name(structof_phandle_args*args,constchar**dai_name){// 获取 component 的 of_node,这里就是 rk809_codec 节点component_of_node=soc_component_to_node(pos);// 尝试通过 of_xlate_dai_name 获取 dai_nameret=snd_soc_component_of_xlate_dai_name(pos,args,dai_name);if(component->driver->of_xlate_dai_name)returncomponent->driver->of_xlate_dai_name(component,args,dai_name);}

但是,soc_codec_dev_rk817没有提供of_xlate_dai_name函数:

staticconststructsnd_soc_component_driversoc_codec_dev_rk817={.probe=rk817_probe,.remove=rk817_remove,.suspend=rk817_suspend,.resume=rk817_resume,.idle_bias_on=1,.use_pmdown_time=1,.endianness=1,.non_legacy_dai_naming=1,// 没有 .of_xlate_dai_name};

因此snd_soc_component_of_xlate_dai_name返回-ENOTSUPP

降级匹配逻辑

回到rk_multicodecs_probe

staticintrk_multicodecs_probe(structplatform_device*pdev){// 解析 rockchip,codec,args.node 就是 rk809_codecof_parse_phandle_with_fixed_args(np,"rockchip,codec",0,i,&args);// 获取 dai_namesnd_soc_get_dai_name(&args,&codecs[idx].dai_name);// snd_soc_get_dai_name 内部:// 1. component_of_node = soc_component_to_node(pos)// 2. 比较 component_of_node 与 args->np// - Machine 端解析 rockchip,codec → args.node = rk809_codec// - Codec 端 component->dev->of_node = rk809_codec// - 两者匹配!if(component_of_node!=args->np||!pos->num_dai)continue;if(ret==-ENOTSUPP){structsnd_soc_dai*dai;for_each_component_dais(pos,dai){*dai_name=dai->driver->name;if(!*dai_name)*dai_name=pos->name;}}}

关键点

  • possnd_soc_component,即之前在 Codec 驱动中注册的 component
  • for_each_component_dais遍历pos->dai_list,从中取出snd_soc_dai
  • Codec 驱动中的snd_soc_componentsnd_soc_component_driver结构体填充(snd_soc_component_initialize

2.4 DAI Name 的来源

DAI List 的构建

DAI List 是由 Codec 驱动在注册时添加的:

staticintrk817_platform_probe(structplatform_device*pdev)// Codec 的 probe{devm_snd_soc_register_component(&pdev->dev,&soc_codec_dev_rk817,rk817_dai,ARRAY_SIZE(rk817_dai));snd_soc_register_component(dev,cmpnt_drv,dai_drv,num_dai);snd_soc_add_component(component,dai_drv,num_dai);snd_soc_register_dais(component,dai_drv,num_dai);snd_soc_register_dai(component,dai_drv+i,...);dai->component=component;dai->dev=dev;dai->driver=dai_drv;// 在这里添加到 component 的 dai_listlist_add_tail(&dai->list,&component->dai_list);}
DAI Name 的取值

dai_name的取值优先级为:

  1. dai->driver->name(如果非空)
  2. pos->name(降级方案)

Codec 提供的dai_drvrk817_dai(取自devm_snd_soc_register_component的第 3 个参数),第 0 项的名字是rk817-hifi

staticstructsnd_soc_dai_driverrk817_dai[]={{.name="rk817-hifi",.id=RK817_HIFI,.playback={.stream_name="HiFi Playback",.channels_min=2,.channels_max=8,.rates=RK817_PLAYBACK_RATES,.formats=RK817_FORMATS,},.capture={.stream_name="HiFi Capture",.channels_min=2,.channels_max=8,.rates=RK817_CAPTURE_RATES,.formats=RK817_FORMATS,},.ops=&rk817_dai_ops,},// ...};

因此最终codecs[idx].dai_name = "rk817-hifi",可以用printk打印验证。


2.5 Machine 绑定与注册

解析完 DAI Name 后,Machine 驱动继续完成声卡绑定:

// 解析 daifmtrk_multicodecs_parse_daifmt(np,codecs[0].of_node,mc_data,prefix);// np = Machine 节点 rk809-sound// codecs[0].of_node = Codec 节点 rk809_codec// prefix = "rockchip,"ret=of_property_read_string(np,"dai-format",&str);// 设备树没有提供 dai-format,跳过snprintf(prop,sizeof(prop),"%sformat",prefix);// prop = "rockchip,format"of_property_read_string(np,prop,&str);// str = "i2s"(来自 dts: rockchip,format = "i2s";)// 设置 CPU DAI 和 Platformlink->cpus->of_node=of_parse_phandle(np,"rockchip,cpu",0);link->platforms->of_node=link->cpus->of_node;// platform 也指向 &sai0,说明 CPU DAI 和 Platform 是同一个 I2S 控制器// Platform 就是 DMA// 注册声卡snd_soc_card_set_drvdata(card,mc_data);devm_snd_soc_register_card(&pdev->dev,card);snd_soc_register_card(card);snd_soc_bind_card(card);snd_card_new(card->dev,SNDRV_DEFAULT_IDX1,SNDRV_DEFAULT_STR1,card->owner,0,&card->snd_card);snd_ctl_create(card);dev_set_name(&card->ctl_dev,"controlC%d",card->number);snd_device_new(card,SNDRV_DEV_CONTROL,card,&ops);snd_info_card_create(card);

3. 总结

Machine 驱动的核心工作可以概括为以下几步:

  1. 解析设备树:获取rockchip,cpu(I2S 控制器)和rockchip,codec(Codec)的 phandle 引用
  2. 匹配 DAI
    • 遍历全局component_list,通过soc_component_to_node获取每个 component 的 of_node
    • of_parse_phandle解析出的 Codec 节点比较,找到匹配的 component
    • 从 component 的dai_list中取出 DAI,获取dai_name
  3. 建立链接
    • link->cpus->of_node→ I2S 控制器
    • link->codecs->of_node→ Codec 节点
    • link->platforms->of_node→ 与 CPU DAI 相同(I2S 本身也承担 DMA 角色)
  4. 注册声卡:调用devm_snd_soc_register_card完成声卡绑定和注册

关键结论

  • Machine 端直接用dev->of_node(Codec 子节点)进行匹配
  • Codec 端读取属性时绕道dev->parent->of_node(PMIC 父节点)再通过of_get_child_by_name("codec")找到 Codec 子节点
    dai_name
  1. 建立链接
    • link->cpus->of_node→ I2S 控制器
    • link->codecs->of_node→ Codec 节点
    • link->platforms->of_node→ 与 CPU DAI 相同(I2S 本身也承担 DMA 角色)
  2. 注册声卡:调用devm_snd_soc_register_card完成声卡绑定和注册

关键结论

  • Machine 端直接用dev->of_node(Codec 子节点)进行匹配
  • Codec 端读取属性时绕道dev->parent->of_node(PMIC 父节点)再通过of_get_child_by_name("codec")找到 Codec 子节点
  • 两者殊途同归,最终都指向同一个 Codec 子节点

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询