root/sound/soc/meson/axg-tdmout.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. axg_tdmout_get_be
  2. axg_tdmout_get_tdm_stream
  3. axg_tdmout_enable
  4. axg_tdmout_disable
  5. axg_tdmout_prepare

   1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2 //
   3 // Copyright (c) 2018 BayLibre, SAS.
   4 // Author: Jerome Brunet <jbrunet@baylibre.com>
   5 
   6 #include <linux/module.h>
   7 #include <linux/of_platform.h>
   8 #include <linux/regmap.h>
   9 #include <sound/soc.h>
  10 #include <sound/soc-dai.h>
  11 
  12 #include "axg-tdm-formatter.h"
  13 
  14 #define TDMOUT_CTRL0                    0x00
  15 #define  TDMOUT_CTRL0_BITNUM_MASK       GENMASK(4, 0)
  16 #define  TDMOUT_CTRL0_BITNUM(x)         ((x) << 0)
  17 #define  TDMOUT_CTRL0_SLOTNUM_MASK      GENMASK(9, 5)
  18 #define  TDMOUT_CTRL0_SLOTNUM(x)        ((x) << 5)
  19 #define  TDMOUT_CTRL0_INIT_BITNUM_MASK  GENMASK(19, 15)
  20 #define  TDMOUT_CTRL0_INIT_BITNUM(x)    ((x) << 15)
  21 #define  TDMOUT_CTRL0_ENABLE            BIT(31)
  22 #define  TDMOUT_CTRL0_RST_OUT           BIT(29)
  23 #define  TDMOUT_CTRL0_RST_IN            BIT(28)
  24 #define TDMOUT_CTRL1                    0x04
  25 #define  TDMOUT_CTRL1_TYPE_MASK         GENMASK(6, 4)
  26 #define  TDMOUT_CTRL1_TYPE(x)           ((x) << 4)
  27 #define  SM1_TDMOUT_CTRL1_GAIN_EN       7
  28 #define  TDMOUT_CTRL1_MSB_POS_MASK      GENMASK(12, 8)
  29 #define  TDMOUT_CTRL1_MSB_POS(x)        ((x) << 8)
  30 #define  TDMOUT_CTRL1_SEL_SHIFT         24
  31 #define  TDMOUT_CTRL1_GAIN_EN           26
  32 #define  TDMOUT_CTRL1_WS_INV            BIT(28)
  33 #define TDMOUT_SWAP                     0x08
  34 #define TDMOUT_MASK0                    0x0c
  35 #define TDMOUT_MASK1                    0x10
  36 #define TDMOUT_MASK2                    0x14
  37 #define TDMOUT_MASK3                    0x18
  38 #define TDMOUT_STAT                     0x1c
  39 #define TDMOUT_GAIN0                    0x20
  40 #define TDMOUT_GAIN1                    0x24
  41 #define TDMOUT_MUTE_VAL                 0x28
  42 #define TDMOUT_MUTE0                    0x2c
  43 #define TDMOUT_MUTE1                    0x30
  44 #define TDMOUT_MUTE2                    0x34
  45 #define TDMOUT_MUTE3                    0x38
  46 #define TDMOUT_MASK_VAL                 0x3c
  47 
  48 static const struct regmap_config axg_tdmout_regmap_cfg = {
  49         .reg_bits       = 32,
  50         .val_bits       = 32,
  51         .reg_stride     = 4,
  52         .max_register   = TDMOUT_MASK_VAL,
  53 };
  54 
  55 static struct snd_soc_dai *
  56 axg_tdmout_get_be(struct snd_soc_dapm_widget *w)
  57 {
  58         struct snd_soc_dapm_path *p = NULL;
  59         struct snd_soc_dai *be;
  60 
  61         snd_soc_dapm_widget_for_each_sink_path(w, p) {
  62                 if (!p->connect)
  63                         continue;
  64 
  65                 if (p->sink->id == snd_soc_dapm_dai_in)
  66                         return (struct snd_soc_dai *)p->sink->priv;
  67 
  68                 be = axg_tdmout_get_be(p->sink);
  69                 if (be)
  70                         return be;
  71         }
  72 
  73         return NULL;
  74 }
  75 
  76 static struct axg_tdm_stream *
  77 axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w)
  78 {
  79         struct snd_soc_dai *be = axg_tdmout_get_be(w);
  80 
  81         if (!be)
  82                 return NULL;
  83 
  84         return be->playback_dma_data;
  85 }
  86 
  87 static void axg_tdmout_enable(struct regmap *map)
  88 {
  89         /* Apply both reset */
  90         regmap_update_bits(map, TDMOUT_CTRL0,
  91                            TDMOUT_CTRL0_RST_OUT | TDMOUT_CTRL0_RST_IN, 0);
  92 
  93         /* Clear out reset before in reset */
  94         regmap_update_bits(map, TDMOUT_CTRL0,
  95                            TDMOUT_CTRL0_RST_OUT, TDMOUT_CTRL0_RST_OUT);
  96         regmap_update_bits(map, TDMOUT_CTRL0,
  97                            TDMOUT_CTRL0_RST_IN,  TDMOUT_CTRL0_RST_IN);
  98 
  99         /* Actually enable tdmout */
 100         regmap_update_bits(map, TDMOUT_CTRL0,
 101                            TDMOUT_CTRL0_ENABLE, TDMOUT_CTRL0_ENABLE);
 102 }
 103 
 104 static void axg_tdmout_disable(struct regmap *map)
 105 {
 106         regmap_update_bits(map, TDMOUT_CTRL0, TDMOUT_CTRL0_ENABLE, 0);
 107 }
 108 
 109 static int axg_tdmout_prepare(struct regmap *map,
 110                               const struct axg_tdm_formatter_hw *quirks,
 111                               struct axg_tdm_stream *ts)
 112 {
 113         unsigned int val, skew = quirks->skew_offset;
 114 
 115         /* Set the stream skew */
 116         switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 117         case SND_SOC_DAIFMT_I2S:
 118         case SND_SOC_DAIFMT_DSP_A:
 119                 break;
 120 
 121         case SND_SOC_DAIFMT_LEFT_J:
 122         case SND_SOC_DAIFMT_DSP_B:
 123                 skew += 1;
 124                 break;
 125 
 126         default:
 127                 pr_err("Unsupported format: %u\n",
 128                        ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK);
 129                 return -EINVAL;
 130         }
 131 
 132         val = TDMOUT_CTRL0_INIT_BITNUM(skew);
 133 
 134         /* Set the slot width */
 135         val |= TDMOUT_CTRL0_BITNUM(ts->iface->slot_width - 1);
 136 
 137         /* Set the slot number */
 138         val |= TDMOUT_CTRL0_SLOTNUM(ts->iface->slots - 1);
 139 
 140         regmap_update_bits(map, TDMOUT_CTRL0,
 141                            TDMOUT_CTRL0_INIT_BITNUM_MASK |
 142                            TDMOUT_CTRL0_BITNUM_MASK |
 143                            TDMOUT_CTRL0_SLOTNUM_MASK, val);
 144 
 145         /* Set the sample width */
 146         val = TDMOUT_CTRL1_MSB_POS(ts->width - 1);
 147 
 148         /* FIFO data are arranged in chunks of 64bits */
 149         switch (ts->physical_width) {
 150         case 8:
 151                 /* 8 samples of 8 bits */
 152                 val |= TDMOUT_CTRL1_TYPE(0);
 153                 break;
 154         case 16:
 155                 /* 4 samples of 16 bits - right justified */
 156                 val |= TDMOUT_CTRL1_TYPE(2);
 157                 break;
 158         case 32:
 159                 /* 2 samples of 32 bits - right justified */
 160                 val |= TDMOUT_CTRL1_TYPE(4);
 161                 break;
 162         default:
 163                 pr_err("Unsupported physical width: %u\n",
 164                        ts->physical_width);
 165                 return -EINVAL;
 166         }
 167 
 168         /* If the sample clock is inverted, invert it back for the formatter */
 169         if (axg_tdm_lrclk_invert(ts->iface->fmt))
 170                 val |= TDMOUT_CTRL1_WS_INV;
 171 
 172         regmap_update_bits(map, TDMOUT_CTRL1,
 173                            (TDMOUT_CTRL1_TYPE_MASK | TDMOUT_CTRL1_MSB_POS_MASK |
 174                             TDMOUT_CTRL1_WS_INV), val);
 175 
 176         /* Set static swap mask configuration */
 177         regmap_write(map, TDMOUT_SWAP, 0x76543210);
 178 
 179         return axg_tdm_formatter_set_channel_masks(map, ts, TDMOUT_MASK0);
 180 }
 181 
 182 static const struct snd_kcontrol_new axg_tdmout_controls[] = {
 183         SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0,  0,  8, 255, 0),
 184         SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
 185         SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1,  0,  8, 255, 0),
 186         SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
 187         SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
 188                    TDMOUT_CTRL1_GAIN_EN, 1, 0),
 189 };
 190 
 191 static const char * const axg_tdmout_sel_texts[] = {
 192         "IN 0", "IN 1", "IN 2",
 193 };
 194 
 195 static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1,
 196                             TDMOUT_CTRL1_SEL_SHIFT, axg_tdmout_sel_texts);
 197 
 198 static const struct snd_kcontrol_new axg_tdmout_in_mux =
 199         SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum);
 200 
 201 static const struct snd_soc_dapm_widget axg_tdmout_dapm_widgets[] = {
 202         SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
 203         SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
 204         SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
 205         SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmout_in_mux),
 206         SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0,
 207                            axg_tdm_formatter_event,
 208                            (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
 209         SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
 210 };
 211 
 212 static const struct snd_soc_dapm_route axg_tdmout_dapm_routes[] = {
 213         { "SRC SEL", "IN 0", "IN 0" },
 214         { "SRC SEL", "IN 1", "IN 1" },
 215         { "SRC SEL", "IN 2", "IN 2" },
 216         { "ENC", NULL, "SRC SEL" },
 217         { "OUT", NULL, "ENC" },
 218 };
 219 
 220 static const struct snd_soc_component_driver axg_tdmout_component_drv = {
 221         .controls               = axg_tdmout_controls,
 222         .num_controls           = ARRAY_SIZE(axg_tdmout_controls),
 223         .dapm_widgets           = axg_tdmout_dapm_widgets,
 224         .num_dapm_widgets       = ARRAY_SIZE(axg_tdmout_dapm_widgets),
 225         .dapm_routes            = axg_tdmout_dapm_routes,
 226         .num_dapm_routes        = ARRAY_SIZE(axg_tdmout_dapm_routes),
 227 };
 228 
 229 static const struct axg_tdm_formatter_ops axg_tdmout_ops = {
 230         .get_stream     = axg_tdmout_get_tdm_stream,
 231         .prepare        = axg_tdmout_prepare,
 232         .enable         = axg_tdmout_enable,
 233         .disable        = axg_tdmout_disable,
 234 };
 235 
 236 static const struct axg_tdm_formatter_driver axg_tdmout_drv = {
 237         .component_drv  = &axg_tdmout_component_drv,
 238         .regmap_cfg     = &axg_tdmout_regmap_cfg,
 239         .ops            = &axg_tdmout_ops,
 240         .quirks         = &(const struct axg_tdm_formatter_hw) {
 241                 .invert_sclk = true,
 242                 .skew_offset = 1,
 243         },
 244 };
 245 
 246 static const struct axg_tdm_formatter_driver g12a_tdmout_drv = {
 247         .component_drv  = &axg_tdmout_component_drv,
 248         .regmap_cfg     = &axg_tdmout_regmap_cfg,
 249         .ops            = &axg_tdmout_ops,
 250         .quirks         = &(const struct axg_tdm_formatter_hw) {
 251                 .invert_sclk = true,
 252                 .skew_offset = 2,
 253         },
 254 };
 255 
 256 static const struct snd_kcontrol_new sm1_tdmout_controls[] = {
 257         SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0,  0,  8, 255, 0),
 258         SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
 259         SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1,  0,  8, 255, 0),
 260         SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
 261         SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
 262                    SM1_TDMOUT_CTRL1_GAIN_EN, 1, 0),
 263 };
 264 
 265 static const char * const sm1_tdmout_sel_texts[] = {
 266         "IN 0", "IN 1", "IN 2", "IN 3", "IN 4",
 267 };
 268 
 269 static SOC_ENUM_SINGLE_DECL(sm1_tdmout_sel_enum, TDMOUT_CTRL1,
 270                             TDMOUT_CTRL1_SEL_SHIFT, sm1_tdmout_sel_texts);
 271 
 272 static const struct snd_kcontrol_new sm1_tdmout_in_mux =
 273         SOC_DAPM_ENUM("Input Source", sm1_tdmout_sel_enum);
 274 
 275 static const struct snd_soc_dapm_widget sm1_tdmout_dapm_widgets[] = {
 276         SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
 277         SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
 278         SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
 279         SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
 280         SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
 281         SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &sm1_tdmout_in_mux),
 282         SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0,
 283                            axg_tdm_formatter_event,
 284                            (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
 285         SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
 286 };
 287 
 288 static const struct snd_soc_dapm_route sm1_tdmout_dapm_routes[] = {
 289         { "SRC SEL", "IN 0", "IN 0" },
 290         { "SRC SEL", "IN 1", "IN 1" },
 291         { "SRC SEL", "IN 2", "IN 2" },
 292         { "SRC SEL", "IN 3", "IN 3" },
 293         { "SRC SEL", "IN 4", "IN 4" },
 294         { "ENC", NULL, "SRC SEL" },
 295         { "OUT", NULL, "ENC" },
 296 };
 297 
 298 static const struct snd_soc_component_driver sm1_tdmout_component_drv = {
 299         .controls               = sm1_tdmout_controls,
 300         .num_controls           = ARRAY_SIZE(sm1_tdmout_controls),
 301         .dapm_widgets           = sm1_tdmout_dapm_widgets,
 302         .num_dapm_widgets       = ARRAY_SIZE(sm1_tdmout_dapm_widgets),
 303         .dapm_routes            = sm1_tdmout_dapm_routes,
 304         .num_dapm_routes        = ARRAY_SIZE(sm1_tdmout_dapm_routes),
 305 };
 306 
 307 static const struct axg_tdm_formatter_driver sm1_tdmout_drv = {
 308         .component_drv  = &sm1_tdmout_component_drv,
 309         .regmap_cfg     = &axg_tdmout_regmap_cfg,
 310         .ops            = &axg_tdmout_ops,
 311         .quirks         = &(const struct axg_tdm_formatter_hw) {
 312                 .invert_sclk = true,
 313                 .skew_offset = 2,
 314         },
 315 };
 316 
 317 static const struct of_device_id axg_tdmout_of_match[] = {
 318         {
 319                 .compatible = "amlogic,axg-tdmout",
 320                 .data = &axg_tdmout_drv,
 321         }, {
 322                 .compatible = "amlogic,g12a-tdmout",
 323                 .data = &g12a_tdmout_drv,
 324         }, {
 325                 .compatible = "amlogic,sm1-tdmout",
 326                 .data = &sm1_tdmout_drv,
 327         }, {}
 328 };
 329 MODULE_DEVICE_TABLE(of, axg_tdmout_of_match);
 330 
 331 static struct platform_driver axg_tdmout_pdrv = {
 332         .probe = axg_tdm_formatter_probe,
 333         .driver = {
 334                 .name = "axg-tdmout",
 335                 .of_match_table = axg_tdmout_of_match,
 336         },
 337 };
 338 module_platform_driver(axg_tdmout_pdrv);
 339 
 340 MODULE_DESCRIPTION("Amlogic AXG TDM output formatter driver");
 341 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 342 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */