1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
5 * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
8 #include <linux/dma-mapping.h>
9 #include <linux/export.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <sound/pcm_params.h>
14 #include <linux/regmap.h>
15 #include <sound/soc.h>
16 #include "lpass-lpaif-reg.h"
19 #define DRV_NAME "lpass-platform"
21 struct lpass_pcm_data
{
26 #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
27 #define LPASS_PLATFORM_PERIODS 2
29 static const struct snd_pcm_hardware lpass_platform_pcm_hardware
= {
30 .info
= SNDRV_PCM_INFO_MMAP
|
31 SNDRV_PCM_INFO_MMAP_VALID
|
32 SNDRV_PCM_INFO_INTERLEAVED
|
33 SNDRV_PCM_INFO_PAUSE
|
34 SNDRV_PCM_INFO_RESUME
,
35 .formats
= SNDRV_PCM_FMTBIT_S16
|
36 SNDRV_PCM_FMTBIT_S24
|
38 .rates
= SNDRV_PCM_RATE_8000_192000
,
43 .buffer_bytes_max
= LPASS_PLATFORM_BUFFER_SIZE
,
44 .period_bytes_max
= LPASS_PLATFORM_BUFFER_SIZE
/
45 LPASS_PLATFORM_PERIODS
,
46 .period_bytes_min
= LPASS_PLATFORM_BUFFER_SIZE
/
47 LPASS_PLATFORM_PERIODS
,
48 .periods_min
= LPASS_PLATFORM_PERIODS
,
49 .periods_max
= LPASS_PLATFORM_PERIODS
,
53 static int lpass_platform_pcmops_open(struct snd_soc_component
*component
,
54 struct snd_pcm_substream
*substream
)
56 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
57 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
58 struct snd_soc_dai
*cpu_dai
= soc_runtime
->cpu_dai
;
59 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
60 struct lpass_variant
*v
= drvdata
->variant
;
61 int ret
, dma_ch
, dir
= substream
->stream
;
62 struct lpass_pcm_data
*data
;
64 data
= devm_kzalloc(soc_runtime
->dev
, sizeof(*data
), GFP_KERNEL
);
68 data
->i2s_port
= cpu_dai
->driver
->id
;
69 runtime
->private_data
= data
;
71 if (v
->alloc_dma_channel
)
72 dma_ch
= v
->alloc_dma_channel(drvdata
, dir
);
79 drvdata
->substream
[dma_ch
] = substream
;
81 ret
= regmap_write(drvdata
->lpaif_map
,
82 LPAIF_DMACTL_REG(v
, dma_ch
, dir
), 0);
84 dev_err(soc_runtime
->dev
,
85 "error writing to rdmactl reg: %d\n", ret
);
89 data
->dma_ch
= dma_ch
;
91 snd_soc_set_runtime_hwparams(substream
, &lpass_platform_pcm_hardware
);
93 runtime
->dma_bytes
= lpass_platform_pcm_hardware
.buffer_bytes_max
;
95 ret
= snd_pcm_hw_constraint_integer(runtime
,
96 SNDRV_PCM_HW_PARAM_PERIODS
);
98 dev_err(soc_runtime
->dev
, "setting constraints failed: %d\n",
103 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
108 static int lpass_platform_pcmops_close(struct snd_soc_component
*component
,
109 struct snd_pcm_substream
*substream
)
111 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
112 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
113 struct lpass_variant
*v
= drvdata
->variant
;
114 struct lpass_pcm_data
*data
;
116 data
= runtime
->private_data
;
117 drvdata
->substream
[data
->dma_ch
] = NULL
;
118 if (v
->free_dma_channel
)
119 v
->free_dma_channel(drvdata
, data
->dma_ch
);
124 static int lpass_platform_pcmops_hw_params(struct snd_soc_component
*component
,
125 struct snd_pcm_substream
*substream
,
126 struct snd_pcm_hw_params
*params
)
128 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
129 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
130 struct snd_pcm_runtime
*rt
= substream
->runtime
;
131 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
132 struct lpass_variant
*v
= drvdata
->variant
;
133 snd_pcm_format_t format
= params_format(params
);
134 unsigned int channels
= params_channels(params
);
136 int ch
, dir
= substream
->stream
;
138 int ret
, dma_port
= pcm_data
->i2s_port
+ v
->dmactl_audif_start
;
140 ch
= pcm_data
->dma_ch
;
142 bitwidth
= snd_pcm_format_width(format
);
144 dev_err(soc_runtime
->dev
, "invalid bit width given: %d\n",
149 regval
= LPAIF_DMACTL_BURSTEN_INCR4
|
150 LPAIF_DMACTL_AUDINTF(dma_port
) |
151 LPAIF_DMACTL_FIFOWM_8
;
158 regval
|= LPAIF_DMACTL_WPSCNT_ONE
;
161 regval
|= LPAIF_DMACTL_WPSCNT_TWO
;
164 regval
|= LPAIF_DMACTL_WPSCNT_THREE
;
167 regval
|= LPAIF_DMACTL_WPSCNT_FOUR
;
170 dev_err(soc_runtime
->dev
,
171 "invalid PCM config given: bw=%d, ch=%u\n",
180 regval
|= LPAIF_DMACTL_WPSCNT_ONE
;
183 regval
|= LPAIF_DMACTL_WPSCNT_TWO
;
186 regval
|= LPAIF_DMACTL_WPSCNT_FOUR
;
189 regval
|= LPAIF_DMACTL_WPSCNT_SIX
;
192 regval
|= LPAIF_DMACTL_WPSCNT_EIGHT
;
195 dev_err(soc_runtime
->dev
,
196 "invalid PCM config given: bw=%d, ch=%u\n",
202 dev_err(soc_runtime
->dev
, "invalid PCM config given: bw=%d, ch=%u\n",
207 ret
= regmap_write(drvdata
->lpaif_map
,
208 LPAIF_DMACTL_REG(v
, ch
, dir
), regval
);
210 dev_err(soc_runtime
->dev
, "error writing to rdmactl reg: %d\n",
218 static int lpass_platform_pcmops_hw_free(struct snd_soc_component
*component
,
219 struct snd_pcm_substream
*substream
)
221 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
222 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
223 struct snd_pcm_runtime
*rt
= substream
->runtime
;
224 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
225 struct lpass_variant
*v
= drvdata
->variant
;
229 reg
= LPAIF_DMACTL_REG(v
, pcm_data
->dma_ch
, substream
->stream
);
230 ret
= regmap_write(drvdata
->lpaif_map
, reg
, 0);
232 dev_err(soc_runtime
->dev
, "error writing to rdmactl reg: %d\n",
238 static int lpass_platform_pcmops_prepare(struct snd_soc_component
*component
,
239 struct snd_pcm_substream
*substream
)
241 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
242 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
243 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
244 struct snd_pcm_runtime
*rt
= substream
->runtime
;
245 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
246 struct lpass_variant
*v
= drvdata
->variant
;
247 int ret
, ch
, dir
= substream
->stream
;
249 ch
= pcm_data
->dma_ch
;
251 ret
= regmap_write(drvdata
->lpaif_map
,
252 LPAIF_DMABASE_REG(v
, ch
, dir
),
255 dev_err(soc_runtime
->dev
, "error writing to rdmabase reg: %d\n",
260 ret
= regmap_write(drvdata
->lpaif_map
,
261 LPAIF_DMABUFF_REG(v
, ch
, dir
),
262 (snd_pcm_lib_buffer_bytes(substream
) >> 2) - 1);
264 dev_err(soc_runtime
->dev
, "error writing to rdmabuff reg: %d\n",
269 ret
= regmap_write(drvdata
->lpaif_map
,
270 LPAIF_DMAPER_REG(v
, ch
, dir
),
271 (snd_pcm_lib_period_bytes(substream
) >> 2) - 1);
273 dev_err(soc_runtime
->dev
, "error writing to rdmaper reg: %d\n",
278 ret
= regmap_update_bits(drvdata
->lpaif_map
,
279 LPAIF_DMACTL_REG(v
, ch
, dir
),
280 LPAIF_DMACTL_ENABLE_MASK
, LPAIF_DMACTL_ENABLE_ON
);
282 dev_err(soc_runtime
->dev
, "error writing to rdmactl reg: %d\n",
290 static int lpass_platform_pcmops_trigger(struct snd_soc_component
*component
,
291 struct snd_pcm_substream
*substream
,
294 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
295 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
296 struct snd_pcm_runtime
*rt
= substream
->runtime
;
297 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
298 struct lpass_variant
*v
= drvdata
->variant
;
299 int ret
, ch
, dir
= substream
->stream
;
301 ch
= pcm_data
->dma_ch
;
304 case SNDRV_PCM_TRIGGER_START
:
305 case SNDRV_PCM_TRIGGER_RESUME
:
306 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
307 /* clear status before enabling interrupts */
308 ret
= regmap_write(drvdata
->lpaif_map
,
309 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
312 dev_err(soc_runtime
->dev
,
313 "error writing to irqclear reg: %d\n", ret
);
317 ret
= regmap_update_bits(drvdata
->lpaif_map
,
318 LPAIF_IRQEN_REG(v
, LPAIF_IRQ_PORT_HOST
),
322 dev_err(soc_runtime
->dev
,
323 "error writing to irqen reg: %d\n", ret
);
327 ret
= regmap_update_bits(drvdata
->lpaif_map
,
328 LPAIF_DMACTL_REG(v
, ch
, dir
),
329 LPAIF_DMACTL_ENABLE_MASK
,
330 LPAIF_DMACTL_ENABLE_ON
);
332 dev_err(soc_runtime
->dev
,
333 "error writing to rdmactl reg: %d\n", ret
);
337 case SNDRV_PCM_TRIGGER_STOP
:
338 case SNDRV_PCM_TRIGGER_SUSPEND
:
339 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
340 ret
= regmap_update_bits(drvdata
->lpaif_map
,
341 LPAIF_DMACTL_REG(v
, ch
, dir
),
342 LPAIF_DMACTL_ENABLE_MASK
,
343 LPAIF_DMACTL_ENABLE_OFF
);
345 dev_err(soc_runtime
->dev
,
346 "error writing to rdmactl reg: %d\n", ret
);
350 ret
= regmap_update_bits(drvdata
->lpaif_map
,
351 LPAIF_IRQEN_REG(v
, LPAIF_IRQ_PORT_HOST
),
352 LPAIF_IRQ_ALL(ch
), 0);
354 dev_err(soc_runtime
->dev
,
355 "error writing to irqen reg: %d\n", ret
);
364 static snd_pcm_uframes_t
lpass_platform_pcmops_pointer(
365 struct snd_soc_component
*component
,
366 struct snd_pcm_substream
*substream
)
368 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
369 struct lpass_data
*drvdata
= snd_soc_component_get_drvdata(component
);
370 struct snd_pcm_runtime
*rt
= substream
->runtime
;
371 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
372 struct lpass_variant
*v
= drvdata
->variant
;
373 unsigned int base_addr
, curr_addr
;
374 int ret
, ch
, dir
= substream
->stream
;
376 ch
= pcm_data
->dma_ch
;
378 ret
= regmap_read(drvdata
->lpaif_map
,
379 LPAIF_DMABASE_REG(v
, ch
, dir
), &base_addr
);
381 dev_err(soc_runtime
->dev
,
382 "error reading from rdmabase reg: %d\n", ret
);
386 ret
= regmap_read(drvdata
->lpaif_map
,
387 LPAIF_DMACURR_REG(v
, ch
, dir
), &curr_addr
);
389 dev_err(soc_runtime
->dev
,
390 "error reading from rdmacurr reg: %d\n", ret
);
394 return bytes_to_frames(substream
->runtime
, curr_addr
- base_addr
);
397 static int lpass_platform_pcmops_mmap(struct snd_soc_component
*component
,
398 struct snd_pcm_substream
*substream
,
399 struct vm_area_struct
*vma
)
401 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
403 return dma_mmap_coherent(substream
->pcm
->card
->dev
, vma
,
404 runtime
->dma_area
, runtime
->dma_addr
,
408 static irqreturn_t
lpass_dma_interrupt_handler(
409 struct snd_pcm_substream
*substream
,
410 struct lpass_data
*drvdata
,
411 int chan
, u32 interrupts
)
413 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
414 struct lpass_variant
*v
= drvdata
->variant
;
415 irqreturn_t ret
= IRQ_NONE
;
418 if (interrupts
& LPAIF_IRQ_PER(chan
)) {
419 rv
= regmap_write(drvdata
->lpaif_map
,
420 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
421 LPAIF_IRQ_PER(chan
));
423 dev_err(soc_runtime
->dev
,
424 "error writing to irqclear reg: %d\n", rv
);
427 snd_pcm_period_elapsed(substream
);
431 if (interrupts
& LPAIF_IRQ_XRUN(chan
)) {
432 rv
= regmap_write(drvdata
->lpaif_map
,
433 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
434 LPAIF_IRQ_XRUN(chan
));
436 dev_err(soc_runtime
->dev
,
437 "error writing to irqclear reg: %d\n", rv
);
440 dev_warn(soc_runtime
->dev
, "xrun warning\n");
441 snd_pcm_stop_xrun(substream
);
445 if (interrupts
& LPAIF_IRQ_ERR(chan
)) {
446 rv
= regmap_write(drvdata
->lpaif_map
,
447 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
448 LPAIF_IRQ_ERR(chan
));
450 dev_err(soc_runtime
->dev
,
451 "error writing to irqclear reg: %d\n", rv
);
454 dev_err(soc_runtime
->dev
, "bus access error\n");
455 snd_pcm_stop(substream
, SNDRV_PCM_STATE_DISCONNECTED
);
462 static irqreturn_t
lpass_platform_lpaif_irq(int irq
, void *data
)
464 struct lpass_data
*drvdata
= data
;
465 struct lpass_variant
*v
= drvdata
->variant
;
469 rv
= regmap_read(drvdata
->lpaif_map
,
470 LPAIF_IRQSTAT_REG(v
, LPAIF_IRQ_PORT_HOST
), &irqs
);
472 pr_err("error reading from irqstat reg: %d\n", rv
);
476 /* Handle per channel interrupts */
477 for (chan
= 0; chan
< LPASS_MAX_DMA_CHANNELS
; chan
++) {
478 if (irqs
& LPAIF_IRQ_ALL(chan
) && drvdata
->substream
[chan
]) {
479 rv
= lpass_dma_interrupt_handler(
480 drvdata
->substream
[chan
],
481 drvdata
, chan
, irqs
);
482 if (rv
!= IRQ_HANDLED
)
490 static int lpass_platform_pcm_new(struct snd_soc_component
*component
,
491 struct snd_soc_pcm_runtime
*soc_runtime
)
493 struct snd_pcm
*pcm
= soc_runtime
->pcm
;
494 struct snd_pcm_substream
*psubstream
, *csubstream
;
496 size_t size
= lpass_platform_pcm_hardware
.buffer_bytes_max
;
498 psubstream
= pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
;
500 ret
= snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV
,
502 size
, &psubstream
->dma_buffer
);
504 dev_err(soc_runtime
->dev
, "Cannot allocate buffer(s)\n");
509 csubstream
= pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
;
511 ret
= snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV
,
513 size
, &csubstream
->dma_buffer
);
515 dev_err(soc_runtime
->dev
, "Cannot allocate buffer(s)\n");
517 snd_dma_free_pages(&psubstream
->dma_buffer
);
526 static void lpass_platform_pcm_free(struct snd_soc_component
*component
,
529 struct snd_pcm_substream
*substream
;
532 for (i
= 0; i
< ARRAY_SIZE(pcm
->streams
); i
++) {
533 substream
= pcm
->streams
[i
].substream
;
535 snd_dma_free_pages(&substream
->dma_buffer
);
536 substream
->dma_buffer
.area
= NULL
;
537 substream
->dma_buffer
.addr
= 0;
542 static const struct snd_soc_component_driver lpass_component_driver
= {
544 .open
= lpass_platform_pcmops_open
,
545 .close
= lpass_platform_pcmops_close
,
546 .hw_params
= lpass_platform_pcmops_hw_params
,
547 .hw_free
= lpass_platform_pcmops_hw_free
,
548 .prepare
= lpass_platform_pcmops_prepare
,
549 .trigger
= lpass_platform_pcmops_trigger
,
550 .pointer
= lpass_platform_pcmops_pointer
,
551 .mmap
= lpass_platform_pcmops_mmap
,
552 .pcm_construct
= lpass_platform_pcm_new
,
553 .pcm_destruct
= lpass_platform_pcm_free
,
557 int asoc_qcom_lpass_platform_register(struct platform_device
*pdev
)
559 struct lpass_data
*drvdata
= platform_get_drvdata(pdev
);
560 struct lpass_variant
*v
= drvdata
->variant
;
563 drvdata
->lpaif_irq
= platform_get_irq_byname(pdev
, "lpass-irq-lpaif");
564 if (drvdata
->lpaif_irq
< 0)
567 /* ensure audio hardware is disabled */
568 ret
= regmap_write(drvdata
->lpaif_map
,
569 LPAIF_IRQEN_REG(v
, LPAIF_IRQ_PORT_HOST
), 0);
571 dev_err(&pdev
->dev
, "error writing to irqen reg: %d\n", ret
);
575 ret
= devm_request_irq(&pdev
->dev
, drvdata
->lpaif_irq
,
576 lpass_platform_lpaif_irq
, IRQF_TRIGGER_RISING
,
577 "lpass-irq-lpaif", drvdata
);
579 dev_err(&pdev
->dev
, "irq request failed: %d\n", ret
);
584 return devm_snd_soc_register_component(&pdev
->dev
,
585 &lpass_component_driver
, NULL
, 0);
587 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register
);
589 MODULE_DESCRIPTION("QTi LPASS Platform Driver");
590 MODULE_LICENSE("GPL v2");