Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,58 @@
==============
Audio Clocking
==============
This text describes the audio clocking terms in ASoC and digital audio in
general. Note: Audio clocking can be complex!
Master Clock
------------
Every audio subsystem is driven by a master clock (sometimes referred to as MCLK
or SYSCLK). This audio master clock can be derived from a number of sources
(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
audio playback and capture sample rates.
Some master clocks (e.g. PLLs and CPU based clocks) are configurable in that
their speed can be altered by software (depending on the system use and to save
power). Other master clocks are fixed at a set frequency (i.e. crystals).
DAI Clocks
----------
The Digital Audio Interface is usually driven by a Bit Clock (often referred to
as BCLK). This clock is used to drive the digital audio data across the link
between the codec and CPU.
The DAI also has a frame clock to signal the start of each audio frame. This
clock is sometimes referred to as LRC (left right clock) or FRAME. This clock
runs at exactly the sample rate (LRC = Rate).
Bit Clock can be generated as follows:-
- BCLK = MCLK / x, or
- BCLK = LRC * x, or
- BCLK = LRC * Channels * Word Size
This relationship depends on the codec or SoC CPU in particular. In general
it is best to configure BCLK to the lowest possible speed (depending on your
rate, number of channels and word size) to save on power.
It is also desirable to use the codec (if possible) to drive (or master) the
audio clocks as it usually gives more accurate sample rates than the CPU.
ASoC provided clock APIs
------------------------
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_sysclk
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_clkdiv
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_pll
.. kernel-doc:: sound/soc/soc-dai.c
:identifiers: snd_soc_dai_set_bclk_ratio
@@ -0,0 +1,115 @@
==============================================
Creating codec to codec dai link for ALSA dapm
==============================================
Mostly the flow of audio is always from CPU to codec so your system
will look as below:
::
--------- ---------
| | dai | |
CPU -------> codec
| | | |
--------- ---------
In case your system looks as below:
::
---------
| |
codec-2
| |
---------
|
dai-2
|
---------- ---------
| | dai-1 | |
CPU -------> codec-1
| | | |
---------- ---------
|
dai-3
|
---------
| |
codec-3
| |
---------
Suppose codec-2 is a bluetooth chip and codec-3 is connected to
a speaker and you have a below scenario:
codec-2 will receive the audio data and the user wants to play that
audio through codec-3 without involving the CPU.This
aforementioned case is the ideal case when codec to codec
connection should be used.
Your dai_link should appear as below in your machine
file:
::
/*
* this pcm stream only supports 24 bit, 2 channel and
* 48k sampling rate.
*/
static const struct snd_soc_pcm_stream dsp_codec_params = {
.formats = SNDRV_PCM_FMTBIT_S24_LE,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
};
{
.name = "CPU-DSP",
.stream_name = "CPU-DSP",
.cpu_dai_name = "samsung-i2s.0",
.codec_name = "codec-2,
.codec_dai_name = "codec-2-dai_name",
.platform_name = "samsung-i2s.0",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
.c2c_params = &dsp_codec_params,
.num_c2c_params = 1,
},
{
.name = "DSP-CODEC",
.stream_name = "DSP-CODEC",
.cpu_dai_name = "wm0010-sdi2",
.codec_name = "codec-3,
.codec_dai_name = "codec-3-dai_name",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.ignore_suspend = 1,
.c2c_params = &dsp_codec_params,
.num_c2c_params = 1,
},
Above code snippet is motivated from sound/soc/samsung/speyside.c.
Note the "c2c_params" callback which lets the dapm know that this
dai_link is a codec to codec connection.
In dapm core a route is created between cpu_dai playback widget
and codec_dai capture widget for playback path and vice-versa is
true for capture path. In order for this aforementioned route to get
triggered, DAPM needs to find a valid endpoint which could be either
a sink or source widget corresponding to playback and capture path
respectively.
In order to trigger this dai_link widget, a thin codec driver for
the speaker amp can be created as demonstrated in wm8727.c file, it
sets appropriate constraints for the device even if it needs no control.
Make sure to name your corresponding cpu and codec playback and capture
dai names ending with "Playback" and "Capture" respectively as dapm core
will link and power those dais based on the name.
A dai_link in a "simple-audio-card" will automatically be detected as
codec to codec when all DAIs on the link belong to codec components.
The dai_link will be initialized with the subset of stream parameters
(channels, format, sample rate) supported by all DAIs on the link. Since
there is no way to provide these parameters in the device tree, this is
mostly useful for communication with simple fixed-function codecs, such
as a Bluetooth controller or cellular modem.
@@ -0,0 +1,190 @@
=======================
ASoC Codec Class Driver
=======================
The codec class driver is generic and hardware independent code that configures
the codec, FM, MODEM, BT or external DSP to provide audio capture and playback.
It should contain no code that is specific to the target platform or machine.
All platform and machine specific code should be added to the platform and
machine drivers respectively.
Each codec class driver *must* provide the following features:-
1. Codec DAI and PCM configuration
2. Codec control IO - using RegMap API
3. Mixers and audio controls
4. Codec audio operations
5. DAPM description.
6. DAPM event handler.
Optionally, codec drivers can also provide:-
7. DAC Digital mute control.
Its probably best to use this guide in conjunction with the existing codec
driver code in sound/soc/codecs/
ASoC Codec driver breakdown
===========================
Codec DAI and PCM configuration
-------------------------------
Each codec driver must have a struct snd_soc_dai_driver to define its DAI and
PCM capabilities and operations. This struct is exported so that it can be
registered with the core by your machine driver.
e.g.
::
static struct snd_soc_dai_ops wm8731_dai_ops = {
.prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params,
.shutdown = wm8731_shutdown,
.mute_stream = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
};
struct snd_soc_dai_driver wm8731_dai = {
.name = "wm8731-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
.ops = &wm8731_dai_ops,
.symmetric_rate = 1,
};
Codec control IO
----------------
The codec can usually be controlled via an I2C or SPI style interface
(AC97 combines control with data in the DAI). The codec driver should use the
Regmap API for all codec IO. Please see include/linux/regmap.h and existing
codec drivers for example regmap usage.
Mixers and audio controls
-------------------------
All the codec mixers and audio controls can be defined using the convenience
macros defined in soc.h.
::
#define SOC_SINGLE(xname, reg, shift, mask, invert)
Defines a single control as follows:-
::
xname = Control name e.g. "Playback Volume"
reg = codec register
shift = control bit(s) offset in register
mask = control bit size(s) e.g. mask of 7 = 3 bits
invert = the control is inverted
Other macros include:-
::
#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)
A stereo control
::
#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)
A stereo control spanning 2 registers
::
#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
Defines an single enumerated control as follows:-
::
xreg = register
xshift = control bit(s) offset in register
xmask = control bit(s) size
xtexts = pointer to array of strings that describe each setting
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
Defines a stereo enumerated control
Codec Audio Operations
----------------------
The codec driver also supports the following ALSA PCM operations:-
::
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
};
Please refer to the :doc:`ALSA driver PCM documentation
<../kernel-api/writing-an-alsa-driver>` for details.
DAPM description
----------------
The Dynamic Audio Power Management description describes the codec power
components and their relationships and registers to the ASoC core.
Please read dapm.rst for details of building the description.
Please also see the examples in other codec drivers.
DAPM event handler
------------------
This function is a callback that handles codec domain PM calls and system
domain PM calls (e.g. suspend and resume). It is used to put the codec
to sleep when not in use.
Power states:-
::
SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, clk and osc on, active */
SNDRV_CTL_POWER_D1: /* partial On */
SNDRV_CTL_POWER_D2: /* partial On */
SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, inactive */
SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
Codec DAC digital mute control
------------------------------
Most codecs have a digital mute before the DACs that can be used to
minimise any system noise. The mute stops any digital data from
entering the DAC.
A callback can be created that is called by the core for each codec DAI
when the mute is applied or freed.
i.e.
::
static int wm8974_mute(struct snd_soc_dai *dai, int mute, int direction)
{
struct snd_soc_component *component = dai->component;
u16 mute_reg = snd_soc_component_read(component, WM8974_DAC) & 0xffbf;
if (mute)
snd_soc_component_write(component, WM8974_DAC, mute_reg | 0x40);
else
snd_soc_component_write(component, WM8974_DAC, mute_reg);
return 0;
}
@@ -0,0 +1,64 @@
==================================
ASoC Digital Audio Interface (DAI)
==================================
ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.
AC97
====
AC97 is a five wire interface commonly found on many PC sound cards. It is
now also popular in many portable devices. This DAI has a RESET line and time
multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines.
The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
frame is 21uS long and is divided into 13 time slots.
The AC97 specification can be found at :
https://www.intel.com/p/en_US/business/design
I2S
===
I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and
Rx lines are used for audio transmission, while the bit clock (BCLK) and
left/right clock (LRC) synchronise the link. I2S is flexible in that either the
controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
usually varies depending on the sample rate and the master system clock
(SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
ADC and DAC LRCLKs, this allows for simultaneous capture and playback at
different sample rates.
I2S has several different operating modes:-
I2S
MSB is transmitted on the falling edge of the first BCLK after LRC
transition.
Left Justified
MSB is transmitted on transition of LRC.
Right Justified
MSB is transmitted sample size BCLKs before LRC transition.
PCM
===
PCM is another 4 wire interface, very similar to I2S, which can support a more
flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used
to synchronise the link while the Tx and Rx lines are used to transmit and
receive the audio data. Bit clock usually varies depending on sample rate
while sync runs at the sample rate. PCM also supports Time Division
Multiplexing (TDM) in that several devices can use the bus simultaneously (this
is sometimes referred to as network mode).
Common PCM operating modes:-
Mode A
MSB is transmitted on falling edge of first BCLK after FRAME/SYNC.
Mode B
MSB is transmitted on rising edge of FRAME/SYNC.
@@ -0,0 +1,375 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: G Pages: 1 -->
<svg width="900pt" height="630pt"
viewBox="0.00 0.00 900.00 630.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 626)">
<title>G</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-626 896,-626 896,4 -4,4"/>
<g id="clust1" class="cluster">
<title>ROOT</title>
<polygon fill="none" stroke="dodgerblue" points="8,-537 8,-614 102,-614 102,-537 8,-537"/>
<text text-anchor="middle" x="55" y="-598.8" font-family="sans-serif" font-size="14.00">ROOT</text>
</g>
<g id="clust2" class="cluster">
<title>4000b000.audio&#45;controller</title>
<polygon fill="none" stroke="dodgerblue" points="120,-378 120,-455 312,-455 312,-378 120,-378"/>
<text text-anchor="middle" x="216" y="-439.8" font-family="sans-serif" font-size="14.00">4000b000.audio&#45;controller</text>
</g>
<g id="clust5" class="cluster">
<title>cs42l51.0&#45;004a</title>
<polygon fill="none" stroke="dodgerblue" points="330,-8 330,-614 884,-614 884,-8 330,-8"/>
<text text-anchor="middle" x="607" y="-598.8" font-family="sans-serif" font-size="14.00">cs42l51.0&#45;004a</text>
</g>
<g id="clust9" class="cluster">
<title>hdmi&#45;audio&#45;codec.1.auto</title>
<polygon fill="none" stroke="dodgerblue" points="110,-463 110,-614 314,-614 314,-463 110,-463"/>
<text text-anchor="middle" x="212" y="-598.8" font-family="sans-serif" font-size="14.00">hdmi&#45;audio&#45;codec.1.auto</text>
</g>
<!-- ROOT_Amplifier -->
<g id="node1" class="node">
<title>ROOT_Amplifier</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="93.5,-583 16.5,-583 16.5,-545 93.5,-545 93.5,-583"/>
<text text-anchor="middle" x="55" y="-567.8" font-family="sans-serif" font-size="14.00">Amplifier</text>
<text text-anchor="middle" x="55" y="-552.8" font-family="sans-serif" font-size="14.00">[out_drv]</text>
</g>
<!-- 4000b000.audio&#45;controller_capture -->
<g id="node2" class="node">
<title>4000b000.audio&#45;controller_capture</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="202,-424 128,-424 128,-386 202,-386 202,-424"/>
<text text-anchor="middle" x="165" y="-408.8" font-family="sans-serif" font-size="14.00">capture</text>
<text text-anchor="middle" x="165" y="-393.8" font-family="sans-serif" font-size="14.00">[dai_out]</text>
</g>
<!-- 4000b000.audio&#45;controller_playback -->
<g id="node3" class="node">
<title>4000b000.audio&#45;controller_playback</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="304,-424 230,-424 230,-386 304,-386 304,-424"/>
<text text-anchor="middle" x="267" y="-408.8" font-family="sans-serif" font-size="14.00">playback</text>
<text text-anchor="middle" x="267" y="-393.8" font-family="sans-serif" font-size="14.00">[dai_in]</text>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_I2S Playback -->
<g id="node28" class="node">
<title>hdmi&#45;audio&#45;codec.1.auto_I2S Playback</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="306,-583 208,-583 208,-545 306,-545 306,-583"/>
<text text-anchor="middle" x="257" y="-567.8" font-family="sans-serif" font-size="14.00">I2S Playback</text>
<text text-anchor="middle" x="257" y="-552.8" font-family="sans-serif" font-size="14.00">[dai_in]</text>
</g>
<!-- 4000b000.audio&#45;controller_playback&#45;&gt;hdmi&#45;audio&#45;codec.1.auto_I2S Playback -->
<g id="edge21" class="edge">
<title>4000b000.audio&#45;controller_playback&#45;&gt;hdmi&#45;audio&#45;codec.1.auto_I2S Playback</title>
<path fill="none" stroke="black" d="M276.84,-424.14C282.19,-435.06 288.26,-449.42 291,-463 295.05,-483.04 296.67,-489.36 291,-509 288.25,-518.54 283.26,-528.01 277.93,-536.3"/>
<polygon fill="black" stroke="black" points="274.89,-534.55 272.11,-544.78 280.66,-538.51 274.89,-534.55"/>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_Capture -->
<g id="node4" class="node">
<title>hdmi&#45;audio&#45;codec.1.auto_Capture</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="192,-509 118,-509 118,-471 192,-471 192,-509"/>
<text text-anchor="middle" x="155" y="-493.8" font-family="sans-serif" font-size="14.00">Capture</text>
<text text-anchor="middle" x="155" y="-478.8" font-family="sans-serif" font-size="14.00">[dai_out]</text>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_Capture&#45;&gt;4000b000.audio&#45;controller_capture -->
<g id="edge1" class="edge">
<title>hdmi&#45;audio&#45;codec.1.auto_Capture&#45;&gt;4000b000.audio&#45;controller_capture</title>
<path fill="none" stroke="black" d="M157.17,-470.99C158.46,-460.3 160.12,-446.5 161.58,-434.37"/>
<polygon fill="black" stroke="black" points="165.08,-434.61 162.8,-424.26 158.13,-433.77 165.08,-434.61"/>
</g>
<!-- cs42l51.0&#45;004a_AIN1L -->
<g id="node5" class="node">
<title>cs42l51.0&#45;004a_AIN1L</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="836.5,-583 775.5,-583 775.5,-545 836.5,-545 836.5,-583"/>
<text text-anchor="middle" x="806" y="-567.8" font-family="sans-serif" font-size="14.00">AIN1L</text>
<text text-anchor="middle" x="806" y="-552.8" font-family="sans-serif" font-size="14.00">[input]</text>
</g>
<!-- cs42l51.0&#45;004a_PGA&#45;ADC Mux Left -->
<g id="node22" class="node">
<title>cs42l51.0&#45;004a_PGA&#45;ADC Mux Left</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="876,-509 736,-509 736,-471 876,-471 876,-509"/>
<text text-anchor="middle" x="806" y="-493.8" font-family="sans-serif" font-size="14.00">PGA&#45;ADC Mux Left</text>
<text text-anchor="middle" x="806" y="-478.8" font-family="sans-serif" font-size="14.00">[mux]</text>
</g>
<!-- cs42l51.0&#45;004a_AIN1L&#45;&gt;cs42l51.0&#45;004a_PGA&#45;ADC Mux Left -->
<g id="edge14" class="edge">
<title>cs42l51.0&#45;004a_AIN1L&#45;&gt;cs42l51.0&#45;004a_PGA&#45;ADC Mux Left</title>
<path fill="none" stroke="black" d="M806,-544.83C806,-537.13 806,-527.97 806,-519.42"/>
<polygon fill="black" stroke="black" points="809.5,-519.41 806,-509.41 802.5,-519.41 809.5,-519.41"/>
</g>
<!-- cs42l51.0&#45;004a_AIN1R -->
<g id="node6" class="node">
<title>cs42l51.0&#45;004a_AIN1R</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="738.5,-583 677.5,-583 677.5,-545 738.5,-545 738.5,-583"/>
<text text-anchor="middle" x="708" y="-567.8" font-family="sans-serif" font-size="14.00">AIN1R</text>
<text text-anchor="middle" x="708" y="-552.8" font-family="sans-serif" font-size="14.00">[input]</text>
</g>
<!-- cs42l51.0&#45;004a_PGA&#45;ADC Mux Right -->
<g id="node23" class="node">
<title>cs42l51.0&#45;004a_PGA&#45;ADC Mux Right</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="717.5,-509 568.5,-509 568.5,-471 717.5,-471 717.5,-509"/>
<text text-anchor="middle" x="643" y="-493.8" font-family="sans-serif" font-size="14.00">PGA&#45;ADC Mux Right</text>
<text text-anchor="middle" x="643" y="-478.8" font-family="sans-serif" font-size="14.00">[mux]</text>
</g>
<!-- cs42l51.0&#45;004a_AIN1R&#45;&gt;cs42l51.0&#45;004a_PGA&#45;ADC Mux Right -->
<g id="edge15" class="edge">
<title>cs42l51.0&#45;004a_AIN1R&#45;&gt;cs42l51.0&#45;004a_PGA&#45;ADC Mux Right</title>
<path fill="none" stroke="black" d="M691.6,-544.83C683.96,-536.37 674.73,-526.15 666.38,-516.9"/>
<polygon fill="black" stroke="black" points="668.92,-514.49 659.62,-509.41 663.73,-519.18 668.92,-514.49"/>
</g>
<!-- cs42l51.0&#45;004a_AIN2L -->
<g id="node7" class="node">
<title>cs42l51.0&#45;004a_AIN2L</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="659.5,-583 598.5,-583 598.5,-545 659.5,-545 659.5,-583"/>
<text text-anchor="middle" x="629" y="-567.8" font-family="sans-serif" font-size="14.00">AIN2L</text>
<text text-anchor="middle" x="629" y="-552.8" font-family="sans-serif" font-size="14.00">[input]</text>
</g>
<!-- cs42l51.0&#45;004a_AIN2R -->
<g id="node8" class="node">
<title>cs42l51.0&#45;004a_AIN2R</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="580.5,-583 519.5,-583 519.5,-545 580.5,-545 580.5,-583"/>
<text text-anchor="middle" x="550" y="-567.8" font-family="sans-serif" font-size="14.00">AIN2R</text>
<text text-anchor="middle" x="550" y="-552.8" font-family="sans-serif" font-size="14.00">[input]</text>
</g>
<!-- cs42l51.0&#45;004a_Capture -->
<g id="node9" class="node">
<title>cs42l51.0&#45;004a_Capture</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="692,-276 618,-276 618,-238 692,-238 692,-276"/>
<text text-anchor="middle" x="655" y="-260.8" font-family="sans-serif" font-size="14.00">Capture</text>
<text text-anchor="middle" x="655" y="-245.8" font-family="sans-serif" font-size="14.00">[dai_out]</text>
</g>
<!-- cs42l51.0&#45;004a_DAC Mux -->
<g id="node10" class="node">
<title>cs42l51.0&#45;004a_DAC Mux</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="598.5,-202 521.5,-202 521.5,-164 598.5,-164 598.5,-202"/>
<text text-anchor="middle" x="560" y="-186.8" font-family="sans-serif" font-size="14.00">DAC Mux</text>
<text text-anchor="middle" x="560" y="-171.8" font-family="sans-serif" font-size="14.00">[mux]</text>
</g>
<!-- cs42l51.0&#45;004a_Left DAC -->
<g id="node14" class="node">
<title>cs42l51.0&#45;004a_Left DAC</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="548,-128 474,-128 474,-90 548,-90 548,-128"/>
<text text-anchor="middle" x="511" y="-112.8" font-family="sans-serif" font-size="14.00">Left DAC</text>
<text text-anchor="middle" x="511" y="-97.8" font-family="sans-serif" font-size="14.00">[dac]</text>
</g>
<!-- cs42l51.0&#45;004a_DAC Mux&#45;&gt;cs42l51.0&#45;004a_Left DAC -->
<g id="edge9" class="edge">
<title>cs42l51.0&#45;004a_DAC Mux&#45;&gt;cs42l51.0&#45;004a_Left DAC</title>
<path fill="none" stroke="black" d="M547.64,-163.83C542.05,-155.62 535.34,-145.76 529.19,-136.73"/>
<polygon fill="black" stroke="black" points="532.05,-134.71 523.53,-128.41 526.26,-138.65 532.05,-134.71"/>
</g>
<!-- cs42l51.0&#45;004a_Right DAC -->
<g id="node26" class="node">
<title>cs42l51.0&#45;004a_Right DAC</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="649.5,-128 566.5,-128 566.5,-90 649.5,-90 649.5,-128"/>
<text text-anchor="middle" x="608" y="-112.8" font-family="sans-serif" font-size="14.00">Right DAC</text>
<text text-anchor="middle" x="608" y="-97.8" font-family="sans-serif" font-size="14.00">[dac]</text>
</g>
<!-- cs42l51.0&#45;004a_DAC Mux&#45;&gt;cs42l51.0&#45;004a_Right DAC -->
<g id="edge18" class="edge">
<title>cs42l51.0&#45;004a_DAC Mux&#45;&gt;cs42l51.0&#45;004a_Right DAC</title>
<path fill="none" stroke="black" d="M572.11,-163.83C577.53,-155.71 584.02,-145.96 589.99,-137.01"/>
<polygon fill="black" stroke="black" points="593.09,-138.68 595.72,-128.41 587.27,-134.79 593.09,-138.68"/>
</g>
<!-- cs42l51.0&#45;004a_HPL -->
<g id="node11" class="node">
<title>cs42l51.0&#45;004a_HPL</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="546.5,-54 475.5,-54 475.5,-16 546.5,-16 546.5,-54"/>
<text text-anchor="middle" x="511" y="-38.8" font-family="sans-serif" font-size="14.00">HPL</text>
<text text-anchor="middle" x="511" y="-23.8" font-family="sans-serif" font-size="14.00">[output]</text>
</g>
<!-- cs42l51.0&#45;004a_HPR -->
<g id="node12" class="node">
<title>cs42l51.0&#45;004a_HPR</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="643.5,-54 572.5,-54 572.5,-16 643.5,-16 643.5,-54"/>
<text text-anchor="middle" x="608" y="-38.8" font-family="sans-serif" font-size="14.00">HPR</text>
<text text-anchor="middle" x="608" y="-23.8" font-family="sans-serif" font-size="14.00">[output]</text>
</g>
<!-- cs42l51.0&#45;004a_Left ADC -->
<g id="node13" class="node">
<title>cs42l51.0&#45;004a_Left ADC</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="822,-350 748,-350 748,-312 822,-312 822,-350"/>
<text text-anchor="middle" x="785" y="-334.8" font-family="sans-serif" font-size="14.00">Left ADC</text>
<text text-anchor="middle" x="785" y="-319.8" font-family="sans-serif" font-size="14.00">[adc]</text>
</g>
<!-- cs42l51.0&#45;004a_Left ADC&#45;&gt;cs42l51.0&#45;004a_Capture -->
<g id="edge4" class="edge">
<title>cs42l51.0&#45;004a_Left ADC&#45;&gt;cs42l51.0&#45;004a_Capture</title>
<path fill="none" stroke="black" d="M752.2,-311.83C735.41,-302.54 714.8,-291.12 696.88,-281.2"/>
<polygon fill="black" stroke="black" points="698.24,-277.95 687.79,-276.16 694.85,-284.07 698.24,-277.95"/>
</g>
<!-- cs42l51.0&#45;004a_Left DAC&#45;&gt;cs42l51.0&#45;004a_HPL -->
<g id="edge6" class="edge">
<title>cs42l51.0&#45;004a_Left DAC&#45;&gt;cs42l51.0&#45;004a_HPL</title>
<path fill="none" stroke="black" d="M511,-89.83C511,-82.13 511,-72.97 511,-64.42"/>
<polygon fill="black" stroke="black" points="514.5,-64.41 511,-54.41 507.5,-64.41 514.5,-64.41"/>
</g>
<!-- cs42l51.0&#45;004a_Left PGA -->
<g id="node15" class="node">
<title>cs42l51.0&#45;004a_Left PGA</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="838,-424 764,-424 764,-386 838,-386 838,-424"/>
<text text-anchor="middle" x="801" y="-408.8" font-family="sans-serif" font-size="14.00">Left PGA</text>
<text text-anchor="middle" x="801" y="-393.8" font-family="sans-serif" font-size="14.00">[pga]</text>
</g>
<!-- cs42l51.0&#45;004a_Left PGA&#45;&gt;cs42l51.0&#45;004a_Left ADC -->
<g id="edge8" class="edge">
<title>cs42l51.0&#45;004a_Left PGA&#45;&gt;cs42l51.0&#45;004a_Left ADC</title>
<path fill="none" stroke="black" d="M796.96,-385.83C795.25,-378.13 793.22,-368.97 791.31,-360.42"/>
<polygon fill="black" stroke="black" points="794.68,-359.42 789.09,-350.41 787.84,-360.93 794.68,-359.42"/>
</g>
<!-- cs42l51.0&#45;004a_MCLK -->
<g id="node16" class="node">
<title>cs42l51.0&#45;004a_MCLK</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="594.5,-350 525.5,-350 525.5,-312 594.5,-312 594.5,-350"/>
<text text-anchor="middle" x="560" y="-334.8" font-family="sans-serif" font-size="14.00">MCLK</text>
<text text-anchor="middle" x="560" y="-319.8" font-family="sans-serif" font-size="14.00">[supply]</text>
</g>
<!-- cs42l51.0&#45;004a_MCLK&#45;&gt;cs42l51.0&#45;004a_Capture -->
<g id="edge2" class="edge">
<title>cs42l51.0&#45;004a_MCLK&#45;&gt;cs42l51.0&#45;004a_Capture</title>
<path fill="none" stroke="black" d="M583.97,-311.83C595.79,-302.88 610.2,-291.96 622.94,-282.3"/>
<polygon fill="black" stroke="black" points="625.18,-284.99 631.04,-276.16 620.95,-279.41 625.18,-284.99"/>
</g>
<!-- cs42l51.0&#45;004a_Playback -->
<g id="node24" class="node">
<title>cs42l51.0&#45;004a_Playback</title>
<polygon fill="none" stroke="#008b00" stroke-width="2" points="597,-276 523,-276 523,-238 597,-238 597,-276"/>
<text text-anchor="middle" x="560" y="-260.8" font-family="sans-serif" font-size="14.00">Playback</text>
<text text-anchor="middle" x="560" y="-245.8" font-family="sans-serif" font-size="14.00">[dai_in]</text>
</g>
<!-- cs42l51.0&#45;004a_MCLK&#45;&gt;cs42l51.0&#45;004a_Playback -->
<g id="edge16" class="edge">
<title>cs42l51.0&#45;004a_MCLK&#45;&gt;cs42l51.0&#45;004a_Playback</title>
<path fill="none" stroke="black" d="M560,-311.83C560,-304.13 560,-294.97 560,-286.42"/>
<polygon fill="black" stroke="black" points="563.5,-286.41 560,-276.41 556.5,-286.41 563.5,-286.41"/>
</g>
<!-- cs42l51.0&#45;004a_MICL -->
<g id="node17" class="node">
<title>cs42l51.0&#45;004a_MICL</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="399.5,-509 338.5,-509 338.5,-471 399.5,-471 399.5,-509"/>
<text text-anchor="middle" x="369" y="-493.8" font-family="sans-serif" font-size="14.00">MICL</text>
<text text-anchor="middle" x="369" y="-478.8" font-family="sans-serif" font-size="14.00">[input]</text>
</g>
<!-- cs42l51.0&#45;004a_Mic Preamp Left -->
<g id="node20" class="node">
<title>cs42l51.0&#45;004a_Mic Preamp Left</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="461.5,-424 338.5,-424 338.5,-386 461.5,-386 461.5,-424"/>
<text text-anchor="middle" x="400" y="-408.8" font-family="sans-serif" font-size="14.00">Mic Preamp Left</text>
<text text-anchor="middle" x="400" y="-393.8" font-family="sans-serif" font-size="14.00">[mixer]</text>
</g>
<!-- cs42l51.0&#45;004a_MICL&#45;&gt;cs42l51.0&#45;004a_Mic Preamp Left -->
<g id="edge12" class="edge">
<title>cs42l51.0&#45;004a_MICL&#45;&gt;cs42l51.0&#45;004a_Mic Preamp Left</title>
<path fill="none" stroke="black" d="M375.73,-470.99C379.8,-460.08 385.08,-445.94 389.68,-433.64"/>
<polygon fill="black" stroke="black" points="392.96,-434.85 393.18,-424.26 386.4,-432.4 392.96,-434.85"/>
</g>
<!-- cs42l51.0&#45;004a_MICR -->
<g id="node18" class="node">
<title>cs42l51.0&#45;004a_MICR</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="501.5,-583 440.5,-583 440.5,-545 501.5,-545 501.5,-583"/>
<text text-anchor="middle" x="471" y="-567.8" font-family="sans-serif" font-size="14.00">MICR</text>
<text text-anchor="middle" x="471" y="-552.8" font-family="sans-serif" font-size="14.00">[input]</text>
</g>
<!-- cs42l51.0&#45;004a_Mic Preamp Right -->
<g id="node21" class="node">
<title>cs42l51.0&#45;004a_Mic Preamp Right</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="550.5,-509 417.5,-509 417.5,-471 550.5,-471 550.5,-509"/>
<text text-anchor="middle" x="484" y="-493.8" font-family="sans-serif" font-size="14.00">Mic Preamp Right</text>
<text text-anchor="middle" x="484" y="-478.8" font-family="sans-serif" font-size="14.00">[mixer]</text>
</g>
<!-- cs42l51.0&#45;004a_MICR&#45;&gt;cs42l51.0&#45;004a_Mic Preamp Right -->
<g id="edge13" class="edge">
<title>cs42l51.0&#45;004a_MICR&#45;&gt;cs42l51.0&#45;004a_Mic Preamp Right</title>
<path fill="none" stroke="black" d="M474.28,-544.83C475.67,-537.13 477.32,-527.97 478.87,-519.42"/>
<polygon fill="black" stroke="black" points="482.34,-519.88 480.68,-509.41 475.45,-518.63 482.34,-519.88"/>
</g>
<!-- cs42l51.0&#45;004a_Mic Bias -->
<g id="node19" class="node">
<title>cs42l51.0&#45;004a_Mic Bias</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="409.5,-583 338.5,-583 338.5,-545 409.5,-545 409.5,-583"/>
<text text-anchor="middle" x="374" y="-567.8" font-family="sans-serif" font-size="14.00">Mic Bias</text>
<text text-anchor="middle" x="374" y="-552.8" font-family="sans-serif" font-size="14.00">[supply]</text>
</g>
<!-- cs42l51.0&#45;004a_Mic Bias&#45;&gt;cs42l51.0&#45;004a_MICL -->
<g id="edge11" class="edge">
<title>cs42l51.0&#45;004a_Mic Bias&#45;&gt;cs42l51.0&#45;004a_MICL</title>
<path fill="none" stroke="black" d="M372.74,-544.83C372.2,-537.13 371.57,-527.97 370.97,-519.42"/>
<polygon fill="black" stroke="black" points="374.46,-519.15 370.28,-509.41 367.48,-519.63 374.46,-519.15"/>
</g>
<!-- cs42l51.0&#45;004a_PGA&#45;ADC Mux Left&#45;&gt;cs42l51.0&#45;004a_Left PGA -->
<g id="edge10" class="edge">
<title>cs42l51.0&#45;004a_PGA&#45;ADC Mux Left&#45;&gt;cs42l51.0&#45;004a_Left PGA</title>
<path fill="none" stroke="black" d="M804.92,-470.99C804.27,-460.3 803.44,-446.5 802.71,-434.37"/>
<polygon fill="black" stroke="black" points="806.2,-434.03 802.1,-424.26 799.21,-434.45 806.2,-434.03"/>
</g>
<!-- cs42l51.0&#45;004a_Right PGA -->
<g id="node27" class="node">
<title>cs42l51.0&#45;004a_Right PGA</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="688.5,-424 605.5,-424 605.5,-386 688.5,-386 688.5,-424"/>
<text text-anchor="middle" x="647" y="-408.8" font-family="sans-serif" font-size="14.00">Right PGA</text>
<text text-anchor="middle" x="647" y="-393.8" font-family="sans-serif" font-size="14.00">[pga]</text>
</g>
<!-- cs42l51.0&#45;004a_PGA&#45;ADC Mux Right&#45;&gt;cs42l51.0&#45;004a_Right PGA -->
<g id="edge19" class="edge">
<title>cs42l51.0&#45;004a_PGA&#45;ADC Mux Right&#45;&gt;cs42l51.0&#45;004a_Right PGA</title>
<path fill="none" stroke="black" d="M643.87,-470.99C644.38,-460.3 645.05,-446.5 645.63,-434.37"/>
<polygon fill="black" stroke="black" points="649.13,-434.42 646.12,-424.26 642.14,-434.08 649.13,-434.42"/>
</g>
<!-- cs42l51.0&#45;004a_Playback&#45;&gt;cs42l51.0&#45;004a_DAC Mux -->
<g id="edge5" class="edge">
<title>cs42l51.0&#45;004a_Playback&#45;&gt;cs42l51.0&#45;004a_DAC Mux</title>
<path fill="none" stroke="black" d="M560,-237.83C560,-230.13 560,-220.97 560,-212.42"/>
<polygon fill="black" stroke="black" points="563.5,-212.41 560,-202.41 556.5,-212.41 563.5,-212.41"/>
</g>
<!-- cs42l51.0&#45;004a_Right ADC -->
<g id="node25" class="node">
<title>cs42l51.0&#45;004a_Right ADC</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="697,-350 613,-350 613,-312 697,-312 697,-350"/>
<text text-anchor="middle" x="655" y="-334.8" font-family="sans-serif" font-size="14.00">Right ADC</text>
<text text-anchor="middle" x="655" y="-319.8" font-family="sans-serif" font-size="14.00">[adc]</text>
</g>
<!-- cs42l51.0&#45;004a_Right ADC&#45;&gt;cs42l51.0&#45;004a_Capture -->
<g id="edge3" class="edge">
<title>cs42l51.0&#45;004a_Right ADC&#45;&gt;cs42l51.0&#45;004a_Capture</title>
<path fill="none" stroke="black" d="M655,-311.83C655,-304.13 655,-294.97 655,-286.42"/>
<polygon fill="black" stroke="black" points="658.5,-286.41 655,-276.41 651.5,-286.41 658.5,-286.41"/>
</g>
<!-- cs42l51.0&#45;004a_Right DAC&#45;&gt;cs42l51.0&#45;004a_HPR -->
<g id="edge7" class="edge">
<title>cs42l51.0&#45;004a_Right DAC&#45;&gt;cs42l51.0&#45;004a_HPR</title>
<path fill="none" stroke="black" d="M608,-89.83C608,-82.13 608,-72.97 608,-64.42"/>
<polygon fill="black" stroke="black" points="611.5,-64.41 608,-54.41 604.5,-64.41 611.5,-64.41"/>
</g>
<!-- cs42l51.0&#45;004a_Right PGA&#45;&gt;cs42l51.0&#45;004a_Right ADC -->
<g id="edge17" class="edge">
<title>cs42l51.0&#45;004a_Right PGA&#45;&gt;cs42l51.0&#45;004a_Right ADC</title>
<path fill="none" stroke="black" d="M649.02,-385.83C649.87,-378.13 650.89,-368.97 651.84,-360.42"/>
<polygon fill="black" stroke="black" points="655.33,-360.74 652.95,-350.41 648.37,-359.97 655.33,-360.74"/>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_TX -->
<g id="node30" class="node">
<title>hdmi&#45;audio&#45;codec.1.auto_TX</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="281.5,-509 210.5,-509 210.5,-471 281.5,-471 281.5,-509"/>
<text text-anchor="middle" x="246" y="-493.8" font-family="sans-serif" font-size="14.00">TX</text>
<text text-anchor="middle" x="246" y="-478.8" font-family="sans-serif" font-size="14.00">[output]</text>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_I2S Playback&#45;&gt;hdmi&#45;audio&#45;codec.1.auto_TX -->
<g id="edge22" class="edge">
<title>hdmi&#45;audio&#45;codec.1.auto_I2S Playback&#45;&gt;hdmi&#45;audio&#45;codec.1.auto_TX</title>
<path fill="none" stroke="black" d="M254.22,-544.83C253.05,-537.13 251.65,-527.97 250.34,-519.42"/>
<polygon fill="black" stroke="black" points="253.78,-518.77 248.81,-509.41 246.86,-519.83 253.78,-518.77"/>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_RX -->
<g id="node29" class="node">
<title>hdmi&#45;audio&#45;codec.1.auto_RX</title>
<polygon fill="#f2f2f2" stroke="#4d4d4d" points="189.5,-583 118.5,-583 118.5,-545 189.5,-545 189.5,-583"/>
<text text-anchor="middle" x="154" y="-567.8" font-family="sans-serif" font-size="14.00">RX</text>
<text text-anchor="middle" x="154" y="-552.8" font-family="sans-serif" font-size="14.00">[output]</text>
</g>
<!-- hdmi&#45;audio&#45;codec.1.auto_RX&#45;&gt;hdmi&#45;audio&#45;codec.1.auto_Capture -->
<g id="edge20" class="edge">
<title>hdmi&#45;audio&#45;codec.1.auto_RX&#45;&gt;hdmi&#45;audio&#45;codec.1.auto_Capture</title>
<path fill="none" stroke="black" d="M154.25,-544.83C154.36,-537.13 154.49,-527.97 154.61,-519.42"/>
<polygon fill="black" stroke="black" points="158.1,-519.46 154.74,-509.41 151.11,-519.36 158.1,-519.46"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

@@ -0,0 +1,422 @@
===================================================
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
You can also generate compatible graph for your sound card using
`tools/sound/dapm-graph` utility.
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)
@@ -0,0 +1,387 @@
===========
Dynamic PCM
===========
Description
===========
Dynamic PCM allows an ALSA PCM device to digitally route its PCM audio to
various digital endpoints during the PCM stream runtime. e.g. PCM0 can route
digital audio to I2S DAI0, I2S DAI1 or PDM DAI2. This is useful for on SoC DSP
drivers that expose several ALSA PCMs and can route to multiple DAIs.
The DPCM runtime routing is determined by the ALSA mixer settings in the same
way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM
graph representing the DSP internal audio paths and uses the mixer settings to
determine the path used by each ALSA PCM.
DPCM re-uses all the existing component codec, platform and DAI drivers without
any modifications.
Phone Audio System with SoC based DSP
-------------------------------------
Consider the following phone audio subsystem. This will be used in this
document for all examples :-
::
| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
This diagram shows a simple smart phone audio subsystem. It supports Bluetooth,
FM digital radio, Speakers, Headset Jack, digital microphones and cellular
modem. This sound card exposes 4 DSP front end (FE) ALSA PCM devices and
supports 6 back end (BE) DAIs. Each FE PCM can digitally route audio data to any
of the BE DAIs. The FE PCM devices can also route audio to more than 1 BE DAI.
Example - DPCM Switching playback from DAI0 to DAI1
---------------------------------------------------
Audio is being played to the Headset. After a while the user removes the headset
and audio continues playing on the speakers.
Playback on PCM0 to Headset would look like :-
::
*************
PCM0 <============> * * <====DAI0=====> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
The headset is removed from the jack by user so the speakers must now be used :-
::
*************
PCM0 <============> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <====DAI1=====> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
The audio driver processes this as follows :-
1. Machine driver receives Jack removal event.
2. Machine driver OR audio HAL disables the Headset path.
3. DPCM runs the PCM trigger(stop), hw_free(), shutdown() operations on DAI0
for headset since the path is now disabled.
4. Machine driver or audio HAL enables the speaker path.
5. DPCM runs the PCM ops for startup(), hw_params(), prepare() and
trigger(start) for DAI1 Speakers since the path is enabled.
In this example, the machine driver or userspace audio HAL can alter the routing
and then DPCM will take care of managing the DAI PCM operations to either bring
the link up or down. Audio playback does not stop during this transition.
DPCM machine driver
===================
The DPCM enabled ASoC machine driver is similar to normal machine drivers
except that we also have to :-
1. Define the FE and BE DAI links.
2. Define any FE/BE PCM operations.
3. Define widget graph connections.
FE and BE DAI links
-------------------
::
| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
For the example above we have to define 4 FE DAI links and 6 BE DAI links. The
FE DAI links are defined as follows :-
::
SND_SOC_DAILINK_DEFS(pcm0,
DAILINK_COMP_ARRAY(COMP_CPU("System Pin")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_PLATFORM("dsp-audio")));
static struct snd_soc_dai_link machine_dais[] = {
{
.name = "PCM0 System",
.stream_name = "System Playback",
SND_SOC_DAILINK_REG(pcm0),
.dynamic = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
},
.....< other FE and BE DAI links here >
};
This FE DAI link is pretty similar to a regular DAI link except that we also
set the DAI link to a DPCM FE with the ``dynamic = 1``.
There is also an option to specify the ordering of the trigger call for
each FE. This allows the ASoC core to trigger the DSP before or after the other
components (as some DSPs have strong requirements for the ordering DAI/DSP
start and stop sequences).
The FE DAI above sets the codec and code DAIs to dummy devices since the BE is
dynamic and will change depending on runtime config.
The BE DAIs are configured as follows :-
::
SND_SOC_DAILINK_DEFS(headset,
DAILINK_COMP_ARRAY(COMP_CPU("ssp-dai.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("rt5640.0-001c", "rt5640-aif1")));
static struct snd_soc_dai_link machine_dais[] = {
.....< FE DAI links here >
{
.name = "Codec Headset",
SND_SOC_DAILINK_REG(headset),
.no_pcm = 1,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = hswult_ssp0_fixup,
.ops = &haswell_ops,
},
.....< other BE DAI links here >
};
This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets
the ``no_pcm`` flag to mark it has a BE.
The BE has also flags set for ignoring suspend and PM down time. This allows
the BE to work in a hostless mode where the host CPU is not transferring data
like a BT phone call :-
::
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <====DAI2=====> MODEM
* *
PCM3 <------------> * * <====DAI3=====> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
This allows the host CPU to sleep while the DSP, MODEM DAI and the BT DAI are
still in operation.
A BE DAI link can also set the codec to a dummy device if the codec is a device
that is managed externally.
Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the
DSP firmware.
FE/BE PCM operations
--------------------
The BE above also exports some PCM operations and a ``fixup`` callback. The fixup
callback is used by the machine driver to (re)configure the DAI based upon the
FE hw params. i.e. the DSP may perform SRC or ASRC from the FE to BE.
e.g. DSP converts all FE hw params to run at fixed rate of 48k, 16bit, stereo for
DAI0. This means all FE hw_params have to be fixed in the machine driver for
DAI0 so that the DAI is running at desired configuration regardless of the FE
configuration.
::
static int dai0_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
/* The DSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set DAI0 to 16 bit */
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
The other PCM operation are the same as for regular DAI links. Use as necessary.
Widget graph connections
------------------------
The BE DAI links will normally be connected to the graph at initialisation time
by the ASoC DAPM core. However, if the BE codec or BE DAI is a dummy then this
has to be set explicitly in the driver :-
::
/* BE for codec Headset - DAI0 is dummy and managed by DSP FW */
{"DAI0 CODEC IN", NULL, "AIF1 Capture"},
{"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
Writing a DPCM DSP driver
=========================
The DPCM DSP driver looks much like a standard platform class ASoC driver
combined with elements from a codec class driver. A DSP platform driver must
implement :-
1. Front End PCM DAIs - i.e. struct snd_soc_dai_driver.
2. DAPM graph showing DSP audio routing from FE DAIs to BEs.
3. DAPM widgets from DSP graph.
4. Mixers for gains, routing, etc.
5. DMA configuration.
6. BE AIF widgets.
Items 6 is important for routing the audio outside of the DSP. AIF need to be
defined for each BE and each stream direction. e.g for BE DAI0 above we would
have :-
::
SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),
The BE AIF are used to connect the DSP graph to the graphs for the other
component drivers (e.g. codec graph).
Hostless PCM streams
====================
A hostless PCM stream is a stream that is not routed through the host CPU. An
example of this would be a phone call from handset to modem.
::
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <====DAI1=====> Codec Speakers/Mic
* DSP *
PCM2 <------------> * * <====DAI2=====> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
In this case the PCM data is routed via the DSP. The host CPU in this use case
is only used for control and can sleep during the runtime of the stream.
The host can control the hostless link either by :-
1. Configuring the link as a CODEC <-> CODEC style link. In this case the link
is enabled or disabled by the state of the DAPM graph. This usually means
there is a mixer control that can be used to connect or disconnect the path
between both DAIs.
2. Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM
graph. Control is then carried out by the FE as regular PCM operations.
This method gives more control over the DAI links, but requires much more
userspace code to control the link. Its recommended to use CODEC<->CODEC
unless your HW needs more fine grained sequencing of the PCM ops.
CODEC <-> CODEC link
--------------------
This DAI link is enabled when DAPM detects a valid path within the DAPM graph.
The machine driver sets some additional parameters to the DAI link i.e.
::
static const struct snd_soc_pcm_stream dai_params = {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rate_min = 8000,
.rate_max = 8000,
.channels_min = 2,
.channels_max = 2,
};
static struct snd_soc_dai_link dais[] = {
< ... more DAI links above ... >
{
.name = "MODEM",
.stream_name = "MODEM",
.cpu_dai_name = "dai2",
.codec_dai_name = "modem-aif1",
.codec_name = "modem",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.c2c_params = &dai_params,
.num_c2c_params = 1,
}
< ... more DAI links here ... >
These parameters are used to configure the DAI hw_params() when DAPM detects a
valid path and then calls the PCM operations to start the link. DAPM will also
call the appropriate PCM operations to disable the DAI when the path is no
longer valid.
Hostless FE
-----------
The DAI link(s) are enabled by a FE that does not read or write any PCM data.
This means creating a new FE that is connected with a virtual path to both
DAI links. The DAI links will be started when the FE PCM is started and stopped
when the FE PCM is stopped. Note that the FE PCM cannot read or write data in
this configuration.
@@ -0,0 +1,21 @@
==============
ALSA SoC Layer
==============
The documentation is split into the following sections:-
.. toctree::
:maxdepth: 2
overview
codec
dai
dapm
platform
machine
pops-clicks
clocking
jack
dpcm
codec-to-codec
usb
@@ -0,0 +1,72 @@
===================
ASoC jack detection
===================
ALSA has a standard API for representing physical jacks to user space,
the kernel side of which can be seen in include/sound/jack.h. ASoC
provides a version of this API adding two additional features:
- It allows more than one jack detection method to work together on one
user visible jack. In embedded systems it is common for multiple
to be present on a single jack but handled by separate bits of
hardware.
- Integration with DAPM, allowing DAPM endpoints to be updated
automatically based on the detected jack status (eg, turning off the
headphone outputs if no headphones are present).
This is done by splitting the jacks up into three things working
together: the jack itself represented by a struct snd_soc_jack, sets of
snd_soc_jack_pins representing DAPM endpoints to update and blocks of
code providing jack reporting mechanisms.
For example, a system may have a stereo headset jack with two reporting
mechanisms, one for the headphone and one for the microphone. Some
systems won't be able to use their speaker output while a headphone is
connected and so will want to make sure to update both speaker and
headphone when the headphone jack status changes.
The jack - struct snd_soc_jack
==============================
This represents a physical jack on the system and is what is visible to
user space. The jack itself is completely passive, it is set up by the
machine driver and updated by jack detection methods.
Jacks are created by the machine driver calling snd_soc_jack_new().
snd_soc_jack_pin
================
These represent a DAPM pin to update depending on some of the status
bits supported by the jack. Each snd_soc_jack has zero or more of these
which are updated automatically. They are created by the machine driver
and associated with the jack using snd_soc_jack_add_pins(). The status
of the endpoint may configured to be the opposite of the jack status if
required (eg, enabling a built in microphone if a microphone is not
connected via a jack).
Jack detection methods
======================
Actual jack detection is done by code which is able to monitor some
input to the system and update a jack by calling snd_soc_jack_report(),
specifying a subset of bits to update. The jack detection code should
be set up by the machine driver, taking configuration for the jack to
update and the set of things to report when the jack is connected.
Often this is done based on the status of a GPIO - a handler for this is
provided by the snd_soc_jack_add_gpio() function. Other methods are
also available, for example integrated into CODECs. One example of
CODEC integrated jack detection can be see in the WM8350 driver.
Each jack may have multiple reporting mechanisms, though it will need at
least one to be useful.
Machine drivers
===============
These are all hooked together by the machine driver depending on the
system hardware. The machine driver will set up the snd_soc_jack and
the list of pins to update then set up one or more jack detection
mechanisms to update that jack based on their current status.
@@ -0,0 +1,123 @@
===================
ASoC Machine Driver
===================
The ASoC machine (or board) driver is the code that glues together all the
component drivers (e.g. codecs, platforms and DAIs). It also describes the
relationships between each component which include audio paths, GPIOs,
interrupts, clocking, jacks and voltage regulators.
The machine driver can contain codec and platform specific code. It registers
the audio subsystem with the kernel as a platform device and is represented by
the following struct:-
::
/* SoC machine */
struct snd_soc_card {
char *name;
...
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
/* the pre and post PM functions are used to do any PM work before and
* after the codec and DAIs do any PM work. */
int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
int (*resume_pre)(struct platform_device *pdev);
int (*resume_post)(struct platform_device *pdev);
...
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
...
};
probe()/remove()
----------------
probe/remove are optional. Do any machine specific probe here.
suspend()/resume()
------------------
The machine driver has pre and post versions of suspend and resume to take care
of any machine audio tasks that have to be done before or after the codec, DAIs
and DMA is suspended and resumed. Optional.
Machine DAI Configuration
-------------------------
The machine DAI configuration glues all the codec and CPU DAIs together. It can
also be used to set up the DAI system clock and for any machine related DAI
initialisation e.g. the machine audio map can be connected to the codec audio
map, unconnected codec pins can be set as such.
struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
::
/* corgi digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link corgi_dai = {
.name = "WM8731",
.stream_name = "WM8731",
.cpu_dai_name = "pxa-is2-dai",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm8713-codec.0-001a",
.init = corgi_wm8731_init,
.ops = &corgi_ops,
};
In the above struct, dai’s are registered using names but you can pass
either dai name or device tree node but not both. Also, names used here
for cpu/codec/platform dais should be globally unique.
Additionally below example macro can be used to register cpu, codec and
platform dai::
SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,
DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
struct snd_soc_card then sets up the machine with its DAIs. e.g.
::
/* corgi audio machine driver */
static struct snd_soc_card snd_soc_corgi = {
.name = "Corgi",
.dai_link = &corgi_dai,
.num_links = 1,
};
Following this, ``devm_snd_soc_register_card`` can be used to register
the sound card. During the registration, the individual components
such as the codec, CPU, and platform are probed. If all these components
are successfully probed, the sound card gets registered.
Machine Power Map
-----------------
The machine driver can optionally extend the codec power map and to become an
audio power map of the audio subsystem. This allows for automatic power up/down
of speaker/HP amplifiers, etc. Codec pins can be connected to the machines jack
sockets in the machine init function.
Machine Controls
----------------
Machine specific audio mixer controls can be added in the DAI init function.
Clocking Controls
-----------------
As previously noted, clock configuration is handled within the machine driver.
For details on the clock APIs that the machine driver can utilize for
setup, please refer to Documentation/sound/soc/clocking.rst. However, the
callback needs to be registered by the CPU/Codec/Platform drivers to configure
the clocks that is needed for the corresponding device operation.
@@ -0,0 +1,69 @@
=======================
ALSA SoC Layer Overview
=======================
The overall project goal of the ALSA System on Chip (ASoC) layer is to
provide better ALSA support for embedded system-on-chip processors (e.g.
pxa2xx, au1x00, iMX, etc) and portable audio codecs. Prior to the ASoC
subsystem there was some support in the kernel for SoC audio, however it
had some limitations:-
* Codec drivers were often tightly coupled to the underlying SoC
CPU. This is not ideal and leads to code duplication - for example,
Linux had different wm8731 drivers for 4 different SoC platforms.
* There was no standard method to signal user initiated audio events (e.g.
Headphone/Mic insertion, Headphone/Mic detection after an insertion
event). These are quite common events on portable devices and often require
machine specific code to re-route audio, enable amps, etc., after such an
event.
* Drivers tended to power up the entire codec when playing (or
recording) audio. This is fine for a PC, but tends to waste a lot of
power on portable devices. There was also no support for saving
power via changing codec oversampling rates, bias currents, etc.
ASoC Design
===========
The ASoC layer is designed to address these issues and provide the following
features :-
* Codec independence. Allows reuse of codec drivers on other platforms
and machines.
* Easy I2S/PCM audio interface setup between codec and SoC. Each SoC
interface and codec registers its audio interface capabilities with the
core and are subsequently matched and configured when the application
hardware parameters are known.
* Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
its minimum power state at all times. This includes powering up/down
internal power blocks depending on the internal codec audio routing and any
active streams.
* Pop and click reduction. Pops and clicks can be reduced by powering the
codec up/down in the correct sequence (including using digital mute). ASoC
signals the codec when to change power states.
* Machine specific controls: Allow machines to add controls to the sound card
(e.g. volume control for speaker amplifier).
To achieve all this, ASoC basically splits an embedded audio system into
multiple re-usable component drivers :-
* Codec class drivers: The codec class driver is platform independent and
contains audio controls, audio interface capabilities, codec DAPM
definition and codec IO functions. This class extends to BT, FM and MODEM
ICs if required. Codec class drivers should be generic code that can run
on any architecture and machine.
* Platform class drivers: The platform class driver includes the audio DMA
engine driver, digital audio interface (DAI) drivers (e.g. I2S, AC97, PCM)
and any audio DSP drivers for that platform.
* Machine class driver: The machine driver class acts as the glue that
describes and binds the other component drivers together to form an ALSA
"sound card device". It handles any machine specific controls and
machine level audio events (e.g. turning on an amp at start of playback).
@@ -0,0 +1,78 @@
====================
ASoC Platform Driver
====================
An ASoC platform driver class can be divided into audio DMA drivers, SoC DAI
drivers and DSP drivers. The platform drivers only target the SoC CPU and must
have no board specific code.
Audio DMA
=========
The platform DMA driver optionally supports the following ALSA operations:-
::
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
int (*trigger)(struct snd_pcm_substream *, int);
};
The platform driver exports its DMA functionality via struct
snd_soc_component_driver:-
::
struct snd_soc_component_driver {
const char *name;
...
int (*probe)(struct snd_soc_component *);
void (*remove)(struct snd_soc_component *);
int (*suspend)(struct snd_soc_component *);
int (*resume)(struct snd_soc_component *);
/* pcm creation and destruction */
int (*pcm_new)(struct snd_soc_pcm_runtime *);
void (*pcm_free)(struct snd_pcm *);
...
const struct snd_pcm_ops *ops;
const struct snd_compr_ops *compr_ops;
...
};
Please refer to the :doc:`ALSA driver documentation
<../kernel-api/writing-an-alsa-driver>` for details of audio DMA.
An example DMA driver is soc/pxa/pxa2xx-pcm.c
SoC DAI Drivers
===============
Each SoC DAI driver must provide the following features:-
1. Digital audio interface (DAI) description
2. Digital audio interface configuration
3. PCM's description
4. SYSCLK configuration
5. Suspend and resume (optional)
Please see codec.rst for a description of items 1 - 4.
SoC DSP Drivers
===============
Each SoC DSP driver usually supplies the following features :-
1. DAPM graph
2. Mixer controls
3. DMA IO to/from DSP buffers (if applicable)
4. Definition of DSP front end (FE) PCM devices.
Please see dpcm.rst for a description of item 4.
@@ -0,0 +1,55 @@
=====================
Audio Pops and Clicks
=====================
Pops and clicks are unwanted audio artifacts caused by the powering up and down
of components within the audio subsystem. This is noticeable on PCs when an
audio module is either loaded or unloaded (at module load time the sound card is
powered up and causes a popping noise on the speakers).
Pops and clicks can be more frequent on portable systems with DAPM. This is
because the components within the subsystem are being dynamically powered
depending on the audio usage and this can subsequently cause a small pop or
click every time a component power state is changed.
Minimising Playback Pops and Clicks
===================================
Playback pops in portable audio subsystems cannot be completely eliminated
currently, however future audio codec hardware will have better pop and click
suppression. Pops can be reduced within playback by powering the audio
components in a specific order. This order is different for startup and
shutdown and follows some basic rules:-
::
Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
Shutdown Order :- Digital Mute --> Output PGA --> Mixers --> DAC
This assumes that the codec PCM output path from the DAC is via a mixer and then
a PGA (programmable gain amplifier) before being output to the speakers.
Minimising Capture Pops and Clicks
==================================
Capture artifacts are somewhat easier to get rid as we can delay activating the
ADC until all the pops have occurred. This follows similar power rules to
playback in that components are powered in a sequence depending upon stream
startup or shutdown.
::
Startup Order - Input PGA --> Mixers --> ADC
Shutdown Order - ADC --> Mixers --> Input PGA
Zipper Noise
============
An unwanted zipper noise can occur within the audio playback or capture stream
when a volume control is changed near its maximum gain value. The zipper noise
is heard when the gain increase or decrease changes the mean audio signal
amplitude too quickly. It can be minimised by enabling the zero cross setting
for each volume control. The ZC forces the gain change to occur when the signal
crosses the zero amplitude line.
+482
View File
@@ -0,0 +1,482 @@
================
ASoC USB support
================
Overview
========
In order to leverage the existing USB sound device support in ALSA, the
ASoC USB APIs are introduced to allow the subsystems to exchange
configuration information.
One potential use case would be to support USB audio offloading, which is
an implementation that allows for an alternate power-optimized path in the audio
subsystem to handle the transfer of audio data over the USB bus. This would
let the main processor to stay in lower power modes for longer duration. The
following is an example design of how the ASoC and ALSA pieces can be connected
together to achieve this:
::
USB | ASoC
| _________________________
| | ASoC Platform card |
| |_________________________|
| | |
| ___V____ ____V____
| |ASoC BE | |ASoC FE |
| |DAI LNK | |DAI LNK |
| |________| |_________|
| ^ ^ ^
| | |________|
| ___V____ |
| |SoC-USB | |
________ ________ | | |
|USB SND |<--->|USBSND |<------------>|________| |
|(card.c)| |offld |<---------- |
|________| |________|___ | | |
^ ^ | | | ____________V_________
| | | | | |IPC |
__ V_______________V_____ | | | |______________________|
|USB SND (endpoint.c) | | | | ^
|_________________________| | | | |
^ | | | ___________V___________
| | | |->|audio DSP |
___________V_____________ | | |_______________________|
|XHCI HCD |<- |
|_________________________| |
SoC USB driver
==============
Structures
----------
``struct snd_soc_usb``
- ``list``: list head for SND SoC struct list
- ``component``: reference to ASoC component
- ``connection_status_cb``: callback to notify connection events
- ``update_offload_route_info``: callback to fetch selected USB sound card/PCM
device
- ``priv_data``: driver data
The snd_soc_usb structure can be referenced using the ASoC platform card
device, or a USB device (udev->dev). This is created by the ASoC BE DAI
link, and the USB sound entity will be able to pass information to the
ASoC BE DAI link using this structure.
``struct snd_soc_usb_device``
- ``card_idx``: sound card index associated with USB sound device
- ``chip_idx``: USB sound chip array index
- ``cpcm_idx``: capture pcm device indexes associated with the USB sound device
- ``ppcm_idx``: playback pcm device indexes associated with the USB sound device
- ``num_playback``: number of playback streams
- ``num_capture``: number of capture streams
- ``list``: list head for the USB sound device list
The struct snd_soc_usb_device is created by the USB sound offload driver.
This will carry basic parameters/limitations that will be used to
determine the possible offloading paths for this USB audio device.
Functions
---------
.. code-block:: rst
int snd_soc_usb_find_supported_format(int card_idx,
struct snd_pcm_hw_params *params, int direction)
..
- ``card_idx``: the index into the USB sound chip array.
- ``params``: Requested PCM parameters from the USB DPCM BE DAI link
- ``direction``: capture or playback
**snd_soc_usb_find_supported_format()** ensures that the requested audio profile
being requested by the external DSP is supported by the USB device.
Returns 0 on success, and -EOPNOTSUPP on failure.
.. code-block:: rst
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
..
- ``usbdev``: the usb device that was discovered
- ``sdev``: capabilities of the device
**snd_soc_usb_connect()** notifies the ASoC USB DPCM BE DAI link of a USB
audio device detection. This can be utilized in the BE DAI
driver to keep track of available USB audio devices. This is intended
to be called by the USB offload driver residing in USB SND.
Returns 0 on success, negative error code on failure.
.. code-block:: rst
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
..
- ``usbdev``: the usb device that was removed
- ``sdev``: capabilities to free
**snd_soc_usb_disconnect()** notifies the ASoC USB DPCM BE DAI link of a USB
audio device removal. This is intended to be called by the USB offload
driver that resides in USB SND.
.. code-block:: rst
void *snd_soc_usb_find_priv_data(struct device *usbdev)
..
- ``usbdev``: the usb device to reference to find private data
**snd_soc_usb_find_priv_data()** fetches the private data saved to the SoC USB
device.
Returns pointer to priv_data on success, NULL on failure.
.. code-block:: rst
int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack)
..
- ``component``: ASoC component to add the jack
- ``jack``: jack component to populate
**snd_soc_usb_setup_offload_jack()** is a helper to add a sound jack control to
the platform sound card. This will allow for consistent naming to be used on
designs that support USB audio offloading. Additionally, this will enable the
jack to notify of changes.
Returns 0 on success, negative otherwise.
.. code-block:: rst
int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
int direction, enum snd_soc_usb_kctl path,
long *route)
..
- ``dev``: USB device to look up offload path mapping
- ``card``: USB sound card index
- ``pcm``: USB sound PCM device index
- ``direction``: direction to fetch offload routing information
- ``path``: kcontrol selector - pcm device or card index
- ``route``: mapping of sound card and pcm indexes for the offload path. This is
an array of two integers that will carry the card and pcm device indexes
in that specific order. This can be used as the array for the kcontrol
output.
**snd_soc_usb_update_offload_route()** calls a registered callback to the USB BE DAI
link to fetch the information about the mapped ASoC devices for executing USB audio
offload for the device. ``route`` may be a pointer to a kcontrol value output array,
which carries values when the kcontrol is read.
Returns 0 on success, negative otherwise.
.. code-block:: rst
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
void *data);
..
- ``component``: DPCM BE DAI link component
- ``data``: private data
**snd_soc_usb_allocate_port()** allocates a SoC USB device and populates standard
parameters that is used for further operations.
Returns a pointer to struct soc_usb on success, negative on error.
.. code-block:: rst
void snd_soc_usb_free_port(struct snd_soc_usb *usb);
..
- ``usb``: SoC USB device to free
**snd_soc_usb_free_port()** frees a SoC USB device.
.. code-block:: rst
void snd_soc_usb_add_port(struct snd_soc_usb *usb);
..
- ``usb``: SoC USB device to add
**snd_soc_usb_add_port()** add an allocated SoC USB device to the SOC USB framework.
Once added, this device can be referenced by further operations.
.. code-block:: rst
void snd_soc_usb_remove_port(struct snd_soc_usb *usb);
..
- ``usb``: SoC USB device to remove
**snd_soc_usb_remove_port()** removes a SoC USB device from the SoC USB framework.
After removing a device, any SOC USB operations would not be able to reference the
device removed.
How to Register to SoC USB
--------------------------
The ASoC DPCM USB BE DAI link is the entity responsible for allocating and
registering the SoC USB device on the component bind. Likewise, it will
also be responsible for freeing the allocated resources. An example can
be shown below:
.. code-block:: rst
static int q6usb_component_probe(struct snd_soc_component *component)
{
...
data->usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
if (!data->usb)
return -ENOMEM;
usb->connection_status_cb = q6usb_alsa_connection_cb;
ret = snd_soc_usb_add_port(usb);
if (ret < 0) {
dev_err(component->dev, "failed to add usb port\n");
goto free_usb;
}
...
}
static void q6usb_component_remove(struct snd_soc_component *component)
{
...
snd_soc_usb_remove_port(data->usb);
snd_soc_usb_free_port(data->usb);
}
static const struct snd_soc_component_driver q6usb_dai_component = {
.probe = q6usb_component_probe,
.remove = q6usb_component_remove,
.name = "q6usb-dai-component",
...
};
..
BE DAI links can pass along vendor specific information as part of the
call to allocate the SoC USB device. This will allow any BE DAI link
parameters or settings to be accessed by the USB offload driver that
resides in USB SND.
USB Audio Device Connection Flow
--------------------------------
USB devices can be hotplugged into the USB ports at any point in time.
The BE DAI link should be aware of the current state of the physical USB
port, i.e. if there are any USB devices with audio interface(s) connected.
connection_status_cb() can be used to notify the BE DAI link of any change.
This is called whenever there is a USB SND interface bind or remove event,
using snd_soc_usb_connect() or snd_soc_usb_disconnect():
.. code-block:: rst
static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
{
...
snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
...
}
static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
{
...
snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev);
...
}
..
In order to account for conditions where driver or device existence is
not guaranteed, USB SND exposes snd_usb_rediscover_devices() to resend the
connect events for any identified USB audio interfaces. Consider the
the following situation:
**usb_audio_probe()**
| --> USB audio streams allocated and saved to usb_chip[]
| --> Propagate connect event to USB offload driver in USB SND
| --> **snd_soc_usb_connect()** exits as USB BE DAI link is not ready
BE DAI link component probe
| --> DAI link is probed and SoC USB port is allocated
| --> The USB audio device connect event is missed
To ensure connection events are not missed, **snd_usb_rediscover_devices()**
is executed when the SoC USB device is registered. Now, when the BE DAI
link component probe occurs, the following highlights the sequence:
BE DAI link component probe
| --> DAI link is probed and SoC USB port is allocated
| --> SoC USB device added, and **snd_usb_rediscover_devices()** runs
**snd_usb_rediscover_devices()**
| --> Traverses through usb_chip[] and for non-NULL entries issue
| **connection_status_cb()**
In the case where the USB offload driver is unbound, while USB SND is ready,
the **snd_usb_rediscover_devices()** is called during module init. This allows
for the offloading path to also be enabled with the following flow:
**usb_audio_probe()**
| --> USB audio streams allocated and saved to usb_chip[]
| --> Propagate connect event to USB offload driver in USB SND
| --> USB offload driver **NOT** ready!
BE DAI link component probe
| --> DAI link is probed and SoC USB port is allocated
| --> No USB connect event due to missing USB offload driver
USB offload driver probe
| --> **qc_usb_audio_offload_init()**
| --> Calls **snd_usb_rediscover_devices()** to notify of devices
USB Offload Related Kcontrols
=============================
Details
-------
A set of kcontrols can be utilized by applications to help select the proper sound
devices to enable USB audio offloading. SoC USB exposes the get_offload_dev()
callback that designs can use to ensure that the proper indices are returned to the
application.
Implementation
--------------
**Example:**
**Sound Cards**:
::
0 [SM8250MTPWCD938]: sm8250 - SM8250-MTP-WCD9380-WSA8810-VA-D
SM8250-MTP-WCD9380-WSA8810-VA-DMIC
1 [Seri ]: USB-Audio - Plantronics Blackwire 3225 Seri
Plantronics Plantronics Blackwire
3225 Seri at usb-xhci-hcd.1.auto-1.1,
full sp
2 [C320M ]: USB-Audio - Plantronics C320-M
Plantronics Plantronics C320-M at usb-xhci-hcd.1.auto-1.2, full speed
**PCM Devices**:
::
card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 0: MultiMedia1 (*) []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 1: MultiMedia2 (*) []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Seri [Plantronics Blackwire 3225 Seri], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 2: C320M [Plantronics C320-M], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
**USB Sound Card** - card#1:
::
USB Offload Playback Card Route PCM#0 -1 (range -1->32)
USB Offload Playback PCM Route PCM#0 -1 (range -1->255)
**USB Sound Card** - card#2:
::
USB Offload Playback Card Route PCM#0 0 (range -1->32)
USB Offload Playback PCM Route PCM#0 1 (range -1->255)
The above example shows a scenario where the system has one ASoC platform card
(card#0) and two USB sound devices connected (card#1 and card#2). When reading
the available kcontrols for each USB audio device, the following kcontrols lists
the mapped offload card and pcm device indexes for the specific USB device:
``USB Offload Playback Card Route PCM#*``
``USB Offload Playback PCM Route PCM#*``
The kcontrol is indexed, because a USB audio device could potentially have
several PCM devices. The above kcontrols are defined as:
- ``USB Offload Playback Card Route PCM#`` **(R)**: Returns the ASoC platform sound
card index for a mapped offload path. The output **"0"** (card index) signifies
that there is an available offload path for the USB SND device through card#0.
If **"-1"** is seen, then no offload path is available for the USB SND device.
This kcontrol exists for each USB audio device that exists in the system, and
its expected to derive the current status of offload based on the output value
for the kcontrol along with the PCM route kcontrol.
- ``USB Offload Playback PCM Route PCM#`` **(R)**: Returns the ASoC platform sound
PCM device index for a mapped offload path. The output **"1"** (PCM device index)
signifies that there is an available offload path for the USB SND device through
PCM device#0. If **"-1"** is seen, then no offload path is available for the USB\
SND device. This kcontrol exists for each USB audio device that exists in the
system, and its expected to derive the current status of offload based on the
output value for this kcontrol, in addition to the card route kcontrol.
USB Offload Playback Route Kcontrol
-----------------------------------
In order to allow for vendor specific implementations on audio offloading device
selection, the SoC USB layer exposes the following:
.. code-block:: rst
int (*update_offload_route_info)(struct snd_soc_component *component,
int card, int pcm, int direction,
enum snd_soc_usb_kctl path,
long *route)
..
These are specific for the **USB Offload Playback Card Route PCM#** and **USB
Offload PCM Route PCM#** kcontrols.
When users issue get calls to the kcontrol, the registered SoC USB callbacks will
execute the registered function calls to the DPCM BE DAI link.
**Callback Registration:**
.. code-block:: rst
static int q6usb_component_probe(struct snd_soc_component *component)
{
...
usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
if (IS_ERR(usb))
return -ENOMEM;
usb->connection_status_cb = q6usb_alsa_connection_cb;
usb->update_offload_route_info = q6usb_get_offload_dev;
ret = snd_soc_usb_add_port(usb);
..
Existing USB Sound Kcontrol
---------------------------
With the introduction of USB offload support, the above USB offload kcontrol
will be added to the pre existing list of kcontrols identified by the USB sound
framework. These kcontrols are still the main controls that are used to
modify characteristics pertaining to the USB audio device.
::
Number of controls: 9
ctl type num name value
0 INT 2 Capture Channel Map 0, 0 (range 0->36)
1 INT 2 Playback Channel Map 0, 0 (range 0->36)
2 BOOL 1 Headset Capture Switch On
3 INT 1 Headset Capture Volume 10 (range 0->13)
4 BOOL 1 Sidetone Playback Switch On
5 INT 1 Sidetone Playback Volume 4096 (range 0->8192)
6 BOOL 1 Headset Playback Switch On
7 INT 2 Headset Playback Volume 20, 20 (range 0->24)
8 INT 1 USB Offload Playback Card Route PCM#0 0 (range -1->32)
9 INT 1 USB Offload Playback PCM Route PCM#0 1 (range -1->255)
Since USB audio device controls are handled over the USB control endpoint, use the
existing mechanisms present in the USB mixer to set parameters, such as volume.