ALSA声卡驱动:

1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介

2.Linux ALSA声卡驱动之二:Platform

3. Linux ALSA声卡驱动之三:Platform之Cpu_dai

4. Linux ALSA声卡驱动之四:Codec 以及Codec_dai

5.Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

6.Linux ALSA声卡驱动之六:PCM的注册流程

7.Linux ALSA声卡驱动之七:录音(Capture) 调用流程

一、CPU_DAI驱动在ASoC中的作用

从上一章Linux ALSA声卡驱动之二:Platform我们知道platform负责管理音频数据,把音频数据通过dma或其他操作传送至cpu dai中,cpu_dai则主要完成cpu一侧的dai的参数配置。在查看cpu_dai的代码后,确实是作为参数配置来使用。其中最关键的代码片段是mtk_dai_stub_dai

static struct snd_soc_dai_driver mtk_dai_stub_dai[] = {{.playback = {.stream_name = MT_SOC_DL1_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_192000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 2,.rate_min = 8000,.rate_max = 192000,},.name = MT_SOC_DL1DAI_NAME,.ops = &mtk_dai_stub_ops,},{.capture = {.stream_name = MT_SOC_UL1_STREAM_NAME,.rates = SOC_HIGH_USE_RATE,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 4,.rate_min = 8000,.rate_max = 260000,},.name = MT_SOC_UL1DAI_NAME,},{.capture = {.stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_48000,.formats = SNDRV_PCM_FMTBIT_S16_LE,.channels_min = 2,.channels_max = 2,.rate_min = 8000,.rate_max = 48000,},.name = MT_SOC_TDMRX_NAME,.ops = &mtk_dai_stub_ops,},
#ifdef CONFIG_MTK_HDMI_TDM{.playback = {.stream_name = MT_SOC_HDMI_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_192000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 8,.rate_min = 8000,.rate_max = 192000,},.capture = {.stream_name = MT_SOC_HDMI_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_192000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 8,.rate_min = 8000,.rate_max = 192000,},.name = MT_SOC_HDMI_NAME,.ops = &mtk_dai_stub_ops,},
.............

这段代码片段比较简单,就是snd_soc_dai_driver结构体数组的赋值。

对此你会对此产生疑问?这样赋值的作用是什么?

    对于这个问题我们后面会回答。

二、CPU_DAI驱动的注册 snd_soc_register_component

  • snd_soc_register_component函数执行的时序图

上一章节(Linux ALSA声卡驱动之二:Platform)我们已经分析了snd_soc_register_platform的时序图,就不再关注,所以我们直接看 mtk-soc-dai-stub.c调用snd_soc_register_component的时序图。在 snd_soc_register_component函数时序图的调用中我们发现snd_soc_component_add_unlocked函数又被调用。这个函数在snd_soc_register_platform的时序图也有被调用,当时分析snd_soc_component_add_unlocked函数的主要作用是:把platform->component添加到component_list  ,platform->component是snd_soc_component结构体,component_list是一个链表。所以不难看出snd_soc_register_component的作用就是把mtk_dai_stub_dai相关配置赋值给snd_soc_component,snd_soc_component添加到component_list链表,添加的方式跟上章说的Platform一样。

