2 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
16 #include <linux/dma-mapping.h>
17 #include <linux/export.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <sound/pcm_params.h>
22 #include <linux/regmap.h>
23 #include <sound/soc.h>
24 #include "lpass-lpaif-reg.h"
27 struct lpass_pcm_data
{
32 #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
33 #define LPASS_PLATFORM_PERIODS 2
35 static const struct snd_pcm_hardware lpass_platform_pcm_hardware
= {
36 .info
= SNDRV_PCM_INFO_MMAP
|
37 SNDRV_PCM_INFO_MMAP_VALID
|
38 SNDRV_PCM_INFO_INTERLEAVED
|
39 SNDRV_PCM_INFO_PAUSE
|
40 SNDRV_PCM_INFO_RESUME
,
41 .formats
= SNDRV_PCM_FMTBIT_S16
|
42 SNDRV_PCM_FMTBIT_S24
|
44 .rates
= SNDRV_PCM_RATE_8000_192000
,
49 .buffer_bytes_max
= LPASS_PLATFORM_BUFFER_SIZE
,
50 .period_bytes_max
= LPASS_PLATFORM_BUFFER_SIZE
/
51 LPASS_PLATFORM_PERIODS
,
52 .period_bytes_min
= LPASS_PLATFORM_BUFFER_SIZE
/
53 LPASS_PLATFORM_PERIODS
,
54 .periods_min
= LPASS_PLATFORM_PERIODS
,
55 .periods_max
= LPASS_PLATFORM_PERIODS
,
59 static int lpass_platform_pcmops_open(struct snd_pcm_substream
*substream
)
61 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
62 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
63 struct snd_soc_dai
*cpu_dai
= soc_runtime
->cpu_dai
;
64 struct lpass_data
*drvdata
=
65 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
66 struct lpass_variant
*v
= drvdata
->variant
;
67 int ret
, dma_ch
, dir
= substream
->stream
;
68 struct lpass_pcm_data
*data
;
70 data
= devm_kzalloc(soc_runtime
->dev
, sizeof(*data
), GFP_KERNEL
);
74 data
->i2s_port
= cpu_dai
->driver
->id
;
75 runtime
->private_data
= data
;
77 if (v
->alloc_dma_channel
)
78 dma_ch
= v
->alloc_dma_channel(drvdata
, dir
);
85 drvdata
->substream
[dma_ch
] = substream
;
87 ret
= regmap_write(drvdata
->lpaif_map
,
88 LPAIF_DMACTL_REG(v
, dma_ch
, dir
), 0);
90 dev_err(soc_runtime
->dev
,
91 "error writing to rdmactl reg: %d\n", ret
);
95 data
->dma_ch
= dma_ch
;
97 snd_soc_set_runtime_hwparams(substream
, &lpass_platform_pcm_hardware
);
99 runtime
->dma_bytes
= lpass_platform_pcm_hardware
.buffer_bytes_max
;
101 ret
= snd_pcm_hw_constraint_integer(runtime
,
102 SNDRV_PCM_HW_PARAM_PERIODS
);
104 dev_err(soc_runtime
->dev
, "setting constraints failed: %d\n",
109 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
114 static int lpass_platform_pcmops_close(struct snd_pcm_substream
*substream
)
116 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
117 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
118 struct lpass_data
*drvdata
=
119 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
120 struct lpass_variant
*v
= drvdata
->variant
;
121 struct lpass_pcm_data
*data
;
123 data
= runtime
->private_data
;
124 drvdata
->substream
[data
->dma_ch
] = NULL
;
125 if (v
->free_dma_channel
)
126 v
->free_dma_channel(drvdata
, data
->dma_ch
);
131 static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream
*substream
,
132 struct snd_pcm_hw_params
*params
)
134 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
135 struct lpass_data
*drvdata
=
136 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
137 struct snd_pcm_runtime
*rt
= substream
->runtime
;
138 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
139 struct lpass_variant
*v
= drvdata
->variant
;
140 snd_pcm_format_t format
= params_format(params
);
141 unsigned int channels
= params_channels(params
);
143 int ch
, dir
= substream
->stream
;
145 int ret
, dma_port
= pcm_data
->i2s_port
+ v
->dmactl_audif_start
;
147 ch
= pcm_data
->dma_ch
;
149 bitwidth
= snd_pcm_format_width(format
);
151 dev_err(soc_runtime
->dev
, "invalid bit width given: %d\n",
156 regval
= LPAIF_DMACTL_BURSTEN_INCR4
|
157 LPAIF_DMACTL_AUDINTF(dma_port
) |
158 LPAIF_DMACTL_FIFOWM_8
;
165 regval
|= LPAIF_DMACTL_WPSCNT_ONE
;
168 regval
|= LPAIF_DMACTL_WPSCNT_TWO
;
171 regval
|= LPAIF_DMACTL_WPSCNT_THREE
;
174 regval
|= LPAIF_DMACTL_WPSCNT_FOUR
;
177 dev_err(soc_runtime
->dev
,
178 "invalid PCM config given: bw=%d, ch=%u\n",
187 regval
|= LPAIF_DMACTL_WPSCNT_ONE
;
190 regval
|= LPAIF_DMACTL_WPSCNT_TWO
;
193 regval
|= LPAIF_DMACTL_WPSCNT_FOUR
;
196 regval
|= LPAIF_DMACTL_WPSCNT_SIX
;
199 regval
|= LPAIF_DMACTL_WPSCNT_EIGHT
;
202 dev_err(soc_runtime
->dev
,
203 "invalid PCM config given: bw=%d, ch=%u\n",
209 dev_err(soc_runtime
->dev
, "invalid PCM config given: bw=%d, ch=%u\n",
214 ret
= regmap_write(drvdata
->lpaif_map
,
215 LPAIF_DMACTL_REG(v
, ch
, dir
), regval
);
217 dev_err(soc_runtime
->dev
, "error writing to rdmactl reg: %d\n",
225 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream
*substream
)
227 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
228 struct lpass_data
*drvdata
=
229 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
230 struct snd_pcm_runtime
*rt
= substream
->runtime
;
231 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
232 struct lpass_variant
*v
= drvdata
->variant
;
236 reg
= LPAIF_DMACTL_REG(v
, pcm_data
->dma_ch
, substream
->stream
);
237 ret
= regmap_write(drvdata
->lpaif_map
, reg
, 0);
239 dev_err(soc_runtime
->dev
, "error writing to rdmactl reg: %d\n",
245 static int lpass_platform_pcmops_prepare(struct snd_pcm_substream
*substream
)
247 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
248 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
249 struct lpass_data
*drvdata
=
250 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
251 struct snd_pcm_runtime
*rt
= substream
->runtime
;
252 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
253 struct lpass_variant
*v
= drvdata
->variant
;
254 int ret
, ch
, dir
= substream
->stream
;
256 ch
= pcm_data
->dma_ch
;
258 ret
= regmap_write(drvdata
->lpaif_map
,
259 LPAIF_DMABASE_REG(v
, ch
, dir
),
262 dev_err(soc_runtime
->dev
, "error writing to rdmabase reg: %d\n",
267 ret
= regmap_write(drvdata
->lpaif_map
,
268 LPAIF_DMABUFF_REG(v
, ch
, dir
),
269 (snd_pcm_lib_buffer_bytes(substream
) >> 2) - 1);
271 dev_err(soc_runtime
->dev
, "error writing to rdmabuff reg: %d\n",
276 ret
= regmap_write(drvdata
->lpaif_map
,
277 LPAIF_DMAPER_REG(v
, ch
, dir
),
278 (snd_pcm_lib_period_bytes(substream
) >> 2) - 1);
280 dev_err(soc_runtime
->dev
, "error writing to rdmaper reg: %d\n",
285 ret
= regmap_update_bits(drvdata
->lpaif_map
,
286 LPAIF_DMACTL_REG(v
, ch
, dir
),
287 LPAIF_DMACTL_ENABLE_MASK
, LPAIF_DMACTL_ENABLE_ON
);
289 dev_err(soc_runtime
->dev
, "error writing to rdmactl reg: %d\n",
297 static int lpass_platform_pcmops_trigger(struct snd_pcm_substream
*substream
,
300 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
301 struct lpass_data
*drvdata
=
302 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
303 struct snd_pcm_runtime
*rt
= substream
->runtime
;
304 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
305 struct lpass_variant
*v
= drvdata
->variant
;
306 int ret
, ch
, dir
= substream
->stream
;
308 ch
= pcm_data
->dma_ch
;
311 case SNDRV_PCM_TRIGGER_START
:
312 case SNDRV_PCM_TRIGGER_RESUME
:
313 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
314 /* clear status before enabling interrupts */
315 ret
= regmap_write(drvdata
->lpaif_map
,
316 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
319 dev_err(soc_runtime
->dev
,
320 "error writing to irqclear reg: %d\n", ret
);
324 ret
= regmap_update_bits(drvdata
->lpaif_map
,
325 LPAIF_IRQEN_REG(v
, LPAIF_IRQ_PORT_HOST
),
329 dev_err(soc_runtime
->dev
,
330 "error writing to irqen reg: %d\n", ret
);
334 ret
= regmap_update_bits(drvdata
->lpaif_map
,
335 LPAIF_DMACTL_REG(v
, ch
, dir
),
336 LPAIF_DMACTL_ENABLE_MASK
,
337 LPAIF_DMACTL_ENABLE_ON
);
339 dev_err(soc_runtime
->dev
,
340 "error writing to rdmactl reg: %d\n", ret
);
344 case SNDRV_PCM_TRIGGER_STOP
:
345 case SNDRV_PCM_TRIGGER_SUSPEND
:
346 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
347 ret
= regmap_update_bits(drvdata
->lpaif_map
,
348 LPAIF_DMACTL_REG(v
, ch
, dir
),
349 LPAIF_DMACTL_ENABLE_MASK
,
350 LPAIF_DMACTL_ENABLE_OFF
);
352 dev_err(soc_runtime
->dev
,
353 "error writing to rdmactl reg: %d\n", ret
);
357 ret
= regmap_update_bits(drvdata
->lpaif_map
,
358 LPAIF_IRQEN_REG(v
, LPAIF_IRQ_PORT_HOST
),
359 LPAIF_IRQ_ALL(ch
), 0);
361 dev_err(soc_runtime
->dev
,
362 "error writing to irqen reg: %d\n", ret
);
371 static snd_pcm_uframes_t
lpass_platform_pcmops_pointer(
372 struct snd_pcm_substream
*substream
)
374 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
375 struct lpass_data
*drvdata
=
376 snd_soc_platform_get_drvdata(soc_runtime
->platform
);
377 struct snd_pcm_runtime
*rt
= substream
->runtime
;
378 struct lpass_pcm_data
*pcm_data
= rt
->private_data
;
379 struct lpass_variant
*v
= drvdata
->variant
;
380 unsigned int base_addr
, curr_addr
;
381 int ret
, ch
, dir
= substream
->stream
;
383 ch
= pcm_data
->dma_ch
;
385 ret
= regmap_read(drvdata
->lpaif_map
,
386 LPAIF_DMABASE_REG(v
, ch
, dir
), &base_addr
);
388 dev_err(soc_runtime
->dev
,
389 "error reading from rdmabase reg: %d\n", ret
);
393 ret
= regmap_read(drvdata
->lpaif_map
,
394 LPAIF_DMACURR_REG(v
, ch
, dir
), &curr_addr
);
396 dev_err(soc_runtime
->dev
,
397 "error reading from rdmacurr reg: %d\n", ret
);
401 return bytes_to_frames(substream
->runtime
, curr_addr
- base_addr
);
404 static int lpass_platform_pcmops_mmap(struct snd_pcm_substream
*substream
,
405 struct vm_area_struct
*vma
)
407 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
409 return dma_mmap_coherent(substream
->pcm
->card
->dev
, vma
,
410 runtime
->dma_area
, runtime
->dma_addr
,
414 static const struct snd_pcm_ops lpass_platform_pcm_ops
= {
415 .open
= lpass_platform_pcmops_open
,
416 .close
= lpass_platform_pcmops_close
,
417 .ioctl
= snd_pcm_lib_ioctl
,
418 .hw_params
= lpass_platform_pcmops_hw_params
,
419 .hw_free
= lpass_platform_pcmops_hw_free
,
420 .prepare
= lpass_platform_pcmops_prepare
,
421 .trigger
= lpass_platform_pcmops_trigger
,
422 .pointer
= lpass_platform_pcmops_pointer
,
423 .mmap
= lpass_platform_pcmops_mmap
,
426 static irqreturn_t
lpass_dma_interrupt_handler(
427 struct snd_pcm_substream
*substream
,
428 struct lpass_data
*drvdata
,
429 int chan
, u32 interrupts
)
431 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
432 struct lpass_variant
*v
= drvdata
->variant
;
433 irqreturn_t ret
= IRQ_NONE
;
436 if (interrupts
& LPAIF_IRQ_PER(chan
)) {
437 rv
= regmap_write(drvdata
->lpaif_map
,
438 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
439 LPAIF_IRQ_PER(chan
));
441 dev_err(soc_runtime
->dev
,
442 "error writing to irqclear reg: %d\n", rv
);
445 snd_pcm_period_elapsed(substream
);
449 if (interrupts
& LPAIF_IRQ_XRUN(chan
)) {
450 rv
= regmap_write(drvdata
->lpaif_map
,
451 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
452 LPAIF_IRQ_XRUN(chan
));
454 dev_err(soc_runtime
->dev
,
455 "error writing to irqclear reg: %d\n", rv
);
458 dev_warn(soc_runtime
->dev
, "xrun warning\n");
459 snd_pcm_stop(substream
, SNDRV_PCM_STATE_XRUN
);
463 if (interrupts
& LPAIF_IRQ_ERR(chan
)) {
464 rv
= regmap_write(drvdata
->lpaif_map
,
465 LPAIF_IRQCLEAR_REG(v
, LPAIF_IRQ_PORT_HOST
),
466 LPAIF_IRQ_ERR(chan
));
468 dev_err(soc_runtime
->dev
,
469 "error writing to irqclear reg: %d\n", rv
);
472 dev_err(soc_runtime
->dev
, "bus access error\n");
473 snd_pcm_stop(substream
, SNDRV_PCM_STATE_DISCONNECTED
);
480 static irqreturn_t
lpass_platform_lpaif_irq(int irq
, void *data
)
482 struct lpass_data
*drvdata
= data
;
483 struct lpass_variant
*v
= drvdata
->variant
;
487 rv
= regmap_read(drvdata
->lpaif_map
,
488 LPAIF_IRQSTAT_REG(v
, LPAIF_IRQ_PORT_HOST
), &irqs
);
490 pr_err("error reading from irqstat reg: %d\n", rv
);
494 /* Handle per channel interrupts */
495 for (chan
= 0; chan
< LPASS_MAX_DMA_CHANNELS
; chan
++) {
496 if (irqs
& LPAIF_IRQ_ALL(chan
) && drvdata
->substream
[chan
]) {
497 rv
= lpass_dma_interrupt_handler(
498 drvdata
->substream
[chan
],
499 drvdata
, chan
, irqs
);
500 if (rv
!= IRQ_HANDLED
)
508 static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime
*soc_runtime
)
510 struct snd_pcm
*pcm
= soc_runtime
->pcm
;
511 struct snd_pcm_substream
*psubstream
, *csubstream
;
513 size_t size
= lpass_platform_pcm_hardware
.buffer_bytes_max
;
515 psubstream
= pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
;
517 ret
= snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV
,
518 soc_runtime
->platform
->dev
,
519 size
, &psubstream
->dma_buffer
);
521 dev_err(soc_runtime
->dev
, "Cannot allocate buffer(s)\n");
526 csubstream
= pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
;
528 ret
= snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV
,
529 soc_runtime
->platform
->dev
,
530 size
, &csubstream
->dma_buffer
);
532 dev_err(soc_runtime
->dev
, "Cannot allocate buffer(s)\n");
534 snd_dma_free_pages(&psubstream
->dma_buffer
);
543 static void lpass_platform_pcm_free(struct snd_pcm
*pcm
)
545 struct snd_pcm_substream
*substream
;
548 for (i
= 0; i
< ARRAY_SIZE(pcm
->streams
); i
++) {
549 substream
= pcm
->streams
[i
].substream
;
551 snd_dma_free_pages(&substream
->dma_buffer
);
552 substream
->dma_buffer
.area
= NULL
;
553 substream
->dma_buffer
.addr
= 0;
558 static const struct snd_soc_platform_driver lpass_platform_driver
= {
559 .pcm_new
= lpass_platform_pcm_new
,
560 .pcm_free
= lpass_platform_pcm_free
,
561 .ops
= &lpass_platform_pcm_ops
,
564 int asoc_qcom_lpass_platform_register(struct platform_device
*pdev
)
566 struct lpass_data
*drvdata
= platform_get_drvdata(pdev
);
567 struct lpass_variant
*v
= drvdata
->variant
;
570 drvdata
->lpaif_irq
= platform_get_irq_byname(pdev
, "lpass-irq-lpaif");
571 if (drvdata
->lpaif_irq
< 0) {
572 dev_err(&pdev
->dev
, "error getting irq handle: %d\n",
577 /* ensure audio hardware is disabled */
578 ret
= regmap_write(drvdata
->lpaif_map
,
579 LPAIF_IRQEN_REG(v
, LPAIF_IRQ_PORT_HOST
), 0);
581 dev_err(&pdev
->dev
, "error writing to irqen reg: %d\n", ret
);
585 ret
= devm_request_irq(&pdev
->dev
, drvdata
->lpaif_irq
,
586 lpass_platform_lpaif_irq
, IRQF_TRIGGER_RISING
,
587 "lpass-irq-lpaif", drvdata
);
589 dev_err(&pdev
->dev
, "irq request failed: %d\n", ret
);
594 return devm_snd_soc_register_platform(&pdev
->dev
,
595 &lpass_platform_driver
);
597 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register
);
599 MODULE_DESCRIPTION("QTi LPASS Platform Driver");
600 MODULE_LICENSE("GPL v2");