高通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之数学(math)和随机数(random)
math包包含了最基本的数学运算函数,如果想要更加高级的数学功能,可以使用标准库外的numpy和scipy库,他们不但支持数组和矩阵运算, 还有丰富的数学和物理方程可供使用 random包可以用来生成 ...
- 使用express+multer实现node中的图片上传
使用express+multer实现node中的图片上传 在前端中,我们使用ajax来异步上传图片,使用file-input来上传图片,使用formdata对象来处理图片数据,post到服务器中 在n ...
- MS SQL 日志记录管理
MS SQL的日志信息/日志记录,可能对你来说,既熟悉又陌生,熟悉是因为你可能一直都在使用,查看.关注一些日志信息/记录,例如,作业历史记录:陌生是因为你可能从不关注日志信息/记录的管理,这里我一直用 ...
- 安卓studio导入jra包和so包,百度地图so包加载
导入so包 这个我只接受测试可用的一种方法 第一步:把so包放在libs目录下,可以是文件夹也可以是单独的一个个so文件 然后在src同级的目录下找到build.gradle文件下如下信息 sourc ...
- Not Found The requested URL / was not found on this server.
http://www.wanysys.cc/coding/php/800.html 今天在做本地PHP项目的时候,想把之前wampserver的本地虚拟服务器环境访问方式改为本地localhost访问 ...
- Tomcat(四):tomcat图形管理和身份认证
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- tomcat无法打开8080页面
tomcat已启动 app已经正常执行 但不能打开8080管理页面 可能是在webapps目录下没有ROOT目录
- TCP/IP详解 卷1 第二十一章 TCP的超时与重传
21.1 引言 可靠性的保证之一就是超时重传 前面两个超时重传的例子 1) ICMP端口不能到达时,TFTP客户使用UDP实现了一个简单的超时和重传机制,假定5s是一个适当是时间间隔,并每隔5s进行 ...
- redis 简易监控的几种方法
简介 针对Redis 实现性能监控的几种方法 一.使用info命令 命令说明 127.0.0.1:6380> info # Server redis_version:3.2.11 redis_g ...
- JFinal源码 分析之 Core包分析
ActionHandler.java 这个类继承了上面 说的Handler类,首先我们 上 几个属性 ,下面几个 属性我们 需要 关心哪些东西 呢?首先 是ActionMapping和RenderMa ...