高通ASOC中的codec驱动
继上一篇文章:高通Audio中ASOC的machine驱动(一)
ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台。

1、重要的数据结构:
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
const char *name; /* dai的名字 */
struct device *dev; /* 设备指针 */ /* driver ops */
struct snd_soc_dai_driver *driver; /* 指向dai驱动结构的指针 */ /* DAI runtime info */
unsigned int capture_active:; /* stream is in use */
unsigned int playback_active:; /* stream is in use */ /* DAI DMA data */
void *playback_dma_data; /* 用于管理playback dma */
void *capture_dma_data; /* 用于管理capture dma */ /* parent platform/codec */
union {
struct snd_soc_platform *platform; /* 如果是cpu dai,指向所绑定的平台 */
struct snd_soc_codec *codec; /* 如果是codec dai指向所绑定的codec */
};
struct snd_soc_card *card; /* 指向Machine驱动中的crad实例 */
};
snd_soc_dai_driver:
/*
* Digital Audio Interface Driver.
*
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
* operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
* interface.
*/
struct snd_soc_dai_driver {
/* DAI description */
const char *name; /* dai驱动名字 */ /* DAI driver callbacks */
int (*probe)(struct snd_soc_dai *dai); /* dai驱动的probe函数,由snd_soc_instantiate_card回调 */
int (*remove)(struct snd_soc_dai *dai);
int (*suspend)(struct snd_soc_dai *dai); /* 电源管理 */
int (*resume)(struct snd_soc_dai *dai); /* ops */
const struct snd_soc_dai_ops *ops; /* 指向本dai的snd_soc_dai_ops结构 */ /* DAI capabilities */
struct snd_soc_pcm_stream capture; /* 描述capture的能力 */
struct snd_soc_pcm_stream playback; /* 描述playback的能力 */
};
/* SoC Audio Codec device */
struct snd_soc_codec {
const char *name; /* Codec的名字*/
struct device *dev; /* 指向Codec设备的指针 */
const struct snd_soc_codec_driver *driver; /* 指向该codec的驱动的指针 */
struct snd_soc_card *card; /* 指向Machine驱动的card实例 */
int num_dai; /* 该Codec数字接口的个数,目前越来越多的Codec带有多个I2S或者是PCM接口 */
int (*volatile_register)(...); /* 用于判定某一寄存器是否是volatile */
int (*readable_register)(...); /* 用于判定某一寄存器是否可读 */
int (*writable_register)(...); /* 用于判定某一寄存器是否可写 */ /* runtime */
......
/* codec IO */
void *control_data; /* 该指针指向的结构用于对codec的控制,通常和read,write字段联合使用 */
enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPI,SND_SOC_I2C,SND_SOC_REGMAP中的一种 */
unsigned int (*read)(struct snd_soc_codec *, unsigned int); /* 读取Codec寄存器的函数 */
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); /* 写入Codec寄存器的函数 */
/* dapm */
struct snd_soc_dapm_context dapm; /* 用于DAPM控件 */
};
snd_soc_codec_driver:
/* codec driver */
struct snd_soc_codec_driver {
/* driver ops */
int (*probe)(struct snd_soc_codec *); /* codec驱动的probe函数,由snd_soc_instantiate_card回调 */
int (*remove)(struct snd_soc_codec *);
int (*suspend)(struct snd_soc_codec *); /* 电源管理 */
int (*resume)(struct snd_soc_codec *); /* 电源管理 */ /* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls; /* 音频控件指针 */
const struct snd_soc_dapm_widget *dapm_widgets; /* dapm部件指针 */
const struct snd_soc_dapm_route *dapm_routes; /* dapm路由指针 */ /* codec wide operations */
int (*set_sysclk)(...); /* 时钟配置函数 */
int (*set_pll)(...); /* 锁相环配置函数 */ /* codec IO */
unsigned int (*read)(...); /* 读取codec寄存器函数 */
int (*write)(...); /* 写入codec寄存器函数 */
int (*volatile_register)(...); /* 用于判定某一寄存器是否是volatile */
int (*readable_register)(...); /* 用于判定某一寄存器是否可读 */
int (*writable_register)(...); /* 用于判定某一寄存器是否可写 */ /* codec bias level */
int (*set_bias_level)(...); /* 偏置电压配置函数 */ };
2、Codec代码分析:
2.1 找到codec的代码:
{
.name = LPASS_BE_TERT_MI2S_TX,
.stream_name = "Tertiary MI2S Capture",
.cpu_dai_name = "msm-dai-q6-mi2s.2",
.platform_name = "msm-pcm-routing",
.codec_name = MSM8X16_CODEC_NAME,
.codec_dai_name = "msm8x16_wcd_i2s_tx1",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
.be_hw_params_fixup = msm_tx_be_hw_params_fixup,
.ops = &msm8x16_mi2s_be_ops,
.ignore_suspend = ,
},
由dai_link中codec_name,可以知道我们的codec驱动在哪。
由高通Audio中ASOC的machine驱动这篇文章中的匹配并注册相应驱动的那一章分析可知,codec驱动代码就是msm8x16-wcd.c这个文件;
3、查看codec的probe函数:
static int msm8x16_wcd_spmi_probe(struct spmi_device *spmi)
{
int ret = ;
struct msm8x16_wcd *msm8x16 = NULL;
struct msm8x16_wcd_pdata *pdata;
struct resource *wcd_resource;
int modem_state; dev_dbg(&spmi->dev, "%s(%d):slave ID = 0x%x\n",
__func__, __LINE__, spmi->sid); modem_state = apr_get_modem_state();
if (modem_state != APR_SUBSYS_LOADED) {
dev_dbg(&spmi->dev, "Modem is not loaded yet %d\n",
modem_state);
return -EPROBE_DEFER;
} wcd_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, );
if (!wcd_resource) {
dev_err(&spmi->dev, "Unable to get Tombak base address\n");
return -ENXIO;
} switch (wcd_resource->start) {
case TOMBAK_CORE_0_SPMI_ADDR:
msm8x16_wcd_modules[].spmi = spmi;
msm8x16_wcd_modules[].base = (spmi->sid << ) +
wcd_resource->start;
wcd9xxx_spmi_set_dev(msm8x16_wcd_modules[].spmi, );
device_init_wakeup(&spmi->dev, true);
break;
case TOMBAK_CORE_1_SPMI_ADDR:
msm8x16_wcd_modules[].spmi = spmi;
msm8x16_wcd_modules[].base = (spmi->sid << ) +
wcd_resource->start;
wcd9xxx_spmi_set_dev(msm8x16_wcd_modules[].spmi, );
if (wcd9xxx_spmi_irq_init()) {
dev_err(&spmi->dev,
"%s: irq initialization failed\n", __func__);
} else {
dev_dbg(&spmi->dev,
"%s: irq initialization passed\n", __func__);
}
goto rtn;
default:
ret = -EINVAL;
goto rtn;
} dev_dbg(&spmi->dev, "%s(%d):start addr = 0x%pa\n",
__func__, __LINE__, &wcd_resource->start); if (wcd_resource->start != TOMBAK_CORE_0_SPMI_ADDR)
goto rtn; dev_set_name(&spmi->dev, "%s", MSM8X16_CODEC_NAME);
if (spmi->dev.of_node) {
dev_dbg(&spmi->dev, "%s:Platform data from device tree\n",
__func__);
pdata = msm8x16_wcd_populate_dt_pdata(&spmi->dev);
spmi->dev.platform_data = pdata;
} else {
dev_dbg(&spmi->dev, "%s:Platform data from board file\n",
__func__);
pdata = spmi->dev.platform_data;
} msm8x16 = kzalloc(sizeof(struct msm8x16_wcd), GFP_KERNEL);
if (msm8x16 == NULL) {
dev_err(&spmi->dev,
"%s: error, allocation failed\n", __func__);
ret = -ENOMEM;
goto rtn;
} msm8x16->dev = &spmi->dev;
msm8x16->read_dev = __msm8x16_wcd_reg_read;
msm8x16->write_dev = __msm8x16_wcd_reg_write;
ret = msm8x16_wcd_init_supplies(msm8x16, pdata);
if (ret) {
dev_err(&spmi->dev, "%s: Fail to enable Codec supplies\n",
__func__);
goto err_codec;
} ret = msm8x16_wcd_enable_static_supplies(msm8x16, pdata);
if (ret) {
dev_err(&spmi->dev,
"%s: Fail to enable Codec pre-reset supplies\n",
__func__);
goto err_codec;
}
usleep_range(, ); ret = msm8x16_wcd_device_init(msm8x16);
if (ret) {
dev_err(&spmi->dev,
"%s:msm8x16_wcd_device_init failed with error %d\n",
__func__, ret);
goto err_supplies;
}
dev_set_drvdata(&spmi->dev, msm8x16); ret = snd_soc_register_codec(&spmi->dev, &soc_codec_dev_msm8x16_wcd,
msm8x16_wcd_i2s_dai,
ARRAY_SIZE(msm8x16_wcd_i2s_dai));
if (ret) {
dev_err(&spmi->dev,
"%s:snd_soc_register_codec failed with error %d\n",
__func__, ret);
} else {
goto rtn;
}
err_supplies:
msm8x16_wcd_disable_supplies(msm8x16, pdata);
err_codec:
kfree(msm8x16);
rtn:
return ret;
}
msm8x16_wcd_spmi_probe
SPMI总线是高通电源管理的一种规范,也就是通过PMU控制音频(具体我也不够了解,有待以后深入理解)
看看最重要的函数:
ret = snd_soc_register_codec(&spmi->dev, &soc_codec_dev_msm8x16_wcd,
msm8x16_wcd_i2s_dai,
ARRAY_SIZE(msm8x16_wcd_i2s_dai));
此函数通过snd_soc_register_codec函数注册了wcd9320的codec,同时传入了snd_soc_codec_driver和snd_soc_dai_driver结构。
static struct snd_soc_codec_driver soc_codec_dev_msm8x16_wcd = {
.probe = msm8x16_wcd_codec_probe, /*codec驱动的probe函数,由snd_soc_instantiate_card回调*/
.remove = msm8x16_wcd_codec_remove,
.read = msm8x16_wcd_read,
.write = msm8x16_wcd_write,
.suspend = msm8x16_wcd_suspend, /*电源管理*/
.resume = msm8x16_wcd_resume, /*电源管理*/
.readable_register = msm8x16_wcd_readable,
.volatile_register = msm8x16_wcd_volatile,
.reg_cache_size = MSM8X16_WCD_CACHE_SIZE,
.reg_cache_default = msm8x16_wcd_reset_reg_defaults,
.reg_word_size = ,
.controls = msm8x16_wcd_snd_controls,
.num_controls = ARRAY_SIZE(msm8x16_wcd_snd_controls),
.dapm_widgets = msm8x16_wcd_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(msm8x16_wcd_dapm_widgets),
.dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map),
};
static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[] = {
{
.name = "msm8x16_wcd_i2s_rx1",
.id = AIF1_PB,
.playback = {
.stream_name = "AIF1 Playback",
.rates = MSM8X16_WCD_RATES,
.formats = MSM8X16_WCD_FORMATS,
.rate_max = ,
.rate_min = ,
.channels_min = ,
.channels_max = ,
},
.ops = &msm8x16_wcd_dai_ops,
},
{
.name = "msm8x16_wcd_i2s_tx1",
.id = AIF1_CAP,
.capture = {
.stream_name = "AIF1 Capture",
.rates = MSM8X16_WCD_RATES,
.formats = MSM8X16_WCD_FORMATS,
.rate_max = ,
.rate_min = ,
.channels_min = ,
.channels_max = ,
},
.ops = &msm8x16_wcd_dai_ops,
},
};
4、snd_soc_register_codec函数分析:
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
codec->name = fmt_single_name(dev, &codec->id);
codec->write = codec_drv->write;
codec->read = codec_drv->read;
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
codec->writable_register = codec_drv->writable_register;
codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
codec->dapm.seq_notifier = codec_drv->seq_notifier;
codec->dapm.stream_event = codec_drv->stream_event;
codec->dev = dev;
codec->driver = codec_drv;
codec->num_dai = num_dai;
/* register any DAIs */
ret = snd_soc_register_dais(dev, dai_drv, num_dai);
if (ret < ) {
dev_err(codec->dev, "ASoC: Failed to regster DAIs: %d\n", ret);
goto fail_codec_name;
}
它把codec实例链接到全局链表codec_list中:
mutex_lock(&client_mutex);
list_add(&codec->list, &codec_list);
mutex_unlock(&client_mutex);
并且调用snd_soc_instantiate_cards对machine驱动进行一次匹配绑定的操作;

