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

\mode<presentation>
\usetheme{Bootlin}

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

\begin{document}

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

\begin{frame}{Embedded Linux Conference Europe 2016}
  \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 Strong open-source focus
      \end{itemize}
    \item Open-source contributor
      \begin{itemize}
      \item Maintainer for the Linux kernel {\bf RTC subsystem}
      \item Co-Maintainer of {\bf kernel support for Atmel ARM
        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}{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 Machine drivers: board specific driver that serves as a glue
    between the SoC interface driver and the codec driver.
    It describes how both are connected. If you properly selected your
    hardware components, this is the only driver that needs to e
    written.
  \end{itemize}
  Note: The codec can be part of another IC (Bluetooth or MODEM
  chips).
\end{frame}

\begin{frame}[fragile]{Machine driver}
The machine driver registers a \code{struct snd_soc_card}.
      \begin{block}{include/sound/soc.h}
        \fontsize{8}{8}\selectfont
        \begin{minted}{c}
int snd_soc_register_card(struct snd_soc_card *card);
int snd_soc_unregister_card(struct snd_soc_card *card);
int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card);
[...]
/* SoC card */
struct snd_soc_card {
        const char *name;
        const char *long_name;
        const char *driver_name;
        struct device *dev;
        struct snd_card *snd_card;
[...]
        /* CPU <--> Codec DAI links  */
        struct snd_soc_dai_link *dai_link;  /* predefined links only */
        int num_links;  /* predefined links only */
        struct list_head dai_link_list; /* all links */
        int num_dai_links;
[...]
};
        \end{minted}
      \end{block}
\end{frame}

\begin{frame}[fragile]{\code{struct snd_soc_dai_link}}
\code{struct snd_soc_dai_link} is used to create the link between the
  CPU DAI and the codec DAI.
  \begin{block}{include/sound/soc.h}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
struct snd_soc_dai_link {
        /* config - must be set by machine driver */
        const char *name;                        /* Codec name */
        const char *stream_name;                /* Stream name */
        /*
         * You MAY specify the link's CPU-side device, either by device name,
         * or by DT/OF node, but not both. If this information is omitted,
         * the CPU-side DAI is matched using .cpu_dai_name only, which hence
         * must be globally unique. These fields are currently typically used
         * only for codec to codec links, or systems using device tree.
         */
        const char *cpu_name;
        struct device_node *cpu_of_node;
        /*
         * You MAY specify the DAI name of the CPU DAI. If this information is
         * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node
         * only, which only works well when that device exposes a single DAI.
         */
        const char *cpu_dai_name;
     \end{minted}
   \end{block}
\end{frame}

\begin{frame}[fragile]{\code{struct snd_soc_dai_link}}
  \begin{block}{}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}
        /*
         * You MUST specify the link's codec, either by device name, or by
         * DT/OF node, but not both.
         */
        const char *codec_name;
        struct device_node *codec_of_node;
        /* You MUST specify the DAI name within the codec */
        const char *codec_dai_name;

        struct snd_soc_dai_link_component *codecs;
        unsigned int num_codecs;
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{\code{struct snd_soc_dai_link}}
  \begin{block}{}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}
        /*
         * You MAY specify the link's platform/PCM/DMA driver, either by
         * device name, or by DT/OF node, but not both. Some forms of link
         * do not need a platform.
         */
        const char *platform_name;
        struct device_node *platform_of_node;
        int id;        /* optional ID for machine driver link identification */

        const struct snd_soc_pcm_stream *params;
        unsigned int num_params;

        unsigned int dai_fmt;           /* format to set on init */
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example 1}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}
static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
        .name = "WM8904",
        .stream_name = "WM8904 PCM",
        .codec_dai_name = "wm8904-hifi",
        .dai_fmt = SND_SOC_DAIFMT_I2S
                | SND_SOC_DAIFMT_NB_NF
                | SND_SOC_DAIFMT_CBM_CFM,
        .ops = &atmel_asoc_wm8904_ops,
};

