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