您的位置:首页 > 产品设计 > 产品经理

Asoc dapm(四) - dapm widgets & dapm route注册

2015-12-05 19:12 656 查看

dapm widgets和dapm route数组

codec driver那边一般都有dapm widgets,dapm route数组,platform driver和machine driver那边可能也会有dapm widgets,dapm route数组。

有两种方式可以注册dapm widgets和dapm route数组

1) 可以通过在struct snd_soc_codec_driver soc_codec_dev_rt5658结构体中初始化dapm widgets数组和dapm route数组,如下:

static struct snd_soc_codec_driver soc_codec_dev_rt5658 = {
.probe = rt5658_probe,
.remove = rt5658_remove,
.suspend = rt5658_suspend,
.resume = rt5658_resume,
.set_bias_level = rt5658_set_bias_level,
.idle_bias_off = true,
.controls = rt5658_snd_controls,
.num_controls = ARRAY_SIZE(rt5658_snd_controls),
.dapm_widgets = rt5658_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rt5658_dapm_widgets),
.dapm_routes = rt5658_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5658_dapm_routes),
};


2) 也可以在struct snd_soc_codec_driver的probe函数中注册

static int rt5645_probe(struct snd_soc_codec *codec)
{
...
snd_soc_add_codec_controls(codec, rt5645_snd_controls,
ARRAY_SIZE(rt5645_snd_controls));
snd_soc_dapm_new_controls(&codec->dapm, rt5645_dapm_widgets,
ARRAY_SIZE(rt5645_dapm_widgets));
snd_soc_dapm_add_routes(&codec->dapm, rt5645_dapm_routes,
ARRAY_SIZE(rt5645_dapm_routes));
...
}

static struct snd_soc_codec_driver soc_codec_dev_rt5645 = {
.probe = rt5645_probe,
.remove = rt5645_remove,
.suspend = rt5645_suspend,
.resume = rt5645_resume,
.set_bias_level = rt5645_set_bias_level,
.reg_cache_size = RT5645_VENDOR_ID2 + 1,
.reg_word_size = sizeof(u16),
.reg_cache_default = rt5645_reg,
.volatile_register = rt5645_volatile_register,
.readable_register = rt5645_readable_register,
.reg_cache_step = 1,
};


方式一虽然没有显示的调用snd_soc_dapm_new_controls和snd_soc_dapm_add_routes注册dapm widgets和dapm route,但是在将struct snd_soc_codec_driver注册到asoc core里的时候会通过这两个函数注册。

snd_soc_dapm_new_control

static struct snd_soc_dapm_widget *
snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
size_t name_len;
int ret;

if ((w = dapm_cnew_widget(widget)) == NULL)
return NULL;

switch (w->id) {
case snd_soc_dapm_regulator_supply:
w->regulator = devm_regulator_get(dapm->dev, w->name);
if (IS_ERR(w->regulator)) {
ret = PTR_ERR(w->regulator);
dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
w->name, ret);
return NULL;
}

if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
ret = regulator_allow_bypass(w->regulator, true);
if (ret != 0)
dev_warn(w->dapm->dev,
"ASoC: Failed to unbypass %s: %d\n",
w->name, ret);
}
break;
case snd_soc_dapm_clock_supply:
#ifdef CONFIG_CLKDEV_LOOKUP
w->clk = devm_clk_get(dapm->dev, w->name);
if (IS_ERR(w->clk)) {
ret = PTR_ERR(w->clk);
dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
w->name, ret);
return NULL;
}
#else
return NULL;
#endif
break;
default:
break;
}

name_len = strlen(widget->name) + 1;
if (dapm->codec && dapm->codec->name_prefix)
name_len += 1 + strlen(dapm->codec->name_prefix);
w->name = kmalloc(name_len, GFP_KERNEL);
if (w->name == NULL) {
kfree(w);
return NULL;
}
if (dapm->codec && dapm->codec->name_prefix)
snprintf((char *)w->name, name_len, "%s %s",
dapm->codec->name_prefix, widget->name);
else
snprintf((char *)w->name, name_len, "%s", widget->name);

switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_out:
w->power_check = dapm_adc_check_power;
break;
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_dai_in:
w->power_check = dapm_dac_check_power;
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_dai_link:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
w->power_check = dapm_supply_check_power;
break;
default:
w->power_check = dapm_always_on_check_power;
break;
}

w->dapm = dapm;
w->codec = dapm->codec;
w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets);

/* machine layer set ups unconnected pins and insertions */
w->connected = 1;
return w;
}


在这个函数中主要做了以下几件事:

1) 初始化struct snd_soc_dapm_widget *w

2) 如果w->id为snd_soc_dapm_regulator_supply或者snd_soc_dapm_clock_supply则获取w->regulator和w->clk,以便后面对regulator和clk进行设置。

3) 设置w->power_check回调函数

4) list_add(&w->list, &dapm->card->widgets);将此widget添加到card->widgets链表中

snd_soc_dapm_add_route

static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
const char *sink;
const char *control = route->control;
const char *source;
char prefixed_sink[80];
char prefixed_source[80];
int ret = 0;

if (dapm->codec && dapm->codec->name_prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
dapm->codec->name_prefix, route->sink);
sink = prefixed_sink;
snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
dapm->codec->name_prefix, route->source);
source = prefixed_source;
} else {
sink = route->sink;
source = route->source;
}

