\documentclass[aspectratio=169,obeyspaces,spaces,hyphens,dvipsnames]{beamer}
\usepackage{minted}
\usepackage{newunicodechar}
\usepackage{hyperref}

\mode<presentation>
\usetheme{Bootlin}

\title{ASoC: Supporting Audio on an Embedded Board}

\authors{Alexandre Belloni}
\email{alexandre.belloni@bootlin.com}
\slidesurl{http://bootlin.com/pub/conferences/2020/lee/belloni-alsa-asoc/}

\begin{document}

\begin{frame}{Live Embedded Event 2020}
  \fontsize{19}{19}\selectfont
  ASoC: Supporting Audio on an Embedded Board\\
  \begin{columns}
    \column{0.6\textwidth}
    \vfill
    \Large
    Alexandre Belloni\\
    Bootlin\\
    \normalsize
    {\em alexandre.belloni@bootlin.com}
    \column{0.4\textwidth}
    \includegraphics[width=0.8\textwidth]{TUX_G2_hp.pdf}\\
  \end{columns}
\end{frame}

\begin{frame}{Alexandre Belloni}
  \begin{columns}
    \column{0.6\textwidth}
    \begin{itemize}
    \item Embedded Linux engineer at Bootlin
      \begin{itemize}
      \item Embedded Linux {\bf expertise}
      \item {\bf Development}, consulting and training
      \item Bootloader, Linux kernel, Yocto Project, Buildroot
      \item Complete Linux BSP development
      \item Strong open-source focus: upstreaming and contributions
      \item Freely available training materials
      \end{itemize}
    \item Open-source contributor
      \begin{itemize}
      \item Maintainer for the Linux kernel {\bf I3C subsystem}
      \item Maintainer for the Linux kernel {\bf RTC subsystem}
      \item Co-Maintainer of {\bf kernel support for Microchip (ARM and MIPS)
        processors}
      \end{itemize}
    \end{itemize}
    \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{common/logo-square.pdf}\\
  \end{columns}
\end{frame}

\begin{frame}{Anatomy}
    \includegraphics[width=\textwidth]{anatomy.pdf}\\
\end{frame}

\begin{frame}{Anatomy}
  \begin{itemize}
  \item codec configuration usually happens on a simple serial bus, I2C
    or SPI.
  \item SoC DAI: the SoC Digital Audio Interface.
    \begin{itemize}
    \item sometimes called synchronous serial interface
    \item provides audio data to the codec
    \item formats are usually AC97, I2S, PCM (TDM, network mode), DSP
      A/B
    \item Examples: Atmel SSC, NXP SSI, TI McASP.
    \item Some SoCs have a separate SPDIF controller
    \end{itemize}
  \item Amplifier is optional
  \end{itemize}
  Some SoCs (Allwinner A33, Atmel SAMA5D2) have the codec and the
  amplifier on the SoC itself.
\end{frame}

\begin{frame}[fragile]{Digital formats - signals}
  \begin{center}
    \includegraphics[height=0.4\textheight]{i2s.pdf}\\
  \end{center}
\end{frame}

\begin{frame}[fragile]{Digital formats - DAI}
  \begin{itemize}
  \item The Digital Audio Interface is a serial interface
  \item It uses two clocks: the bit clock and the frame clock.
  \begin{itemize}
  \item The bit clock is usually referred to as BCK or BCLK
  \item The frame clock is often called FCLK/FSCK/FSCLK, LRCK/LRCLK
    (Left Right clock) or WCLK (word clock). Its rate is the sample
    rate also called Fs.
  \item The relationship between BCK and FSCK is: bck = fsck ∗
    Nchannels ∗ BitDepth
  \end{itemize}
  \item It also has one or multiple data lines.
  \end{itemize}
\end{frame}

\begin{frame}{MCLK}
  \begin{itemize}
  \item MCLK is the codec clock. It is sometimes referred as the
    system clock. The IC needs it to be working.
  \item Some codecs will also require it to be able to use the
    control interface.
  \item Can be provided by the SoC when it has suitable clocks or a
    crystal.
  \item Some codecs are able to use BCLK or LRCLK as their clock,
    making MCLK optional.
  \item usually the codecs will expect MCLK to be a multiple of BCLK.
    Usually specified as a multiple of Fs.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Clocks: master/slave}
  \begin{itemize}
  \item One of the DAI is responsible to generate the bit clock, it is
    the bit clock master.
  \item One of the DAI is responsible to generate the frame clock, it is
    the frame master.
  \item Some have a great set of PLLs and dividers, allowing to get a
    precise BCLK from many different MCLK rates.
  \item Quite often, it is better to use the codec as master. However,
    some SoCs have specialized audio PLLs.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Digital formats - Data}
  \begin{itemize}
  \item Codecs may have multiple data in or data out lines, one line
    per channel pair.
  \item Codecs may also have multiple DAI, one full interface for data
    in and one for data out.
  \end{itemize}
  \begin{columns}
    \column{0.5\textwidth}
    e.g. AD1937 has:
    \begin{itemize}
    \item 8 DACs in 4 pairs, 4 ADCs in 2 pairs
    \item clocks for data-in: DBCLK, DLRCLK
    \item 4 data-in lines (DSDATA[1-4])
    \item clocks for data-out: ABCLK, ALRCLK
    \item 2 data-out lines (ASDATA[1-4])
    \end{itemize}
    \column{0.5\textwidth}
    \includegraphics[width=\textwidth]{ad1937.pdf}\\
  \end{columns}
