1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ALSA SoC SPDIF Audio Layer |
4 | * |
5 | * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it> |
6 | * Copyright 2015 Marcus Cooper <codekipper@gmail.com> |
7 | * |
8 | * Based on the Allwinner SDK driver, released under the GPL. |
9 | */ |
10 | |
11 | #include <linux/clk.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/device.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/init.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/of.h> |
18 | #include <linux/ioport.h> |
19 | #include <linux/module.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/pm_runtime.h> |
22 | #include <linux/reset.h> |
23 | #include <linux/spinlock.h> |
24 | #include <sound/asoundef.h> |
25 | #include <sound/dmaengine_pcm.h> |
26 | #include <sound/pcm_params.h> |
27 | #include <sound/soc.h> |
28 | |
29 | #define SUN4I_SPDIF_CTL (0x00) |
30 | #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ |
31 | #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) |
32 | #define SUN4I_SPDIF_CTL_GEN BIT(1) |
33 | #define SUN4I_SPDIF_CTL_RESET BIT(0) |
34 | |
35 | #define SUN4I_SPDIF_TXCFG (0x04) |
36 | #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) |
37 | #define SUN4I_SPDIF_TXCFG_ASS BIT(17) |
38 | #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16) |
39 | #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4) |
40 | #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4) |
41 | #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2) |
42 | #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2) |
43 | #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2) |
44 | #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2) |
45 | #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1) |
46 | #define SUN4I_SPDIF_TXCFG_TXEN BIT(0) |
47 | |
48 | #define SUN4I_SPDIF_RXCFG (0x08) |
49 | #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4) |
50 | #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3) |
51 | #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1) |
52 | #define SUN4I_SPDIF_RXCFG_RXEN BIT(0) |
53 | |
54 | #define SUN4I_SPDIF_TXFIFO (0x0C) |
55 | |
56 | #define SUN4I_SPDIF_RXFIFO (0x10) |
57 | |
58 | #define SUN4I_SPDIF_FCTL (0x14) |
59 | #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31) |
60 | #define SUN4I_SPDIF_FCTL_FTX BIT(17) |
61 | #define SUN4I_SPDIF_FCTL_FRX BIT(16) |
62 | #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8) |
63 | #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8) |
64 | #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3) |
65 | #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3) |
66 | #define SUN4I_SPDIF_FCTL_TXIM BIT(2) |
67 | #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0) |
68 | #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) |
69 | |
70 | #define SUN50I_H6_SPDIF_FCTL (0x14) |
71 | #define SUN50I_H6_SPDIF_FCTL_HUB_EN BIT(31) |
72 | #define SUN50I_H6_SPDIF_FCTL_FTX BIT(30) |
73 | #define SUN50I_H6_SPDIF_FCTL_FRX BIT(29) |
74 | #define SUN50I_H6_SPDIF_FCTL_TXTL(v) ((v) << 12) |
75 | #define SUN50I_H6_SPDIF_FCTL_TXTL_MASK GENMASK(19, 12) |
76 | #define SUN50I_H6_SPDIF_FCTL_RXTL(v) ((v) << 4) |
77 | #define SUN50I_H6_SPDIF_FCTL_RXTL_MASK GENMASK(10, 4) |
78 | #define SUN50I_H6_SPDIF_FCTL_TXIM BIT(2) |
79 | #define SUN50I_H6_SPDIF_FCTL_RXOM(v) ((v) << 0) |
80 | #define SUN50I_H6_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) |
81 | |
82 | #define SUN4I_SPDIF_FSTA (0x18) |
83 | #define SUN4I_SPDIF_FSTA_TXE BIT(14) |
84 | #define SUN4I_SPDIF_FSTA_TXECNTSHT (8) |
85 | #define SUN4I_SPDIF_FSTA_RXA BIT(6) |
86 | #define SUN4I_SPDIF_FSTA_RXACNTSHT (0) |
87 | |
88 | #define SUN4I_SPDIF_INT (0x1C) |
89 | #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18) |
90 | #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17) |
91 | #define SUN4I_SPDIF_INT_RXPARERREN BIT(16) |
92 | #define SUN4I_SPDIF_INT_TXDRQEN BIT(7) |
93 | #define SUN4I_SPDIF_INT_TXUIEN BIT(6) |
94 | #define SUN4I_SPDIF_INT_TXOIEN BIT(5) |
95 | #define SUN4I_SPDIF_INT_TXEIEN BIT(4) |
96 | #define SUN4I_SPDIF_INT_RXDRQEN BIT(2) |
97 | #define SUN4I_SPDIF_INT_RXOIEN BIT(1) |
98 | #define SUN4I_SPDIF_INT_RXAIEN BIT(0) |
99 | |
100 | #define SUN4I_SPDIF_ISTA (0x20) |
101 | #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18) |
102 | #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17) |
103 | #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16) |
104 | #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6) |
105 | #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5) |
106 | #define SUN4I_SPDIF_ISTA_TXESTA BIT(4) |
107 | #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1) |
108 | #define SUN4I_SPDIF_ISTA_RXASTA BIT(0) |
109 | |
110 | #define SUN8I_SPDIF_TXFIFO (0x20) |
111 | |
112 | #define SUN4I_SPDIF_TXCNT (0x24) |
113 | |
114 | #define SUN4I_SPDIF_RXCNT (0x28) |
115 | |
116 | #define SUN4I_SPDIF_TXCHSTA0 (0x2C) |
117 | #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28) |
118 | #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24) |
119 | #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24) |
120 | #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20) |
121 | #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20) |
122 | #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16) |
123 | #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8) |
124 | #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6) |
125 | #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3) |
126 | #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2) |
127 | #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1) |
128 | #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0) |
129 | |
130 | #define SUN4I_SPDIF_TXCHSTA1 (0x30) |
131 | #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8) |
132 | #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4) |
133 | #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4) |
134 | #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1) |
135 | #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0) |
136 | |
137 | #define SUN4I_SPDIF_RXCHSTA0 (0x34) |
138 | #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28) |
139 | #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24) |
140 | #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20) |
141 | #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16) |
142 | #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8) |
143 | #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6) |
144 | #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3) |
145 | #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2) |
146 | #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1) |
147 | #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0) |
148 | |
149 | #define SUN4I_SPDIF_RXCHSTA1 (0x38) |
150 | #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8) |
151 | #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4) |
152 | #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1) |
153 | #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0) |
154 | |
155 | /* Defines for Sampling Frequency */ |
156 | #define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0 |
157 | #define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1 |
158 | #define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2 |
159 | #define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3 |
160 | #define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4 |
161 | #define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6 |
162 | #define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8 |
163 | #define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9 |
164 | #define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa |
165 | #define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc |
166 | #define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe |
167 | |
168 | /** |
169 | * struct sun4i_spdif_quirks - Differences between SoC variants. |
170 | * |
171 | * @reg_dac_txdata: TX FIFO offset for DMA config. |
172 | * @has_reset: SoC needs reset deasserted. |
173 | * @val_fctl_ftx: TX FIFO flush bitmask. |
174 | */ |
175 | struct sun4i_spdif_quirks { |
176 | unsigned int reg_dac_txdata; |
177 | bool has_reset; |
178 | unsigned int val_fctl_ftx; |
179 | }; |
180 | |
181 | struct sun4i_spdif_dev { |
182 | struct platform_device *pdev; |
183 | struct clk *spdif_clk; |
184 | struct clk *apb_clk; |
185 | struct reset_control *rst; |
186 | struct snd_soc_dai_driver cpu_dai_drv; |
187 | struct regmap *regmap; |
188 | struct snd_dmaengine_dai_dma_data dma_params_tx; |
189 | const struct sun4i_spdif_quirks *quirks; |
190 | spinlock_t lock; |
191 | }; |
192 | |
193 | static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) |
194 | { |
195 | const struct sun4i_spdif_quirks *quirks = host->quirks; |
196 | |
197 | /* soft reset SPDIF */ |
198 | regmap_write(map: host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); |
199 | |
200 | /* flush TX FIFO */ |
201 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_FCTL, |
202 | mask: quirks->val_fctl_ftx, val: quirks->val_fctl_ftx); |
203 | |
204 | /* clear TX counter */ |
205 | regmap_write(map: host->regmap, SUN4I_SPDIF_TXCNT, val: 0); |
206 | } |
207 | |
208 | static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream, |
209 | struct sun4i_spdif_dev *host) |
210 | { |
211 | if (substream->runtime->channels == 1) |
212 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_TXCFG, |
213 | SUN4I_SPDIF_TXCFG_SINGLEMOD, |
214 | SUN4I_SPDIF_TXCFG_SINGLEMOD); |
215 | |
216 | /* SPDIF TX ENABLE */ |
217 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_TXCFG, |
218 | SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN); |
219 | |
220 | /* DRQ ENABLE */ |
221 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_INT, |
222 | SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN); |
223 | |
224 | /* Global enable */ |
225 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_CTL, |
226 | SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN); |
227 | } |
228 | |
229 | static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream, |
230 | struct sun4i_spdif_dev *host) |
231 | { |
232 | /* SPDIF TX DISABLE */ |
233 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_TXCFG, |
234 | SUN4I_SPDIF_TXCFG_TXEN, val: 0); |
235 | |
236 | /* DRQ DISABLE */ |
237 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_INT, |
238 | SUN4I_SPDIF_INT_TXDRQEN, val: 0); |
239 | |
240 | /* Global disable */ |
241 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_CTL, |
242 | SUN4I_SPDIF_CTL_GEN, val: 0); |
243 | } |
244 | |
245 | static int sun4i_spdif_startup(struct snd_pcm_substream *substream, |
246 | struct snd_soc_dai *cpu_dai) |
247 | { |
248 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
249 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); |
250 | |
251 | if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) |
252 | return -EINVAL; |
253 | |
254 | sun4i_spdif_configure(host); |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, |
260 | struct snd_pcm_hw_params *params, |
261 | struct snd_soc_dai *cpu_dai) |
262 | { |
263 | int ret = 0; |
264 | int fmt; |
265 | unsigned long rate = params_rate(p: params); |
266 | u32 mclk_div = 0; |
267 | unsigned int mclk = 0; |
268 | u32 reg_val; |
269 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai: cpu_dai); |
270 | struct platform_device *pdev = host->pdev; |
271 | |
272 | /* Add the PCM and raw data select interface */ |
273 | switch (params_channels(p: params)) { |
274 | case 1: /* PCM mode */ |
275 | case 2: |
276 | fmt = 0; |
277 | break; |
278 | case 4: /* raw data mode */ |
279 | fmt = SUN4I_SPDIF_TXCFG_NONAUDIO; |
280 | break; |
281 | default: |
282 | return -EINVAL; |
283 | } |
284 | |
285 | switch (params_format(p: params)) { |
286 | case SNDRV_PCM_FORMAT_S16_LE: |
287 | fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT; |
288 | break; |
289 | case SNDRV_PCM_FORMAT_S20_3LE: |
290 | fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT; |
291 | break; |
292 | case SNDRV_PCM_FORMAT_S24_LE: |
293 | fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT; |
294 | break; |
295 | default: |
296 | return -EINVAL; |
297 | } |
298 | |
299 | switch (rate) { |
300 | case 22050: |
301 | case 44100: |
302 | case 88200: |
303 | case 176400: |
304 | mclk = 22579200; |
305 | break; |
306 | case 24000: |
307 | case 32000: |
308 | case 48000: |
309 | case 96000: |
310 | case 192000: |
311 | mclk = 24576000; |
312 | break; |
313 | default: |
314 | return -EINVAL; |
315 | } |
316 | |
317 | ret = clk_set_rate(clk: host->spdif_clk, rate: mclk); |
318 | if (ret < 0) { |
319 | dev_err(&pdev->dev, |
320 | "Setting SPDIF clock rate for %d Hz failed!\n" , mclk); |
321 | return ret; |
322 | } |
323 | |
324 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_FCTL, |
325 | SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); |
326 | |
327 | switch (rate) { |
328 | case 22050: |
329 | case 24000: |
330 | mclk_div = 8; |
331 | break; |
332 | case 32000: |
333 | mclk_div = 6; |
334 | break; |
335 | case 44100: |
336 | case 48000: |
337 | mclk_div = 4; |
338 | break; |
339 | case 88200: |
340 | case 96000: |
341 | mclk_div = 2; |
342 | break; |
343 | case 176400: |
344 | case 192000: |
345 | mclk_div = 1; |
346 | break; |
347 | default: |
348 | return -EINVAL; |
349 | } |
350 | |
351 | reg_val = 0; |
352 | reg_val |= SUN4I_SPDIF_TXCFG_ASS; |
353 | reg_val |= fmt; /* set non audio and bit depth */ |
354 | reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE; |
355 | reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1); |
356 | regmap_write(map: host->regmap, SUN4I_SPDIF_TXCFG, val: reg_val); |
357 | |
358 | return 0; |
359 | } |
360 | |
361 | static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, |
362 | struct snd_soc_dai *dai) |
363 | { |
364 | int ret = 0; |
365 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); |
366 | |
367 | if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) |
368 | return -EINVAL; |
369 | |
370 | switch (cmd) { |
371 | case SNDRV_PCM_TRIGGER_START: |
372 | case SNDRV_PCM_TRIGGER_RESUME: |
373 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
374 | sun4i_snd_txctrl_on(substream, host); |
375 | break; |
376 | |
377 | case SNDRV_PCM_TRIGGER_STOP: |
378 | case SNDRV_PCM_TRIGGER_SUSPEND: |
379 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
380 | sun4i_snd_txctrl_off(substream, host); |
381 | break; |
382 | |
383 | default: |
384 | ret = -EINVAL; |
385 | break; |
386 | } |
387 | return ret; |
388 | } |
389 | |
390 | static int sun4i_spdif_info(struct snd_kcontrol *kcontrol, |
391 | struct snd_ctl_elem_info *uinfo) |
392 | { |
393 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
394 | uinfo->count = 1; |
395 | |
396 | return 0; |
397 | } |
398 | |
399 | static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol, |
400 | struct snd_ctl_elem_value *ucontrol) |
401 | { |
402 | u8 *status = ucontrol->value.iec958.status; |
403 | |
404 | status[0] = 0xff; |
405 | status[1] = 0xff; |
406 | status[2] = 0xff; |
407 | status[3] = 0xff; |
408 | status[4] = 0xff; |
409 | status[5] = 0x03; |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol, |
415 | struct snd_ctl_elem_value *ucontrol) |
416 | { |
417 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); |
418 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai: cpu_dai); |
419 | u8 *status = ucontrol->value.iec958.status; |
420 | unsigned long flags; |
421 | unsigned int reg; |
422 | |
423 | spin_lock_irqsave(&host->lock, flags); |
424 | |
425 | regmap_read(map: host->regmap, SUN4I_SPDIF_TXCHSTA0, val: ®); |
426 | |
427 | status[0] = reg & 0xff; |
428 | status[1] = (reg >> 8) & 0xff; |
429 | status[2] = (reg >> 16) & 0xff; |
430 | status[3] = (reg >> 24) & 0xff; |
431 | |
432 | regmap_read(map: host->regmap, SUN4I_SPDIF_TXCHSTA1, val: ®); |
433 | |
434 | status[4] = reg & 0xff; |
435 | status[5] = (reg >> 8) & 0x3; |
436 | |
437 | spin_unlock_irqrestore(lock: &host->lock, flags); |
438 | |
439 | return 0; |
440 | } |
441 | |
442 | static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol, |
443 | struct snd_ctl_elem_value *ucontrol) |
444 | { |
445 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); |
446 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai: cpu_dai); |
447 | u8 *status = ucontrol->value.iec958.status; |
448 | unsigned long flags; |
449 | unsigned int reg; |
450 | bool chg0, chg1; |
451 | |
452 | spin_lock_irqsave(&host->lock, flags); |
453 | |
454 | reg = (u32)status[3] << 24; |
455 | reg |= (u32)status[2] << 16; |
456 | reg |= (u32)status[1] << 8; |
457 | reg |= (u32)status[0]; |
458 | |
459 | regmap_update_bits_check(map: host->regmap, SUN4I_SPDIF_TXCHSTA0, |
460 | GENMASK(31,0), val: reg, change: &chg0); |
461 | |
462 | reg = (u32)status[5] << 8; |
463 | reg |= (u32)status[4]; |
464 | |
465 | regmap_update_bits_check(map: host->regmap, SUN4I_SPDIF_TXCHSTA1, |
466 | GENMASK(9,0), val: reg, change: &chg1); |
467 | |
468 | reg = SUN4I_SPDIF_TXCFG_CHSTMODE; |
469 | if (status[0] & IEC958_AES0_NONAUDIO) |
470 | reg |= SUN4I_SPDIF_TXCFG_NONAUDIO; |
471 | |
472 | regmap_update_bits(map: host->regmap, SUN4I_SPDIF_TXCFG, |
473 | SUN4I_SPDIF_TXCFG_CHSTMODE | |
474 | SUN4I_SPDIF_TXCFG_NONAUDIO, val: reg); |
475 | |
476 | spin_unlock_irqrestore(lock: &host->lock, flags); |
477 | |
478 | return chg0 || chg1; |
479 | } |
480 | |
481 | static struct snd_kcontrol_new sun4i_spdif_controls[] = { |
482 | { |
483 | .access = SNDRV_CTL_ELEM_ACCESS_READ, |
484 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
485 | .name = SNDRV_CTL_NAME_IEC958("" , PLAYBACK, MASK), |
486 | .info = sun4i_spdif_info, |
487 | .get = sun4i_spdif_get_status_mask |
488 | }, |
489 | { |
490 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
491 | .name = SNDRV_CTL_NAME_IEC958("" , PLAYBACK, DEFAULT), |
492 | .info = sun4i_spdif_info, |
493 | .get = sun4i_spdif_get_status, |
494 | .put = sun4i_spdif_set_status |
495 | } |
496 | }; |
497 | |
498 | static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) |
499 | { |
500 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); |
501 | |
502 | snd_soc_dai_init_dma_data(dai, playback: &host->dma_params_tx, NULL); |
503 | snd_soc_add_dai_controls(dai, controls: sun4i_spdif_controls, |
504 | ARRAY_SIZE(sun4i_spdif_controls)); |
505 | |
506 | return 0; |
507 | } |
508 | |
509 | static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = { |
510 | .probe = sun4i_spdif_soc_dai_probe, |
511 | .startup = sun4i_spdif_startup, |
512 | .trigger = sun4i_spdif_trigger, |
513 | .hw_params = sun4i_spdif_hw_params, |
514 | }; |
515 | |
516 | static const struct regmap_config sun4i_spdif_regmap_config = { |
517 | .reg_bits = 32, |
518 | .reg_stride = 4, |
519 | .val_bits = 32, |
520 | .max_register = SUN4I_SPDIF_RXCHSTA1, |
521 | }; |
522 | |
523 | #define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 |
524 | |
525 | #define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ |
526 | SNDRV_PCM_FORMAT_S20_3LE | \ |
527 | SNDRV_PCM_FORMAT_S24_LE) |
528 | |
529 | static struct snd_soc_dai_driver sun4i_spdif_dai = { |
530 | .playback = { |
531 | .channels_min = 1, |
532 | .channels_max = 2, |
533 | .rates = SUN4I_RATES, |
534 | .formats = SUN4I_FORMATS, |
535 | }, |
536 | .ops = &sun4i_spdif_dai_ops, |
537 | .name = "spdif" , |
538 | }; |
539 | |
540 | static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { |
541 | .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, |
542 | .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, |
543 | }; |
544 | |
545 | static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { |
546 | .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, |
547 | .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, |
548 | .has_reset = true, |
549 | }; |
550 | |
551 | static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { |
552 | .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, |
553 | .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, |
554 | .has_reset = true, |
555 | }; |
556 | |
557 | static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { |
558 | .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, |
559 | .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, |
560 | .has_reset = true, |
561 | }; |
562 | |
563 | static const struct of_device_id sun4i_spdif_of_match[] = { |
564 | { |
565 | .compatible = "allwinner,sun4i-a10-spdif" , |
566 | .data = &sun4i_a10_spdif_quirks, |
567 | }, |
568 | { |
569 | .compatible = "allwinner,sun6i-a31-spdif" , |
570 | .data = &sun6i_a31_spdif_quirks, |
571 | }, |
572 | { |
573 | .compatible = "allwinner,sun8i-h3-spdif" , |
574 | .data = &sun8i_h3_spdif_quirks, |
575 | }, |
576 | { |
577 | .compatible = "allwinner,sun50i-h6-spdif" , |
578 | .data = &sun50i_h6_spdif_quirks, |
579 | }, |
580 | { |
581 | .compatible = "allwinner,sun50i-h616-spdif" , |
582 | /* Essentially the same as the H6, but without RX */ |
583 | .data = &sun50i_h6_spdif_quirks, |
584 | }, |
585 | { /* sentinel */ } |
586 | }; |
587 | MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); |
588 | |
589 | static const struct snd_soc_component_driver sun4i_spdif_component = { |
590 | .name = "sun4i-spdif" , |
591 | .legacy_dai_naming = 1, |
592 | }; |
593 | |
594 | static int sun4i_spdif_runtime_suspend(struct device *dev) |
595 | { |
596 | struct sun4i_spdif_dev *host = dev_get_drvdata(dev); |
597 | |
598 | clk_disable_unprepare(clk: host->spdif_clk); |
599 | clk_disable_unprepare(clk: host->apb_clk); |
600 | |
601 | return 0; |
602 | } |
603 | |
604 | static int sun4i_spdif_runtime_resume(struct device *dev) |
605 | { |
606 | struct sun4i_spdif_dev *host = dev_get_drvdata(dev); |
607 | int ret; |
608 | |
609 | ret = clk_prepare_enable(clk: host->spdif_clk); |
610 | if (ret) |
611 | return ret; |
612 | ret = clk_prepare_enable(clk: host->apb_clk); |
613 | if (ret) |
614 | clk_disable_unprepare(clk: host->spdif_clk); |
615 | |
616 | return ret; |
617 | } |
618 | |
619 | static int sun4i_spdif_probe(struct platform_device *pdev) |
620 | { |
621 | struct sun4i_spdif_dev *host; |
622 | struct resource *res; |
623 | const struct sun4i_spdif_quirks *quirks; |
624 | int ret; |
625 | void __iomem *base; |
626 | |
627 | dev_dbg(&pdev->dev, "Entered %s\n" , __func__); |
628 | |
629 | host = devm_kzalloc(dev: &pdev->dev, size: sizeof(*host), GFP_KERNEL); |
630 | if (!host) |
631 | return -ENOMEM; |
632 | |
633 | host->pdev = pdev; |
634 | spin_lock_init(&host->lock); |
635 | |
636 | /* Initialize this copy of the CPU DAI driver structure */ |
637 | memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); |
638 | host->cpu_dai_drv.name = dev_name(dev: &pdev->dev); |
639 | |
640 | /* Get the addresses */ |
641 | base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
642 | if (IS_ERR(ptr: base)) |
643 | return PTR_ERR(ptr: base); |
644 | |
645 | quirks = of_device_get_match_data(dev: &pdev->dev); |
646 | if (quirks == NULL) { |
647 | dev_err(&pdev->dev, "Failed to determine the quirks to use\n" ); |
648 | return -ENODEV; |
649 | } |
650 | host->quirks = quirks; |
651 | |
652 | host->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
653 | &sun4i_spdif_regmap_config); |
654 | |
655 | /* Clocks */ |
656 | host->apb_clk = devm_clk_get(dev: &pdev->dev, id: "apb" ); |
657 | if (IS_ERR(ptr: host->apb_clk)) { |
658 | dev_err(&pdev->dev, "failed to get a apb clock.\n" ); |
659 | return PTR_ERR(ptr: host->apb_clk); |
660 | } |
661 | |
662 | host->spdif_clk = devm_clk_get(dev: &pdev->dev, id: "spdif" ); |
663 | if (IS_ERR(ptr: host->spdif_clk)) { |
664 | dev_err(&pdev->dev, "failed to get a spdif clock.\n" ); |
665 | return PTR_ERR(ptr: host->spdif_clk); |
666 | } |
667 | |
668 | host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; |
669 | host->dma_params_tx.maxburst = 8; |
670 | host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
671 | |
672 | platform_set_drvdata(pdev, data: host); |
673 | |
674 | if (quirks->has_reset) { |
675 | host->rst = devm_reset_control_get_optional_exclusive(dev: &pdev->dev, |
676 | NULL); |
677 | if (PTR_ERR(ptr: host->rst) == -EPROBE_DEFER) { |
678 | ret = -EPROBE_DEFER; |
679 | dev_err(&pdev->dev, "Failed to get reset: %d\n" , ret); |
680 | return ret; |
681 | } |
682 | if (!IS_ERR(ptr: host->rst)) |
683 | reset_control_deassert(rstc: host->rst); |
684 | } |
685 | |
686 | ret = devm_snd_soc_register_component(dev: &pdev->dev, |
687 | component_driver: &sun4i_spdif_component, dai_drv: &sun4i_spdif_dai, num_dai: 1); |
688 | if (ret) |
689 | return ret; |
690 | |
691 | pm_runtime_enable(dev: &pdev->dev); |
692 | if (!pm_runtime_enabled(dev: &pdev->dev)) { |
693 | ret = sun4i_spdif_runtime_resume(dev: &pdev->dev); |
694 | if (ret) |
695 | goto err_unregister; |
696 | } |
697 | |
698 | ret = devm_snd_dmaengine_pcm_register(dev: &pdev->dev, NULL, flags: 0); |
699 | if (ret) |
700 | goto err_suspend; |
701 | return 0; |
702 | err_suspend: |
703 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
704 | sun4i_spdif_runtime_suspend(dev: &pdev->dev); |
705 | err_unregister: |
706 | pm_runtime_disable(dev: &pdev->dev); |
707 | return ret; |
708 | } |
709 | |
710 | static void sun4i_spdif_remove(struct platform_device *pdev) |
711 | { |
712 | pm_runtime_disable(dev: &pdev->dev); |
713 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
714 | sun4i_spdif_runtime_suspend(dev: &pdev->dev); |
715 | } |
716 | |
717 | static const struct dev_pm_ops sun4i_spdif_pm = { |
718 | SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, |
719 | sun4i_spdif_runtime_resume, NULL) |
720 | }; |
721 | |
722 | static struct platform_driver sun4i_spdif_driver = { |
723 | .driver = { |
724 | .name = "sun4i-spdif" , |
725 | .of_match_table = sun4i_spdif_of_match, |
726 | .pm = &sun4i_spdif_pm, |
727 | }, |
728 | .probe = sun4i_spdif_probe, |
729 | .remove_new = sun4i_spdif_remove, |
730 | }; |
731 | |
732 | module_platform_driver(sun4i_spdif_driver); |
733 | |
734 | MODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>" ); |
735 | MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>" ); |
736 | MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface" ); |
737 | MODULE_LICENSE("GPL" ); |
738 | MODULE_ALIAS("platform:sun4i-spdif" ); |
739 | |