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_pcm_substream *substream)
55 struct snd_pcm_runtime *runtime = substream->runtime;
56 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
57 struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
58 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
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 = kzalloc(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);
81 drvdata->substream[dma_ch] = substream;
83 ret = regmap_write(drvdata->lpaif_map,
84 LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
86 dev_err(soc_runtime->dev,
87 "error writing to rdmactl reg: %d\n", ret);
91 data->dma_ch = dma_ch;
93 snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
95 runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
97 ret = snd_pcm_hw_constraint_integer(runtime,
98 SNDRV_PCM_HW_PARAM_PERIODS);
101 dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
106 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
111 static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
113 struct snd_pcm_runtime *runtime = substream->runtime;
114 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
115 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
116 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
117 struct lpass_variant *v = drvdata->variant;
118 struct lpass_pcm_data *data;
120 data = runtime->private_data;
121 drvdata->substream[data->dma_ch] = NULL;
122 if (v->free_dma_channel)
123 v->free_dma_channel(drvdata, data->dma_ch);
129 static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
130 struct snd_pcm_hw_params *params)
132 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
133 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
134 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
135 struct snd_pcm_runtime *rt = substream->runtime;
136 struct lpass_pcm_data *pcm_data = rt->private_data;
137 struct lpass_variant *v = drvdata->variant;
138 snd_pcm_format_t format = params_format(params);
139 unsigned int channels = params_channels(params);
141 int ch, dir = substream->stream;
143 int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
145 ch = pcm_data->dma_ch;
147 bitwidth = snd_pcm_format_width(format);
149 dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
154 regval = LPAIF_DMACTL_BURSTEN_INCR4 |
155 LPAIF_DMACTL_AUDINTF(dma_port) |
156 LPAIF_DMACTL_FIFOWM_8;
163 regval |= LPAIF_DMACTL_WPSCNT_ONE;
166 regval |= LPAIF_DMACTL_WPSCNT_TWO;
169 regval |= LPAIF_DMACTL_WPSCNT_THREE;
172 regval |= LPAIF_DMACTL_WPSCNT_FOUR;
175 dev_err(soc_runtime->dev,
176 "invalid PCM config given: bw=%d, ch=%u\n",
185 regval |= LPAIF_DMACTL_WPSCNT_ONE;
188 regval |= LPAIF_DMACTL_WPSCNT_TWO;
191 regval |= LPAIF_DMACTL_WPSCNT_FOUR;
194 regval |= LPAIF_DMACTL_WPSCNT_SIX;
197 regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
200 dev_err(soc_runtime->dev,
201 "invalid PCM config given: bw=%d, ch=%u\n",
207 dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
212 ret = regmap_write(drvdata->lpaif_map,
213 LPAIF_DMACTL_REG(v, ch, dir), regval);
215 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
223 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
225 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
226 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
227 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
228 struct snd_pcm_runtime *rt = substream->runtime;
229 struct lpass_pcm_data *pcm_data = rt->private_data;
230 struct lpass_variant *v = drvdata->variant;
234 reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);
235 ret = regmap_write(drvdata->lpaif_map, reg, 0);
237 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
243 static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
245 struct snd_pcm_runtime *runtime = substream->runtime;
246 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
247 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
248 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
249 struct snd_pcm_runtime *rt = substream->runtime;
250 struct lpass_pcm_data *pcm_data = rt->private_data;
251 struct lpass_variant *v = drvdata->variant;
252 int ret, ch, dir = substream->stream;
254 ch = pcm_data->dma_ch;
256 ret = regmap_write(drvdata->lpaif_map,
257 LPAIF_DMABASE_REG(v, ch, dir),
260 dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
265 ret = regmap_write(drvdata->lpaif_map,
266 LPAIF_DMABUFF_REG(v, ch, dir),
267 (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
269 dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
274 ret = regmap_write(drvdata->lpaif_map,
275 LPAIF_DMAPER_REG(v, ch, dir),
276 (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
278 dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
283 ret = regmap_update_bits(drvdata->lpaif_map,
284 LPAIF_DMACTL_REG(v, ch, dir),
285 LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
287 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
295 static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
298 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
299 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
300 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
301 struct snd_pcm_runtime *rt = substream->runtime;
302 struct lpass_pcm_data *pcm_data = rt->private_data;
303 struct lpass_variant *v = drvdata->variant;
304 int ret, ch, dir = substream->stream;
306 ch = pcm_data->dma_ch;
309 case SNDRV_PCM_TRIGGER_START:
310 case SNDRV_PCM_TRIGGER_RESUME:
311 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
312 /* clear status before enabling interrupts */
313 ret = regmap_write(drvdata->lpaif_map,
314 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
317 dev_err(soc_runtime->dev,
318 "error writing to irqclear reg: %d\n", ret);
322 ret = regmap_update_bits(drvdata->lpaif_map,
323 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
327 dev_err(soc_runtime->dev,
328 "error writing to irqen reg: %d\n", ret);
332 ret = regmap_update_bits(drvdata->lpaif_map,
333 LPAIF_DMACTL_REG(v, ch, dir),
334 LPAIF_DMACTL_ENABLE_MASK,
335 LPAIF_DMACTL_ENABLE_ON);
337 dev_err(soc_runtime->dev,
338 "error writing to rdmactl reg: %d\n", ret);
342 case SNDRV_PCM_TRIGGER_STOP:
343 case SNDRV_PCM_TRIGGER_SUSPEND:
344 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
345 ret = regmap_update_bits(drvdata->lpaif_map,
346 LPAIF_DMACTL_REG(v, ch, dir),
347 LPAIF_DMACTL_ENABLE_MASK,
348 LPAIF_DMACTL_ENABLE_OFF);
350 dev_err(soc_runtime->dev,
351 "error writing to rdmactl reg: %d\n", ret);
355 ret = regmap_update_bits(drvdata->lpaif_map,
356 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
357 LPAIF_IRQ_ALL(ch), 0);
359 dev_err(soc_runtime->dev,
360 "error writing to irqen reg: %d\n", ret);
369 static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
370 struct snd_pcm_substream *substream)
372 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
373 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
374 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
375 struct snd_pcm_runtime *rt = substream->runtime;
376 struct lpass_pcm_data *pcm_data = rt->private_data;
377 struct lpass_variant *v = drvdata->variant;
378 unsigned int base_addr, curr_addr;
379 int ret, ch, dir = substream->stream;
381 ch = pcm_data->dma_ch;
383 ret = regmap_read(drvdata->lpaif_map,
384 LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
386 dev_err(soc_runtime->dev,
387 "error reading from rdmabase reg: %d\n", ret);
391 ret = regmap_read(drvdata->lpaif_map,
392 LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
394 dev_err(soc_runtime->dev,
395 "error reading from rdmacurr reg: %d\n", ret);
399 return bytes_to_frames(substream->runtime, curr_addr - base_addr);
402 static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
403 struct vm_area_struct *vma)
405 struct snd_pcm_runtime *runtime = substream->runtime;
407 return dma_mmap_coherent(substream->pcm->card->dev, vma,
408 runtime->dma_area, runtime->dma_addr,
412 static const struct snd_pcm_ops lpass_platform_pcm_ops = {
413 .open = lpass_platform_pcmops_open,
414 .close = lpass_platform_pcmops_close,
415 .ioctl = snd_pcm_lib_ioctl,
416 .hw_params = lpass_platform_pcmops_hw_params,
417 .hw_free = lpass_platform_pcmops_hw_free,
418 .prepare = lpass_platform_pcmops_prepare,
419 .trigger = lpass_platform_pcmops_trigger,
420 .pointer = lpass_platform_pcmops_pointer,
421 .mmap = lpass_platform_pcmops_mmap,
424 static irqreturn_t lpass_dma_interrupt_handler(
425 struct snd_pcm_substream *substream,
426 struct lpass_data *drvdata,
427 int chan, u32 interrupts)
429 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
430 struct lpass_variant *v = drvdata->variant;
431 irqreturn_t ret = IRQ_NONE;
434 if (interrupts & LPAIF_IRQ_PER(chan)) {
435 rv = regmap_write(drvdata->lpaif_map,
436 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
437 LPAIF_IRQ_PER(chan));
439 dev_err(soc_runtime->dev,
440 "error writing to irqclear reg: %d\n", rv);
443 snd_pcm_period_elapsed(substream);
447 if (interrupts & LPAIF_IRQ_XRUN(chan)) {
448 rv = regmap_write(drvdata->lpaif_map,
449 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
450 LPAIF_IRQ_XRUN(chan));
452 dev_err(soc_runtime->dev,
453 "error writing to irqclear reg: %d\n", rv);
456 dev_warn(soc_runtime->dev, "xrun warning\n");
457 snd_pcm_stop_xrun(substream);
461 if (interrupts & LPAIF_IRQ_ERR(chan)) {
462 rv = regmap_write(drvdata->lpaif_map,
463 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
464 LPAIF_IRQ_ERR(chan));
466 dev_err(soc_runtime->dev,
467 "error writing to irqclear reg: %d\n", rv);
470 dev_err(soc_runtime->dev, "bus access error\n");
471 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
478 static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
480 struct lpass_data *drvdata = data;
481 struct lpass_variant *v = drvdata->variant;
485 rv = regmap_read(drvdata->lpaif_map,
486 LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
488 pr_err("error reading from irqstat reg: %d\n", rv);
492 /* Handle per channel interrupts */
493 for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
494 if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
495 rv = lpass_dma_interrupt_handler(
496 drvdata->substream[chan],
497 drvdata, chan, irqs);
498 if (rv != IRQ_HANDLED)
506 static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
508 struct snd_pcm *pcm = soc_runtime->pcm;
509 struct snd_pcm_substream *psubstream, *csubstream;
510 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
512 size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
514 psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
516 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
518 size, &psubstream->dma_buffer);
520 dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
525 csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
527 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
529 size, &csubstream->dma_buffer);
531 dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
533 snd_dma_free_pages(&psubstream->dma_buffer);
542 static void lpass_platform_pcm_free(struct snd_pcm *pcm)
544 struct snd_pcm_substream *substream;
547 for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
548 substream = pcm->streams[i].substream;
550 snd_dma_free_pages(&substream->dma_buffer);
551 substream->dma_buffer.area = NULL;
552 substream->dma_buffer.addr = 0;
557 static const struct snd_soc_component_driver lpass_component_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)
574 /* ensure audio hardware is disabled */
575 ret = regmap_write(drvdata->lpaif_map,
576 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
578 dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
582 ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
583 lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
584 "lpass-irq-lpaif", drvdata);
586 dev_err(&pdev->dev, "irq request failed: %d\n", ret);
591 return devm_snd_soc_register_component(&pdev->dev,
592 &lpass_component_driver, NULL, 0);
594 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
596 MODULE_DESCRIPTION("QTi LPASS Platform Driver");
597 MODULE_LICENSE("GPL v2");