Broadcom SDK and wireless driver: another attempt to update to ver. 5.10.147.0
[tomato.git] / release / src-rt / linux / linux-2.6 / sound / soc / bcm947xx / bcm947xx-pcm.c
blob9dc9f182b74837a20ce3de59e008f24e12154e42
1 /*
2 * ALSA PCM Interface for the Broadcom BCM947XX family of SOCs
4 * Copyright (C) 2009, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: bcm947xx-pcm.c,v 1.2 2009/11/12 22:25:16 Exp $
15 #include <linux/init.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
19 #include <linux/dma-mapping.h>
21 #include <asm/io.h>
23 #include <sound/driver.h>
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
29 #include <typedefs.h>
30 #include <bcmdevs.h>
31 #include <pcicfg.h>
32 #include <hndsoc.h>
33 #include <osl.h>
34 #include <bcmutils.h>
35 #include <siutils.h>
36 #include <sbhnddma.h>
37 #include <hnddma.h>
38 #include <i2s_core.h>
40 #include "bcm947xx-i2s.h"
41 #include "bcm947xx-pcm.h"
44 /* Be careful here... turning on prints can break everything, if you start seeing FIFO underflows
45 * then it might be due to excessive printing
47 #define BCM947XX_PCM_DEBUG 0
48 #if BCM947XX_PCM_DEBUG
49 #define DBG(x...) printk(KERN_ERR x)
50 #else
51 #define DBG(x...)
52 #endif
55 static const struct snd_pcm_hardware bcm947xx_pcm_hardware = {
56 .info = SNDRV_PCM_INFO_MMAP |
57 SNDRV_PCM_INFO_MMAP_VALID |
58 /* SNDRV_PCM_INFO_BLOCK_TRANSFER | */
59 SNDRV_PCM_INFO_INTERLEAVED |
60 SNDRV_PCM_INFO_PAUSE |
61 SNDRV_PCM_INFO_RESUME,
62 .formats = SNDRV_PCM_FMTBIT_U16_LE |
63 SNDRV_PCM_FMTBIT_S16_LE |
64 SNDRV_PCM_FMTBIT_S20_3LE |
65 SNDRV_PCM_FMTBIT_S24_LE |
66 SNDRV_PCM_FMTBIT_S24_3LE |
67 SNDRV_PCM_FMTBIT_S32_LE,
68 .channels_min = 2,
69 .channels_max = 2,
70 .period_bytes_min = 32,
71 .period_bytes_max = 4096,
72 .periods_min = 2,
73 .periods_max = 64,
74 .buffer_bytes_max = 128 * 1024,
75 .fifo_size = 128,
78 struct bcm947xx_runtime_data {
79 spinlock_t lock;
80 bcm947xx_i2s_info_t *snd_bcm;
81 unsigned int dma_loaded;
82 unsigned int dma_limit;
83 unsigned int dma_period;
84 dma_addr_t dma_start;
85 dma_addr_t dma_pos;
86 dma_addr_t dma_end;
87 uint state;
88 hnddma_t *di[1]; /* hnddma handles, per fifo */
92 #if BCM947XX_PCM_DEBUG
93 void
94 prhex(const char *msg, uchar *buf, uint nbytes)
96 char line[128], *p;
97 int len = sizeof(line);
98 int nchar;
99 uint i;
101 if (msg && (msg[0] != '\0'))
102 printf("%s: @%p\n", msg, buf);
104 p = line;
105 for (i = 0; i < nbytes; i++) {
106 if (i % 16 == 0) {
107 nchar = snprintf(p, len, " %04d: ", i); /* line prefix */
108 p += nchar;
109 len -= nchar;
111 if (len > 0) {
112 nchar = snprintf(p, len, "%02x ", buf[i]);
113 p += nchar;
114 len -= nchar;
117 if (i % 16 == 15) {
118 printf("%s\n", line); /* flush line */
119 p = line;
120 len = sizeof(line);
124 /* flush last partial line */
125 if (p != line)
126 printf("%s\n", line);
128 #endif /* BCM947XX_PCM_DEBUG */
130 static void bcm947xx_pcm_enqueue(struct snd_pcm_substream *substream)
132 struct bcm947xx_runtime_data *brtd = substream->runtime->private_data;
133 dma_addr_t pos = brtd->dma_pos;
134 int ret;
136 while (brtd->dma_loaded < brtd->dma_limit) {
137 unsigned long len = brtd->dma_period;
139 if ((pos & ~0xFFF) != (((pos+len - 1) & ~0xFFF))) {
140 len = ((pos+len) & ~0xFFF) - pos;
143 if ((pos + len) > brtd->dma_end) {
144 len = brtd->dma_end - pos;
147 ret = dma_txunframed(snd_bcm->di[0], (void *)pos, len, TRUE);
149 if (ret == 0) {
150 pos += len;
151 brtd->dma_loaded++;
152 if (pos >= brtd->dma_end)
153 pos = brtd->dma_start;
154 } else
155 break;
158 brtd->dma_pos = pos;
162 struct snd_pcm_substream *my_stream;
165 irqreturn_t bcm947xx_i2s_isr(int irq, void *devid)
167 uint32 intstatus = R_REG(snd_bcm->osh, &snd_bcm->regs->intstatus);
168 #if BCM947XX_PCM_DEBUG
169 uint32 intmask = R_REG(snd_bcm->osh, &snd_bcm->regs->intmask);
170 #endif
171 uint32 intstatus_new = 0;
172 uint32 int_errmask = I2S_INT_DESCERR | I2S_INT_DATAERR | I2S_INT_DESC_PROTO_ERR |
173 I2S_INT_RCVFIFO_OFLOW | I2S_INT_XMTFIFO_UFLOW | I2S_INT_SPDIF_PAR_ERR;
174 struct bcm947xx_runtime_data *brtd = my_stream->runtime->private_data;
176 if (intstatus & I2S_INT_XMT_INT) {
177 /* reclaim descriptors that have been TX'd */
178 dma_getnexttxp(snd_bcm->di[0], HNDDMA_RANGE_TRANSMITTED);
180 /* clear this bit by writing a "1" back, we've serviced this */
181 intstatus_new |= I2S_INT_XMT_INT;
184 if (intstatus & int_errmask) {
185 DBG("\n\n%s: Turning off all interrupts due to error\n", __FUNCTION__);
186 DBG("%s: intstatus 0x%x intmask 0x%x\n", __FUNCTION__, intstatus, intmask);
189 /* something bad happened, turn off all interrupts */
190 W_REG(snd_bcm->osh, &snd_bcm->regs->intmask, 0);
193 snd_pcm_period_elapsed(my_stream);
195 spin_lock(&brtd->lock);
196 brtd->dma_loaded--;
197 if (brtd->state & BCM_I2S_RUNNING) {
198 bcm947xx_pcm_enqueue(my_stream);
200 spin_unlock(&brtd->lock);
202 W_REG(snd_bcm->osh, &snd_bcm->regs->intstatus, intstatus_new);
204 return IRQ_RETVAL(intstatus);
208 static int bcm947xx_pcm_open(struct snd_pcm_substream *substream)
210 struct snd_pcm_runtime *runtime = substream->runtime;
211 struct bcm947xx_runtime_data *brtd;
213 DBG("%s\n", __FUNCTION__);
215 snd_soc_set_runtime_hwparams(substream, &bcm947xx_pcm_hardware);
217 brtd = kzalloc(sizeof(struct bcm947xx_runtime_data), GFP_KERNEL);
218 if (brtd == NULL) {
219 return -ENOMEM;
221 brtd->snd_bcm = snd_bcm;
223 spin_lock_init(&brtd->lock);
225 runtime->private_data = brtd;
227 /* probably should put this somewhere else, after setting up isr ??? */
228 dma_txreset(snd_bcm->di[0]);
229 dma_txinit(snd_bcm->di[0]);
231 #if BCM947XX_PCM_DEBUG
232 DBG("%s: i2s devcontrol 0x%x devstatus 0x%x\n", __FUNCTION__,
233 R_REG(snd_bcm->osh, &snd_bcm->regs->devcontrol),
234 R_REG(snd_bcm->osh, &snd_bcm->regs->devstatus));
235 DBG("%s: i2s intstatus 0x%x intmask 0x%x\n", __FUNCTION__,
236 R_REG(snd_bcm->osh, &snd_bcm->regs->intstatus),
237 R_REG(snd_bcm->osh, &snd_bcm->regs->intmask));
238 DBG("%s: i2s control 0x%x\n", __FUNCTION__,
239 R_REG(snd_bcm->osh, &snd_bcm->regs->i2scontrol));
240 DBG("%s: i2s clkdivider 0x%x txplayth 0x%x\n", __FUNCTION__,
241 R_REG(snd_bcm->osh, &snd_bcm->regs->clkdivider),
242 R_REG(snd_bcm->osh, &snd_bcm->regs->txplayth));
243 DBG("%s: i2s stxctrl 0x%x\n", __FUNCTION__,
244 R_REG(snd_bcm->osh, &snd_bcm->regs->stxctrl));
247 uint32 temp;
248 temp = R_REG(snd_bcm->osh, &snd_bcm->regs->fifocounter);
249 DBG("%s: i2s txcnt 0x%x rxcnt 0x%x\n", __FUNCTION__,
250 (temp & I2S_FC_TX_CNT_MASK)>> I2S_FC_TX_CNT_SHIFT,
251 (temp & I2S_FC_RX_CNT_MASK)>> I2S_FC_RX_CNT_SHIFT);
253 #endif
256 return 0;
259 static int bcm947xx_pcm_close(struct snd_pcm_substream *substream)
261 struct bcm947xx_runtime_data *brtd = substream->runtime->private_data;
263 /* Turn off interrupts... */
264 W_REG(snd_bcm->osh, &snd_bcm->regs->intmask,
265 R_REG(snd_bcm->osh, &snd_bcm->regs->intmask) & ~I2S_INT_XMT_INT);
267 #if BCM947XX_PCM_DEBUG
269 /* dump dma rings to console */
270 #if !defined(FIFOERROR_DUMP_SIZE)
271 #define FIFOERROR_DUMP_SIZE 8192
272 #endif
273 char *tmp;
274 struct bcmstrbuf b;
275 if (snd_bcm->di[0] && (tmp = MALLOC(snd_bcm->osh, FIFOERROR_DUMP_SIZE))) {
276 bcm_binit(&b, tmp, FIFOERROR_DUMP_SIZE);
277 dma_dump(snd_bcm->di[0], &b, TRUE);
278 printbig(tmp);
279 MFREE(snd_bcm->osh, tmp, FIFOERROR_DUMP_SIZE);
282 #endif /* BCM947XX_PCM_DEBUG */
284 /* reclaim all descriptors */
285 dma_txreclaim(snd_bcm->di[0], HNDDMA_RANGE_ALL);
287 if (brtd)
288 kfree(brtd);
289 else
290 DBG("%s: called with brtd == NULL\n", __FUNCTION__);
292 return 0;
295 static int bcm947xx_pcm_hw_params(struct snd_pcm_substream *substream,
296 struct snd_pcm_hw_params *params)
298 struct snd_pcm_runtime *runtime = substream->runtime;
299 struct bcm947xx_runtime_data *brtd = runtime->private_data;
300 //struct snd_soc_pcm_runtime *rtd = substream->private_data;
301 //struct bcm947xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
302 unsigned long totbytes = params_buffer_bytes(params);
304 int ret = 0;
306 #if BCM947XX_PCM_DEBUG
307 size_t buffer_size = params_buffer_size(params);
308 size_t buffer_bytes = params_buffer_bytes(params);
309 size_t period_size = params_period_size(params);
310 size_t period_bytes = params_period_bytes(params);
311 size_t periods = params_periods(params);
312 size_t tick_time = params_tick_time(params);
314 DBG("%s: hw.periods_min %d dma_addr %p dma_bytes %d\n",
315 __FUNCTION__, runtime->hw.periods_min, (void *)runtime->dma_addr, runtime->dma_bytes);
316 DBG("%s: buffer_size 0x%x buffer_bytes 0x%x\n", __FUNCTION__, buffer_size, buffer_bytes);
317 DBG("%s: period_size 0x%x period_bytes 0x%x\n", __FUNCTION__, period_size, period_bytes);
318 DBG("%s: periods 0x%x tick_time0x%x\n", __FUNCTION__, periods, tick_time);
319 #endif
321 my_stream = substream;
323 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
324 runtime->dma_bytes = totbytes;
326 spin_lock_irq(&brtd->lock);
327 brtd->dma_limit = runtime->hw.periods_min;
328 brtd->dma_period = params_period_bytes(params);
329 /* Virtual address of our runtime buffer */
330 brtd->dma_start = (dma_addr_t)runtime->dma_area;
331 brtd->dma_loaded = 0;
332 brtd->dma_pos = brtd->dma_start;
333 brtd->dma_end = brtd->dma_start + totbytes;
334 spin_lock(&brtd->lock);
335 spin_unlock_irq(&brtd->lock);
337 return ret;
341 static int bcm947xx_pcm_hw_free(struct snd_pcm_substream *substream)
343 //DBG("%s\n", __FUNCTION__);
344 snd_pcm_set_runtime_buffer(substream, NULL);
346 my_stream = NULL;
348 return 0;
351 static int bcm947xx_pcm_prepare(struct snd_pcm_substream *substream)
353 uint32 intmask = R_REG(snd_bcm->osh, &snd_bcm->regs->intmask);
354 int ret = 0;
356 /* Turn on Tx interrupt */
357 W_REG(snd_bcm->osh, &snd_bcm->regs->intmask, intmask | I2S_INT_XMT_INT);
359 /* enqueue dma buffers */
360 bcm947xx_pcm_enqueue(substream);
362 return ret;
365 static int bcm947xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
367 struct bcm947xx_runtime_data *brtd = substream->runtime->private_data;
368 int ret = 0;
370 //DBG("%s w/cmd %d\n", __FUNCTION__, cmd);
372 spin_lock(&brtd->lock);
374 switch (cmd) {
376 case SNDRV_PCM_TRIGGER_START:
377 case SNDRV_PCM_TRIGGER_RESUME:
378 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
379 brtd->state |= BCM_I2S_RUNNING;
380 break;
382 case SNDRV_PCM_TRIGGER_STOP:
383 case SNDRV_PCM_TRIGGER_SUSPEND:
384 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
385 brtd->state &= ~BCM_I2S_RUNNING;
386 break;
388 default:
389 ret = -EINVAL;
390 break;
392 spin_unlock(&brtd->lock);
394 return ret;
398 static int
399 bcm947xx_dma_getposition(dma_addr_t *src, dma_addr_t *dst)
401 if (src) {
402 *src = (dma_addr_t)dma_getpos(snd_bcm->di[0], DMA_TX);
403 } else if (dst) {
404 *dst = (dma_addr_t)dma_getpos(snd_bcm->di[0], DMA_RX);
407 return 0;
410 static snd_pcm_uframes_t
411 bcm947xx_pcm_pointer(struct snd_pcm_substream *substream)
413 struct snd_pcm_runtime *runtime = substream->runtime;
414 struct bcm947xx_runtime_data *brtd = runtime->private_data;
415 unsigned long res;
416 dma_addr_t pos = 0;
418 spin_lock(&brtd->lock);
420 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
421 bcm947xx_dma_getposition(NULL, &pos);
422 } else {
423 bcm947xx_dma_getposition(&pos, NULL);
426 if ((void *)pos == NULL)
427 res = 0; /* DMA not running? */
428 else {
429 res = pos - brtd->dma_start;
430 DBG("%s: pos %p - dma_start %p = 0x%x\n", __FUNCTION__, pos, brtd->dma_start, res);
433 spin_unlock(&brtd->lock);
435 return bytes_to_frames(substream->runtime, res);
439 /* Currently unused... memory mapping is automatically done in the dma code */
440 static int bcm947xx_pcm_mmap(struct snd_pcm_substream *substream,
441 struct vm_area_struct *vma)
443 DBG("Entered %s\n", __FUNCTION__);
444 return 0;
448 struct snd_pcm_ops bcm947xx_pcm_ops = {
449 .open = bcm947xx_pcm_open,
450 .close = bcm947xx_pcm_close,
451 .ioctl = snd_pcm_lib_ioctl,
452 .hw_params = bcm947xx_pcm_hw_params,
453 .hw_free = bcm947xx_pcm_hw_free,
454 .prepare = bcm947xx_pcm_prepare,
455 .trigger = bcm947xx_pcm_trigger,
456 .pointer = bcm947xx_pcm_pointer,
457 .mmap = bcm947xx_pcm_mmap,
461 static int bcm947xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
464 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
465 struct snd_dma_buffer *buf = &substream->dma_buffer;
466 size_t size = bcm947xx_pcm_hardware.buffer_bytes_max;
468 buf->dev.type = SNDRV_DMA_TYPE_DEV;
469 buf->dev.dev = pcm->card->dev;
470 buf->private_data = NULL;
471 buf->area = kmalloc(size, GFP_ATOMIC);
472 DBG("%s: size %d @ 0x%p\n", __FUNCTION__, size, buf->area);
474 if (!buf->area) {
475 DBG("%s: dma_alloc failed\n", __FUNCTION__);
476 return -ENOMEM;
478 buf->bytes = size;
480 return 0;
483 static void bcm947xx_pcm_free(struct snd_pcm *pcm)
485 struct snd_pcm_substream *substream;
486 struct snd_dma_buffer *buf;
487 int stream;
489 DBG("%s\n", __FUNCTION__);
491 for (stream = 0; stream < 2; stream++) {
492 substream = pcm->streams[stream].substream;
493 if (!substream)
494 continue;
496 buf = &substream->dma_buffer;
497 if (!buf->area)
498 continue;
500 kfree(buf->area);
501 buf->area = NULL;
504 free_irq(snd_bcm->irq, snd_bcm);
509 static u64 bcm947xx_pcm_dmamask = DMA_32BIT_MASK;
511 int bcm947xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
512 struct snd_pcm *pcm)
514 int ret = 0;
516 DBG("%s\n", __FUNCTION__);
518 if (!card->dev->dma_mask)
519 card->dev->dma_mask = &bcm947xx_pcm_dmamask;
520 if (!card->dev->coherent_dma_mask)
521 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
523 if (dai->playback.channels_min) {
524 ret = bcm947xx_pcm_preallocate_dma_buffer(pcm,
525 SNDRV_PCM_STREAM_PLAYBACK);
526 if (ret)
527 goto out;
530 if ((request_irq(snd_bcm->irq,
531 bcm947xx_i2s_isr, IRQF_SHARED, "i2s", snd_bcm)) < 0) {
532 DBG("%s: request_irq failure\n", __FUNCTION__);
536 out:
537 return ret;
541 struct snd_soc_platform bcm947xx_soc_platform = {
542 .name = "bcm947xx-audio",
543 .pcm_ops = &bcm947xx_pcm_ops,
544 .pcm_new = bcm947xx_pcm_new,
545 .pcm_free = bcm947xx_pcm_free,
548 EXPORT_SYMBOL_GPL(bcm947xx_soc_platform);
551 MODULE_LICENSE("GPL");
552 /* MODULE_AUTHOR(""); */
553 MODULE_DESCRIPTION("BCM947XX PCM module");