Ingenic Jz4740 PCM driver: add some locking here and there (doesn't fix all PCM issues)
[kugel-rb/myfork.git] / firmware / target / mips / ingenic_jz47xx / pcm-jz4740.c
blob1fbc94065ba7e7e8819193901a0d14b30a7de86b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Maurus Cuelenaere
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "system.h"
23 #include "kernel.h"
24 #include "logf.h"
25 #include "audio.h"
26 #include "sound.h"
27 #include "pcm.h"
28 #include "jz4740.h"
31 /****************************************************************************
32 ** Playback DMA transfer
33 **/
35 void pcm_postinit(void)
37 audiohw_postinit();
39 /* playback sample: 16 bits burst: 16 bytes */
40 __i2s_set_iss_sample_size(16);
41 __i2s_set_oss_sample_size(16);
42 __i2s_set_transmit_trigger(10);
43 __i2s_set_receive_trigger(1);
45 /* Flush FIFO */
46 __aic_flush_fifo();
49 void pcm_play_dma_init(void)
51 /* TODO */
53 system_enable_irq(DMA_IRQ(DMA_AIC_TX_CHANNEL));
55 /* Initialize default register values. */
56 audiohw_init();
59 void pcm_dma_apply_settings(void)
61 /* TODO */
62 audiohw_set_frequency(pcm_sampr);
65 static void* playback_address;
66 static inline void set_dma(const void *addr, size_t size)
68 logf("%x %x %d %d %x", (unsigned int)addr, size, (REG_AIC_SR>>24) & 0x20, (REG_AIC_SR>>8) & 0x20, REG_AIC_SR & 0xF);
70 __dcache_writeback_all();
71 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = DMAC_DCCSR_NDES;
72 REG_DMAC_DSAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)addr);
73 REG_DMAC_DTAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)AIC_DR);
74 REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL) = size / 16;
75 REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT;
76 REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE |
77 DMAC_DCMD_RDIL_IGN);
79 playback_address = (void*)addr;
82 static inline void play_dma_callback(void)
84 unsigned char *start;
85 size_t size = 0;
86 pcm_callback_for_more(&start, &size);
88 if(LIKELY(size > 0))
90 set_dma(start, size);
91 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
93 else
95 /* Error, callback missing or no more DMA to do */
96 pcm_play_dma_stop();
97 pcm_play_dma_stopped_callback();
101 void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) __attribute__ ((section(".icode")));
102 void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void)
104 if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_AR)
106 logf("PCM DMA address error");
107 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_AR;
110 if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_HLT)
112 logf("PCM DMA halt");
113 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_HLT;
116 if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_TT)
118 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_TT;
119 play_dma_callback();
123 void pcm_play_dma_start(const void *addr, size_t size)
125 dma_enable();
127 set_dma(addr, size);
129 __aic_enable_transmit_dma();
130 __aic_enable_replay();
132 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
135 void pcm_play_dma_stop(void)
137 int flags = disable_irq_save();
139 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN;
141 dma_disable();
143 __aic_disable_transmit_dma();
144 __aic_disable_replay();
146 restore_irq(flags);
149 static unsigned int play_lock = 0;
150 void pcm_play_lock(void)
152 int flags = disable_irq_save();
154 if (++play_lock == 1)
155 __dmac_channel_disable_irq(DMA_AIC_TX_CHANNEL);
157 restore_irq(flags);
160 void pcm_play_unlock(void)
162 int flags = disable_irq_save();
164 if (--play_lock == 0)
165 __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL);
167 restore_irq(flags);
170 void pcm_play_dma_pause(bool pause)
172 int flags = disable_irq_save();
174 if(pause)
175 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_EN;
176 else
177 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
179 restore_irq(flags);
182 size_t pcm_get_bytes_waiting(void)
184 return REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN ?
185 (REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL) * 16) & ~3 : 0;
188 const void * pcm_play_dma_get_peak_buffer(int *count)
190 int flags = disable_irq_save();
192 const void* addr;
193 if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN)
195 *count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL)*16 >> 2;
196 addr = (const void*)(playback_address + ((REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL)*16 + 2) & ~3));
198 else
200 *count = 0;
201 addr = NULL;
204 restore_irq(flags);
206 return addr;
209 void audiohw_close(void)
213 #ifdef HAVE_RECORDING
214 /* TODO */
215 void pcm_rec_dma_init(void)
219 void pcm_rec_dma_close(void)
223 void pcm_rec_dma_start(void *addr, size_t size)
225 (void) addr;
226 (void) size;
229 void pcm_rec_dma_stop(void)
233 void pcm_rec_lock(void)
237 void pcm_rec_unlock(void)
241 const void * pcm_rec_dma_get_peak_buffer(int *count)
243 *count = 0;
244 return NULL;
247 void pcm_record_more(void *start, size_t size)
249 (void) start;
250 (void) size;
252 #endif