Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
0002 //
0003 // Copyright (c) 2018 BayLibre, SAS.
0004 // Author: Jerome Brunet <jbrunet@baylibre.com>
0005 
0006 #include <linux/module.h>
0007 #include <linux/of_platform.h>
0008 #include <linux/regmap.h>
0009 #include <sound/soc.h>
0010 #include <sound/soc-dai.h>
0011 
0012 #include "axg-tdm-formatter.h"
0013 
0014 #define TDMIN_CTRL          0x00
0015 #define  TDMIN_CTRL_ENABLE      BIT(31)
0016 #define  TDMIN_CTRL_I2S_MODE        BIT(30)
0017 #define  TDMIN_CTRL_RST_OUT     BIT(29)
0018 #define  TDMIN_CTRL_RST_IN      BIT(28)
0019 #define  TDMIN_CTRL_WS_INV      BIT(25)
0020 #define  TDMIN_CTRL_SEL_SHIFT       20
0021 #define  TDMIN_CTRL_IN_BIT_SKEW_MASK    GENMASK(18, 16)
0022 #define  TDMIN_CTRL_IN_BIT_SKEW(x)  ((x) << 16)
0023 #define  TDMIN_CTRL_LSB_FIRST       BIT(5)
0024 #define  TDMIN_CTRL_BITNUM_MASK GENMASK(4, 0)
0025 #define  TDMIN_CTRL_BITNUM(x)       ((x) << 0)
0026 #define TDMIN_SWAP          0x04
0027 #define TDMIN_MASK0         0x08
0028 #define TDMIN_MASK1         0x0c
0029 #define TDMIN_MASK2         0x10
0030 #define TDMIN_MASK3         0x14
0031 #define TDMIN_STAT          0x18
0032 #define TDMIN_MUTE_VAL          0x1c
0033 #define TDMIN_MUTE0         0x20
0034 #define TDMIN_MUTE1         0x24
0035 #define TDMIN_MUTE2         0x28
0036 #define TDMIN_MUTE3         0x2c
0037 
0038 static const struct regmap_config axg_tdmin_regmap_cfg = {
0039     .reg_bits   = 32,
0040     .val_bits   = 32,
0041     .reg_stride = 4,
0042     .max_register   = TDMIN_MUTE3,
0043 };
0044 
0045 static const char * const axg_tdmin_sel_texts[] = {
0046     "IN 0", "IN 1", "IN 2",  "IN 3",  "IN 4",  "IN 5",  "IN 6",  "IN 7",
0047     "IN 8", "IN 9", "IN 10", "IN 11", "IN 12", "IN 13", "IN 14", "IN 15",
0048 };
0049 
0050 /* Change to special mux control to reset dapm */
0051 static SOC_ENUM_SINGLE_DECL(axg_tdmin_sel_enum, TDMIN_CTRL,
0052                 TDMIN_CTRL_SEL_SHIFT, axg_tdmin_sel_texts);
0053 
0054 static const struct snd_kcontrol_new axg_tdmin_in_mux =
0055     SOC_DAPM_ENUM("Input Source", axg_tdmin_sel_enum);
0056 
0057 static struct snd_soc_dai *
0058 axg_tdmin_get_be(struct snd_soc_dapm_widget *w)
0059 {
0060     struct snd_soc_dapm_path *p;
0061     struct snd_soc_dai *be;
0062 
0063     snd_soc_dapm_widget_for_each_source_path(w, p) {
0064         if (!p->connect)
0065             continue;
0066 
0067         if (p->source->id == snd_soc_dapm_dai_out)
0068             return (struct snd_soc_dai *)p->source->priv;
0069 
0070         be = axg_tdmin_get_be(p->source);
0071         if (be)
0072             return be;
0073     }
0074 
0075     return NULL;
0076 }
0077 
0078 static struct axg_tdm_stream *
0079 axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w)
0080 {
0081     struct snd_soc_dai *be = axg_tdmin_get_be(w);
0082 
0083     if (!be)
0084         return NULL;
0085 
0086     return be->capture_dma_data;
0087 }
0088 
0089 static void axg_tdmin_enable(struct regmap *map)
0090 {
0091     /* Apply both reset */
0092     regmap_update_bits(map, TDMIN_CTRL,
0093                TDMIN_CTRL_RST_OUT | TDMIN_CTRL_RST_IN, 0);
0094 
0095     /* Clear out reset before in reset */
0096     regmap_update_bits(map, TDMIN_CTRL,
0097                TDMIN_CTRL_RST_OUT, TDMIN_CTRL_RST_OUT);
0098     regmap_update_bits(map, TDMIN_CTRL,
0099                TDMIN_CTRL_RST_IN,  TDMIN_CTRL_RST_IN);
0100 
0101     /* Actually enable tdmin */
0102     regmap_update_bits(map, TDMIN_CTRL,
0103                TDMIN_CTRL_ENABLE, TDMIN_CTRL_ENABLE);
0104 }
0105 
0106 static void axg_tdmin_disable(struct regmap *map)
0107 {
0108     regmap_update_bits(map, TDMIN_CTRL, TDMIN_CTRL_ENABLE, 0);
0109 }
0110 
0111 static int axg_tdmin_prepare(struct regmap *map,
0112                  const struct axg_tdm_formatter_hw *quirks,
0113                  struct axg_tdm_stream *ts)
0114 {
0115     unsigned int val, skew = quirks->skew_offset;
0116 
0117     /* Set stream skew */
0118     switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0119     case SND_SOC_DAIFMT_I2S:
0120     case SND_SOC_DAIFMT_DSP_A:
0121         skew += 1;
0122         break;
0123 
0124     case SND_SOC_DAIFMT_LEFT_J:
0125     case SND_SOC_DAIFMT_DSP_B:
0126         break;
0127 
0128     default:
0129         pr_err("Unsupported format: %u\n",
0130                ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK);
0131         return -EINVAL;
0132     }
0133 
0134     val = TDMIN_CTRL_IN_BIT_SKEW(skew);
0135 
0136     /* Set stream format mode */
0137     switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0138     case SND_SOC_DAIFMT_I2S:
0139     case SND_SOC_DAIFMT_LEFT_J:
0140     case SND_SOC_DAIFMT_RIGHT_J:
0141         val |= TDMIN_CTRL_I2S_MODE;
0142         break;
0143     }
0144 
0145     /* If the sample clock is inverted, invert it back for the formatter */
0146     if (axg_tdm_lrclk_invert(ts->iface->fmt))
0147         val |= TDMIN_CTRL_WS_INV;
0148 
0149     /* Set the slot width */
0150     val |= TDMIN_CTRL_BITNUM(ts->iface->slot_width - 1);
0151 
0152     /*
0153      * The following also reset LSB_FIRST which result in the formatter
0154      * placing the first bit received at bit 31
0155      */
0156     regmap_update_bits(map, TDMIN_CTRL,
0157                (TDMIN_CTRL_IN_BIT_SKEW_MASK | TDMIN_CTRL_WS_INV |
0158                 TDMIN_CTRL_I2S_MODE | TDMIN_CTRL_LSB_FIRST |
0159                 TDMIN_CTRL_BITNUM_MASK), val);
0160 
0161     /* Set static swap mask configuration */
0162     regmap_write(map, TDMIN_SWAP, 0x76543210);
0163 
0164     return axg_tdm_formatter_set_channel_masks(map, ts, TDMIN_MASK0);
0165 }
0166 
0167 static const struct snd_soc_dapm_widget axg_tdmin_dapm_widgets[] = {
0168     SND_SOC_DAPM_AIF_IN("IN 0",  NULL, 0, SND_SOC_NOPM, 0, 0),
0169     SND_SOC_DAPM_AIF_IN("IN 1",  NULL, 0, SND_SOC_NOPM, 0, 0),
0170     SND_SOC_DAPM_AIF_IN("IN 2",  NULL, 0, SND_SOC_NOPM, 0, 0),
0171     SND_SOC_DAPM_AIF_IN("IN 3",  NULL, 0, SND_SOC_NOPM, 0, 0),
0172     SND_SOC_DAPM_AIF_IN("IN 4",  NULL, 0, SND_SOC_NOPM, 0, 0),
0173     SND_SOC_DAPM_AIF_IN("IN 5",  NULL, 0, SND_SOC_NOPM, 0, 0),
0174     SND_SOC_DAPM_AIF_IN("IN 6",  NULL, 0, SND_SOC_NOPM, 0, 0),
0175     SND_SOC_DAPM_AIF_IN("IN 7",  NULL, 0, SND_SOC_NOPM, 0, 0),
0176     SND_SOC_DAPM_AIF_IN("IN 8",  NULL, 0, SND_SOC_NOPM, 0, 0),
0177     SND_SOC_DAPM_AIF_IN("IN 9",  NULL, 0, SND_SOC_NOPM, 0, 0),
0178     SND_SOC_DAPM_AIF_IN("IN 10", NULL, 0, SND_SOC_NOPM, 0, 0),
0179     SND_SOC_DAPM_AIF_IN("IN 11", NULL, 0, SND_SOC_NOPM, 0, 0),
0180     SND_SOC_DAPM_AIF_IN("IN 12", NULL, 0, SND_SOC_NOPM, 0, 0),
0181     SND_SOC_DAPM_AIF_IN("IN 13", NULL, 0, SND_SOC_NOPM, 0, 0),
0182     SND_SOC_DAPM_AIF_IN("IN 14", NULL, 0, SND_SOC_NOPM, 0, 0),
0183     SND_SOC_DAPM_AIF_IN("IN 15", NULL, 0, SND_SOC_NOPM, 0, 0),
0184     SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmin_in_mux),
0185     SND_SOC_DAPM_PGA_E("DEC", SND_SOC_NOPM, 0, 0, NULL, 0,
0186                axg_tdm_formatter_event,
0187                (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
0188     SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
0189 };
0190 
0191 static const struct snd_soc_dapm_route axg_tdmin_dapm_routes[] = {
0192     { "SRC SEL", "IN 0",  "IN 0" },
0193     { "SRC SEL", "IN 1",  "IN 1" },
0194     { "SRC SEL", "IN 2",  "IN 2" },
0195     { "SRC SEL", "IN 3",  "IN 3" },
0196     { "SRC SEL", "IN 4",  "IN 4" },
0197     { "SRC SEL", "IN 5",  "IN 5" },
0198     { "SRC SEL", "IN 6",  "IN 6" },
0199     { "SRC SEL", "IN 7",  "IN 7" },
0200     { "SRC SEL", "IN 8",  "IN 8" },
0201     { "SRC SEL", "IN 9",  "IN 9" },
0202     { "SRC SEL", "IN 10", "IN 10" },
0203     { "SRC SEL", "IN 11", "IN 11" },
0204     { "SRC SEL", "IN 12", "IN 12" },
0205     { "SRC SEL", "IN 13", "IN 13" },
0206     { "SRC SEL", "IN 14", "IN 14" },
0207     { "SRC SEL", "IN 15", "IN 15" },
0208     { "DEC", NULL, "SRC SEL" },
0209     { "OUT", NULL, "DEC" },
0210 };
0211 
0212 static const struct snd_soc_component_driver axg_tdmin_component_drv = {
0213     .dapm_widgets       = axg_tdmin_dapm_widgets,
0214     .num_dapm_widgets   = ARRAY_SIZE(axg_tdmin_dapm_widgets),
0215     .dapm_routes        = axg_tdmin_dapm_routes,
0216     .num_dapm_routes    = ARRAY_SIZE(axg_tdmin_dapm_routes),
0217 };
0218 
0219 static const struct axg_tdm_formatter_ops axg_tdmin_ops = {
0220     .get_stream = axg_tdmin_get_tdm_stream,
0221     .prepare    = axg_tdmin_prepare,
0222     .enable     = axg_tdmin_enable,
0223     .disable    = axg_tdmin_disable,
0224 };
0225 
0226 static const struct axg_tdm_formatter_driver axg_tdmin_drv = {
0227     .component_drv  = &axg_tdmin_component_drv,
0228     .regmap_cfg = &axg_tdmin_regmap_cfg,
0229     .ops        = &axg_tdmin_ops,
0230     .quirks     = &(const struct axg_tdm_formatter_hw) {
0231         .skew_offset    = 3,
0232     },
0233 };
0234 
0235 static const struct of_device_id axg_tdmin_of_match[] = {
0236     {
0237         .compatible = "amlogic,axg-tdmin",
0238         .data = &axg_tdmin_drv,
0239     }, {
0240         .compatible = "amlogic,g12a-tdmin",
0241         .data = &axg_tdmin_drv,
0242     }, {
0243         .compatible = "amlogic,sm1-tdmin",
0244         .data = &axg_tdmin_drv,
0245     }, {}
0246 };
0247 MODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
0248 
0249 static struct platform_driver axg_tdmin_pdrv = {
0250     .probe = axg_tdm_formatter_probe,
0251     .driver = {
0252         .name = "axg-tdmin",
0253         .of_match_table = axg_tdmin_of_match,
0254     },
0255 };
0256 module_platform_driver(axg_tdmin_pdrv);
0257 
0258 MODULE_DESCRIPTION("Amlogic AXG TDM input formatter driver");
0259 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0260 MODULE_LICENSE("GPL v2");