blob: 7cf2316c69a2aba8d011ebe010bc8d4e82fa7261 [file] [log] [blame]
Katsuhiro Suzuki139a3422018-01-19 18:25:31 +09001// SPDX-License-Identifier: GPL-2.0
2//
3// Socionext UniPhier AIO ALSA CPU DAI driver.
4//
5// Copyright (c) 2016-2018 Socionext Inc.
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License
9// as published by the Free Software Foundation; version 2
10// of the License.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, see <http://www.gnu.org/licenses/>.
19
20#include <linux/clk.h>
21#include <linux/errno.h>
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/of.h>
25#include <linux/of_platform.h>
26#include <linux/platform_device.h>
27#include <linux/reset.h>
28#include <sound/core.h>
29#include <sound/pcm.h>
30#include <sound/pcm_params.h>
31#include <sound/soc.h>
32
33#include "aio.h"
34
35static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id)
36{
37 struct device *dev = &chip->pdev->dev;
38
39 if (pll_id < 0 || chip->num_plls <= pll_id) {
40 dev_err(dev, "PLL(%d) is not supported\n", pll_id);
41 return false;
42 }
43
44 return chip->plls[pll_id].enable;
45}
46
47static bool match_spec(const struct uniphier_aio_spec *spec,
48 const char *name, int dir)
49{
50 if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
51 spec->swm.dir != PORT_DIR_OUTPUT) {
52 return false;
53 }
54
55 if (dir == SNDRV_PCM_STREAM_CAPTURE &&
56 spec->swm.dir != PORT_DIR_INPUT) {
57 return false;
58 }
59
60 if (spec->name && strcmp(spec->name, name) == 0)
61 return true;
62
63 if (spec->gname && strcmp(spec->gname, name) == 0)
64 return true;
65
66 return false;
67}
68
69/**
70 * find_spec - find HW specification info by name
71 * @aio: the AIO device pointer
72 * @name: name of device
73 * @direction: the direction of substream, SNDRV_PCM_STREAM_*
74 *
75 * Find hardware specification information from list by device name. This
76 * information is used for telling the difference of SoCs to driver.
77 *
78 * Specification list is array of 'struct uniphier_aio_spec' which is defined
79 * in each drivers (see: aio-i2s.c).
80 *
81 * Return: The pointer of hardware specification of AIO if successful,
82 * otherwise NULL on error.
83 */
84static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
85 const char *name,
86 int direction)
87{
88 const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
89 int i;
90
91 for (i = 0; i < chip_spec->num_specs; i++) {
92 const struct uniphier_aio_spec *spec = &chip_spec->specs[i];
93
94 if (match_spec(spec, name, direction))
95 return spec;
96 }
97
98 return NULL;
99}
100
101/**
102 * find_divider - find clock divider by frequency
103 * @aio: the AIO device pointer
104 * @pll_id: PLL ID, should be AUD_PLL_XX
105 * @freq: required frequency
106 *
107 * Find suitable clock divider by frequency.
108 *
109 * Return: The ID of PLL if successful, otherwise negative error value.
110 */
111static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq)
112{
113 struct uniphier_aio_pll *pll;
114 int mul[] = { 1, 1, 1, 2, };
115 int div[] = { 2, 3, 1, 3, };
116 int i;
117
118 if (!is_valid_pll(aio->chip, pll_id))
119 return -EINVAL;
120
121 pll = &aio->chip->plls[pll_id];
122 for (i = 0; i < ARRAY_SIZE(mul); i++)
123 if (pll->freq * mul[i] / div[i] == freq)
124 return i;
125
126 return -ENOTSUPP;
127}
128
129static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id,
130 unsigned int freq, int dir)
131{
132 struct uniphier_aio *aio = uniphier_priv(dai);
133 struct device *dev = &aio->chip->pdev->dev;
134 bool pll_auto = false;
135 int pll_id, div_id;
136
Katsuhiro Suzuki139a3422018-01-19 18:25:31 +0900137 switch (clk_id) {
138 case AUD_CLK_IO:
139 return -ENOTSUPP;
140 case AUD_CLK_A1:
141 pll_id = AUD_PLL_A1;
142 break;
143 case AUD_CLK_F1:
144 pll_id = AUD_PLL_F1;
145 break;
146 case AUD_CLK_A2:
147 pll_id = AUD_PLL_A2;
148 break;
149 case AUD_CLK_F2:
150 pll_id = AUD_PLL_F2;
151 break;
152 case AUD_CLK_A:
153 pll_id = AUD_PLL_A1;
154 pll_auto = true;
155 break;
156 case AUD_CLK_F:
157 pll_id = AUD_PLL_F1;
158 pll_auto = true;
159 break;
160 case AUD_CLK_APLL:
161 pll_id = AUD_PLL_APLL;
162 break;
163 case AUD_CLK_RX0:
164 pll_id = AUD_PLL_RX0;
165 break;
166 case AUD_CLK_USB0:
167 pll_id = AUD_PLL_USB0;
168 break;
169 case AUD_CLK_HSC0:
170 pll_id = AUD_PLL_HSC0;
171 break;
172 default:
173 dev_err(dev, "Sysclk(%d) is not supported\n", clk_id);
174 return -EINVAL;
175 }
176
177 if (pll_auto) {
178 for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
179 div_id = find_divider(aio, pll_id, freq);
180 if (div_id >= 0) {
181 aio->plldiv = div_id;
182 break;
183 }
184 }
185 if (pll_id == aio->chip->num_plls) {
186 dev_err(dev, "Sysclk frequency is not supported(%d)\n",
187 freq);
188 return -EINVAL;
189 }
190 }
191
192 if (dir == SND_SOC_CLOCK_OUT)
193 aio->pll_out = pll_id;
194 else
195 aio->pll_in = pll_id;
196
197 return 0;
198}
199
200static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id,
201 int source, unsigned int freq_in,
202 unsigned int freq_out)
203{
204 struct uniphier_aio *aio = uniphier_priv(dai);
205 struct device *dev = &aio->chip->pdev->dev;
206 int ret;
207
208 if (!is_valid_pll(aio->chip, pll_id))
209 return -EINVAL;
210 if (!aio->chip->plls[pll_id].enable) {
211 dev_err(dev, "PLL(%d) is not implemented\n", pll_id);
212 return -ENOTSUPP;
213 }
214
215 ret = aio_chip_set_pll(aio->chip, pll_id, freq_out);
216 if (ret < 0)
217 return ret;
218
219 return 0;
220}
221
222static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
223{
224 struct uniphier_aio *aio = uniphier_priv(dai);
225 struct device *dev = &aio->chip->pdev->dev;
226
227 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
228 case SND_SOC_DAIFMT_LEFT_J:
229 case SND_SOC_DAIFMT_RIGHT_J:
230 case SND_SOC_DAIFMT_I2S:
231 aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
232 break;
233 default:
234 dev_err(dev, "Format is not supported(%d)\n",
235 fmt & SND_SOC_DAIFMT_FORMAT_MASK);
236 return -EINVAL;
237 }
238
239 return 0;
240}
241
242static int uniphier_aio_startup(struct snd_pcm_substream *substream,
243 struct snd_soc_dai *dai)
244{
245 struct uniphier_aio *aio = uniphier_priv(dai);
246 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
247 int ret;
248
249 sub->substream = substream;
250 sub->pass_through = 0;
251 sub->use_mmap = true;
252
253 ret = aio_init(sub);
254 if (ret)
255 return ret;
256
257 return 0;
258}
259
260static void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
261 struct snd_soc_dai *dai)
262{
263 struct uniphier_aio *aio = uniphier_priv(dai);
264 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
265
266 sub->substream = NULL;
267}
268
269static int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
270 struct snd_pcm_hw_params *params,
271 struct snd_soc_dai *dai)
272{
273 struct uniphier_aio *aio = uniphier_priv(dai);
274 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
275 struct device *dev = &aio->chip->pdev->dev;
276 int freq, ret;
277
278 switch (params_rate(params)) {
279 case 48000:
280 case 32000:
281 case 24000:
282 freq = 12288000;
283 break;
284 case 44100:
285 case 22050:
286 freq = 11289600;
287 break;
288 default:
289 dev_err(dev, "Rate is not supported(%d)\n",
290 params_rate(params));
291 return -EINVAL;
292 }
293 ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
294 freq, SND_SOC_CLOCK_OUT);
295 if (ret)
296 return ret;
297
298 sub->params = *params;
299 sub->setting = 1;
300
301 aio_port_reset(sub);
302 aio_src_reset(sub);
303
304 return 0;
305}
306
307static int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
308 struct snd_soc_dai *dai)
309{
310 struct uniphier_aio *aio = uniphier_priv(dai);
311 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
312
313 sub->setting = 0;
314
315 return 0;
316}
317
318static int uniphier_aio_prepare(struct snd_pcm_substream *substream,
319 struct snd_soc_dai *dai)
320{
321 struct uniphier_aio *aio = uniphier_priv(dai);
322 struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
323 int ret;
324
325 ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
326 if (ret)
327 return ret;
328 ret = aio_src_set_param(sub, &sub->params);
329 if (ret)
330 return ret;
331 aio_port_set_enable(sub, 1);
332
333 ret = aio_if_set_param(sub, sub->pass_through);
334 if (ret)
335 return ret;
336
337 if (sub->swm->type == PORT_TYPE_CONV) {
338 ret = aio_srcif_set_param(sub);
339 if (ret)
340 return ret;
341 ret = aio_srcch_set_param(sub);
342 if (ret)
343 return ret;
344 aio_srcch_set_enable(sub, 1);
345 }
346
347 return 0;
348}
349
350const struct snd_soc_dai_ops uniphier_aio_i2s_ops = {
351 .set_sysclk = uniphier_aio_set_sysclk,
352 .set_pll = uniphier_aio_set_pll,
353 .set_fmt = uniphier_aio_set_fmt,
354 .startup = uniphier_aio_startup,
355 .shutdown = uniphier_aio_shutdown,
356 .hw_params = uniphier_aio_hw_params,
357 .hw_free = uniphier_aio_hw_free,
358 .prepare = uniphier_aio_prepare,
359};
360EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops);
361
362const struct snd_soc_dai_ops uniphier_aio_spdif_ops = {
363 .set_sysclk = uniphier_aio_set_sysclk,
364 .set_pll = uniphier_aio_set_pll,
365 .startup = uniphier_aio_startup,
366 .shutdown = uniphier_aio_shutdown,
367 .hw_params = uniphier_aio_hw_params,
368 .hw_free = uniphier_aio_hw_free,
369 .prepare = uniphier_aio_prepare,
370};
371EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops);
372
373int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
374{
375 struct uniphier_aio *aio = uniphier_priv(dai);
376 int i;
377
378 for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
379 struct uniphier_aio_sub *sub = &aio->sub[i];
380 const struct uniphier_aio_spec *spec;
381
382 spec = find_spec(aio, dai->name, i);
383 if (!spec)
384 continue;
385
386 sub->swm = &spec->swm;
387 sub->spec = spec;
388 }
389
390 aio_chip_init(aio->chip);
391 aio->chip->active = 1;
392
393 return 0;
394}
395EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe);
396
397int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
398{
399 struct uniphier_aio *aio = uniphier_priv(dai);
400
401 aio->chip->active = 0;
402
403 return 0;
404}
405EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove);
406
407int uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
408{
409 struct uniphier_aio *aio = uniphier_priv(dai);
410
411 reset_control_assert(aio->chip->rst);
412 clk_disable_unprepare(aio->chip->clk);
413
414 return 0;
415}
416EXPORT_SYMBOL_GPL(uniphier_aio_dai_suspend);
417
418int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
419{
420 struct uniphier_aio *aio = uniphier_priv(dai);
421 int ret, i;
422
423 if (!aio->chip->active)
424 return 0;
425
426 ret = clk_prepare_enable(aio->chip->clk);
427 if (ret)
428 return ret;
429
430 ret = reset_control_deassert(aio->chip->rst);
431 if (ret)
432 goto err_out_clock;
433
434 aio_chip_init(aio->chip);
435
436 for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
437 struct uniphier_aio_sub *sub = &aio->sub[i];
438
439 if (!sub->spec || !sub->substream)
440 continue;
441
442 ret = aio_init(sub);
443 if (ret)
444 goto err_out_clock;
445
446 if (!sub->setting)
447 continue;
448
449 aio_port_reset(sub);
450 aio_src_reset(sub);
451 }
452
453 return 0;
454
455err_out_clock:
456 clk_disable_unprepare(aio->chip->clk);
457
458 return ret;
459}
460EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume);
461
462static const struct snd_soc_component_driver uniphier_aio_component = {
463 .name = "uniphier-aio",
464};
465
466int uniphier_aio_probe(struct platform_device *pdev)
467{
468 struct uniphier_aio_chip *chip;
469 struct device *dev = &pdev->dev;
470 int ret, i, j;
471
472 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
473 if (!chip)
474 return -ENOMEM;
475
476 chip->chip_spec = of_device_get_match_data(dev);
477 if (!chip->chip_spec)
478 return -EINVAL;
479
480 chip->clk = devm_clk_get(dev, "aio");
481 if (IS_ERR(chip->clk))
482 return PTR_ERR(chip->clk);
483
484 chip->rst = devm_reset_control_get_shared(dev, "aio");
485 if (IS_ERR(chip->rst))
486 return PTR_ERR(chip->rst);
487
488 chip->num_aios = chip->chip_spec->num_dais;
489 chip->aios = devm_kzalloc(dev,
490 sizeof(struct uniphier_aio) * chip->num_aios,
491 GFP_KERNEL);
492 if (!chip->aios)
493 return -ENOMEM;
494
495 chip->num_plls = chip->chip_spec->num_plls;
496 chip->plls = devm_kzalloc(dev, sizeof(struct uniphier_aio_pll) *
497 chip->num_plls, GFP_KERNEL);
498 if (!chip->plls)
499 return -ENOMEM;
500 memcpy(chip->plls, chip->chip_spec->plls,
501 sizeof(struct uniphier_aio_pll) * chip->num_plls);
502
503 for (i = 0; i < chip->num_aios; i++) {
504 struct uniphier_aio *aio = &chip->aios[i];
505
506 aio->chip = chip;
507 aio->fmt = SND_SOC_DAIFMT_I2S;
508
509 for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
510 struct uniphier_aio_sub *sub = &aio->sub[j];
511
512 sub->aio = aio;
513 spin_lock_init(&sub->lock);
514 }
515 }
516
517 chip->pdev = pdev;
518 platform_set_drvdata(pdev, chip);
519
520 ret = clk_prepare_enable(chip->clk);
521 if (ret)
522 return ret;
523
524 ret = reset_control_deassert(chip->rst);
525 if (ret)
526 goto err_out_clock;
527
528 ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
529 chip->chip_spec->dais,
530 chip->chip_spec->num_dais);
531 if (ret) {
532 dev_err(dev, "Register component failed.\n");
533 goto err_out_reset;
534 }
535
536 ret = uniphier_aiodma_soc_register_platform(pdev);
537 if (ret) {
538 dev_err(dev, "Register platform failed.\n");
539 goto err_out_reset;
540 }
541
542 return 0;
543
544err_out_reset:
545 reset_control_assert(chip->rst);
546
547err_out_clock:
548 clk_disable_unprepare(chip->clk);
549
550 return ret;
551}
552EXPORT_SYMBOL_GPL(uniphier_aio_probe);
553
554int uniphier_aio_remove(struct platform_device *pdev)
555{
556 struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
557
558 reset_control_assert(chip->rst);
559 clk_disable_unprepare(chip->clk);
560
561 return 0;
562}
563EXPORT_SYMBOL_GPL(uniphier_aio_remove);
564
565MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
566MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver.");
567MODULE_LICENSE("GPL v2");