概述

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: %d\n", 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;elsedai->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相关推荐

  1. Linux 音频驱动

    Linux 音频驱动 硬件介绍 WM8960与IMX6ULL之间有两个通信接口:I2C和I2S 其中I2C用于配置WM8960 I2S用于音频数据传输 修改设备树文件 编写I2C子节点设备树 code ...

  2. Linux 音频驱动(四) ASoC音频驱动之Machine驱动

    目录 1. 基本介绍 2. 源码分析 2.1. Machine数据结构 struct snd_soc_dai_link 3. 声卡 3.1. 数据结构struct snd_soc_card 3.2. ...

  3. Linux 音频驱动(五) ALSA音频驱动之PCM逻辑设备

    目录 1. 前言 2. PCM逻辑设备 2.1. 创建 PCM逻辑设备: 2.2. PCM逻辑设备文件操作函数集:snd_pcm_f_ops[] 2.3. Open PCM逻辑设备 2.4. Writ ...

  4. STM32MP157驱动开发——Linux 音频驱动

    STM32MP157驱动开发--Linux 音频驱动 一.简介 1.CS42L51 简介 2.I2S总线 3.STM32MP1 SAI 总线接口 二.驱动开发 1.音频驱动 1)修改设备树 i2c 接 ...

  5. Linux音频驱动之二:Control接口的调用

    本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记 一. control接口说明 Control接口主要让用户空间的应用程序(alsa-lib)可以访问和控制音频 ...

  6. 转载:Linux音频驱动-OSS和ALSA声音系统简介及其比较

    Linux音频驱动-OSS和ALSA声音系统简介及其比较 概述 昨天想在Ubuntu上用一下HTK工具包来绘制语音信号的频谱图和提取MFCC的结果,但由于前段时间把Ubuntu升级到13.04,系统的 ...

  7. Linux 音频驱动(二) ASoC音频驱动之Platform驱动

    目录 1. 简介 2. 源码分析 2.1. CPU DAI 2.1.1. 数据结构struct snd_soc_dai_driver 2.1.2. 注册CPU DAI:snd_soc_register ...

  8. linux 音频架构绕过,linux音频驱动架构

    1.linux音频驱动架构分为3部分组成:硬件无关层(核心层ALSA).板级音频数字接口层驱动(McASP.McBSP等).外部codes驱动 sound/soc/davinci/ti81xx-etv ...

  9. linux 音频驱动的流程,Intel平台下Linux音频驱动流程分析

    [软件框架] 在对要做的事情一无所知的时候,从全局看看系统的拓扑图对我们认识新事物有很大的帮助.Audio 部分的驱动程序框架如下图所示: 这幅图明显地分为 3 级. 上方蓝色系的 ALSA Kern ...

  10. Linux音频驱动开发概括

    原址 1.嵌入式音频系统硬件连接 下图所示的嵌入式设备使用IIS将音频数据发送给编解码器.对编解码器的I/O寄存器的编程通过IIC总线进行. 2.音频体系结构-ALSA ALSA是Advanced L ...

最新文章

  1. linux默认shell类型转换,Linux中默认的shell如何切换为其他类型的shell
  2. 蓄力十年,做一个成就
  3. 每天学习flash一点(3) flash外部读取xml
  4. Unity C# namespace 命名空间的使用
  5. ios 图片居中裁剪_[ iOS Shortcuts / Workflow ] 分享一个给照片批量加「底部居中偏上」水印的捷径,可适应不同水印的尺寸...
  6. linux杀掉进程后总是重启,Linux监控进程,进程关闭自动重启方案
  7. 再读《精通css》00
  8. andriod环境搭建
  9. 如何拯救美团的“中年危机”?
  10. java中的龟兔赛跑代码_有关JAVA编写龟兔赛跑的游戏的问题。求助……
  11. Jmeter设置默认中文页面
  12. python脚本操作excel
  13. hbase的region分区
  14. 基于ssm的记账管理系统设计与实现【毕业设计jsp】
  15. 最护眼的电脑屏幕颜色是黑色?
  16. js 仿微信投诉—引入vue.js,拆分组件为单个js
  17. 在c语言中 if语句后的一对原括号,c语言中if语句后的一对圆括号中
  18. 浅谈如何做好督查督办工作?
  19. 成功实现NAS家庭服务器(流媒体播放、文件共享及下载机)
  20. 高薪运维经典企业版面试题汇总

热门文章

  1. python + eclipse +pydev
  2. Object-C——三大特性之多态
  3. ChipScope用法总结
  4. 大话设计模式--外观模式 Facade -- C++实现实例
  5. C# 中的常用正则表达式汇总
  6. 在DataGrid页眉上添加全选的CheckBox控件
  7. 2017年12月24日23:46:07
  8. 靠播放业务吃不饱?音乐流媒体纷纷“加电商”卖周边
  9. Win7如何删除家庭组
  10. 每天一道算法题(16)——翻转链表