Commit | Line | Data |
---|---|---|
c4458b74 DH |
1 | /* |
2 | * IMG SPDIF input controller driver | |
3 | * | |
4 | * Copyright (C) 2015 Imagination Technologies Ltd. | |
5 | * | |
6 | * Author: Damien Horsley <Damien.Horsley@imgtec.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms and conditions of the GNU General Public License, | |
10 | * version 2, as published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/clk.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/reset.h> | |
20 | ||
21 | #include <sound/core.h> | |
22 | #include <sound/dmaengine_pcm.h> | |
23 | #include <sound/initval.h> | |
24 | #include <sound/pcm.h> | |
25 | #include <sound/pcm_params.h> | |
26 | #include <sound/soc.h> | |
27 | ||
28 | #define IMG_SPDIF_IN_RX_FIFO_OFFSET 0 | |
29 | ||
30 | #define IMG_SPDIF_IN_CTL 0x4 | |
31 | #define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff | |
32 | #define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0 | |
33 | #define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00 | |
34 | #define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8 | |
35 | #define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000 | |
36 | #define IMG_SPDIF_IN_CTL_TRK_SHIFT 16 | |
37 | #define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000 | |
38 | #define IMG_SPDIF_IN_CTL_SRD_SHIFT 28 | |
39 | #define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31) | |
40 | ||
41 | #define IMG_SPDIF_IN_STATUS 0x8 | |
42 | #define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000 | |
43 | #define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12 | |
44 | #define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15) | |
45 | #define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15 | |
46 | ||
47 | #define IMG_SPDIF_IN_CLKGEN 0x1c | |
48 | #define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff | |
49 | #define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0 | |
50 | #define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000 | |
51 | #define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16 | |
52 | ||
53 | #define IMG_SPDIF_IN_CSL 0x20 | |
54 | ||
55 | #define IMG_SPDIF_IN_CSH 0x24 | |
56 | #define IMG_SPDIF_IN_CSH_MASK 0xff | |
57 | #define IMG_SPDIF_IN_CSH_SHIFT 0 | |
58 | ||
59 | #define IMG_SPDIF_IN_SOFT_RESET 0x28 | |
60 | #define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0) | |
61 | ||
62 | #define IMG_SPDIF_IN_ACLKGEN_START 0x2c | |
63 | #define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff | |
64 | #define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0 | |
65 | #define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00 | |
66 | #define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10 | |
67 | #define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000 | |
68 | #define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20 | |
69 | ||
70 | #define IMG_SPDIF_IN_NUM_ACLKGEN 4 | |
71 | ||
72 | struct img_spdif_in { | |
73 | spinlock_t lock; | |
74 | void __iomem *base; | |
75 | struct clk *clk_sys; | |
76 | struct snd_dmaengine_dai_dma_data dma_data; | |
77 | struct device *dev; | |
78 | unsigned int trk; | |
79 | bool multi_freq; | |
80 | int lock_acquire; | |
81 | int lock_release; | |
82 | unsigned int single_freq; | |
83 | unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; | |
84 | bool active; | |
85 | ||
86 | /* Write-only registers */ | |
87 | unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; | |
88 | }; | |
89 | ||
90 | static inline void img_spdif_in_writel(struct img_spdif_in *spdif, | |
91 | u32 val, u32 reg) | |
92 | { | |
93 | writel(val, spdif->base + reg); | |
94 | } | |
95 | ||
96 | static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg) | |
97 | { | |
98 | return readl(spdif->base + reg); | |
99 | } | |
100 | ||
101 | static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif, | |
102 | u32 index) | |
103 | { | |
104 | img_spdif_in_writel(spdif, spdif->aclkgen_regs[index], | |
105 | IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4)); | |
106 | } | |
107 | ||
108 | static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif, | |
109 | unsigned int sample_rate, unsigned long *actual_freq) | |
110 | { | |
111 | unsigned long min_freq, freq_t; | |
112 | ||
113 | /* Clock rate must be at least 24x the bit rate */ | |
114 | min_freq = sample_rate * 2 * 32 * 24; | |
115 | ||
116 | freq_t = clk_get_rate(spdif->clk_sys); | |
117 | ||
118 | if (freq_t < min_freq) | |
119 | return -EINVAL; | |
120 | ||
121 | *actual_freq = freq_t; | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom, | |
127 | unsigned int *phld, unsigned long clk_rate) | |
128 | { | |
129 | unsigned int ori, nom, hld; | |
130 | ||
131 | /* | |
132 | * Calculate oversampling ratio, nominal phase increment and hold | |
133 | * increment for the given rate / frequency | |
134 | */ | |
135 | ||
136 | if (!rate) | |
137 | return -EINVAL; | |
138 | ||
139 | ori = clk_rate / (rate * 64); | |
140 | ||
141 | if (!ori) | |
142 | return -EINVAL; | |
143 | ||
144 | nom = (4096 / ori) + 1; | |
145 | do | |
146 | hld = 4096 - (--nom * (ori - 1)); | |
147 | while (hld < 120); | |
148 | ||
149 | *pnom = nom; | |
150 | *phld = hld; | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif, | |
156 | unsigned int rate) | |
157 | { | |
158 | unsigned int nom, hld; | |
159 | unsigned long flags, clk_rate; | |
160 | int ret = 0; | |
161 | u32 reg; | |
162 | ||
163 | ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate); | |
164 | if (ret) | |
165 | return ret; | |
166 | ||
167 | ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate); | |
168 | if (ret) | |
169 | return ret; | |
170 | ||
171 | reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) & | |
172 | IMG_SPDIF_IN_CLKGEN_NOM_MASK; | |
173 | reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) & | |
174 | IMG_SPDIF_IN_CLKGEN_HLD_MASK; | |
175 | ||
176 | spin_lock_irqsave(&spdif->lock, flags); | |
177 | ||
178 | if (spdif->active) { | |
179 | spin_unlock_irqrestore(&spdif->lock, flags); | |
180 | return -EBUSY; | |
181 | } | |
182 | ||
183 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN); | |
184 | ||
185 | spdif->single_freq = rate; | |
186 | ||
187 | spin_unlock_irqrestore(&spdif->lock, flags); | |
188 | ||
189 | return 0; | |
190 | } | |
191 | ||
192 | static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif, | |
193 | unsigned int multi_freqs[]) | |
194 | { | |
195 | unsigned int nom, hld, rate, max_rate = 0; | |
196 | unsigned long flags, clk_rate; | |
197 | int i, ret = 0; | |
198 | u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; | |
199 | ||
200 | for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) | |
201 | if (multi_freqs[i] > max_rate) | |
202 | max_rate = multi_freqs[i]; | |
203 | ||
204 | ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate); | |
205 | if (ret) | |
206 | return ret; | |
207 | ||
208 | for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { | |
209 | rate = multi_freqs[i]; | |
210 | ||
211 | ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate); | |
212 | if (ret) | |
213 | return ret; | |
214 | ||
215 | reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) & | |
216 | IMG_SPDIF_IN_ACLKGEN_NOM_MASK; | |
217 | reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) & | |
218 | IMG_SPDIF_IN_ACLKGEN_HLD_MASK; | |
219 | temp_regs[i] = reg; | |
220 | } | |
221 | ||
222 | spin_lock_irqsave(&spdif->lock, flags); | |
223 | ||
224 | if (spdif->active) { | |
225 | spin_unlock_irqrestore(&spdif->lock, flags); | |
226 | return -EBUSY; | |
227 | } | |
228 | ||
229 | trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT; | |
230 | ||
231 | for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { | |
232 | spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg; | |
233 | img_spdif_in_aclkgen_writel(spdif, i); | |
234 | } | |
235 | ||
236 | spdif->multi_freq = true; | |
237 | spdif->multi_freqs[0] = multi_freqs[0]; | |
238 | spdif->multi_freqs[1] = multi_freqs[1]; | |
239 | spdif->multi_freqs[2] = multi_freqs[2]; | |
240 | spdif->multi_freqs[3] = multi_freqs[3]; | |
241 | ||
242 | spin_unlock_irqrestore(&spdif->lock, flags); | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
247 | static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol, | |
248 | struct snd_ctl_elem_info *uinfo) | |
249 | { | |
250 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | |
251 | uinfo->count = 1; | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
256 | static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol, | |
257 | struct snd_ctl_elem_value *ucontrol) | |
258 | { | |
259 | ucontrol->value.iec958.status[0] = 0xff; | |
260 | ucontrol->value.iec958.status[1] = 0xff; | |
261 | ucontrol->value.iec958.status[2] = 0xff; | |
262 | ucontrol->value.iec958.status[3] = 0xff; | |
263 | ucontrol->value.iec958.status[4] = 0xff; | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
268 | static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol, | |
269 | struct snd_ctl_elem_value *ucontrol) | |
270 | { | |
271 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
272 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
273 | u32 reg; | |
274 | ||
275 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL); | |
276 | ucontrol->value.iec958.status[0] = reg & 0xff; | |
277 | ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff; | |
278 | ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff; | |
279 | ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff; | |
280 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH); | |
281 | ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK) | |
282 | >> IMG_SPDIF_IN_CSH_SHIFT; | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol, | |
288 | struct snd_ctl_elem_info *uinfo) | |
289 | { | |
290 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
291 | uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN; | |
292 | uinfo->value.integer.min = 0; | |
293 | uinfo->value.integer.max = LONG_MAX; | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol, | |
299 | struct snd_ctl_elem_value *ucontrol) | |
300 | { | |
301 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
302 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
303 | unsigned long flags; | |
304 | ||
305 | spin_lock_irqsave(&spdif->lock, flags); | |
306 | if (spdif->multi_freq) { | |
307 | ucontrol->value.integer.value[0] = spdif->multi_freqs[0]; | |
308 | ucontrol->value.integer.value[1] = spdif->multi_freqs[1]; | |
309 | ucontrol->value.integer.value[2] = spdif->multi_freqs[2]; | |
310 | ucontrol->value.integer.value[3] = spdif->multi_freqs[3]; | |
311 | } else { | |
312 | ucontrol->value.integer.value[0] = 0; | |
313 | ucontrol->value.integer.value[1] = 0; | |
314 | ucontrol->value.integer.value[2] = 0; | |
315 | ucontrol->value.integer.value[3] = 0; | |
316 | } | |
317 | spin_unlock_irqrestore(&spdif->lock, flags); | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
322 | static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol, | |
323 | struct snd_ctl_elem_value *ucontrol) | |
324 | { | |
325 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
326 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
327 | unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; | |
328 | bool multi_freq; | |
329 | unsigned long flags; | |
330 | ||
331 | if ((ucontrol->value.integer.value[0] == 0) && | |
332 | (ucontrol->value.integer.value[1] == 0) && | |
333 | (ucontrol->value.integer.value[2] == 0) && | |
334 | (ucontrol->value.integer.value[3] == 0)) { | |
335 | multi_freq = false; | |
336 | } else { | |
337 | multi_freqs[0] = ucontrol->value.integer.value[0]; | |
338 | multi_freqs[1] = ucontrol->value.integer.value[1]; | |
339 | multi_freqs[2] = ucontrol->value.integer.value[2]; | |
340 | multi_freqs[3] = ucontrol->value.integer.value[3]; | |
341 | multi_freq = true; | |
342 | } | |
343 | ||
344 | if (multi_freq) | |
345 | return img_spdif_in_do_clkgen_multi(spdif, multi_freqs); | |
346 | ||
347 | spin_lock_irqsave(&spdif->lock, flags); | |
348 | ||
349 | if (spdif->active) { | |
350 | spin_unlock_irqrestore(&spdif->lock, flags); | |
351 | return -EBUSY; | |
352 | } | |
353 | ||
354 | spdif->multi_freq = false; | |
355 | ||
356 | spin_unlock_irqrestore(&spdif->lock, flags); | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol, | |
362 | struct snd_ctl_elem_info *uinfo) | |
363 | { | |
364 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
365 | uinfo->count = 1; | |
366 | uinfo->value.integer.min = 0; | |
367 | uinfo->value.integer.max = LONG_MAX; | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
372 | static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol, | |
373 | struct snd_ctl_elem_value *uc) | |
374 | { | |
375 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
376 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
377 | u32 reg; | |
378 | int i; | |
379 | unsigned long flags; | |
380 | ||
381 | spin_lock_irqsave(&spdif->lock, flags); | |
382 | ||
383 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS); | |
384 | if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) { | |
385 | if (spdif->multi_freq) { | |
386 | i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >> | |
387 | IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1; | |
388 | uc->value.integer.value[0] = spdif->multi_freqs[i]; | |
389 | } else { | |
390 | uc->value.integer.value[0] = spdif->single_freq; | |
391 | } | |
392 | } else { | |
393 | uc->value.integer.value[0] = 0; | |
394 | } | |
395 | ||
396 | spin_unlock_irqrestore(&spdif->lock, flags); | |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
401 | static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol, | |
402 | struct snd_ctl_elem_info *uinfo) | |
403 | { | |
404 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
405 | uinfo->count = 1; | |
406 | uinfo->value.integer.min = 0; | |
407 | uinfo->value.integer.max = 255; | |
408 | ||
409 | return 0; | |
410 | } | |
411 | ||
412 | static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol, | |
413 | struct snd_ctl_elem_value *ucontrol) | |
414 | { | |
415 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
416 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
417 | ||
418 | ucontrol->value.integer.value[0] = spdif->trk; | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
423 | static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol, | |
424 | struct snd_ctl_elem_value *ucontrol) | |
425 | { | |
426 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
427 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
428 | unsigned long flags; | |
429 | int i; | |
430 | u32 reg; | |
431 | ||
432 | spin_lock_irqsave(&spdif->lock, flags); | |
433 | ||
434 | if (spdif->active) { | |
435 | spin_unlock_irqrestore(&spdif->lock, flags); | |
436 | return -EBUSY; | |
437 | } | |
438 | ||
439 | spdif->trk = ucontrol->value.integer.value[0]; | |
440 | ||
441 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); | |
442 | reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK; | |
443 | reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT; | |
444 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); | |
445 | ||
446 | for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { | |
447 | spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] & | |
448 | ~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) | | |
449 | (spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT); | |
450 | ||
451 | img_spdif_in_aclkgen_writel(spdif, i); | |
452 | } | |
453 | ||
454 | spin_unlock_irqrestore(&spdif->lock, flags); | |
455 | ||
456 | return 0; | |
457 | } | |
458 | ||
459 | static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol, | |
460 | struct snd_ctl_elem_info *uinfo) | |
461 | { | |
462 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
463 | uinfo->count = 1; | |
464 | uinfo->value.integer.min = -128; | |
465 | uinfo->value.integer.max = 127; | |
466 | ||
467 | return 0; | |
468 | } | |
469 | ||
470 | static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol, | |
471 | struct snd_ctl_elem_value *ucontrol) | |
472 | { | |
473 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
474 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
475 | ||
476 | ucontrol->value.integer.value[0] = spdif->lock_acquire; | |
477 | ||
478 | return 0; | |
479 | } | |
480 | ||
481 | static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol, | |
482 | struct snd_ctl_elem_value *ucontrol) | |
483 | { | |
484 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
485 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
486 | unsigned long flags; | |
487 | u32 reg; | |
488 | ||
489 | spin_lock_irqsave(&spdif->lock, flags); | |
490 | ||
491 | if (spdif->active) { | |
492 | spin_unlock_irqrestore(&spdif->lock, flags); | |
493 | return -EBUSY; | |
494 | } | |
495 | ||
496 | spdif->lock_acquire = ucontrol->value.integer.value[0]; | |
497 | ||
498 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); | |
499 | reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK; | |
500 | reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) & | |
501 | IMG_SPDIF_IN_CTL_LOCKHI_MASK; | |
502 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); | |
503 | ||
504 | spin_unlock_irqrestore(&spdif->lock, flags); | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
509 | static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol, | |
510 | struct snd_ctl_elem_value *ucontrol) | |
511 | { | |
512 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
513 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
514 | ||
515 | ucontrol->value.integer.value[0] = spdif->lock_release; | |
516 | ||
517 | return 0; | |
518 | } | |
519 | ||
520 | static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol, | |
521 | struct snd_ctl_elem_value *ucontrol) | |
522 | { | |
523 | struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); | |
524 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); | |
525 | unsigned long flags; | |
526 | u32 reg; | |
527 | ||
528 | spin_lock_irqsave(&spdif->lock, flags); | |
529 | ||
530 | if (spdif->active) { | |
531 | spin_unlock_irqrestore(&spdif->lock, flags); | |
532 | return -EBUSY; | |
533 | } | |
534 | ||
535 | spdif->lock_release = ucontrol->value.integer.value[0]; | |
536 | ||
537 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); | |
538 | reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK; | |
539 | reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) & | |
540 | IMG_SPDIF_IN_CTL_LOCKLO_MASK; | |
541 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); | |
542 | ||
543 | spin_unlock_irqrestore(&spdif->lock, flags); | |
544 | ||
545 | return 0; | |
546 | } | |
547 | ||
548 | static struct snd_kcontrol_new img_spdif_in_controls[] = { | |
549 | { | |
550 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | |
551 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
552 | .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), | |
553 | .info = img_spdif_in_iec958_info, | |
554 | .get = img_spdif_in_get_status_mask | |
555 | }, | |
556 | { | |
557 | .access = SNDRV_CTL_ELEM_ACCESS_READ | | |
558 | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | |
559 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
560 | .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), | |
561 | .info = img_spdif_in_iec958_info, | |
562 | .get = img_spdif_in_get_status | |
563 | }, | |
564 | { | |
565 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
566 | .name = "SPDIF In Multi Frequency Acquire", | |
567 | .info = img_spdif_in_info_multi_freq, | |
568 | .get = img_spdif_in_get_multi_freq, | |
569 | .put = img_spdif_in_set_multi_freq | |
570 | }, | |
571 | { | |
572 | .access = SNDRV_CTL_ELEM_ACCESS_READ | | |
573 | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | |
574 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
575 | .name = "SPDIF In Lock Frequency", | |
576 | .info = img_spdif_in_info_lock_freq, | |
577 | .get = img_spdif_in_get_lock_freq | |
578 | }, | |
579 | { | |
580 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
581 | .name = "SPDIF In Lock TRK", | |
582 | .info = img_spdif_in_info_trk, | |
583 | .get = img_spdif_in_get_trk, | |
584 | .put = img_spdif_in_set_trk | |
585 | }, | |
586 | { | |
587 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
588 | .name = "SPDIF In Lock Acquire Threshold", | |
589 | .info = img_spdif_in_info_lock, | |
590 | .get = img_spdif_in_get_lock_acquire, | |
591 | .put = img_spdif_in_set_lock_acquire | |
592 | }, | |
593 | { | |
594 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
595 | .name = "SPDIF In Lock Release Threshold", | |
596 | .info = img_spdif_in_info_lock, | |
597 | .get = img_spdif_in_get_lock_release, | |
598 | .put = img_spdif_in_set_lock_release | |
599 | } | |
600 | }; | |
601 | ||
602 | static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, | |
603 | struct snd_soc_dai *dai) | |
604 | { | |
605 | unsigned long flags; | |
606 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); | |
607 | int ret = 0; | |
608 | u32 reg; | |
609 | ||
610 | spin_lock_irqsave(&spdif->lock, flags); | |
611 | ||
612 | switch (cmd) { | |
613 | case SNDRV_PCM_TRIGGER_START: | |
614 | case SNDRV_PCM_TRIGGER_RESUME: | |
615 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
616 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); | |
617 | if (spdif->multi_freq) | |
618 | reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK; | |
619 | else | |
620 | reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT); | |
621 | reg |= IMG_SPDIF_IN_CTL_SRT_MASK; | |
622 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); | |
623 | spdif->active = true; | |
624 | break; | |
625 | case SNDRV_PCM_TRIGGER_STOP: | |
626 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
627 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
628 | reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); | |
629 | reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK; | |
630 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); | |
631 | spdif->active = false; | |
632 | break; | |
633 | default: | |
634 | ret = -EINVAL; | |
635 | } | |
636 | ||
637 | spin_unlock_irqrestore(&spdif->lock, flags); | |
638 | ||
639 | return ret; | |
640 | } | |
641 | ||
642 | static int img_spdif_in_hw_params(struct snd_pcm_substream *substream, | |
643 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | |
644 | { | |
645 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); | |
646 | unsigned int rate, channels; | |
647 | snd_pcm_format_t format; | |
648 | ||
649 | rate = params_rate(params); | |
650 | channels = params_channels(params); | |
651 | format = params_format(params); | |
652 | ||
653 | if (format != SNDRV_PCM_FORMAT_S32_LE) | |
654 | return -EINVAL; | |
655 | ||
656 | if (channels != 2) | |
657 | return -EINVAL; | |
658 | ||
659 | return img_spdif_in_do_clkgen_single(spdif, rate); | |
660 | } | |
661 | ||
662 | static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { | |
663 | .trigger = img_spdif_in_trigger, | |
664 | .hw_params = img_spdif_in_hw_params | |
665 | }; | |
666 | ||
667 | static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) | |
668 | { | |
669 | struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); | |
670 | ||
671 | snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data); | |
672 | ||
673 | snd_soc_add_dai_controls(dai, img_spdif_in_controls, | |
674 | ARRAY_SIZE(img_spdif_in_controls)); | |
675 | ||
676 | return 0; | |
677 | } | |
678 | ||
679 | static struct snd_soc_dai_driver img_spdif_in_dai = { | |
680 | .probe = img_spdif_in_dai_probe, | |
681 | .capture = { | |
682 | .channels_min = 2, | |
683 | .channels_max = 2, | |
684 | .rates = SNDRV_PCM_RATE_8000_192000, | |
685 | .formats = SNDRV_PCM_FMTBIT_S32_LE | |
686 | }, | |
687 | .ops = &img_spdif_in_dai_ops | |
688 | }; | |
689 | ||
690 | static const struct snd_soc_component_driver img_spdif_in_component = { | |
691 | .name = "img-spdif-in" | |
692 | }; | |
693 | ||
694 | static int img_spdif_in_probe(struct platform_device *pdev) | |
695 | { | |
696 | struct img_spdif_in *spdif; | |
697 | struct resource *res; | |
698 | void __iomem *base; | |
699 | int ret; | |
700 | struct reset_control *rst; | |
701 | u32 reg; | |
702 | struct device *dev = &pdev->dev; | |
703 | ||
704 | spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); | |
705 | if (!spdif) | |
706 | return -ENOMEM; | |
707 | ||
708 | platform_set_drvdata(pdev, spdif); | |
709 | ||
710 | spdif->dev = &pdev->dev; | |
711 | ||
712 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
713 | base = devm_ioremap_resource(&pdev->dev, res); | |
714 | if (IS_ERR(base)) | |
715 | return PTR_ERR(base); | |
716 | ||
717 | spdif->base = base; | |
718 | ||
719 | spdif->clk_sys = devm_clk_get(dev, "sys"); | |
720 | if (IS_ERR(spdif->clk_sys)) { | |
721 | if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) | |
722 | dev_err(dev, "Failed to acquire clock 'sys'\n"); | |
723 | return PTR_ERR(spdif->clk_sys); | |
724 | } | |
725 | ||
726 | ret = clk_prepare_enable(spdif->clk_sys); | |
727 | if (ret) | |
728 | return ret; | |
729 | ||
730 | rst = devm_reset_control_get(&pdev->dev, "rst"); | |
731 | if (IS_ERR(rst)) { | |
732 | if (PTR_ERR(rst) == -EPROBE_DEFER) { | |
733 | ret = -EPROBE_DEFER; | |
734 | goto err_clk_disable; | |
735 | } | |
736 | dev_dbg(dev, "No top level reset found\n"); | |
737 | img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK, | |
738 | IMG_SPDIF_IN_SOFT_RESET); | |
739 | img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET); | |
740 | } else { | |
741 | reset_control_assert(rst); | |
742 | reset_control_deassert(rst); | |
743 | } | |
744 | ||
745 | spin_lock_init(&spdif->lock); | |
746 | ||
747 | spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET; | |
748 | spdif->dma_data.addr_width = 4; | |
749 | spdif->dma_data.maxburst = 4; | |
750 | spdif->trk = 0x80; | |
751 | spdif->lock_acquire = 4; | |
752 | spdif->lock_release = -128; | |
753 | ||
754 | reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) & | |
755 | IMG_SPDIF_IN_CTL_LOCKHI_MASK; | |
756 | reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) & | |
757 | IMG_SPDIF_IN_CTL_LOCKLO_MASK; | |
758 | reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) & | |
759 | IMG_SPDIF_IN_CTL_TRK_MASK; | |
760 | img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); | |
761 | ||
762 | ret = devm_snd_soc_register_component(&pdev->dev, | |
763 | &img_spdif_in_component, &img_spdif_in_dai, 1); | |
764 | if (ret) | |
765 | goto err_clk_disable; | |
766 | ||
767 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); | |
768 | if (ret) | |
769 | goto err_clk_disable; | |
770 | ||
771 | return 0; | |
772 | ||
773 | err_clk_disable: | |
774 | clk_disable_unprepare(spdif->clk_sys); | |
775 | ||
776 | return ret; | |
777 | } | |
778 | ||
779 | static int img_spdif_in_dev_remove(struct platform_device *pdev) | |
780 | { | |
781 | struct img_spdif_in *spdif = platform_get_drvdata(pdev); | |
782 | ||
783 | clk_disable_unprepare(spdif->clk_sys); | |
784 | ||
785 | return 0; | |
786 | } | |
787 | ||
788 | static const struct of_device_id img_spdif_in_of_match[] = { | |
789 | { .compatible = "img,spdif-in" }, | |
790 | {} | |
791 | }; | |
792 | MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); | |
793 | ||
794 | static struct platform_driver img_spdif_in_driver = { | |
795 | .driver = { | |
796 | .name = "img-spdif-in", | |
797 | .of_match_table = img_spdif_in_of_match | |
798 | }, | |
799 | .probe = img_spdif_in_probe, | |
800 | .remove = img_spdif_in_dev_remove | |
801 | }; | |
802 | module_platform_driver(img_spdif_in_driver); | |
803 | ||
804 | MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); | |
805 | MODULE_DESCRIPTION("IMG SPDIF Input driver"); | |
806 | MODULE_LICENSE("GPL v2"); |