ARM: use BX when branch to an address in register
[kugel-rb.git] / firmware / target / arm / s3c2440 / gigabeat-fx / pcm-meg-fx.c
blobe9f55479c770595b5dad3fa66cf795798124d614
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 "system.h"
23 #include "kernel.h"
24 #include "logf.h"
25 #include "audio.h"
26 #include "sound.h"
27 #include "file.h"
29 /* PCM interrupt routine lockout */
30 static struct
32 int locked;
33 unsigned long state;
34 } dma_play_lock =
36 .locked = 0,
37 .state = 0,
40 #define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
42 /* Setup for the DMA controller */
43 #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
45 /* DMA count has hit zero - no more data */
46 /* Get more data from the callback and top off the FIFO */
47 void fiq_handler(void) __attribute__((interrupt ("FIQ")));
49 /* Mask the DMA interrupt */
50 void pcm_play_lock(void)
52 if (++dma_play_lock.locked == 1)
53 s3c_regset32(&INTMSK, DMA2_MASK);
56 /* Unmask the DMA interrupt if enabled */
57 void pcm_play_unlock(void)
59 if (--dma_play_lock.locked == 0)
60 s3c_regclr32(&INTMSK, dma_play_lock.state);
63 void pcm_play_dma_init(void)
65 /* There seem to be problems when changing the IIS interface configuration
66 * when a clock is not present.
68 s3c_regset32(&CLKCON, 1<<17);
69 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
70 BCLK 32fs */
71 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
73 /* RX,TX off,on */
74 IISCON |= (1<<3) | (1<<2);
76 s3c_regclr32(&CLKCON, 1<<17);
78 audiohw_init();
80 /* init GPIO */
81 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
82 GPCDAT |= (1<<7);
83 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
84 GPECON = (GPECON & ~0x3ff) | 0x2aa;
86 /* Do not service DMA requests, yet */
88 /* clear any pending int and mask it */
89 s3c_regset32(&INTMSK, DMA2_MASK);
90 SRCPND = DMA2_MASK;
92 /* connect to FIQ */
93 s3c_regset32(&INTMOD, DMA2_MASK);
96 void pcm_postinit(void)
98 audiohw_postinit();
101 void pcm_dma_apply_settings(void)
103 audiohw_set_frequency(pcm_fsel);
106 /* Connect the DMA and start filling the FIFO */
107 static void play_start_pcm(void)
109 /* clear pending DMA interrupt */
110 SRCPND = DMA2_MASK;
112 /* Flush any pending writes */
113 clean_dcache_range((char*)DISRC2-0x30000000, (DCON2 & 0xFFFFF) * 2);
115 /* unmask DMA interrupt when unlocking */
116 dma_play_lock.state = DMA2_MASK;
118 /* turn on the request */
119 IISCON |= (1<<5);
121 /* Activate the channel */
122 DMASKTRIG2 = 0x2;
124 /* turn off the idle */
125 IISCON &= ~(1<<3);
127 /* start the IIS */
128 IISCON |= (1<<0);
131 /* Disconnect the DMA and wait for the FIFO to clear */
132 static void play_stop_pcm(void)
134 /* Mask DMA interrupt */
135 s3c_regset32(&INTMSK, DMA2_MASK);
137 /* De-Activate the DMA channel */
138 DMASKTRIG2 = 0x4;
140 /* are we playing? wait for the chunk to finish */
141 if (dma_play_lock.state != 0)
143 /* wait for the FIFO to empty and DMA to stop */
144 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
147 /* Keep interrupt masked when unlocking */
148 dma_play_lock.state = 0;
150 /* turn off the request */
151 IISCON &= ~(1<<5);
153 /* turn on the idle */
154 IISCON |= (1<<3);
156 /* stop the IIS */
157 IISCON &= ~(1<<0);
160 void pcm_play_dma_start(const void *addr, size_t size)
162 /* Enable the IIS clock */
163 s3c_regset32(&CLKCON, 1<<17);
165 /* stop any DMA in progress - idle IIS */
166 play_stop_pcm();
168 /* connect DMA to the FIFO and enable the FIFO */
169 IISFCON = (1<<15) | (1<<13);
171 /* set DMA dest */
172 DIDST2 = (unsigned int)&IISFIFO;
174 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
175 DIDSTC2 = 0x03;
177 /* set DMA source and options */
178 DISRC2 = (unsigned int)addr + 0x30000000;
179 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
180 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
181 /* no auto-reload, half-word (16bit) */
182 DCON2 = DMA_CONTROL_SETUP | (size / 2);
183 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
185 play_start_pcm();
188 /* Promptly stop DMA transfers and stop IIS */
189 void pcm_play_dma_stop(void)
191 play_stop_pcm();
193 /* Disconnect the IIS clock */
194 s3c_regclr32(&CLKCON, 1<<17);
197 void pcm_play_dma_pause(bool pause)
199 if (pause)
201 /* pause playback on current buffer */
202 play_stop_pcm();
204 else
206 /* restart playback on current buffer */
207 /* make sure we're aligned on left channel - skip any right
208 channel sample left waiting */
209 DISRC2 = (DCSRC2 + 2) & ~0x3;
210 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
211 play_start_pcm();
215 void fiq_handler(void)
217 static void *start;
218 static size_t size;
220 /* clear any pending interrupt */
221 SRCPND = DMA2_MASK;
223 /* Buffer empty. Try to get more. */
224 pcm_play_get_more_callback(&start, &size);
226 if (size == 0)
227 return;
229 /* Flush any pending cache writes */
230 clean_dcache_range(start, size);
232 /* set the new DMA values */
233 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
234 DISRC2 = (unsigned int)start + 0x30000000;
236 /* Re-Activate the channel */
237 DMASKTRIG2 = 0x2;
240 size_t pcm_get_bytes_waiting(void)
242 /* lie a little and only return full pairs */
243 return (DSTAT2 & 0xFFFFE) * 2;
246 const void * pcm_play_dma_get_peak_buffer(int *count)
248 unsigned long addr = DCSRC2;
249 int cnt = DSTAT2;
250 *count = (cnt & 0xFFFFF) >> 1;
251 return (void *)((addr + 2) & ~3);