Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/pxa/mmp-sspa.c
26388 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* linux/sound/soc/pxa/mmp-sspa.c
4
* Base on pxa2xx-ssp.c
5
*
6
* Copyright (C) 2011 Marvell International Ltd.
7
*/
8
#include <linux/init.h>
9
#include <linux/module.h>
10
#include <linux/platform_device.h>
11
#include <linux/delay.h>
12
#include <linux/clk.h>
13
#include <linux/slab.h>
14
#include <linux/io.h>
15
#include <linux/dmaengine.h>
16
#include <linux/pm_runtime.h>
17
18
#include <sound/core.h>
19
#include <sound/pcm.h>
20
#include <sound/initval.h>
21
#include <sound/pcm_params.h>
22
#include <sound/soc.h>
23
#include <sound/pxa2xx-lib.h>
24
#include <sound/dmaengine_pcm.h>
25
#include "mmp-sspa.h"
26
27
/*
28
* SSPA audio private data
29
*/
30
struct sspa_priv {
31
void __iomem *tx_base;
32
void __iomem *rx_base;
33
34
struct snd_dmaengine_dai_dma_data playback_dma_data;
35
struct snd_dmaengine_dai_dma_data capture_dma_data;
36
struct clk *clk;
37
struct clk *audio_clk;
38
struct clk *sysclk;
39
40
int running_cnt;
41
u32 sp;
42
u32 ctrl;
43
};
44
45
static void mmp_sspa_tx_enable(struct sspa_priv *sspa)
46
{
47
unsigned int sspa_sp = sspa->sp;
48
49
sspa_sp &= ~SSPA_SP_MSL;
50
sspa_sp |= SSPA_SP_S_EN;
51
sspa_sp |= SSPA_SP_WEN;
52
__raw_writel(sspa_sp, sspa->tx_base + SSPA_SP);
53
}
54
55
static void mmp_sspa_tx_disable(struct sspa_priv *sspa)
56
{
57
unsigned int sspa_sp = sspa->sp;
58
59
sspa_sp &= ~SSPA_SP_MSL;
60
sspa_sp &= ~SSPA_SP_S_EN;
61
sspa_sp |= SSPA_SP_WEN;
62
__raw_writel(sspa_sp, sspa->tx_base + SSPA_SP);
63
}
64
65
static void mmp_sspa_rx_enable(struct sspa_priv *sspa)
66
{
67
unsigned int sspa_sp = sspa->sp;
68
69
sspa_sp |= SSPA_SP_S_EN;
70
sspa_sp |= SSPA_SP_WEN;
71
__raw_writel(sspa_sp, sspa->rx_base + SSPA_SP);
72
}
73
74
static void mmp_sspa_rx_disable(struct sspa_priv *sspa)
75
{
76
unsigned int sspa_sp = sspa->sp;
77
78
sspa_sp &= ~SSPA_SP_S_EN;
79
sspa_sp |= SSPA_SP_WEN;
80
__raw_writel(sspa_sp, sspa->rx_base + SSPA_SP);
81
}
82
83
static int mmp_sspa_startup(struct snd_pcm_substream *substream,
84
struct snd_soc_dai *dai)
85
{
86
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
87
88
clk_prepare_enable(sspa->sysclk);
89
clk_prepare_enable(sspa->clk);
90
91
return 0;
92
}
93
94
static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
95
struct snd_soc_dai *dai)
96
{
97
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
98
99
clk_disable_unprepare(sspa->clk);
100
clk_disable_unprepare(sspa->sysclk);
101
}
102
103
/*
104
* Set the SSP ports SYSCLK.
105
*/
106
static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
107
int clk_id, unsigned int freq, int dir)
108
{
109
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);
110
struct device *dev = cpu_dai->component->dev;
111
int ret = 0;
112
113
if (dev->of_node)
114
return -ENOTSUPP;
115
116
switch (clk_id) {
117
case MMP_SSPA_CLK_AUDIO:
118
ret = clk_set_rate(sspa->audio_clk, freq);
119
if (ret)
120
return ret;
121
break;
122
case MMP_SSPA_CLK_PLL:
123
case MMP_SSPA_CLK_VCXO:
124
/* not support yet */
125
return -EINVAL;
126
default:
127
return -EINVAL;
128
}
129
130
return 0;
131
}
132
133
static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
134
int source, unsigned int freq_in,
135
unsigned int freq_out)
136
{
137
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);
138
struct device *dev = cpu_dai->component->dev;
139
int ret = 0;
140
141
if (dev->of_node)
142
return -ENOTSUPP;
143
144
switch (pll_id) {
145
case MMP_SYSCLK:
146
ret = clk_set_rate(sspa->sysclk, freq_out);
147
if (ret)
148
return ret;
149
break;
150
case MMP_SSPA_CLK:
151
ret = clk_set_rate(sspa->clk, freq_out);
152
if (ret)
153
return ret;
154
break;
155
default:
156
return -ENODEV;
157
}
158
159
return 0;
160
}
161
162
/*
163
* Set up the sspa dai format.
164
*/
165
static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
166
unsigned int fmt)
167
{
168
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);
169
170
/* reset port settings */
171
sspa->sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;
172
sspa->ctrl = 0;
173
174
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
175
case SND_SOC_DAIFMT_BP_FP:
176
sspa->sp |= SSPA_SP_MSL;
177
break;
178
case SND_SOC_DAIFMT_BC_FC:
179
break;
180
default:
181
return -EINVAL;
182
}
183
184
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
185
case SND_SOC_DAIFMT_NB_NF:
186
sspa->sp |= SSPA_SP_FSP;
187
break;
188
default:
189
return -EINVAL;
190
}
191
192
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
193
case SND_SOC_DAIFMT_I2S:
194
sspa->ctrl |= SSPA_CTL_XDATDLY(1);
195
break;
196
default:
197
return -EINVAL;
198
}
199
200
/* Since we are configuring the timings for the format by hand
201
* we have to defer some things until hw_params() where we
202
* know parameters like the sample size.
203
*/
204
return 0;
205
}
206
207
/*
208
* Set the SSPA audio DMA parameters and sample size.
209
* Can be called multiple times by oss emulation.
210
*/
211
static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
212
struct snd_pcm_hw_params *params,
213
struct snd_soc_dai *dai)
214
{
215
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
216
struct device *dev = dai->component->dev;
217
u32 sspa_ctrl = sspa->ctrl;
218
int bits;
219
int bitval;
220
221
switch (params_format(params)) {
222
case SNDRV_PCM_FORMAT_S8:
223
bits = 8;
224
bitval = SSPA_CTL_8_BITS;
225
break;
226
case SNDRV_PCM_FORMAT_S16_LE:
227
bits = 16;
228
bitval = SSPA_CTL_16_BITS;
229
break;
230
case SNDRV_PCM_FORMAT_S24_3LE:
231
bits = 24;
232
bitval = SSPA_CTL_24_BITS;
233
break;
234
case SNDRV_PCM_FORMAT_S32_LE:
235
bits = 32;
236
bitval = SSPA_CTL_32_BITS;
237
break;
238
default:
239
return -EINVAL;
240
}
241
242
sspa_ctrl &= ~SSPA_CTL_XPH;
243
if (dev->of_node || params_channels(params) == 2)
244
sspa_ctrl |= SSPA_CTL_XPH;
245
246
sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
247
sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval);
248
249
sspa_ctrl &= ~SSPA_CTL_XWDLEN2_MASK;
250
sspa_ctrl |= SSPA_CTL_XWDLEN2(bitval);
251
252
sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;
253
sspa_ctrl |= SSPA_CTL_XSSZ1(bitval);
254
255
sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK;
256
sspa_ctrl |= SSPA_CTL_XSSZ2(bitval);
257
258
sspa->sp &= ~SSPA_SP_FWID_MASK;
259
sspa->sp |= SSPA_SP_FWID(bits - 1);
260
261
sspa->sp &= ~SSPA_TXSP_FPER_MASK;
262
sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1);
263
264
if (dev->of_node) {
265
clk_set_rate(sspa->clk, params_rate(params) *
266
params_channels(params) * bits);
267
}
268
269
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
270
__raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL);
271
__raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL);
272
} else {
273
__raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL);
274
__raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL);
275
}
276
277
return 0;
278
}
279
280
static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,
281
struct snd_soc_dai *dai)
282
{
283
struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);
284
int ret = 0;
285
286
switch (cmd) {
287
case SNDRV_PCM_TRIGGER_START:
288
case SNDRV_PCM_TRIGGER_RESUME:
289
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
290
/*
291
* whatever playback or capture, must enable rx.
292
* this is a hw issue, so need check if rx has been
293
* enabled or not; if has been enabled by another
294
* stream, do not enable again.
295
*/
296
if (!sspa->running_cnt)
297
mmp_sspa_rx_enable(sspa);
298
299
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
300
mmp_sspa_tx_enable(sspa);
301
302
sspa->running_cnt++;
303
break;
304
305
case SNDRV_PCM_TRIGGER_STOP:
306
case SNDRV_PCM_TRIGGER_SUSPEND:
307
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
308
sspa->running_cnt--;
309
310
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
311
mmp_sspa_tx_disable(sspa);
312
313
/* have no capture stream, disable rx port */
314
if (!sspa->running_cnt)
315
mmp_sspa_rx_disable(sspa);
316
break;
317
318
default:
319
ret = -EINVAL;
320
}
321
322
return ret;
323
}
324
325
static int mmp_sspa_probe(struct snd_soc_dai *dai)
326
{
327
struct sspa_priv *sspa = dev_get_drvdata(dai->dev);
328
329
snd_soc_dai_init_dma_data(dai,
330
&sspa->playback_dma_data,
331
&sspa->capture_dma_data);
332
333
return 0;
334
}
335
336
#define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000
337
#define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
338
SNDRV_PCM_FMTBIT_S16_LE | \
339
SNDRV_PCM_FMTBIT_S24_3LE | \
340
SNDRV_PCM_FMTBIT_S32_LE)
341
342
static const struct snd_soc_dai_ops mmp_sspa_dai_ops = {
343
.probe = mmp_sspa_probe,
344
.startup = mmp_sspa_startup,
345
.shutdown = mmp_sspa_shutdown,
346
.trigger = mmp_sspa_trigger,
347
.hw_params = mmp_sspa_hw_params,
348
.set_sysclk = mmp_sspa_set_dai_sysclk,
349
.set_pll = mmp_sspa_set_dai_pll,
350
.set_fmt = mmp_sspa_set_dai_fmt,
351
};
352
353
static struct snd_soc_dai_driver mmp_sspa_dai = {
354
.playback = {
355
.channels_min = 1,
356
.channels_max = 128,
357
.rates = MMP_SSPA_RATES,
358
.formats = MMP_SSPA_FORMATS,
359
},
360
.capture = {
361
.channels_min = 1,
362
.channels_max = 2,
363
.rates = MMP_SSPA_RATES,
364
.formats = MMP_SSPA_FORMATS,
365
},
366
.ops = &mmp_sspa_dai_ops,
367
};
368
369
#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
370
SNDRV_PCM_INFO_MMAP_VALID | \
371
SNDRV_PCM_INFO_INTERLEAVED | \
372
SNDRV_PCM_INFO_PAUSE | \
373
SNDRV_PCM_INFO_RESUME | \
374
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
375
376
static const struct snd_pcm_hardware mmp_pcm_hardware[] = {
377
{
378
.info = MMP_PCM_INFO,
379
.period_bytes_min = 1024,
380
.period_bytes_max = 2048,
381
.periods_min = 2,
382
.periods_max = 32,
383
.buffer_bytes_max = 4096,
384
.fifo_size = 32,
385
},
386
{
387
.info = MMP_PCM_INFO,
388
.period_bytes_min = 1024,
389
.period_bytes_max = 2048,
390
.periods_min = 2,
391
.periods_max = 32,
392
.buffer_bytes_max = 4096,
393
.fifo_size = 32,
394
},
395
};
396
397
static const struct snd_dmaengine_pcm_config mmp_pcm_config = {
398
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
399
.pcm_hardware = mmp_pcm_hardware,
400
.prealloc_buffer_size = 4096,
401
};
402
403
static int mmp_pcm_mmap(struct snd_soc_component *component,
404
struct snd_pcm_substream *substream,
405
struct vm_area_struct *vma)
406
{
407
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
408
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
409
return remap_pfn_range(vma, vma->vm_start,
410
substream->dma_buffer.addr >> PAGE_SHIFT,
411
vma->vm_end - vma->vm_start, vma->vm_page_prot);
412
}
413
414
static int mmp_sspa_open(struct snd_soc_component *component,
415
struct snd_pcm_substream *substream)
416
{
417
struct sspa_priv *sspa = snd_soc_component_get_drvdata(component);
418
419
pm_runtime_get_sync(component->dev);
420
421
/* we can only change the settings if the port is not in use */
422
if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) ||
423
(__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) {
424
dev_err(component->dev,
425
"can't change hardware dai format: stream is in use\n");
426
return -EBUSY;
427
}
428
429
__raw_writel(sspa->sp, sspa->tx_base + SSPA_SP);
430
__raw_writel(sspa->sp, sspa->rx_base + SSPA_SP);
431
432
sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);
433
__raw_writel(sspa->sp, sspa->tx_base + SSPA_SP);
434
__raw_writel(sspa->sp, sspa->rx_base + SSPA_SP);
435
436
/*
437
* FIXME: hw issue, for the tx serial port,
438
* can not config the master/slave mode;
439
* so must clean this bit.
440
* The master/slave mode has been set in the
441
* rx port.
442
*/
443
__raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP);
444
445
__raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL);
446
__raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL);
447
448
return 0;
449
}
450
451
static int mmp_sspa_close(struct snd_soc_component *component,
452
struct snd_pcm_substream *substream)
453
{
454
pm_runtime_put_sync(component->dev);
455
return 0;
456
}
457
458
static const struct snd_soc_component_driver mmp_sspa_component = {
459
.name = "mmp-sspa",
460
.mmap = mmp_pcm_mmap,
461
.open = mmp_sspa_open,
462
.close = mmp_sspa_close,
463
.legacy_dai_naming = 1,
464
};
465
466
static int asoc_mmp_sspa_probe(struct platform_device *pdev)
467
{
468
struct sspa_priv *sspa;
469
int ret;
470
471
sspa = devm_kzalloc(&pdev->dev,
472
sizeof(struct sspa_priv), GFP_KERNEL);
473
if (!sspa)
474
return -ENOMEM;
475
476
if (pdev->dev.of_node) {
477
sspa->rx_base = devm_platform_ioremap_resource(pdev, 0);
478
if (IS_ERR(sspa->rx_base))
479
return PTR_ERR(sspa->rx_base);
480
481
sspa->tx_base = devm_platform_ioremap_resource(pdev, 1);
482
if (IS_ERR(sspa->tx_base))
483
return PTR_ERR(sspa->tx_base);
484
485
sspa->clk = devm_clk_get(&pdev->dev, "bitclk");
486
if (IS_ERR(sspa->clk))
487
return PTR_ERR(sspa->clk);
488
489
sspa->audio_clk = devm_clk_get(&pdev->dev, "audio");
490
if (IS_ERR(sspa->audio_clk))
491
return PTR_ERR(sspa->audio_clk);
492
} else {
493
struct resource *res;
494
495
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
496
if (res == NULL)
497
return -ENODEV;
498
499
sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30);
500
if (!sspa->rx_base)
501
return -ENOMEM;
502
503
sspa->tx_base = devm_ioremap(&pdev->dev,
504
res->start + 0x80, 0x30);
505
if (!sspa->tx_base)
506
return -ENOMEM;
507
508
sspa->clk = devm_clk_get(&pdev->dev, NULL);
509
if (IS_ERR(sspa->clk))
510
return PTR_ERR(sspa->clk);
511
512
sspa->audio_clk = clk_get(NULL, "mmp-audio");
513
if (IS_ERR(sspa->audio_clk))
514
return PTR_ERR(sspa->audio_clk);
515
516
sspa->sysclk = clk_get(NULL, "mmp-sysclk");
517
if (IS_ERR(sspa->sysclk)) {
518
clk_put(sspa->audio_clk);
519
return PTR_ERR(sspa->sysclk);
520
}
521
}
522
platform_set_drvdata(pdev, sspa);
523
524
sspa->playback_dma_data.maxburst = 4;
525
sspa->capture_dma_data.maxburst = 4;
526
/* You know, these addresses are actually ignored. */
527
sspa->capture_dma_data.addr = SSPA_D;
528
sspa->playback_dma_data.addr = 0x80 + SSPA_D;
529
530
if (pdev->dev.of_node) {
531
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
532
&mmp_pcm_config, 0);
533
if (ret)
534
return ret;
535
}
536
537
ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component,
538
&mmp_sspa_dai, 1);
539
if (ret)
540
return ret;
541
542
pm_runtime_enable(&pdev->dev);
543
clk_prepare_enable(sspa->audio_clk);
544
545
return 0;
546
}
547
548
static void asoc_mmp_sspa_remove(struct platform_device *pdev)
549
{
550
struct sspa_priv *sspa = platform_get_drvdata(pdev);
551
552
clk_disable_unprepare(sspa->audio_clk);
553
pm_runtime_disable(&pdev->dev);
554
555
if (pdev->dev.of_node)
556
return;
557
558
clk_put(sspa->audio_clk);
559
clk_put(sspa->sysclk);
560
}
561
562
#ifdef CONFIG_OF
563
static const struct of_device_id mmp_sspa_of_match[] = {
564
{ .compatible = "marvell,mmp-sspa" },
565
{},
566
};
567
568
MODULE_DEVICE_TABLE(of, mmp_sspa_of_match);
569
#endif
570
571
static struct platform_driver asoc_mmp_sspa_driver = {
572
.driver = {
573
.name = "mmp-sspa-dai",
574
.of_match_table = of_match_ptr(mmp_sspa_of_match),
575
},
576
.probe = asoc_mmp_sspa_probe,
577
.remove = asoc_mmp_sspa_remove,
578
};
579
580
module_platform_driver(asoc_mmp_sspa_driver);
581
582
MODULE_AUTHOR("Leo Yan <[email protected]>");
583
MODULE_DESCRIPTION("MMP SSPA SoC Interface");
584
MODULE_LICENSE("GPL");
585
MODULE_ALIAS("platform:mmp-sspa-dai");
586
587