static struct snd_soc_card atmel_asoc_wm8904_card = {
        .name = "atmel_asoc_wm8904",
        .owner = THIS_MODULE,
        .dai_link = &atmel_asoc_wm8904_dailink,
        .num_links = 1,
        .dapm_widgets = atmel_asoc_wm8904_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(atmel_asoc_wm8904_dapm_widgets),
        .fully_routed = true,
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example 1}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
{
        struct device_node *np = pdev->dev.of_node;
        struct device_node *codec_np, *cpu_np;
        struct snd_soc_card *card = &atmel_asoc_wm8904_card;
        struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
[...]
        cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
        if (!cpu_np) {
                dev_err(&pdev->dev, "failed to get dai and pcm info\n");
                ret = -EINVAL;
                return ret;
        }
        dailink->cpu_of_node = cpu_np;
        dailink->platform_of_node = cpu_np;
        of_node_put(cpu_np);

        codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
        if (!codec_np) {
                dev_err(&pdev->dev, "failed to get codec info\n");
                ret = -EINVAL;
                return ret;
        }
        dailink->codec_of_node = codec_np;
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example 1}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
{
        struct snd_soc_card *card = &atmel_asoc_wm8904_card;
        struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
        int id, ret;

        card->dev = &pdev->dev;
        ret = atmel_asoc_wm8904_dt_init(pdev);
        if (ret) {
                dev_err(&pdev->dev, "failed to init dt info\n");
                return ret;
        }

        id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
        ret = atmel_ssc_set_audio(id);
        if (ret != 0) {
                dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id);
                return ret;
        }

        ret = snd_soc_register_card(card);
[...]
}
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Routing}
  After linking the codec driver with the SoC DAI driver, it is still
  necessary to define what are the codec outputs and inputs that are
  actually used on the board. This is called routing.

  \begin{itemize}
  \item statically: using the \code{.dapm_routes} and
    \code{.num_dapm_routes} members of \code{struct snd_soc_card}
  \item from device tree:
    \begin{minted}{c}
    int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                       const char *propname);
    \end{minted}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Routing example: static}
  \begin{block}{\code{sound/soc/rockchip/rockchip_max98090.c}}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
static const struct snd_soc_dapm_route rk_audio_map[] = {
        {"IN34", NULL, "Headset Mic"},
        {"IN34", NULL, "MICBIAS"},
        {"Headset Mic", NULL, "MICBIAS"},
        {"DMICL", NULL, "Int Mic"},
        {"Headphone", NULL, "HPL"},
        {"Headphone", NULL, "HPR"},
        {"Speaker", NULL, "SPKL"},
        {"Speaker", NULL, "SPKR"},
};
[...]
static struct snd_soc_card snd_soc_card_rk = {
        .name = "ROCKCHIP-I2S",
        .owner = THIS_MODULE,
        .dai_link = &rk_dailink,
        .num_links = 1,
[...]
        .dapm_widgets = rk_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
        .dapm_routes = rk_audio_map,
        .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
        .controls = rk_mc_controls,
        .num_controls = ARRAY_SIZE(rk_mc_controls),
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Routing example: DT}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}

static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
{
[...]
        ret = snd_soc_of_parse_card_name(card, "atmel,model");
        if (ret) {
                dev_err(&pdev->dev, "failed to parse card name\n");
                return ret;
        }

        ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
        if (ret) {
                dev_err(&pdev->dev, "failed to parse audio routing\n");
                return ret;
        }
[...]
}
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Routing example: DT}
  \begin{block}{\code{Documentation/devicetree/bindings/sound/atmel-wm8904.txt}}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
  - atmel,audio-routing: A list of the connections between audio components.
    Each entry is a pair of strings, the first being the connection's sink,
    the second being the connection's source. Valid names for sources and
    sinks are the WM8904's pins, and the jacks on the board:

    WM8904 pins:

    * IN1L
    * IN1R
    * IN2L
    * IN2R
    * IN3L
    * IN3R
    * HPOUTL
    * HPOUTR
    * LINEOUTL
    * LINEOUTR
    * MICBIAS

    Board connectors:

    * Headphone Jack
    * Line In Jack
    * Mic
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Routing example}
  \begin{block}{\code{Documentation/devicetree/bindings/sound/atmel-wm8904.txt}}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
