blob: b1981d84ac18c5bef69fa333bfb9e0f1785cea64 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-only
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/*
3 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
4 *
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00005 * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
6 */
7
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"
17#include "lpass.h"
18
19#define DRV_NAME "lpass-platform"
20
21struct lpass_pcm_data {
22 int dma_ch;
23 int i2s_port;
24};
25
26#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
27#define LPASS_PLATFORM_PERIODS 2
28
29static 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 |
37 SNDRV_PCM_FMTBIT_S32,
38 .rates = SNDRV_PCM_RATE_8000_192000,
39 .rate_min = 8000,
40 .rate_max = 192000,
41 .channels_min = 1,
42 .channels_max = 8,
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,
50 .fifo_size = 0,
51};
52
53static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
54{
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;
63
Olivier Deprez0e641232021-09-23 10:07:05 +020064 data = kzalloc(sizeof(*data), GFP_KERNEL);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000065 if (!data)
66 return -ENOMEM;
67
68 data->i2s_port = cpu_dai->driver->id;
69 runtime->private_data = data;
70
71 if (v->alloc_dma_channel)
72 dma_ch = v->alloc_dma_channel(drvdata, dir);
73 else
74 dma_ch = 0;
75
Olivier Deprez0e641232021-09-23 10:07:05 +020076 if (dma_ch < 0) {
77 kfree(data);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000078 return dma_ch;
Olivier Deprez0e641232021-09-23 10:07:05 +020079 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000080
81 drvdata->substream[dma_ch] = substream;
82
83 ret = regmap_write(drvdata->lpaif_map,
84 LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
85 if (ret) {
86 dev_err(soc_runtime->dev,
87 "error writing to rdmactl reg: %d\n", ret);
David Brazdil0f672f62019-12-10 10:32:29 +000088 return ret;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000089 }
90
91 data->dma_ch = dma_ch;
92
93 snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
94
95 runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
96
97 ret = snd_pcm_hw_constraint_integer(runtime,
98 SNDRV_PCM_HW_PARAM_PERIODS);
99 if (ret < 0) {
Olivier Deprez0e641232021-09-23 10:07:05 +0200100 kfree(data);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000101 dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
102 ret);
103 return -EINVAL;
104 }
105
106 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
107
108 return 0;
109}
110
111static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
112{
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;
119
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);
124
Olivier Deprez0e641232021-09-23 10:07:05 +0200125 kfree(data);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000126 return 0;
127}
128
129static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
130 struct snd_pcm_hw_params *params)
131{
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);
140 unsigned int regval;
141 int ch, dir = substream->stream;
142 int bitwidth;
143 int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
144
145 ch = pcm_data->dma_ch;
146
147 bitwidth = snd_pcm_format_width(format);
148 if (bitwidth < 0) {
149 dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
150 bitwidth);
151 return bitwidth;
152 }
153
154 regval = LPAIF_DMACTL_BURSTEN_INCR4 |
155 LPAIF_DMACTL_AUDINTF(dma_port) |
156 LPAIF_DMACTL_FIFOWM_8;
157
158 switch (bitwidth) {
159 case 16:
160 switch (channels) {
161 case 1:
162 case 2:
163 regval |= LPAIF_DMACTL_WPSCNT_ONE;
164 break;
165 case 4:
166 regval |= LPAIF_DMACTL_WPSCNT_TWO;
167 break;
168 case 6:
169 regval |= LPAIF_DMACTL_WPSCNT_THREE;
170 break;
171 case 8:
172 regval |= LPAIF_DMACTL_WPSCNT_FOUR;
173 break;
174 default:
175 dev_err(soc_runtime->dev,
176 "invalid PCM config given: bw=%d, ch=%u\n",
177 bitwidth, channels);
178 return -EINVAL;
179 }
180 break;
181 case 24:
182 case 32:
183 switch (channels) {
184 case 1:
185 regval |= LPAIF_DMACTL_WPSCNT_ONE;
186 break;
187 case 2:
188 regval |= LPAIF_DMACTL_WPSCNT_TWO;
189 break;
190 case 4:
191 regval |= LPAIF_DMACTL_WPSCNT_FOUR;
192 break;
193 case 6:
194 regval |= LPAIF_DMACTL_WPSCNT_SIX;
195 break;
196 case 8:
197 regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
198 break;
199 default:
200 dev_err(soc_runtime->dev,
201 "invalid PCM config given: bw=%d, ch=%u\n",
202 bitwidth, channels);
203 return -EINVAL;
204 }
205 break;
206 default:
207 dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
208 bitwidth, channels);
209 return -EINVAL;
210 }
211
212 ret = regmap_write(drvdata->lpaif_map,
213 LPAIF_DMACTL_REG(v, ch, dir), regval);
214 if (ret) {
215 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
216 ret);
217 return ret;
218 }
219
220 return 0;
221}
222
223static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
224{
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;
231 unsigned int reg;
232 int ret;
233
234 reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);
235 ret = regmap_write(drvdata->lpaif_map, reg, 0);
236 if (ret)
237 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
238 ret);
239
240 return ret;
241}
242
243static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
244{
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;
253
254 ch = pcm_data->dma_ch;
255
256 ret = regmap_write(drvdata->lpaif_map,
257 LPAIF_DMABASE_REG(v, ch, dir),
258 runtime->dma_addr);
259 if (ret) {
260 dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
261 ret);
262 return ret;
263 }
264
265 ret = regmap_write(drvdata->lpaif_map,
266 LPAIF_DMABUFF_REG(v, ch, dir),
267 (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
268 if (ret) {
269 dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
270 ret);
271 return ret;
272 }
273
274 ret = regmap_write(drvdata->lpaif_map,
275 LPAIF_DMAPER_REG(v, ch, dir),
276 (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
277 if (ret) {
278 dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
279 ret);
280 return ret;
281 }
282
283 ret = regmap_update_bits(drvdata->lpaif_map,
284 LPAIF_DMACTL_REG(v, ch, dir),
285 LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
286 if (ret) {
287 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
288 ret);
289 return ret;
290 }
291
292 return 0;
293}
294
295static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
296 int cmd)
297{
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;
305
306 ch = pcm_data->dma_ch;
307
308 switch (cmd) {
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),
315 LPAIF_IRQ_ALL(ch));
316 if (ret) {
317 dev_err(soc_runtime->dev,
318 "error writing to irqclear reg: %d\n", ret);
319 return ret;
320 }
321
322 ret = regmap_update_bits(drvdata->lpaif_map,
323 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
324 LPAIF_IRQ_ALL(ch),
325 LPAIF_IRQ_ALL(ch));
326 if (ret) {
327 dev_err(soc_runtime->dev,
328 "error writing to irqen reg: %d\n", ret);
329 return ret;
330 }
331
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);
336 if (ret) {
337 dev_err(soc_runtime->dev,
338 "error writing to rdmactl reg: %d\n", ret);
339 return ret;
340 }
341 break;
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);
349 if (ret) {
350 dev_err(soc_runtime->dev,
351 "error writing to rdmactl reg: %d\n", ret);
352 return ret;
353 }
354
355 ret = regmap_update_bits(drvdata->lpaif_map,
356 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
357 LPAIF_IRQ_ALL(ch), 0);
358 if (ret) {
359 dev_err(soc_runtime->dev,
360 "error writing to irqen reg: %d\n", ret);
361 return ret;
362 }
363 break;
364 }
365
366 return 0;
367}
368
369static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
370 struct snd_pcm_substream *substream)
371{
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;
380
381 ch = pcm_data->dma_ch;
382
383 ret = regmap_read(drvdata->lpaif_map,
384 LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
385 if (ret) {
386 dev_err(soc_runtime->dev,
387 "error reading from rdmabase reg: %d\n", ret);
388 return ret;
389 }
390
391 ret = regmap_read(drvdata->lpaif_map,
392 LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
393 if (ret) {
394 dev_err(soc_runtime->dev,
395 "error reading from rdmacurr reg: %d\n", ret);
396 return ret;
397 }
398
399 return bytes_to_frames(substream->runtime, curr_addr - base_addr);
400}
401
402static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
403 struct vm_area_struct *vma)
404{
405 struct snd_pcm_runtime *runtime = substream->runtime;
406
407 return dma_mmap_coherent(substream->pcm->card->dev, vma,
408 runtime->dma_area, runtime->dma_addr,
409 runtime->dma_bytes);
410}
411
412static 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,
422};
423
424static irqreturn_t lpass_dma_interrupt_handler(
425 struct snd_pcm_substream *substream,
426 struct lpass_data *drvdata,
427 int chan, u32 interrupts)
428{
429 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
430 struct lpass_variant *v = drvdata->variant;
431 irqreturn_t ret = IRQ_NONE;
432 int rv;
433
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));
438 if (rv) {
439 dev_err(soc_runtime->dev,
440 "error writing to irqclear reg: %d\n", rv);
441 return IRQ_NONE;
442 }
443 snd_pcm_period_elapsed(substream);
444 ret = IRQ_HANDLED;
445 }
446
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));
451 if (rv) {
452 dev_err(soc_runtime->dev,
453 "error writing to irqclear reg: %d\n", rv);
454 return IRQ_NONE;
455 }
456 dev_warn(soc_runtime->dev, "xrun warning\n");
457 snd_pcm_stop_xrun(substream);
458 ret = IRQ_HANDLED;
459 }
460
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));
465 if (rv) {
466 dev_err(soc_runtime->dev,
467 "error writing to irqclear reg: %d\n", rv);
468 return IRQ_NONE;
469 }
470 dev_err(soc_runtime->dev, "bus access error\n");
471 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
472 ret = IRQ_HANDLED;
473 }
474
475 return ret;
476}
477
478static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
479{
480 struct lpass_data *drvdata = data;
481 struct lpass_variant *v = drvdata->variant;
482 unsigned int irqs;
483 int rv, chan;
484
485 rv = regmap_read(drvdata->lpaif_map,
486 LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
487 if (rv) {
488 pr_err("error reading from irqstat reg: %d\n", rv);
489 return IRQ_NONE;
490 }
491
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)
499 return rv;
500 }
501 }
502
503 return IRQ_HANDLED;
504}
505
506static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
507{
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);
511 int ret = -EINVAL;
512 size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
513
514 psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
515 if (psubstream) {
516 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
517 component->dev,
518 size, &psubstream->dma_buffer);
519 if (ret) {
520 dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
521 return ret;
522 }
523 }
524
525 csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
526 if (csubstream) {
527 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
528 component->dev,
529 size, &csubstream->dma_buffer);
530 if (ret) {
531 dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
532 if (psubstream)
533 snd_dma_free_pages(&psubstream->dma_buffer);
534 return ret;
535 }
536
537 }
538
539 return 0;
540}
541
542static void lpass_platform_pcm_free(struct snd_pcm *pcm)
543{
544 struct snd_pcm_substream *substream;
545 int i;
546
547 for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
548 substream = pcm->streams[i].substream;
549 if (substream) {
550 snd_dma_free_pages(&substream->dma_buffer);
551 substream->dma_buffer.area = NULL;
552 substream->dma_buffer.addr = 0;
553 }
554 }
555}
556
557static const struct snd_soc_component_driver lpass_component_driver = {
558 .name = DRV_NAME,
559 .pcm_new = lpass_platform_pcm_new,
560 .pcm_free = lpass_platform_pcm_free,
561 .ops = &lpass_platform_pcm_ops,
562};
563
564int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
565{
566 struct lpass_data *drvdata = platform_get_drvdata(pdev);
567 struct lpass_variant *v = drvdata->variant;
568 int ret;
569
570 drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
David Brazdil0f672f62019-12-10 10:32:29 +0000571 if (drvdata->lpaif_irq < 0)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000572 return -ENODEV;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000573
574 /* ensure audio hardware is disabled */
575 ret = regmap_write(drvdata->lpaif_map,
576 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
577 if (ret) {
578 dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
579 return ret;
580 }
581
582 ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
583 lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
584 "lpass-irq-lpaif", drvdata);
585 if (ret) {
586 dev_err(&pdev->dev, "irq request failed: %d\n", ret);
587 return ret;
588 }
589
590
591 return devm_snd_soc_register_component(&pdev->dev,
592 &lpass_component_driver, NULL, 0);
593}
594EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
595
596MODULE_DESCRIPTION("QTi LPASS Platform Driver");
597MODULE_LICENSE("GPL v2");