4155a82f6a
Some new event types now exist, so update the code fragment. Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com> Link: https://lore.kernel.org/r/20240426-dapm-docs-v2-12-87b07547eb5b@bootlin.com Signed-off-by: Mark Brown <broonie@kernel.org>
420 lines
13 KiB
ReStructuredText
420 lines
13 KiB
ReStructuredText
===================================================
|
|
Dynamic Audio Power Management for Portable Devices
|
|
===================================================
|
|
|
|
Description
|
|
===========
|
|
|
|
Dynamic Audio Power Management (DAPM) is designed to allow portable
|
|
Linux devices to use the minimum amount of power within the audio
|
|
subsystem at all times. It is independent of other kernel power
|
|
management frameworks and, as such, can easily co-exist with them.
|
|
|
|
DAPM is also completely transparent to all user space applications as
|
|
all power switching is done within the ASoC core. No code changes or
|
|
recompiling are required for user space applications. DAPM makes power
|
|
switching decisions based upon any audio stream (capture/playback)
|
|
activity and audio mixer settings within the device.
|
|
|
|
DAPM is based on two basic elements, called widgets and routes:
|
|
|
|
* a **widget** is every part of the audio hardware that can be enabled by
|
|
software when in use and disabled to save power when not in use
|
|
* a **route** is an interconnection between widgets that exists when sound
|
|
can flow from one widget to the other
|
|
|
|
All DAPM power switching decisions are made automatically by consulting an
|
|
audio routing graph. This graph is specific to each sound card and spans
|
|
the whole sound card, so some DAPM routes connect two widgets belonging to
|
|
different components (e.g. the LINE OUT pin of a CODEC and the input pin of
|
|
an amplifier).
|
|
|
|
The graph for the STM32MP1-DK1 sound card is shown in picture:
|
|
|
|
.. kernel-figure:: dapm-graph.svg
|
|
:alt: Example DAPM graph
|
|
:align: center
|
|
|
|
DAPM power domains
|
|
==================
|
|
|
|
There are 4 power domains within DAPM:
|
|
|
|
Codec bias domain
|
|
VREF, VMID (core codec and audio power)
|
|
|
|
Usually controlled at codec probe/remove and suspend/resume, although
|
|
can be set at stream time if power is not needed for sidetone, etc.
|
|
|
|
Platform/Machine domain
|
|
physically connected inputs and outputs
|
|
|
|
Is platform/machine and user action specific, is configured by the
|
|
machine driver and responds to asynchronous events e.g when HP
|
|
are inserted
|
|
|
|
Path domain
|
|
audio subsystem signal paths
|
|
|
|
Automatically set when mixer and mux settings are changed by the user.
|
|
e.g. alsamixer, amixer.
|
|
|
|
Stream domain
|
|
DACs and ADCs.
|
|
|
|
Enabled and disabled when stream playback/capture is started and
|
|
stopped respectively. e.g. aplay, arecord.
|
|
|
|
|
|
DAPM Widgets
|
|
============
|
|
|
|
Audio DAPM widgets fall into a number of types:
|
|
|
|
Mixer
|
|
Mixes several analog signals into a single analog signal.
|
|
Mux
|
|
An analog switch that outputs only one of many inputs.
|
|
PGA
|
|
A programmable gain amplifier or attenuation widget.
|
|
ADC
|
|
Analog to Digital Converter
|
|
DAC
|
|
Digital to Analog Converter
|
|
Switch
|
|
An analog switch
|
|
Input
|
|
A codec input pin
|
|
Output
|
|
A codec output pin
|
|
Headphone
|
|
Headphone (and optional Jack)
|
|
Mic
|
|
Mic (and optional Jack)
|
|
Line
|
|
Line Input/Output (and optional Jack)
|
|
Speaker
|
|
Speaker
|
|
Supply
|
|
Power or clock supply widget used by other widgets.
|
|
Regulator
|
|
External regulator that supplies power to audio components.
|
|
Clock
|
|
External clock that supplies clock to audio components.
|
|
AIF IN
|
|
Audio Interface Input (with TDM slot mask).
|
|
AIF OUT
|
|
Audio Interface Output (with TDM slot mask).
|
|
Siggen
|
|
Signal Generator.
|
|
DAI IN
|
|
Digital Audio Interface Input.
|
|
DAI OUT
|
|
Digital Audio Interface Output.
|
|
DAI Link
|
|
DAI Link between two DAI structures
|
|
Pre
|
|
Special PRE widget (exec before all others)
|
|
Post
|
|
Special POST widget (exec after all others)
|
|
Buffer
|
|
Inter widget audio data buffer within a DSP.
|
|
Scheduler
|
|
DSP internal scheduler that schedules component/pipeline processing
|
|
work.
|
|
Effect
|
|
Widget that performs an audio processing effect.
|
|
SRC
|
|
Sample Rate Converter within DSP or CODEC
|
|
ASRC
|
|
Asynchronous Sample Rate Converter within DSP or CODEC
|
|
Encoder
|
|
Widget that encodes audio data from one format (usually PCM) to another
|
|
usually more compressed format.
|
|
Decoder
|
|
Widget that decodes audio data from a compressed format to an
|
|
uncompressed format like PCM.
|
|
|
|
|
|
(Widgets are defined in include/sound/soc-dapm.h)
|
|
|
|
Widgets can be added to the sound card by any of the component driver types.
|
|
There are convenience macros defined in soc-dapm.h that can be used to quickly
|
|
build a list of widgets of the codecs and machines DAPM widgets.
|
|
|
|
Most widgets have a name, register, shift and invert. Some widgets have extra
|
|
parameters for stream name and kcontrols.
|
|
|
|
|
|
Stream Domain Widgets
|
|
---------------------
|
|
|
|
Stream Widgets relate to the stream power domain and only consist of ADCs
|
|
(analog to digital converters), DACs (digital to analog converters),
|
|
AIF IN and AIF OUT.
|
|
|
|
Stream widgets have the following format:
|
|
::
|
|
|
|
SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
|
|
SND_SOC_DAPM_AIF_IN(name, stream, slot, reg, shift, invert)
|
|
|
|
NOTE: the stream name must match the corresponding stream name in your codec
|
|
snd_soc_dai_driver.
|
|
|
|
e.g. stream widgets for HiFi playback and capture
|
|
::
|
|
|
|
SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
|
|
SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
|
|
|
|
e.g. stream widgets for AIF
|
|
::
|
|
|
|
SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
|
|
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
|
|
|
|
|
|
Path Domain Widgets
|
|
-------------------
|
|
|
|
Path domain widgets have a ability to control or affect the audio signal or
|
|
audio paths within the audio subsystem. They have the following form:
|
|
::
|
|
|
|
SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
|
|
|
|
Any widget kcontrols can be set using the controls and num_controls members.
|
|
|
|
e.g. Mixer widget (the kcontrols are declared first)
|
|
::
|
|
|
|
/* Output Mixer */
|
|
static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
|
|
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
|
|
SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
|
|
SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
|
|
};
|
|
|
|
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
|
|
ARRAY_SIZE(wm8731_output_mixer_controls)),
|
|
|
|
If you don't want the mixer elements prefixed with the name of the mixer widget,
|
|
you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
|
|
as for SND_SOC_DAPM_MIXER.
|
|
|
|
|
|
Machine domain Widgets
|
|
----------------------
|
|
|
|
Machine widgets are different from codec widgets in that they don't have a
|
|
codec register bit associated with them. A machine widget is assigned to each
|
|
machine audio component (non codec or DSP) that can be independently
|
|
powered. e.g.
|
|
|
|
* Speaker Amp
|
|
* Microphone Bias
|
|
* Jack connectors
|
|
|
|
A machine widget can have an optional call back.
|
|
|
|
e.g. Jack connector widget for an external Mic that enables Mic Bias
|
|
when the Mic is inserted::
|
|
|
|
static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
|
|
{
|
|
gpio_set_value(SPITZ_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
|
|
return 0;
|
|
}
|
|
|
|
SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
|
|
|
|
|
|
Codec (BIAS) Domain
|
|
-------------------
|
|
|
|
The codec bias power domain has no widgets and is handled by the codec DAPM
|
|
event handler. This handler is called when the codec powerstate is changed wrt
|
|
to any stream event or by kernel PM events.
|
|
|
|
|
|
Virtual Widgets
|
|
---------------
|
|
|
|
Sometimes widgets exist in the codec or machine audio graph that don't have any
|
|
corresponding soft power control. In this case it is necessary to create
|
|
a virtual widget - a widget with no control bits e.g.
|
|
::
|
|
|
|
SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
|
|
|
|
This can be used to merge two signal paths together in software.
|
|
|
|
Registering DAPM controls
|
|
=========================
|
|
|
|
In many cases the DAPM widgets are implemented statically in a ``static
|
|
const struct snd_soc_dapm_widget`` array in a codec driver, and simply
|
|
declared via the ``dapm_widgets`` and ``num_dapm_widgets`` fields of the
|
|
``struct snd_soc_component_driver``.
|
|
|
|
Similarly, routes connecting them are implemented statically in a ``static
|
|
const struct snd_soc_dapm_route`` array and declared via the
|
|
``dapm_routes`` and ``num_dapm_routes`` fields of the same struct.
|
|
|
|
With the above declared, the driver registration will take care of
|
|
populating them::
|
|
|
|
static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = {
|
|
SND_SOC_DAPM_OUTPUT("SPKN"),
|
|
SND_SOC_DAPM_OUTPUT("SPKP"),
|
|
...
|
|
};
|
|
|
|
/* Target, Path, Source */
|
|
static const struct snd_soc_dapm_route wm2000_audio_map[] = {
|
|
{ "SPKN", NULL, "ANC Engine" },
|
|
{ "SPKP", NULL, "ANC Engine" },
|
|
...
|
|
};
|
|
|
|
static const struct snd_soc_component_driver soc_component_dev_wm2000 = {
|
|
...
|
|
.dapm_widgets = wm2000_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets),
|
|
.dapm_routes = wm2000_audio_map,
|
|
.num_dapm_routes = ARRAY_SIZE(wm2000_audio_map),
|
|
...
|
|
};
|
|
|
|
In more complex cases the list of DAPM widgets and/or routes can be only
|
|
known at probe time. This happens for example when a driver supports
|
|
different models having a different set of features. In those cases
|
|
separate widgets and routes arrays implementing the case-specific features
|
|
can be registered programmatically by calling snd_soc_dapm_new_controls()
|
|
and snd_soc_dapm_add_routes().
|
|
|
|
|
|
Codec/DSP Widget Interconnections
|
|
=================================
|
|
|
|
Widgets are connected to each other within the codec, platform and machine by
|
|
audio paths (called interconnections). Each interconnection must be defined in
|
|
order to create a graph of all audio paths between widgets.
|
|
|
|
This is easiest with a diagram of the codec or DSP (and schematic of the machine
|
|
audio system), as it requires joining widgets together via their audio signal
|
|
paths.
|
|
|
|
For example the WM8731 output mixer (wm8731.c) has 3 inputs (sources):
|
|
|
|
1. Line Bypass Input
|
|
2. DAC (HiFi playback)
|
|
3. Mic Sidetone Input
|
|
|
|
Each input in this example has a kcontrol associated with it (defined in
|
|
the example above) and is connected to the output mixer via its kcontrol
|
|
name. We can now connect the destination widget (wrt audio signal) with its
|
|
source widgets. ::
|
|
|
|
/* output mixer */
|
|
{"Output Mixer", "Line Bypass Switch", "Line Input"},
|
|
{"Output Mixer", "HiFi Playback Switch", "DAC"},
|
|
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
|
|
|
|
So we have:
|
|
|
|
* Destination Widget <=== Path Name <=== Source Widget, or
|
|
* Sink, Path, Source, or
|
|
* ``Output Mixer`` is connected to the ``DAC`` via the ``HiFi Playback Switch``.
|
|
|
|
When there is no path name connecting widgets (e.g. a direct connection) we
|
|
pass NULL for the path name.
|
|
|
|
Interconnections are created with a call to::
|
|
|
|
snd_soc_dapm_connect_input(codec, sink, path, source);
|
|
|
|
Finally, snd_soc_dapm_new_widgets() must be called after all widgets and
|
|
interconnections have been registered with the core. This causes the core to
|
|
scan the codec and machine so that the internal DAPM state matches the
|
|
physical state of the machine.
|
|
|
|
|
|
Machine Widget Interconnections
|
|
-------------------------------
|
|
Machine widget interconnections are created in the same way as codec ones and
|
|
directly connect the codec pins to machine level widgets.
|
|
|
|
e.g. connects the speaker out codec pins to the internal speaker.
|
|
::
|
|
|
|
/* ext speaker connected to codec pins LOUT2, ROUT2 */
|
|
{"Ext Spk", NULL , "ROUT2"},
|
|
{"Ext Spk", NULL , "LOUT2"},
|
|
|
|
This allows the DAPM to power on and off pins that are connected (and in use)
|
|
and pins that are NC respectively.
|
|
|
|
|
|
Endpoint Widgets
|
|
================
|
|
An endpoint is a start or end point (widget) of an audio signal within the
|
|
machine and includes the codec. e.g.
|
|
|
|
* Headphone Jack
|
|
* Internal Speaker
|
|
* Internal Mic
|
|
* Mic Jack
|
|
* Codec Pins
|
|
|
|
Endpoints are added to the DAPM graph so that their usage can be determined in
|
|
order to save power. e.g. NC codecs pins will be switched OFF, unconnected
|
|
jacks can also be switched OFF.
|
|
|
|
|
|
DAPM Widget Events
|
|
==================
|
|
|
|
Widgets needing to implement a more complex behaviour than what DAPM can do
|
|
can set a custom "event handler" by setting a function pointer. An example
|
|
is a power supply needing to enable a GPIO::
|
|
|
|
static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol, int event)
|
|
{
|
|
if (SND_SOC_DAPM_EVENT_ON(event))
|
|
gpiod_set_value_cansleep(gpio_pa, true);
|
|
else
|
|
gpiod_set_value_cansleep(gpio_pa, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dapm_widget st_widgets[] = {
|
|
...
|
|
SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
|
|
sof_es8316_speaker_power_event,
|
|
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
|
|
};
|
|
|
|
See soc-dapm.h for all other widgets that support events.
|
|
|
|
|
|
Event types
|
|
-----------
|
|
|
|
The following event types are supported by event widgets::
|
|
|
|
/* dapm event types */
|
|
#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
|
|
#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
|
|
#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
|
|
#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
|
|
#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
|
|
#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
|
|
#define SND_SOC_DAPM_WILL_PMU 0x40 /* called at start of sequence */
|
|
#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */
|
|
#define SND_SOC_DAPM_PRE_POST_PMD (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
|
|
#define SND_SOC_DAPM_PRE_POST_PMU (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU)
|