2 * ALSA PCM Interface for the Broadcom BCM947XX family of SOCs
4 * Copyright (C) 2009, Broadcom Corporation
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>
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>
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)
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
,
70 .period_bytes_min
= 32,
71 .period_bytes_max
= 4096,
74 .buffer_bytes_max
= 128 * 1024,
78 struct bcm947xx_runtime_data
{
80 bcm947xx_i2s_info_t
*snd_bcm
;
81 unsigned int dma_loaded
;
82 unsigned int dma_limit
;
83 unsigned int dma_period
;
88 hnddma_t
*di
[1]; /* hnddma handles, per fifo */
92 #if BCM947XX_PCM_DEBUG
94 prhex(const char *msg
, uchar
*buf
, uint nbytes
)
97 int len
= sizeof(line
);
101 if (msg
&& (msg
[0] != '\0'))
102 printf("%s: @%p\n", msg
, buf
);
105 for (i
= 0; i
< nbytes
; i
++) {
107 nchar
= snprintf(p
, len
, " %04d: ", i
); /* line prefix */
112 nchar
= snprintf(p
, len
, "%02x ", buf
[i
]);
118 printf("%s\n", line
); /* flush line */
124 /* flush last partial 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
;
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
);
152 if (pos
>= brtd
->dma_end
)
153 pos
= brtd
->dma_start
;
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
);
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
);
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
);
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
));
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
);
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
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
);
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
);
290 DBG("%s: called with brtd == NULL\n", __FUNCTION__
);
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
);
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
);
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
);
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
);
351 static int bcm947xx_pcm_prepare(struct snd_pcm_substream
*substream
)
353 uint32 intmask
= R_REG(snd_bcm
->osh
, &snd_bcm
->regs
->intmask
);
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
);
365 static int bcm947xx_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
367 struct bcm947xx_runtime_data
*brtd
= substream
->runtime
->private_data
;
370 //DBG("%s w/cmd %d\n", __FUNCTION__, cmd);
372 spin_lock(&brtd
->lock
);
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
;
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
;
392 spin_unlock(&brtd
->lock
);
399 bcm947xx_dma_getposition(dma_addr_t
*src
, dma_addr_t
*dst
)
402 *src
= (dma_addr_t
)dma_getpos(snd_bcm
->di
[0], DMA_TX
);
404 *dst
= (dma_addr_t
)dma_getpos(snd_bcm
->di
[0], DMA_RX
);
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
;
418 spin_lock(&brtd
->lock
);
420 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
) {
421 bcm947xx_dma_getposition(NULL
, &pos
);
423 bcm947xx_dma_getposition(&pos
, NULL
);
426 if ((void *)pos
== NULL
)
427 res
= 0; /* DMA not running? */
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__
);
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
);
475 DBG("%s: dma_alloc failed\n", __FUNCTION__
);
483 static void bcm947xx_pcm_free(struct snd_pcm
*pcm
)
485 struct snd_pcm_substream
*substream
;
486 struct snd_dma_buffer
*buf
;
489 DBG("%s\n", __FUNCTION__
);
491 for (stream
= 0; stream
< 2; stream
++) {
492 substream
= pcm
->streams
[stream
].substream
;
496 buf
= &substream
->dma_buffer
;
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
,
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
);
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__
);
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");