  • snd_soc_register_component函数相关的结构体

snd_soc_dai结构体有两个比较关键的成员:struct snd_soc_dai_driver *driver和struct snd_soc_component *component。driver就是之前的mtk_dai_stub_dai  ,component是通过snd_soc_dai的一系列的赋值而得到的。

snd_soc_dai与snd_soc_component的关系是:snd_soc_dai 中的*component 指针指向snd_soc_component结构体,snd_soc_component.dai_list 链表挂载着许多个snd_soc_dai变量。

snd_soc_component通过list_add(&component->list, &component_list)  添加到全局变量component_list链表中。

2.2  snd_soc_register_component函数详细执行过程

static int mtk_dai_stub_dev_probe(struct platform_device *pdev)
{int rc = 0;pr_warn("mtk_dai_stub_dev_probe  name %s\n", dev_name(&pdev->dev));pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);if (pdev->dev.dma_mask == NULL)pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;if (pdev->dev.of_node)dev_set_name(&pdev->dev, "%s", MT_SOC_DAI_NAME);pr_warn("%s: dev name %s\n", __func__, dev_name(&pdev->dev));//上面都是一些结构体成员的赋值,不关键,snd_soc_register_component才是关键函数。//mtk_dai_stub_dai 是snd_soc_dai_driver结构体变量rc = snd_soc_register_component(&pdev->dev, &mt_dai_component,mtk_dai_stub_dai, ARRAY_SIZE(mtk_dai_stub_dai));pr_warn("%s: rc  = %d\n", __func__, rc);return rc;
}
struct snd_soc_dai_driver {/* DAI description */const char *name;unsigned int id;unsigned int base;/* DAI driver callbacks */int (*probe)(struct snd_soc_dai *dai);int (*remove)(struct snd_soc_dai *dai);int (*suspend)(struct snd_soc_dai *dai);int (*resume)(struct snd_soc_dai *dai);/* compress dai */int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);/* DAI is also used for the control bus */bool bus_control;/* ops */const struct snd_soc_dai_ops *ops;//一个struct snd_soc_dai_ops类型的结构, 该dai的操作函数集/* DAI capabilities */struct snd_soc_pcm_stream capture;//录音参数struct snd_soc_pcm_stream playback;播放参数unsigned int symmetric_rates:1;unsigned int symmetric_channels:1;unsigned int symmetric_samplebits:1;/* probe ordering - for components with runtime dependencies */int probe_order;int remove_order;
};
struct snd_soc_pcm_stream {const char *stream_name;u64 formats;  //这个格式定义在pcm.h SNDRV_PCM_FMTBITxxxx     /* SNDRV_PCM_FMTBIT_* */unsigned int rates;  //采样率 我们常说16k 48k  /* SNDRV_PCM_RATE_* */unsigned int rate_min;    //最小采样率 /* min rate */unsigned int rate_max;    //最大采样率 /* max rate */unsigned int channels_min;    //最小通道数,单声道1/* min channels */unsigned int channels_max; //最大通道数  立体声道2/* max channels */unsigned int sig_bits;  //位数 8  还是16    /* number of bits of content */
};
  • snd_soc_register_component   :dai_drv 数组转换成一个个snd_soc_dai结构体,通过snd_soc_dai.list 全面添加到snd_soc_component.dai_list,snd_soc_component.list 通过snd_soc_component_add把自己添加到全局链表component_list

int snd_soc_register_component(struct device *dev,const struct snd_soc_component_driver *cmpnt_drv,struct snd_soc_dai_driver *dai_drv,int num_dai)
{struct snd_soc_component *cmpnt;int ret;cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);if (!cmpnt) {dev_err(dev, "ASoC: Failed to allocate memory\n");return -ENOMEM;}ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);//初始化cmpnt,并把cmpnt_drv和dev赋值到cmpntif (ret)goto err_free;cmpnt->ignore_pmdown_time = true;cmpnt->registered_as_component = true;//1.初始化snd_soc_component,并赋值,//2.dai_drv(也就是mtk_dai_stub_dai) 执行for循环逐一取出snd_soc_dai_driver结构体//创建新的snd_soc_dai 并且添加到component->dai_listret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);if (ret < 0) {dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret);goto err_cleanup;}//cmpnt添加到component_list;snd_soc_component_add(cmpnt);return 0;err_cleanup:snd_soc_component_cleanup(cmpnt);
err_free:kfree(cmpnt);return ret;
}
static int snd_soc_register_dais(struct snd_soc_component *component,struct snd_soc_dai_driver *dai_drv, size_t count,bool legacy_dai_naming)
{struct device *dev = component->dev;struct snd_soc_dai *dai;unsigned int i;int ret;dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);//snd_soc_dai_driver 添加到snd_soc_componentcomponent->dai_drv = dai_drv;component->num_dai = count;for (i = 0; i < count; i++) {dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);if (dai == NULL) {ret = -ENOMEM;goto err;}/** Back in the old days when we still had component-less DAIs,* instead of having a static name, component-less DAIs would* inherit the name of the parent device so it is possible to* register multiple instances of the DAI. We still need to keep* the same naming style even though those DAIs are not* component-less anymore.*///snd_soc_dai_driver name 赋值到snd_soc_dai name 并且赋值 idif (count == 1 && legacy_dai_naming &&(dai_drv[i].id == 0 || dai_drv[i].name == NULL)) {dai->name = fmt_single_name(dev, &dai->id);} else {dai->name = fmt_multiple_name(dev, &dai_drv[i]);if (dai_drv[i].id)dai->id = dai_drv[i].id;elsedai->id = i;}if (dai->name == NULL) {kfree(dai);ret = -ENOMEM;goto err;}// 创建新的snd_soc_dai 并且挂在component->dai_listdai->component = component;dai->dev = dev;dai->driver = &dai_drv[i];//ops 由dai_drv赋值的,  可能是cpu dai 就是:mtk_dai_stub_dai ,codec dai:mtk_6357_dai_codecsif (!dai->driver->ops)dai->driver->ops = &null_dai_ops;list_add(&dai->list, &component->dai_list);dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);}return 0;err:snd_soc_unregister_dais(component);return ret;
}
  • snd_soc_component结构体

