Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2020 BayLibre, SAS.
0004 // Author: Jerome Brunet <jbrunet@baylibre.com>
0005 
0006 #include <linux/bitfield.h>
0007 #include <linux/clk.h>
0008 #include <linux/module.h>
0009 #include <sound/pcm_params.h>
0010 #include <linux/regmap.h>
0011 #include <linux/regulator/consumer.h>
0012 #include <linux/reset.h>
0013 #include <sound/soc.h>
0014 #include <sound/soc-dai.h>
0015 
0016 #include <dt-bindings/sound/meson-g12a-toacodec.h>
0017 #include "axg-tdm.h"
0018 #include "meson-codec-glue.h"
0019 
0020 #define G12A_TOACODEC_DRV_NAME "g12a-toacodec"
0021 
0022 #define TOACODEC_CTRL0          0x0
0023 #define  CTRL0_ENABLE_SHIFT     31
0024 #define  CTRL0_DAT_SEL_SM1_MSB      19
0025 #define  CTRL0_DAT_SEL_SM1_LSB      18
0026 #define  CTRL0_DAT_SEL_MSB      15
0027 #define  CTRL0_DAT_SEL_LSB      14
0028 #define  CTRL0_LANE_SEL_SM1     16
0029 #define  CTRL0_LANE_SEL         12
0030 #define  CTRL0_LRCLK_SEL_SM1_MSB    14
0031 #define  CTRL0_LRCLK_SEL_SM1_LSB    12
0032 #define  CTRL0_LRCLK_SEL_MSB        9
0033 #define  CTRL0_LRCLK_SEL_LSB        8
0034 #define  CTRL0_LRCLK_INV_SM1        BIT(10)
0035 #define  CTRL0_BLK_CAP_INV_SM1      BIT(9)
0036 #define  CTRL0_BLK_CAP_INV      BIT(7)
0037 #define  CTRL0_BCLK_O_INV_SM1       BIT(8)
0038 #define  CTRL0_BCLK_O_INV       BIT(6)
0039 #define  CTRL0_BCLK_SEL_SM1_MSB     6
0040 #define  CTRL0_BCLK_SEL_MSB     5
0041 #define  CTRL0_BCLK_SEL_LSB     4
0042 #define  CTRL0_MCLK_SEL         GENMASK(2, 0)
0043 
0044 #define TOACODEC_OUT_CHMAX      2
0045 
0046 struct g12a_toacodec {
0047     struct regmap_field *field_dat_sel;
0048     struct regmap_field *field_lrclk_sel;
0049     struct regmap_field *field_bclk_sel;
0050 };
0051 
0052 struct g12a_toacodec_match_data {
0053     const struct snd_soc_component_driver *component_drv;
0054     struct reg_field field_dat_sel;
0055     struct reg_field field_lrclk_sel;
0056     struct reg_field field_bclk_sel;
0057 };
0058 
0059 static const char * const g12a_toacodec_mux_texts[] = {
0060     "I2S A", "I2S B", "I2S C",
0061 };
0062 
0063 static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
0064                       struct snd_ctl_elem_value *ucontrol)
0065 {
0066     struct snd_soc_component *component =
0067         snd_soc_dapm_kcontrol_component(kcontrol);
0068     struct g12a_toacodec *priv = snd_soc_component_get_drvdata(component);
0069     struct snd_soc_dapm_context *dapm =
0070         snd_soc_dapm_kcontrol_dapm(kcontrol);
0071     struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
0072     unsigned int mux, reg;
0073 
0074     mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
0075     regmap_field_read(priv->field_dat_sel, &reg);
0076 
0077     if (mux == reg)
0078         return 0;
0079 
0080     /* Force disconnect of the mux while updating */
0081     snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
0082 
0083     regmap_field_write(priv->field_dat_sel, mux);
0084     regmap_field_write(priv->field_lrclk_sel, mux);
0085     regmap_field_write(priv->field_bclk_sel, mux);
0086 
0087     /*
0088      * FIXME:
0089      * On this soc, the glue gets the MCLK directly from the clock
0090      * controller instead of going the through the TDM interface.
0091      *
0092      * Here we assume interface A uses clock A, etc ... While it is
0093      * true for now, it could be different. Instead the glue should
0094      * find out the clock used by the interface and select the same
0095      * source. For that, we will need regmap backed clock mux which
0096      * is a work in progress
0097      */
0098     snd_soc_component_update_bits(component, e->reg,
0099                       CTRL0_MCLK_SEL,
0100                       FIELD_PREP(CTRL0_MCLK_SEL, mux));
0101 
0102     snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
0103 
0104     return 0;
0105 }
0106 
0107 static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0,
0108                 CTRL0_DAT_SEL_LSB,
0109                 g12a_toacodec_mux_texts);
0110 
0111 static SOC_ENUM_SINGLE_DECL(sm1_toacodec_mux_enum, TOACODEC_CTRL0,
0112                 CTRL0_DAT_SEL_SM1_LSB,
0113                 g12a_toacodec_mux_texts);
0114 
0115 static const struct snd_kcontrol_new g12a_toacodec_mux =
0116     SOC_DAPM_ENUM_EXT("Source", g12a_toacodec_mux_enum,
0117               snd_soc_dapm_get_enum_double,
0118               g12a_toacodec_mux_put_enum);
0119 
0120 static const struct snd_kcontrol_new sm1_toacodec_mux =
0121     SOC_DAPM_ENUM_EXT("Source", sm1_toacodec_mux_enum,
0122               snd_soc_dapm_get_enum_double,
0123               g12a_toacodec_mux_put_enum);
0124 
0125 static const struct snd_kcontrol_new g12a_toacodec_out_enable =
0126     SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOACODEC_CTRL0,
0127                     CTRL0_ENABLE_SHIFT, 1, 0);
0128 
0129 static const struct snd_soc_dapm_widget g12a_toacodec_widgets[] = {
0130     SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
0131              &g12a_toacodec_mux),
0132     SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
0133                 &g12a_toacodec_out_enable),
0134 };
0135 
0136 static const struct snd_soc_dapm_widget sm1_toacodec_widgets[] = {
0137     SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
0138              &sm1_toacodec_mux),
0139     SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
0140                 &g12a_toacodec_out_enable),
0141 };
0142 
0143 static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
0144                      struct snd_pcm_hw_params *params,
0145                      struct snd_soc_dai *dai)
0146 {
0147     struct meson_codec_glue_input *data;
0148     int ret;
0149 
0150     ret = meson_codec_glue_input_hw_params(substream, params, dai);
0151     if (ret)
0152         return ret;
0153 
0154     /* The glue will provide 1 lane out of the 4 to the output */
0155     data = meson_codec_glue_input_get_data(dai);
0156     data->params.channels_min = min_t(unsigned int, TOACODEC_OUT_CHMAX,
0157                     data->params.channels_min);
0158     data->params.channels_max = min_t(unsigned int, TOACODEC_OUT_CHMAX,
0159                     data->params.channels_max);
0160 
0161     return 0;
0162 }
0163 
0164 static const struct snd_soc_dai_ops g12a_toacodec_input_ops = {
0165     .hw_params  = g12a_toacodec_input_hw_params,
0166     .set_fmt    = meson_codec_glue_input_set_fmt,
0167 };
0168 
0169 static const struct snd_soc_dai_ops g12a_toacodec_output_ops = {
0170     .startup    = meson_codec_glue_output_startup,
0171 };
0172 
0173 #define TOACODEC_STREAM(xname, xsuffix, xchmax)         \
0174 {                               \
0175     .stream_name    = xname " " xsuffix,            \
0176     .channels_min   = 1,                    \
0177     .channels_max   = (xchmax),             \
0178     .rate_min       = 5512,                 \
0179     .rate_max   = 192000,               \
0180     .formats    = AXG_TDM_FORMATS,          \
0181 }
0182 
0183 #define TOACODEC_INPUT(xname, xid) {                    \
0184     .name = xname,                          \
0185     .id = (xid),                            \
0186     .playback = TOACODEC_STREAM(xname, "Playback", 8),      \
0187     .ops = &g12a_toacodec_input_ops,                \
0188     .probe = meson_codec_glue_input_dai_probe,          \
0189     .remove = meson_codec_glue_input_dai_remove,            \
0190 }
0191 
0192 #define TOACODEC_OUTPUT(xname, xid) {                   \
0193     .name = xname,                          \
0194     .id = (xid),                            \
0195     .capture = TOACODEC_STREAM(xname, "Capture", TOACODEC_OUT_CHMAX), \
0196     .ops = &g12a_toacodec_output_ops,               \
0197 }
0198 
0199 static struct snd_soc_dai_driver g12a_toacodec_dai_drv[] = {
0200     TOACODEC_INPUT("IN A", TOACODEC_IN_A),
0201     TOACODEC_INPUT("IN B", TOACODEC_IN_B),
0202     TOACODEC_INPUT("IN C", TOACODEC_IN_C),
0203     TOACODEC_OUTPUT("OUT", TOACODEC_OUT),
0204 };
0205 
0206 static int g12a_toacodec_component_probe(struct snd_soc_component *c)
0207 {
0208     /* Initialize the static clock parameters */
0209     return snd_soc_component_write(c, TOACODEC_CTRL0,
0210                        CTRL0_BLK_CAP_INV);
0211 }
0212 
0213 static int sm1_toacodec_component_probe(struct snd_soc_component *c)
0214 {
0215     /* Initialize the static clock parameters */
0216     return snd_soc_component_write(c, TOACODEC_CTRL0,
0217                        CTRL0_BLK_CAP_INV_SM1);
0218 }
0219 
0220 static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
0221     { "SRC", "I2S A", "IN A Playback" },
0222     { "SRC", "I2S B", "IN B Playback" },
0223     { "SRC", "I2S C", "IN C Playback" },
0224     { "OUT EN", "Switch", "SRC" },
0225     { "OUT Capture", NULL, "OUT EN" },
0226 };
0227 
0228 static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
0229     SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
0230 };
0231 
0232 static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
0233     SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
0234 };
0235 
0236 static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
0237     .probe          = g12a_toacodec_component_probe,
0238     .controls       = g12a_toacodec_controls,
0239     .num_controls       = ARRAY_SIZE(g12a_toacodec_controls),
0240     .dapm_widgets       = g12a_toacodec_widgets,
0241     .num_dapm_widgets   = ARRAY_SIZE(g12a_toacodec_widgets),
0242     .dapm_routes        = g12a_toacodec_routes,
0243     .num_dapm_routes    = ARRAY_SIZE(g12a_toacodec_routes),
0244     .endianness     = 1,
0245 };
0246 
0247 static const struct snd_soc_component_driver sm1_toacodec_component_drv = {
0248     .probe          = sm1_toacodec_component_probe,
0249     .controls       = sm1_toacodec_controls,
0250     .num_controls       = ARRAY_SIZE(sm1_toacodec_controls),
0251     .dapm_widgets       = sm1_toacodec_widgets,
0252     .num_dapm_widgets   = ARRAY_SIZE(sm1_toacodec_widgets),
0253     .dapm_routes        = g12a_toacodec_routes,
0254     .num_dapm_routes    = ARRAY_SIZE(g12a_toacodec_routes),
0255     .endianness     = 1,
0256 };
0257 
0258 static const struct regmap_config g12a_toacodec_regmap_cfg = {
0259     .reg_bits   = 32,
0260     .val_bits   = 32,
0261     .reg_stride = 4,
0262 };
0263 
0264 static const struct g12a_toacodec_match_data g12a_toacodec_match_data = {
0265     .component_drv  = &g12a_toacodec_component_drv,
0266     .field_dat_sel  = REG_FIELD(TOACODEC_CTRL0, 14, 15),
0267     .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 8, 9),
0268     .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 5),
0269 };
0270 
0271 static const struct g12a_toacodec_match_data sm1_toacodec_match_data = {
0272     .component_drv  = &sm1_toacodec_component_drv,
0273     .field_dat_sel  = REG_FIELD(TOACODEC_CTRL0, 18, 19),
0274     .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 12, 14),
0275     .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 6),
0276 };
0277 
0278 static const struct of_device_id g12a_toacodec_of_match[] = {
0279     {
0280         .compatible = "amlogic,g12a-toacodec",
0281         .data = &g12a_toacodec_match_data,
0282     },
0283     {
0284         .compatible = "amlogic,sm1-toacodec",
0285         .data = &sm1_toacodec_match_data,
0286     },
0287     {}
0288 };
0289 MODULE_DEVICE_TABLE(of, g12a_toacodec_of_match);
0290 
0291 static int g12a_toacodec_probe(struct platform_device *pdev)
0292 {
0293     const struct g12a_toacodec_match_data *data;
0294     struct device *dev = &pdev->dev;
0295     struct g12a_toacodec *priv;
0296     void __iomem *regs;
0297     struct regmap *map;
0298     int ret;
0299 
0300     data = device_get_match_data(dev);
0301     if (!data) {
0302         dev_err(dev, "failed to match device\n");
0303         return -ENODEV;
0304     }
0305 
0306     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0307     if (!priv)
0308         return -ENOMEM;
0309 
0310     platform_set_drvdata(pdev, priv);
0311 
0312     ret = device_reset(dev);
0313     if (ret)
0314         return ret;
0315 
0316     regs = devm_platform_ioremap_resource(pdev, 0);
0317     if (IS_ERR(regs))
0318         return PTR_ERR(regs);
0319 
0320     map = devm_regmap_init_mmio(dev, regs, &g12a_toacodec_regmap_cfg);
0321     if (IS_ERR(map)) {
0322         dev_err(dev, "failed to init regmap: %ld\n",
0323             PTR_ERR(map));
0324         return PTR_ERR(map);
0325     }
0326 
0327     priv->field_dat_sel = devm_regmap_field_alloc(dev, map, data->field_dat_sel);
0328     if (IS_ERR(priv->field_dat_sel))
0329         return PTR_ERR(priv->field_dat_sel);
0330 
0331     priv->field_lrclk_sel = devm_regmap_field_alloc(dev, map, data->field_lrclk_sel);
0332     if (IS_ERR(priv->field_lrclk_sel))
0333         return PTR_ERR(priv->field_lrclk_sel);
0334 
0335     priv->field_bclk_sel = devm_regmap_field_alloc(dev, map, data->field_bclk_sel);
0336     if (IS_ERR(priv->field_bclk_sel))
0337         return PTR_ERR(priv->field_bclk_sel);
0338 
0339     return devm_snd_soc_register_component(dev,
0340             data->component_drv, g12a_toacodec_dai_drv,
0341             ARRAY_SIZE(g12a_toacodec_dai_drv));
0342 }
0343 
0344 static struct platform_driver g12a_toacodec_pdrv = {
0345     .driver = {
0346         .name = G12A_TOACODEC_DRV_NAME,
0347         .of_match_table = g12a_toacodec_of_match,
0348     },
0349     .probe = g12a_toacodec_probe,
0350 };
0351 module_platform_driver(g12a_toacodec_pdrv);
0352 
0353 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0354 MODULE_DESCRIPTION("Amlogic G12a To Internal DAC Codec Driver");
0355 MODULE_LICENSE("GPL v2");