\end{frame}

\begin{frame}{Digital formats - Left Justified}
    \includegraphics[width=\textwidth]{LJ.png}\\
\end{frame}

\begin{frame}{Digital formats - Right Justified}
    \includegraphics[width=\textwidth]{RJ.png}\\
\end{frame}

\begin{frame}{Digital formats - I2S}
    \includegraphics[width=\textwidth]{I2S.png}\\
\end{frame}

\begin{frame}{Digital formats - DSP A}
    \includegraphics[width=\textwidth]{DSP_A.png}\\
\end{frame}

\begin{frame}{Digital formats - DSP B}
    \includegraphics[width=\textwidth]{DSP_B.png}\\
\end{frame}

\begin{frame}{Digital formats - TDM}
    \includegraphics[width=\textwidth]{TDM.png}\\
\end{frame}

\begin{frame}{Digital formats - PDM}
  Two signals per channels, clock and data\\
  \begin{center}
    \includegraphics[height=0.8\textheight]{PDM.pdf}\\
  \end{center}
\end{frame}

\begin{frame}{ASoC}
  ASoC, ALSA System on Chip: is a Linux kernel subsystem created to
  provide better ALSA support for system-on-chip and portable audio
  codecs. It allows to reuse codec drivers across multiple
  architectures and provides an API to integrate them with the SoC
  audio interface.
  \begin{itemize}
  \item created for that use case
  \item designed for codec drivers reuse
  \item has an API to write codec drivers
  \item has an API to write SoC interface drivers
  \end{itemize}
\end{frame}

\begin{frame}{ASoC components}
  \begin{itemize}
  \item Codec class drivers: define the codec capabilities (audio
    interface, audio controls, analog inputs and outputs).
  \item Platform class drivers: defines the SoC audio interface (also
    referred as CPU DAI), sets up DMA when applicable.
  \item Codec to platform integration: nowadays, usually done through
    device tree, previously required writing a machine driver in C.
    See my 2016 ELC-E talk.
  \end{itemize}
  Note: The codec can be part of another IC (PMIC, Bluetooth or MODEM
  chips).
\end{frame}