至此,codec的注册就分析完毕。
2. 定义struct snd_soc_codec_driver结构,设置,初始化。
高通ASOC中的codec驱动的更多相关文章
- 高通ASOC中的machine驱动
ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...
- 高通Audio中ASOC的machine驱动(一)
ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...
- 高通LCD的pwm背光驱动
发生异常的现象: msm8953 lcd在快速亮灭的情况下背光概率性休眠不灭:测量高通pwm,发现正常的时候pwm的管脚LCM_BL_PWM为低电平,失败的时候为高电平: 根据原理图: mpp是什么? ...
- Linux ALSA声卡驱动之七:ASoC架构中的Codec
1. Codec简介(ad/da) 在移动设备中,Codec的作用可以归结为4种,分别是: 对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号 对Mic.Linein或者其他输入源的模拟信 ...
- 高通android开发摘要
一部分是开源的,可以从codeaurora.org上下载,还有一部分是高通产权的,需要从高通的网站上下载. 将高通产权的代码放到:vendor/qcom/proprietary 1. 设置bms一些参 ...
- 【转】高通平台android 环境配置编译及开发经验总结
原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...
- 高通adsp架构下sensor
一.高通sensor架构: linux驱动由浅入深系列:高通sensor架构实例分析之一(整体概览+AP侧代码分析) linux驱动由浅入深系列:高通sensor架构实例分析之二(adsp驱动代码结构 ...
- 高通(QCOM)sensor bring up
高通7150平台 1.添加驱动文件 2.添加编译 3.配置json文件 4.高通默认配置 5.部分sensor外挂电源 6.遇到的问题 1.添加驱动文件 路径:adsp_proc/ssc/sensor ...
- 高通、猎户机型Android典型bootloader分析
1.bootloader是什么? 简单地说,bootloader 就是在操作系统内核运行之前运行的一段小程序.通过这段小程序,我们可以初始化硬件设备.建立内存空间的映射图,从而将系统的软硬件环境带到一 ...
随机推荐
- Python中变量和常量的理解
一.变量的定义:把程序运算的中间结果临时存到内存里,以备后面的代码继续调用,这几个名字的学名就叫做"变量". 二.变量的作用:变量用于存储要在计算机程序中引用和操作的信息.它提供了 ...
- Linux指令--cd,pwd
本文参照博客做了修改和总结,感谢作者的分享.http://www.cnblogs.com/peida/archive/2012/10/24/2736501.html cd 命令可以说是Linux中最基 ...
- MyEclipse安装插件
原文地址:http://www.cnblogs.com/pharen/archive/2012/02/08/2343342.html 本文讲解MyEclipse(MyEclipse10)的三种方法,以 ...
- C++——函数重载
C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载,方便使用,便于记忆. /*形参类型不同*/ int add(int x,int y); float add(float x,fl ...
- 节点操作,节点属性的操作及DOM event事件
##1. 节点操作 createElement(标签名) 创建一个指定名称的元素 someone.appendChild(new_node) 追加一个子节点(作为最后的子节点) someone.ins ...
- null undefiend NaN
console.log(typeof NaN) console.log(typeof undefined) console.log(typeof null)
- JMS基础篇
首先我们需要下载 ActiveMQ:http://activemq.apache.org/. 启动 ActiveMQ 服务:解包下载的 ActiveMQ >进去其bin 目录>双击 act ...
- MySQL5学习笔记(三)
系统变量 mysqld服务器维护两种变量.全局变量(GLOBAL)影响服务器整体操作.会话变量(SESSION)影响具体客户端连接的操作. 当服务器启动时,它将所有全局变量初始化为默认值.这些 ...
- BZOJ 3993: [SDOI2015]星际战争 [二分答案 二分图]
3993: [SDOI2015]星际战争 题意:略 R1D2T1考了裸二分答案+二分图最大匹配... #include <iostream> #include <cstdio> ...
- BZOJ 3809: Gty的二逼妹子序列 & 3236: [Ahoi2013]作业 [莫队]
题意: 询问区间权值在$[a,b]$范围内种类数和个数 莫队 权值分块维护种类数和个数$O(1)-O(\sqrt{N})$ #include <iostream> #include < ...