news 2026/1/18 7:27:27

dai link代码分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
dai link代码分析

dai link代码分析

  1. snd_soc_dapm_link_dai_widgets 函数连接 codec 的 widget 与其他的 widget。
// msm_tx_cdc_dma_be_dai_links(sun.c)staticstructsnd_soc_dai_linkmsm_rx_tx_cdc_dma_be_dai_links[]={{.name=LPASS_BE_TX_CDC_DMA_TX_4,// "CODEC_DMA-LPAIF_RXTX-TX-4".stream_name=LPASS_BE_TX_CDC_DMA_TX_4,// "CODEC_DMA-LPAIF_RXTX-TX-4".capture_only=1,.trigger={SND_SOC_DPCM_TRIGGER_POST,SND_SOC_DPCM_TRIGGER_POST},.ignore_suspend=1,.ops=&msm_common_be_ops,SND_SOC_DAILINK_REG(tx_dma_tx4),},}SND_SOC_DAILINK_DEFS(tx_dma_tx4,DAILINK_COMP_ARRAY(COMP_CPU("snd-soc-dummy-dai")),DAILINK_COMP_ARRAY(COMP_CODEC("lpass-cdc","tx_macro_tx2"),COMP_CODEC("wcd937x_codec","wcd937x_cdc"),COMP_CODEC("wcd938x_codec","wcd938x_cdc"),COMP_CODEC("wcd939x_codec","wcd939x_cdc"),COMP_CODEC("swr-dmic.01","swr_dmic_tx0"),COMP_CODEC("swr-dmic.02","swr_dmic_tx1"),COMP_CODEC("swr-dmic.03","swr_dmic_tx2"),COMP_CODEC("swr-dmic.04","swr_dmic_tx3")),DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));staticstructsnd_soc_dai_driverdummy_dai={.name="snd-soc-dummy-dai",.playback={.stream_name="Playback",.channels_min=1,.channels_max=384,.rates=STUB_RATES,.formats=STUB_FORMATS,},.capture={.stream_name="Capture",.channels_min=1,.channels_max=384,.rates=STUB_RATES,.formats=STUB_FORMATS,},.ops=&dummy_dai_ops,};// 最终会创建一个 name和sname 都为 "TX_AIF2 Capture" 的widget// 具体代码可查看 snd_soc_dapm_new_dai_widgets() 函数staticstructsnd_soc_dai_driverlpass_cdc_tx_macro_dai[]={{.name="tx_macro_tx2",.id=LPASS_CDC_TX_MACRO_AIF2_CAP,.capture={.stream_name="TX_AIF2 Capture",.rates=LPASS_CDC_TX_MACRO_RATES,.formats=LPASS_CDC_TX_MACRO_FORMATS,.rate_max=192000,.rate_min=8000,.channels_min=1,.channels_max=8,},.ops=&lpass_cdc_tx_macro_dai_ops,},}staticconststructsnd_soc_dapm_widgetlpass_cdc_tx_macro_dapm_widgets[]={SND_SOC_DAPM_AIF_OUT("TX_AIF1 CAP","TX_AIF1 Capture",0,SND_SOC_NOPM,LPASS_CDC_TX_MACRO_AIF1_CAP,0),SND_SOC_DAPM_AIF_OUT("TX_AIF2 CAP","TX_AIF2 Capture",0,SND_SOC_NOPM,LPASS_CDC_TX_MACRO_AIF2_CAP,0),}// 创建dai widgetsoc_probe_component()for_each_component_dais()snd_soc_dapm_new_dai_widgets()// 连接dai widget与其他的 widget(根据sname)snd_soc_dapm_link_dai_widgets()

简单梳理下流程:

  • 在调用 snd_soc_bind_card 函数之前,会调用 populate_snd_card_dailinks 填充所有要使用的 dai link。
  • soc_probe_component 函数会遍历 component 中的 dai,为每个 dai 创建对应的 widget,widget 的 name 与 sname 是 dai 中 stream 的 name
  • snd_soc_dapm_link_dai_widgets 函数会根据 sname 连接 dai widget 与普通的 widget。

