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