Example:
sound {
        compatible = "atmel,asoc-wm8904";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_pck0_as_mck>;

        atmel,model = "wm8904 @ AT91SAM9N12EK";

        atmel,audio-routing =
                "Headphone Jack", "HPOUTL",
                "Headphone Jack", "HPOUTR",
                "IN2L", "Line In Jack",
                "IN2R", "Line In Jack",
                "Mic", "MICBIAS",
                "IN1L", "Mic";

        atmel,ssc-controller = <&ssc0>;
        atmel,audio-codec = <&wm8904>;
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Routing: codec pins}
  The available codec pins are defined in the codec driver. Look for
  the \code{SND_SOC_DAPM_INPUT} and \code{SND_SOC_DAPM_OUTPUT}
  definitions.

  \begin{block}{\code{sound/soc/codecs/wm8904.c}}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
static const struct snd_soc_dapm_widget wm8904_adc_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN1R"),
SND_SOC_DAPM_INPUT("IN2L"),
SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"),
[...]
};

static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = {
[...]
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
SND_SOC_DAPM_OUTPUT("LINEOUTL"),
SND_SOC_DAPM_OUTPUT("LINEOUTR"),
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Routing: board connectors}
  The board connectors are defined in the machine driver, in the
  \code{struct snd_soc_dapm_widget} part of the registered
  \code{struct snd_soc_card}. 
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}
static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_MIC("Mic", NULL),
        SND_SOC_DAPM_LINE("Line In Jack", NULL),
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Clocking}
  \begin{itemize}
  \item depending on characteristics (sample rate, bit depth) of the audio
    to be played or recorded, it may be necessary to reconfigure the
    clocks
  \item one of the most difficult part when writing a machine driver
  \item the SoC or the codec may be driving the clocks (master)
  \end{itemize}
\end{frame}
  
\begin{frame}[fragile]{Clocking: I²S}
  \begin{center}
    \includegraphics[height=0.5\textheight]{i2s.pdf}\\
    \includegraphics[height=0.3\textheight]{i2s_timings.pdf}\\
  \end{center}
\end{frame}

\begin{frame}[fragile]{Clocking}
  \begin{itemize}
  \item MCLK is the codec clock. The IC needs it to be working. Some
    codecs are able to use BLCK as their clock. It is sometimes
    referred as the system clock.
  \item BCLK is the bit clock. It is provided by the {\em master}
    (either the SoC or the codec)
  \item WCLK is the word clock. It is often called LRCLK (Left Right
    clock) or FCLK/FSCLK (Frame clock). It is provided by the {\em master}
    (either the SoC or the codec). Its rate is the sample rate.
  \item DIN and DOUT are the data lines
  \item The relationship between BLCK and WCLK is:
    \begin{math}Bclk = Wclk * Nchannels * BitDepth\end{math}
  \item usually the codecs will expect MCLK to be a multiple of BCLK.
    Usually specified as a multiple of Fs.
  \item however, some codec have a great set of PLLs and dividers,
    allowing to get a precise BCLK from many different MCLK rate
  \item quite often, not the case for the SoC, then use the codec as
    master!
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Clocking: master/slave}
  The master/slave relationship is declared part of the
  \code{.dai_fmt} field of \code{struct snd_soc_dai_link}.
  \begin{block}{include/sound/soc.h}
    \fontsize{8}{8}\selectfont
    \begin{minted}{c}
/*
 * DAI hardware clock masters.
 *
 * This is wrt the codec, the inverse is true for the interface
 * i.e. if the codec is clk and FRM master then the interface is
 * clk and frame slave.
 */
#define SND_SOC_DAIFMT_CBM_CFM          (1 << 12) /* codec clk & FRM master */
#define SND_SOC_DAIFMT_CBS_CFM          (2 << 12) /* codec clk slave & FRM master */
#define SND_SOC_DAIFMT_CBM_CFS          (3 << 12) /* codec clk master & frame slave */
#define SND_SOC_DAIFMT_CBS_CFS          (4 << 12) /* codec clk & FRM slave */
    \end{minted}
  \end{block}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}
          .dai_fmt = SND_SOC_DAIFMT_I2S
                | SND_SOC_DAIFMT_NB_NF
                | SND_SOC_DAIFMT_CBM_CFM,
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Clocking: dynamically changing clocks}
  The \code{.ops} member of \code{struct snd_soc_dai_link} contains
  useful callbacks.
  \begin{block}{include/sound/soc.h}
    \fontsize{10}{10}\selectfont
    \begin{minted}{c}