\begin{frame}[fragile]{simple-card}
  Most sound cards, can now be described using device tree. This is
  done using a sound node with a \code{simple-audio-card} compatible
  string.
  \begin{itemize}
  \item The DT bindings are documented in
    \code{Documentation/devicetree/bindings/sound/simple-card.yaml}
  \item The driver handling it is \code{sound/soc/generic/simple-card.c}
  \end{itemize}
  Since 2017, OF-graph based bindings are available.
  \begin{itemize}
  \item They are documented in
    \code{Documentation/devicetree/bindings/sound/audio-graph-card.txt}
  \item The driver handling it is \code{sound/soc/generic/audio-graph-card.c}
  \end{itemize}
  Both required a few changes in the SoC DAI drivers to be usable for
  example to select the audio mode for the SSC on Microchip SoCs or
  configure properly the i.MX audmux.
\end{frame}

\begin{frame}[fragile]{simple-card - example 1}
  Let's say we have an ADAU1372 codec connected to an i.Mx6UL SAI.
  First, enable the SAI and the codec:
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
&sai2 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_sai2>;
        status = "okay";
};

&i2c1 {
        adau1372: codec@3c {
                #sound-dai-cells = <0>;
                compatible = "adi,adau1372";
                reg = <0x3c>;
                clock-names = "mclk";
                clocks = <&adau1372z_xtal>;
        };
};

/ {
        adau1372z_xtal: adau1372z_xtal {
                compatible = "fixed-clock";
                #clock-cells = <0>;
                clock-frequency = <12288000>;
        };
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{simple-card - example 1}
  Now, describe the sound card:
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
        sound {
                compatible = "simple-audio-card";
                simple-audio-card,name = "imx6ul-adau1372";

                simple-audio-card,dai-link@0 {
                        format = "i2s";
                        bitclock-master = <&adau1372_dai>;
                        frame-master = <&adau1372_dai>;

                        sai2_dai: cpu {
                                sound-dai = <&sai2>;
                        };

                        adau1372_dai: codec {
                                sound-dai = <&adau1372>;
                        };
                };
        };
    \end{minted}
  \end{block}
  For convenience, the codec is the master, it generates both BCLK and
  FSCLK.
\end{frame}

\begin{frame}[fragile]{simple-card - example 2}
  The ADAU1372 has actually 4 channels and can do TDM:
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
        sound {
                compatible = "simple-audio-card";
                simple-audio-card,name = "imx6ul-adau1372";

                simple-audio-card,dai-link@0 {
                        format = "i2s";
                        bitclock-master = <&adau1372_dai>;
                        frame-master = <&adau1372_dai>;

                        sai2_dai: cpu {
                                sound-dai = <&sai2>;
                                dai-tdm-slot-num = <4>;
                                dai-tdm-slot-width = <32>;
                        };

                        adau1372_dai: codec {
                                sound-dai = <&adau1372>;
                                dai-tdm-slot-num = <4>;
                                dai-tdm-slot-width = <32>;
                        };
                };
        };
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{simple-card - example 3}
  However, the ADAU1372 has an hardware issue and doesn't generate the
  proper BCLK when doing TDM4 with a 32kHz sample rate. The SAI has to
  be master:
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
        sound {
                compatible = "simple-audio-card";
                simple-audio-card,name = "imx6ul-adau1372";

                simple-audio-card,dai-link@0 {
                        format = "i2s";
                        bitclock-master = <&sai2_dai>;
                        frame-master = <&sai2_dai>;

                        sai2_dai: cpu {
                                sound-dai = <&sai2>;
                                dai-tdm-slot-num = <4>;
                                dai-tdm-slot-width = <32>;
                        };

                        adau1372_dai: codec {
                                sound-dai = <&adau1372>;
                                dai-tdm-slot-num = <4>;
                                dai-tdm-slot-width = <32>;
                        };
                };
        };
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{simple-card - example 3}
  The result is not what is expected:
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{console}
    # aplay test.wav
    Playing WAVE 'test.wav' :
    Signed 16 bit Little Endian, Rate  32000 Hz, Stereo
    aplay: set_params:1403: Unable to install hw params:
    [...]
    # dmesg
    [...]
    fsl-sai 202c000.sai: failed to derive required Tx rate: 4096000
    fsl-sai 202c000.sai: ASoC: can't set 202c000.sai hw params: -22
    # cat /sys/kernel/debug/clk/clk_summary
    pll3                              1        1        0   480000000          0     0  50000
       pll3_bypass                    1        1        0   480000000          0     0  50000
          pll3_usb_otg                2        3        0   480000000          0     0  50000
             pll3_pfd2_508m           0        0        0   508235294          0     0  50000
                sai2_sel              0        0        0   508235294          0     0  50000
                   sai2_pred          0        0        0   127058824          0     0  50000
                      sai2_podf       0        0        0    63529412          0     0  50000
                         sai2         0        0        0    63529412          0     0  50000
    \end{minted}
  \end{block}
  Indeed, there is no way for the SAI to divide 63529412 to get the
  proper BCLK!
\end{frame}

\begin{frame}[fragile]{device tree - clocks}
  It is possible to reparent clocks using
  \code{assigned-clock-parents} and set the clock rate using
  \code{assigned-clock-rates}.
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
&sai2 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_sai2>;
        assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>, <&clks IMX6UL_CLK_SAI2>;
        assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
        assigned-clock-rates = <196608000>, <24576000>;
        status = "okay";
};
    \end{minted}
  \end{block}
  Notice that 24.576MHz was selected for the sai input clock as it is
  not able to divide by 3 to obtain the 4.096MHz BCLK.
