blob: 8ab429a9ea2a0ba3171fa13dcf04ee752d0ba2e3 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/dma-mapping.h>
16#include <linux/debugfs.h>
17#include <linux/delay.h>
18#include <linux/uaccess.h>
19#include <linux/android_pmem.h>
20#include <linux/irq.h>
21#include <linux/interrupt.h>
22#include <linux/spinlock.h>
23#include <linux/slab.h>
24#include <linux/msm_audio.h>
25#include <linux/clk.h>
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/soc.h>
29#include <mach/msm_iomap-8x60.h>
30#include <mach/audio_dma_msm8k.h>
31#include <sound/dai.h>
32#include "msm8660-pcm.h"
33
34struct dai_baseinfo {
35 void __iomem *base;
36};
37
38static struct dai_baseinfo dai_info;
39
40struct dai_drv {
41 u8 *buffer;
42 u32 buffer_phys;
43 int channels;
44 irqreturn_t (*callback) (int intrsrc, void *private_data);
45 void *private_data;
46 int in_use;
47 u32 buffer_len;
48 u32 period_len;
49 u32 master_mode;
50};
51
52static struct dai_drv *dai[MAX_CHANNELS];
53static spinlock_t dai_lock;
54
55static int dai_find_dma_channel(uint32_t intrsrc)
56{
57 int i, dma_channel = 0;
58 pr_debug("%s\n", __func__);
59
60 for (i = 0; i <= 27; i += 3) {
61 if (intrsrc & (1 << i)) {
62 dma_channel = i / 3;
63 break;
64 }
65 }
66 return dma_channel;
67}
68
69void register_dma_irq_handler(int dma_ch,
70 irqreturn_t (*callback) (int intrsrc, void *private_data),
71 void *private_data)
72{
73 pr_debug("%s\n", __func__);
74 dai[dma_ch]->callback = callback;
75 dai[dma_ch]->private_data = private_data;
76}
77
78void unregister_dma_irq_handler(int dma_ch)
79{
80 pr_debug("%s\n", __func__);
81 dai[dma_ch]->callback = NULL;
82 dai[dma_ch]->private_data = NULL;
83}
84
85static irqreturn_t dai_irq_handler(int irq, void *data)
86{
87 unsigned long flag;
88 uint32_t intrsrc;
89 uint32_t dma_ch = 0;
90 irqreturn_t ret = IRQ_HANDLED;
91
92 pr_debug("%s\n", __func__);
93 spin_lock_irqsave(&dai_lock, flag);
94 intrsrc = readl(dai_info.base + LPAIF_IRQ_STAT(0));
95 writel(intrsrc, dai_info.base + LPAIF_IRQ_CLEAR(0));
96 while (intrsrc) {
97 dma_ch = dai_find_dma_channel(intrsrc);
98
99 if (!dai[dma_ch]->callback)
100 goto handled;
101 if (!dai[dma_ch]->private_data)
102 goto handled;
103 ret = dai[dma_ch]->callback(intrsrc,
104 dai[dma_ch]->private_data);
105 intrsrc &= ~(0x7 << (dma_ch * 3));
106 }
107handled:
108 spin_unlock_irqrestore(&dai_lock, flag);
109 return ret;
110}
111
112void dai_print_state(uint32_t dma_ch)
113{
114 int i = 0;
115 unsigned long *ptrmem = (unsigned long *)dai_info.base;
116
117 for (i = 0; i < 4; i++, ++ptrmem)
118 pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
119 (unsigned int)*ptrmem);
120
121 ptrmem = (unsigned long *)(dai_info.base
122 + DMA_CH_CTL_BASE + DMA_CH_INDEX(dma_ch));
123 for (i = 0; i < 10; i++, ++ptrmem)
124 pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
125 (unsigned int) *ptrmem);
126}
127
128static int dai_enable_irq(uint32_t dma_ch)
129{
130 int ret;
131 pr_debug("%s\n", __func__);
132 ret = request_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, dai_irq_handler,
133 IRQF_TRIGGER_RISING | IRQF_SHARED, "msm-i2s",
134 (void *) (dma_ch+1));
135 if (ret < 0) {
136 pr_debug("Request Irq Failed err = %d\n", ret);
137 return ret;
138 }
139 return ret;
140}
141
142static void dai_config_dma(uint32_t dma_ch)
143{
144 pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
145
146 writel(dai[dma_ch]->buffer_phys,
147 dai_info.base + LPAIF_DMA_BASE(dma_ch));
148 writel(((dai[dma_ch]->buffer_len >> 2) - 1),
149 dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch));
150 writel(((dai[dma_ch]->period_len >> 2) - 1),
151 dai_info.base + LPAIF_DMA_PER_LEN(dma_ch));
152}
153
154static void dai_enable_codec(uint32_t dma_ch, int codec)
155{
156 uint32_t intrVal;
157 uint32_t i2sctl;
158 pr_debug("%s\n", __func__);
159
160 intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
161 intrVal = intrVal | (7 << (dma_ch * 3));
162 writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
163 if (codec == DAI_SPKR) {
164 writel(0x0813, dai_info.base + LPAIF_DMA_CTL(dma_ch));
165 i2sctl = 0x4400;
166 i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
167 writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_SPKR));
168 } else if (codec == DAI_MIC) {
169 writel(0x81b, dai_info.base + LPAIF_DMA_CTL(dma_ch));
170 i2sctl = 0x0110;
171 i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
172 writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_MIC));
173 }
174}
175
176static void dai_disable_codec(uint32_t dma_ch, int codec)
177{
178 uint32_t intrVal = 0;
179 uint32_t intrVal1 = 0;
180 unsigned long flag = 0x0;
181
182 pr_debug("%s\n", __func__);
183 spin_lock_irqsave(&dai_lock, flag);
184
185 intrVal1 = readl(dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
186
187 if (codec == DAI_SPKR)
188 intrVal1 = intrVal1 & ~(1 << 14);
189 else if (codec == DAI_MIC)
190 intrVal1 = intrVal1 & ~(1 << 8);
191
192 writel(intrVal1, dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
193 intrVal = 0x0;
194 writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
195
196 spin_unlock_irqrestore(&dai_lock, flag);
197}
198
199int dai_open(uint32_t dma_ch)
200{
201
202 pr_debug("%s\n", __func__);
203 if (!dai_info.base) {
204 pr_debug("%s failed as no msm-dai device\n", __func__);
205 return -ENODEV;
206 }
207 if (dma_ch >= MAX_CHANNELS) {
208 pr_debug("%s over max channesl %d\n", __func__, dma_ch);
209 return -ENODEV;
210 }
211 return 0;
212}
213
214void dai_close(uint32_t dma_ch)
215{
216 pr_debug("%s\n", __func__);
217 if ((dma_ch >= 0) && (dma_ch < 5))
218 dai_disable_codec(dma_ch, DAI_SPKR);
219 else
220 dai_disable_codec(dma_ch, DAI_MIC);
221 free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
222}
223
224void dai_set_master_mode(uint32_t dma_ch, int mode)
225{
226 if (dma_ch < MAX_CHANNELS)
227 dai[dma_ch]->master_mode = mode;
228 else
229 pr_err("%s: invalid dma channel\n", __func__);
230}
231
232int dai_set_params(uint32_t dma_ch, struct dai_dma_params *params)
233{
234 pr_debug("%s\n", __func__);
235 dai[dma_ch]->buffer = params->buffer;
236 dai[dma_ch]->buffer_phys = params->src_start;
237 dai[dma_ch]->channels = params->channels;
238 dai[dma_ch]->buffer_len = params->buffer_size;
239 dai[dma_ch]->period_len = params->period_size;
240 dai_config_dma(dma_ch);
241 return dma_ch;
242}
243
244int dai_start(uint32_t dma_ch)
245{
246 unsigned long flag = 0x0;
247
248 spin_lock_irqsave(&dai_lock, flag);
249 dai_enable_irq(dma_ch);
250 if ((dma_ch >= 0) && (dma_ch < 5))
251 dai_enable_codec(dma_ch, DAI_SPKR);
252 else
253 dai_enable_codec(dma_ch, DAI_MIC);
254 spin_unlock_irqrestore(&dai_lock, flag);
255 dai_print_state(dma_ch);
256 return 0;
257}
258
259#define HDMI_BURST_INCR4 (1 << 11)
260#define HDMI_WPSCNT (1 << 8)
261#define HDMI_AUDIO_INTF (5 << 4)
262#define HDMI_FIFO_WATER_MARK (7 << 1)
263#define HDMI_ENABLE (1)
264
265int dai_start_hdmi(uint32_t dma_ch)
266{
267 unsigned long flag = 0x0;
268 uint32_t val;
269
270 pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
271
272 spin_lock_irqsave(&dai_lock, flag);
273
274 dai_enable_irq(dma_ch);
275
276 if ((dma_ch >= 0) && (dma_ch < 5)) {
277
278 val = readl(dai_info.base + LPAIF_IRQ_EN(0));
279 val = val | (7 << (dma_ch * 3));
280 writel(val, dai_info.base + LPAIF_IRQ_EN(0));
281
282
283 val = (HDMI_BURST_INCR4 | HDMI_WPSCNT | HDMI_AUDIO_INTF |
284 HDMI_FIFO_WATER_MARK | HDMI_ENABLE);
285
286 writel(val, dai_info.base + LPAIF_DMA_CTL(dma_ch));
287 }
288 spin_unlock_irqrestore(&dai_lock, flag);
289
290 dai_print_state(dma_ch);
291 return 0;
292}
293
294void dai_stop_hdmi(uint32_t dma_ch)
295{
296 unsigned long flag = 0x0;
297 uint32_t intrVal;
298 uint32_t int_mask = 0x00000007;
299
300 pr_debug("%s dma_ch %u\n", __func__, dma_ch);
301
302 spin_lock_irqsave(&dai_lock, flag);
303
304 free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
305
306
307 intrVal = 0x0;
308 writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
309
310 intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
311
312 int_mask = ((int_mask) << (dma_ch * 3));
313 int_mask = ~int_mask;
314
315 intrVal = intrVal && int_mask;
316 writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
317
318 spin_unlock_irqrestore(&dai_lock, flag);
319}
320
321int dai_stop(uint32_t dma_ch)
322{
323 pr_debug("%s\n", __func__);
324 return 0;
325}
326
327
328uint32_t dai_get_dma_pos(uint32_t dma_ch)
329{
330
331 uint32_t addr;
332
333 pr_debug("%s\n", __func__);
334 addr = readl(dai_info.base + LPAIF_DMA_CURR_ADDR(dma_ch));
335
336 return addr;
337}
338
339static int __devinit dai_probe(struct platform_device *pdev)
340{
341 int rc = 0;
342 int i = 0;
343 struct resource *src;
344 src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msm-dai");
345 if (!src) {
346 rc = -ENODEV;
347 pr_debug("%s Error rc=%d\n", __func__, rc);
348 goto error;
349 }
350 for (i = 0; i <= MAX_CHANNELS; i++) {
351 dai[i] = kzalloc(sizeof(struct dai_drv), GFP_KERNEL);
352 if (!dai[0]) {
353 pr_debug("Allocation failed for dma_channel = 0\n");
354 return -ENODEV;
355 }
356 }
357 dai_info.base = ioremap(src->start, (src->end - src->start) + 1);
358 pr_debug("%s: msm-dai: 0x%08x\n", __func__,
359 (unsigned int)dai_info.base);
360 spin_lock_init(&dai_lock);
361error:
362 return rc;
363}
364
365static int dai_remove(struct platform_device *pdev)
366{
367 iounmap(dai_info.base);
368 return 0;
369}
370
371static struct platform_driver dai_driver = {
372 .probe = dai_probe,
373 .remove = dai_remove,
374 .driver = {
375 .name = "msm-dai",
376 .owner = THIS_MODULE
377 },
378};
379
380static struct resource msm_lpa_resources[] = {
381 {
382 .start = MSM_LPA_PHYS,
383 .end = MSM_LPA_END,
384 .flags = IORESOURCE_MEM,
385 .name = "msm-dai",
386 },
387};
388
389static struct platform_device *codec_device;
390
391static int msm_dai_dev_register(const char *name)
392{
393 int ret = 0;
394
395 pr_debug("%s : called\n", __func__);
396 codec_device = platform_device_alloc(name, -1);
397 if (codec_device == NULL) {
398 pr_debug("Failed to allocate %s\n", name);
399 return -ENODEV;
400 }
401
402 platform_set_drvdata(codec_device, (void *)&dai_info);
403 platform_device_add_resources(codec_device, &msm_lpa_resources[0],
404 ARRAY_SIZE(msm_lpa_resources));
405 ret = platform_device_add(codec_device);
406 if (ret != 0) {
407 pr_debug("Failed to register %s: %d\n", name, ret);
408 platform_device_put(codec_device);
409 }
410 return ret;
411}
412
413static int __init dai_init(void)
414{
415 if (msm_dai_dev_register("msm-dai")) {
416 pr_notice("dai_init: msm-dai Failed");
417 return -ENODEV;
418 }
419 return platform_driver_register(&dai_driver);
420}
421
422static void __exit dai_exit(void)
423{
424 platform_driver_unregister(&dai_driver);
425 platform_device_put(codec_device);
426}
427
428module_init(dai_init);
429module_exit(dai_exit);
430
431MODULE_DESCRIPTION("MSM I2S driver");
432MODULE_LICENSE("GPL v2");