Make PCM->driver interface about as simple as it will get. Registered callback, zero...
[kugel-rb.git] / firmware / target / arm / s3c2440 / mini2440 / pcm-mini2440.c
blob30db29c42c0df2c545dd5f943c2acc1c75dd4012
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Michael Sevakis
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 ****************************************************************************/
21 #include <stdlib.h>
22 #include "config.h"
23 #include "system.h"
24 #include "kernel.h"
25 #include "logf.h"
26 #include "audio.h"
27 #include "sound.h"
28 #include "file.h"
30 /* PCM interrupt routine lockout */
31 static struct
33 int locked;
34 unsigned long state;
35 } dma_play_lock =
37 .locked = 0,
38 .state = 0,
41 #define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
43 /* Setup for the DMA controller */
44 #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
46 #ifdef HAVE_UDA1341
47 /* for PCLK = 50 MHz, frame size = 32 */
48 /* [prescaler, master clock rate] */
49 static const unsigned char pcm_freq_parms[HW_NUM_FREQ][2] =
51 [HW_FREQ_44] = { 2, IISMOD_MASTER_CLOCK_384FS },
52 [HW_FREQ_22] = { 8, IISMOD_MASTER_CLOCK_256FS },
53 [HW_FREQ_11] = { 17, IISMOD_MASTER_CLOCK_256FS },
55 #endif
57 /* DMA count has hit zero - no more data */
58 /* Get more data from the callback and top off the FIFO */
59 void fiq_handler(void) __attribute__((interrupt ("FIQ")));
61 /* Mask the DMA interrupt */
62 void pcm_play_lock(void)
64 if (++dma_play_lock.locked == 1)
65 s3c_regset32(&INTMSK, DMA2_MASK);
68 /* Unmask the DMA interrupt if enabled */
69 void pcm_play_unlock(void)
71 if (--dma_play_lock.locked == 0)
72 s3c_regclr32(&INTMSK, dma_play_lock.state);
75 void pcm_play_dma_init(void)
77 /* There seem to be problems when changing the IIS interface configuration
78 * when a clock is not present.
80 s3c_regset32(&CLKCON, 1<<17);
82 #ifdef HAVE_UDA1341
83 /* master, transmit mode, 16 bit samples, BCLK 32fs, PCLK */
84 IISMOD = IISMOD_MASTER_CLOCK_PCLK | IISMOD_MASTER_MODE | IISMOD_TRANSMIT_MODE
85 | IISMOD_16_BIT | IISMOD_MASTER_CLOCK_256FS | IISMOD_BIT_CLOCK_32FS;
87 /* TX idle, enable prescaler */
88 IISCON |= IISCON_TX_IDLE | IISCON_IIS_PRESCALER_ENABLE;
89 #else
90 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
91 BCLK 32fs */
92 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
94 /* RX,TX off,on */
95 IISCON |= (1<<3) | (1<<2);
96 #endif
98 s3c_regclr32(&CLKCON, 1<<17);
100 audiohw_init();
102 /* init GPIO */
103 #ifdef GIGABEAT_F
104 /* GPCCON = (GPCCON & ~(3<<14)) | (1<<14); */
105 S3C244_GPIO_CONFIG (GPCCON, 7, GPIO_OUTPUT);
106 GPCDAT |= (1<<7);
107 #endif
109 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
110 GPECON = (GPECON & ~0x3ff) | 0x2aa;
112 /* Do not service DMA requests, yet */
114 /* clear any pending int and mask it */
115 s3c_regset32(&INTMSK, DMA2_MASK);
116 SRCPND = DMA2_MASK;
118 /* connect to FIQ */
119 s3c_regset32(&INTMOD, DMA2_MASK);
122 void pcm_postinit(void)
124 audiohw_postinit();
127 void pcm_dma_apply_settings(void)
129 #ifdef HAVE_UDA1341
130 unsigned int reg_val;
131 /* set prescaler and master clock rate according to freq */
132 reg_val = (pcm_freq_parms [pcm_fsel][0] << 5) | pcm_freq_parms [pcm_fsel][0];
134 IISMOD = (IISMOD & ~IISMOD_MASTER_CLOCK_384FS) | pcm_freq_parms [pcm_fsel][1] ;
135 IISPSR = reg_val;
136 #endif
138 audiohw_set_frequency(pcm_fsel);
141 /* Connect the DMA and start filling the FIFO */
142 static void play_start_pcm(void)
144 /* clear pending DMA interrupt */
145 SRCPND = DMA2_MASK;
147 /* Flush any pending writes */
148 clean_dcache_range((char*)DISRC2-0x30000000, (DCON2 & 0xFFFFF) * 2);
150 /* unmask DMA interrupt when unlocking */
151 dma_play_lock.state = DMA2_MASK;
153 /* turn on the request */
154 IISCON |= (1<<5);
156 /* Activate the channel */
157 DMASKTRIG2 = 0x2;
159 /* turn off the idle */
160 IISCON &= ~(1<<3);
162 #ifdef HAVE_UDA1341
163 IISMOD = (IISMOD & ~IISMOD_MASTER_CLOCK_384FS) | pcm_freq_parms [pcm_fsel][1] ;
164 IISPSR = (pcm_freq_parms [pcm_fsel][0] << 5) | pcm_freq_parms [pcm_fsel][0];
165 #endif
167 /* start the IIS */
168 IISCON |= (1<<0);
171 /* Disconnect the DMA and wait for the FIFO to clear */
172 static void play_stop_pcm(void)
174 /* Mask DMA interrupt */
175 s3c_regset32(&INTMSK, DMA2_MASK);
177 /* De-Activate the DMA channel */
178 DMASKTRIG2 = 0x4;
180 /* are we playing? wait for the chunk to finish */
181 if (dma_play_lock.state != 0)
183 /* wait for the FIFO to empty and DMA to stop */
184 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
187 /* Keep interrupt masked when unlocking */
188 dma_play_lock.state = 0;
190 /* turn off the request */
191 IISCON &= ~(1<<5);
193 /* turn on the idle */
194 IISCON |= (1<<3);
196 /* stop the IIS */
197 IISCON &= ~(1<<0);
200 void pcm_play_dma_start(const void *addr, size_t size)
202 /* Enable the IIS clock */
203 s3c_regset32(&CLKCON, 1<<17);
205 /* stop any DMA in progress - idle IIS */
206 play_stop_pcm();
208 /* connect DMA to the FIFO and enable the FIFO */
209 IISFCON = (1<<15) | (1<<13);
211 /* set DMA dest */
212 DIDST2 = (unsigned int)&IISFIFO;
214 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
215 DIDSTC2 = 0x03;
217 /* set DMA source and options */
218 DISRC2 = (unsigned int)addr + 0x30000000;
219 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
220 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
221 /* no auto-reload, half-word (16bit) */
222 DCON2 = DMA_CONTROL_SETUP | (size / 2);
223 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
225 play_start_pcm();
228 /* Promptly stop DMA transfers and stop IIS */
229 void pcm_play_dma_stop(void)
231 play_stop_pcm();
233 /* Disconnect the IIS clock */
234 s3c_regclr32(&CLKCON, 1<<17);
237 void pcm_play_dma_pause(bool pause)
239 if (pause)
241 /* pause playback on current buffer */
242 play_stop_pcm();
244 else
246 /* restart playback on current buffer */
247 /* make sure we're aligned on left channel - skip any right
248 channel sample left waiting */
249 DISRC2 = (DCSRC2 + 2) & ~0x3;
250 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
251 play_start_pcm();
255 void fiq_handler(void)
257 static void *start;
258 static size_t size;
260 /* clear any pending interrupt */
261 SRCPND = DMA2_MASK;
263 /* Buffer empty. Try to get more. */
264 pcm_play_get_more_callback(&start, &size);
266 if (size == 0)
267 return;
269 /* Flush any pending cache writes */
270 clean_dcache_range(start, size);
272 /* set the new DMA values */
273 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
274 DISRC2 = (unsigned int)start + 0x30000000;
276 /* Re-Activate the channel */
277 DMASKTRIG2 = 0x2;
280 size_t pcm_get_bytes_waiting(void)
282 /* lie a little and only return full pairs */
283 return (DSTAT2 & 0xFFFFE) * 2;
286 const void * pcm_play_dma_get_peak_buffer(int *count)
288 unsigned long addr = DCSRC2;
289 int cnt = DSTAT2;
290 *count = (cnt & 0xFFFFF) >> 1;
291 return (void *)((addr + 2) & ~3);