/*
* find src and dest widgets over all widgets but favor a widget from
* current DAPM context
*/
list_for_each_entry(w, &dapm->card->widgets, list) {
if (!wsink && !(strcmp(w->name, sink))) {
wtsink = w;
if (w->dapm == dapm)
wsink = w;
continue;
}
if (!wsource && !(strcmp(w->name, source))) {
wtsource = w;
if (w->dapm == dapm)
wsource = w;
}
}
/* use widget from another DAPM context if not found from this */
if (!wsink)
wsink = wtsink;
if (!wsource)
wsource = wtsource;

if (wsource == NULL) {
dev_err(dapm->dev, "ASoC: no source widget found for %s\n",
route->source);
return -ENODEV;
}
if (wsink == NULL) {
dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",
route->sink);
return -ENODEV;
}

path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path)
return -ENOMEM;

path->source = wsource;
path->sink = wsink;
path->connected = route->connected;
INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);

/* check for external widgets */
if (wsink->id == snd_soc_dapm_input) {
if (wsource->id == snd_soc_dapm_micbias ||
wsource->id == snd_soc_dapm_mic ||
wsource->id == snd_soc_dapm_line ||
wsource->id == snd_soc_dapm_output)
wsink->ext = 1;
}
if (wsource->id == snd_soc_dapm_output) {
if (wsink->id == snd_soc_dapm_spk ||
wsink->id == snd_soc_dapm_hp ||
wsink->id == snd_soc_dapm_line ||
wsink->id == snd_soc_dapm_input)
wsource->ext = 1;
}

/* connect static paths */
if (control == NULL) {
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
return 0;
}

/* connect dynamic paths */
switch (wsink->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_siggen:
case snd_soc_dapm_micbias:
case snd_soc_dapm_vmid:
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_dai_link:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
return 0;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
&wsink->kcontrol_news[0]);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 0;
return 0;
}

dapm_mark_dirty(wsource, "Route added");
dapm_mark_dirty(wsink, "Route added");

return 0;

err:
dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n",
source, control, sink);
kfree(path);
return ret;
}


这个函数做了以下几件事:

1) 根据route中source和sink的name在card->widgets链表中找到对应的source widget和sink widget,并分别赋值给path的wsource和wsink赋值。

2) 对于没有kcontrol的path

将path添加到card->paths链表中

将path添加到wsink->sources链表中

将path添加到wsource->sinks链表中

之后就直接返回了,比后面有kcontrol的path少执行两个函数dapm_mark_dirty(wsource, “Route added”);和dapm_mark_dirty(wsink, “Route added”);

3) 对于有kcontrol的path,同样

将path添加到card->paths链表中

将path添加到wsink->sources链表中

将path添加到wsource->sinks链表中

但是在这里,mux和mixer比较特别,分别会调用dapm_connect_mux和dapm_connect_mixer,这两个函数除了像上面一样需要添加path到card->path, wsink->sources,wsource->sinks链表中之外,还会执行dapm_set_path_status函数,dapm_set_path_status读取codec寄存器的值,并更新path的connect状态

snd_soc_dapm_new_widgets

函数snd_soc_dapm_new_widgets和函数snd_soc_dapm_new_controls在名字上有点让人混淆,在前面所讲的snd_soc_dapm_new_controls其实是添加dapm_widgets到card->widgets链表中,而这里的snd_soc_dapm_new_widgets主要作用是将这个dapm_widgets中的kcontrol添加card->kcontrol链表中,采用和之前添加普通kcontrol一样的方法snd_ctl_add来添加。

int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_dapm_widget *w;
unsigned int val;

mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);

list_for_each_entry(w, &dapm->card->widgets, list)
{
if (w->new)
continue;

if (w->num_kcontrols) {
w->kcontrols = kzalloc(w->num_kcontrols *
sizeof(struct snd_kcontrol *),
GFP_KERNEL);
if (!w->kcontrols) {
mutex_unlock(&dapm->card->dapm_mutex);
return -ENOMEM;
}
}

switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(w);
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w);
break;
default:
break;
}

/* Read the initial power state from the device */
if (w->reg >= 0) {
val = soc_widget_read(w, w->reg);
val &= 1 << w->shift;
if (w->invert)
val = !val;

if (val)
w->power = 1;
}

w->new = 1;

dapm_mark_dirty(w, "new widget");
dapm_debugfs_add_widget(w);
}

dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
mutex_unlock(&dapm->card->dapm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);


这个函数还是比较好理解的

1) 遍历card->widgets链表中的所有widget,对于每个widget,mixer或者mux分别会调用dapm_new_mixer(w);和dapm_new_mux(w);来讲widget下面的所有kcontrol添加到card->kcontrol链表中

2) 调用dapm_power_widgets统一处理widgets链表上的widget状态的改变

附上几篇文章的链接

1. Asoc dapm(一) - kcontrol

2. Asoc dapm(二) - kcontrol注册与使用

3. Asoc dapm(三) - dapm widgets & dapm kcontrol & dapm route

4. Asoc dapm(四) - dapm widgets & dapm route注册

5. Asoc dapm(五) - dapm widget链表更新
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: