summaryrefslogtreecommitdiff
path: root/firmware/target/coldfire/pcm-coldfire.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-03-07 06:23:02 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-03-07 06:23:02 +0000
commit633f3880024a2f83a21dd96368aa397940bc9ad6 (patch)
tree7c4319b1715c5c86252fd816764fdf53435163fb /firmware/target/coldfire/pcm-coldfire.c
parent2c94c1afc377c6605772008e6c27b06d579cecf4 (diff)
downloadrockbox-633f3880024a2f83a21dd96368aa397940bc9ad6.tar.gz
rockbox-633f3880024a2f83a21dd96368aa397940bc9ad6.zip
Coldfire targets: Shuffle IRQ levels around to have all interaction between low level audio function calls and DMA be atomic. Make recording and playback independently startable and stoppable so one can be running and not interfere with the other. All tests I can do at the moment check out ok (play, record, play+record, FM radio on iRivers, S/PDIF on H120 (w/running optical on/off), and on-the-fly samplerate changes). Recording tested for well over an hour run and no problems.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12658 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/coldfire/pcm-coldfire.c')
-rw-r--r--firmware/target/coldfire/pcm-coldfire.c134
1 files changed, 106 insertions, 28 deletions
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c
index 2addcb9da8..ef847004ce 100644
--- a/firmware/target/coldfire/pcm-coldfire.c
+++ b/firmware/target/coldfire/pcm-coldfire.c
@@ -55,6 +55,21 @@ static int peaks[4]; /* p-l, p-r, r-l, r-r */
55#define IIS_PLAY IIS2CONFIG 55#define IIS_PLAY IIS2CONFIG
56#endif 56#endif
57 57
58static bool is_playback_monitoring(void)
59{
60 return (IIS_PLAY & (7 << 8)) == (3 << 8);
61}
62
63static void iis_play_reset_if_playback(bool if_playback)
64{
65 bool is_playback = is_playback_monitoring();
66
67 if (is_playback != if_playback)
68 return;
69
70 or_l(IIS_FIFO_RESET, &IIS_PLAY);
71}
72
58#define PLLCR_SET_AUDIO_BITS_DEFPARM \ 73#define PLLCR_SET_AUDIO_BITS_DEFPARM \
59 ((freq_ent[FPARM_CLSEL] << 28) | (1 << 22)) 74 ((freq_ent[FPARM_CLSEL] << 28) | (1 << 22))
60 75
@@ -113,9 +128,10 @@ void pcm_set_frequency(unsigned int frequency)
113} /* pcm_set_frequency */ 128} /* pcm_set_frequency */
114 129
115/* apply audio settings */ 130/* apply audio settings */
116void _pcm_apply_settings(bool clear_reset) 131bool _pcm_apply_settings(bool clear_reset)
117{ 132{
118 static int last_pcm_freq = 0; 133 static int last_pcm_freq = 0;
134 bool did_reset = false;
119 135
120 if (pcm_freq != last_pcm_freq) 136 if (pcm_freq != last_pcm_freq)
121 { 137 {
@@ -125,6 +141,7 @@ void _pcm_apply_settings(bool clear_reset)
125 or_l(IIS_FIFO_RESET, &IIS_PLAY); 141 or_l(IIS_FIFO_RESET, &IIS_PLAY);
126 audiohw_set_frequency(freq_ent[FPARM_FSEL]); 142 audiohw_set_frequency(freq_ent[FPARM_FSEL]);
127 coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); 143 coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM);
144 did_reset = true;
128 } 145 }
129 146
130 SET_IIS_PLAY(IIS_PLAY_DEFPARM | 147 SET_IIS_PLAY(IIS_PLAY_DEFPARM |
@@ -132,12 +149,25 @@ void _pcm_apply_settings(bool clear_reset)
132#if 0 149#if 0
133 logf("IISPLAY: %08X", IIS_PLAY); 150 logf("IISPLAY: %08X", IIS_PLAY);
134#endif 151#endif
152
153 return did_reset;
135} /* _pcm_apply_settings */ 154} /* _pcm_apply_settings */
136 155
137/* This clears the reset bit to enable monitoring immediately */ 156/* This clears the reset bit to enable monitoring immediately if monitoring
157 recording sources or always if playback is in progress - we might be
158 switching samplerates on the fly */
138void pcm_apply_settings(void) 159void pcm_apply_settings(void)
139{ 160{
140 _pcm_apply_settings(true); 161 int level = set_irq_level(DMA_IRQ_LEVEL);
162 bool pbm = is_playback_monitoring();
163 bool kick = (DCR0 & DMA_EEXT) != 0 && pbm;
164
165 /* Clear reset if not playback monitoring or peripheral request is
166 active and playback monitoring */
167 if (_pcm_apply_settings(!pbm || kick) && kick)
168 PDOR3 = 0; /* Kick FIFO out of reset by writing to it */
169
170 set_irq_level(level);
141} /* pcm_apply_settings */ 171} /* pcm_apply_settings */
142 172
143/** DMA **/ 173/** DMA **/
@@ -149,11 +179,17 @@ void pcm_apply_settings(void)
149/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ 179/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
150void pcm_play_dma_start(const void *addr, size_t size) 180void pcm_play_dma_start(const void *addr, size_t size)
151{ 181{
182 int level;
183
152 logf("pcm_play_dma_start"); 184 logf("pcm_play_dma_start");
153 185
154 addr = (void *)((unsigned long)addr & ~3); /* Align data */ 186 addr = (void *)((unsigned long)addr & ~3); /* Align data */
155 size &= ~3; /* Size must be multiple of 4 */ 187 size &= ~3; /* Size must be multiple of 4 */
156 188
189 /* If a tranfer is going, prevent an interrupt while setting up
190 a new one */
191 level = set_irq_level(DMA_IRQ_LEVEL);
192
157 pcm_playing = true; 193 pcm_playing = true;
158 194
159 /* Set up DMA transfer */ 195 /* Set up DMA transfer */
@@ -162,28 +198,38 @@ void pcm_play_dma_start(const void *addr, size_t size)
162 BCR0 = (unsigned long)size; /* Bytes to transfer */ 198 BCR0 = (unsigned long)size; /* Bytes to transfer */
163 199
164 /* Enable the FIFO and force one write to it */ 200 /* Enable the FIFO and force one write to it */
165 pcm_apply_settings(); 201 _pcm_apply_settings(is_playback_monitoring());
166 202
167 DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | 203 DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
168 DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START; 204 DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
205
206 set_irq_level(level);
169} /* pcm_play_dma_start */ 207} /* pcm_play_dma_start */
170 208
171/* Stops the DMA transfer and interrupt */ 209/* Stops the DMA transfer and interrupt */
172void pcm_play_dma_stop(void) 210static void pcm_play_dma_stop_irq(void)
173{ 211{
174#if 0
175 logf("pcm_play_dma_stop");
176#endif
177
178 pcm_playing = false; 212 pcm_playing = false;
179 213
180 DSR0 = 1; 214 DSR0 = 1;
181 DCR0 = 0; 215 DCR0 = 0;
182 216
183 /* Place FIFO in reset condition */ 217 /* Place TX FIFO in reset condition if playback monitoring is on.
184 or_l(IIS_FIFO_RESET, &IIS_PLAY); 218 Recording monitoring something else should not be stopped. */
219 iis_play_reset_if_playback(true);
185 220
186 pcm_playing = false; 221 pcm_playing = false;
222} /* pcm_play_dma_stop_irq */
223
224void pcm_play_dma_stop(void)
225{
226 int level = set_irq_level(DMA_IRQ_LEVEL);
227
228 logf("pcm_play_dma_stop");
229
230 pcm_play_dma_stop_irq();
231
232 set_irq_level(level);
187} /* pcm_play_dma_stop */ 233} /* pcm_play_dma_stop */
188 234
189void pcm_init(void) 235void pcm_init(void)
@@ -236,8 +282,8 @@ void pcm_init(void)
236 (DAC should be at zero point now). */ 282 (DAC should be at zero point now). */
237 audiohw_mute(false); 283 audiohw_mute(false);
238 284
239 /* Enable interrupt at level 7, priority 0 */ 285 /* Enable interrupt at level 6, priority 0 */
240 ICR6 = (7 << 2); 286 ICR6 = (6 << 2);
241 and_l(~(1 << 14), &IMR); /* bit 14 is DMA0 */ 287 and_l(~(1 << 14), &IMR); /* bit 14 is DMA0 */
242} /* pcm_init */ 288} /* pcm_init */
243 289
@@ -292,7 +338,7 @@ void DMA0(void)
292#endif 338#endif
293 } 339 }
294 340
295 pcm_play_dma_stop(); 341 pcm_play_dma_stop_irq();
296} /* DMA0 */ 342} /* DMA0 */
297 343
298/**************************************************************************** 344/****************************************************************************
@@ -300,17 +346,21 @@ void DMA0(void)
300 **/ 346 **/
301void pcm_rec_dma_start(void *addr, size_t size) 347void pcm_rec_dma_start(void *addr, size_t size)
302{ 348{
349 int level;
303 logf("pcm_rec_dma_start"); 350 logf("pcm_rec_dma_start");
304 351
305 addr = (void *)((unsigned long)addr & ~3); /* Align data */ 352 addr = (void *)((unsigned long)addr & ~3); /* Align data */
306 size &= ~3; /* Size must be multiple of 4 */ 353 size &= ~3; /* Size must be multiple of 4 */
307 354
355 /* No DMA1 interrupts while setting up a new transfer */
356 level = set_irq_level(DMA_IRQ_LEVEL);
357
308 pcm_recording = true; 358 pcm_recording = true;
309 359
310 and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); 360 and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
311 /* Clear reset bit if the source is not set to monitor playback 361 /* Clear TX FIFO reset bit if the source is not set to monitor playback
312 otherwise maintain independence between playback and recording. */ 362 otherwise maintain independence between playback and recording. */
313 _pcm_apply_settings((IIS_PLAY & (7 << 8)) != (3 << 8)); 363 _pcm_apply_settings(!is_playback_monitoring());
314 364
315 /* Start the DMA transfer.. */ 365 /* Start the DMA transfer.. */
316#ifdef HAVE_SPDIF_IN 366#ifdef HAVE_SPDIF_IN
@@ -324,21 +374,35 @@ void pcm_rec_dma_start(void *addr, size_t size)
324 374
325 DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC | 375 DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC |
326 DMA_DSIZE(DMA_SIZE_LINE) /* | DMA_START */; 376 DMA_DSIZE(DMA_SIZE_LINE) /* | DMA_START */;
377
378 set_irq_level(level);
327} /* pcm_dma_start */ 379} /* pcm_dma_start */
328 380
329void pcm_rec_dma_stop(void) 381static void pcm_rec_dma_stop_irq(void)
330{ 382{
331 logf("pcm_rec_dma_stop");
332
333 DSR1 = 1; /* Clear interrupt */ 383 DSR1 = 1; /* Clear interrupt */
334 DCR1 = 0; 384 DCR1 = 0;
335
336 pcm_recording = false; 385 pcm_recording = false;
337 or_l(PDIR2_FIFO_RESET, &DATAINCONTROL); 386 or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
338} /* pcm_dma_stop */ 387
388 iis_play_reset_if_playback(false);
389} /* pcm_rec_dma_stop_irq */
390
391void pcm_rec_dma_stop(void)
392{
393 int level = set_irq_level(DMA_IRQ_LEVEL);
394
395 logf("pcm_rec_dma_stop");
396
397 pcm_rec_dma_stop_irq();
398
399 set_irq_level(level);
400} /* pcm_rec_dma_stop */
339 401
340void pcm_init_recording(void) 402void pcm_init_recording(void)
341{ 403{
404 int level = set_irq_level(DMA_IRQ_LEVEL);
405
342 logf("pcm_init_recording"); 406 logf("pcm_init_recording");
343 407
344 pcm_recording = false; 408 pcm_recording = false;
@@ -349,21 +413,27 @@ void pcm_init_recording(void)
349 and_l(0xffff00ff, &DMAROUTE); 413 and_l(0xffff00ff, &DMAROUTE);
350 or_l(DMA1_REQ_AUDIO_2, &DMAROUTE); 414 or_l(DMA1_REQ_AUDIO_2, &DMAROUTE);
351 415
352 pcm_rec_dma_stop(); 416 pcm_rec_dma_stop_irq();
353 417
354 ICR7 = (7 << 2); /* Enable interrupt at level 7, priority 0 */ 418 ICR7 = (6 << 2) | (1 << 0); /* Enable interrupt at level 6, priority 1 */
355 and_l(~(1 << 15), &IMR); /* bit 15 is DMA1 */ 419 and_l(~(1 << 15), &IMR); /* bit 15 is DMA1 */
420
421 set_irq_level(level);
356} /* pcm_init_recording */ 422} /* pcm_init_recording */
357 423
358void pcm_close_recording(void) 424void pcm_close_recording(void)
359{ 425{
426 int level = set_irq_level(DMA_IRQ_LEVEL);
427
360 logf("pcm_close_recording"); 428 logf("pcm_close_recording");
361 429
362 pcm_rec_dma_stop(); 430 pcm_rec_dma_stop_irq();
363 431
364 and_l(0xffff00ff, &DMAROUTE); 432 and_l(0xffff00ff, &DMAROUTE);
365 ICR7 = 0x00; /* Disable interrupt */ 433 ICR7 = 0x00; /* Disable interrupt */
366 or_l((1 << 15), &IMR); /* bit 15 is DMA1 */ 434 or_l((1 << 15), &IMR); /* bit 15 is DMA1 */
435
436 set_irq_level(level);
367} /* pcm_close_recording */ 437} /* pcm_close_recording */
368 438
369/* DMA1 Interrupt is called when the DMA has finished transfering a chunk 439/* DMA1 Interrupt is called when the DMA has finished transfering a chunk
@@ -409,10 +479,10 @@ void DMA1(void)
409 logf("DMA1 done:%04x %d", res, status); 479 logf("DMA1 done:%04x %d", res, status);
410#endif 480#endif
411 /* Finished recording */ 481 /* Finished recording */
412 pcm_rec_dma_stop(); 482 pcm_rec_dma_stop_irq();
413} /* DMA1 */ 483} /* DMA1 */
414 484
415/* Continue transferring data in */ 485/* Continue transferring data in - call from interrupt callback */
416void pcm_record_more(void *start, size_t size) 486void pcm_record_more(void *start, size_t size)
417{ 487{
418 rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ 488 rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
@@ -431,15 +501,23 @@ void pcm_mute(bool mute)
431void pcm_play_pause_pause(void) 501void pcm_play_pause_pause(void)
432{ 502{
433 /* Disable DMA peripheral request. */ 503 /* Disable DMA peripheral request. */
504 int level = set_irq_level(DMA_IRQ_LEVEL);
505
434 and_l(~DMA_EEXT, &DCR0); 506 and_l(~DMA_EEXT, &DCR0);
435 or_l(IIS_FIFO_RESET, &IIS_PLAY); 507 iis_play_reset_if_playback(true);
508
509 set_irq_level(level);
436} /* pcm_play_pause_pause */ 510} /* pcm_play_pause_pause */
437 511
438void pcm_play_pause_unpause(void) 512void pcm_play_pause_unpause(void)
439{ 513{
514 int level = set_irq_level(DMA_IRQ_LEVEL);
515
440 /* Enable the FIFO and force one write to it */ 516 /* Enable the FIFO and force one write to it */
441 pcm_apply_settings(); 517 _pcm_apply_settings(is_playback_monitoring());
442 or_l(DMA_EEXT | DMA_START, &DCR0); 518 or_l(DMA_EEXT | DMA_START, &DCR0);
519
520 set_irq_level(level);
443} /* pcm_play_pause_unpause */ 521} /* pcm_play_pause_unpause */
444 522
445/** 523/**