比如上述的过程就会根据lpass_cdc_tx_macro_dai创建一个名为TX_AIF2 Capture的dai widget,并根据它的sname连接TX_AIF2 CAP这个widget。

  1. 函数 snd_soc_dapm_connect_dai_link_widgets 负责连接 cpu dai 与 codec dai。

    snd_soc_bind_card()soc_probe_link_components()soc_probe_link_dais()// 以下的两个函数都在前面函数的调用之后,因为前面两个函数会将对应的widget注册到声卡之中,没有注册后面的连接就无法进行snd_soc_dapm_link_dai_widgets()// 连接codec dai widget与 普通的 widgetsnd_soc_dapm_connect_dai_link_widgets()// 连接cpu dai widget与codec dai widget
  2. 每个dai link会对应一个pcm runtime,通过下面的调用在声卡中注册对应的pcm设备

    snd_soc_bind_card()for_each_card_rtds(card,rtd)soc_init_pcm_runtime(card,rtd);// 自动寻找空闲的次设备号,然后设置对应的snd_card_register中使用的回调函数soc_new_pcm()snd_card_register()// 创建一个名为 "CODEC_DMA-LPAIF_RXTX-TX-4" 的 rtdsoc_new_pcm_runtime()dev=kzalloc(sizeof(structdevice),GFP_KERNEL);// 这里会创建一个名为"CODEC_DMA-LPAIF_RXTX-TX-4"的devicedev_set_name(dev,"%s",dai_link->name);// 注册设备ret=device_register(dev);// 创建pcm设备,并添加到card的device中的详细调用栈如下:for_each_card_rtds(card,rtd)// 这个函数会将rtd中的pcm初始化,包括各种操作函数soc_init_pcm_runtime(card,rtd)soc_new_pcm()soc_create_pcm()snd_pcm_new()_snd_pcm_new()// 将创建的pcm设备添加到card的device链表之中snd_device_new()

