程序总体结构
以sound/soc/pxa/corgi.c为例来分析
- 为了描述声卡,定义snd_soc_card实例,填充dai_link成员。
- 为了能在ALSA上层控制SPK,HP,MIC的开启关闭,定义kcontrol实例。
- 为了能让ALSA自动控制SPK,HP,MIC的功耗,定义widget,route实例。
- 在snd_soc_card--dai_link--init里,注册上面声明的kcontrol,widget,route实例。
下面以SPK的kcontrol,widget,route为例,分析三种数据结构的定义
kcontrol的实现
定义kcontrol数组,提供向上的参数和向下的实际控制函数 。
- 向上的参数,用于上层amixer程序设置控件为不同的功能。
- 向下的实际控制函数,用于底层驱动程序控制HP MIC SPK的开关。
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", "Off"};
static const char *spk_function[] = {"On", "Off"};
static const struct soc_enum corgi_enum[] = {
SOC_ENUM_SINGLE_EXT(5, jack_function),//控件参数1-4
SOC_ENUM_SINGLE_EXT(2, spk_function),//控件参数0-1
};
static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
corgi_set_jack),//get,set分别读取和设置硬件工作模式
SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
corgi_set_spk),
};
get,set函数的实现
static int corgi_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = corgi_spk_func; //全局变量为0-1表示ON OFF
return 0;
}
static int corgi_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (corgi_spk_func == ucontrol->value.integer.value[0])
return 0;
corgi_spk_func = ucontrol->value.integer.value[0];//保存上层设置的参数
corgi_ext_control(codec);//设置SPK为ON OFF模式
return 1;
}
//corgi_ext_control函数的实现
static void corgi_ext_control(struct snd_soc_codec *codec)
{
if (corgi_spk_func == CORGI_SPK_ON)
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
//通过enable widget,开启电源,widget的实现在后面描述
else
snd_soc_dapm_disable_pin(dapm, "Ext Spk");//通过disable widget,关闭电源
/* signal a DAPM event */
snd_soc_dapm_sync(dapm);
}
snd_soc_card->dai_link->init里,通过snd_soc_add_controls注册control。
widget的实现
//定义widget
/* corgi machine dapm widgets */
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),//定义widget名字,设置电源控制接口
};
//电源控制接口的实现
static int corgi_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
//通过GPIO开关SPK的前置放大器
gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
snd_soc_card->dai_link->init里,通过snd_soc_dapm_new_controls注册widget。
route的实现
//定义route
/* Corgi machine audio map (connections to the codec pins) */
static const struct snd_soc_dapm_route audio_map[] = {
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},//设置SPK直连codec的ROUT和LOUT
{"Ext Spk", NULL, "LOUT"},
};
snd_soc_card->dai_link->init里,通过snd_soc_dapm_add_routes注册route。
从datesheet上,可以看到ROUT和LOUT是用来外接SPK的。
从codec驱动里,可以看到ROUT,LOUT的widget定义。
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
};
文章上没有添加附件选项,将machine驱动,codec驱动和手册添加到了链接:
http://download.csdn.net/download/a903265446/10235150
一般产品开发,codec驱动和platform驱动由芯片厂商提供,只需要开发machine驱动。
本文由头条号“嵌入式FM106点1”发布,各种原创技术干货,欢迎关注。