ASoC: qcom: remove incorrect dependencies
[deliverable/linux.git] / sound / soc / qcom / lpass-platform.c
CommitLineData
c5c8635a
KW
1/*
2 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
3 *
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.
7 *
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.
12 *
13 * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
14 */
15
c5c8635a 16#include <linux/dma-mapping.h>
c5c8635a
KW
17#include <linux/export.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
c5c8635a 20#include <linux/platform_device.h>
c5c8635a
KW
21#include <sound/pcm_params.h>
22#include <linux/regmap.h>
23#include <sound/soc.h>
24#include "lpass-lpaif-ipq806x.h"
25#include "lpass.h"
26
27#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
28#define LPASS_PLATFORM_PERIODS 2
29
30static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
31 .info = SNDRV_PCM_INFO_MMAP |
32 SNDRV_PCM_INFO_MMAP_VALID |
33 SNDRV_PCM_INFO_INTERLEAVED |
34 SNDRV_PCM_INFO_PAUSE |
35 SNDRV_PCM_INFO_RESUME,
36 .formats = SNDRV_PCM_FMTBIT_S16 |
37 SNDRV_PCM_FMTBIT_S24 |
38 SNDRV_PCM_FMTBIT_S32,
39 .rates = SNDRV_PCM_RATE_8000_192000,
40 .rate_min = 8000,
41 .rate_max = 192000,
42 .channels_min = 1,
43 .channels_max = 8,
44 .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE,
45 .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE /
46 LPASS_PLATFORM_PERIODS,
47 .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE /
48 LPASS_PLATFORM_PERIODS,
49 .periods_min = LPASS_PLATFORM_PERIODS,
50 .periods_max = LPASS_PLATFORM_PERIODS,
51 .fifo_size = 0,
52};
53
54static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
55{
56 struct snd_pcm_runtime *runtime = substream->runtime;
57 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
58 int ret;
59
60 snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
61
62 runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
63
64 ret = snd_pcm_hw_constraint_integer(runtime,
65 SNDRV_PCM_HW_PARAM_PERIODS);
66 if (ret < 0) {
67 dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n",
68 __func__, ret);
69 return -EINVAL;
70 }
71
72 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
73
74 return 0;
75}
76
77static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
78 struct snd_pcm_hw_params *params)
79{
80 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
81 struct lpass_data *drvdata =
82 snd_soc_platform_get_drvdata(soc_runtime->platform);
83 snd_pcm_format_t format = params_format(params);
84 unsigned int channels = params_channels(params);
85 unsigned int regval;
86 int bitwidth;
87 int ret;
88
89 bitwidth = snd_pcm_format_width(format);
90 if (bitwidth < 0) {
91 dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
92 __func__, bitwidth);
93 return bitwidth;
94 }
95
96 regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
97 LPAIF_RDMACTL_AUDINTF_MI2S |
98 LPAIF_RDMACTL_FIFOWM_8;
99
100 switch (bitwidth) {
101 case 16:
102 switch (channels) {
103 case 1:
104 case 2:
105 regval |= LPAIF_RDMACTL_WPSCNT_ONE;
106 break;
107 case 4:
108 regval |= LPAIF_RDMACTL_WPSCNT_TWO;
109 break;
110 case 6:
111 regval |= LPAIF_RDMACTL_WPSCNT_THREE;
112 break;
113 case 8:
114 regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
115 break;
116 default:
117 dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
118 __func__, bitwidth, channels);
119 return -EINVAL;
120 }
121 break;
122 case 24:
123 case 32:
124 switch (channels) {
125 case 1:
126 regval |= LPAIF_RDMACTL_WPSCNT_ONE;
127 break;
128 case 2:
129 regval |= LPAIF_RDMACTL_WPSCNT_TWO;
130 break;
131 case 4:
132 regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
133 break;
134 case 6:
135 regval |= LPAIF_RDMACTL_WPSCNT_SIX;
136 break;
137 case 8:
138 regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
139 break;
140 default:
141 dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
142 __func__, bitwidth, channels);
143 return -EINVAL;
144 }
145 break;
146 default:
147 dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
148 __func__, bitwidth, channels);
149 return -EINVAL;
150 }
151
152 ret = regmap_write(drvdata->lpaif_map,
153 LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
154 if (ret) {
155 dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
156 __func__, ret);
157 return ret;
158 }
159
160 return 0;
161}
162
163static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
164{
165 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
166 struct lpass_data *drvdata =
167 snd_soc_platform_get_drvdata(soc_runtime->platform);
168 int ret;
169
170 ret = regmap_write(drvdata->lpaif_map,
171 LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
172 if (ret)
173 dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
174 __func__, ret);
175
176 return ret;
177}
178
179static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
180{
181 struct snd_pcm_runtime *runtime = substream->runtime;
182 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
183 struct lpass_data *drvdata =
184 snd_soc_platform_get_drvdata(soc_runtime->platform);
185 int ret;
186
187 ret = regmap_write(drvdata->lpaif_map,
188 LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
189 runtime->dma_addr);
190 if (ret) {
191 dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
192 __func__, ret);
193 return ret;
194 }
195
196 ret = regmap_write(drvdata->lpaif_map,
197 LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
198 (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
199 if (ret) {
200 dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
201 __func__, ret);
202 return ret;
203 }
204
205 ret = regmap_write(drvdata->lpaif_map,
206 LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
207 (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
208 if (ret) {
209 dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
210 __func__, ret);
211 return ret;
212 }
213
214 ret = regmap_update_bits(drvdata->lpaif_map,
215 LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
216 LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
217 if (ret) {
218 dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
219 __func__, ret);
220 return ret;
221 }
222
223 return 0;
224}
225
226static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
227 int cmd)
228{
229 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
230 struct lpass_data *drvdata =
231 snd_soc_platform_get_drvdata(soc_runtime->platform);
232 int ret;
233
234 switch (cmd) {
235 case SNDRV_PCM_TRIGGER_START:
236 case SNDRV_PCM_TRIGGER_RESUME:
237 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
238 /* clear status before enabling interrupts */
239 ret = regmap_write(drvdata->lpaif_map,
240 LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
241 LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
242 if (ret) {
243 dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
244 __func__, ret);
245 return ret;
246 }
247
248 ret = regmap_update_bits(drvdata->lpaif_map,
249 LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
250 LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
251 LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
252 if (ret) {
253 dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
254 __func__, ret);
255 return ret;
256 }
257
258 ret = regmap_update_bits(drvdata->lpaif_map,
259 LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
260 LPAIF_RDMACTL_ENABLE_MASK,
261 LPAIF_RDMACTL_ENABLE_ON);
262 if (ret) {
263 dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
264 __func__, ret);
265 return ret;
266 }
267 break;
268 case SNDRV_PCM_TRIGGER_STOP:
269 case SNDRV_PCM_TRIGGER_SUSPEND:
270 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
271 ret = regmap_update_bits(drvdata->lpaif_map,
272 LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
273 LPAIF_RDMACTL_ENABLE_MASK,
274 LPAIF_RDMACTL_ENABLE_OFF);
275 if (ret) {
276 dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
277 __func__, ret);
278 return ret;
279 }
280
281 ret = regmap_update_bits(drvdata->lpaif_map,
282 LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
283 LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
284 if (ret) {
285 dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
286 __func__, ret);
287 return ret;
288 }
289 break;
290 }
291
292 return 0;
293}
294
295static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
296 struct snd_pcm_substream *substream)
297{
298 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
299 struct lpass_data *drvdata =
300 snd_soc_platform_get_drvdata(soc_runtime->platform);
301 unsigned int base_addr, curr_addr;
302 int ret;
303
304 ret = regmap_read(drvdata->lpaif_map,
305 LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
306 if (ret) {
307 dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
308 __func__, ret);
309 return ret;
310 }
311
312 ret = regmap_read(drvdata->lpaif_map,
313 LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
314 if (ret) {
315 dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
316 __func__, ret);
317 return ret;
318 }
319
320 return bytes_to_frames(substream->runtime, curr_addr - base_addr);
321}
322
323static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
324 struct vm_area_struct *vma)
325{
326 struct snd_pcm_runtime *runtime = substream->runtime;
327
328 return dma_mmap_coherent(substream->pcm->card->dev, vma,
329 runtime->dma_area, runtime->dma_addr,
330 runtime->dma_bytes);
331}
332
333static struct snd_pcm_ops lpass_platform_pcm_ops = {
334 .open = lpass_platform_pcmops_open,
335 .ioctl = snd_pcm_lib_ioctl,
336 .hw_params = lpass_platform_pcmops_hw_params,
337 .hw_free = lpass_platform_pcmops_hw_free,
338 .prepare = lpass_platform_pcmops_prepare,
339 .trigger = lpass_platform_pcmops_trigger,
340 .pointer = lpass_platform_pcmops_pointer,
341 .mmap = lpass_platform_pcmops_mmap,
342};
343
344static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
345{
346 struct snd_pcm_substream *substream = data;
347 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
348 struct lpass_data *drvdata =
349 snd_soc_platform_get_drvdata(soc_runtime->platform);
350 unsigned int interrupts;
351 irqreturn_t ret = IRQ_NONE;
352 int rv;
353
354 rv = regmap_read(drvdata->lpaif_map,
355 LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
356 if (rv) {
357 dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
358 __func__, rv);
359 return IRQ_NONE;
360 }
361 interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
362
363 if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
364 rv = regmap_write(drvdata->lpaif_map,
365 LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
366 LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
367 if (rv) {
368 dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
369 __func__, rv);
370 return IRQ_NONE;
371 }
372 snd_pcm_period_elapsed(substream);
373 ret = IRQ_HANDLED;
374 }
375
376 if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
377 rv = regmap_write(drvdata->lpaif_map,
378 LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
379 LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
380 if (rv) {
381 dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
382 __func__, rv);
383 return IRQ_NONE;
384 }
385 dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__);
386 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
387 ret = IRQ_HANDLED;
388 }
389
390 if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
391 rv = regmap_write(drvdata->lpaif_map,
392 LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
393 LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
394 if (rv) {
395 dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
396 __func__, rv);
397 return IRQ_NONE;
398 }
399 dev_err(soc_runtime->dev, "%s() bus access error\n", __func__);
400 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
401 ret = IRQ_HANDLED;
402 }
403
404 return ret;
405}
406
407static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
408 struct snd_soc_pcm_runtime *soc_runtime)
409{
410 struct snd_dma_buffer *buf = &substream->dma_buffer;
411 size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
412
413 buf->dev.type = SNDRV_DMA_TYPE_DEV;
414 buf->dev.dev = soc_runtime->dev;
415 buf->private_data = NULL;
416 buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr,
417 GFP_KERNEL);
418 if (!buf->area) {
419 dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n",
420 __func__);
421 return -ENOMEM;
422 }
423 buf->bytes = size;
424
425 return 0;
426}
427
428static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
429 struct snd_soc_pcm_runtime *soc_runtime)
430{
431 struct snd_dma_buffer *buf = &substream->dma_buffer;
432
433 if (buf->area) {
434 dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area,
435 buf->addr);
436 }
437 buf->area = NULL;
438}
439
440static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
441{
442 struct snd_pcm *pcm = soc_runtime->pcm;
443 struct snd_pcm_substream *substream =
444 pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
445 struct lpass_data *drvdata =
446 snd_soc_platform_get_drvdata(soc_runtime->platform);
447 int ret;
448
449 soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
450 soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
451
452 ret = lpass_platform_alloc_buffer(substream, soc_runtime);
453 if (ret)
454 return ret;
455
456 ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
457 lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
458 "lpass-irq-lpaif", substream);
459 if (ret) {
460 dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
461 __func__, ret);
462 goto err_buf;
463 }
464
465 /* ensure audio hardware is disabled */
466 ret = regmap_write(drvdata->lpaif_map,
467 LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
468 if (ret) {
469 dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
470 __func__, ret);
471 return ret;
472 }
473 ret = regmap_write(drvdata->lpaif_map,
474 LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
475 if (ret) {
476 dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
477 __func__, ret);
478 return ret;
479 }
480
481 return 0;
482
483err_buf:
484 lpass_platform_free_buffer(substream, soc_runtime);
485 return ret;
486}
487
488static void lpass_platform_pcm_free(struct snd_pcm *pcm)
489{
490 struct snd_pcm_substream *substream =
491 pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
492 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
493
494 lpass_platform_free_buffer(substream, soc_runtime);
495}
496
497static struct snd_soc_platform_driver lpass_platform_driver = {
498 .pcm_new = lpass_platform_pcm_new,
499 .pcm_free = lpass_platform_pcm_free,
500 .ops = &lpass_platform_pcm_ops,
501};
502
503int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
504{
505 struct lpass_data *drvdata = platform_get_drvdata(pdev);
506
507 drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
508 if (drvdata->lpaif_irq < 0) {
509 dev_err(&pdev->dev, "%s() error getting irq handle: %d\n",
510 __func__, drvdata->lpaif_irq);
511 return -ENODEV;
512 }
513
514 return devm_snd_soc_register_platform(&pdev->dev,
515 &lpass_platform_driver);
516}
517EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
518
519MODULE_DESCRIPTION("QTi LPASS Platform Driver");
520MODULE_LICENSE("GPL v2");
This page took 0.047031 seconds and 5 git commands to generate.