/* 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);
};
    \end{minted}
  \end{block}
  \code{.hw_params} is called when setting up the audio stream. The
  \code{struct snd_pcm_hw_params} contains the audio characteristics.
  Use \code{params_rate()} to get the sample rate,
  \code{params_channels} for the number of channels and
  \code{params_format} to get the format (including the bit depth).
  Finally, \code{snd_soc_params_to_bclk} calculates the bit clock.
\end{frame}

\begin{frame}[fragile]{Clocking: \code{hw_params}}
  \begin{itemize}
  \item \code{params_rate} gets the sample rate
  \item \code{params_channels} gets the number of channels
  \item \code{params_format} gets the format (including the bit depth)
  \item \code{snd_soc_params_to_bclk} calculates the bit clock.
  \item \code{snd_soc_dai_set_sysclk} sets the clock rate and
    direction for the DAI (SoC or codec)
    \begin{block}{}
      \begin{minted}{c}
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        unsigned int freq, int dir);
      \end{minted}
    \end{block}
  \item it is also possible to configure the PLLs and clock divisors
    if necessary
    \begin{block}{}
      \fontsize{9}{9}\selectfont
      \begin{minted}{c}
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div);
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
        int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
      \end{minted}
    \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Clocking example}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{9}{9}\selectfont
    \begin{minted}{c}
static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
                struct snd_pcm_hw_params *params)
{
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret;

        ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
                32768, params_rate(params) * 256);
        if (ret < 0) {
                pr_err("%s - failed to set wm8904 codec PLL.", __func__);
                return ret;
        }
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Clocking example}
  \begin{block}{\code{sound/soc/atmel/atmel_wm8904.c}}
    \fontsize{9}{9}\selectfont
    \begin{minted}{c}
        /*
         * As here wm8904 use FLL output as its system clock
         * so calling set_sysclk won't care freq parameter
         * then we pass 0
         */
        ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL,
                        0, SND_SOC_CLOCK_IN);
        if (ret < 0) {
                pr_err("%s -failed to set wm8904 SYSCLK\n", __func__);
                return ret;
        }

        return 0;
}

static struct snd_soc_ops atmel_asoc_wm8904_ops = {
        .hw_params = atmel_asoc_wm8904_hw_params,
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{simple-card}
  You may not need to know all that!
  \begin{itemize}
  \item A driver, \code{simple-card}, can be configured from device
    tree.
  \item It allows to specify the connection between the SoC and the
    codec.
  \item Documented in \code{Documentation/devicetree/bindings/sound/simple-card.txt}
  \end{itemize}
  \begin{block}{}
    \fontsize{7}{6}\selectfont
    \begin{minted}{c}
sound {
        compatible = "simple-audio-card";
        simple-audio-card,name = "VF610-Tower-Sound-Card";
        simple-audio-card,format = "left_j";
        simple-audio-card,bitclock-master = <&dailink0_master>;
        simple-audio-card,frame-master = <&dailink0_master>;
        simple-audio-card,widgets =
                "Microphone", "Microphone Jack",
                "Headphone", "Headphone Jack",
                "Speaker", "External Speaker";
        simple-audio-card,routing =
                "MIC_IN", "Microphone Jack",
                "Headphone Jack", "HP_OUT",
                "External Speaker", "LINE_OUT";

        simple-audio-card,cpu {
                sound-dai = <&sh_fsi2 0>;
        };

        dailink0_master: simple-audio-card,codec {
                sound-dai = <&ak4648>;
                clocks = <&osc>;
        };
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Amplifier}
  What about the amplifier?
  \begin{itemize}
  \item Supported using {\em auxiliary devices}
  \item Register a \code{struct snd_soc_aux_dev} array using the
    \code{.aux_dev} and \code{.num_aux_devs} fields of the registered
    \code{struct snd_soc_card}
  \item This will expose the auxiliary devices control widgets as part
    of the sound card
  \end{itemize}
\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 widgets
  \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{git://opensource.wolfsonmicro.com/asoc-tools.git}
  \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}{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}
