diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-05-03 15:14:52 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-05-03 15:14:52 +0000 |
commit | 5df4405317890cc4a84edcfe827a765b52a712c9 (patch) | |
tree | c4293ce39c1d3e6be351670179b8fac9761391db /firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | |
parent | d0e32119f1a639ab372258e4e3dbb5349bb086ec (diff) | |
download | rockbox-5df4405317890cc4a84edcfe827a765b52a712c9.tar.gz rockbox-5df4405317890cc4a84edcfe827a765b52a712c9.zip |
Gigabeat S: Man it's so loud in here. We have SOUND! Someone please make keymaps consistent; it's rather messy atm.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17327 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 228 |
1 files changed, 209 insertions, 19 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c index e37f6bfbe2..9ac96fd801 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2006 by Michael Sevakis | 10 | * Copyright (C) 2008 by Michael Sevakis |
11 | * | 11 | * |
12 | * All files in this archive are subject to the GNU General Public License. | 12 | * All files in this archive are subject to the GNU General Public License. |
13 | * See the file COPYING in the source tree root for full license agreement. | 13 | * See the file COPYING in the source tree root for full license agreement. |
@@ -19,69 +19,256 @@ | |||
19 | #include <stdlib.h> | 19 | #include <stdlib.h> |
20 | #include "system.h" | 20 | #include "system.h" |
21 | #include "kernel.h" | 21 | #include "kernel.h" |
22 | #include "logf.h" | ||
23 | #include "audio.h" | 22 | #include "audio.h" |
24 | #include "sound.h" | 23 | #include "sound.h" |
25 | #include "file.h" | 24 | #include "avic-imx31.h" |
26 | #include "mmu-imx31.h" | 25 | #include "clkctl-imx31.h" |
27 | 26 | ||
28 | #if 0 | 27 | /* This isn't DMA-based at the moment and is handled like Portal Player but |
29 | static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | 28 | * will suffice for starters. */ |
30 | #endif | 29 | |
30 | struct dma_data | ||
31 | { | ||
32 | uint16_t *p; | ||
33 | size_t size; | ||
34 | int locked; | ||
35 | int state; | ||
36 | }; | ||
37 | |||
38 | static unsigned long pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
39 | |||
40 | static struct dma_data dma_play_data = | ||
41 | { | ||
42 | /* Initialize to a locked, stopped state */ | ||
43 | .p = NULL, | ||
44 | .size = 0, | ||
45 | .locked = 0, | ||
46 | .state = 0 | ||
47 | }; | ||
31 | 48 | ||
32 | void pcm_play_lock(void) | 49 | void pcm_play_lock(void) |
33 | { | 50 | { |
51 | if (++dma_play_data.locked == 1) | ||
52 | { | ||
53 | /* Atomically disable transmit interrupt */ | ||
54 | imx31_regmod32(&SSI_SIER1, 0, SSI_SIER_TIE); | ||
55 | } | ||
34 | } | 56 | } |
35 | 57 | ||
36 | void pcm_play_unlock(void) | 58 | void pcm_play_unlock(void) |
37 | { | 59 | { |
60 | if (--dma_play_data.locked == 0 && dma_play_data.state != 0) | ||
61 | { | ||
62 | /* Atomically enable transmit interrupt */ | ||
63 | imx31_regmod32(&SSI_SIER1, SSI_SIER_TIE, SSI_SIER_TIE); | ||
64 | } | ||
38 | } | 65 | } |
39 | 66 | ||
40 | #if 0 | ||
41 | static void _pcm_apply_settings(void) | 67 | static void _pcm_apply_settings(void) |
42 | { | 68 | { |
69 | if (pcm_freq != pcm_curr_sampr) | ||
70 | { | ||
71 | pcm_curr_sampr = pcm_freq; | ||
72 | // TODO: audiohw_set_frequency(sr_ctrl); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | static void __attribute__((interrupt("IRQ"))) SSI1_HANDLER(void) | ||
77 | { | ||
78 | register pcm_more_callback_type get_more; | ||
79 | |||
80 | do | ||
81 | { | ||
82 | while (dma_play_data.size > 0) | ||
83 | { | ||
84 | if (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 6) | ||
85 | { | ||
86 | return; | ||
87 | } | ||
88 | SSI_STX0_1 = *dma_play_data.p++; | ||
89 | SSI_STX0_1 = *dma_play_data.p++; | ||
90 | dma_play_data.size -= 4; | ||
91 | } | ||
92 | |||
93 | /* p is empty, get some more data */ | ||
94 | get_more = pcm_callback_for_more; | ||
95 | |||
96 | if (get_more) | ||
97 | { | ||
98 | get_more((unsigned char **)&dma_play_data.p, | ||
99 | &dma_play_data.size); | ||
100 | } | ||
101 | } | ||
102 | while (dma_play_data.size > 0); | ||
103 | |||
104 | /* No more data, so disable the FIFO/interrupt */ | ||
105 | pcm_play_dma_stop(); | ||
106 | pcm_play_dma_stopped_callback(); | ||
43 | } | 107 | } |
44 | #endif | ||
45 | 108 | ||
46 | void pcm_apply_settings(void) | 109 | void pcm_apply_settings(void) |
47 | { | 110 | { |
111 | int oldstatus = disable_fiq_save(); | ||
112 | |||
113 | _pcm_apply_settings(); | ||
114 | |||
115 | restore_fiq(oldstatus); | ||
48 | } | 116 | } |
49 | 117 | ||
50 | void pcm_play_dma_init(void) | 118 | void pcm_play_dma_init(void) |
51 | { | 119 | { |
120 | imx31_clkctl_module_clock_gating(CG_SSI1, CGM_ON_ALL); | ||
121 | imx31_clkctl_module_clock_gating(CG_SSI2, CGM_ON_ALL); | ||
122 | |||
123 | /* Reset & disable SSIs */ | ||
124 | SSI_SCR2 &= ~SSI_SCR_SSIEN; | ||
125 | SSI_SCR1 &= ~SSI_SCR_SSIEN; | ||
126 | |||
127 | SSI_SIER1 = SSI_SIER_TFE0; | ||
128 | SSI_SIER2 = 0; | ||
129 | |||
130 | /* Set up audio mux */ | ||
131 | |||
132 | /* Port 1 (internally connected to SSI1) | ||
133 | * All clocking is output sourced from port 4 */ | ||
134 | AUDMUX_PTCR1 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | | ||
135 | AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | | ||
136 | AUDMUX_PTCR_RFSDIR | AUDMUX_PTCR_RFSSEL_PORT4 | | ||
137 | AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4 | | ||
138 | AUDMUX_PTCR_SYN; | ||
139 | |||
140 | /* Receive data from port 4 */ | ||
141 | AUDMUX_PDCR1 = AUDMUX_PDCR_RXDSEL_PORT4; | ||
142 | /* All clock lines are inputs sourced from the master mode codec and | ||
143 | * sent back to SSI1 through port 1 */ | ||
144 | AUDMUX_PTCR4 = AUDMUX_PTCR_SYN; | ||
145 | |||
146 | /* Receive data from port 1 */ | ||
147 | AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1; | ||
148 | |||
149 | /* Port 2 (internally connected to SSI2) routes clocking to port 5 to | ||
150 | * provide MCLK to the codec */ | ||
151 | /* All port 2 clocks are inputs taken from SSI2 */ | ||
152 | AUDMUX_PTCR2 = 0; | ||
153 | AUDMUX_PDCR2 = 0; | ||
154 | /* Port 5 outputs TCLK sourced from port 2 */ | ||
155 | AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT2; | ||
156 | AUDMUX_PDCR5 = 0; | ||
157 | |||
158 | /* Setup SSIs */ | ||
159 | |||
160 | /* SSI1 - interface for all I2S data */ | ||
161 | SSI_SCR1 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; | ||
162 | SSI_STCR1 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | | ||
163 | SSI_STCR_TEFS | SSI_STCR_TFEN0; | ||
164 | |||
165 | /* 16 bits per word, 2 words per frame */ | ||
166 | SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | | ||
167 | SSI_STRCCR_PMw(4-1); | ||
168 | |||
169 | SSI_STMSK1 = 0; | ||
170 | |||
171 | /* Receive */ | ||
172 | SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | | ||
173 | SSI_SRCR_REFS | SSI_SRCR_RFEN0; | ||
174 | |||
175 | /* 16 bits per word, 2 words per frame */ | ||
176 | SSI_SRCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | | ||
177 | SSI_STRCCR_PMw(4-1); | ||
178 | |||
179 | /* Receive high watermark - 6 samples in FIFO | ||
180 | * Transmit low watermark - 2 samples in FIFO */ | ||
181 | SSI_SFCSR1 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_TFWM1w(1) | | ||
182 | SSI_SFCSR_RFWM0w(6) | SSI_SFCSR_TFWM0w(2); | ||
183 | |||
184 | SSI_SRMSK1 = 0; | ||
185 | |||
186 | /* SSI2 - provides MCLK only */ | ||
187 | SSI_SCR2 = 0; | ||
188 | SSI_SRCR2 = 0; | ||
189 | SSI_STCR2 = SSI_STCR_TXDIR; | ||
190 | SSI_STCCR2 = SSI_STRCCR_PMw(0); | ||
191 | |||
192 | /* Enable SSIs */ | ||
193 | SSI_SCR2 |= SSI_SCR_SSIEN; | ||
194 | |||
52 | audiohw_init(); | 195 | audiohw_init(); |
53 | } | 196 | } |
54 | 197 | ||
55 | void pcm_postinit(void) | 198 | void pcm_postinit(void) |
56 | { | 199 | { |
57 | audiohw_postinit(); | 200 | audiohw_postinit(); |
201 | avic_enable_int(SSI1, IRQ, 8, SSI1_HANDLER); | ||
58 | } | 202 | } |
59 | 203 | ||
60 | #if 0 | ||
61 | /* Connect the DMA and start filling the FIFO */ | ||
62 | static void play_start_pcm(void) | 204 | static void play_start_pcm(void) |
63 | { | 205 | { |
206 | /* Stop transmission (if in progress) */ | ||
207 | SSI_SCR1 &= ~SSI_SCR_TE; | ||
208 | |||
209 | /* Apply new settings */ | ||
210 | _pcm_apply_settings(); | ||
211 | |||
212 | /* Enable interrupt on unlock */ | ||
213 | dma_play_data.state = 1; | ||
214 | |||
215 | /* Fill the FIFO or start when data is used up */ | ||
216 | while (1) | ||
217 | { | ||
218 | if (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 6 || dma_play_data.size == 0) | ||
219 | { | ||
220 | SSI_SCR1 |= (SSI_SCR_TE | SSI_SCR_SSIEN); /* Start transmitting */ | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | SSI_STX0_1 = *dma_play_data.p++; | ||
225 | SSI_STX0_1 = *dma_play_data.p++; | ||
226 | dma_play_data.size -= 4; | ||
227 | } | ||
64 | } | 228 | } |
65 | 229 | ||
66 | /* Disconnect the DMA and wait for the FIFO to clear */ | ||
67 | static void play_stop_pcm(void) | 230 | static void play_stop_pcm(void) |
68 | { | 231 | { |
232 | /* Disable interrupt */ | ||
233 | SSI_SIER1 &= ~SSI_SIER_TIE; | ||
234 | |||
235 | /* Wait for FIFO to empty */ | ||
236 | while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0); | ||
237 | |||
238 | /* Disable transmission */ | ||
239 | SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); | ||
240 | |||
241 | /* Do not enable interrupt on unlock */ | ||
242 | dma_play_data.state = 0; | ||
69 | } | 243 | } |
70 | #endif | ||
71 | 244 | ||
72 | void pcm_play_dma_start(const void *addr, size_t size) | 245 | void pcm_play_dma_start(const void *addr, size_t size) |
73 | { | 246 | { |
74 | (void)addr; | 247 | dma_play_data.p = (void *)(((uintptr_t)addr + 3) & ~3); |
75 | (void)size; | 248 | dma_play_data.size = (size & ~3); |
249 | |||
250 | play_start_pcm(); | ||
76 | } | 251 | } |
77 | 252 | ||
78 | void pcm_play_dma_stop(void) | 253 | void pcm_play_dma_stop(void) |
79 | { | 254 | { |
255 | play_stop_pcm(); | ||
256 | dma_play_data.size = 0; | ||
80 | } | 257 | } |
81 | 258 | ||
82 | void pcm_play_dma_pause(bool pause) | 259 | void pcm_play_dma_pause(bool pause) |
83 | { | 260 | { |
84 | (void)pause; | 261 | if (pause) |
262 | { | ||
263 | play_stop_pcm(); | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | uint32_t addr = (uint32_t)dma_play_data.p; | ||
268 | dma_play_data.p = (void *)((addr + 2) & ~3); | ||
269 | dma_play_data.size &= ~3; | ||
270 | play_start_pcm(); | ||
271 | } | ||
85 | } | 272 | } |
86 | 273 | ||
87 | /* Set the pcm frequency hardware will use when play is next started or | 274 | /* Set the pcm frequency hardware will use when play is next started or |
@@ -89,20 +276,23 @@ void pcm_play_dma_pause(bool pause) | |||
89 | hardware here but simply cache it. */ | 276 | hardware here but simply cache it. */ |
90 | void pcm_set_frequency(unsigned int frequency) | 277 | void pcm_set_frequency(unsigned int frequency) |
91 | { | 278 | { |
279 | /* TODO */ | ||
92 | (void)frequency; | 280 | (void)frequency; |
93 | } | 281 | } |
94 | 282 | ||
95 | /* Return the number of bytes waiting - full L-R sample pairs only */ | 283 | /* Return the number of bytes waiting - full L-R sample pairs only */ |
96 | size_t pcm_get_bytes_waiting(void) | 284 | size_t pcm_get_bytes_waiting(void) |
97 | { | 285 | { |
98 | return 0; | 286 | return dma_play_data.size & ~3; |
99 | } | 287 | } |
100 | 288 | ||
101 | /* Return a pointer to the samples and the number of them in *count */ | 289 | /* Return a pointer to the samples and the number of them in *count */ |
102 | const void * pcm_play_dma_get_peak_buffer(int *count) | 290 | const void * pcm_play_dma_get_peak_buffer(int *count) |
103 | { | 291 | { |
104 | (void)count; | 292 | uint32_t addr = (uint32_t)dma_play_data.p; |
105 | return NULL; | 293 | size_t cnt = dma_play_data.size; |
294 | *count = cnt >> 2; | ||
295 | return (void *)((addr + 2) & ~3); | ||
106 | } | 296 | } |
107 | 297 | ||
108 | /* Any recording functionality should be implemented similarly */ | 298 | /* Any recording functionality should be implemented similarly */ |