1. 概述

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。

分析例子:

machine: sound\soc\samsung\s3c24xx_uda134x.c

platform: sound\soc\samsung\s3c24xx-i2s.c

codec:   sound\soc\codecs\uda134x.c

2. 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",},.probe = uda134x_codec_probe,.remove = uda134x_codec_remove,
};

之间查看此platform_driver的probe函数。

static int uda134x_codec_probe(struct platform_device *pdev)
{struct uda134x_platform_data *pd = pdev->dev.platform_data;struct uda134x_priv *uda134x;int ret;if (!pd) {dev_err(&pdev->dev, "Missing L3 bitbang function\n");return -ENODEV;}uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL);if (!uda134x)return -ENOMEM;uda134x->pd = pd;platform_set_drvdata(pdev, uda134x);if (pd->l3.use_gpios) {ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3);if (ret < 0)return ret;}uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,&uda134x_regmap_config);if (IS_ERR(uda134x->regmap))return PTR_ERR(uda134x->regmap);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 const struct snd_soc_codec_driver soc_codec_dev_uda134x = {.probe =        uda134x_soc_probe,.set_bias_level = uda134x_set_bias_level,.suspend_bias_off = true,.component_driver = {.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,
};

2.1 snd_soc_register_codec


/*** snd_soc_register_codec - Register a codec with the ASoC core** @dev: The parent device for this codec* @codec_drv: Codec driver* @dai_drv: The associated DAI driver* @num_dai: Number of DAIs*/
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_dapm_context *dapm;struct snd_soc_codec *codec;struct snd_soc_dai *dai;int ret, i;dev_dbg(dev, "codec register %s\n", dev_name(dev));codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);if (codec == NULL)return -ENOMEM;codec->component.codec = codec;ret = snd_soc_component_initialize(&codec->component,&codec_drv->component_driver, dev);if (ret)goto err_free;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->suspend)codec->component.suspend = snd_soc_codec_drv_suspend;if (codec_drv->resume)codec->component.resume = snd_soc_codec_drv_resume;if (codec_drv->write)codec->component.write = snd_soc_codec_drv_write;if (codec_drv->read)codec->component.read = snd_soc_codec_drv_read;if (codec_drv->set_sysclk)codec->component.set_sysclk = snd_soc_codec_set_sysclk_;if (codec_drv->set_pll)codec->component.set_pll = snd_soc_codec_set_pll_;if (codec_drv->set_jack)codec->component.set_jack = snd_soc_codec_set_jack_;codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;dapm = snd_soc_codec_get_dapm(codec);dapm->idle_bias_off = codec_drv->idle_bias_off;dapm->suspend_bias_off = codec_drv->suspend_bias_off;if (codec_drv->seq_notifier)dapm->seq_notifier = codec_drv->seq_notifier;if (codec_drv->set_bias_level)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;#ifdef CONFIG_DEBUG_FScodec->component.init_debugfs = soc_init_codec_debugfs;codec->component.debugfs_prefix = "codec";
#endifif (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);}ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);if (ret < 0) {dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret);goto err_cleanup;}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);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;
}

2.1.1 分配一个snd_soc_codec结构,设置其component参数

 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);if (codec == NULL)return -ENOMEM;codec->component.codec = codec;

2.1.2 调用snd_soc_component_initialize函数进行component部件的初始化

 ret = snd_soc_component_initialize(&codec->component,&codec_drv->component_driver, dev);if (ret)goto err_free;

2.1.3 根据snd_soc_codec_driver的参数,进行一系列的初始化

 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->suspend)codec->component.suspend = snd_soc_codec_drv_suspend;if (codec_drv->resume)codec->component.resume = snd_soc_codec_drv_resume;if (codec_drv->write)codec->component.write = snd_soc_codec_drv_write;if (codec_drv->read)codec->component.read = snd_soc_codec_drv_read;if (codec_drv->set_sysclk)codec->component.set_sysclk = snd_soc_codec_set_sysclk_;if (codec_drv->set_pll)codec->component.set_pll = snd_soc_codec_set_pll_;if (codec_drv->set_jack)codec->component.set_jack = snd_soc_codec_set_jack_;codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;dapm = snd_soc_codec_get_dapm(codec);dapm->idle_bias_off = codec_drv->idle_bias_off;dapm->suspend_bias_off = codec_drv->suspend_bias_off;if (codec_drv->seq_notifier)dapm->seq_notifier = codec_drv->seq_notifier;if (codec_drv->set_bias_level)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;

2.1.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);}

2.1.5 调用snd_soc_register_dais接口注册dai

 ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);if (ret < 0) {dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret);goto err_cleanup;}

根据dai的数目,分配snd_soc_dai结构,根据dai的数目设置dai的名字,这是dai的传入参数驱动,然后将此dai加入到component的dai_list中。


