高通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 就是在操作系统内核运行之前运行的一段小程序.通过这段小程序,我们可以初始化硬件设备.建立内存空间的映射图,从而将系统的软硬件环境带到一 ...
随机推荐
- CDuiString和String的转换
很多时候 难免用到CDuiString和string的转换. 我们应该注意到,CDuiString类有个方法: LPCTSTR GetData() const; 可以通过这个方法,把CDuiStrin ...
- Windows脚本相关
1 获取IP地址 echo StartChangeIPFile echo 获取主机名 for /f %%i in ('hostname') do (set pcName=%%i) ::ping %pc ...
- linkin大话面向对象--初始化块
java使用构造器来对单个对象进行初始化操作,使用构造器先完成整个java对象的状态初始化,然后将java对象返回给程序,从而让整个java对象的信息更加完整.与构造器作用非常类似的是初始化块,它也可 ...
- SCOPE_IDENTITY()
@@IDENTYITY,SCOPE_IDENTITY的主要区别:在有触发器中而且触发器的内容里面含有插入标识符的操作的时候,@@IDENTITY则返回的是触发器里面新插入标识符的值而SCOPE_IDE ...
- 根据URL下载图片至客户端、服务器实例
1.保存至服务器 根据路径保存至项目所在服务器上. String imgUrl="";//图片地址 try { // 构造URL URL url = new URL(imgUrl) ...
- 【php】RBAC 管理权限
用户 角色 权限 用户:张三 角色:管理员 权限:page/index1.php 能访问的页面
- ubuntu网桥设置
什么是桥接? 桥接(Bridging),是指依据OSI网络模型的链路层的地址,对网络数据包进行转发的过程,工作在OSI的第二层.一般的交换机,网桥就有桥接作用. 一般的交换机,网桥就有桥接作用.就交换 ...
- get请求中文乱码及get,post编码探究
在我使用get请求进行查询的时候遇到一个问题: 当我的请求参数中有中文时,出现乱码. 可是即使我设置了Spring的characterEncodingFilter,也还是出现乱码. 原因:tomcat ...
- vue项目中关于axios的简单使用
axios介绍 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中 官方仓库:https://github.com/axios/axios 中文文档:htt ...
- spring之AspectJ基于xml AOP编程
一.引言: AspectJ框架不仅实现了面向切面编程,而且还支持注解,spring将它引入自己的规范之中. 二.需要了解: AspectJ是基于java语言的AOP框架 spring2.0之后支持As ...