\end{frame}

\begin{frame}[fragile]{simple-card - example 4}
  There is a possible cost reduction, the SAI is able to output its
  clock to feed to the codec MCLK instead of the crystal:
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
&sai2 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_sai2>;
        fsl,sai-mclk-direction-output;
        status = "okay";
};

&i2c1 {
        adau1372: codec@3c {
                #sound-dai-cells = <0>;
                compatible = "adi,adau1372";
                reg = <0x3c>;
                clock-names = "mclk";
                clocks = <&clks IMX6UL_CLK_SAI2>;
                assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>, <&clks IMX6UL_CLK_SAI2>;
                assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
                assigned-clock-rates = <196608000>, <24576000>;
        };
};
    \end{minted}
  \end{block}
  This replaces the 12.288MHz crystal by the 24.576 MCLK from the SAI.
  This works because the codec has a configurable divider for MCLK and
  can divide by 2. Also the clock parents and rates assignment has
  moved to the codec because of probing order.
\end{frame}

\begin{frame}[fragile]{simple-card - routing}
  It is possible but not mandatory to list the actual audio
  connections present on the board, this is called routing.
  The first step is to define the board connectors, in this case two
  stereo line input jack (Line0 and Line1) and a stereo jack output.
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
        simple-audio-card,widgets =
                "Line", "Line0",
                "Line", "Line1",
                "Headphone", "Headphone Jack",
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{simple-card - routing}
  Routing audio from the codec to the board connector is then done
  using \code{simple-audio-card,routing}
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
        simple-audio-card,routing =
                "AIN0", "Line0",
                "AIN1", "Line0",
                "AIN2", "Line1",
                "AIN3", "Line1",
                "Headphone Jack", "HPOUTL",
                "Headphone Jack", "HPOUTR",
    \end{minted}
  \end{block}
  Look for \code{SND_SOC_DAPM_OUTPUT} and \code{SND_SOC_DAPM_INPUT} to
  know what the codec is providing.
\end{frame}