/* 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;}dai->component = component;dai->dev = dev;dai->driver = dai_drv;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;
}

2.1.6 根据component的dai_list,对所有的dai设置其所属的codec

 list_for_each_entry(dai, &codec->component.dai_list, list)dai->codec = codec;

2.1.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);  

3. 总结

总结:  通过调用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内核4.14版本——alsa框架分析(8)-ASoC(Codec)相关推荐

  1. Linux内核4.14版本——alsa框架分析(1)—alsa简介

    目录 一,ALSA声音编程介绍 二,ALSA历史 三,数字音频基础 四,ALSA基础 五,ALSA体系结构 六,设备命名 七,声音缓存和数据传输 八,Over and Under Run 九,一个典型 ...

  2. Linux内核4.14版本——alsa框架分析(11)——DAPM(2)——widget、route和path的概念

    目录 1. DAPM的基本单元:widget(struct  snd_soc_dapm_widget) 2. widget的种类 3. widget之间的连接器:path(struct snd_soc ...

  3. Linux内核4.14版本——drm框架分析(1)——drm简介

    目录 1. DRM简介(Direct Rendering Manager) 1.1 DRM发展历史 1.2 DRM架构对比FB架构优势 1.3 DRM图形显示框架 1.4 DRM图形显示框架涉及元素 ...

  4. Linux内核4.14版本——mmc框架_软件总体架构

    目录 1. 前言 2. 软件架构 3. 工作流程 4. mmc设备 4.1 mmc type card 4.2 sd type card 4.3 sdio type card 5. mmc协议 5.1 ...

  5. Linux内核4.14版本:ARM64的内核启动过程(二)——start_kernel

    目录 1. rest_init 2. init 进程(kernel_init) 2.1 kernel_init_freeable 2.1.1 do_basic_setup 2.1.2 prepare_ ...

  6. Linux内核4.14版本——watchdog看门狗框架分析

    目录 0 简介 1. 设备的注册 1.1 dw_wdt_drv_probe 1.2 watchdog_register_device 1.3 __watchdog_register_device 1. ...

  7. Linux内核4.14版本——DMA Engine框架分析(6)-实战(测试dma驱动)

    1. dw-axi-dmac驱动 2. dma的测试程序 2.1 内核程序 2.2 用户测试程序 1. dw-axi-dmac驱动 dw-axi-dmac驱动4.14版本没有,是从5.4版本移植的,基 ...

  8. Linux内核4.14版本——Nand子系统(1)——hisi504_nand.c分析

    1. 简介 2. DTS 3. hisi_nfc_probe函数 3.1 获取dts中的资源 3.2 设置nand-chip结构体中必要的字段 3.3 nand_scan_ident扫描识别nand控 ...

  9. Linux内核4.14版本——DMA Engine框架分析(2)_功能介绍及解接口分析(slave client driver)

    1 前言 2  Slave-DMA API和Async TX API 3 dma engine的使用步骤 3.1 申请DMA channel 3.2 配置DMA channel的参数 3.3 获取传输 ...

最新文章

  1. 开发日记-20190401
  2. 硬链接、软链接的区别
  3. python课程价格哪个好-上海Python线上远程课
  4. progress组件(进度条)
  5. jQuery表单验证的几种方法
  6. UISlider 滑块控件—IOS开发
  7. c3p0-0.9.1.2.jar
  8. C#编程(三十三)----------Array类
  9. [转]Java实现定时任务的三种方法
  10. MAC VSCode Go代码第一次运行配置
  11. tcp 裸流 发送 html,ffmpeg 命令学习
  12. 微软发布TypeScript用以改进JavaScript开发
  13. java记事本写玫瑰花代码_第一个用记事本写的java代码
  14. ASEMI肖特基二极管MBR10200CT参数,MBR10200CT封装
  15. android 电话号码发iphone怎么样,安卓手机如何轻松的向iPhone发文件呢?
  16. Java——名片管理系统
  17. 影院卖品既然除了爆米花都卖不动,为什么不降价?
  18. Android学习——5个UI界面设计
  19. dialer元认知架构简介
  20. 学习网站 | 计算机专业学生常用网站,必备!

热门文章

  1. 有源晶振和无源晶振的比较
  2. teraterm乱码linux,[Unity3D][转] 关于Assets资源目录结构管理
  3. DIY 空气质量检测表
  4. 电源系列1:LDO 基本 原理(一)
  5. 数据结构之线性表及C语言实现
  6. 真实感受一下县比省大不包邮,省市区乡镇多级数据重装上阵
  7. 【草稿待续】统一的嵌入式软件编程模型——驱动
  8. 红茶馆:承诺满天下,守信行万里
  9. 汉字转拼音 Npinyin
  10. 类图,类与类之间的关系