//当底层驱动注册platform、codec+codec dai、cpu dai时, 核心层都会创建一个对应的snd_soc_component,并且会挂到component_list 链表中
struct snd_soc_component {const char *name;  //这个跟device_driver->name 和snd_soc_component_driver->id有关,int id;const char *name_prefix;struct device *dev;struct snd_soc_card *card;unsigned int active;unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */unsigned int registered_as_component:1;//在snd_soc_register_component时修改为1struct list_head list;//用于把自己挂载到全局链表component_list下 ,component_list 实在soc-core 中保持的全局变量struct snd_soc_dai_driver *dai_drv;//dai  有可能是cpu dai 也有可能是 codec dai  是一个数组int num_dai;//dai 的数量const struct snd_soc_component_driver *driver;//指向下属的snd_soc_component_driver, 该结构体一般由底层平台驱动实现struct list_head dai_list;//链表头, 挂接snd_soc_dai->list   list_add(&dai->list, &component->dai_list)int (*read)(struct snd_soc_component *, unsigned int, unsigned int *);int (*write)(struct snd_soc_component *, unsigned int, unsigned int);struct regmap *regmap;int val_bytes;struct mutex io_mutex;/* attached dynamic objects */struct list_head dobj_list;#ifdef CONFIG_DEBUG_FSstruct dentry *debugfs_root;
#endif/** DO NOT use any of the fields below in drivers, they are temporary and* are going to be removed again soon. If you use them in driver code the* driver will be marked as BROKEN when these fields are removed.*//* Don't use these, use snd_soc_component_get_dapm() */struct snd_soc_dapm_context dapm;const struct snd_kcontrol_new *controls;unsigned int num_controls;const struct snd_soc_dapm_widget *dapm_widgets;unsigned int num_dapm_widgets;const struct snd_soc_dapm_route *dapm_routes;unsigned int num_dapm_routes;struct snd_soc_codec *codec;int (*probe)(struct snd_soc_component *);void (*remove)(struct snd_soc_component *);#ifdef CONFIG_DEBUG_FSvoid (*init_debugfs)(struct snd_soc_component *component);const char *debugfs_prefix;
#endif
};
  • snd_soc_component_driver

//底层驱动需要填充该结构体, 然后向ASoC核心层注册.
struct snd_soc_component_driver {
//ASoC核心层用名字区分不同的snd_soc_component_driver.
//注意这个name与snd_soc_component->name不是同一个. 这里的name由驱动编写者填入, 而component->name由系统自动生成.const char *name;/* Default control and setup, added after probe() is run */const struct snd_kcontrol_new *controls;unsigned int num_controls;//dapm_widgets、dapm_routes : 与dapm相关, dapm其实是对kcontrol做了一层封装const struct snd_soc_dapm_widget *dapm_widgets;unsigned int num_dapm_widgets;const struct snd_soc_dapm_route *dapm_routes;unsigned int num_dapm_routes;int (*probe)(struct snd_soc_component *);void (*remove)(struct snd_soc_component *);/* DT */int (*of_xlate_dai_name)(struct snd_soc_component *component,struct of_phandle_args *args,const char **dai_name);void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,int subseq);int (*stream_event)(struct snd_soc_component *, int event);/* probe ordering - for components with runtime dependencies */int probe_order;int remove_order;
};
  • snd_soc_dai

//由核心层内部创建和维护, 用于代表一个dai. 可以是cpu_dai或codec_dai.
struct snd_soc_dai {const char *name;//该dai的名称, ASoC核心层靠name来区分不同的dai.int id;//应该是核心层自动分配的一个id号struct device *dev;/* driver ops */struct snd_soc_dai_driver *driver;//指向下属的snd_soc_dai_driver, 该结构体一般由底层驱动实现/* DAI runtime info */unsigned int capture_active:1;        /* stream is in use */unsigned int playback_active:1;       /* stream is in use */unsigned int symmetric_rates:1;unsigned int symmetric_channels:1;unsigned int symmetric_samplebits:1;unsigned int active;unsigned char probed:1;struct snd_soc_dapm_widget *playback_widget;struct snd_soc_dapm_widget *capture_widget;/* DAI DMA data */void *playback_dma_data;void *capture_dma_data;/* Symmetry data - only valid if symmetry is being enforced */unsigned int rate;unsigned int channels;unsigned int sample_bits;/* parent platform/codec */struct snd_soc_codec *codec;struct snd_soc_component *component;/* CODEC TDM slot masks and params (for fixup) */unsigned int tx_mask;unsigned int rx_mask;struct list_head list;//用于把自己挂载到component->dai_list下.   list_add(&dai->list, &component->dai_list)
};

