arm平台:IMX6Q
内核:Linux4.1.15
系统:Android6.0
codec:wm8960
主要内容为Machine、Platform和Codec三大部分之间的关系和实现。文章中的举例和代码均为imx6q-Android6.0源码和文件。
文章中如有错误和不严谨内容,欢迎指正。
在设备中,Codec的作用可以归结为4种,分别是:
1).对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号
2).对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号
3).对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的
4).对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等
ASoC对Codec的这些功能都定义好了一系列的相应的接口,以方便地对Codec进行控制。ASoC对Codec驱动的一个基本要求是:驱动程序的代码必须要做到平台无关性,以方便同一个Codec的代码不经修改即可用在不同的平台上。
1、snd_soc_codec_driver和snd_soc_dai_driver
kernel_imx/sound/soc/codecs/wm8960.c
static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
.probe = wm8960_probe,
.set_bias_level = wm8960_set_bias_level,
.suspend_bias_off = true,
};
static const struct snd_soc_dai_ops wm8960_dai_ops = {
.hw_params = wm8960_hw_params,
.hw_free = wm8960_hw_free,
.digital_mute = wm8960_mute,
.set_fmt = wm8960_set_dai_fmt,
.set_clkdiv = wm8960_set_dai_clkdiv,
.set_pll = wm8960_set_dai_pll,
.set_sysclk = wm8960_set_dai_sysclk,
};
static struct snd_soc_dai_driver wm8960_dai = {
.name = "wm8960-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.ops = &wm8960_dai_ops,
.symmetric_rates = 1,
};
/*注册*/
snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
2、snd_soc_register_codec
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)
{
/*分配一个snd_soc_codec实例*/
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
/*用snd_soc_codec_driver来填充snd_soc_codec结构体*/
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;
/*snd_soc_register_dais函数其实和snd_soc_register_codec类似,先是为每个snd_soc_dai实例分配内存,
确定dai的名字,用snd_soc_dai_driver实例的字段对它进行必要初始化,最后把该dai链接到全局链表dai_list中*/
snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
/*把codec添加到全局链表codec_list中*/
list_add(&codec->list, &codec_list);