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
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"
25 #include "lpass.h"
26
27 #define DRV_NAME "lpass-platform"
28
29 struct lpass_pcm_data {
30 int dma_ch;
31 int i2s_port;
32 };
33
34 #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
35 #define LPASS_PLATFORM_PERIODS 2
36
37 static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
38 .info = SNDRV_PCM_INFO_MMAP |
39 SNDRV_PCM_INFO_MMAP_VALID |
40 SNDRV_PCM_INFO_INTERLEAVED |
41 SNDRV_PCM_INFO_PAUSE |
42 SNDRV_PCM_INFO_RESUME,
43 .formats = SNDRV_PCM_FMTBIT_S16 |
44 SNDRV_PCM_FMTBIT_S24 |
45 SNDRV_PCM_FMTBIT_S32,
46 .rates = SNDRV_PCM_RATE_8000_192000,
47 .rate_min = 8000,
48 .rate_max = 192000,
49 .channels_min = 1,
50 .channels_max = 8,
51 .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE,
52 .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE /
53 LPASS_PLATFORM_PERIODS,
54 .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE /
55 LPASS_PLATFORM_PERIODS,
56 .periods_min = LPASS_PLATFORM_PERIODS,
57 .periods_max = LPASS_PLATFORM_PERIODS,
58 .fifo_size = 0,
59 };
60
lpass_platform_pcmops_open(struct snd_pcm_substream * substream)61 static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
62 {
63 struct snd_pcm_runtime *runtime = substream->runtime;
64 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
65 struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
66 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
67 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
68 struct lpass_variant *v = drvdata->variant;
69 int ret, dma_ch, dir = substream->stream;
70 struct lpass_pcm_data *data;
71
72 data = kzalloc(sizeof(*data), GFP_KERNEL);
73 if (!data)
74 return -ENOMEM;
75
76 data->i2s_port = cpu_dai->driver->id;
77 runtime->private_data = data;
78
79 if (v->alloc_dma_channel)
80 dma_ch = v->alloc_dma_channel(drvdata, dir);
81 else
82 dma_ch = 0;
83
84 if (dma_ch < 0)
85 return dma_ch;
86
87 drvdata->substream[dma_ch] = substream;
88
89 ret = regmap_write(drvdata->lpaif_map,
90 LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
91 if (ret) {
92 dev_err(soc_runtime->dev,
93 "error writing to rdmactl reg: %d\n", ret);
94 return ret;
95 }
96
97 data->dma_ch = dma_ch;
98
99 snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
100
101 runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
102
103 ret = snd_pcm_hw_constraint_integer(runtime,
104 SNDRV_PCM_HW_PARAM_PERIODS);
105 if (ret < 0) {
106 dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
107 ret);
108 return -EINVAL;
109 }
110
111 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
112
113 return 0;
114 }
115
lpass_platform_pcmops_close(struct snd_pcm_substream * substream)116 static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
117 {
118 struct snd_pcm_runtime *runtime = substream->runtime;
119 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
120 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
121 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
122 struct lpass_variant *v = drvdata->variant;
123 struct lpass_pcm_data *data;
124
125 data = runtime->private_data;
126 drvdata->substream[data->dma_ch] = NULL;
127 if (v->free_dma_channel)
128 v->free_dma_channel(drvdata, data->dma_ch);
129
130 kfree(data);
131 return 0;
132 }
133
lpass_platform_pcmops_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)134 static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
135 struct snd_pcm_hw_params *params)
136 {
137 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
138 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
139 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
140 struct snd_pcm_runtime *rt = substream->runtime;
141 struct lpass_pcm_data *pcm_data = rt->private_data;
142 struct lpass_variant *v = drvdata->variant;
143 snd_pcm_format_t format = params_format(params);
144 unsigned int channels = params_channels(params);
145 unsigned int regval;
146 int ch, dir = substream->stream;
147 int bitwidth;
148 int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
149
150 ch = pcm_data->dma_ch;
151
152 bitwidth = snd_pcm_format_width(format);
153 if (bitwidth < 0) {
154 dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
155 bitwidth);
156 return bitwidth;
157 }
158
159 regval = LPAIF_DMACTL_BURSTEN_INCR4 |
160 LPAIF_DMACTL_AUDINTF(dma_port) |
161 LPAIF_DMACTL_FIFOWM_8;
162
163 switch (bitwidth) {
164 case 16:
165 switch (channels) {
166 case 1:
167 case 2:
168 regval |= LPAIF_DMACTL_WPSCNT_ONE;
169 break;
170 case 4:
171 regval |= LPAIF_DMACTL_WPSCNT_TWO;
172 break;
173 case 6:
174 regval |= LPAIF_DMACTL_WPSCNT_THREE;
175 break;
176 case 8:
177 regval |= LPAIF_DMACTL_WPSCNT_FOUR;
178 break;
179 default:
180 dev_err(soc_runtime->dev,
181 "invalid PCM config given: bw=%d, ch=%u\n",
182 bitwidth, channels);
183 return -EINVAL;
184 }
185 break;
186 case 24:
187 case 32:
188 switch (channels) {
189 case 1:
190 regval |= LPAIF_DMACTL_WPSCNT_ONE;
191 break;
192 case 2:
193 regval |= LPAIF_DMACTL_WPSCNT_TWO;
194 break;
195 case 4:
196 regval |= LPAIF_DMACTL_WPSCNT_FOUR;
197 break;
198 case 6:
199 regval |= LPAIF_DMACTL_WPSCNT_SIX;
200 break;
201 case 8:
202 regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
203 break;
204 default:
205 dev_err(soc_runtime->dev,
206 "invalid PCM config given: bw=%d, ch=%u\n",
207 bitwidth, channels);
208 return -EINVAL;
209 }
210 break;
211 default:
212 dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
213 bitwidth, channels);
214 return -EINVAL;
215 }
216
217 ret = regmap_write(drvdata->lpaif_map,
218 LPAIF_DMACTL_REG(v, ch, dir), regval);
219 if (ret) {
220 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
221 ret);
222 return ret;
223 }
224
225 return 0;
226 }
227
lpass_platform_pcmops_hw_free(struct snd_pcm_substream * substream)228 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
229 {
230 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
231 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
232 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
233 struct snd_pcm_runtime *rt = substream->runtime;
234 struct lpass_pcm_data *pcm_data = rt->private_data;
235 struct lpass_variant *v = drvdata->variant;
236 unsigned int reg;
237 int ret;
238
239 reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);
240 ret = regmap_write(drvdata->lpaif_map, reg, 0);
241 if (ret)
242 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
243 ret);
244
245 return ret;
246 }
247
lpass_platform_pcmops_prepare(struct snd_pcm_substream * substream)248 static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
249 {
250 struct snd_pcm_runtime *runtime = substream->runtime;
251 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
252 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
253 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
254 struct snd_pcm_runtime *rt = substream->runtime;
255 struct lpass_pcm_data *pcm_data = rt->private_data;
256 struct lpass_variant *v = drvdata->variant;
257 int ret, ch, dir = substream->stream;
258
259 ch = pcm_data->dma_ch;
260
261 ret = regmap_write(drvdata->lpaif_map,
262 LPAIF_DMABASE_REG(v, ch, dir),
263 runtime->dma_addr);
264 if (ret) {
265 dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
266 ret);
267 return ret;
268 }
269
270 ret = regmap_write(drvdata->lpaif_map,
271 LPAIF_DMABUFF_REG(v, ch, dir),
272 (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
273 if (ret) {
274 dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
275 ret);
276 return ret;
277 }
278
279 ret = regmap_write(drvdata->lpaif_map,
280 LPAIF_DMAPER_REG(v, ch, dir),
281 (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
282 if (ret) {
283 dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
284 ret);
285 return ret;
286 }
287
288 ret = regmap_update_bits(drvdata->lpaif_map,
289 LPAIF_DMACTL_REG(v, ch, dir),
290 LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
291 if (ret) {
292 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
293 ret);
294 return ret;
295 }
296
297 return 0;
298 }
299
lpass_platform_pcmops_trigger(struct snd_pcm_substream * substream,int cmd)300 static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
301 int cmd)
302 {
303 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
304 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
305 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
306 struct snd_pcm_runtime *rt = substream->runtime;
307 struct lpass_pcm_data *pcm_data = rt->private_data;
308 struct lpass_variant *v = drvdata->variant;
309 int ret, ch, dir = substream->stream;
310
311 ch = pcm_data->dma_ch;
312
313 switch (cmd) {
314 case SNDRV_PCM_TRIGGER_START:
315 case SNDRV_PCM_TRIGGER_RESUME:
316 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
317 /* clear status before enabling interrupts */
318 ret = regmap_write(drvdata->lpaif_map,
319 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
320 LPAIF_IRQ_ALL(ch));
321 if (ret) {
322 dev_err(soc_runtime->dev,
323 "error writing to irqclear reg: %d\n", ret);
324 return ret;
325 }
326
327 ret = regmap_update_bits(drvdata->lpaif_map,
328 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
329 LPAIF_IRQ_ALL(ch),
330 LPAIF_IRQ_ALL(ch));
331 if (ret) {
332 dev_err(soc_runtime->dev,
333 "error writing to irqen reg: %d\n", ret);
334 return ret;
335 }
336
337 ret = regmap_update_bits(drvdata->lpaif_map,
338 LPAIF_DMACTL_REG(v, ch, dir),
339 LPAIF_DMACTL_ENABLE_MASK,
340 LPAIF_DMACTL_ENABLE_ON);
341 if (ret) {
342 dev_err(soc_runtime->dev,
343 "error writing to rdmactl reg: %d\n", ret);
344 return ret;
345 }
346 break;
347 case SNDRV_PCM_TRIGGER_STOP:
348 case SNDRV_PCM_TRIGGER_SUSPEND:
349 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
350 ret = regmap_update_bits(drvdata->lpaif_map,
351 LPAIF_DMACTL_REG(v, ch, dir),
352 LPAIF_DMACTL_ENABLE_MASK,
353 LPAIF_DMACTL_ENABLE_OFF);
354 if (ret) {
355 dev_err(soc_runtime->dev,
356 "error writing to rdmactl reg: %d\n", ret);
357 return ret;
358 }
359
360 ret = regmap_update_bits(drvdata->lpaif_map,
361 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
362 LPAIF_IRQ_ALL(ch), 0);
363 if (ret) {
364 dev_err(soc_runtime->dev,
365 "error writing to irqen reg: %d\n", ret);
366 return ret;
367 }
368 break;
369 }
370
371 return 0;
372 }
373
lpass_platform_pcmops_pointer(struct snd_pcm_substream * substream)374 static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
375 struct snd_pcm_substream *substream)
376 {
377 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
378 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
379 struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
380 struct snd_pcm_runtime *rt = substream->runtime;
381 struct lpass_pcm_data *pcm_data = rt->private_data;
382 struct lpass_variant *v = drvdata->variant;
383 unsigned int base_addr, curr_addr;
384 int ret, ch, dir = substream->stream;
385
386 ch = pcm_data->dma_ch;
387
388 ret = regmap_read(drvdata->lpaif_map,
389 LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
390 if (ret) {
391 dev_err(soc_runtime->dev,
392 "error reading from rdmabase reg: %d\n", ret);
393 return ret;
394 }
395
396 ret = regmap_read(drvdata->lpaif_map,
397 LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
398 if (ret) {
399 dev_err(soc_runtime->dev,
400 "error reading from rdmacurr reg: %d\n", ret);
401 return ret;
402 }
403
404 return bytes_to_frames(substream->runtime, curr_addr - base_addr);
405 }
406
lpass_platform_pcmops_mmap(struct snd_pcm_substream * substream,struct vm_area_struct * vma)407 static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
408 struct vm_area_struct *vma)
409 {
410 struct snd_pcm_runtime *runtime = substream->runtime;
411
412 return dma_mmap_coherent(substream->pcm->card->dev, vma,
413 runtime->dma_area, runtime->dma_addr,
414 runtime->dma_bytes);
415 }
416
417 static const struct snd_pcm_ops lpass_platform_pcm_ops = {
418 .open = lpass_platform_pcmops_open,
419 .close = lpass_platform_pcmops_close,
420 .ioctl = snd_pcm_lib_ioctl,
421 .hw_params = lpass_platform_pcmops_hw_params,
422 .hw_free = lpass_platform_pcmops_hw_free,
423 .prepare = lpass_platform_pcmops_prepare,
424 .trigger = lpass_platform_pcmops_trigger,
425 .pointer = lpass_platform_pcmops_pointer,
426 .mmap = lpass_platform_pcmops_mmap,
427 };
428
lpass_dma_interrupt_handler(struct snd_pcm_substream * substream,struct lpass_data * drvdata,int chan,u32 interrupts)429 static irqreturn_t lpass_dma_interrupt_handler(
430 struct snd_pcm_substream *substream,
431 struct lpass_data *drvdata,
432 int chan, u32 interrupts)
433 {
434 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
435 struct lpass_variant *v = drvdata->variant;
436 irqreturn_t ret = IRQ_NONE;
437 int rv;
438
439 if (interrupts & LPAIF_IRQ_PER(chan)) {
440 rv = regmap_write(drvdata->lpaif_map,
441 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
442 LPAIF_IRQ_PER(chan));
443 if (rv) {
444 dev_err(soc_runtime->dev,
445 "error writing to irqclear reg: %d\n", rv);
446 return IRQ_NONE;
447 }
448 snd_pcm_period_elapsed(substream);
449 ret = IRQ_HANDLED;
450 }
451
452 if (interrupts & LPAIF_IRQ_XRUN(chan)) {
453 rv = regmap_write(drvdata->lpaif_map,
454 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
455 LPAIF_IRQ_XRUN(chan));
456 if (rv) {
457 dev_err(soc_runtime->dev,
458 "error writing to irqclear reg: %d\n", rv);
459 return IRQ_NONE;
460 }
461 dev_warn(soc_runtime->dev, "xrun warning\n");
462 snd_pcm_stop_xrun(substream);
463 ret = IRQ_HANDLED;
464 }
465
466 if (interrupts & LPAIF_IRQ_ERR(chan)) {
467 rv = regmap_write(drvdata->lpaif_map,
468 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
469 LPAIF_IRQ_ERR(chan));
470 if (rv) {
471 dev_err(soc_runtime->dev,
472 "error writing to irqclear reg: %d\n", rv);
473 return IRQ_NONE;
474 }
475 dev_err(soc_runtime->dev, "bus access error\n");
476 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
477 ret = IRQ_HANDLED;
478 }
479
480 return ret;
481 }
482
lpass_platform_lpaif_irq(int irq,void * data)483 static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
484 {
485 struct lpass_data *drvdata = data;
486 struct lpass_variant *v = drvdata->variant;
487 unsigned int irqs;
488 int rv, chan;
489
490 rv = regmap_read(drvdata->lpaif_map,
491 LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
492 if (rv) {
493 pr_err("error reading from irqstat reg: %d\n", rv);
494 return IRQ_NONE;
495 }
496
497 /* Handle per channel interrupts */
498 for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
499 if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
500 rv = lpass_dma_interrupt_handler(
501 drvdata->substream[chan],
502 drvdata, chan, irqs);
503 if (rv != IRQ_HANDLED)
504 return rv;
505 }
506 }
507
508 return IRQ_HANDLED;
509 }
510
lpass_platform_pcm_new(struct snd_soc_pcm_runtime * soc_runtime)511 static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
512 {
513 struct snd_pcm *pcm = soc_runtime->pcm;
514 struct snd_pcm_substream *psubstream, *csubstream;
515 struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
516 int ret = -EINVAL;
517 size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
518
519 psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
520 if (psubstream) {
521 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
522 component->dev,
523 size, &psubstream->dma_buffer);
524 if (ret) {
525 dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
526 return ret;
527 }
528 }
529
530 csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
531 if (csubstream) {
532 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
533 component->dev,
534 size, &csubstream->dma_buffer);
535 if (ret) {
536 dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
537 if (psubstream)
538 snd_dma_free_pages(&psubstream->dma_buffer);
539 return ret;
540 }
541
542 }
543
544 return 0;
545 }
546
lpass_platform_pcm_free(struct snd_pcm * pcm)547 static void lpass_platform_pcm_free(struct snd_pcm *pcm)
548 {
549 struct snd_pcm_substream *substream;
550 int i;
551
552 for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
553 substream = pcm->streams[i].substream;
554 if (substream) {
555 snd_dma_free_pages(&substream->dma_buffer);
556 substream->dma_buffer.area = NULL;
557 substream->dma_buffer.addr = 0;
558 }
559 }
560 }
561
562 static const struct snd_soc_component_driver lpass_component_driver = {
563 .name = DRV_NAME,
564 .pcm_new = lpass_platform_pcm_new,
565 .pcm_free = lpass_platform_pcm_free,
566 .ops = &lpass_platform_pcm_ops,
567 };
568
asoc_qcom_lpass_platform_register(struct platform_device * pdev)569 int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
570 {
571 struct lpass_data *drvdata = platform_get_drvdata(pdev);
572 struct lpass_variant *v = drvdata->variant;
573 int ret;
574
575 drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
576 if (drvdata->lpaif_irq < 0) {
577 dev_err(&pdev->dev, "error getting irq handle: %d\n",
578 drvdata->lpaif_irq);
579 return -ENODEV;
580 }
581
582 /* ensure audio hardware is disabled */
583 ret = regmap_write(drvdata->lpaif_map,
584 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
585 if (ret) {
586 dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
587 return ret;
588 }
589
590 ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
591 lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
592 "lpass-irq-lpaif", drvdata);
593 if (ret) {
594 dev_err(&pdev->dev, "irq request failed: %d\n", ret);
595 return ret;
596 }
597
598
599 return devm_snd_soc_register_component(&pdev->dev,
600 &lpass_component_driver, NULL, 0);
601 }
602 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
603
604 MODULE_DESCRIPTION("QTi LPASS Platform Driver");
605 MODULE_LICENSE("GPL v2");
606