component probe函数调用

  1. 注册component(以wcd938x.c为例子)

    staticstructsnd_soc_component_driversoc_codec_dev_wcd938x={.name=WCD938X_DRV_NAME,.probe=wcd938x_soc_codec_probe,.remove=wcd938x_soc_codec_remove,.controls=wcd938x_snd_controls,.num_controls=ARRAY_SIZE(wcd938x_snd_controls),.dapm_widgets=wcd938x_dapm_widgets,.num_dapm_widgets=ARRAY_SIZE(wcd938x_dapm_widgets),.dapm_routes=wcd938x_audio_map,.num_dapm_routes=ARRAY_SIZE(wcd938x_audio_map),.suspend=wcd938x_soc_codec_suspend,.resume=wcd938x_soc_codec_resume,};wcd938x_bind(){// 注册component到全局的component_list链表之中snd_soc_register_component(dev,&soc_codec_dev_wcd938x,wcd938x_dai,ARRAY_SIZE(wcd938x_dai))}
  2. 寻找component

    populate_snd_card_dailinks(){// 拷贝dai—link数据到msm_kalama_dai_links数组中memcpy(msm_kalama_dai_links+total_links,msm_rx_tx_cdc_dma_be_dai_links,sizeof(msm_rx_tx_cdc_dma_be_dai_links));}// 遍历msm_kalama_dai_links数组,创建对应的pcm_runtime结构for_each_card_prelinks(card,i,dai_link)// 里面会找cpu、codec、platform对应的dai和component,然后添加到rtd的components数组中snd_soc_add_pcm_runtime(card,dai_link);// 根据dai link中的dai link component找到对应的component,然后找到component对应的daisnd_soc_find_dai()
  3. 调用component 的 probe函数: 一般的功能就是注册widget,比如tfa9874这个smart pa的中定义的widget,将其注册到声卡之中。

    // 调用component driver中的probe函数soc_probe_link_components()snd_soc_component_probe()// 调用component driver中的probe函数staticintsoc_probe_link_components(structsnd_soc_card*card){structsnd_soc_component*component;structsnd_soc_pcm_runtime*rtd;inti,ret,order;for_each_comp_order(order){for_each_card_rtds(card,rtd){for_each_rtd_components(rtd,i,component){// 按照顺序执行probe函数if(component->driver->probe_order!=order)continue;// 根据rtd得到对应的componentret=soc_probe_component(card,component);if(ret<0)returnret;}}}return0;}soc_probe_link_dais()// 根据rtd中的dai,调用对应的dai probe函数snd_soc_pcm_dai_probe()intsnd_soc_pcm_dai_probe(structsnd_soc_pcm_runtime*rtd,intorder){structsnd_soc_dai*dai;inti;for_each_rtd_dais(rtd,i,dai){if(dai->driver->probe_order!=order)continue;// 现在已经改版为dai->driver->ops->probeif(dai->driver->probe){intret=dai->driver->probe(dai);if(ret<0)returnsoc_dai_ret(dai,ret);}dai->probed=1;}return0;}
  4. 为什么component的probe函数还需要在声卡注册阶段主动的调用,而不是直接驱动自动触发的probe调用

    • 他也会注册为平台设备,但是平台设备的probe函数的功能是去注册component组件。后续由声卡从component_list中找到组件,然后进行调用

      • 第一次probe,平台设备驱动的probe函数,它负责注册component到compnent_list中
      • 第二次probe,声卡的驱动程序从component_list中找到对应的数据,调用其component driver的probe函数与dai driver的probe函数
      staticconststructdev_pm_opswcd938x_dev_pm_ops={.suspend_late=wcd938x_suspend,.resume_early=wcd938x_resume,};#endifstaticstructplatform_driverwcd938x_codec_driver={.probe=wcd938x_probe,.remove=wcd938x_remove,.driver={.name="wcd938x_codec",.owner=THIS_MODULE,.of_match_table=of_match_ptr(wcd938x_dt_match),#ifdefCONFIG_PM_SLEEP.pm=&wcd938x_dev_pm_ops,#endif.suppress_bind_attrs=true,},};staticconststructof_device_idwcd938x_dt_match[]={{.compatible="qcom,wcd938x-codec",.data="wcd938x"},{}};// 注册为平台设备驱动程序module_platform_driver(wcd938x_codec_driver);// 平台驱动对应的probe函数wcd938x_probe()wcd938x_bind()snd_soc_register_component(dev,&soc_codec_dev_wcd938x,wcd938x_dai,ARRAY_SIZE(wcd938x_dai));
    • 声卡注册为平台设备,声卡的驱动程序在匹配到对应的设备时,就会自动调用probe函数。后续的component的probe与dai的probe函数都是在声卡驱动程序中调用的。如下:

      staticconststructof_device_idkalama_asoc_machine_of_match[]={{.compatible="qcom,kalama-asoc-snd",.data="codec"},{.compatible="qcom,kalama-asoc-snd-stub",.data="stub_codec"},{},};staticstructplatform_driverkalama_asoc_machine_driver={.driver={.name=DRV_NAME,.owner=THIS_MODULE,.pm=&snd_soc_pm_ops,.of_match_table=kalama_asoc_machine_of_match,.suppress_bind_attrs=true,},.probe=msm_asoc_machine_probe,.remove=msm_asoc_machine_remove,};staticint__initmsm_asoc_machine_init(void){snd_card_sysfs_init();returnplatform_driver_register(&kalama_asoc_machine_driver);}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/18 2:52:36

MCEdit 2.0地图编辑器:3大核心功能解析与实战应用

MCEdit 2.0地图编辑器&#xff1a;3大核心功能解析与实战应用 【免费下载链接】mcedit2 MCEdit 2.0 - World Editor for Minecraft. 项目地址: https://gitcode.com/gh_mirrors/mc/mcedit2 MCEdit 2.0作为《我的世界》专业地图编辑工具&#xff0c;为玩家提供了前所未有…

作者头像 李华
网站建设 2026/1/18 3:16:04

如何快速掌握网页长截图:Chrome扩展的终极完整指南

如何快速掌握网页长截图&#xff1a;Chrome扩展的终极完整指南 【免费下载链接】full-page-screen-capture-chrome-extension One-click full page screen captures in Google Chrome 项目地址: https://gitcode.com/gh_mirrors/fu/full-page-screen-capture-chrome-extensio…

作者头像 李华
网站建设 2026/1/18 3:31:36

楼层设备系统架构标准:楼宇运维中ICT设施稳定性的底层支撑

楼层设备布局&#xff1a;物理空间与功能需求的协同设计楼层设备布局是楼宇ICT系统稳定运行的物理基础&#xff0c;需以‘功能分区明确、运维路径最短、资源占用最优’为核心原则。在实际规划中&#xff0c;弱电间的设置是关键环节——每3-5层设置一个弱电间&#xff0c;位置需…

作者头像 李华
网站建设 2026/1/17 20:57:40

Supabase替代Firebase:AI配置Auth与Storage模块

Supabase替代Firebase&#xff1a;AI配置Auth与Storage模块 在AI应用开发的实践中&#xff0c;越来越多研究者和开发者开始质疑一个看似“理所当然”的选择——使用Firebase作为默认后端。尤其当项目聚焦于轻量级推理模型、学术实验或低成本部署时&#xff0c;Firebase那套封闭…

作者头像 李华
网站建设 2026/1/17 6:47:34

【VSCode加载提速终极指南】:揭秘9大卡顿元凶及优化策略

第一章&#xff1a;VSCode加载性能问题的现状与影响Visual Studio Code&#xff08;简称 VSCode&#xff09;作为当前最受欢迎的代码编辑器之一&#xff0c;凭借其轻量、可扩展和跨平台特性赢得了广大开发者的青睐。然而&#xff0c;随着项目规模扩大和插件生态的不断丰富&…

作者头像 李华
网站建设 2026/1/16 21:31:54

5个Applite性能调优技巧:让你的macOS软件管理快如闪电

5个Applite性能调优技巧&#xff1a;让你的macOS软件管理快如闪电 【免费下载链接】Applite User-friendly GUI macOS application for Homebrew Casks 项目地址: https://gitcode.com/gh_mirrors/ap/Applite Applite作为macOS上优秀的Homebrew Casks图形化管理工具&…

作者头像 李华