- 论坛徽章:
- 0
|
内核ASoC之path
http://www.alivepea.me/kernel/asoc-path/
ASoC的DAPM是怎么做到需要用户空间参与功耗最小的呢?以codec为例,它运行时的功耗 主要取决于:1. codec供电电压越低,功耗越低; 2. 将codec中没有使用的部件widget 下电禁用。第1点和硬件设计有关,第2点就是DAPM最重要的工作内容了。那它是怎么做 到呢?
widget与path
下图是WM8750的框图。
如上图,speaker右声道播放音频流时,流经与DAPM相关的widget: ①snd_soc_dapm_dai_in. ②snd_soc_dapm_dac. ③snd_soc_dapm_mixer. ④snd_soc_dapm_pga. ⑤snd_soc_dapm_spk.
speaker播放音频时的通路Route:①→②→③→④→⑤. 在这条通路上的widget上电, 不在这条通路上的widget,即没有使用到的,都需要下电禁用。
如果这时插入耳机,用户希望声音从耳机出来,会怎么样呢?如下图。
上图中widget:⑥snd_soc_dapm_pga. ⑦snd_soc_hp.
在耳机播放播放右声道音频流的通路Route:①→②→③→⑥→⑦. 在给widget⑥和⑦上 电之前,还要先让widget④和⑤下电。
各部件widget之间的连通关系通过path表示,如下图。
widget通过sinks链接上的path遍历下一个sink widget,通过sources链接上的path遍 历上一个source widget. 判断一个widget是否在活动的通路上,就是判断该widget能 否遍历到音频输入端点和输出端点.如上图speaker播放时,输入端点是①,输出端点是 ⑤;earphone播放时,输入端点是①,输出端点是⑦.
实现
下面是代码is_connect_output_ep()的解析,用于判断某个widget能否连通到输出端 点。
static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
struct snd_soc_dapm_widget_list **list)
{
struct snd_soc_dapm_path *path;
int con = 0;
if (widget->outputs >= 0)
return widget->outputs;
DAPM_UPDATE_STAT(widget, path_checks);
switch (widget->id) { /* 这几个部件都不可能连通到输出端点 */
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
return 0;
default:
break;
}
switch (widget->id) { /* 录制时的输出端点 */
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_out:
if (widget->active) { /* 只有substream打开后,才能成为活动的输出端点 */
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
}
default:
break;
}
if (widget->connected) { /* 可插拨的widget如earphone是否在板上 */
/* connected pin ? */
if (widget->id == snd_soc_dapm_output && !widget->ext) { /* 没有外接widget的output也是输出端点 */
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
}
/* connected jack or spk ? */
if (widget->id == snd_soc_dapm_hp ||
widget->id == snd_soc_dapm_spk ||
(widget->id == snd_soc_dapm_line &&
!list_empty(&widget->sources))) { /* hp,spk,line都是输出端点 */
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
}
}
list_for_each_entry(path, &widget->sinks, list_source) { /* 枚举像mixer/mux widget的下一个widget */
DAPM_UPDATE_STAT(widget, neighbour_checks);
if (path->weak) /* 需要略过的路径 */
continue;
if (path->walking) /* 回环的路径 */
return 1;
if (path->walked) /* 已遍历的路径 */
continue;
trace_snd_soc_dapm_output_path(widget, path);
if (path->sink && path->connect) { /* 对于mixer/mux部件来说,如果当前的path是连通的,继续向下遍历 */
path->walked = 1;
path->walking = 1;
/* do we need to add this widget to the list ? */
if (list) {
int err;
err = dapm_list_add_widget(list, path->sink); /* 保存遍历轨迹 */
if (err < 0) {
dev_err(widget->dapm->dev,
"ASoC: could not add widget %s\n",
widget->name);
path->walking = 0;
return con;
}
}
con += is_connected_output_ep(path->sink, list); /* 进入下一个sink widget 继续遍历 */
path->walking = 0;
}
}
widget->outputs = con;
return con; /* 返回遍历输出端点的连通数*/
}
view rawis_connected_output_ep.c hosted with ❤ by GitHub
上面的代码其实就是特定有向图的路径深度优先的搜索算法。当没有音频在播放时,由 于widget①的active状态为0,不是输入端点,整个codec就没有活动通路,所有的 widget都在下电状态。 当speaker播放时,耳机没有插入时,snd_soc_dapm_headphone 的connected状态为0,不能被当作活动通路上的输出端点,widgets⑥和⑦仍处下电状态。 当耳机插入后,如果用户不希望声音同时从headphone/speaker同时出来,设定speaker 的widget->connected后,会自动将widget④和⑤下电,⑥和⑦上电。
判断是否连通到输入端点的代码is_connect_input_ep()与上面很相似,就略过了。
总结
DAPM通过对widget建立path,在不需要用户的干预下根据不同的通路让codec处在功耗最 小的工作状态。在注意的是,除了播放时插拨耳机改变通路外,还有播放时,用户通过 切换mixer/mux这种widgets,也会改变通路。
~EOF~
|
|