\begin{frame}[fragile]{Amplifier}
  What about the amplifier?
  \begin{itemize}
  \item Supported using {\em auxiliary devices}
  \item There is a driver for simple amplifiers driven by a single
    GPIO, \code{simple-amplifier}.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{simple-amplifier}
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
        dio2133: analog-amplifier {
                compatible = "simple-audio-amplifier";
                sound-name-prefix = "AU2";
                VCC-supply = <&hdmi_5v>;
                enable-gpios = <&gpio GPIOH_5 GPIO_ACTIVE_HIGH>;
        };

        sound {
                compatible = "amlogic,gx-sound-card";
                model = "GXL-LIBRETECH-S905X-CC";
                audio-aux-devs = <&dio2133>;
                audio-widgets = "Line", "Lineout";
                audio-routing = "AU2 INL", "ACODEC LOLN",
                                "AU2 INR", "ACODEC LORN",
                                "Lineout", "AU2 OUTL",
                                "Lineout", "AU2 OUTR";
    \end{minted}
  \end{block}
  Audio is routed through\code{AU2}, the amplifier.
\end{frame}

\begin{frame}{Troubleshooting: no sound}
  Audio seems to play for the correct duration but there is no sound:
  \begin{itemize}
  \item Unmute \code{Master} and the relevant controls
  \item Turn up the volume
  \item Check the codec analog muxing and mixing (use alsamixer)
  \item Check the amplifier configuration
  \item Check the routing
  \end{itemize}
\end{frame}

\begin{frame}{Troubleshooting: no sound}
  When trying to play sound but it seems stuck:
  \begin{itemize}
  \item Check pinmuxing
  \item Check the configured clock directions
  \item Check the master/slave configuration
  \item Check the clocks using an oscilloscope
  \item Check pinmuxing
  \item Some SoCs also have more muxing (NXP i.Mx AUDMUX, TI McASP)
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Troubleshooting: write error}
  \begin{block}{}
    \fontsize{10}{10}\selectfont
    \begin{minted}{console}
# aplay test.wav
Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
aplay: pcm_write:1737: write error: Input/output error
    \end{minted}
  \end{block}
  \begin{itemize}
  \item Usually caused by an issue in the routing
  \item Check that the codec driver exposes a stream named "Playback"
  \item Use \code{vizdapm}:
    \url{https://github.com/mihais/asoc-tools}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Troubleshooting: over/underruns}
  \begin{block}{}
    \fontsize{10}{10}\selectfont
    \begin{minted}{console}
# aplay test.wav 
Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
underrun!!! (at least 1.899 ms long)
underrun!!! (at least 0.818 ms long)
underrun!!! (at least 2.912 ms long)
underrun!!! (at least 8.558 ms long)
    \end{minted}
  \end{block}
  \begin{itemize}
  \item Usually caused by an imprecise BCLK
  \item Try to find a better PLL and dividers combination
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Troubleshooting: going further}
  \begin{itemize}
  \item Have a look at the CPU DAI driver and its callback. In
    particular: \code{.set_clkdiv} and \code{.set_sysclk} to
    understand how the various clock dividers are setup.
    \code{.hw_params} or \code{.set_dai_fmt} may do some muxing
  \item Have a look at the codec driver callbacks, \code{.set_sysclk}
    as the \code{clk_id} parameter is codec specific.
  \item Remember using a codec as slave is an uncommon configuration
    and is probably untested.
  \item When in doubt, use \code{devmem} or \code{i2cget}
  \end{itemize}
\end{frame}

\begin{frame}{Going further}
  Once the kernel and device tree part are working, it is often
  necessary to split multiple channel inputs and outputs in individual
  channels or pair of channels. This can be done efficiently in
  userspace using alsalib, more on that on our blog:
  \url{https://bootlin.com/blog/audio-multi-channel-routing-and-mixing-using-alsalib/}
\end{frame}

\begin{frame}{References}
  \begin{itemize}
  \item \code{Documentation/sound/alsa/soc/}
  \item Common Inter-IC Digital Interfaces for Audio Data Transfer by Jerad Lewis, Analog Devices, Inc.
    \url{http://www.analog.com/media/en/technical-documentation/technical-articles/MS-2275.pdf?doc=an-1327.pdf}
  \item I²S specification
    \url{https://web.archive.org/web/20060702004954/http://www.semiconductors.philips.com/acrobat_download/various/I2SBUS.pdf}
  \end{itemize}
\end{frame}

\questionslide

\end{document}
