概述
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",
- };
- static struct platform_driver uda134x_codec_driver = {
- .driver = {
- .name = "uda134x-codec",
- .owner = THIS_MODULE,
- },
- .probe = uda134x_codec_probe,
- .remove = uda134x_codec_remove,
- };
- static int uda134x_codec_probe(struct platform_device *pdev)
- {
- return snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_uda134x, &uda134x_dai, 1);
- }
- 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),
- };
- 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,
- };
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;
- ret = snd_soc_component_initialize(&codec->component,
- &codec_drv->component_driver, dev);
- 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);
- for (i = 0; i < num_dai; i++) {
- fixup_codec_formats(&dai_drv[i].playback);
- fixup_codec_formats(&dai_drv[i].capture);
- }
- ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
- if (ret < 0) {
- dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
- goto err_cleanup;
- }
- 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);
- }
- list_for_each_entry(dai, &codec->component.dai_list, list)
- dai->codec = codec;
- 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结构,设置,初始化。
2. 定义struct snd_soc_codec_driver结构,设置,初始化。
3. 定义struct snd_soc_dai_driver结构,设置,初始化。
4. 调用snd_soc_register_codec函数注册codec。