概述
https://cloud.tencent.com/developer/article/1603869
概述
ASOC的出现是为了让Codec独立于CPU,减少和CPU之间的耦合,这样同一个Codec驱动无需修改就可以适用任何一款平台。还是以下图做参考例子:
在Machine中已经知道,snd_soc_dai_link结构就指明了该Machine所使用的Platform和Codec。在Codec这边通过codec_dai和Platform侧的cpu_dai相互通信,既然相互通信,就需要遵守一定的规则,其中codec_dai和cpu_dai统一抽象为struct snd_soc_dai结构,而将dai的相关操作使用snd_soc_dai_driver抽象。同时也需要对所有的codec设备进行抽象封装,linux使用snd_soc_codec进行所有codec设备的抽象,而将codec的驱动抽象为snd_soc_codec_driver结构。
所有简单来说,Codec侧有四个重要的数据结构:
struct snd_soc_dai,struct snd_soc_dai_driver,struct snd_soc_codec,struct snd_soc_codec_driver。
Codec代码分析
如何找到codec的代码呢? 答案是通过machine中的snd_soc_dai_link结构:
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, .platform_name = "s3c24xx-iis", };
在内核中搜索codec_name="uda134x-codec",会在kernel/sound/soc/codec/uda134x.c中发现。
static struct platform_driver uda134x_codec_driver = { .driver = { .name = "uda134x-codec", .owner = THIS_MODULE, }, .probe = uda134x_codec_probe, .remove = uda134x_codec_remove, };
之间查看此platform_driver的probe函数
static int uda134x_codec_probe(struct platform_device *pdev) { return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_uda134x, &uda134x_dai, 1); }
此出通过snd_soc_register_codec函数注册了uda134x的codec,同时传入了snd_soc_codec_driver和snd_soc_dai_driver结构。
static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .probe = uda134x_soc_probe, .remove = uda134x_soc_remove, .suspend = uda134x_soc_suspend, .resume = uda134x_soc_resume, .reg_cache_size = sizeof(uda134x_reg), .reg_word_size = sizeof(u8), .reg_cache_default = uda134x_reg, .reg_cache_step = 1, .read = uda134x_read_reg_cache, .write = uda134x_write, .set_bias_level = uda134x_set_bias_level, .dapm_widgets = uda134x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), .dapm_routes = uda134x_dapm_routes, .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), };
snd_soc_dai_driver结构:
static const struct snd_soc_dai_ops uda134x_dai_ops = { .startup = uda134x_startup, .shutdown = uda134x_shutdown, .hw_params = uda134x_hw_params, .digital_mute = uda134x_mute, .set_sysclk = uda134x_set_dai_sysclk, .set_fmt = uda134x_set_dai_fmt, }; static struct snd_soc_dai_driver uda134x_dai = { .name = "uda134x-hifi", /* playback capabilities */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda134x_dai_ops, };
继续进入snd_soc_register_codec函数分析。
1. 分配一个snd_soc_codec结构,设置component参数。
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; codec->component.dapm_ptr = &codec->dapm; codec->component.codec = codec;
2. 调用snd_soc_component_initialize函数进行component部件的初始化,会根据snd_soc_codec_driver中的struct snd_soc_component_driver结构设置snd_soc_codec中的component组件,详细代码不在贴出。
ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev);
3. 根据snd_soc_codec_driver的参数,进行一系列的初始化,
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);
4. 根据snd_soc_dai_driver中的参数设置Format
for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); }
5. 调用snd_soc_register_dais接口注册dai,传入参数有dai的驱动,以及dai的参数,因为一个codec不止一个dai接口。
ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); if (ret < 0) { dev_err(dev, "ASoC: Failed to regster DAIs: %dn", ret); goto err_cleanup; }
根据dai的数目,分配snd_soc_dai结构,根据dai的数目设置dai的名字,这是dai的传入参数驱动,然后将此dai加入到component的dai_list中。
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. */ 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); }
6. 根据component的dai_list,对所有的dai设置其所属的codec。
list_for_each_entry(dai, &codec->component.dai_list, list) dai->codec = codec;
7. 将所有的组间加入到component_list中,然后将注册的codec存入codec_list中。
mutex_lock(&client_mutex); snd_soc_component_add_unlocked(&codec->component); list_add(&codec->list, &codec_list); mutex_unlock(&client_mutex);
至此,codec的注册就分析完毕。 总结: 通过调用snd_soc_register_codec函数之后,分配的codec最终放入了codec_list链表中,codec下的component组件全部放入component_list链表中,codec下的dai全部存放入code->component.dai_list中。 可以在上节的Machine中看到,machine匹配codec_dai和cpu_dai也是从code->component.dai_list中获取的。
关于codec侧驱动总结:
1. 分配名字为"codec_name"的平台驱动,注册。 2. 定义struct snd_soc_codec_driver结构,设置,初始化。
3. 定义struct snd_soc_dai_driver结构,设置,初始化。
4. 调用snd_soc_register_codec函数注册codec。
最后
以上就是美满衬衫为你收集整理的Linux音频驱动-AOSC之Codec的全部内容,希望文章能够帮你解决Linux音频驱动-AOSC之Codec所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复