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