In the last lecture of this free Linux kernel development course we built a plain
ALSA mixer control array for our example codec and registered it with
snd_soc_add_component_controls(). That approach works for something a user turns on
or off by hand, like a volume slider. But real audio codecs power dozens of internal
blocks — amplifiers, ADCs, DACs, mixers — and those blocks should only draw current when
they are actually part of an active audio path. This lecture introduces ALSA DAPM widgets,
the graph-based power model that the ASoC (ALSA System on Chip) layer uses to answer that
exact question automatically, and shows how to describe a codec’s internal audio paths with
widgets and routes on a modern kernel.
What You Will Learn
struct snd_soc_dapm_widget
SND_SOC_DAPM_* macros
struct snd_soc_dapm_route
Widget graph power-up/down
Registering widgets on a component
Verifying DAPM state from userspace
Prerequisites
This lecture continues directly from lecture 6 of this ALSA SoC series, so you should
already be comfortable with struct snd_soc_component_driver, ordinary ALSA mixer
controls built with the SOC_SINGLE/SOC_DOUBLE_R family of macros, and how a
component is registered with devm_snd_soc_register_component(). General familiarity
with kernel platform drivers and probe/remove callbacks from earlier chapters of this free
embedded Linux course is assumed.
Why A Kcontrol Array Is Not Enough
A mixer control changes a register value. It has no idea whether the block it controls is
currently part of a live audio stream. If your codec has an internal microphone
pre-amplifier, that amplifier only needs to be powered while a capture stream is running
and the microphone input is actually routed to it. Powering it all the time wastes current;
leaving the decision to userspace is fragile and inconsistent across boards. ALSA DAPM
(Dynamic Audio Power Management) solves this by modelling every functional block inside the
codec — and every physical pin on the board — as a node in a directed graph. Each node is a
DAPM widget. The edges between widgets are DAPM routes. Whenever a stream starts, stops, or
a mixer/mux control changes, the ASoC core walks this graph from every active endpoint and
powers up exactly the widgets that lie on a live path, and powers down everything else.
This is the same DAPM machinery that decides, for example, that your laptop’s speaker
amplifier can stay off while you are only using headphones.
The DAPM Widget Structure
On a current kernel the widget is represented by struct snd_soc_dapm_widget,
declared in include/sound/soc-dapm.h. You rarely fill this structure by hand — the
framework gives you a set of macros that build one entry at a time inside a static array.
The fields that matter for driver authors are the widget’s id (its type, such as
mixer, ADC, or output pin), its name (used both internally for routing and by
userspace tools like amixer and alsamixer), the register/bit pair that controls
its power state when the widget is register-backed, and an optional event callback
that fires when the widget is about to power up or has just powered down.
Widgets fall into a small number of practical categories that you will use in almost
every codec driver:
- Pins —
SND_SOC_DAPM_INPUTandSND_SOC_DAPM_OUTPUTrepresent physical
pins on the codec package itself. - Board-level endpoints —
SND_SOC_DAPM_MIC,SND_SOC_DAPM_SPK,
SND_SOC_DAPM_HP, andSND_SOC_DAPM_LINErepresent the physical mic, speaker,
headphone, and line jacks that the machine driver connects to. - Signal-path blocks —
SND_SOC_DAPM_PGAfor a programmable gain amplifier,
SND_SOC_DAPM_MIXERfor an internal analog/digital mixer,SND_SOC_DAPM_MUXfor a
selector,SND_SOC_DAPM_ADCandSND_SOC_DAPM_DACfor the converters themselves. - Supplies —
SND_SOC_DAPM_SUPPLYfor things like a bias voltage or clock that
several other widgets depend on but which is not itself part of the audio signal path.
Describing A Codec’s Audio Path: Widgets
Let’s extend the example codec from the previous lecture with a minimal capture and
playback path: a mic pin feeding a PGA, into an ADC, then into a digital mixer that also
takes DAC output, and finally out to a speaker pin. Every widget name below is original to
this course, not copied from any textbook.
static const struct snd_soc_dapm_widget ep_codec_dapm_widgets[] = {
/* board-level endpoints */
SND_SOC_DAPM_MIC("EP Mic Jack", NULL),
SND_SOC_DAPM_SPK("EP Speaker Out", NULL),
/* internal signal path blocks */
SND_SOC_DAPM_PGA("EP Mic PGA", EP_REG_PGA_PWR, 0, 0, NULL, 0),
SND_SOC_DAPM_ADC("EP ADC", "EP Capture", EP_REG_ADC_PWR, 1, 0),
SND_SOC_DAPM_DAC("EP DAC", "EP Playback", EP_REG_DAC_PWR, 2, 0),
SND_SOC_DAPM_MIXER("EP Output Mixer", EP_REG_MIX_PWR, 0, 0,
ep_output_mixer_controls,
ARRAY_SIZE(ep_output_mixer_controls)),
};
Two details are worth calling out. First, the ADC and DAC macros take a stream
name ("EP Capture"/"EP Playback") as their second argument — this string must
match the stream name used in your snd_soc_dai_driver‘s playback/capture
stream_name, because that is how DAPM links a widget to an active PCM stream. Second,
notice that the mixer widget reuses ep_output_mixer_controls, the same kcontrol array
from lecture 6 — DAPM mixer and mux widgets are built on top of ordinary kcontrols, they
simply add power awareness around them.
Wiring The Graph: DAPM Routes
Widgets alone are just isolated nodes. You connect them with an array of
struct snd_soc_dapm_route entries. Each route is a three-element tuple: the
destination widget (sink), an optional control name if the connection passes through
a mux or mixer input (control), and the source widget (source). A route with
NULL as the control means the two widgets are always connected whenever both are
powered.
static const struct snd_soc_dapm_route ep_codec_dapm_routes[] = {
/* capture path: mic jack -> PGA -> ADC */
{ "EP Mic PGA", NULL, "EP Mic Jack" },
{ "EP ADC", NULL, "EP Mic PGA" },
/* playback path: DAC -> output mixer -> speaker */
{ "EP Output Mixer", "DAC Switch", "EP DAC" },
{ "EP Speaker Out", NULL, "EP Output Mixer" },
};
Here "DAC Switch" refers to a named input of the EP Output Mixer widget, defined
inside ep_output_mixer_controls exactly as an ordinary SOC_DAPM_SINGLE mixer
switch. When userspace enables that switch through amixer, the route becomes active
and, if the DAC is also part of a running playback stream, DAPM powers the whole chain from
DAC through to the speaker pin.
Registering Widgets And Routes
On modern kernels you no longer call separate “new controls” and “add routes” functions
by hand for a static widget set — you simply point the four relevant fields of
struct snd_soc_component_driver at your arrays, and the ASoC core registers them
automatically while it is probing the component:
static const struct snd_soc_component_driver ep_codec_component_driver = {
.controls = ep_codec_controls,
.num_controls = ARRAY_SIZE(ep_codec_controls),
.dapm_widgets = ep_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ep_codec_dapm_widgets),
.dapm_routes = ep_codec_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ep_codec_dapm_routes),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
};
static int ep_codec_probe(struct i2c_client *client)
{
return devm_snd_soc_register_component(&client->dev,
&ep_codec_component_driver,
&ep_codec_dai_driver, 1);
}
If your driver needs to add widgets dynamically after probe — for example, a widget set
that depends on a value read from device tree — you can still call
snd_soc_dapm_new_controls() and snd_soc_dapm_add_routes() directly against
snd_soc_component_get_dapm(component), followed by
snd_soc_dapm_new_widgets(component->card) once, after all widgets for the card have
been added.
How The Power Walk Actually Runs
Every time a stream is opened/closed or a route-affecting control changes, DAPM marks the
touched widgets dirty and schedules a power update. The core then walks outward from every
widget currently acting as an active endpoint — a running PCM stream, or a widget with
SND_SOC_DAPM_FORCE — following connected routes. Any widget reachable from an active
endpoint through a chain of connected, powered routes is powered up; anything no longer
reachable is powered down. Widgets that define an event callback and set
event_flags such as SND_SOC_DAPM_PRE_PMU or SND_SOC_DAPM_POST_PMD get a chance
to run driver code immediately before power-up or immediately after power-down — useful for
things like a mic bias ramp-up delay.
Building And Testing On A Real Board
Build the module exactly as you would any other ASoC component driver:
obj-m += ep_codec.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
$ make
$ sudo insmod ep_codec.ko
$ dmesg | tail -n 8
ep_codec 1-001a: EP Codec component registered
asoc-simple-card sound: ep-card <-> ep_codec mapping ok
snd_soc_dapm_new_widgets: card ep-card: new widgets 6, paths 4
With the module loaded, list the mixer controls DAPM exposed and confirm the DAC switch
starts powered down:
$ amixer -c ep-card contents | grep -A2 "DAC Switch"
numid=7,iface=MIXER,name='DAC Switch'
; type=BOOLEAN,access=rw------,values=1
: values=off
Flip it on and start a playback stream, then check the widget power state exposed under
the sound card’s debugfs DAPM directory (typically
/sys/kernel/debug/asoc/<card-name>/dapm/):
$ amixer -c ep-card cset name='DAC Switch' on
$ aplay -D hw:ep-card,0 test.wav &
$ cat /sys/kernel/debug/asoc/ep-card/dapm/EP\ Speaker\ Out
EP Speaker Out: On, In
Stop playback and turn the switch back off, and the same file should read Off a
moment later — confirming the widget graph, not your driver code, decided when the speaker
amplifier actually needed current.
Kcontrol vs DAPM Widget: When To Use Which
| Aspect | Plain Kcontrol | DAPM Widget |
|---|---|---|
| Awareness of active stream | None | Yes — power tracks the live audio path |
| Typical use | Volume, simple on/off switch | ADC/DAC/PGA/mixer/mux power, board pins |
| Registration field | .controls / .num_controls |
.dapm_widgets / .dapm_routes |
| Can trigger register writes on power change | No | Yes, via event callback |
Common Mistakes
- Widget name typos in the routes array — the route table matches widgets by exact string,
and a silent mismatch just leaves the graph disconnected with no build-time error. - Forgetting that the ADC/DAC stream name in the widget macro must match the DAI driver’s
stream_nameexactly, or the widget never links to the running PCM substream. - Adding routes for a widget that was never declared in the widgets array — this is caught
at card probe time with a “no dapm match” warning in the kernel log, not a build failure. - Doing register writes for power control directly in a kcontrol’s
put()callback
instead of letting DAPM’seventmechanism drive it, which breaks the automatic
power-tracking DAPM is supposed to give you.
Best Practices, Performance, And Security Notes
Keep widget names short but unique per card, since they double as the identifiers
userspace tools query. Prefer register-backed widgets (passing a real reg/shift) over
SND_SOC_NOPM whenever the hardware genuinely has a bit for that block, since it lets
DAPM read back real hardware state after resume instead of assuming software state is
correct. From a power standpoint, resist the temptation to mark widgets
SND_SOC_DAPM_FORCE just to simplify debugging — every forced widget stays powered
regardless of the live path and directly costs battery life on portable hardware. On the
security side, DAPM controls are ordinary ALSA controls exposed to any local user with
access to the sound device node, so don’t rely on DAPM route state as an access-control
mechanism — pair it with standard device-node permissions if a path should be restricted.
Summary And Key Takeaways
ALSA DAPM widgets turn a codec’s internal audio blocks into nodes of a graph, and DAPM
routes describe how audio actually flows between them. Instead of a driver manually
deciding when to power an amplifier or converter, the ASoC core walks this graph on every
stream and control change and powers exactly what a live path needs. You now have the
pattern for declaring widgets with the SND_SOC_DAPM_* macro family, wiring them with
struct snd_soc_dapm_route, registering both through
struct snd_soc_component_driver, and verifying the result with amixer and the
DAPM debugfs tree. The next lecture in this free Linux kernel development course moves from
the codec side to the platform/CPU DAI side of DAPM, covering how DMA-backed playback and
capture widgets interact with the routes you just built.
FAQ
What is the difference between a DAPM widget and a normal ALSA kcontrol?
A kcontrol only changes a register value when userspace asks it to. A DAPM widget wraps
a functional block of the codec into a node of a power graph, so the ASoC core can turn
that block on or off automatically based on whether it lies on a currently active audio
path.
Do I always need both widgets and routes?
Yes, for anything beyond a single isolated widget. Widgets declare what exists; routes
declare how those things are connected. Without routes, every widget is an isolated,
unreachable node and DAPM has no path to power up.
How does DAPM know a widget belongs to a running PCM stream?
The stream name passed to the SND_SOC_DAPM_ADC/SND_SOC_DAPM_DAC macro must match
the stream_name field configured in the corresponding playback or capture struct inside
your snd_soc_dai_driver. That shared string is how DAPM links the widget graph to an
open PCM substream.
Can I add or remove widgets after the card has already probed?
Yes. Call snd_soc_dapm_new_controls() and snd_soc_dapm_add_routes() against the
component’s DAPM context, then call snd_soc_dapm_new_widgets() once on the card to
apply the change and recompute power state.
Why did my route silently do nothing?
Almost always a widget name mismatch between the widgets array and the routes array.
DAPM matches purely by string, so check the kernel log for a “no dapm match” warning at
card probe time.
Is DAPM specific to ASoC, or does it apply to plain ALSA drivers too?
DAPM is part of the ASoC (ALSA System on Chip) layer specifically. Plain, non-SoC ALSA
codec drivers do not use the widget/route graph — they rely on simpler power management
hooks.
What does SND_SOC_NOPM mean in a widget macro?
It tells the framework this widget has no dedicated hardware power register bit of its
own — DAPM still tracks and reports its logical power state, but no register write happens
purely for power purposes when it is powered up or down.
How can I inspect widget power state without adding debug prints?
Mount debugfs and read the per-widget file under
/sys/kernel/debug/asoc/<card-name>/dapm/<widget-name>, which reports whether the
widget is currently On or Off and its In/Out connection state.
Continue The Free Linux Kernel Development Course
Keep building real ASoC driver skills, lecture by lecture, at no cost.
PGA -> ADC -> mixer -> DAC -> speaker pin.
ALT: “ALSA DAPM widgets audio signal path diagram for Linux kernel ASoC driver”
2. Screenshot mockup of alsamixer showing a DAC Switch control.
ALT: “alsamixer DAC Switch control exposed by ALSA DAPM widget in Linux driver”
3. Terminal screenshot mockup of debugfs DAPM widget power state output.
ALT: “Linux ALSA DAPM debugfs widget power state On Off example”
4. Flowchart mockup of the DAPM power-walk from active stream to endpoint widgets.
ALT: “ALSA DAPM power walk algorithm diagram free Linux kernel course”
Outbound Reference Links:
– https://www.kernel.org/doc/html/latest/sound/soc/dapm.html
– https://elixir.bootlin.com/linux/latest/source/include/sound/soc-dapm.h
– https://elixir.bootlin.com/linux/latest/source/sound/soc/soc-dapm.c
– https://www.kernel.org/doc/html/latest/sound/soc/codec.html
Internal Link Suggestions:
– Previous lecture: ldd2ch5_6.html (ALSA mixer control macros and kcontrol arrays)
– Next lecture: ldd2ch5_8.html (platform/CPU DAI side of DAPM and DMA-backed widgets)
– Related: lddch9 series (Regmap API, used for register-backed widget power bits)
============================================================ –>
