Linux音频驱动-AOSC之Codec

概述

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结构:
[cpp]  view plain  copy
  1. static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {  
  2.     .name = "UDA134X",  
  3.     .stream_name = "UDA134X",  
  4.     .codec_name = "uda134x-codec",  
  5.     .codec_dai_name = "uda134x-hifi",  
  6.     .cpu_dai_name = "s3c24xx-iis",  
  7.     .ops = &s3c24xx_uda134x_ops,  
  8.     .platform_name  = "s3c24xx-iis",  
  9. };  
在内核中搜索codec_name="uda134x-codec",会在kernel/sound/soc/codec/uda134x.c中发现。
[cpp]  view plain  copy
  1. static struct platform_driver uda134x_codec_driver = {  
  2.     .driver = {  
  3.         .name = "uda134x-codec",  
  4.         .owner = THIS_MODULE,  
  5.     },  
  6.     .probe = uda134x_codec_probe,  
  7.     .remove = uda134x_codec_remove,  
  8. };  
之间查看此platform_driver的probe函数
[cpp]  view plain  copy
  1. static int uda134x_codec_probe(struct platform_device *pdev)  
  2. {  
  3.     return snd_soc_register_codec(&pdev->dev,  
  4.             &soc_codec_dev_uda134x, &uda134x_dai, 1);  
  5. }  
此出通过snd_soc_register_codec函数注册了uda134x的codec,同时传入了snd_soc_codec_driver和snd_soc_dai_driver结构。
[cpp]  view plain  copy
  1. static struct snd_soc_codec_driver soc_codec_dev_uda134x = {  
  2.     .probe =        uda134x_soc_probe,  
  3.     .remove =       uda134x_soc_remove,  
  4.     .suspend =      uda134x_soc_suspend,  
  5.     .resume =       uda134x_soc_resume,  
  6.     .reg_cache_size = sizeof(uda134x_reg),  
  7.     .reg_word_size = sizeof(u8),  
  8.     .reg_cache_default = uda134x_reg,  
  9.     .reg_cache_step = 1,  
  10.     .read = uda134x_read_reg_cache,  
  11.     .write = uda134x_write,  
  12.     .set_bias_level = uda134x_set_bias_level,  
  13.     .dapm_widgets = uda134x_dapm_widgets,  
  14.     .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),  
  15.     .dapm_routes = uda134x_dapm_routes,  
  16.     .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),  
  17. };  
snd_soc_dai_driver结构:
[cpp]  view plain  copy
  1. static const struct snd_soc_dai_ops uda134x_dai_ops = {  
  2.     .startup    = uda134x_startup,  
  3.     .shutdown   = uda134x_shutdown,  
  4.     .hw_params  = uda134x_hw_params,  
  5.     .digital_mute   = uda134x_mute,  
  6.     .set_sysclk = uda134x_set_dai_sysclk,  
  7.     .set_fmt    = uda134x_set_dai_fmt,  
  8. };  
  9.   
  10. static struct snd_soc_dai_driver uda134x_dai = {  
  11.     .name = "uda134x-hifi",  
  12.     /* playback capabilities */  
  13.     .playback = {  
  14.         .stream_name = "Playback",  
  15.         .channels_min = 1,  
  16.         .channels_max = 2,  
  17.         .rates = UDA134X_RATES,  
  18.         .formats = UDA134X_FORMATS,  
  19.     },  
  20.     /* capture capabilities */  
  21.     .capture = {  
  22.         .stream_name = "Capture",  
  23.         .channels_min = 1,  
  24.         .channels_max = 2,  
  25.         .rates = UDA134X_RATES,  
  26.         .formats = UDA134X_FORMATS,  
  27.     },  
  28.     /* pcm operations */  
  29.     .ops = &uda134x_dai_ops,  
  30. };  
继续进入snd_soc_register_codec函数分析。
1.  分配一个snd_soc_codec结构,设置component参数。
[cpp]  view plain  copy
  1. codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);  
  2. if (codec == NULL)  
  3.     return -ENOMEM;  
  4.   
  5. codec->component.dapm_ptr = &codec->dapm;  
  6. codec->component.codec = codec;  
2.  调用snd_soc_component_initialize函数进行component部件的初始化,会根据snd_soc_codec_driver中的struct snd_soc_component_driver结构设置snd_soc_codec中的component组件,详细代码不在贴出。
[cpp]  view plain  copy
  1. ret = snd_soc_component_initialize(&codec->component,  
  2.         &codec_drv->component_driver, dev);  
