Merge tag 'powerpc-5.18-3' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[linux-2.6-microblaze.git] / sound / soc / meson / g12a-toacodec.c
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 <linux/clk.h>
8 #include <linux/module.h>
9 #include <sound/pcm_params.h>
10 #include <linux/regmap.h>
11 #include <linux/regulator/consumer.h>
12 #include <linux/reset.h>
13 #include <sound/soc.h>
14 #include <sound/soc-dai.h>
15
16 #include <dt-bindings/sound/meson-g12a-toacodec.h>
17 #include "axg-tdm.h"
18 #include "meson-codec-glue.h"
19
20 #define G12A_TOACODEC_DRV_NAME "g12a-toacodec"
21
22 #define TOACODEC_CTRL0                  0x0
23 #define  CTRL0_ENABLE_SHIFT             31
24 #define  CTRL0_DAT_SEL_SM1_MSB          19
25 #define  CTRL0_DAT_SEL_SM1_LSB          18
26 #define  CTRL0_DAT_SEL_MSB              15
27 #define  CTRL0_DAT_SEL_LSB              14
28 #define  CTRL0_LANE_SEL_SM1             16
29 #define  CTRL0_LANE_SEL                 12
30 #define  CTRL0_LRCLK_SEL_SM1_MSB        14
31 #define  CTRL0_LRCLK_SEL_SM1_LSB        12
32 #define  CTRL0_LRCLK_SEL_MSB            9
33 #define  CTRL0_LRCLK_SEL_LSB            8
34 #define  CTRL0_LRCLK_INV_SM1            BIT(10)
35 #define  CTRL0_BLK_CAP_INV_SM1          BIT(9)
36 #define  CTRL0_BLK_CAP_INV              BIT(7)
37 #define  CTRL0_BCLK_O_INV_SM1           BIT(8)
38 #define  CTRL0_BCLK_O_INV               BIT(6)
39 #define  CTRL0_BCLK_SEL_SM1_MSB         6
40 #define  CTRL0_BCLK_SEL_MSB             5
41 #define  CTRL0_BCLK_SEL_LSB             4
42 #define  CTRL0_MCLK_SEL                 GENMASK(2, 0)
43
44 #define TOACODEC_OUT_CHMAX              2
45
46 struct g12a_toacodec {
47         struct regmap_field *field_dat_sel;
48         struct regmap_field *field_lrclk_sel;
49         struct regmap_field *field_bclk_sel;
50 };
51
52 struct g12a_toacodec_match_data {
53         const struct snd_soc_component_driver *component_drv;
54         struct reg_field field_dat_sel;
55         struct reg_field field_lrclk_sel;
56         struct reg_field field_bclk_sel;
57 };
58
59 static const char * const g12a_toacodec_mux_texts[] = {
60         "I2S A", "I2S B", "I2S C",
61 };
62
63 static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
64                                       struct snd_ctl_elem_value *ucontrol)
65 {
66         struct snd_soc_component *component =
67                 snd_soc_dapm_kcontrol_component(kcontrol);
68         struct g12a_toacodec *priv = snd_soc_component_get_drvdata(component);
69         struct snd_soc_dapm_context *dapm =
70                 snd_soc_dapm_kcontrol_dapm(kcontrol);
71         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
72         unsigned int mux, reg;
73
74         mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
75         regmap_field_read(priv->field_dat_sel, &reg);
76
77         if (mux == reg)
78                 return 0;
79
80         /* Force disconnect of the mux while updating */
81         snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
82
83         regmap_field_write(priv->field_dat_sel, mux);
84         regmap_field_write(priv->field_lrclk_sel, mux);
85         regmap_field_write(priv->field_bclk_sel, mux);
86
87         /*
88          * FIXME:
89          * On this soc, the glue gets the MCLK directly from the clock
90          * controller instead of going the through the TDM interface.
91          *
92          * Here we assume interface A uses clock A, etc ... While it is
93          * true for now, it could be different. Instead the glue should
94          * find out the clock used by the interface and select the same
95          * source. For that, we will need regmap backed clock mux which
96          * is a work in progress
97          */
98         snd_soc_component_update_bits(component, e->reg,
99                                       CTRL0_MCLK_SEL,
100                                       FIELD_PREP(CTRL0_MCLK_SEL, mux));
101
102         snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
103
104         return 0;
105 }
106
107 static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0,
108                             CTRL0_DAT_SEL_LSB,
109                             g12a_toacodec_mux_texts);
110
111 static SOC_ENUM_SINGLE_DECL(sm1_toacodec_mux_enum, TOACODEC_CTRL0,
112                             CTRL0_DAT_SEL_SM1_LSB,
113                             g12a_toacodec_mux_texts);
114
115 static const struct snd_kcontrol_new g12a_toacodec_mux =
116         SOC_DAPM_ENUM_EXT("Source", g12a_toacodec_mux_enum,
117                           snd_soc_dapm_get_enum_double,
118                           g12a_toacodec_mux_put_enum);
119
120 static const struct snd_kcontrol_new sm1_toacodec_mux =
121         SOC_DAPM_ENUM_EXT("Source", sm1_toacodec_mux_enum,
122                           snd_soc_dapm_get_enum_double,
123                           g12a_toacodec_mux_put_enum);
124
125 static const struct snd_kcontrol_new g12a_toacodec_out_enable =
126         SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOACODEC_CTRL0,
127                                     CTRL0_ENABLE_SHIFT, 1, 0);
128
129 static const struct snd_soc_dapm_widget g12a_toacodec_widgets[] = {
130         SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
131                          &g12a_toacodec_mux),
132         SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
133                             &g12a_toacodec_out_enable),
134 };
135
136 static const struct snd_soc_dapm_widget sm1_toacodec_widgets[] = {
137         SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
138                          &sm1_toacodec_mux),
139         SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
140                             &g12a_toacodec_out_enable),
141 };
142
143 static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
144                                          struct snd_pcm_hw_params *params,
145                                          struct snd_soc_dai *dai)
146 {
147         struct meson_codec_glue_input *data;
148         int ret;
149
150         ret = meson_codec_glue_input_hw_params(substream, params, dai);
151         if (ret)
152                 return ret;
153
154         /* The glue will provide 1 lane out of the 4 to the output */
155         data = meson_codec_glue_input_get_data(dai);
156         data->params.channels_min = min_t(unsigned int, TOACODEC_OUT_CHMAX,
157                                         data->params.channels_min);
158         data->params.channels_max = min_t(unsigned int, TOACODEC_OUT_CHMAX,
159                                         data->params.channels_max);
160
161         return 0;
162 }
163
164 static const struct snd_soc_dai_ops g12a_toacodec_input_ops = {
165         .hw_params      = g12a_toacodec_input_hw_params,
166         .set_fmt        = meson_codec_glue_input_set_fmt,
167 };
168
169 static const struct snd_soc_dai_ops g12a_toacodec_output_ops = {
170         .startup        = meson_codec_glue_output_startup,
171 };
172
173 #define TOACODEC_STREAM(xname, xsuffix, xchmax)                 \
174 {                                                               \
175         .stream_name    = xname " " xsuffix,                    \
176         .channels_min   = 1,                                    \
177         .channels_max   = (xchmax),                             \
178         .rate_min       = 5512,                                 \
179         .rate_max       = 192000,                               \
180         .formats        = AXG_TDM_FORMATS,                      \
181 }
182
183 #define TOACODEC_INPUT(xname, xid) {                                    \
184         .name = xname,                                                  \
185         .id = (xid),                                                    \
186         .playback = TOACODEC_STREAM(xname, "Playback", 8),              \
187         .ops = &g12a_toacodec_input_ops,                                \
188         .probe = meson_codec_glue_input_dai_probe,                      \
189         .remove = meson_codec_glue_input_dai_remove,                    \
190 }
191
192 #define TOACODEC_OUTPUT(xname, xid) {                                   \
193         .name = xname,                                                  \
194         .id = (xid),                                                    \
195         .capture = TOACODEC_STREAM(xname, "Capture", TOACODEC_OUT_CHMAX), \
196         .ops = &g12a_toacodec_output_ops,                               \
197 }
198
199 static struct snd_soc_dai_driver g12a_toacodec_dai_drv[] = {
200         TOACODEC_INPUT("IN A", TOACODEC_IN_A),
201         TOACODEC_INPUT("IN B", TOACODEC_IN_B),
202         TOACODEC_INPUT("IN C", TOACODEC_IN_C),
203         TOACODEC_OUTPUT("OUT", TOACODEC_OUT),
204 };
205
206 static int g12a_toacodec_component_probe(struct snd_soc_component *c)
207 {
208         /* Initialize the static clock parameters */
209         return snd_soc_component_write(c, TOACODEC_CTRL0,
210                                        CTRL0_BLK_CAP_INV);
211 }
212
213 static int sm1_toacodec_component_probe(struct snd_soc_component *c)
214 {
215         /* Initialize the static clock parameters */
216         return snd_soc_component_write(c, TOACODEC_CTRL0,
217                                        CTRL0_BLK_CAP_INV_SM1);
218 }
219
220 static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
221         { "SRC", "I2S A", "IN A Playback" },
222         { "SRC", "I2S B", "IN B Playback" },
223         { "SRC", "I2S C", "IN C Playback" },
224         { "OUT EN", "Switch", "SRC" },
225         { "OUT Capture", NULL, "OUT EN" },
226 };
227
228 static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
229         SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
230 };
231
232 static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
233         SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
234 };
235
236 static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
237         .probe                  = g12a_toacodec_component_probe,
238         .controls               = g12a_toacodec_controls,
239         .num_controls           = ARRAY_SIZE(g12a_toacodec_controls),
240         .dapm_widgets           = g12a_toacodec_widgets,
241         .num_dapm_widgets       = ARRAY_SIZE(g12a_toacodec_widgets),
242         .dapm_routes            = g12a_toacodec_routes,
243         .num_dapm_routes        = ARRAY_SIZE(g12a_toacodec_routes),
244         .endianness             = 1,
245         .non_legacy_dai_naming  = 1,
246 };
247
248 static const struct snd_soc_component_driver sm1_toacodec_component_drv = {
249         .probe                  = sm1_toacodec_component_probe,
250         .controls               = sm1_toacodec_controls,
251         .num_controls           = ARRAY_SIZE(sm1_toacodec_controls),
252         .dapm_widgets           = sm1_toacodec_widgets,
253         .num_dapm_widgets       = ARRAY_SIZE(sm1_toacodec_widgets),
254         .dapm_routes            = g12a_toacodec_routes,
255         .num_dapm_routes        = ARRAY_SIZE(g12a_toacodec_routes),
256         .endianness             = 1,
257         .non_legacy_dai_naming  = 1,
258 };
259
260 static const struct regmap_config g12a_toacodec_regmap_cfg = {
261         .reg_bits       = 32,
262         .val_bits       = 32,
263         .reg_stride     = 4,
264 };
265
266 static const struct g12a_toacodec_match_data g12a_toacodec_match_data = {
267         .component_drv  = &g12a_toacodec_component_drv,
268         .field_dat_sel  = REG_FIELD(TOACODEC_CTRL0, 14, 15),
269         .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 8, 9),
270         .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 5),
271 };
272
273 static const struct g12a_toacodec_match_data sm1_toacodec_match_data = {
274         .component_drv  = &sm1_toacodec_component_drv,
275         .field_dat_sel  = REG_FIELD(TOACODEC_CTRL0, 18, 19),
276         .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 12, 14),
277         .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 6),
278 };
279
280 static const struct of_device_id g12a_toacodec_of_match[] = {
281         {
282                 .compatible = "amlogic,g12a-toacodec",
283                 .data = &g12a_toacodec_match_data,
284         },
285         {
286                 .compatible = "amlogic,sm1-toacodec",
287                 .data = &sm1_toacodec_match_data,
288         },
289         {}
290 };
291 MODULE_DEVICE_TABLE(of, g12a_toacodec_of_match);
292
293 static int g12a_toacodec_probe(struct platform_device *pdev)
294 {
295         const struct g12a_toacodec_match_data *data;
296         struct device *dev = &pdev->dev;
297         struct g12a_toacodec *priv;
298         void __iomem *regs;
299         struct regmap *map;
300         int ret;
301
302         data = device_get_match_data(dev);
303         if (!data) {
304                 dev_err(dev, "failed to match device\n");
305                 return -ENODEV;
306         }
307
308         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
309         if (!priv)
310                 return -ENOMEM;
311
312         platform_set_drvdata(pdev, priv);
313
314         ret = device_reset(dev);
315         if (ret)
316                 return ret;
317
318         regs = devm_platform_ioremap_resource(pdev, 0);
319         if (IS_ERR(regs))
320                 return PTR_ERR(regs);
321
322         map = devm_regmap_init_mmio(dev, regs, &g12a_toacodec_regmap_cfg);
323         if (IS_ERR(map)) {
324                 dev_err(dev, "failed to init regmap: %ld\n",
325                         PTR_ERR(map));
326                 return PTR_ERR(map);
327         }
328
329         priv->field_dat_sel = devm_regmap_field_alloc(dev, map, data->field_dat_sel);
330         if (IS_ERR(priv->field_dat_sel))
331                 return PTR_ERR(priv->field_dat_sel);
332
333         priv->field_lrclk_sel = devm_regmap_field_alloc(dev, map, data->field_lrclk_sel);
334         if (IS_ERR(priv->field_lrclk_sel))
335                 return PTR_ERR(priv->field_lrclk_sel);
336
337         priv->field_bclk_sel = devm_regmap_field_alloc(dev, map, data->field_bclk_sel);
338         if (IS_ERR(priv->field_bclk_sel))
339                 return PTR_ERR(priv->field_bclk_sel);
340
341         return devm_snd_soc_register_component(dev,
342                         data->component_drv, g12a_toacodec_dai_drv,
343                         ARRAY_SIZE(g12a_toacodec_dai_drv));
344 }
345
346 static struct platform_driver g12a_toacodec_pdrv = {
347         .driver = {
348                 .name = G12A_TOACODEC_DRV_NAME,
349                 .of_match_table = g12a_toacodec_of_match,
350         },
351         .probe = g12a_toacodec_probe,
352 };
353 module_platform_driver(g12a_toacodec_pdrv);
354
355 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
356 MODULE_DESCRIPTION("Amlogic G12a To Internal DAC Codec Driver");
357 MODULE_LICENSE("GPL v2");