linux/sound/soc/meson/aiu-codec-ctrl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2020 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6#include <linux/bitfield.h>
   7#include <sound/pcm_params.h>
   8#include <sound/soc.h>
   9#include <sound/soc-dai.h>
  10
  11#include <dt-bindings/sound/meson-aiu.h>
  12#include "aiu.h"
  13#include "meson-codec-glue.h"
  14
  15#define CTRL_CLK_SEL            GENMASK(1, 0)
  16#define CTRL_DATA_SEL_SHIFT     4
  17#define CTRL_DATA_SEL           (0x3 << CTRL_DATA_SEL_SHIFT)
  18
  19static const char * const aiu_codec_ctrl_mux_texts[] = {
  20        "DISABLED", "PCM", "I2S",
  21};
  22
  23static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
  24                                       struct snd_ctl_elem_value *ucontrol)
  25{
  26        struct snd_soc_component *component =
  27                snd_soc_dapm_kcontrol_component(kcontrol);
  28        struct snd_soc_dapm_context *dapm =
  29                snd_soc_dapm_kcontrol_dapm(kcontrol);
  30        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
  31        unsigned int mux, changed;
  32
  33        mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
  34        changed = snd_soc_component_test_bits(component, e->reg,
  35                                              CTRL_DATA_SEL,
  36                                              FIELD_PREP(CTRL_DATA_SEL, mux));
  37
  38        if (!changed)
  39                return 0;
  40
  41        /* Force disconnect of the mux while updating */
  42        snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
  43
  44        /* Reset the source first */
  45        snd_soc_component_update_bits(component, e->reg,
  46                                      CTRL_CLK_SEL |
  47                                      CTRL_DATA_SEL,
  48                                      FIELD_PREP(CTRL_CLK_SEL, 0) |
  49                                      FIELD_PREP(CTRL_DATA_SEL, 0));
  50
  51        /* Set the appropriate source */
  52        snd_soc_component_update_bits(component, e->reg,
  53                                      CTRL_CLK_SEL |
  54                                      CTRL_DATA_SEL,
  55                                      FIELD_PREP(CTRL_CLK_SEL, mux) |
  56                                      FIELD_PREP(CTRL_DATA_SEL, mux));
  57
  58        snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
  59
  60        return 0;
  61}
  62
  63static SOC_ENUM_SINGLE_DECL(aiu_hdmi_ctrl_mux_enum, AIU_HDMI_CLK_DATA_CTRL,
  64                            CTRL_DATA_SEL_SHIFT,
  65                            aiu_codec_ctrl_mux_texts);
  66
  67static const struct snd_kcontrol_new aiu_hdmi_ctrl_mux =
  68        SOC_DAPM_ENUM_EXT("HDMI Source", aiu_hdmi_ctrl_mux_enum,
  69                          snd_soc_dapm_get_enum_double,
  70                          aiu_codec_ctrl_mux_put_enum);
  71
  72static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = {
  73        SND_SOC_DAPM_MUX("HDMI CTRL SRC", SND_SOC_NOPM, 0, 0,
  74                         &aiu_hdmi_ctrl_mux),
  75};
  76
  77static const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = {
  78        .hw_params      = meson_codec_glue_input_hw_params,
  79        .set_fmt        = meson_codec_glue_input_set_fmt,
  80};
  81
  82static const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = {
  83        .startup        = meson_codec_glue_output_startup,
  84};
  85
  86#define AIU_CODEC_CTRL_FORMATS                                  \
  87        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |   \
  88         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |   \
  89         SNDRV_PCM_FMTBIT_S32_LE)
  90
  91#define AIU_CODEC_CTRL_STREAM(xname, xsuffix)                   \
  92{                                                               \
  93        .stream_name    = xname " " xsuffix,                    \
  94        .channels_min   = 1,                                    \
  95        .channels_max   = 8,                                    \
  96        .rate_min       = 5512,                                 \
  97        .rate_max       = 192000,                               \
  98        .formats        = AIU_CODEC_CTRL_FORMATS,               \
  99}
 100
 101#define AIU_CODEC_CTRL_INPUT(xname) {                           \
 102        .name = "CODEC CTRL " xname,                            \
 103        .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"),   \
 104        .ops = &aiu_codec_ctrl_input_ops,                       \
 105        .probe = meson_codec_glue_input_dai_probe,              \
 106        .remove = meson_codec_glue_input_dai_remove,            \
 107}
 108
 109#define AIU_CODEC_CTRL_OUTPUT(xname) {                          \
 110        .name = "CODEC CTRL " xname,                            \
 111        .capture = AIU_CODEC_CTRL_STREAM(xname, "Capture"),     \
 112        .ops = &aiu_codec_ctrl_output_ops,                      \
 113}
 114
 115static struct snd_soc_dai_driver aiu_hdmi_ctrl_dai_drv[] = {
 116        [CTRL_I2S] = AIU_CODEC_CTRL_INPUT("HDMI I2S IN"),
 117        [CTRL_PCM] = AIU_CODEC_CTRL_INPUT("HDMI PCM IN"),
 118        [CTRL_OUT] = AIU_CODEC_CTRL_OUTPUT("HDMI OUT"),
 119};
 120
 121static const struct snd_soc_dapm_route aiu_hdmi_ctrl_routes[] = {
 122        { "HDMI CTRL SRC", "I2S", "HDMI I2S IN Playback" },
 123        { "HDMI CTRL SRC", "PCM", "HDMI PCM IN Playback" },
 124        { "HDMI OUT Capture", NULL, "HDMI CTRL SRC" },
 125};
 126
 127static int aiu_hdmi_of_xlate_dai_name(struct snd_soc_component *component,
 128                                      struct of_phandle_args *args,
 129                                      const char **dai_name)
 130{
 131        return aiu_of_xlate_dai_name(component, args, dai_name, AIU_HDMI);
 132}
 133
 134static const struct snd_soc_component_driver aiu_hdmi_ctrl_component = {
 135        .name                   = "AIU HDMI Codec Control",
 136        .dapm_widgets           = aiu_hdmi_ctrl_widgets,
 137        .num_dapm_widgets       = ARRAY_SIZE(aiu_hdmi_ctrl_widgets),
 138        .dapm_routes            = aiu_hdmi_ctrl_routes,
 139        .num_dapm_routes        = ARRAY_SIZE(aiu_hdmi_ctrl_routes),
 140        .of_xlate_dai_name      = aiu_hdmi_of_xlate_dai_name,
 141        .endianness             = 1,
 142        .non_legacy_dai_naming  = 1,
 143};
 144
 145int aiu_hdmi_ctrl_register_component(struct device *dev)
 146{
 147        return snd_soc_register_component(dev, &aiu_hdmi_ctrl_component,
 148                                          aiu_hdmi_ctrl_dai_drv,
 149                                          ARRAY_SIZE(aiu_hdmi_ctrl_dai_drv));
 150}
 151
 152