DPAM是什么?
1)DAPM是Dynamic Audio Power的缩写,直译过来就是动态音频电源管理的意思。
2)DAPM是为了急于Linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。
3)DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都工作在ASoc Core中完成。
4)DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或者关闭。
5)DPAM包括route,path和widget。
LINPUT
说明:
如红色框所示,需要经过一个开关,一个放大器,一个开关和混合器。
简化图
说明:要使用LINPUT1的录音功能,需要打开如红色框所示的开关。
wdiget
场景需求:
在打开LINPUT1 Switch开关时,同时打开Left Boost Mixer,便产生了widget这个功能。
说明:
1)mixer有多个输入源,只要其中的某个开关使能,顺便把mixer也使能。
2)使用一个widget来表示上图的结构,则widget组成如下:
一个mixer和3个开关;开关用kcontrol表示,kcontrol中的put函数不仅设置开关的reg,顺便设置mixer的reg。
代码如下:
271 SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
272 wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
224 static const struct snd_kcontrol_new wm8960_lin_boost[] = {
225 SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
226 SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
227 SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
228 };
说明:
1)就是图中的3个LINPUT Switch和Left Boost Mixer组成一个widget。
2)普通的snd_kcontrol_new通过SOC_SINGLE声明;DPAM的snd_kcontrol_new通过SOC_DAPM_SINGLE声明。
route
场景:
当LINPUT1 switch打开时,LINPUT1应该打开,Left Boost Mixer也应该打开,LINPUT1怎么知道左右连接了那些widget?这时候就需要route,驱动定义一个snd_soc_route结构数组,数组的条目描述了目的widget和源widget的名称,以及这个kcontrol的名称。
说明:
1)蓝色框LINPUT1 Switch是kcontrol,红色框是两个 wdiget,绿色框是一个route.
2)route的格式:
{sink,kcontrol,source}
261 static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
262 SND_SOC_DAPM_INPUT("LINPUT1"),
268
269 SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
270
271 SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
272 wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
224 static const struct snd_kcontrol_new wm8960_lin_boost[] = {
225 SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
226 SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
227 SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
228 };
323 static const struct snd_soc_dapm_route audio_paths[] = {
324 { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
.....
}
说明:代码如上,结合图,LINPUT1,LINPUT1 Switch和Left Boost Mixer组成一个root。
path的创建
1)widget没有连接,wdiget之间使用snd_soc_path结构进行连接的,驱动 使用snd_soc_dapm_add_routes来注册连接信息。
2)根据route中各个name,创建path。
sound/soc/codecs/wm8960.c中
386 static int wm8960_add_widgets(struct snd_soc_codec *codec)
387 {
388 struct wm8960_data *pdata = codec->dev->platform_data;
389 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
390 struct snd_soc_dapm_context *dapm = &codec->dapm;
391 struct snd_soc_dapm_widget *w;
392
393 snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
394 ARRAY_SIZE(wm8960_dapm_widgets));
395
396 snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
}
1810 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
1811 const struct snd_soc_dapm_route *route, int num)
1812 {
1813 int i, ret;
1814
1815 for (i = 0; i < num; i++) {
1816 ret = snd_soc_dapm_add_route(dapm, route);
1817 if (ret < 0) {
1818 dev_err(dapm->dev, "Failed to add route %s->%s\n",
1819 route->source, route->sink);
1820 return ret;
1821 }
1822 route++;
1823 }
1824
1825 return 0;
1826 }
说明:
在396行中,进入snd_soc_dapm_add_routes中,在1816行进入snd_soc_dapm_add_route
1656 static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
1657 const struct snd_soc_dapm_route *route)
1658 {
1659 struct snd_soc_dapm_path *path;
1660 struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
1661 struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
1662 const char *sink;
1663 const char *control = route->control;
1664 const char *source;
1665 char prefixed_sink[80];
1666 char prefixed_source[80];
1667 int ret = 0;
1668
1669 if (dapm->codec && dapm->codec->name_prefix) {
1670 snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
1671 dapm->codec->name_prefix, route->sink);
1672 sink = prefixed_sink;
1673 snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
1674 dapm->codec->name_prefix, route->source);
1675 source = prefixed_source;
1676 } else {
1677 sink = route->sink;
1678 source = route->source;
1679 }
1680
1681 /*
1682 * find src and dest widgets over all widgets but favor a widget from
1683 * current DAPM context
.....
1778 case snd_soc_dapm_hp:
1779 case snd_soc_dapm_mic:
1780 case snd_soc_dapm_line:
1781 case snd_soc_dapm_spk:
1782 list_add(&path->list, &dapm->card->paths);
1783 list_add(&path->list_sink, &wsink->sources);
1784 list_add(&path->list_source, &wsource->sinks);
1785 path->connect = 0;
1786 return 0;
1787 }
1788 return 0;
1789
1790 err:
1791 dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s\n",
1792 source, control, sink);
1793 kfree(path);
1794 return ret;
1795 }
说明:创建struct snd_soc_dapm_path指针,然后把audio里面的sink,kcontrol和source创建route。
结构体的定义:
420 struct snd_soc_dapm_path {
421 char *name;
422 char *long_name;
423
424 /* source (input) and sink (output) widgets */
425 struct snd_soc_dapm_widget *source;
426 struct snd_soc_dapm_widget *sink;
427 struct snd_kcontrol *kcontrol;
428
429 /* status */
430 u32 connect:1; /* source and sink widgets are connected */
431 u32 walked:1; /* path has been walked */
432
433 int (*connected)(struct snd_soc_dapm_widget *source,
434 struct snd_soc_dapm_widget *sink);
435
436 struct list_head list_source;
437 struct list_head list_sink;
438 struct list_head list;
439 };
结构体snd_soc_dapm_path定义如下:
419 /* dapm audio path between two widgets */
420 struct snd_soc_dapm_path {
421 char *name;
422 char *long_name;
423
424 /* source (input) and sink (output) widgets */
425 struct snd_soc_dapm_widget *source;
426 struct snd_soc_dapm_widget *sink;
427 struct snd_kcontrol *kcontrol;
428
429 /* status */
430 u32 connect:1; /* source and sink widgets are connected */
431 u32 walked:1; /* path has been walked */
432
433 int (*connected)(struct snd_soc_dapm_widget *source,
434 struct snd_soc_dapm_widget *sink);
435
436 struct list_head list_source;
437 struct list_head list_sink;
438 struct list_head list;
439 };
kcontrol的put函数的动作
1968 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1969 struct snd_ctl_elem_value *ucontrol)
1970 {
1971 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
1972 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
1973 struct snd_soc_codec *codec = widget->codec;
1974 struct soc_mixer_control *mc =
1975 (struct soc_mixer_control *)kcontrol->private_value;
1976 unsigned int reg = mc->reg;
1977 unsigned int shift = mc->shift;
1978 int max = mc->max;
1979 unsigned int mask = (1 << fls(max)) - 1;
1980 unsigned int invert = mc->invert;
1981 unsigned int val;
1982 int connect, change;
1983 struct snd_soc_dapm_update update;
1984 int wi;
1985
1986 val = (ucontrol->value.integer.value[0] & mask);
1987
1988 if (invert)
1989 val = max - val;
1990 mask = mask << shift;
1991 val = val << shift;
1992
1993 if (val)
1994 /* new connection */
1995 connect = invert ? 0 : 1;
1996 else
1997 /* old connection must be powered down */
1998 connect = invert ? 1 : 0;
1999
2000 mutex_lock(&codec->mutex);
2001
2002 change = snd_soc_test_bits(widget->codec, reg, mask, val);
2003 if (change) {
2004 for (wi = 0; wi < wlist->num_widgets; wi++) {
2005 widget = wlist->widgets[wi];
2006
2007 widget->value = val;
2008
2009 update.kcontrol = kcontrol;
2010 update.widget = widget;
2011 update.reg = reg;
2012 update.mask = mask;
2013 update.val = val;
2014 widget->dapm->update = &update;
2015
2016 dapm_mixer_update_power(widget, kcontrol, connect);
2017
2018 widget->dapm->update = NULL;
2019 }
2020 }
1470 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
1471 struct snd_kcontrol *kcontrol, int connect)
1472 {
1473 struct snd_soc_dapm_path *path;
1474 int found = 0;
1475
1476 if (widget->id != snd_soc_dapm_mixer &&
1477 widget->id != snd_soc_dapm_mixer_named_ctl &&
1478 widget->id != snd_soc_dapm_switch)
1479 return -ENODEV;
1480
1481 /* find dapm widget path assoc with kcontrol */
1482 list_for_each_entry(path, &widget->dapm->card->paths, list) {
1483 if (path->kcontrol != kcontrol)
1484 continue;
1485
1486 /* found, now check type */
1487 found = 1;
1488 path->connect = connect;
1489 break;
1490 }
1491
1492 if (found)
1493 dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
1494
1495 return 0;
1496 }
kcontrol的put函数的作用:
1)在1995行设置connected
2)在1493行使用dapm_power_widgets函数进行设置。
a. 无人使用声卡,不设置,不启动任何寄存器。
b. 有app使用声卡,也许会设置寄存器。
总结
总结:
a. tinymix设置普通的kcontrol:会直接设置寄存器
b. tinymix设置DAPM的kcontrol:设置所在path的connect,
调用dapm_power_widgets(widget>dpam,SND_SOC_DAPM_STREAM_NOP);
c. tinyplay,tinycap在传输数据之前,调用dapm_power_widgets(dapm,event)
d. dapm_power_widgets在有app使用声卡的前提下会找出complete_path,
设置上面的所有widget。
说明:tinymix命令没有使用声卡,tinyplay和tinycap使用声卡。
complete path
说明:
1)如上图所示,path1,path2和path3组成一个complete path。
2)complete path上各个path都是connect状态,并且有app使用声卡,才会启动所涉及的寄存器或者叫widget。
3)complete path上各个path都是connect状态,并且有app使用声卡,才会启动所涉及的寄存器或者叫widget。
录音步骤
- 使用tinymix打开LINPUT1 Switch和Boost Switch开关。
- 然后使用tinycap录音,tiny会打开complete路上的widget。
说明:
需要集合更多实验例子,才能说明流程。