3.  根据snd_soc_codec_driver的参数,进行一系列的初始化,
[cpp]  view plain  copy
  1. if (codec_drv->controls) {  
  2.     codec->component.controls = codec_drv->controls;  
  3.     codec->component.num_controls = codec_drv->num_controls;  
  4. }  
  5. if (codec_drv->dapm_widgets) {  
  6.     codec->component.dapm_widgets = codec_drv->dapm_widgets;  
  7.     codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;  
  8. }  
  9. if (codec_drv->dapm_routes) {  
  10.     codec->component.dapm_routes = codec_drv->dapm_routes;  
  11.     codec->component.num_dapm_routes = codec_drv->num_dapm_routes;  
  12. }  
  13.   
  14. if (codec_drv->probe)  
  15.     codec->component.probe = snd_soc_codec_drv_probe;  
  16. if (codec_drv->remove)  
  17.     codec->component.remove = snd_soc_codec_drv_remove;  
  18. if (codec_drv->write)  
  19.     codec->component.write = snd_soc_codec_drv_write;  
  20. if (codec_drv->read)  
  21.     codec->component.read = snd_soc_codec_drv_read;  
  22. codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;  
  23. codec->dapm.idle_bias_off = codec_drv->idle_bias_off;  
  24. codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;  
  25. if (codec_drv->seq_notifier)  
  26.     codec->dapm.seq_notifier = codec_drv->seq_notifier;  
  27. if (codec_drv->set_bias_level)  
  28.     codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;  
  29. codec->dev = dev;  
  30. codec->driver = codec_drv;  
  31. codec->component.val_bytes = codec_drv->reg_word_size;  
  32. mutex_init(&codec->mutex);  
4.  根据snd_soc_dai_driver中的参数设置Format
[cpp]  view plain  copy
  1. for (i = 0; i < num_dai; i++) {  
  2.     fixup_codec_formats(&dai_drv[i].playback);  
  3.     fixup_codec_formats(&dai_drv[i].capture);  
  4. }  
5.  调用snd_soc_register_dais接口注册dai,传入参数有dai的驱动,以及dai的参数,因为一个codec不止一个dai接口。
[cpp]  view plain  copy
  1. ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);  
  2. if (ret < 0) {  
  3.     dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);  
  4.     goto err_cleanup;  
  5. }  
根据dai的数目,分配snd_soc_dai结构,根据dai的数目设置dai的名字,这是dai的传入参数驱动,然后将此dai加入到component的dai_list中。
[cpp]  view plain  copy
  1. for (i = 0; i < count; i++) {  
  2.   
  3.     dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);  
  4.     if (dai == NULL) {  
  5.         ret = -ENOMEM;  
  6.         goto err;  
  7.     }  
  8.   
  9.     /* 
  10.      * Back in the old days when we still had component-less DAIs, 
  11.      * instead of having a static name, component-less DAIs would 
  12.      * inherit the name of the parent device so it is possible to 
  13.      * register multiple instances of the DAI. We still need to keep 
  14.      * the same naming style even though those DAIs are not 
  15.      * component-less anymore. 
  16.      */  
  17.     if (count == 1 && legacy_dai_naming) {  
  18.         dai->name = fmt_single_name(dev, &dai->id);  
  19.     } else {  
  20.         dai->name = fmt_multiple_name(dev, &dai_drv[i]);  
  21.         if (dai_drv[i].id)  
  22.             dai->id = dai_drv[i].id;  
  23.         else  
  24.             dai->id = i;  
  25.     }  
  26.     if (dai->name == NULL) {  
  27.         kfree(dai);  
  28.         ret = -ENOMEM;  
  29.         goto err;  
  30.     }  
  31.   
  32.     dai->component = component;  
  33.     dai->dev = dev;  
  34.     dai->driver = &dai_drv[i];  
  35.     if (!dai->driver->ops)  
  36.         dai->driver->ops = &null_dai_ops;  
  37.   
  38.     list_add(&dai->list, &component->dai_list);  
  39.   
  40.     dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);  
  41. }  
6.   根据component的dai_list,对所有的dai设置其所属的codec。
[cpp]  view plain  copy
  1. list_for_each_entry(dai, &codec->component.dai_list, list)  
  2.     dai->codec = codec;  
7.   将所有的组间加入到component_list中,然后将注册的codec存入codec_list中。
[cpp]  view plain  copy
  1. mutex_lock(&client_mutex);  
  2. snd_soc_component_add_unlocked(&codec->component);  
  3. list_add(&codec->list, &codec_list);  
  4. 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。

猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/80611286
今日推荐