一 提纲
二 概述
三 代码分析
一 提纲
codec 分步骤综述
1 定义并且注册 平台驱动 (设备树匹配)
struct platform_driver mtk_codec_6331_driver {}
2 定义 struct snd_soc_codec_driver soc_mtk_codec{}
3 定义 struct snd_soc_dai_driver mtk_6331_dai_codecs{}
4 定义 struct snd_soc_dai_ops mt6323_aif1_dai_ops{}
5 平台驱动的 prob()中
5.1 设置平台设备设备名 platform_device -> device 名称 "mt-soc-codec"
5.2 将 snd_soc_codec_drive 和 snd_soc_dai_driver 注册到 soc-core。
: snd_soc_register_codec(&pdev->dev, &soc_mtk_codec, mtk_6331_dai_codecs, ARRAY_SIZE(mtk_6331_dai_codecs));
5.2
snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai)完成如下工作
1 创建 snd_soc_codec
2 初始化 snd_soc_codec -> snd_soc_componen
2 初始化 snd_soc_codec -> snd_soc_dapm_context
3 创建一个 头为 snd_soc_codec -> snd_soc_component -> dai_list 的双向链表
4 绑定 snd_soc_codec -> device 与 platform_device->device
5 绑定 snd_soc_codec -> snd_soc_codec_drive 与 snd_soc_codec_driver soc_mtk_codec{}
6 snd_soc_register_dais(&codec->component, dai_drv, num_dai, false)
6.1 创建 snd_soc_dai
6.2 snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver = snd_soc_dai_driver mtk_6331_dai_codecs{}
6.3 绑定
struct snd_soc_dai -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dai -> device = snd_soc_codec -> snd_soc_component -> device = platform_device->device
struct snd_soc_dai -> snd_soc_dai_driver = snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver = snd_soc_dai_driver mtk_6331_dai_codecs{}
6.3 添加 struct snd_soc_dai -> list_head 到 snd_soc_codec -> snd_soc_component -> list_head dai_list 双向链表
7 遍历以 &snd_soc_codec->snd_soc_component.list_head 为头节点,以 snd_soc_dai 为节点的双向链表。
初始化链表中节点的 snd_soc_dai -> snd_soc_codec = snd_soc_codec
8 将 snd_soc_codec -> list_head 添加到 codec_list 双向链表
二 概述
总的来讲,主要的工作是
首先定义
struct snd_soc_codec_driver soc_mtk_codec{} 和 struct snd_soc_dai_driver mtk_6331_dai_codecs{} 结构体。
并且将它俩注册到 soc-core。具体的表现形式就是 :
创建一个 snd_soc_codec 实例 并且将前面定义的 struct snd_soc_codec_driver soc_mtk_codec{} 结构体绑定到 snd_soc_codec 实例中对应项 snd_soc_codec -> snd_soc_codec_drive
创建一个 snd_soc_dai 实例 并且将将前面定义的 struct snd_soc_dai_driver mtk_6331_dai_codecs{} 结构体绑定到 snd_soc_dai 实例中对应项 snd_soc_dai -> snd_soc_dai_drive、
添加 snd_soc_dai 实例 到 以 snd_soc_dai 为节点的双向链表
添加 snd_soc_codec 实例 到 以 snd_soc_codec 为节点的双向链表
代码分析
mt3561.dtsi
mt_soc_codec_name {
compatible = "mediatek,mt_soc_codec_63xx";
};
sound/soc/mediatek/mt_soc_audio_6755/mt_soc_codec_63xx.c
static const struct snd_soc_dai_ops mt6323_aif1_dai_ops = {
.startup = mt63xx_codec_startup,
.prepare = mt63xx_codec_prepare,
.trigger = mt6323_codec_trigger,
};
static struct snd_soc_dai_driver mtk_6331_dai_codecs[] = {
{
.name = MT_SOC_CODEC_TXDAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_DL1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_RXDAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.capture = {
.stream_name = MT_SOC_UL1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
......
};
static const struct snd_soc_dapm_widget mt6331_dapm_widgets[] = {
/* Outputs */
SND_SOC_DAPM_OUTPUT("EARPIECE"),
SND_SOC_DAPM_OUTPUT("HEADSET"),
SND_SOC_DAPM_OUTPUT("SPEAKER"),
};
static const struct snd_soc_dapm_route mtk_audio_map[] = {
{"VOICE_Mux_E", "Voice Mux", "SPEAKER PGA"},
};
/*
定义 struct snd_soc_codec_driver soc_mtk_codec
.prob
.remove
.read
.write
.dapm_widgets
.num_dapm_widgets
.dapm_routes
.num_dapm_routes
*/
static struct snd_soc_codec_driver soc_mtk_codec = {
.probe = mt6331_codec_probe,
.remove = mt6331_codec_remove,
.read = mt6331_read,
.write = mt6331_write,
.dapm_widgets = mt6331_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt6331_dapm_widgets),
.dapm_routes = mtk_audio_map,
.num_dapm_routes = ARRAY_SIZE(mtk_audio_map),
};
static int mtk_mt6331_codec_dev_probe(struct platform_device *pdev)
{
/*coherent_dma_mask 表示它能寻址的物理地址的范围*/
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_CODEC_NAME);//#define MT_SOC_CODEC_NAME "mt-soc-codec"
pr_warn("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
/* 将 codec_driver 和 codec_dai_driver 注册到 soc-core。
&pdev->dev : platform_device->device
&soc_mtk_codec : struct snd_soc_codec_driver soc_mtk_codec,codec设备驱动
mtk_6331_dai_codecs : struct snd_soc_dai_driver mtk_6323_dai_codecs[],dai driver
ARRAY_SIZE(mtk_6323_dai_codecs)
*/
return snd_soc_register_codec(&pdev->dev, &soc_mtk_codec, mtk_6331_dai_codecs, ARRAY_SIZE(mtk_6331_dai_codecs));
}
#ifdef CONFIG_OF
/*
设备树匹配
*/
static const struct of_device_id mt_soc_codec_63xx_of_ids[] = {
{.compatible = "mediatek,mt_soc_codec_63xx",},
{}
};
#endif
/*定义平台驱动
*/
static struct platform_driver mtk_codec_6331_driver = {
.driver = {
.name = MT_SOC_CODEC_NAME,//#define MT_SOC_CODEC_NAME "mt-soc-codec"
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = mt_soc_codec_63xx_of_ids,
#endif
},
.probe = mtk_mt6331_codec_dev_probe,
.remove = mtk_mt6331_codec_dev_remove,
};
#ifndef CONFIG_OF
static struct platform_device *soc_mtk_codec6331_dev;
#endif
static int __init mtk_mt6331_codec_init(void)
{
pr_warn("%s:\n", __func__);
#ifndef CONFIG_OF
int ret = 0;
/* 分配平台设备
#define MT_SOC_CODEC_NAME "mt-soc-codec"
*/
soc_mtk_codec6331_dev = platform_device_alloc(MT_SOC_CODEC_NAME, -1);
if (!soc_mtk_codec6331_dev)
return -ENOMEM;
/*添加设备*/
ret = platform_device_add(soc_mtk_codec6331_dev);
if (ret != 0) {
platform_device_put(soc_mtk_codec6331_dev);
return ret;
}
#endif
InitGlobalVarDefault();
/*注册平台驱动*/
return platform_driver_register(&mtk_codec_6331_driver);
}
module_init(mtk_mt6331_codec_init);
static void __exit mtk_mt6331_codec_exit(void)
{
pr_warn("%s:\n", __func__);
platform_driver_unregister(&mtk_codec_6331_driver);
}
module_exit(mtk_mt6331_codec_exit);
/* Module information */
MODULE_DESCRIPTION("MTK codec driver");
MODULE_LICENSE("GPL v2");
---------------------------------------------------------------------------------
soc-core.c
static DEFINE_MUTEX(client_mutex);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
static LIST_HEAD(component_list);
static const struct snd_soc_dai_ops null_dai_ops = {
};
/* 注册 snd_soc_dai_driver
snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver = snd_soc_dai_driver
snd_soc_codec -> snd_soc_component -> num_dai = ARRAY_SIZE(mtk_6323_dai_codecs)
为每一个 snd_soc_dai 分配空间
设置 struct snd_soc_dai -> name
struct snd_soc_dai -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dai -> device = snd_soc_codec -> snd_soc_component -> device
struct snd_soc_dai -> snd_soc_dai_driver = snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver
struct snd_soc_dai -> snd_soc_dai_driver -> snd_soc_dai_ops = 空
添加 struct snd_soc_dai -> list_head 插入到 snd_soc_codec -> snd_soc_component -> list_head dai_list 双向链表
*/
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 snd_soc_dai *dai
定义 struct device *dev,并用 snd_soc_codec -> snd_soc_component -> device 初始化
*/
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_codec -> snd_soc_component -> snd_soc_dai_driver = snd_soc_dai_driver
snd_soc_codec -> snd_soc_component -> num_dai = ARRAY_SIZE(mtk_6323_dai_codecs)
*/
component->dai_drv = dai_drv;
component->num_dai = count;
/* 为每一个 snd_soc_dai 分配空间
设置 struct snd_soc_dai -> name
struct snd_soc_dai -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dai -> device = snd_soc_codec -> snd_soc_component -> device
struct snd_soc_dai -> snd_soc_dai_driver = snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver
struct snd_soc_dai -> snd_soc_dai_driver -> snd_soc_dai_ops = 空
添加 struct snd_soc_dai -> list_head 插入到 snd_soc_codec -> snd_soc_component -> list_head dai_list 双向链表
*/
for (i = 0; i < count; i++) {
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
if (dai == NULL) {
ret = -ENOMEM;
goto err;
}
if (count == 1 && legacy_dai_naming) {
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;
else
dai->id = i;
}
if (dai->name == NULL) {
kfree(dai);
ret = -ENOMEM;
goto err;
}
dai->component = component;
dai->dev = dev;
dai->driver = &dai_drv[i];
if (!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_codec -> snd_soc_component -> name
snd_soc_codec -> snd_soc_component -> device = platform_device->device
snd_soc_codec -> snd_soc_component -> snd_soc_component_driver = snd_soc_codec_driver -> snd_soc_component_driver
snd_soc_codec -> snd_soc_component -> probe = snd_soc_codec-> snd_soc_component -> snd_soc_component_driver -> prob
snd_soc_codec -> snd_soc_component -> remove = snd_soc_codec-> snd_soc_component -> snd_soc_component_driver -> remove
snd_soc_codec -> snd_soc_component -> struct snd_soc_dapm_context *dapm_ptr = snd_soc_codec-> snd_soc_component -> struct snd_soc_dapm_context dapm
struct snd_soc_dapm_context = snd_soc_codec -> snd_soc_component -> struct snd_soc_dapm_context *dapm_ptr
struct snd_soc_dapm_context -> device = platform_device->device
struct snd_soc_dapm_context -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dapm_context -> bias_level = SND_SOC_BIAS_OFF;//关闭电源状态
struct snd_soc_dapm_context -> idle_bias_off = true;//低功耗待机状态
snd_soc_codec -> snd_soc_component -> snd_kcontrol_new = snd_soc_codec_driver -> snd_soc_component_driver -> snd_kcontrol_new
snd_soc_codec -> snd_soc_component -> num_controls = snd_soc_codec_driver -> snd_soc_component_driver -> num_controls
snd_soc_codec -> snd_soc_component -> dapm_widgets = snd_soc_codec_driver -> snd_soc_component_driver -> dapm_widgets
snd_soc_codec -> snd_soc_component -> num_dapm_widgets = snd_soc_codec_driver -> snd_soc_component_driver -> num_dapm_widgets
snd_soc_codec -> snd_soc_component -> dapm_routes = snd_soc_codec_driver -> snd_soc_component_driver -> dapm_routes
snd_soc_codec -> snd_soc_component -> num_dapm_routes = snd_soc_codec_driver -> snd_soc_component_driver -> num_dapm_routes
创建一个 头为 snd_soc_codec -> snd_soc_component -> dai_list 的双向链表
*/
static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev)
{
/*定义 struct snd_soc_dapm_context 动态音频电源管理 */
struct snd_soc_dapm_context *dapm;
/*
获取 snd_soc_codec -> snd_soc_component -> name
snd_soc_codec -> snd_soc_component -> device = platform_device->device
snd_soc_codec -> snd_soc_component -> snd_soc_component_driver = snd_soc_codec_driver -> snd_soc_component_driver
snd_soc_codec -> snd_soc_component -> probe = snd_soc_codec-> snd_soc_component -> snd_soc_component_driver -> prob
snd_soc_codec -> snd_soc_component -> remove = snd_soc_codec-> snd_soc_component -> snd_soc_component_driver -> remove
*/
component->name = fmt_single_name(dev, &component->id);
component->dev = dev;
component->driver = driver;
component->probe = component->driver->probe;
component->remove = component->driver->remove;
/*
snd_soc_codec -> snd_soc_component -> struct snd_soc_dapm_context *dapm_ptr = snd_soc_codec-> snd_soc_component -> struct snd_soc_dapm_context dapm
*/
if (!component->dapm_ptr)
component->dapm_ptr = &component->dapm;
/*
struct snd_soc_dapm_context = snd_soc_codec -> snd_soc_component -> struct snd_soc_dapm_context *dapm_ptr
struct snd_soc_dapm_context -> device = platform_device->device
struct snd_soc_dapm_context -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dapm_context -> bias_level = SND_SOC_BIAS_OFF;//关闭电源状态
struct snd_soc_dapm_context -> idle_bias_off = true;//低功耗待机状态
*/
dapm = component->dapm_ptr;
dapm->dev = dev;
dapm->component = component;
dapm->bias_level = SND_SOC_BIAS_OFF;//关闭电源状态
dapm->idle_bias_off = true;//低功耗待机状态
/*
如果 snd_soc_codec_driver-> seq_notifier() 存在
struct snd_soc_dapm_context -> seq_notifier() = snd_soc_component_seq_notifier()
如果 snd_soc_codec_driver -> seq_notifier() 存在
struct snd_soc_dapm_context -> seq_notifier() = snd_soc_component_stream_event()
*/
if (driver->seq_notifier)
dapm->seq_notifier = snd_soc_component_seq_notifier;
if (driver->stream_event)
dapm->stream_event = snd_soc_component_stream_event;
/*
snd_soc_codec -> snd_soc_component -> snd_kcontrol_new = snd_soc_codec_driver -> snd_soc_component_driver -> snd_kcontrol_new
snd_soc_codec -> snd_soc_component -> num_controls = snd_soc_codec_driver -> snd_soc_component_driver -> num_controls
snd_soc_codec -> snd_soc_component -> dapm_widgets = snd_soc_codec_driver -> snd_soc_component_driver -> dapm_widgets
snd_soc_codec -> snd_soc_component -> num_dapm_widgets = snd_soc_codec_driver -> snd_soc_component_driver -> num_dapm_widgets
snd_soc_codec -> snd_soc_component -> dapm_routes = snd_soc_codec_driver -> snd_soc_component_driver -> dapm_routes
snd_soc_codec -> snd_soc_component -> num_dapm_routes = snd_soc_codec_driver -> snd_soc_component_driver -> num_dapm_routes
*/
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;
/*创建一个 头为 snd_soc_codec -> snd_soc_component -> dai_list 的双向链表*/
INIT_LIST_HEAD(&component->dai_list);
mutex_init(&component->io_mutex);
return 0;
}
/*
sound/soc/mediatek/mt_soc_audio_6755/mt_soc_codec_63xx.c
mtk_mt6331_codec_dev_probe
snd_soc_register_codec(&pdev->dev, &soc_mtk_codec, mtk_6331_dai_codecs, ARRAY_SIZE(mtk_6331_dai_codecs));//将 codec_driver 和 codec_dai_driver 注册到 soc-core。
参数:
struct device *dev ---- platform_device->device
const struct snd_soc_codec_driver *codec_drv ---- struct snd_soc_codec_driver soc_mtk_codec,codec设备驱动
struct snd_soc_dai_driver *dai_drv ---- struct snd_soc_dai_driver mtk_6323_dai_codecs[]
int num_dai ---- ARRAY_SIZE(mtk_6331_dai_codecs)
说明
该函数内部的 所有
struct snd_soc_dai_driver *dai_drv 就是 snd_soc_dai_driver mtk_6323_dai_codecs
struct snd_soc_codec_driver *codec_drv 就是 struct snd_soc_codec_driver soc_mtk_codec
1
定义 struct snd_soc_codec
定义 snd_soc_dai
分配struct snd_soc_codec 空间
2 ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev);
2.1 初始化 snd_soc_codec -> snd_soc_component的成员
struct snd_soc_codec -> snd_soc_component -> snd_soc_codec = struct snd_soc_codec
struct snd_soc_codec -> snd_soc_component -> snd_soc_dapm_context = struct snd_soc_codec -> snd_soc_dapm_context
获取 snd_soc_codec -> snd_soc_component -> name
snd_soc_codec -> snd_soc_component -> device = platform_device->device
snd_soc_codec -> snd_soc_component -> snd_soc_component_driver = snd_soc_codec_driver -> snd_soc_component_driver
snd_soc_codec -> snd_soc_component -> probe = snd_soc_codec-> snd_soc_component -> snd_soc_component_driver -> prob
snd_soc_codec -> snd_soc_component -> remove = snd_soc_codec-> snd_soc_component -> snd_soc_component_driver -> remove
snd_soc_codec -> snd_soc_component -> struct snd_soc_dapm_context *dapm_ptr = snd_soc_codec-> snd_soc_component -> struct snd_soc_dapm_context dapm
snd_soc_codec -> snd_soc_component -> snd_kcontrol_new = snd_soc_codec_driver -> snd_soc_component_driver -> snd_kcontrol_new
snd_soc_codec -> snd_soc_component -> num_controls = snd_soc_codec_driver -> snd_soc_component_driver -> num_controls
snd_soc_codec -> snd_soc_component -> dapm_widgets = snd_soc_codec_driver -> snd_soc_component_driver -> dapm_widgets
snd_soc_codec -> snd_soc_component -> num_dapm_widgets = snd_soc_codec_driver -> snd_soc_component_driver -> num_dapm_widgets
snd_soc_codec -> snd_soc_component -> dapm_routes = snd_soc_codec_driver -> snd_soc_component_driver -> dapm_routes
snd_soc_codec -> snd_soc_component -> num_dapm_routes = snd_soc_codec_driver -> snd_soc_component_driver -> num_dapm_routes
2.2 定义struct snd_soc_dapm_context 并初始化部分成员
struct snd_soc_dapm_context = snd_soc_codec -> snd_soc_component -> struct snd_soc_dapm_context *dapm_ptr
struct snd_soc_dapm_context -> device = platform_device->device
struct snd_soc_dapm_context -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dapm_context -> bias_level = SND_SOC_BIAS_OFF;//关闭电源状态
struct snd_soc_dapm_context -> idle_bias_off = true;//低功耗待机状态
2.3
创建一个 头为 snd_soc_codec -> snd_soc_component -> dai_list 的双向链表
3
3.1 继续初始化 snd_soc_codec -> snd_soc_component 成员
snd_soc_codec -> snd_soc_component -> snd_kcontrol_new = snd_soc_codec_driver -> snd_kcontrol_new
snd_soc_codec -> snd_soc_component -> num_controls = snd_soc_codec_driver -> num_controls
snd_soc_codec -> snd_soc_component -> snd_soc_dapm_widget = snd_soc_codec_driver -> snd_soc_dapm_widget
snd_soc_codec -> snd_soc_component -> num_dapm_widgets = snd_soc_codec_driver -> num_dapm_widgets
snd_soc_codec -> snd_soc_component -> snd_soc_dapm_route = snd_soc_codec_driver -> snd_soc_dapm_route
snd_soc_codec -> snd_soc_component -> num_dapm_routes = snd_soc_codec_driver -> num_dapm_routes
snd_soc_codec -> snd_soc_component -> probe() = snd_soc_codec_drv_probe()
snd_soc_codec -> snd_soc_component -> remove() = snd_soc_codec_drv_remove()
snd_soc_codec -> snd_soc_component -> write() = snd_soc_codec_drv_write()
snd_soc_codec -> snd_soc_component -> read() = snd_soc_codec_drv_read
snd_soc_codec -> snd_soc_component -> ignore_pmdown_time = snd_soc_codec_driver -> ignore_pmdown_time
3.2 初始化 snd_soc_codec -> snd_soc_dapm_context 成员
snd_soc_codec -> snd_soc_dapm_context -> idle_bias_off = snd_soc_codec_driver -> idle_bias_off
snd_soc_codec -> snd_soc_dapm_context -> suspend_bias_off = snd_soc_codec_driver -> suspend_bias_off
snd_soc_codec -> snd_soc_dapm_context -> seq_notifier = snd_soc_codec_driver -> seq_notifier
snd_soc_codec -> snd_soc_dapm_context -> set_bias_level = snd_soc_codec_set_bias_level()
3.3 绑定 snd_soc_codec -> device 与 platform_device->device
snd_soc_codec -> device = platform_device->device
3.4 绑定 snd_soc_codec -> snd_soc_codec_drive 与 snd_soc_codec_driver soc_mtk_codec
snd_soc_codec -> snd_soc_codec_drive = snd_soc_codec_driver
4 register any DAIs : 注册
ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
4.1
创建 struct snd_soc_dai *dai
定义 struct device *dev,并用 snd_soc_codec -> snd_soc_component -> device 初始化。
注意:前面已经将 platform_device->device 与 snd_soc_codec -> snd_soc_component -> device 绑定。此处就是用 platform_device->device 初始化
4.2
snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver = snd_soc_dai_driver
snd_soc_codec -> snd_soc_component -> num_dai = ARRAY_SIZE(mtk_6323_dai_codecs)
4.3 初始化前面创建的 snd_soc_dai 中的成员
为每一个 snd_soc_dai 分配空间
设置 struct snd_soc_dai -> name
struct snd_soc_dai -> snd_soc_component = snd_soc_codec -> snd_soc_component
struct snd_soc_dai -> device = snd_soc_codec -> snd_soc_component -> device
struct snd_soc_dai -> snd_soc_dai_driver = snd_soc_codec -> snd_soc_component -> snd_soc_dai_driver
struct snd_soc_dai -> snd_soc_dai_driver -> snd_soc_dai_ops = 空
4.4
添加 struct snd_soc_dai -> list_head 插入到 snd_soc_codec -> snd_soc_component -> list_head dai_list 双向链表
5
遍历以 &snd_soc_codec->snd_soc_component.list_head 为头节点,以 snd_soc_dai 为节点的双向链表。
初始化链表中每一个节点的 snd_soc_dai -> snd_soc_codec = snd_soc_codec
6 将 snd_soc_codec -> list_head 添加到 codec_list 双向链表
list_add(&codec->list, &codec_list);
*/
int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai)
{
/* 定义struct snd_soc_codec 结构体 */
struct snd_soc_codec *codec;
/* 定义 snd_soc_dai 结构体 */
struct snd_soc_dai *dai;
int ret, i;
dev_dbg(dev, "codec register %s\n", dev_name(dev));
/* 分配struct snd_soc_codec codec设备空间 */
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
/*
struct snd_soc_codec -> snd_soc_component -> snd_soc_codec = struct snd_soc_codec
struct snd_soc_codec -> snd_soc_component -> snd_soc_dapm_context = struct snd_soc_codec -> snd_soc_dapm_context
*/
codec->component.dapm_ptr = &codec->dapm;
codec->component.codec = codec;
ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev);
if (ret)
goto err_free;
/*
snd_soc_codec -> snd_soc_component -> snd_kcontrol_new = snd_soc_codec_driver -> snd_kcontrol_new
snd_soc_codec -> snd_soc_component -> num_controls = snd_soc_codec_driver -> num_controls
snd_soc_codec -> snd_soc_component -> snd_soc_dapm_widget = snd_soc_codec_driver -> snd_soc_dapm_widget
snd_soc_codec -> snd_soc_component -> num_dapm_widgets = snd_soc_codec_driver -> num_dapm_widgets
snd_soc_codec -> snd_soc_component -> snd_soc_dapm_route = snd_soc_codec_driver -> snd_soc_dapm_route
snd_soc_codec -> snd_soc_component -> num_dapm_routes = snd_soc_codec_driver -> num_dapm_routes
snd_soc_codec -> snd_soc_component -> probe() = snd_soc_codec_drv_probe()
snd_soc_codec -> snd_soc_component -> remove() = snd_soc_codec_drv_remove()
snd_soc_codec -> snd_soc_component -> write() = snd_soc_codec_drv_write()
snd_soc_codec -> snd_soc_component -> read() = snd_soc_codec_drv_read
snd_soc_codec -> snd_soc_component -> ignore_pmdown_time = snd_soc_codec_driver -> ignore_pmdown_time
snd_soc_codec -> snd_soc_dapm_context -> idle_bias_off = snd_soc_codec_driver -> idle_bias_off
snd_soc_codec -> snd_soc_dapm_context -> suspend_bias_off = snd_soc_codec_driver -> suspend_bias_off
snd_soc_codec -> snd_soc_dapm_context -> seq_notifier = snd_soc_codec_driver -> seq_notifier
snd_soc_codec -> snd_soc_dapm_context -> set_bias_level = snd_soc_codec_set_bias_level()
snd_soc_codec -> device = platform_device->device
snd_soc_codec -> snd_soc_codec_drive = snd_soc_codec_driver
snd_soc_codec -> snd_soc_component -> val_bytes = snd_soc_codec_driver -> reg_word_size
*/
if (codec_drv->controls) {
codec->component.controls = codec_drv->controls;
codec->component.num_controls = codec_drv->num_controls;
}
if (codec_drv->dapm_widgets) {
codec->component.dapm_widgets = codec_drv->dapm_widgets;
codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
}
if (codec_drv->dapm_routes) {
codec->component.dapm_routes = codec_drv->dapm_routes;
codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
}
if (codec_drv->probe)
codec->component.probe = snd_soc_codec_drv_probe;
if (codec_drv->remove)
codec->component.remove = snd_soc_codec_drv_remove;
if (codec_drv->write)
codec->component.write = snd_soc_codec_drv_write;
if (codec_drv->read)
codec->component.read = snd_soc_codec_drv_read;
codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
if (codec_drv->seq_notifier)
codec->dapm.seq_notifier = codec_drv->seq_notifier;
if (codec_drv->set_bias_level)
codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
codec->dev = dev;
codec->driver = codec_drv;
codec->component.val_bytes = codec_drv->reg_word_size;
mutex_init(&codec->mutex);
#ifdef CONFIG_DEBUG_FS
codec->component.init_debugfs = soc_init_codec_debugfs;
codec->component.debugfs_prefix = "codec";
#endif
if (codec_drv->get_regmap)
codec->component.regmap = codec_drv->get_regmap(dev);
for (i = 0; i < num_dai; i++) {
fixup_codec_formats(&dai_drv[i].playback);
fixup_codec_formats(&dai_drv[i].capture);
}
/* register any DAIs */
ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
/* 遍历以 &snd_soc_codec->snd_soc_component.list_head 为头节点,以 snd_soc_dai 为节点的双向链表。
初始化链表中节点的 snd_soc_dai -> snd_soc_codec = snd_soc_codec
*/
list_for_each_entry(dai, &codec->component.dai_list, list)
dai->codec = codec;
mutex_lock(&client_mutex);
snd_soc_component_add_unlocked(&codec->component);
/* 将 snd_soc_codec -> list_head 添加到 codec_list 双向链表*/
list_add(&codec->list, &codec_list);
mutex_unlock(&client_mutex);
dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
codec->component.name);
return 0;
err_cleanup:
snd_soc_component_cleanup(&codec->component);
err_free:
kfree(codec);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_codec);