Commit | Line | Data |
---|---|---|
139a3420 KS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Socionext UniPhier AIO ALSA CPU DAI driver. | |
4 | // | |
5 | // Copyright (c) 2016-2018 Socionext Inc. | |
139a3420 KS |
6 | |
7 | #include <linux/clk.h> | |
8 | #include <linux/errno.h> | |
9 | #include <linux/kernel.h> | |
7c3c20f2 | 10 | #include <linux/mfd/syscon.h> |
139a3420 KS |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> | |
13 | #include <linux/of_platform.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/reset.h> | |
16 | #include <sound/core.h> | |
17 | #include <sound/pcm.h> | |
18 | #include <sound/pcm_params.h> | |
19 | #include <sound/soc.h> | |
20 | ||
21 | #include "aio.h" | |
22 | ||
23 | static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id) | |
24 | { | |
25 | struct device *dev = &chip->pdev->dev; | |
26 | ||
27 | if (pll_id < 0 || chip->num_plls <= pll_id) { | |
28 | dev_err(dev, "PLL(%d) is not supported\n", pll_id); | |
29 | return false; | |
30 | } | |
31 | ||
32 | return chip->plls[pll_id].enable; | |
33 | } | |
34 | ||
5a748de0 KS |
35 | /** |
36 | * find_volume - find volume supported HW port by HW port number | |
37 | * @chip: the AIO chip pointer | |
38 | * @oport_hw: HW port number, one of AUD_HW_XXXX | |
39 | * | |
40 | * Find AIO device from device list by HW port number. Volume feature is | |
41 | * available only in Output and PCM ports, this limitation comes from HW | |
42 | * specifications. | |
43 | * | |
44 | * Return: The pointer of AIO substream if successful, otherwise NULL on error. | |
45 | */ | |
46 | static struct uniphier_aio_sub *find_volume(struct uniphier_aio_chip *chip, | |
47 | int oport_hw) | |
48 | { | |
49 | int i; | |
50 | ||
51 | for (i = 0; i < chip->num_aios; i++) { | |
52 | struct uniphier_aio_sub *sub = &chip->aios[i].sub[0]; | |
53 | ||
54 | if (!sub->swm) | |
55 | continue; | |
56 | ||
57 | if (sub->swm->oport.hw == oport_hw) | |
58 | return sub; | |
59 | } | |
60 | ||
61 | return NULL; | |
62 | } | |
63 | ||
139a3420 KS |
64 | static bool match_spec(const struct uniphier_aio_spec *spec, |
65 | const char *name, int dir) | |
66 | { | |
67 | if (dir == SNDRV_PCM_STREAM_PLAYBACK && | |
68 | spec->swm.dir != PORT_DIR_OUTPUT) { | |
69 | return false; | |
70 | } | |
71 | ||
72 | if (dir == SNDRV_PCM_STREAM_CAPTURE && | |
73 | spec->swm.dir != PORT_DIR_INPUT) { | |
74 | return false; | |
75 | } | |
76 | ||
77 | if (spec->name && strcmp(spec->name, name) == 0) | |
78 | return true; | |
79 | ||
80 | if (spec->gname && strcmp(spec->gname, name) == 0) | |
81 | return true; | |
82 | ||
83 | return false; | |
84 | } | |
85 | ||
86 | /** | |
87 | * find_spec - find HW specification info by name | |
88 | * @aio: the AIO device pointer | |
89 | * @name: name of device | |
90 | * @direction: the direction of substream, SNDRV_PCM_STREAM_* | |
91 | * | |
92 | * Find hardware specification information from list by device name. This | |
93 | * information is used for telling the difference of SoCs to driver. | |
94 | * | |
95 | * Specification list is array of 'struct uniphier_aio_spec' which is defined | |
96 | * in each drivers (see: aio-i2s.c). | |
97 | * | |
98 | * Return: The pointer of hardware specification of AIO if successful, | |
99 | * otherwise NULL on error. | |
100 | */ | |
101 | static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio, | |
102 | const char *name, | |
103 | int direction) | |
104 | { | |
105 | const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec; | |
106 | int i; | |
107 | ||
108 | for (i = 0; i < chip_spec->num_specs; i++) { | |
109 | const struct uniphier_aio_spec *spec = &chip_spec->specs[i]; | |
110 | ||
111 | if (match_spec(spec, name, direction)) | |
112 | return spec; | |
113 | } | |
114 | ||
115 | return NULL; | |
116 | } | |
117 | ||
118 | /** | |
119 | * find_divider - find clock divider by frequency | |
120 | * @aio: the AIO device pointer | |
121 | * @pll_id: PLL ID, should be AUD_PLL_XX | |
122 | * @freq: required frequency | |
123 | * | |
124 | * Find suitable clock divider by frequency. | |
125 | * | |
126 | * Return: The ID of PLL if successful, otherwise negative error value. | |
127 | */ | |
128 | static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq) | |
129 | { | |
130 | struct uniphier_aio_pll *pll; | |
131 | int mul[] = { 1, 1, 1, 2, }; | |
132 | int div[] = { 2, 3, 1, 3, }; | |
133 | int i; | |
134 | ||
135 | if (!is_valid_pll(aio->chip, pll_id)) | |
136 | return -EINVAL; | |
137 | ||
138 | pll = &aio->chip->plls[pll_id]; | |
139 | for (i = 0; i < ARRAY_SIZE(mul); i++) | |
140 | if (pll->freq * mul[i] / div[i] == freq) | |
141 | return i; | |
142 | ||
143 | return -ENOTSUPP; | |
144 | } | |
145 | ||
146 | static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id, | |
147 | unsigned int freq, int dir) | |
148 | { | |
149 | struct uniphier_aio *aio = uniphier_priv(dai); | |
150 | struct device *dev = &aio->chip->pdev->dev; | |
151 | bool pll_auto = false; | |
152 | int pll_id, div_id; | |
153 | ||
139a3420 KS |
154 | switch (clk_id) { |
155 | case AUD_CLK_IO: | |
156 | return -ENOTSUPP; | |
157 | case AUD_CLK_A1: | |
158 | pll_id = AUD_PLL_A1; | |
159 | break; | |
160 | case AUD_CLK_F1: | |
161 | pll_id = AUD_PLL_F1; | |
162 | break; | |
163 | case AUD_CLK_A2: | |
164 | pll_id = AUD_PLL_A2; | |
165 | break; | |
166 | case AUD_CLK_F2: | |
167 | pll_id = AUD_PLL_F2; | |
168 | break; | |
169 | case AUD_CLK_A: | |
170 | pll_id = AUD_PLL_A1; | |
171 | pll_auto = true; | |
172 | break; | |
173 | case AUD_CLK_F: | |
174 | pll_id = AUD_PLL_F1; | |
175 | pll_auto = true; | |
176 | break; | |
177 | case AUD_CLK_APLL: | |
178 | pll_id = AUD_PLL_APLL; | |
179 | break; | |
180 | case AUD_CLK_RX0: | |
181 | pll_id = AUD_PLL_RX0; | |
182 | break; | |
183 | case AUD_CLK_USB0: | |
184 | pll_id = AUD_PLL_USB0; | |
185 | break; | |
186 | case AUD_CLK_HSC0: | |
187 | pll_id = AUD_PLL_HSC0; | |
188 | break; | |
189 | default: | |
190 | dev_err(dev, "Sysclk(%d) is not supported\n", clk_id); | |
191 | return -EINVAL; | |
192 | } | |
193 | ||
194 | if (pll_auto) { | |
195 | for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) { | |
196 | div_id = find_divider(aio, pll_id, freq); | |
197 | if (div_id >= 0) { | |
198 | aio->plldiv = div_id; | |
199 | break; | |
200 | } | |
201 | } | |
202 | if (pll_id == aio->chip->num_plls) { | |
203 | dev_err(dev, "Sysclk frequency is not supported(%d)\n", | |
204 | freq); | |
205 | return -EINVAL; | |
206 | } | |
207 | } | |
208 | ||
209 | if (dir == SND_SOC_CLOCK_OUT) | |
210 | aio->pll_out = pll_id; | |
211 | else | |
212 | aio->pll_in = pll_id; | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id, | |
218 | int source, unsigned int freq_in, | |
219 | unsigned int freq_out) | |
220 | { | |
221 | struct uniphier_aio *aio = uniphier_priv(dai); | |
222 | struct device *dev = &aio->chip->pdev->dev; | |
223 | int ret; | |
224 | ||
225 | if (!is_valid_pll(aio->chip, pll_id)) | |
226 | return -EINVAL; | |
227 | if (!aio->chip->plls[pll_id].enable) { | |
228 | dev_err(dev, "PLL(%d) is not implemented\n", pll_id); | |
229 | return -ENOTSUPP; | |
230 | } | |
231 | ||
232 | ret = aio_chip_set_pll(aio->chip, pll_id, freq_out); | |
233 | if (ret < 0) | |
234 | return ret; | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |
240 | { | |
241 | struct uniphier_aio *aio = uniphier_priv(dai); | |
242 | struct device *dev = &aio->chip->pdev->dev; | |
243 | ||
244 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
245 | case SND_SOC_DAIFMT_LEFT_J: | |
246 | case SND_SOC_DAIFMT_RIGHT_J: | |
247 | case SND_SOC_DAIFMT_I2S: | |
248 | aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; | |
249 | break; | |
250 | default: | |
251 | dev_err(dev, "Format is not supported(%d)\n", | |
252 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | |
253 | return -EINVAL; | |
254 | } | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | static int uniphier_aio_startup(struct snd_pcm_substream *substream, | |
260 | struct snd_soc_dai *dai) | |
261 | { | |
262 | struct uniphier_aio *aio = uniphier_priv(dai); | |
263 | struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; | |
264 | int ret; | |
265 | ||
266 | sub->substream = substream; | |
267 | sub->pass_through = 0; | |
268 | sub->use_mmap = true; | |
269 | ||
270 | ret = aio_init(sub); | |
271 | if (ret) | |
272 | return ret; | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
277 | static void uniphier_aio_shutdown(struct snd_pcm_substream *substream, | |
278 | struct snd_soc_dai *dai) | |
279 | { | |
280 | struct uniphier_aio *aio = uniphier_priv(dai); | |
281 | struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; | |
282 | ||
283 | sub->substream = NULL; | |
284 | } | |
285 | ||
286 | static int uniphier_aio_hw_params(struct snd_pcm_substream *substream, | |
287 | struct snd_pcm_hw_params *params, | |
288 | struct snd_soc_dai *dai) | |
289 | { | |
290 | struct uniphier_aio *aio = uniphier_priv(dai); | |
291 | struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; | |
292 | struct device *dev = &aio->chip->pdev->dev; | |
293 | int freq, ret; | |
294 | ||
295 | switch (params_rate(params)) { | |
296 | case 48000: | |
297 | case 32000: | |
298 | case 24000: | |
299 | freq = 12288000; | |
300 | break; | |
301 | case 44100: | |
302 | case 22050: | |
303 | freq = 11289600; | |
304 | break; | |
305 | default: | |
306 | dev_err(dev, "Rate is not supported(%d)\n", | |
307 | params_rate(params)); | |
308 | return -EINVAL; | |
309 | } | |
310 | ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A, | |
311 | freq, SND_SOC_CLOCK_OUT); | |
312 | if (ret) | |
313 | return ret; | |
314 | ||
315 | sub->params = *params; | |
316 | sub->setting = 1; | |
317 | ||
318 | aio_port_reset(sub); | |
5a748de0 | 319 | aio_port_set_volume(sub, sub->vol); |
139a3420 KS |
320 | aio_src_reset(sub); |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
325 | static int uniphier_aio_hw_free(struct snd_pcm_substream *substream, | |
326 | struct snd_soc_dai *dai) | |
327 | { | |
328 | struct uniphier_aio *aio = uniphier_priv(dai); | |
329 | struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; | |
330 | ||
331 | sub->setting = 0; | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
336 | static int uniphier_aio_prepare(struct snd_pcm_substream *substream, | |
337 | struct snd_soc_dai *dai) | |
338 | { | |
339 | struct uniphier_aio *aio = uniphier_priv(dai); | |
340 | struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; | |
341 | int ret; | |
342 | ||
343 | ret = aio_port_set_param(sub, sub->pass_through, &sub->params); | |
344 | if (ret) | |
345 | return ret; | |
346 | ret = aio_src_set_param(sub, &sub->params); | |
347 | if (ret) | |
348 | return ret; | |
349 | aio_port_set_enable(sub, 1); | |
350 | ||
351 | ret = aio_if_set_param(sub, sub->pass_through); | |
352 | if (ret) | |
353 | return ret; | |
354 | ||
355 | if (sub->swm->type == PORT_TYPE_CONV) { | |
356 | ret = aio_srcif_set_param(sub); | |
357 | if (ret) | |
358 | return ret; | |
359 | ret = aio_srcch_set_param(sub); | |
360 | if (ret) | |
361 | return ret; | |
362 | aio_srcch_set_enable(sub, 1); | |
363 | } | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | const struct snd_soc_dai_ops uniphier_aio_i2s_ops = { | |
369 | .set_sysclk = uniphier_aio_set_sysclk, | |
370 | .set_pll = uniphier_aio_set_pll, | |
371 | .set_fmt = uniphier_aio_set_fmt, | |
372 | .startup = uniphier_aio_startup, | |
373 | .shutdown = uniphier_aio_shutdown, | |
374 | .hw_params = uniphier_aio_hw_params, | |
375 | .hw_free = uniphier_aio_hw_free, | |
376 | .prepare = uniphier_aio_prepare, | |
377 | }; | |
378 | EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops); | |
379 | ||
380 | const struct snd_soc_dai_ops uniphier_aio_spdif_ops = { | |
381 | .set_sysclk = uniphier_aio_set_sysclk, | |
382 | .set_pll = uniphier_aio_set_pll, | |
383 | .startup = uniphier_aio_startup, | |
384 | .shutdown = uniphier_aio_shutdown, | |
385 | .hw_params = uniphier_aio_hw_params, | |
386 | .hw_free = uniphier_aio_hw_free, | |
387 | .prepare = uniphier_aio_prepare, | |
388 | }; | |
389 | EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops); | |
390 | ||
391 | int uniphier_aio_dai_probe(struct snd_soc_dai *dai) | |
392 | { | |
393 | struct uniphier_aio *aio = uniphier_priv(dai); | |
394 | int i; | |
395 | ||
396 | for (i = 0; i < ARRAY_SIZE(aio->sub); i++) { | |
397 | struct uniphier_aio_sub *sub = &aio->sub[i]; | |
398 | const struct uniphier_aio_spec *spec; | |
399 | ||
400 | spec = find_spec(aio, dai->name, i); | |
401 | if (!spec) | |
402 | continue; | |
403 | ||
404 | sub->swm = &spec->swm; | |
405 | sub->spec = spec; | |
5a748de0 KS |
406 | |
407 | sub->vol = AUD_VOL_INIT; | |
139a3420 KS |
408 | } |
409 | ||
7c3c20f2 | 410 | aio_iecout_set_enable(aio->chip, true); |
139a3420 KS |
411 | aio_chip_init(aio->chip); |
412 | aio->chip->active = 1; | |
413 | ||
414 | return 0; | |
415 | } | |
416 | EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe); | |
417 | ||
418 | int uniphier_aio_dai_remove(struct snd_soc_dai *dai) | |
419 | { | |
420 | struct uniphier_aio *aio = uniphier_priv(dai); | |
421 | ||
422 | aio->chip->active = 0; | |
423 | ||
424 | return 0; | |
425 | } | |
426 | EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove); | |
427 | ||
428 | int uniphier_aio_dai_suspend(struct snd_soc_dai *dai) | |
429 | { | |
430 | struct uniphier_aio *aio = uniphier_priv(dai); | |
431 | ||
432 | reset_control_assert(aio->chip->rst); | |
433 | clk_disable_unprepare(aio->chip->clk); | |
434 | ||
435 | return 0; | |
436 | } | |
437 | EXPORT_SYMBOL_GPL(uniphier_aio_dai_suspend); | |
438 | ||
439 | int uniphier_aio_dai_resume(struct snd_soc_dai *dai) | |
440 | { | |
441 | struct uniphier_aio *aio = uniphier_priv(dai); | |
442 | int ret, i; | |
443 | ||
444 | if (!aio->chip->active) | |
445 | return 0; | |
446 | ||
447 | ret = clk_prepare_enable(aio->chip->clk); | |
448 | if (ret) | |
449 | return ret; | |
450 | ||
451 | ret = reset_control_deassert(aio->chip->rst); | |
452 | if (ret) | |
453 | goto err_out_clock; | |
454 | ||
7c3c20f2 | 455 | aio_iecout_set_enable(aio->chip, true); |
139a3420 KS |
456 | aio_chip_init(aio->chip); |
457 | ||
458 | for (i = 0; i < ARRAY_SIZE(aio->sub); i++) { | |
459 | struct uniphier_aio_sub *sub = &aio->sub[i]; | |
460 | ||
461 | if (!sub->spec || !sub->substream) | |
462 | continue; | |
463 | ||
464 | ret = aio_init(sub); | |
465 | if (ret) | |
466 | goto err_out_clock; | |
467 | ||
468 | if (!sub->setting) | |
469 | continue; | |
470 | ||
471 | aio_port_reset(sub); | |
472 | aio_src_reset(sub); | |
473 | } | |
474 | ||
475 | return 0; | |
476 | ||
477 | err_out_clock: | |
478 | clk_disable_unprepare(aio->chip->clk); | |
479 | ||
480 | return ret; | |
481 | } | |
482 | EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume); | |
483 | ||
5a748de0 KS |
484 | static int uniphier_aio_vol_info(struct snd_kcontrol *kcontrol, |
485 | struct snd_ctl_elem_info *uinfo) | |
486 | { | |
487 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
488 | uinfo->count = 1; | |
489 | uinfo->value.integer.min = 0; | |
490 | uinfo->value.integer.max = AUD_VOL_MAX; | |
491 | ||
492 | return 0; | |
493 | } | |
494 | ||
495 | static int uniphier_aio_vol_get(struct snd_kcontrol *kcontrol, | |
496 | struct snd_ctl_elem_value *ucontrol) | |
497 | { | |
498 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); | |
499 | struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp); | |
500 | struct uniphier_aio_sub *sub; | |
501 | int oport_hw = kcontrol->private_value; | |
502 | ||
503 | sub = find_volume(chip, oport_hw); | |
504 | if (!sub) | |
505 | return 0; | |
506 | ||
507 | ucontrol->value.integer.value[0] = sub->vol; | |
508 | ||
509 | return 0; | |
510 | } | |
511 | ||
512 | static int uniphier_aio_vol_put(struct snd_kcontrol *kcontrol, | |
513 | struct snd_ctl_elem_value *ucontrol) | |
514 | { | |
515 | struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); | |
516 | struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp); | |
517 | struct uniphier_aio_sub *sub; | |
518 | int oport_hw = kcontrol->private_value; | |
519 | ||
520 | sub = find_volume(chip, oport_hw); | |
521 | if (!sub) | |
522 | return 0; | |
523 | ||
524 | if (sub->vol == ucontrol->value.integer.value[0]) | |
525 | return 0; | |
526 | sub->vol = ucontrol->value.integer.value[0]; | |
527 | ||
528 | aio_port_set_volume(sub, sub->vol); | |
529 | ||
530 | return 0; | |
531 | } | |
532 | ||
533 | static const struct snd_kcontrol_new uniphier_aio_controls[] = { | |
534 | { | |
535 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
536 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
537 | .name = "HPCMOUT1 Volume", | |
538 | .info = uniphier_aio_vol_info, | |
539 | .get = uniphier_aio_vol_get, | |
540 | .put = uniphier_aio_vol_put, | |
541 | .private_value = AUD_HW_HPCMOUT1, | |
542 | }, | |
543 | { | |
544 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
545 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
546 | .name = "PCMOUT1 Volume", | |
547 | .info = uniphier_aio_vol_info, | |
548 | .get = uniphier_aio_vol_get, | |
549 | .put = uniphier_aio_vol_put, | |
550 | .private_value = AUD_HW_PCMOUT1, | |
551 | }, | |
552 | { | |
553 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
554 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
555 | .name = "PCMOUT2 Volume", | |
556 | .info = uniphier_aio_vol_info, | |
557 | .get = uniphier_aio_vol_get, | |
558 | .put = uniphier_aio_vol_put, | |
559 | .private_value = AUD_HW_PCMOUT2, | |
560 | }, | |
561 | { | |
562 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
563 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
564 | .name = "PCMOUT3 Volume", | |
565 | .info = uniphier_aio_vol_info, | |
566 | .get = uniphier_aio_vol_get, | |
567 | .put = uniphier_aio_vol_put, | |
568 | .private_value = AUD_HW_PCMOUT3, | |
569 | }, | |
570 | { | |
571 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
572 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
573 | .name = "HIECOUT1 Volume", | |
574 | .info = uniphier_aio_vol_info, | |
575 | .get = uniphier_aio_vol_get, | |
576 | .put = uniphier_aio_vol_put, | |
577 | .private_value = AUD_HW_HIECOUT1, | |
578 | }, | |
579 | { | |
580 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
581 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
582 | .name = "IECOUT1 Volume", | |
583 | .info = uniphier_aio_vol_info, | |
584 | .get = uniphier_aio_vol_get, | |
585 | .put = uniphier_aio_vol_put, | |
586 | .private_value = AUD_HW_IECOUT1, | |
587 | }, | |
588 | }; | |
589 | ||
139a3420 KS |
590 | static const struct snd_soc_component_driver uniphier_aio_component = { |
591 | .name = "uniphier-aio", | |
5a748de0 KS |
592 | .controls = uniphier_aio_controls, |
593 | .num_controls = ARRAY_SIZE(uniphier_aio_controls), | |
139a3420 KS |
594 | }; |
595 | ||
596 | int uniphier_aio_probe(struct platform_device *pdev) | |
597 | { | |
598 | struct uniphier_aio_chip *chip; | |
599 | struct device *dev = &pdev->dev; | |
600 | int ret, i, j; | |
601 | ||
602 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | |
603 | if (!chip) | |
604 | return -ENOMEM; | |
605 | ||
606 | chip->chip_spec = of_device_get_match_data(dev); | |
607 | if (!chip->chip_spec) | |
608 | return -EINVAL; | |
609 | ||
7c3c20f2 KS |
610 | chip->regmap_sg = syscon_regmap_lookup_by_phandle(dev->of_node, |
611 | "socionext,syscon"); | |
612 | if (IS_ERR(chip->regmap_sg)) { | |
613 | if (PTR_ERR(chip->regmap_sg) == -EPROBE_DEFER) | |
614 | return -EPROBE_DEFER; | |
615 | chip->regmap_sg = NULL; | |
616 | } | |
617 | ||
139a3420 KS |
618 | chip->clk = devm_clk_get(dev, "aio"); |
619 | if (IS_ERR(chip->clk)) | |
620 | return PTR_ERR(chip->clk); | |
621 | ||
622 | chip->rst = devm_reset_control_get_shared(dev, "aio"); | |
623 | if (IS_ERR(chip->rst)) | |
624 | return PTR_ERR(chip->rst); | |
625 | ||
626 | chip->num_aios = chip->chip_spec->num_dais; | |
a86854d0 KC |
627 | chip->aios = devm_kcalloc(dev, |
628 | chip->num_aios, sizeof(struct uniphier_aio), | |
139a3420 KS |
629 | GFP_KERNEL); |
630 | if (!chip->aios) | |
631 | return -ENOMEM; | |
632 | ||
633 | chip->num_plls = chip->chip_spec->num_plls; | |
a86854d0 KC |
634 | chip->plls = devm_kcalloc(dev, |
635 | chip->num_plls, | |
636 | sizeof(struct uniphier_aio_pll), | |
637 | GFP_KERNEL); | |
139a3420 KS |
638 | if (!chip->plls) |
639 | return -ENOMEM; | |
640 | memcpy(chip->plls, chip->chip_spec->plls, | |
641 | sizeof(struct uniphier_aio_pll) * chip->num_plls); | |
642 | ||
643 | for (i = 0; i < chip->num_aios; i++) { | |
644 | struct uniphier_aio *aio = &chip->aios[i]; | |
645 | ||
646 | aio->chip = chip; | |
647 | aio->fmt = SND_SOC_DAIFMT_I2S; | |
648 | ||
649 | for (j = 0; j < ARRAY_SIZE(aio->sub); j++) { | |
650 | struct uniphier_aio_sub *sub = &aio->sub[j]; | |
651 | ||
652 | sub->aio = aio; | |
653 | spin_lock_init(&sub->lock); | |
654 | } | |
655 | } | |
656 | ||
657 | chip->pdev = pdev; | |
658 | platform_set_drvdata(pdev, chip); | |
659 | ||
660 | ret = clk_prepare_enable(chip->clk); | |
661 | if (ret) | |
662 | return ret; | |
663 | ||
664 | ret = reset_control_deassert(chip->rst); | |
665 | if (ret) | |
666 | goto err_out_clock; | |
667 | ||
668 | ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, | |
669 | chip->chip_spec->dais, | |
670 | chip->chip_spec->num_dais); | |
671 | if (ret) { | |
672 | dev_err(dev, "Register component failed.\n"); | |
673 | goto err_out_reset; | |
674 | } | |
675 | ||
676 | ret = uniphier_aiodma_soc_register_platform(pdev); | |
677 | if (ret) { | |
678 | dev_err(dev, "Register platform failed.\n"); | |
679 | goto err_out_reset; | |
680 | } | |
681 | ||
682 | return 0; | |
683 | ||
684 | err_out_reset: | |
685 | reset_control_assert(chip->rst); | |
686 | ||
687 | err_out_clock: | |
688 | clk_disable_unprepare(chip->clk); | |
689 | ||
690 | return ret; | |
691 | } | |
692 | EXPORT_SYMBOL_GPL(uniphier_aio_probe); | |
693 | ||
694 | int uniphier_aio_remove(struct platform_device *pdev) | |
695 | { | |
696 | struct uniphier_aio_chip *chip = platform_get_drvdata(pdev); | |
697 | ||
698 | reset_control_assert(chip->rst); | |
699 | clk_disable_unprepare(chip->clk); | |
700 | ||
701 | return 0; | |
702 | } | |
703 | EXPORT_SYMBOL_GPL(uniphier_aio_remove); | |
704 | ||
705 | MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); | |
706 | MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver."); | |
707 | MODULE_LICENSE("GPL v2"); |