audio驱动之cpu_dai
平台 | os版本 | 内核 |
---|---|---|
MT6765 | Android 9.0 | kernel-4.9 |
audio驱动相关结构体 | 注释 |
---|---|
snd_soc_component | 当底层驱动注册platform、codec+codec dai、cpu dai时, 核心层都会创建一个对应的snd_soc_component,并且会挂到component_list 链表中 |
snd_soc_component_driver | 底层驱动需要填充该结构体, 然后向ASoC核心层注册 |
|snd_soc_dai_driver | 这个结构体用来表示能够传输哪些格式的音频数据,并且提供了设置这些格式的函数|
cpu_dai
cpu_dai
驱动通常对应cpu的一个或者几个 I2S/PCM
接口。用来连接platform
和machine
。
不同的声卡有不同的cpu_dai
,但最终都是通过 snd_soc_register_component()
这个函数来注册cpu_dai
的。cpu_dai
的抽象出来的结构体是mtk_dai_stub_dai
。
下面我们以mtk_dai_stub_init
为入口函数分析一下某一个具体cpu_dai
的注册流程。
入口函数所在文件:
./kernel-4.9/sound/soc/mediatek/common_int/mtk-soc-dai-stub.c
module_init(mtk_dai_stub_init);static int __init mtk_dai_stub_init(void)
{pr_warn("%s:\n", __func__);
#ifndef CONFIG_OFint ret;/* #define MT_SOC_DAI_NAME "mt-soc-dai-driver" */soc_mtk_dai_dev = platform_device_alloc(MT_SOC_DAI_NAME, -1);if (!soc_mtk_dai_dev)return -ENOMEM;ret = platform_device_add(soc_mtk_dai_dev);if (ret != 0) {platform_device_put(soc_mtk_dai_dev);return ret;}
#endif/* 注册platform_driver */return platform_driver_register(&mtk_dai_stub_driver);
}
#ifdef CONFIG_OF
static const struct of_device_id mt_soc_dai_stub_of_ids[] = {{.compatible = "mediatek,mt_soc_dai_stub",},{} };
#endifstatic struct platform_driver mtk_dai_stub_driver = {.probe = mtk_dai_stub_dev_probe,.remove = mtk_dai_stub_dev_remove,.driver = {.name = MT_SOC_DAI_NAME,.owner = THIS_MODULE,
#ifdef CONFIG_OF.of_match_table = mt_soc_dai_stub_of_ids,
#endif},
};
of_match_table
与设备树匹配的节点是"mediatek,mt_soc_pcm_capture"
,它与文件./kernel-4.9/arch/arm/boot/dts/mt6765.dts
中
mt_soc_dai_name {compatible = "mediatek,mt_soc_dai_stub";
};
匹配成功才会调用到mtk_dai_stub_dev_probe
函数。
probe函数所在文件:
./kernel-4.9/sound/soc/mediatek/common_int/mtk-soc-dai-stub.c
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));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;
}
cpu dai
通过 snd_soc_register_component
进行注册,将数组 mtk_dai_stub_dai[]
传入,再通过 snd_soc_register_dais
将所有的 PCM(platform)
(如播放、录音、通话等)循环加入 dai list
里面。
cpu_dai注册函数所在文件:
./kernel-4.9/sound/soc/mediatek/common_int/mtk-soc-dai-stub.c
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;/* 为定义的snd_soc_component结构体申请一块内存 */cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);if (!cmpnt) {dev_err(dev, "ASoC: Failed to allocate memory\n");return -ENOMEM;}/* 将上面分配了内存的cmpnt初始化,并把cmpnt_drv和dev赋值给cmpnt */ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);if (ret)goto err_free;cmpnt->ignore_pmdown_time = true;cmpnt->registered_as_component = true;/****************************************************************** 函数的主要功能:* 1. 初始化snd_soc_component,并把cmpnt_drv和dev赋值到cmpnt;* 2. dai_drv(也就是mtk_dai_stub_dai) 执行for循环逐一取出snd_soc_dai_driver结构体;* 3. 创建新的snd_soc_dai并且添加到component->dai_list*****************************************************************/ret = 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;}/************************************************** 实际上是对snd_soc_component_add_unlocked()的封装,* 作用是将cmpnt添加到component_list; *************************************************/snd_soc_component_add(cmpnt);return 0;err_cleanup:snd_soc_component_cleanup(cmpnt);
err_free:kfree(cmpnt);return ret;
}
我们可以发现snd_soc_register_component
也调用到了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
中。
mtk_dai_stub_dai
数组元素被转换成一个个snd_soc_dai_driver
结构体,通过snd_soc_dai.list
全面添加到snd_soc_component.dai_list
,snd_soc_component.list
通过snd_soc_component_add
把自己添加到全局链表component_list
。
结构体所在文件:
./kernel-4.9/sound/soc/mediatek/common_int/mtk-soc-dai-stub.c
static struct snd_soc_dai_driver mtk_dai_stub_dai[] = {...... /* 省略部分相关代码 */{.capture = {/* #define MT_SOC_BTCVSD_CAPTURE_STREAM_NAME "BTCVSD_Capture" */.stream_name =MT_SOC_BTCVSD_CAPTURE_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_48000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 2,.rate_min = 8000,.rate_max = 48000,},.name = MT_SOC_BTCVSD_RX_DAI_NAME,.ops = &mtk_dai_stub_ops,},{.playback = {/* #define MT_SOC_BTCVSD_PLAYBACK_STREAM_NAME "BTCVSD_Playback" */.stream_name =MT_SOC_BTCVSD_PLAYBACK_STREAM_NAME, .rates = SNDRV_PCM_RATE_8000_48000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 2,.rate_min = 8000,.rate_max = 48000,},.name = MT_SOC_BTCVSD_TX_DAI_NAME,.ops = &mtk_dai_stub_ops,},{.playback = {.stream_name =MT_SOC_BTCVSD_PLAYBACK_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_48000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 2,.rate_min = 8000,.rate_max = 48000,},.capture = {.stream_name =MT_SOC_BTCVSD_CAPTURE_STREAM_NAME,.rates = SNDRV_PCM_RATE_8000_48000,.formats = SND_SOC_ADV_MT_FMTS,.channels_min = 1,.channels_max = 2,.rate_min = 8000,.rate_max = 48000,},.name = MT_SOC_BTCVSD_DAI_NAME,.ops = &mtk_dai_stub_ops,},...... /* 省略部分代码 */
}
mtk_dai_stub_dai
是一个 数组,每一个元素都是一个snd_soc_dai_driver
结构体,在这里对每一个元素都进行了赋值操作,由此可以看出cpu_dai
的驱动中包含这针对不同PCM的cpu_dai
。
上面填充的数组元素实际大部分是对snd_soc_dai_driver
中的snd_soc_pcm_stream
这个结构体的成员变量进行填充的。
结构体定义的所在文件:
./kernel-4.9/include/sound/soc.h
/* SoC PCM stream information */
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 */
};
static struct snd_soc_dai_ops mtk_dai_stub_ops = {.startup = multimedia_startup,
}
函数所在文件:
./kernel-4.9/sound/soc/soc-core.c
static int snd_soc_component_initialize(struct snd_soc_component *component,const struct snd_soc_component_driver *driver, struct device *dev)
{/* 定义了一个动态音频电源管理指针变量dapm */struct snd_soc_dapm_context *dapm;/* component->name由系统生成 */component->name = fmt_single_name(dev, &component->id);if (!component->name) {dev_err(dev, "ASoC: Failed to allocate name\n");return -ENOMEM;}component->dev = dev;component->driver = driver;component->probe = component->driver->probe;component->remove = component->driver->remove;dapm = &component->dapm;dapm->dev = dev;dapm->component = component;dapm->bias_level = SND_SOC_BIAS_OFF;dapm->idle_bias_off = true;if (driver->seq_notifier)dapm->seq_notifier = snd_soc_component_seq_notifier;if (driver->stream_event)dapm->stream_event = snd_soc_component_stream_event;component->controls = driver->controls;component->num_controls = driver->num_controls;component->dapm_widgets = driver->dapm_widgets;component->num_dapm_widgets = driver->num_dapm_widgets;component->dapm_routes = driver->dapm_routes;component->num_dapm_routes = driver->num_dapm_routes;INIT_LIST_HEAD(&component->dai_list);mutex_init(&component->io_mutex);return 0;
}
函数所在文件:
./kernel-4.9/sound/soc/soc-core.c
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_component */component->dai_drv = dai_drv;for (i = 0; i < count; i++) {dai = soc_add_dai(component, dai_drv + i,count == 1 && legacy_dai_naming);if (dai == NULL) {ret = -ENOMEM;goto err;}}return 0;err:snd_soc_unregister_dais(component);return ret;
}
函数所在文件:
./kernel-4.9/sound/soc/soc-core.c
/* Create a DAI and add it to the component's DAI list */
static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,struct snd_soc_dai_driver *dai_drv,bool legacy_dai_naming)
{struct device *dev = component->dev;struct snd_soc_dai *dai;dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev));dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);if (dai == NULL)return NULL;/** 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.*/if (legacy_dai_naming &&(dai_drv->id == 0 || dai_drv->name == NULL)) {dai->name = fmt_single_name(dev, &dai->id);} else {dai->name = fmt_multiple_name(dev, dai_drv);if (dai_drv->id)dai->id = dai_drv->id;elsedai->id = component->num_dai;}if (dai->name == NULL) {kfree(dai);return NULL;}/* 创建新的 snd_soc_dai 并且挂在 component->dai_list */dai->component = component;dai->dev = dev;dai->driver = dai_drv;/************************************ ops 由dai_drv赋值的,* cpu dai 是:mtk_dai_stub_dai * codec dai 是:mtk_6357_dai_codecs***********************************/if (!dai->driver->ops)dai->driver->ops = &null_dai_ops;list_add(&dai->list, &component->dai_list);component->num_dai++;dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);return dai;
}
函数所在文件:
./kernel-4.9/sound/soc/soc-core.c
static void snd_soc_component_add(struct snd_soc_component *component)
{mutex_lock(&client_mutex);/* 该函数在audio的platform驱动注册的时候同样被调用了 */snd_soc_component_add_unlocked(component);mutex_unlock(&client_mutex);
}
audio驱动之cpu_dai相关推荐
- audio驱动之codec和codec_dai
平台 os版本 内核 MT6765 Android 9.0 kernel-4.9 在嵌入式设备中,codec的作用可以简单的分为4种: 对PCM等信号进行D/A转换,把数字的隐僻信号转换为模拟信号. ...
- Waveform Audio 驱动(Wavedev2)之:WAV 驱动解析
Waveform Audio 驱动(Wavedev2)之:WAV 驱动解析 上篇文章中,我们模拟了WAV API.现在进入我们正在要解析的Wave 驱动的架构.我们了解一个驱动的时候,先不去看具体跟硬 ...
- Waveform Audio 驱动(Wavedev2)之:WAV API模拟
Waveform Audio 驱动(Wavedev2)之:WAV API模拟 Waveform 驱动对Windows Mobile来说是一个非常重要的驱动,控制着所有有关声音的操作,包括喇叭.耳机. ...
- MTK 驱动(60)---Audio驱动开发之音频链路
Audio驱动开发之音频链路 [元器件说明] 本文中使用的 Codec 芯片为 ALC5677. [音频链路模型] 一个常见的音频链路如 图1 所示,包含 音频输入.ADC.DSP.DAC.音频输出 ...
- win7(windows7旗舰版)声卡High Definition Audio驱动 (安装失败)解决方法
win7(windows7旗舰版)声卡High Definition Audio驱动 (安装失败)解决方案 前几天装了一下windows7体验一下,结果声卡驱动安装有问题,这电脑没声音我可没法活啊. ...
- 在MTK平台配置一个支持smartPA的audio驱动
文章目录 smartPA概述 smartPA AW87319概述 smartPA AW87319功能特性 在kernel中添加对smartPA的支持 1. 在配置文件中添加对smartPA的支持 2. ...
- Linux Audio驱动系列(技巧篇) - tingmix调试抓Log
By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎订阅! 你的喜欢就是我写作的动力! 目录 ...
- AUDIO驱动点检表
AUDIO驱动点检表 备注:以下都以MT6753,ANDROID 5.1版本为基础. 1.驱动配置部分 1.1 耳机部分 配置文件: accdet_custom_def.h 耳机MIC模式配置: 默认 ...
- SM8150 Audio驱动分析
1. TDM硬件接口介绍: I2S只能传2个声道的数据,PCM可以传多达16路数据,采用时分复用的方式,就是TDM. TDM不像I2S有统一的标准,不同的IC厂商在应用TDM时可能略有差异,这些差异表 ...
最新文章
- git命令合并分支代码
- 通过BeanShell获取UUID并将参数传递给Jmeter
- Pyqt5 获取命令行参数sys.argv
- oracle所有表相关查询
- 即日起更新机器学习相关博客
- 重磅!居全国前列!合肥获批建设3个国家战略性新兴产业集群!
- 苹果6屏幕多大_苹果12使用高通X55,10亿买下的英特尔基带何时能派上用场
- WEB安全基础-Javascrp相关知识点之BOM
- ASP.NET生成静态页面的简单实现
- Docker的思想来自于集装箱
- OLT操作命令集及排障
- Pascal基础教程
- 算数平均数与几何平均数
- [转帖]历史上真实的《勇敢的心》
- c++ min/max
- 怎么提高图片分辨率?如何改变图片的分辨率?
- 文本相似度 Text Similarity
- 跟我一起云计算(6)——openAPI
- C语言 模拟键盘、鼠标事例
- 融合算法性能评价指标