总结:

对此你会对此产生疑问?这样赋值的作用是什么?  

  整个cpu_dai的执行流程我们已经梳理了一遍,对于这个问题也就不难回答。 取值mtk_dai_stub_dai 数组,负值给dai(snd_soc_dai 结构体变量),dai->list 添加到component->dai_list链表中。最后再把component->dai_list添加到component_list全局链表中,以供soc_bind_dai_link绑定cpu_dai 、codec_dai 、codec、platform使用。

Linux ALSA声卡驱动之三:Platform之Cpu_dai相关推荐

  1. [转] Linux ALSA声卡驱动之三:component、dai、codec以及platform之间的关系

    版权声明:本文为CSDN博主「MOON20704」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/moonli ...

  2. Linux ALSA声卡驱动之三:PCM设备的创建

    1. PCM是什么 PCM 是英文Pulse-code modulation的缩写,中文译名是脉冲编码调制.我们知道在现实生活中,人耳听到的声音是模拟信号,PCM就是要把声音从模拟转换成数字信号的一种 ...

  3. linux 声卡设备文件夹,Linux ALSA声卡驱动之三:PCM设备的创建

    4. 设备文件节点的建立(dev/snd/pcmCxxDxxp.pcmCxxDxxc)本文引用地址:http://www.eepw.com.cn/article/201612/341593.htm 4 ...

  4. Linux ALSA声卡驱动之二:Platform

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  5. Linux ALSA声卡驱动之四:Codec 以及Codec_dai

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  6. Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  7. Linux ALSA声卡驱动之七:录音(Capture) 调用流程

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  8. Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  9. Linux ALSA声卡驱动之八:ASoC架构中的Platform

    1.  Platform驱动在ASoC中的作用 前面几章内容已经说过,ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过C ...

最新文章

  1. mysql 资深dba_MySQL数据库专家分享资深DBA经验
  2. springboot静态网页放哪里_STEP4.1 第一个SpringBoot项目
  3. leetcode - 4Sum
  4. Matplotlib实例教程 | 配色表 colors
  5. svn教程----权限分配之示例一:开发人员拥有读写权限
  6. linux 内核中断与时钟的冲突 问题 del_timer,Linux内核开发之中断与时钟(三)
  7. my current journal index is in q3
  8. 飞鸽传书:HTML界面也有它欠缺的方面
  9. 诗与远方:无题(三十二)- 曾经写给妹子的一首诗
  10. C语言输出任意大小矩阵,【c语言】输出以下4*5的矩阵
  11. linux ip命令dhcp,嵌入式linux通过DHCP自动获取IP地址实现获取
  12. linux6.5关闭21端口,linux CentOS6.5 防火墙(关闭除提供系统服务以外的端口)
  13. Markdown 文档生成 PDF
  14. 迅雷有linux版本吗,迅雷 - Linux Wiki
  15. 使用Resource Hacker+W32Dasm+OD移除警告窗口
  16. SpringBoot微服务项目打包流程
  17. 一个最最简单的画图软件
  18. 觅知blibli专业版弹幕播放器开源无加密JSON解析版-后台功能一键管理-开源版22-8-24
  19. 2022.10.3-10.8 AI行业周刊(第118期):AI训练营
  20. win7没有语音识别怎么办|win7系统语音识别设置方法

热门文章

  1. 中国数字化手术室行业发展动态及前景战略深度研究报告2021年版
  2. JavaWeb.09.新闻之分页功能
  3. 3dmax:3dmax三维VR渲染设置之高级灯光渲染(经典案例—利用目标灯光制作光域网筒灯效果)图文教程
  4. 物联网智能停车解决方案
  5. Python数据可视化——词云图
  6. GPU虚拟化-Intel的KVMGT、NVIDIA的vGPU、AMD的MaxGPU
  7. KEGG enrichment富集分析我至今搞不懂原因的问题
  8. 【excel数据转脚本批量插入数据库】将excel中的数据快速生成insert脚本
  9. 学习笔记-工程图学基础(三)
  10. 美不可言的CSS 三