summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-02-23 08:14:46 -0500
committerMichael Sevakis <jethead71@rockbox.org>2012-03-03 07:23:38 +0100
commit286a4c5caa1945c8d1cb365a3d90fb09d5700cb2 (patch)
tree4835f46d16ec78d035ec9f49333079fe618384c1
parent3f82f3aca14eb954e55f761721ffdd2684f0e812 (diff)
downloadrockbox-286a4c5caa1945c8d1cb365a3d90fb09d5700cb2.tar.gz
rockbox-286a4c5caa1945c8d1cb365a3d90fb09d5700cb2.zip
Revise the PCM callback system after adding multichannel audio.
Additional status callback is added to pcm_play/rec_data instead of using a special function to set it. Status includes DMA error reporting to the status callback. Playback and recording callback become more alike except playback uses "const void **addr" (because the data should not be altered) and recording uses "void **addr". "const" is put in place throughout where appropriate. Most changes are fairly trivial. One that should be checked in particular because it isn't so much is telechips, if anyone cares to bother. PP5002 is not so trivial either but that tested as working. Change-Id: I4928d69b3b3be7fb93e259f81635232df9bd1df2 Reviewed-on: http://gerrit.rockbox.org/166 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested-by: Michael Sevakis <jethead71@rockbox.org>
-rw-r--r--apps/beep.c4
-rw-r--r--apps/pcmbuf.c2
-rw-r--r--apps/plugin.h13
-rw-r--r--apps/plugins/beatbox/beatbox.c8
-rw-r--r--apps/plugins/doom/i_sound.c6
-rw-r--r--apps/plugins/fft/fft.c4
-rw-r--r--apps/plugins/metronome.c2
-rw-r--r--apps/plugins/midi/midiplay.c14
-rw-r--r--apps/plugins/mikmod/mikmod.c10
-rw-r--r--apps/plugins/mpegplayer/pcm_output.c2
-rw-r--r--apps/plugins/pacbox/pacbox.c6
-rw-r--r--apps/plugins/pdbox/PDa/src/s_audio_rockbox.c10
-rw-r--r--apps/plugins/pitch_detector.c7
-rw-r--r--apps/plugins/rockboy/rbsound.c6
-rw-r--r--apps/plugins/test_sampr.c6
-rw-r--r--apps/plugins/zxbox/spsound.c9
-rw-r--r--apps/recorder/pcm_record.c36
-rw-r--r--apps/voice_thread.c17
-rw-r--r--firmware/asm/arm/pcm-mixer-armv4.c8
-rw-r--r--firmware/asm/arm/pcm-mixer-armv5.c6
-rw-r--r--firmware/asm/arm/pcm-mixer-armv6.c6
-rw-r--r--firmware/asm/m68k/pcm-mixer.c6
-rw-r--r--firmware/asm/pcm-mixer.c8
-rw-r--r--firmware/export/pcm-internal.h51
-rw-r--r--firmware/export/pcm.h31
-rw-r--r--firmware/export/pcm_mixer.h4
-rw-r--r--firmware/export/pp5002.h2
-rw-r--r--firmware/pcm.c115
-rw-r--r--firmware/pcm_mixer.c35
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c20
-rw-r--r--firmware/target/arm/imx233/pcm-imx233.c9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c119
-rw-r--r--firmware/target/arm/pcm-telechips.c62
-rw-r--r--firmware/target/arm/pnx0101/pcm-pnx0101.c14
-rw-r--r--firmware/target/arm/pp/pcm-pp.c627
-rw-r--r--firmware/target/arm/rk27xx/pcm-rk27xx.c8
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c8
-rw-r--r--firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c8
-rw-r--r--firmware/target/arm/s5l8700/pcm-s5l8700.c9
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c4
-rw-r--r--firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c8
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c8
-rw-r--r--firmware/target/coldfire/pcm-coldfire.c36
-rw-r--r--firmware/target/hosted/android/pcm-android.c16
-rw-r--r--firmware/target/hosted/maemo/pcm-gstreamer.c7
-rw-r--r--firmware/target/hosted/pcm-alsa.c8
-rw-r--r--firmware/target/hosted/sdl/pcm-sdl.c66
-rw-r--r--firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c12
48 files changed, 765 insertions, 718 deletions
diff --git a/apps/beep.c b/apps/beep.c
index d3345afdf9..3b02e5d8a3 100644
--- a/apps/beep.c
+++ b/apps/beep.c
@@ -46,7 +46,7 @@ static int16_t beep_buf[BEEP_BUF_COUNT*2] IBSS_ATTR __attribute__((aligned(4)));
46/* Callback to generate the beep frames - also don't want inlining of 46/* Callback to generate the beep frames - also don't want inlining of
47 call below in beep_play */ 47 call below in beep_play */
48static void __attribute__((noinline)) 48static void __attribute__((noinline))
49beep_get_more(unsigned char **start, size_t *size) 49beep_get_more(const void **start, size_t *size)
50{ 50{
51 int count = beep_count; 51 int count = beep_count;
52 52
@@ -87,7 +87,7 @@ void beep_play(unsigned int frequency, unsigned int duration,
87#endif 87#endif
88 88
89 /* If it fits - avoid cb overhead */ 89 /* If it fits - avoid cb overhead */
90 unsigned char *start; 90 const void *start;
91 size_t size; 91 size_t size;
92 92
93 /* Generate first frame here */ 93 /* Generate first frame here */
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index e0bfa1f62a..7ba4eeef8e 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -694,7 +694,7 @@ void pcmbuf_start_track_change(enum pcm_track_change_type type)
694/** Playback */ 694/** Playback */
695 695
696/* PCM driver callback */ 696/* PCM driver callback */
697static void pcmbuf_pcm_callback(unsigned char **start, size_t *size) 697static void pcmbuf_pcm_callback(const void **start, size_t *size)
698{ 698{
699 /*- Process the chunk that just finished -*/ 699 /*- Process the chunk that just finished -*/
700 size_t index = chunk_ridx; 700 size_t index = chunk_ridx;
diff --git a/apps/plugin.h b/apps/plugin.h
index 2fb085de6d..e07ec92c08 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -153,12 +153,12 @@ void* plugin_get_buffer(size_t *buffer_size);
153#define PLUGIN_MAGIC 0x526F634B /* RocK */ 153#define PLUGIN_MAGIC 0x526F634B /* RocK */
154 154
155/* increase this every time the api struct changes */ 155/* increase this every time the api struct changes */
156#define PLUGIN_API_VERSION 216 156#define PLUGIN_API_VERSION 217
157 157
158/* update this to latest version if a change to the api struct breaks 158/* update this to latest version if a change to the api struct breaks
159 backwards compatibility (and please take the opportunity to sort in any 159 backwards compatibility (and please take the opportunity to sort in any
160 new function which are "waiting" at the end of the function table) */ 160 new function which are "waiting" at the end of the function table) */
161#define PLUGIN_MIN_API_VERSION 216 161#define PLUGIN_MIN_API_VERSION 217
162 162
163/* plugin return codes */ 163/* plugin return codes */
164/* internal returns start at 0x100 to make exit(1..255) work */ 164/* internal returns start at 0x100 to make exit(1..255) work */
@@ -651,7 +651,8 @@ struct plugin_api {
651 const unsigned long *hw_freq_sampr; 651 const unsigned long *hw_freq_sampr;
652 void (*pcm_apply_settings)(void); 652 void (*pcm_apply_settings)(void);
653 void (*pcm_play_data)(pcm_play_callback_type get_more, 653 void (*pcm_play_data)(pcm_play_callback_type get_more,
654 unsigned char* start, size_t size); 654 pcm_status_callback_type status_cb,
655 const void *start, size_t size);
655 void (*pcm_play_stop)(void); 656 void (*pcm_play_stop)(void);
656 void (*pcm_set_frequency)(unsigned int frequency); 657 void (*pcm_set_frequency)(unsigned int frequency);
657 bool (*pcm_is_playing)(void); 658 bool (*pcm_is_playing)(void);
@@ -669,6 +670,7 @@ struct plugin_api {
669 void (*pcm_init_recording)(void); 670 void (*pcm_init_recording)(void);
670 void (*pcm_close_recording)(void); 671 void (*pcm_close_recording)(void);
671 void (*pcm_record_data)(pcm_rec_callback_type more_ready, 672 void (*pcm_record_data)(pcm_rec_callback_type more_ready,
673 pcm_status_callback_type status_cb,
672 void *start, size_t size); 674 void *start, size_t size);
673 void (*pcm_stop_recording)(void); 675 void (*pcm_stop_recording)(void);
674 void (*pcm_calculate_rec_peaks)(int *left, int *right); 676 void (*pcm_calculate_rec_peaks)(int *left, int *right);
@@ -689,12 +691,13 @@ struct plugin_api {
689 int (*dsp_output_count)(struct dsp_config *dsp, int count); 691 int (*dsp_output_count)(struct dsp_config *dsp, int count);
690 692
691 enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel); 693 enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel);
692 void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, int *count); 694 const void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel,
695 int *count);
693 void (*mixer_channel_calculate_peaks)(enum pcm_mixer_channel channel, 696 void (*mixer_channel_calculate_peaks)(enum pcm_mixer_channel channel,
694 int *left, int *right); 697 int *left, int *right);
695 void (*mixer_channel_play_data)(enum pcm_mixer_channel channel, 698 void (*mixer_channel_play_data)(enum pcm_mixer_channel channel,
696 pcm_play_callback_type get_more, 699 pcm_play_callback_type get_more,
697 unsigned char *start, size_t size); 700 const void *start, size_t size);
698 void (*mixer_channel_play_pause)(enum pcm_mixer_channel channel, bool play); 701 void (*mixer_channel_play_pause)(enum pcm_mixer_channel channel, bool play);
699 void (*mixer_channel_stop)(enum pcm_mixer_channel channel); 702 void (*mixer_channel_stop)(enum pcm_mixer_channel channel);
700 void (*mixer_channel_set_amplitude)(enum pcm_mixer_channel channel, 703 void (*mixer_channel_set_amplitude)(enum pcm_mixer_channel channel,
diff --git a/apps/plugins/beatbox/beatbox.c b/apps/plugins/beatbox/beatbox.c
index 8ecbabd1e5..8c7413cc99 100644
--- a/apps/plugins/beatbox/beatbox.c
+++ b/apps/plugins/beatbox/beatbox.c
@@ -509,7 +509,7 @@ void redrawScreen(unsigned char force)
509 rb->lcd_update(); 509 rb->lcd_update();
510} 510}
511 511
512void get_more(unsigned char** start, size_t* size) 512void get_more(const void** start, size_t* size)
513{ 513{
514#ifndef SYNC 514#ifndef SYNC
515 if(lastswap!=swap) 515 if(lastswap!=swap)
@@ -523,10 +523,10 @@ void get_more(unsigned char** start, size_t* size)
523 523
524 *size = BUF_SIZE*sizeof(short); 524 *size = BUF_SIZE*sizeof(short);
525#ifndef SYNC 525#ifndef SYNC
526 *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); 526 *start = swap ? gmbuf : gmbuf + BUF_SIZE;
527 swap=!swap; 527 swap=!swap;
528#else 528#else
529 *start = (unsigned char*)(gmbuf); 529 *start = gmbuf;
530#endif 530#endif
531} 531}
532 532
@@ -537,7 +537,7 @@ int beatboxmain()
537 537
538 numberOfSamples=44100/10; 538 numberOfSamples=44100/10;
539 synthbuf(); 539 synthbuf();
540 rb->pcm_play_data(&get_more, NULL, 0); 540 rb->pcm_play_data(&get_more, NULL, NULL, 0);
541 541
542 rb->lcd_set_background(0x000000); 542 rb->lcd_set_background(0x000000);
543 rb->lcd_clear_display(); 543 rb->lcd_clear_display();
diff --git a/apps/plugins/doom/i_sound.c b/apps/plugins/doom/i_sound.c
index 2d7b592818..bdf70c1215 100644
--- a/apps/plugins/doom/i_sound.c
+++ b/apps/plugins/doom/i_sound.c
@@ -457,11 +457,11 @@ void I_UpdateSound( void )
457// only output be done asynchronous? 457// only output be done asynchronous?
458// 458//
459 459
460void get_more(unsigned char** start, size_t* size) 460void get_more(const void** start, size_t* size)
461{ 461{
462 I_UpdateSound(); // Force sound update 462 I_UpdateSound(); // Force sound update
463 463
464 *start = (unsigned char*)(mixbuffer); 464 *start = mixbuffer;
465 *size = SAMPLECOUNT*2*sizeof(short); 465 *size = SAMPLECOUNT*2*sizeof(short);
466} 466}
467 467
@@ -471,7 +471,7 @@ void I_SubmitSound(void)
471 if (!enable_sound) 471 if (!enable_sound)
472 return; 472 return;
473 473
474 rb->pcm_play_data(&get_more, NULL, 0); 474 rb->pcm_play_data(&get_more, NULL, NULL, 0);
475} 475}
476 476
477void I_ShutdownSound(void) 477void I_ShutdownSound(void)
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index 1c4e1b4c20..f7d8943576 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -1186,8 +1186,8 @@ static inline bool fft_init_fft_lib(void)
1186static inline bool fft_get_fft(void) 1186static inline bool fft_get_fft(void)
1187{ 1187{
1188 int count; 1188 int count;
1189 int16_t *value = 1189 const int16_t *value =
1190 (int16_t *) rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count); 1190 rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count);
1191 /* This block can introduce discontinuities in our data. Meaning, the 1191 /* This block can introduce discontinuities in our data. Meaning, the
1192 * FFT will not be done a continuous segment of the signal. Which can 1192 * FFT will not be done a continuous segment of the signal. Which can
1193 * be bad. Or not. 1193 * be bad. Or not.
diff --git a/apps/plugins/metronome.c b/apps/plugins/metronome.c
index da035a7688..297405571f 100644
--- a/apps/plugins/metronome.c
+++ b/apps/plugins/metronome.c
@@ -721,7 +721,7 @@ static void prepare_tock(void)
721 721
722static void play_tock(void) 722static void play_tock(void)
723{ 723{
724 rb->pcm_play_data(NULL,(unsigned char *)sndbuf,sizeof(sndbuf)); 724 rb->pcm_play_data(NULL, NULL, sndbuf, sizeof(sndbuf));
725} 725}
726 726
727#endif /* CONFIG_CODEC != SWCODEC */ 727#endif /* CONFIG_CODEC != SWCODEC */
diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c
index 40b4f1c83c..1412d4c742 100644
--- a/apps/plugins/midi/midiplay.c
+++ b/apps/plugins/midi/midiplay.c
@@ -319,7 +319,7 @@ static inline void synthbuf(void)
319 samples_in_buf = BUF_SIZE-i; 319 samples_in_buf = BUF_SIZE-i;
320} 320}
321 321
322static void get_more(unsigned char** start, size_t* size) 322static void get_more(const void** start, size_t* size)
323{ 323{
324#ifndef SYNC 324#ifndef SYNC
325 if(lastswap != swap) 325 if(lastswap != swap)
@@ -333,10 +333,10 @@ static void get_more(unsigned char** start, size_t* size)
333 333
334 *size = samples_in_buf*sizeof(int32_t); 334 *size = samples_in_buf*sizeof(int32_t);
335#ifndef SYNC 335#ifndef SYNC
336 *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); 336 *start = swap ? gmbuf : gmbuf + BUF_SIZE;
337 swap = !swap; 337 swap = !swap;
338#else 338#else
339 *start = (unsigned char*)(gmbuf); 339 *start = gmbuf;
340#endif 340#endif
341} 341}
342 342
@@ -396,7 +396,7 @@ static int midimain(const void * filename)
396 samples_this_second = 0; 396 samples_this_second = 0;
397 397
398 synthbuf(); 398 synthbuf();
399 rb->pcm_play_data(&get_more, NULL, 0); 399 rb->pcm_play_data(&get_more, NULL, NULL, 0);
400 400
401 while (!quit) 401 while (!quit)
402 { 402 {
@@ -445,7 +445,7 @@ static int midimain(const void * filename)
445 seekBackward(5); 445 seekBackward(5);
446 midi_debug("Rewind to %d:%02d\n", playing_time/60, playing_time%60); 446 midi_debug("Rewind to %d:%02d\n", playing_time/60, playing_time%60);
447 if (is_playing) 447 if (is_playing)
448 rb->pcm_play_data(&get_more, NULL, 0); 448 rb->pcm_play_data(&get_more, NULL, NULL, 0);
449 break; 449 break;
450 } 450 }
451 451
@@ -455,7 +455,7 @@ static int midimain(const void * filename)
455 seekForward(5); 455 seekForward(5);
456 midi_debug("Skip to %d:%02d\n", playing_time/60, playing_time%60); 456 midi_debug("Skip to %d:%02d\n", playing_time/60, playing_time%60);
457 if (is_playing) 457 if (is_playing)
458 rb->pcm_play_data(&get_more, NULL, 0); 458 rb->pcm_play_data(&get_more, NULL, NULL, 0);
459 break; 459 break;
460 } 460 }
461 461
@@ -470,7 +470,7 @@ static int midimain(const void * filename)
470 { 470 {
471 midi_debug("Playing from %d:%02d\n", playing_time/60, playing_time%60); 471 midi_debug("Playing from %d:%02d\n", playing_time/60, playing_time%60);
472 is_playing = true; 472 is_playing = true;
473 rb->pcm_play_data(&get_more, NULL, 0); 473 rb->pcm_play_data(&get_more, NULL, NULL, 0);
474 } 474 }
475 break; 475 break;
476 } 476 }
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
index a7eeb5019f..eb3be13140 100644
--- a/apps/plugins/mikmod/mikmod.c
+++ b/apps/plugins/mikmod/mikmod.c
@@ -268,7 +268,7 @@ static inline void synthbuf(void)
268 VC_WriteBytes(outptr, BUF_SIZE); 268 VC_WriteBytes(outptr, BUF_SIZE);
269} 269}
270 270
271void get_more(unsigned char** start, size_t* size) 271void get_more(const void** start, size_t* size)
272{ 272{
273#ifndef SYNC 273#ifndef SYNC
274 if (lastswap != swap) 274 if (lastswap != swap)
@@ -282,10 +282,10 @@ void get_more(unsigned char** start, size_t* size)
282 282
283 *size = BUF_SIZE; 283 *size = BUF_SIZE;
284#ifndef SYNC 284#ifndef SYNC
285 *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); 285 *start = swap ? gmbuf : gmbuf + BUF_SIZE;
286 swap = !swap; 286 swap = !swap;
287#else 287#else
288 *start = (unsigned char*)(gmbuf); 288 *start = gmbuf;
289#endif 289#endif
290} 290}
291 291
@@ -660,7 +660,7 @@ int playfile(char* filename)
660 { 660 {
661 display = DISPLAY_INFO; 661 display = DISPLAY_INFO;
662 Player_Start(module); 662 Player_Start(module);
663 rb->pcm_play_data(&get_more, NULL, 0); 663 rb->pcm_play_data(&get_more, NULL, NULL, 0);
664 } 664 }
665 665
666#ifdef HAVE_ADJUSTABLE_CPU_FREQ 666#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -804,7 +804,7 @@ int playfile(char* filename)
804 } 804 }
805 else 805 else
806 { 806 {
807 rb->pcm_play_data(&get_more, NULL, 0); 807 rb->pcm_play_data(&get_more, NULL, NULL, 0);
808 } 808 }
809 Player_TogglePause(); 809 Player_TogglePause();
810 break; 810 break;
diff --git a/apps/plugins/mpegplayer/pcm_output.c b/apps/plugins/mpegplayer/pcm_output.c
index 8db9531049..8694ae4a69 100644
--- a/apps/plugins/mpegplayer/pcm_output.c
+++ b/apps/plugins/mpegplayer/pcm_output.c
@@ -85,7 +85,7 @@ static inline ssize_t pcm_output_bytes_free(void)
85} 85}
86 86
87/* Audio DMA handler */ 87/* Audio DMA handler */
88static void get_more(unsigned char **start, size_t *size) 88static void get_more(const void **start, size_t *size)
89{ 89{
90 ssize_t sz; 90 ssize_t sz;
91 91
diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c
index 5165ff3047..efba47b576 100644
--- a/apps/plugins/pacbox/pacbox.c
+++ b/apps/plugins/pacbox/pacbox.c
@@ -286,7 +286,7 @@ static int16_t raw_buf[NBSAMPLES] IBSS_ATTR;
286/* 286/*
287 Audio callback 287 Audio callback
288 */ 288 */
289static void get_more(unsigned char **start, size_t *size) 289static void get_more(const void **start, size_t *size)
290{ 290{
291 int32_t *out, *outend; 291 int32_t *out, *outend;
292 int16_t *raw; 292 int16_t *raw;
@@ -306,7 +306,7 @@ static void get_more(unsigned char **start, size_t *size)
306 } 306 }
307 while (out < outend); 307 while (out < outend);
308 308
309 *start = (unsigned char *)sound_buf; 309 *start = sound_buf;
310 *size = NBSAMPLES*sizeof(sound_buf[0]); 310 *size = NBSAMPLES*sizeof(sound_buf[0]);
311} 311}
312 312
@@ -339,7 +339,7 @@ static void start_sound(void)
339 wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]); 339 wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]);
340 340
341 rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]); 341 rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]);
342 rb->pcm_play_data(get_more, NULL, 0); 342 rb->pcm_play_data(get_more, NULL, NULL, 0);
343 343
344 sound_playing = true; 344 sound_playing = true;
345} 345}
diff --git a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c
index 76a50fa075..6571f74edf 100644
--- a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c
+++ b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c
@@ -26,7 +26,7 @@
26#include "s_stuff.h" 26#include "s_stuff.h"
27 27
28/* Declare functions that go to IRAM. */ 28/* Declare functions that go to IRAM. */
29void pdbox_get_more(unsigned char** start, size_t* size) ICODE_ATTR; 29void pdbox_get_more(const void** start, size_t* size) ICODE_ATTR;
30int rockbox_send_dacs(void) ICODE_ATTR; 30int rockbox_send_dacs(void) ICODE_ATTR;
31 31
32/* Extern variables. */ 32/* Extern variables. */
@@ -90,12 +90,12 @@ void rockbox_close_audio(void)
90} 90}
91 91
92/* Rockbox audio callback. */ 92/* Rockbox audio callback. */
93void pdbox_get_more(unsigned char** start, size_t* size) 93void pdbox_get_more(const void** start, size_t* size)
94{ 94{
95 if(outbuf_fill > 0) 95 if(outbuf_fill > 0)
96 { 96 {
97 /* Store output data address and size. */ 97 /* Store output data address and size. */
98 *start = (unsigned char*) outbuf[outbuf_tail].data; 98 *start = outbuf[outbuf_tail].data;
99 *size = sizeof(outbuf[outbuf_tail].data); 99 *size = sizeof(outbuf[outbuf_tail].data);
100 100
101 /* Free this part of output buffer. */ 101 /* Free this part of output buffer. */
@@ -116,8 +116,6 @@ void pdbox_get_more(unsigned char** start, size_t* size)
116 playing = false; 116 playing = false;
117 117
118 /* Nothing to play. */ 118 /* Nothing to play. */
119 *start = NULL;
120 *size = 0;
121 } 119 }
122} 120}
123 121
@@ -185,7 +183,7 @@ int rockbox_send_dacs(void)
185 if(!playing && outbuf_fill > 0) 183 if(!playing && outbuf_fill > 0)
186 { 184 {
187 /* Start playing. */ 185 /* Start playing. */
188 rb->pcm_play_data(pdbox_get_more, NULL, 0); 186 rb->pcm_play_data(pdbox_get_more, NULL, NULL, 0);
189 187
190 /* Set status flag. */ 188 /* Set status flag. */
191 playing = true; 189 playing = true;
diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c
index c30d48a025..4ae43b3236 100644
--- a/apps/plugins/pitch_detector.c
+++ b/apps/plugins/pitch_detector.c
@@ -952,7 +952,7 @@ static uint32_t ICODE_ATTR buffer_magnitude(int16_t *input)
952} 952}
953 953
954/* Stop the recording when the buffer is full */ 954/* Stop the recording when the buffer is full */
955static void recording_callback(int status, void **start, size_t *size) 955static void recording_callback(void **start, size_t *size)
956{ 956{
957 int tail = audio_tail ^ 1; 957 int tail = audio_tail ^ 1;
958 958
@@ -963,8 +963,6 @@ static void recording_callback(int status, void **start, size_t *size)
963 /* Always record full buffer, even if not required */ 963 /* Always record full buffer, even if not required */
964 *start = audio_data[tail]; 964 *start = audio_data[tail];
965 *size = BUFFER_SIZE * sizeof (int16_t); 965 *size = BUFFER_SIZE * sizeof (int16_t);
966
967 (void)status;
968} 966}
969#endif /* SIMULATOR */ 967#endif /* SIMULATOR */
970 968
@@ -973,7 +971,8 @@ static void record_data(void)
973{ 971{
974#ifndef SIMULATOR 972#ifndef SIMULATOR
975 /* Always record full buffer, even if not required */ 973 /* Always record full buffer, even if not required */
976 rb->pcm_record_data(recording_callback, audio_data[audio_tail], 974 rb->pcm_record_data(recording_callback, NULL,
975 audio_data[audio_tail],
977 BUFFER_SIZE * sizeof (int16_t)); 976 BUFFER_SIZE * sizeof (int16_t));
978#endif 977#endif
979} 978}
diff --git a/apps/plugins/rockboy/rbsound.c b/apps/plugins/rockboy/rbsound.c
index c0d0277915..628879b4b7 100644
--- a/apps/plugins/rockboy/rbsound.c
+++ b/apps/plugins/rockboy/rbsound.c
@@ -16,10 +16,10 @@ static unsigned short *buf=0, *hwbuf=0;
16 16
17static bool newly_started; 17static bool newly_started;
18 18
19static void get_more(unsigned char** start, size_t* size) 19static void get_more(const void** start, size_t* size)
20{ 20{
21 memcpy(hwbuf, &buf[pcm.len*doneplay], BUF_SIZE*sizeof(short)); 21 memcpy(hwbuf, &buf[pcm.len*doneplay], BUF_SIZE*sizeof(short));
22 *start = (unsigned char*)(hwbuf); 22 *start = hwbuf;
23 *size = BUF_SIZE*sizeof(short); 23 *size = BUF_SIZE*sizeof(short);
24 doneplay=1; 24 doneplay=1;
25} 25}
@@ -76,7 +76,7 @@ int rockboy_pcm_submit(void)
76 76
77 if(newly_started) 77 if(newly_started)
78 { 78 {
79 rb->pcm_play_data(&get_more,NULL,0); 79 rb->pcm_play_data(&get_more, NULL, NULL,0);
80 newly_started = false; 80 newly_started = false;
81 } 81 }
82 82
diff --git a/apps/plugins/test_sampr.c b/apps/plugins/test_sampr.c
index db8301bba6..fc2f695c70 100644
--- a/apps/plugins/test_sampr.c
+++ b/apps/plugins/test_sampr.c
@@ -90,13 +90,13 @@ static int16_t ICODE_ATTR fsin(uint32_t phase)
90} 90}
91 91
92/* ISR handler to get next block of data */ 92/* ISR handler to get next block of data */
93static void get_more(unsigned char **start, size_t *size) 93static void get_more(const void **start, size_t *size)
94{ 94{
95 /* Free previous buffer */ 95 /* Free previous buffer */
96 output_head += output_step; 96 output_head += output_step;
97 output_step = 0; 97 output_step = 0;
98 98
99 *start = (unsigned char *)output_buf[output_head & OUTPUT_CHUNK_MASK]; 99 *start = output_buf[output_head & OUTPUT_CHUNK_MASK];
100 *size = OUTPUT_CHUNK_SIZE; 100 *size = OUTPUT_CHUNK_SIZE;
101 101
102 /* Keep repeating previous if source runs low */ 102 /* Keep repeating previous if source runs low */
@@ -236,7 +236,7 @@ static void play_tone(bool volume_set)
236 IF_PRIO(, PRIORITY_PLAYBACK) 236 IF_PRIO(, PRIORITY_PLAYBACK)
237 IF_COP(, CPU)); 237 IF_COP(, CPU));
238 238
239 rb->pcm_play_data(get_more, NULL, 0); 239 rb->pcm_play_data(get_more, NULL, NULL, 0);
240 240
241#ifndef HAVE_VOLUME_IN_LIST 241#ifndef HAVE_VOLUME_IN_LIST
242 if (volume_set) 242 if (volume_set)
diff --git a/apps/plugins/zxbox/spsound.c b/apps/plugins/zxbox/spsound.c
index 8b3aa3d84f..9d3eefa87f 100644
--- a/apps/plugins/zxbox/spsound.c
+++ b/apps/plugins/zxbox/spsound.c
@@ -189,12 +189,11 @@ void autoclose_sound(void)
189 } 189 }
190#endif 190#endif
191} 191}
192static void get_more(unsigned char** start, size_t* size) 192static void get_more(const void** start, size_t* size)
193{ 193{
194 doneplay = 1; 194 doneplay = 1;
195 rb->pcm_play_stop(); 195 (void)start;
196 (void)*start; 196 (void)size;
197 (void)*size;
198} 197}
199 198
200/* sp_sound_buf is Unsigned 8 bit, Rate 8000 Hz, Mono */ 199/* sp_sound_buf is Unsigned 8 bit, Rate 8000 Hz, Mono */
@@ -219,7 +218,7 @@ static void write_buf(void){
219 = my_buf[j+10] = my_buf[j+11] \ 218 = my_buf[j+10] = my_buf[j+11] \
220 = (((byte)sp_sound_buf[i])<<8) >> settings.volume; 219 = (((byte)sp_sound_buf[i])<<8) >> settings.volume;
221 220
222 rb->pcm_play_data(&get_more,(unsigned char*)(my_buf),TMNUM*4*3*2); 221 rb->pcm_play_data(&get_more,NULL,(unsigned char*)(my_buf),TMNUM*4*3*2);
223 222
224#if 0 223#if 0
225 /* can use to save and later analyze what we produce */ 224 /* can use to save and later analyze what we produce */
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index 90a6163b8f..d904be9a4e 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -257,20 +257,9 @@ enum
257/*******************************************************************/ 257/*******************************************************************/
258 258
259/* Callback for when more data is ready - called in interrupt context */ 259/* Callback for when more data is ready - called in interrupt context */
260static void pcm_rec_have_more(int status, void **start, size_t *size) 260static void pcm_rec_have_more(void **start, size_t *size)
261{ 261{
262 if (status < 0) 262 if (!dma_lock)
263 {
264 /* some error condition */
265 if (status == DMA_REC_ERROR_DMA)
266 {
267 /* Flush recorded data to disk and stop recording */
268 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
269 return;
270 }
271 /* else try again next transmission - frame is invalid */
272 }
273 else if (!dma_lock)
274 { 263 {
275 /* advance write position */ 264 /* advance write position */
276 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; 265 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
@@ -287,6 +276,23 @@ static void pcm_rec_have_more(int status, void **start, size_t *size)
287 *size = PCM_CHUNK_SIZE; 276 *size = PCM_CHUNK_SIZE;
288} /* pcm_rec_have_more */ 277} /* pcm_rec_have_more */
289 278
279static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status)
280{
281 if (status < PCM_DMAST_OK)
282 {
283 /* some error condition */
284 if (status == PCM_DMAST_ERR_DMA)
285 {
286 /* Flush recorded data to disk and stop recording */
287 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
288 return status;
289 }
290 /* else try again next transmission - frame is invalid */
291 }
292
293 return PCM_DMAST_OK;
294} /* pcm_rec_status_callback */
295
290static void reset_hardware(void) 296static void reset_hardware(void)
291{ 297{
292 /* reset pcm to defaults */ 298 /* reset pcm to defaults */
@@ -1239,8 +1245,8 @@ static void pcmrec_set_recording_options(
1239 { 1245 {
1240 /* start DMA transfer */ 1246 /* start DMA transfer */
1241 dma_lock = pre_record_ticks == 0; 1247 dma_lock = pre_record_ticks == 0;
1242 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos), 1248 pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
1243 PCM_CHUNK_SIZE); 1249 GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
1244 } 1250 }
1245 else 1251 else
1246 { 1252 {
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 2f216cde9d..5d23a74cbc 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -116,9 +116,12 @@ enum voice_thread_messages
116/* Structure to store clip data callback info */ 116/* Structure to store clip data callback info */
117struct voice_info 117struct voice_info
118{ 118{
119 pcm_play_callback_type get_more; /* Callback to get more clips */ 119 /* Callback to get more clips */
120 unsigned char *start; /* Start of clip */ 120 void (*get_more)(unsigned char** start, size_t* size);
121 size_t size; /* Size of clip */ 121 /* Start of clip */
122 unsigned char *start;
123 /* Size of clip */
124 size_t size;
122}; 125};
123 126
124/* Private thread data for its current state that must be passed to its 127/* Private thread data for its current state that must be passed to its
@@ -148,14 +151,14 @@ static inline int voice_unplayed_frames(void)
148} 151}
149 152
150/* Mixer channel callback */ 153/* Mixer channel callback */
151static void voice_pcm_callback(unsigned char **start, size_t *size) 154static void voice_pcm_callback(const void **start, size_t *size)
152{ 155{
153 if (voice_unplayed_frames() == 0) 156 if (voice_unplayed_frames() == 0)
154 return; /* Done! */ 157 return; /* Done! */
155 158
156 unsigned int i = ++cur_buf_out % VOICE_FRAMES; 159 unsigned int i = ++cur_buf_out % VOICE_FRAMES;
157 160
158 *start = (unsigned char *)voicebuf[i]; 161 *start = voicebuf[i];
159 *size = voicebuf_sizes[i]; 162 *size = voicebuf_sizes[i];
160} 163}
161 164
@@ -167,7 +170,7 @@ static void voice_start_playback(void)
167 170
168 unsigned int i = cur_buf_out % VOICE_FRAMES; 171 unsigned int i = cur_buf_out % VOICE_FRAMES;
169 mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback, 172 mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback,
170 (unsigned char *)voicebuf[i], voicebuf_sizes[i]); 173 voicebuf[i], voicebuf_sizes[i]);
171} 174}
172 175
173/* Stop the voice channel */ 176/* Stop the voice channel */
@@ -198,7 +201,7 @@ static void voice_buf_commit(size_t size)
198 201
199/* Stop any current clip and start playing a new one */ 202/* Stop any current clip and start playing a new one */
200void mp3_play_data(const unsigned char* start, int size, 203void mp3_play_data(const unsigned char* start, int size,
201 pcm_play_callback_type get_more) 204 void (*get_more)(unsigned char** start, size_t* size))
202{ 205{
203 if (get_more != NULL && start != NULL && (ssize_t)size > 0) 206 if (get_more != NULL && start != NULL && (ssize_t)size > 0)
204 { 207 {
diff --git a/firmware/asm/arm/pcm-mixer-armv4.c b/firmware/asm/arm/pcm-mixer-armv4.c
index 4818544d7b..dc2edb781c 100644
--- a/firmware/asm/arm/pcm-mixer-armv4.c
+++ b/firmware/asm/arm/pcm-mixer-armv4.c
@@ -24,9 +24,9 @@
24 24
25/* Mix channels' samples and apply gain factors */ 25/* Mix channels' samples and apply gain factors */
26static FORCE_INLINE void mix_samples(void *out, 26static FORCE_INLINE void mix_samples(void *out,
27 void *src0, 27 const void *src0,
28 int32_t src0_amp, 28 int32_t src0_amp,
29 void *src1, 29 const void *src1,
30 int32_t src1_amp, 30 int32_t src1_amp,
31 size_t size) 31 size_t size)
32{ 32{
@@ -96,7 +96,7 @@ static FORCE_INLINE void mix_samples(void *out,
96 if (src0_amp != MIX_AMP_UNITY) 96 if (src0_amp != MIX_AMP_UNITY)
97 { 97 {
98 /* Keep unity in src0, amp0 */ 98 /* Keep unity in src0, amp0 */
99 int16_t *src_tmp = src0; 99 const void *src_tmp = src0;
100 src0 = src1; 100 src0 = src1;
101 src1 = src_tmp; 101 src1 = src_tmp;
102 src1_amp = src0_amp; 102 src1_amp = src0_amp;
@@ -133,7 +133,7 @@ static FORCE_INLINE void mix_samples(void *out,
133 133
134/* Write channel's samples and apply gain factor */ 134/* Write channel's samples and apply gain factor */
135static FORCE_INLINE void write_samples(void *out, 135static FORCE_INLINE void write_samples(void *out,
136 void *src, 136 const void *src,
137 int32_t amp, 137 int32_t amp,
138 size_t size) 138 size_t size)
139{ 139{
diff --git a/firmware/asm/arm/pcm-mixer-armv5.c b/firmware/asm/arm/pcm-mixer-armv5.c
index 64f2c86f52..add1522fd9 100644
--- a/firmware/asm/arm/pcm-mixer-armv5.c
+++ b/firmware/asm/arm/pcm-mixer-armv5.c
@@ -24,9 +24,9 @@
24 24
25/* Mix channels' samples and apply gain factors */ 25/* Mix channels' samples and apply gain factors */
26static FORCE_INLINE void mix_samples(void *out, 26static FORCE_INLINE void mix_samples(void *out,
27 void *src0, 27 const void *src0,
28 int32_t src0_amp, 28 int32_t src0_amp,
29 void *src1, 29 const void *src1,
30 int32_t src1_amp, 30 int32_t src1_amp,
31 size_t size) 31 size_t size)
32{ 32{
@@ -57,7 +57,7 @@ static FORCE_INLINE void mix_samples(void *out,
57 57
58/* Write channel's samples and apply gain factor */ 58/* Write channel's samples and apply gain factor */
59static FORCE_INLINE void write_samples(void *out, 59static FORCE_INLINE void write_samples(void *out,
60 void *src, 60 const void *src,
61 int32_t amp, 61 int32_t amp,
62 size_t size) 62 size_t size)
63{ 63{
diff --git a/firmware/asm/arm/pcm-mixer-armv6.c b/firmware/asm/arm/pcm-mixer-armv6.c
index 94eecd0f90..9da5b40ef1 100644
--- a/firmware/asm/arm/pcm-mixer-armv6.c
+++ b/firmware/asm/arm/pcm-mixer-armv6.c
@@ -23,9 +23,9 @@
23 23
24/* Mix channels' samples and apply gain factors */ 24/* Mix channels' samples and apply gain factors */
25static FORCE_INLINE void mix_samples(void *out, 25static FORCE_INLINE void mix_samples(void *out,
26 void *src0, 26 const void *src0,
27 int32_t src0_amp, 27 int32_t src0_amp,
28 void *src1, 28 const void *src1,
29 int32_t src1_amp, 29 int32_t src1_amp,
30 size_t size) 30 size_t size)
31{ 31{
@@ -71,7 +71,7 @@ static FORCE_INLINE void mix_samples(void *out,
71 71
72/* Write channel's samples and apply gain factor */ 72/* Write channel's samples and apply gain factor */
73static FORCE_INLINE void write_samples(void *out, 73static FORCE_INLINE void write_samples(void *out,
74 void *src, 74 const void *src,
75 int32_t amp, 75 int32_t amp,
76 size_t size) 76 size_t size)
77{ 77{
diff --git a/firmware/asm/m68k/pcm-mixer.c b/firmware/asm/m68k/pcm-mixer.c
index d8318fffaf..730ae4ace7 100644
--- a/firmware/asm/m68k/pcm-mixer.c
+++ b/firmware/asm/m68k/pcm-mixer.c
@@ -56,9 +56,9 @@ static FORCE_INLINE void restore_emac_context(void)
56 56
57/* Mix channels' samples and apply gain factors */ 57/* Mix channels' samples and apply gain factors */
58static FORCE_INLINE void mix_samples(void *out, 58static FORCE_INLINE void mix_samples(void *out,
59 void *src0, 59 const void *src0,
60 int32_t src0_amp, 60 int32_t src0_amp,
61 void *src1, 61 const void *src1,
62 int32_t src1_amp, 62 int32_t src1_amp,
63 size_t size) 63 size_t size)
64{ 64{
@@ -94,7 +94,7 @@ static FORCE_INLINE void mix_samples(void *out,
94 94
95/* Write channel's samples and apply gain factor */ 95/* Write channel's samples and apply gain factor */
96static FORCE_INLINE void write_samples(void *out, 96static FORCE_INLINE void write_samples(void *out,
97 void *src, 97 const void *src,
98 int32_t amp, 98 int32_t amp,
99 size_t size) 99 size_t size)
100{ 100{
diff --git a/firmware/asm/pcm-mixer.c b/firmware/asm/pcm-mixer.c
index 62dfa3e9db..3e6e2fff78 100644
--- a/firmware/asm/pcm-mixer.c
+++ b/firmware/asm/pcm-mixer.c
@@ -28,9 +28,9 @@
28#include "dsp-util.h" /* for clip_sample_16 */ 28#include "dsp-util.h" /* for clip_sample_16 */
29/* Mix channels' samples and apply gain factors */ 29/* Mix channels' samples and apply gain factors */
30static FORCE_INLINE void mix_samples(int16_t *out, 30static FORCE_INLINE void mix_samples(int16_t *out,
31 int16_t *src0, 31 const int16_t *src0,
32 int32_t src0_amp, 32 int32_t src0_amp,
33 int16_t *src1, 33 const int16_t *src1,
34 int32_t src1_amp, 34 int32_t src1_amp,
35 size_t size) 35 size_t size)
36{ 36{
@@ -64,7 +64,7 @@ static FORCE_INLINE void mix_samples(int16_t *out,
64 if (src0_amp != MIX_AMP_UNITY) 64 if (src0_amp != MIX_AMP_UNITY)
65 { 65 {
66 /* Keep unity in src0, amp0 */ 66 /* Keep unity in src0, amp0 */
67 int16_t *src_tmp = src0; 67 const int16_t *src_tmp = src0;
68 src0 = src1; 68 src0 = src1;
69 src1 = src_tmp; 69 src1 = src_tmp;
70 src1_amp = src0_amp; 70 src1_amp = src0_amp;
@@ -84,7 +84,7 @@ static FORCE_INLINE void mix_samples(int16_t *out,
84 84
85/* Write channel's samples and apply gain factor */ 85/* Write channel's samples and apply gain factor */
86static FORCE_INLINE void write_samples(int16_t *out, 86static FORCE_INLINE void write_samples(int16_t *out,
87 int16_t *src, 87 const int16_t *src,
88 int32_t amp, 88 int32_t amp,
89 size_t size) 89 size_t size)
90{ 90{
diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h
index 89d895fe4b..abe3fe08dc 100644
--- a/firmware/export/pcm-internal.h
+++ b/firmware/export/pcm-internal.h
@@ -41,24 +41,28 @@ void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
41 41
42/** The following are for internal use between pcm.c and target- 42/** The following are for internal use between pcm.c and target-
43 specific portion **/ 43 specific portion **/
44static FORCE_INLINE enum pcm_dma_status pcm_call_status_cb(
45 pcm_status_callback_type callback, enum pcm_dma_status status)
46{
47 if (!callback)
48 return status;
44 49
45/* Called by the bottom layer ISR when more data is needed. Returns non- 50 return callback(status);
46 * zero size if more data is to be played. Setting start to NULL 51}
47 * forces stop. */
48void pcm_play_get_more_callback(void **start, size_t *size);
49 52
50/* Called by the bottom layer ISR after next transfer has begun in order 53static FORCE_INLINE enum pcm_dma_status
51 to fill more data for next "get more" callback to implement double-buffered 54pcm_play_dma_status_callback(enum pcm_dma_status status)
52 callbacks - except for a couple ASM handlers, help drivers to implement
53 this functionality with minimal overhead */
54static FORCE_INLINE void pcm_play_dma_started_callback(void)
55{ 55{
56 extern void (* pcm_play_dma_started)(void); 56 extern enum pcm_dma_status
57 void (* callback)(void) = pcm_play_dma_started; 57 (* volatile pcm_play_status_callback)(enum pcm_dma_status);
58 if (callback) 58 return pcm_call_status_cb(pcm_play_status_callback, status);
59 callback();
60} 59}
61 60
61/* Called by the bottom layer ISR when more data is needed. Returns true
62 * if a new buffer is available, false otherwise. */
63bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
64 const void **addr, size_t *size);
65
62extern unsigned long pcm_curr_sampr; 66extern unsigned long pcm_curr_sampr;
63extern unsigned long pcm_sampr; 67extern unsigned long pcm_sampr;
64extern int pcm_fsel; 68extern int pcm_fsel;
@@ -90,10 +94,29 @@ extern volatile bool pcm_recording;
90void pcm_rec_dma_init(void); 94void pcm_rec_dma_init(void);
91void pcm_rec_dma_close(void); 95void pcm_rec_dma_close(void);
92void pcm_rec_dma_start(void *addr, size_t size); 96void pcm_rec_dma_start(void *addr, size_t size);
93void pcm_rec_dma_record_more(void *start, size_t size);
94void pcm_rec_dma_stop(void); 97void pcm_rec_dma_stop(void);
95const void * pcm_rec_dma_get_peak_buffer(void); 98const void * pcm_rec_dma_get_peak_buffer(void);
96 99
100static FORCE_INLINE enum pcm_dma_status
101pcm_rec_dma_status_callback(enum pcm_dma_status status)
102{
103 extern enum pcm_dma_status
104 (* volatile pcm_rec_status_callback)(enum pcm_dma_status);
105 return pcm_call_status_cb(pcm_rec_status_callback, status);
106}
107
108
109/* Called by the bottom layer ISR when more data is needed. Returns true
110 * if a new buffer is available, false otherwise. */
111bool pcm_rec_dma_complete_callback(enum pcm_dma_status status,
112 void **addr, size_t *size);
113
114#ifdef HAVE_PCM_REC_DMA_ADDRESS
115#define pcm_rec_dma_addr(addr) pcm_dma_addr(addr)
116#else
117#define pcm_rec_dma_addr(addr) addr
118#endif
119
97#endif /* HAVE_RECORDING */ 120#endif /* HAVE_RECORDING */
98 121
99#endif /* PCM_INTERNAL_H */ 122#endif /* PCM_INTERNAL_H */
diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h
index 4a7a5b3193..df82c6092d 100644
--- a/firmware/export/pcm.h
+++ b/firmware/export/pcm.h
@@ -24,17 +24,23 @@
24#include <string.h> /* size_t */ 24#include <string.h> /* size_t */
25#include "config.h" 25#include "config.h"
26 26
27#define DMA_REC_ERROR_DMA (-1) 27enum pcm_dma_status
28{
28#ifdef HAVE_SPDIF_REC 29#ifdef HAVE_SPDIF_REC
29#define DMA_REC_ERROR_SPDIF (-2) 30 PCM_DMAST_ERR_SPDIF = -2,
30#endif 31#endif
32 PCM_DMAST_ERR_DMA = -1,
33 PCM_DMAST_OK = 0,
34 PCM_DMAST_STARTED = 1,
35};
31 36
32/** RAW PCM routines used with playback and recording **/ 37/** RAW PCM routines used with playback and recording **/
33 38
34/* Typedef for registered callbacks */ 39/* Typedef for registered data callback */
35typedef void (*pcm_play_callback_type)(unsigned char **start, 40typedef void (*pcm_play_callback_type)(const void **start, size_t *size);
36 size_t *size); 41
37typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size); 42/* Typedef for registered status callback */
43typedef enum pcm_dma_status (*pcm_status_callback_type)(enum pcm_dma_status status);
38 44
39/* set the pcm frequency - use values in hw_sampr_list 45/* set the pcm frequency - use values in hw_sampr_list
40 * when CONFIG_SAMPR_TYPES is #defined, or-in SAMPR_TYPE_* fields with 46 * when CONFIG_SAMPR_TYPES is #defined, or-in SAMPR_TYPE_* fields with
@@ -62,7 +68,8 @@ bool pcm_is_initialized(void);
62 68
63/* This is for playing "raw" PCM data */ 69/* This is for playing "raw" PCM data */
64void pcm_play_data(pcm_play_callback_type get_more, 70void pcm_play_data(pcm_play_callback_type get_more,
65 unsigned char* start, size_t size); 71 pcm_status_callback_type status_cb,
72 const void *start, size_t size);
66 73
67void pcm_calculate_peaks(int *left, int *right); 74void pcm_calculate_peaks(int *left, int *right);
68const void* pcm_get_peak_buffer(int* count); 75const void* pcm_get_peak_buffer(int* count);
@@ -73,12 +80,13 @@ void pcm_play_pause(bool play);
73bool pcm_is_paused(void); 80bool pcm_is_paused(void);
74bool pcm_is_playing(void); 81bool pcm_is_playing(void);
75 82
76void pcm_play_set_dma_started_callback(void (* callback)(void));
77
78#ifdef HAVE_RECORDING 83#ifdef HAVE_RECORDING
79 84
80/** RAW PCM recording routines **/ 85/** RAW PCM recording routines **/
81 86
87/* Typedef for registered data callback */
88typedef void (*pcm_rec_callback_type)(void **start, size_t *size);
89
82/* Reenterable locks for locking and unlocking the recording interrupt */ 90/* Reenterable locks for locking and unlocking the recording interrupt */
83void pcm_rec_lock(void); 91void pcm_rec_lock(void);
84void pcm_rec_unlock(void); 92void pcm_rec_unlock(void);
@@ -90,6 +98,7 @@ void pcm_close_recording(void);
90 98
91/* Start recording "raw" PCM data */ 99/* Start recording "raw" PCM data */
92void pcm_record_data(pcm_rec_callback_type more_ready, 100void pcm_record_data(pcm_rec_callback_type more_ready,
101 pcm_status_callback_type status_cb,
93 void *start, size_t size); 102 void *start, size_t size);
94 103
95/* Stop tranferring data into supplied buffer */ 104/* Stop tranferring data into supplied buffer */
@@ -98,10 +107,6 @@ void pcm_stop_recording(void);
98/* Is pcm currently recording? */ 107/* Is pcm currently recording? */
99bool pcm_is_recording(void); 108bool pcm_is_recording(void);
100 109
101/* Called by bottom layer ISR when transfer is complete. Returns non-zero
102 * size if successful. Setting start to NULL forces stop. */
103void pcm_rec_more_ready_callback(int status, void **start, size_t *size);
104
105void pcm_calculate_rec_peaks(int *left, int *right); 110void pcm_calculate_rec_peaks(int *left, int *right);
106 111
107#endif /* HAVE_RECORDING */ 112#endif /* HAVE_RECORDING */
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
index ea26060452..6e1632d5ae 100644
--- a/firmware/export/pcm_mixer.h
+++ b/firmware/export/pcm_mixer.h
@@ -86,7 +86,7 @@ enum channel_status
86/* Start playback on a channel */ 86/* Start playback on a channel */
87void mixer_channel_play_data(enum pcm_mixer_channel channel, 87void mixer_channel_play_data(enum pcm_mixer_channel channel,
88 pcm_play_callback_type get_more, 88 pcm_play_callback_type get_more,
89 unsigned char *start, size_t size); 89 const void *start, size_t size);
90 90
91/* Pause or resume a channel (when started) */ 91/* Pause or resume a channel (when started) */
92void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play); 92void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play);
@@ -105,7 +105,7 @@ enum channel_status mixer_channel_status(enum pcm_mixer_channel channel);
105size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel); 105size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel);
106 106
107/* Return pointer to channel's playing audio data and the size remaining */ 107/* Return pointer to channel's playing audio data and the size remaining */
108void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count); 108const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count);
109 109
110/* Calculate peak values for channel */ 110/* Calculate peak values for channel */
111void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, 111void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
diff --git a/firmware/export/pp5002.h b/firmware/export/pp5002.h
index 95cc8d5058..5966f10d08 100644
--- a/firmware/export/pp5002.h
+++ b/firmware/export/pp5002.h
@@ -43,7 +43,9 @@
43#define IISCONFIG (*(volatile unsigned long *)(0xc0002500)) 43#define IISCONFIG (*(volatile unsigned long *)(0xc0002500))
44#define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c)) 44#define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c))
45#define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540)) 45#define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540))
46#define IISFIFO_WRH (*(volatile unsigned short *)(0xc0002540))
46#define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580)) 47#define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580))
48#define IISFIFO_RDH (*(volatile unsigned short *)(0xc0002540))
47 49
48/** 50/**
49 * IISCONFIG bits: 51 * IISCONFIG bits:
diff --git a/firmware/pcm.c b/firmware/pcm.c
index d1a897dcab..621ed56e0d 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -41,7 +41,8 @@
41 * pcm_play_lock 41 * pcm_play_lock
42 * pcm_play_unlock 42 * pcm_play_unlock
43 * Semi-private - 43 * Semi-private -
44 * pcm_play_get_more_callback 44 * pcm_play_dma_complete_callback
45 * pcm_play_dma_status_callback
45 * pcm_play_dma_init 46 * pcm_play_dma_init
46 * pcm_play_dma_postinit 47 * pcm_play_dma_postinit
47 * pcm_play_dma_start 48 * pcm_play_dma_start
@@ -66,7 +67,8 @@
66 * pcm_rec_lock 67 * pcm_rec_lock
67 * pcm_rec_unlock 68 * pcm_rec_unlock
68 * Semi-private - 69 * Semi-private -
69 * pcm_rec_more_ready_callback 70 * pcm_rec_dma_complete_callback
71 * pcm_rec_dma_status_callback
70 * pcm_rec_dma_init 72 * pcm_rec_dma_init
71 * pcm_rec_dma_close 73 * pcm_rec_dma_close
72 * pcm_rec_dma_start 74 * pcm_rec_dma_start
@@ -83,9 +85,12 @@
83/* 'true' when all stages of pcm initialization have completed */ 85/* 'true' when all stages of pcm initialization have completed */
84static bool pcm_is_ready = false; 86static bool pcm_is_ready = false;
85 87
86/* the registered callback function to ask for more mp3 data */ 88/* The registered callback function to ask for more mp3 data */
87static pcm_play_callback_type pcm_callback_for_more SHAREDBSS_ATTR = NULL; 89static volatile pcm_play_callback_type
88void (* pcm_play_dma_started)(void) SHAREDBSS_ATTR = NULL; 90 pcm_callback_for_more SHAREDBSS_ATTR = NULL;
91/* The registered callback function to inform of DMA status */
92volatile pcm_status_callback_type
93 pcm_play_status_callback SHAREDBSS_ATTR = NULL;
89/* PCM playback state */ 94/* PCM playback state */
90volatile bool pcm_playing SHAREDBSS_ATTR = false; 95volatile bool pcm_playing SHAREDBSS_ATTR = false;
91/* PCM paused state. paused implies playing */ 96/* PCM paused state. paused implies playing */
@@ -104,7 +109,7 @@ static struct pcm_peaks global_peaks;
104static void pcm_play_stopped(void) 109static void pcm_play_stopped(void)
105{ 110{
106 pcm_callback_for_more = NULL; 111 pcm_callback_for_more = NULL;
107 pcm_play_dma_started = NULL; 112 pcm_play_status_callback = NULL;
108 pcm_paused = false; 113 pcm_paused = false;
109 pcm_playing = false; 114 pcm_playing = false;
110} 115}
@@ -258,27 +263,29 @@ bool pcm_is_initialized(void)
258} 263}
259 264
260/* Common code to pcm_play_data and pcm_play_pause */ 265/* Common code to pcm_play_data and pcm_play_pause */
261static void pcm_play_data_start(unsigned char *start, size_t size) 266static void pcm_play_data_start(const void *addr, size_t size)
262{ 267{
263 ALIGN_AUDIOBUF(start, size); 268 ALIGN_AUDIOBUF(addr, size);
264 269
265 if (!(start && size)) 270 if (!(addr && size))
266 { 271 {
267 pcm_play_callback_type get_more = pcm_callback_for_more; 272 pcm_play_callback_type get_more = pcm_callback_for_more;
273 addr = NULL;
268 size = 0; 274 size = 0;
275
269 if (get_more) 276 if (get_more)
270 { 277 {
271 logf(" get_more"); 278 logf(" get_more");
272 get_more(&start, &size); 279 get_more(&addr, &size);
273 ALIGN_AUDIOBUF(start, size); 280 ALIGN_AUDIOBUF(addr, size);
274 } 281 }
275 } 282 }
276 283
277 if (start && size) 284 if (addr && size)
278 { 285 {
279 logf(" pcm_play_dma_start"); 286 logf(" pcm_play_dma_start");
280 pcm_apply_settings(); 287 pcm_apply_settings();
281 pcm_play_dma_start(start, size); 288 pcm_play_dma_start(addr, size);
282 pcm_playing = true; 289 pcm_playing = true;
283 pcm_paused = false; 290 pcm_paused = false;
284 return; 291 return;
@@ -291,13 +298,15 @@ static void pcm_play_data_start(unsigned char *start, size_t size)
291} 298}
292 299
293void pcm_play_data(pcm_play_callback_type get_more, 300void pcm_play_data(pcm_play_callback_type get_more,
294 unsigned char *start, size_t size) 301 pcm_status_callback_type status_cb,
302 const void *start, size_t size)
295{ 303{
296 logf("pcm_play_data"); 304 logf("pcm_play_data");
297 305
298 pcm_play_lock(); 306 pcm_play_lock();
299 307
300 pcm_callback_for_more = get_more; 308 pcm_callback_for_more = get_more;
309 pcm_play_status_callback = status_cb;
301 310
302 logf(" pcm_play_data_start"); 311 logf(" pcm_play_data_start");
303 pcm_play_data_start(start, size); 312 pcm_play_data_start(start, size);
@@ -305,26 +314,33 @@ void pcm_play_data(pcm_play_callback_type get_more,
305 pcm_play_unlock(); 314 pcm_play_unlock();
306} 315}
307 316
308void pcm_play_get_more_callback(void **start, size_t *size) 317bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
318 const void **addr, size_t *size)
309{ 319{
310 pcm_play_callback_type get_more = pcm_callback_for_more; 320 /* Check status callback first if error */
321 if (status < PCM_DMAST_OK)
322 status = pcm_play_dma_status_callback(status);
311 323
312 *size = 0; 324 pcm_play_callback_type get_more = pcm_callback_for_more;
313 325
314 if (get_more && start) 326 if (get_more && status >= PCM_DMAST_OK)
315 { 327 {
316 /* Call registered callback */ 328 *addr = NULL;
317 get_more((unsigned char **)start, size); 329 *size = 0;
318 330
319 ALIGN_AUDIOBUF(*start, *size); 331 /* Call registered callback to obtain next buffer */
332 get_more(addr, size);
333 ALIGN_AUDIOBUF(*addr, *size);
320 334
321 if (*start && *size) 335 if (*addr && *size)
322 return; 336 return true;
323 } 337 }
324 338
325 /* Error, callback missing or no more DMA to do */ 339 /* Error, callback missing or no more DMA to do */
326 pcm_play_dma_stop(); 340 pcm_play_dma_stop();
327 pcm_play_stopped(); 341 pcm_play_stopped();
342
343 return false;
328} 344}
329 345
330void pcm_play_pause(bool play) 346void pcm_play_pause(bool play)
@@ -428,12 +444,6 @@ void pcm_apply_settings(void)
428 } 444 }
429} 445}
430 446
431/* register callback to buffer more data */
432void pcm_play_set_dma_started_callback(void (* callback)(void))
433{
434 pcm_play_dma_started = callback;
435}
436
437#ifdef HAVE_RECORDING 447#ifdef HAVE_RECORDING
438/** Low level pcm recording apis **/ 448/** Low level pcm recording apis **/
439 449
@@ -442,6 +452,8 @@ static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
442/* the registered callback function for when more data is available */ 452/* the registered callback function for when more data is available */
443static volatile pcm_rec_callback_type 453static volatile pcm_rec_callback_type
444 pcm_callback_more_ready SHAREDBSS_ATTR = NULL; 454 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
455volatile pcm_status_callback_type
456 pcm_rec_status_callback SHAREDBSS_ATTR = NULL;
445/* DMA transfer in is currently active */ 457/* DMA transfer in is currently active */
446volatile bool pcm_recording SHAREDBSS_ATTR = false; 458volatile bool pcm_recording SHAREDBSS_ATTR = false;
447 459
@@ -450,6 +462,7 @@ static void pcm_recording_stopped(void)
450{ 462{
451 pcm_recording = false; 463 pcm_recording = false;
452 pcm_callback_more_ready = NULL; 464 pcm_callback_more_ready = NULL;
465 pcm_rec_status_callback = NULL;
453} 466}
454 467
455/** 468/**
@@ -542,13 +555,14 @@ void pcm_close_recording(void)
542} 555}
543 556
544void pcm_record_data(pcm_rec_callback_type more_ready, 557void pcm_record_data(pcm_rec_callback_type more_ready,
545 void *start, size_t size) 558 pcm_status_callback_type status_cb,
559 void *addr, size_t size)
546{ 560{
547 logf("pcm_record_data"); 561 logf("pcm_record_data");
548 562
549 ALIGN_AUDIOBUF(start, size); 563 ALIGN_AUDIOBUF(addr, size);
550 564
551 if (!(start && size)) 565 if (!(addr && size))
552 { 566 {
553 logf(" no buffer"); 567 logf(" no buffer");
554 return; 568 return;
@@ -557,17 +571,14 @@ void pcm_record_data(pcm_rec_callback_type more_ready,
557 pcm_rec_lock(); 571 pcm_rec_lock();
558 572
559 pcm_callback_more_ready = more_ready; 573 pcm_callback_more_ready = more_ready;
574 pcm_rec_status_callback = status_cb;
560 575
561#ifdef HAVE_PCM_REC_DMA_ADDRESS
562 /* Need a physical DMA address translation, if not already physical. */ 576 /* Need a physical DMA address translation, if not already physical. */
563 pcm_rec_peak_addr = pcm_dma_addr(start); 577 pcm_rec_peak_addr = pcm_rec_dma_addr(addr);
564#else
565 pcm_rec_peak_addr = start;
566#endif
567 578
568 logf(" pcm_rec_dma_start"); 579 logf(" pcm_rec_dma_start");
569 pcm_apply_settings(); 580 pcm_apply_settings();
570 pcm_rec_dma_start(start, size); 581 pcm_rec_dma_start(addr, size);
571 pcm_recording = true; 582 pcm_recording = true;
572 583
573 pcm_rec_unlock(); 584 pcm_rec_unlock();
@@ -589,33 +600,35 @@ void pcm_stop_recording(void)
589 pcm_rec_unlock(); 600 pcm_rec_unlock();
590} /* pcm_stop_recording */ 601} /* pcm_stop_recording */
591 602
592void pcm_rec_more_ready_callback(int status, void **start, size_t *size) 603bool pcm_rec_dma_complete_callback(enum pcm_dma_status status,
604 void **addr, size_t *size)
593{ 605{
594 pcm_rec_callback_type have_more = pcm_callback_more_ready; 606 /* Check status callback first if error */
607 if (status < PCM_DMAST_OK)
608 status = pcm_rec_dma_status_callback(status);
595 609
596 *size = 0; 610 pcm_rec_callback_type have_more = pcm_callback_more_ready;
597 611
598 if (have_more && start) 612 if (have_more && status >= PCM_DMAST_OK)
599 { 613 {
600 have_more(status, start, size); 614 /* Call registered callback to obtain next buffer */
601 ALIGN_AUDIOBUF(*start, *size); 615 have_more(addr, size);
616 ALIGN_AUDIOBUF(*addr, *size);
602 617
603 if (*start && *size) 618 if (*addr && *size)
604 { 619 {
605 #ifdef HAVE_PCM_REC_DMA_ADDRESS
606 /* Need a physical DMA address translation, if not already 620 /* Need a physical DMA address translation, if not already
607 * physical. */ 621 * physical. */
608 pcm_rec_peak_addr = pcm_dma_addr(*start); 622 pcm_rec_peak_addr = pcm_rec_dma_addr(*addr);
609 #else 623 return true;
610 pcm_rec_peak_addr = *start;
611 #endif
612 return;
613 } 624 }
614 } 625 }
615 626
616 /* Error, callback missing or no more DMA to do */ 627 /* Error, callback missing or no more DMA to do */
617 pcm_rec_dma_stop(); 628 pcm_rec_dma_stop();
618 pcm_recording_stopped(); 629 pcm_recording_stopped();
630
631 return false;
619} 632}
620 633
621#endif /* HAVE_RECORDING */ 634#endif /* HAVE_RECORDING */
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
index 4fd96bc97c..9077c6f271 100644
--- a/firmware/pcm_mixer.c
+++ b/firmware/pcm_mixer.c
@@ -39,7 +39,7 @@
39/* Descriptor for each channel */ 39/* Descriptor for each channel */
40struct mixer_channel 40struct mixer_channel
41{ 41{
42 unsigned char *start; /* Buffer pointer */ 42 const void *start; /* Buffer pointer */
43 size_t size; /* Bytes remaining */ 43 size_t size; /* Bytes remaining */
44 size_t last_size; /* Size of consumed data in prev. cycle */ 44 size_t last_size; /* Size of consumed data in prev. cycle */
45 pcm_play_callback_type get_more; /* Registered callback */ 45 pcm_play_callback_type get_more; /* Registered callback */
@@ -103,16 +103,20 @@ static void channel_stopped(struct mixer_channel *chan)
103} 103}
104 104
105/* Main PCM callback - sends the current prepared frame to play */ 105/* Main PCM callback - sends the current prepared frame to play */
106static void mixer_pcm_callback(unsigned char **start, size_t *size) 106static void mixer_pcm_callback(const void **addr, size_t *size)
107{ 107{
108 *start = (unsigned char *)downmix_buf[downmix_index]; 108 *addr = downmix_buf[downmix_index];
109 *size = next_size; 109 *size = next_size;
110} 110}
111 111
112/* Buffering callback - calls sub-callbacks and mixes the data for next 112/* Buffering callback - calls sub-callbacks and mixes the data for next
113 buffer to be sent from mixer_pcm_callback() */ 113 buffer to be sent from mixer_pcm_callback() */
114static void MIXER_CALLBACK_ICODE mixer_buffer_callback(void) 114static enum pcm_dma_status MIXER_CALLBACK_ICODE
115mixer_buffer_callback(enum pcm_dma_status status)
115{ 116{
117 if (status != PCM_DMAST_STARTED)
118 return status;
119
116 downmix_index ^= 1; /* Next buffer */ 120 downmix_index ^= 1; /* Next buffer */
117 121
118 void *mixptr = downmix_buf[downmix_index]; 122 void *mixptr = downmix_buf[downmix_index];
@@ -169,12 +173,11 @@ fill_frame:
169 173
170 if (LIKELY(!*chan_p)) 174 if (LIKELY(!*chan_p))
171 { 175 {
172 write_samples(mixptr, (void *)chan->start, 176 write_samples(mixptr, chan->start, chan->amplitude, mixsize);
173 chan->amplitude, mixsize);
174 } 177 }
175 else 178 else
176 { 179 {
177 void *src0, *src1; 180 const void *src0, *src1;
178 unsigned int amp0, amp1; 181 unsigned int amp0, amp1;
179 182
180 /* Mix first two channels with each other as the downmix */ 183 /* Mix first two channels with each other as the downmix */
@@ -228,6 +231,8 @@ fill_frame:
228 if (next_size) 231 if (next_size)
229 *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000; 232 *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000;
230#endif 233#endif
234
235 return PCM_DMAST_OK;
231} 236}
232 237
233/* Start PCM driver if it's not currently playing */ 238/* Start PCM driver if it's not currently playing */
@@ -245,15 +250,15 @@ static void mixer_start_pcm(void)
245 pcm_set_frequency(NATIVE_FREQUENCY); 250 pcm_set_frequency(NATIVE_FREQUENCY);
246 251
247 /* Prepare initial frames and set up the double buffer */ 252 /* Prepare initial frames and set up the double buffer */
248 mixer_buffer_callback(); 253 mixer_buffer_callback(PCM_DMAST_STARTED);
249 254
250 /* Save the previous call's output */ 255 /* Save the previous call's output */
251 void *start = downmix_buf[downmix_index]; 256 void *start = downmix_buf[downmix_index];
252 257
253 mixer_buffer_callback(); 258 mixer_buffer_callback(PCM_DMAST_STARTED);
254 259
255 pcm_play_set_dma_started_callback(mixer_buffer_callback); 260 pcm_play_data(mixer_pcm_callback, mixer_buffer_callback,
256 pcm_play_data(mixer_pcm_callback, start, MIX_FRAME_SIZE); 261 start, MIX_FRAME_SIZE);
257} 262}
258 263
259/** Public interfaces **/ 264/** Public interfaces **/
@@ -261,7 +266,7 @@ static void mixer_start_pcm(void)
261/* Start playback on a channel */ 266/* Start playback on a channel */
262void mixer_channel_play_data(enum pcm_mixer_channel channel, 267void mixer_channel_play_data(enum pcm_mixer_channel channel,
263 pcm_play_callback_type get_more, 268 pcm_play_callback_type get_more,
264 unsigned char *start, size_t size) 269 const void *start, size_t size)
265{ 270{
266 struct mixer_channel *chan = &channels[channel]; 271 struct mixer_channel *chan = &channels[channel];
267 272
@@ -360,12 +365,12 @@ size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel)
360} 365}
361 366
362/* Return pointer to channel's playing audio data and the size remaining */ 367/* Return pointer to channel's playing audio data and the size remaining */
363void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count) 368const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count)
364{ 369{
365 struct mixer_channel *chan = &channels[channel]; 370 struct mixer_channel *chan = &channels[channel];
366 void * buf = *(unsigned char * volatile *)&chan->start; 371 const void * buf = *(const void * volatile *)&chan->start;
367 size_t size = *(size_t volatile *)&chan->size; 372 size_t size = *(size_t volatile *)&chan->size;
368 void * buf2 = *(unsigned char * volatile *)&chan->start; 373 const void * buf2 = *(const void * volatile *)&chan->start;
369 374
370 /* Still same buffer? */ 375 /* Still same buffer? */
371 if (buf == buf2) 376 if (buf == buf2)
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index 0ecc63d018..eb22fc2016 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -36,9 +36,9 @@
36 * and the number of 32bits words has to 36 * and the number of 32bits words has to
37 * fit in 11 bits of DMA register */ 37 * fit in 11 bits of DMA register */
38 38
39static void *dma_start_addr; /* Pointer to callback buffer */ 39static const void *dma_start_addr; /* Pointer to callback buffer */
40static size_t dma_start_size; /* Size of callback buffer */ 40static size_t dma_start_size; /* Size of callback buffer */
41static void *dma_sub_addr; /* Pointer to sub buffer */ 41static const void *dma_sub_addr; /* Pointer to sub buffer */
42static size_t dma_rem_size; /* Remaining size - in 4*32 bits */ 42static size_t dma_rem_size; /* Remaining size - in 4*32 bits */
43static size_t play_sub_size; /* size of current subtransfer */ 43static size_t play_sub_size; /* size of current subtransfer */
44static void dma_callback(void); 44static void dma_callback(void);
@@ -100,9 +100,8 @@ static void dma_callback(void)
100 100
101 if(!dma_rem_size) 101 if(!dma_rem_size)
102 { 102 {
103 pcm_play_get_more_callback(&dma_start_addr, &dma_start_size); 103 if(!pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_start_addr,
104 104 &dma_start_size))
105 if (!dma_start_size)
106 return; 105 return;
107 106
108 dma_sub_addr = dma_start_addr; 107 dma_sub_addr = dma_start_addr;
@@ -111,7 +110,7 @@ static void dma_callback(void)
111 /* force writeback */ 110 /* force writeback */
112 commit_dcache_range(dma_start_addr, dma_start_size); 111 commit_dcache_range(dma_start_addr, dma_start_size);
113 play_start_pcm(); 112 play_start_pcm();
114 pcm_play_dma_started_callback(); 113 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
115 } 114 }
116 else 115 else
117 { 116 {
@@ -123,7 +122,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
123{ 122{
124 is_playing = true; 123 is_playing = true;
125 124
126 dma_start_addr = (void*)addr; 125 dma_start_addr = addr;
127 dma_start_size = size; 126 dma_start_size = size;
128 dma_sub_addr = dma_start_addr; 127 dma_sub_addr = dma_start_addr;
129 dma_rem_size = size; 128 dma_rem_size = size;
@@ -368,7 +367,12 @@ void INT_I2SIN(void)
368 } 367 }
369 } 368 }
370 369
371 pcm_rec_more_ready_callback(0, (void *)&rec_dma_addr, &rec_dma_size); 370 /* Inform middle layer */
371 if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, (void **)&rec_dma_addr,
372 &rec_dma_size))
373 {
374 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
375 }
372} 376}
373 377
374 378
diff --git a/firmware/target/arm/imx233/pcm-imx233.c b/firmware/target/arm/imx233/pcm-imx233.c
index c8b79b3875..c4c512eed6 100644
--- a/firmware/target/arm/imx233/pcm-imx233.c
+++ b/firmware/target/arm/imx233/pcm-imx233.c
@@ -49,15 +49,13 @@ static void play(const void *addr, size_t size)
49 49
50void INT_DAC_DMA(void) 50void INT_DAC_DMA(void)
51{ 51{
52 void *start; 52 const void *start;
53 size_t size; 53 size_t size;
54 54
55 pcm_play_get_more_callback(&start, &size); 55 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
56
57 if(size != 0)
58 { 56 {
59 play(start, size); 57 play(start, size);
60 pcm_play_dma_started_callback(); 58 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
61 } 59 }
62 60
63 imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); 61 imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC);
@@ -65,6 +63,7 @@ void INT_DAC_DMA(void)
65 63
66void INT_DAC_ERROR(void) 64void INT_DAC_ERROR(void)
67{ 65{
66 /* TODO: Inform of error through pcm_play_dma_complete_callback */
68} 67}
69 68
70void pcm_play_lock(void) 69void pcm_play_lock(void)
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
index e106cf78e3..c26349b72e 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
@@ -78,12 +78,20 @@ static struct dma_data dma_play_data =
78 .state = 0 78 .state = 0
79}; 79};
80 80
81static void play_dma_callback(void) 81static void play_start_dma(const void *addr, size_t size)
82{ 82{
83 void *start; 83 commit_dcache_range(addr, size);
84 size_t size; 84
85 bool rror; 85 dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)addr);
86 dma_play_bd.mode.count = size;
87 dma_play_bd.mode.command = TRANSFER_16BIT;
88 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
89
90 sdma_channel_run(DMA_PLAY_CH_NUM);
91}
86 92
93static void play_dma_callback(void)
94{
87 if (dma_play_data.locked != 0) 95 if (dma_play_data.locked != 0)
88 { 96 {
89 /* Callback is locked out */ 97 /* Callback is locked out */
@@ -91,22 +99,17 @@ static void play_dma_callback(void)
91 return; 99 return;
92 } 100 }
93 101
94 rror = dma_play_bd.mode.status & BD_RROR; 102 /* Inform of status and get new buffer */
95 103 enum pcm_dma_status status = (dma_play_bd.mode.status & BD_RROR) ?
96 pcm_play_get_more_callback(rror ? NULL : &start, &size); 104 PCM_DMAST_ERR_DMA : PCM_DMAST_OK;
97 105 const void *addr;
98 if (size == 0) 106 size_t size;
99 return; 107
100 108 if (pcm_play_dma_complete_callback(status, &addr, &size))
101 /* Flush any pending cache writes */ 109 {
102 commit_dcache_range(start, size); 110 play_start_dma(addr, size);
103 dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start); 111 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
104 dma_play_bd.mode.count = size; 112 }
105 dma_play_bd.mode.command = TRANSFER_16BIT;
106 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
107 sdma_channel_run(DMA_PLAY_CH_NUM);
108
109 pcm_play_dma_started_callback();
110} 113}
111 114
112void pcm_play_lock(void) 115void pcm_play_lock(void)
@@ -221,15 +224,11 @@ void pcm_play_dma_start(const void *addr, size_t size)
221 if (!sdma_channel_reset(DMA_PLAY_CH_NUM)) 224 if (!sdma_channel_reset(DMA_PLAY_CH_NUM))
222 return; 225 return;
223 226
224 commit_dcache_range(addr, size); 227 /* Begin I2S transmission */
225 dma_play_bd.buf_addr =
226 (void *)addr_virt_to_phys((unsigned long)(void *)addr);
227 dma_play_bd.mode.count = size;
228 dma_play_bd.mode.command = TRANSFER_16BIT;
229 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
230
231 play_start_pcm(); 228 play_start_pcm();
232 sdma_channel_run(DMA_PLAY_CH_NUM); 229
230 /* Begin DMA transfer */
231 play_start_dma(addr, size);
233} 232}
234 233
235void pcm_play_dma_stop(void) 234void pcm_play_dma_stop(void)
@@ -332,37 +331,39 @@ static struct dma_data dma_rec_data =
332 .state = 0 331 .state = 0
333}; 332};
334 333
335static void rec_dma_callback(void) 334static void rec_start_dma(void *addr, size_t size)
336{ 335{
337 int status = 0; 336 discard_dcache_range(addr, size);
338 void *start; 337
339 size_t size; 338 addr = (void *)addr_virt_to_phys((unsigned long)addr);
340 339
340 dma_rec_bd.buf_addr = addr;
341 dma_rec_bd.mode.count = size;
342 dma_rec_bd.mode.command = TRANSFER_16BIT;
343 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
344
345 sdma_channel_run(DMA_REC_CH_NUM);
346}
347
348static void rec_dma_callback(void)
349{
341 if (dma_rec_data.locked != 0) 350 if (dma_rec_data.locked != 0)
342 { 351 {
343 dma_rec_data.callback_pending = dma_rec_data.state; 352 dma_rec_data.callback_pending = dma_rec_data.state;
344 return; /* Callback is locked out */ 353 return; /* Callback is locked out */
345 } 354 }
346 355
347 if (dma_rec_bd.mode.status & BD_RROR) 356 /* Inform middle layer */
348 status = DMA_REC_ERROR_DMA; 357 enum pcm_dma_status status = (dma_rec_bd.mode.status & BD_RROR) ?
349 358 PCM_DMAST_ERR_DMA : PCM_DMAST_OK;
350 pcm_rec_more_ready_callback(status, &start, &size); 359 void *addr;
351 360 size_t size;
352 if (size == 0)
353 return;
354
355 /* Invalidate - buffer must be coherent */
356 discard_dcache_range(start, size);
357
358 start = (void *)addr_virt_to_phys((unsigned long)start);
359
360 dma_rec_bd.buf_addr = start;
361 dma_rec_bd.mode.count = size;
362 dma_rec_bd.mode.command = TRANSFER_16BIT;
363 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
364 361
365 sdma_channel_run(DMA_REC_CH_NUM); 362 if (pcm_rec_dma_complete_callback(status, &addr, &size))
363 {
364 rec_start_dma(addr, size);
365 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
366 }
366} 367}
367 368
368void pcm_rec_lock(void) 369void pcm_rec_lock(void)
@@ -426,29 +427,21 @@ void pcm_rec_dma_start(void *addr, size_t size)
426 427
427 if (!sdma_channel_reset(DMA_REC_CH_NUM)) 428 if (!sdma_channel_reset(DMA_REC_CH_NUM))
428 return; 429 return;
429
430 /* Invalidate - buffer must be coherent */
431 discard_dcache_range(addr, size);
432 430
433 addr = (void *)addr_virt_to_phys((unsigned long)addr); 431 /* Ensure clear FIFO */
434 dma_rec_bd.buf_addr = addr; 432 while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0)
435 dma_rec_bd.mode.count = size; 433 SSI_SRX0_1;
436 dma_rec_bd.mode.command = TRANSFER_16BIT;
437 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
438 434
439 dma_rec_data.state = 1; /* Check callback on unlock */ 435 dma_rec_data.state = 1; /* Check callback on unlock */
440 436
441 SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ 437 SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */
442 438
443 /* Ensure clear FIFO */
444 while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0)
445 SSI_SRX0_1;
446
447 /* Enable receive */ 439 /* Enable receive */
448 SSI_SCR1 |= SSI_SCR_RE; 440 SSI_SCR1 |= SSI_SCR_RE;
449 SSI_SIER1 |= SSI_SIER_RDMAE; /* Enable DMA req. */ 441 SSI_SIER1 |= SSI_SIER_RDMAE; /* Enable DMA req. */
450 442
451 sdma_channel_run(DMA_REC_CH_NUM); 443 /* Begin DMA transfer */
444 rec_start_dma(addr, size);
452} 445}
453 446
454void pcm_rec_dma_close(void) 447void pcm_rec_dma_close(void)
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c
index ae4aa5ef38..3d62fcd1a9 100644
--- a/firmware/target/arm/pcm-telechips.c
+++ b/firmware/target/arm/pcm-telechips.c
@@ -33,7 +33,12 @@ struct dma_data
33{ 33{
34/* NOTE: The order of size and p is important if you use assembler 34/* NOTE: The order of size and p is important if you use assembler
35 optimised fiq handler, so don't change it. */ 35 optimised fiq handler, so don't change it. */
36 uint16_t *p; 36 union
37 {
38 uint16_t *p;
39 const void *p_r;
40 void *p_w;
41 };
37 size_t size; 42 size_t size;
38#if NUM_CORES > 1 43#if NUM_CORES > 1
39 unsigned core; 44 unsigned core;
@@ -143,7 +148,7 @@ static void play_stop_pcm(void)
143 148
144void pcm_play_dma_start(const void *addr, size_t size) 149void pcm_play_dma_start(const void *addr, size_t size)
145{ 150{
146 dma_play_data.p = (uint16_t*)addr; 151 dma_play_data.p_r = addr;
147 dma_play_data.size = size; 152 dma_play_data.size = size;
148 153
149#if NUM_CORES > 1 154#if NUM_CORES > 1
@@ -248,8 +253,9 @@ void fiq_handler(void)
248 * r0-r3 and r12 is a working register. 253 * r0-r3 and r12 is a working register.
249 */ 254 */
250 asm volatile ( 255 asm volatile (
251 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ 256 "sub lr, lr, #4 \n"
252 "mov r4, #0 \n" /* Was the callback called? */ 257 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
258 "mov r14, #0 \n" /* Was the callback called? */
253#if defined(CPU_TCC780X) 259#if defined(CPU_TCC780X)
254 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ 260 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
255 "ldr r9, =0xf3001004 \n" /* CREQ */ 261 "ldr r9, =0xf3001004 \n" /* CREQ */
@@ -260,7 +266,7 @@ void fiq_handler(void)
260 "str r8, [r9] \n" /* clear DAI IRQs */ 266 "str r8, [r9] \n" /* clear DAI IRQs */
261 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ 267 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
262 "cmp r9, #0x10 \n" /* is size <16? */ 268 "cmp r9, #0x10 \n" /* is size <16? */
263 "blt .more_data \n" /* if so, ask pcmbuf for more data */ 269 "blo .more_data \n" /* if so, ask pcmbuf for more data */
264 270
265 ".fill_fifo: \n" 271 ".fill_fifo: \n"
266 "ldr r12, [r8], #4 \n" /* load two samples */ 272 "ldr r12, [r8], #4 \n" /* load two samples */
@@ -282,29 +288,30 @@ void fiq_handler(void)
282 "sub r9, r9, #0x10 \n" /* 4 words written */ 288 "sub r9, r9, #0x10 \n" /* 4 words written */
283 "stmia r11, { r8-r9 } \n" /* save p and size */ 289 "stmia r11, { r8-r9 } \n" /* save p and size */
284 290
285 "cmp r4, #0 \n" /* Callback called? */ 291 "cmp r14, #0 \n" /* Callback called? */
286 "beq .exit \n" 292 "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
287 /* "mov r4, #0 \n" If get_more could be called multiple times! */
288 "ldr r2, =pcm_play_dma_started\n"
289 "ldr r2, [r2] \n"
290 "cmp r2, #0 \n"
291 "blxne r2 \n"
292 293
293 ".exit: \n" 294 "ldr r1, =pcm_play_status_callback \n"
294 "ldmfd sp!, { r0-r4, lr } \n" 295 "ldr r1, [r1] \n"
295 "subs pc, lr, #4 \n" /* FIQ specific return sequence */ 296 "cmp r1, #0 \n"
297 "movne r0, %1 \n"
298 "blxne r1 \n"
299 "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
296 300
297 ".more_data: \n" 301 ".more_data: \n"
298 "mov r4, #1 \n" /* Remember we got more data in this FIQ */ 302 "mov r14, #1 \n" /* Remember we got more data in this FIQ */
299 "ldr r2, =pcm_play_get_more_callback \n" 303 "mov r0, %0 \n" /* r0 = status */
300 "mov r0, r11 \n" /* r0 = &p */ 304 "mov r1, r11 \n" /* r1 = &dma_play_data.p_r */
301 "add r1, r11, #4 \n" /* r1 = &size */ 305 "add r2, r11, #4 \n" /* r2 = &dma_play_data.size */
302 "blx r2 \n" /* call pcm_play_get_more_callback */ 306 "mov lr, pc \n"
303 "ldmia r11, { r8-r9 } \n" /* load new p and size */ 307 "ldr pc, =pcm_play_dma_complete_callback \n"
304 "cmp r9, #0x10 \n" /* did we actually get enough data? */ 308 "cmp r0, #0 \n" /* any more to play? */
305 "bpl .fill_fifo \n" /* not stop and enough? refill */ 309 "ldmneia r11, { r8-r9 } \n" /* load new p and size */
306 "b .exit \n" 310 "cmpne r9, #0x0f \n" /* did we actually get enough data? */
311 "bhi .fill_fifo \n" /* not stop and enough? refill */
312 "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
307 ".ltorg \n" 313 ".ltorg \n"
314 : : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED)
308 ); 315 );
309} 316}
310#else /* C version for reference */ 317#else /* C version for reference */
@@ -316,9 +323,8 @@ void fiq_handler(void)
316 if (dma_play_data.size < 16) 323 if (dma_play_data.size < 16)
317 { 324 {
318 /* p is empty, get some more data */ 325 /* p is empty, get some more data */
319 new_buffer = true; 326 new_buffer = pcm_play_dma_complete_callback(&dma_play_data.p_r,
320 pcm_play_get_more_callback((void**)&dma_play_data.p, 327 &dma_play_data.size);
321 &dma_play_data.size);
322 } 328 }
323 329
324 if (dma_play_data.size >= 16) 330 if (dma_play_data.size >= 16)
@@ -339,7 +345,7 @@ void fiq_handler(void)
339 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; 345 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
340 346
341 if (new_buffer) 347 if (new_buffer)
342 pcm_play_dma_started_callback(); 348 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
343} 349}
344#endif 350#endif
345 351
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c
index 89d56af374..bb11ad32fe 100644
--- a/firmware/target/arm/pnx0101/pcm-pnx0101.c
+++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c
@@ -28,7 +28,7 @@
28short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; 28short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
29short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; 29short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
30 30
31unsigned short* p IBSS_ATTR; 31const int16_t* p IBSS_ATTR;
32size_t p_size IBSS_ATTR; 32size_t p_size IBSS_ATTR;
33 33
34void pcm_play_lock(void) 34void pcm_play_lock(void)
@@ -41,7 +41,7 @@ void pcm_play_unlock(void)
41 41
42void pcm_play_dma_start(const void *addr, size_t size) 42void pcm_play_dma_start(const void *addr, size_t size)
43{ 43{
44 p = (unsigned short*)addr; 44 p = addr;
45 p_size = size; 45 p_size = size;
46} 46}
47 47
@@ -69,7 +69,7 @@ static inline void fill_dma_buf(int offset)
69 do 69 do
70 { 70 {
71 int count; 71 int count;
72 unsigned short *tmp_p; 72 const int16_t *tmp_p;
73 count = MIN(p_size / 4, (size_t)(lend - l)); 73 count = MIN(p_size / 4, (size_t)(lend - l));
74 tmp_p = p; 74 tmp_p = p;
75 p_size -= count * 4; 75 p_size -= count * 4;
@@ -109,16 +109,14 @@ static inline void fill_dma_buf(int offset)
109 if (new_buffer) 109 if (new_buffer)
110 { 110 {
111 new_buffer = false; 111 new_buffer = false;
112 pcm_play_dma_started_callback(); 112 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
113 } 113 }
114 114
115 if (l >= lend) 115 if (l >= lend)
116 return; 116 return;
117 117
118 pcm_play_get_more_callback((void**)&p, &p_size); 118 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
119 119 &p, &p_size);
120 if (p_size)
121 new_buffer = true;
122 } 120 }
123 while (p_size); 121 while (p_size);
124 } 122 }
diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c
index 1b38994f7b..99d46a6096 100644
--- a/firmware/target/arm/pp/pcm-pp.c
+++ b/firmware/target/arm/pp/pcm-pp.c
@@ -30,26 +30,6 @@
30 30
31/** DMA **/ 31/** DMA **/
32 32
33#ifdef CPU_PP502x
34/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
35#define SAMPLE_SIZE 16
36/* DMA Requests from IIS, Memory to peripheral, single transfer,
37 wait for DMA request, interrupt on complete */
38#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
39 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
40 DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
41/* DMA status cannot be viewed from outside code in control because that can
42 * clear the interrupt from outside the handler and prevent the handler from
43 * from being called. Split up transfers to a reasonable size that is good as
44 * a timer, obtaining a keyclick position and peaking yet still keeps the
45 * FIQ count low.
46 */
47#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
48#else
49/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
50#define SAMPLE_SIZE 32
51#endif
52
53struct dma_data 33struct dma_data
54{ 34{
55/* NOTE: The order of size and p is important if you use assembler 35/* NOTE: The order of size and p is important if you use assembler
@@ -57,6 +37,8 @@ struct dma_data
57 union 37 union
58 { 38 {
59 unsigned long addr; 39 unsigned long addr;
40 const void *p_r;
41 void *p_w;
60 uint32_t *p16; /* For packed 16-16 stereo pairs */ 42 uint32_t *p16; /* For packed 16-16 stereo pairs */
61 uint16_t *p32; /* For individual samples converted to 32-bit */ 43 uint16_t *p32; /* For individual samples converted to 32-bit */
62 }; 44 };
@@ -113,56 +95,208 @@ void pcm_dma_apply_settings(void)
113} 95}
114 96
115#if defined(CPU_PP502x) 97#if defined(CPU_PP502x)
116/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ 98/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
117void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) 99#define SAMPLE_SIZE 16
100/* DMA Requests from IIS, Memory to peripheral, single transfer,
101 wait for DMA request, interrupt on complete */
102#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
103 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
104 DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
105/* DMA status cannot be viewed from outside code in control because that can
106 * clear the interrupt from outside the handler and prevent the handler from
107 * from being called. Split up transfers to a reasonable size that is good as
108 * a timer and peaking yet still keeps the FIQ count low.
109 */
110#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
111
112static inline void dma_tx_init(void)
113{
114 /* Enable DMA controller */
115 DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
116 /* FIQ priority for DMA */
117 CPU_INT_PRIORITY |= DMA_MASK;
118 /* Enable request?? Not setting or clearing everything doesn't seem to
119 * prevent it operating. Perhaps important for reliability (how requests
120 * are handled). */
121 DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS;
122 DMA0_STATUS;
123}
124
125static inline void dma_tx_setup(void)
126{
127 /* Setup DMA controller */
128 DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR;
129 DMA0_FLAGS = DMA_FLAGS_UNK26;
130 DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT;
131}
132
133static inline unsigned long dma_tx_buf_prepare(const void *addr)
134{
135 unsigned long a = (unsigned long)addr;
136
137 if (a < UNCACHED_BASE_ADDR) {
138 /* VA in DRAM - writeback all data and get PA */
139 a = UNCACHED_ADDR(a);
140 commit_dcache();
141 }
142
143 return a;
144}
145
146static inline void dma_tx_start(bool begin)
147{
148 size_t size = MAX_DMA_CHUNK_SIZE;
149
150 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less
151 * than a FIFO's worth of data after this transfer? */
152 if (size + 16*4 > dma_play_data.size)
153 size = dma_play_data.size;
154
155 /* Set the new DMA values and activate channel */
156 DMA0_RAM_ADDR = dma_play_data.addr;
157 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
158
159 (void)begin;
160}
161
162static void dma_tx_stop(void)
163{
164 unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */
165 unsigned long cmd = DMA0_CMD;
166 size_t size = 0;
167
168 /* Stop transfer */
169 DMA0_CMD = cmd & ~(DMA_CMD_START | DMA_CMD_INTR);
170
171 /* Wait for not busy + clear int */
172 while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
173
174 if (status & DMA_STATUS_BUSY) {
175 /* Transfer was interrupted - leave what's left */
176 size = (cmd & 0xfffc) - (status & 0xfffc);
177 }
178 else if (status & DMA_STATUS_INTR) {
179 /* Transfer was finished - DMA0_STATUS will have been reloaded
180 * automatically with size in DMA0_CMD. Setup to restart on next
181 * segment. */
182 size = (cmd & 0xfffc) + 4;
183 }
184 /* else not an active state - size = 0 */
185
186 dma_play_data.addr += size;
187 dma_play_data.size -= size;
188
189 if (dma_play_data.size == 0)
190 dma_play_data.addr = 0; /* Entire buffer has completed. */
191}
192
193static inline void dma_tx_lock(void)
194{
195 CPU_INT_DIS = DMA_MASK;
196}
197
198static inline void dma_tx_unlock(void)
118{ 199{
119 bool new_buffer = false; 200 CPU_INT_EN = DMA_MASK;
120 register size_t size; 201}
121 202
203/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
204void fiq_playback(void) ICODE_ATTR __attribute__((interrupt("FIQ")));
205void fiq_playback(void)
206{
122 DMA0_STATUS; /* Clear any pending interrupt */ 207 DMA0_STATUS; /* Clear any pending interrupt */
123 208
124 size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this 209 size_t size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused
125 interrupt */ 210 this interrupt */
126 dma_play_data.addr += size; 211 dma_play_data.addr += size;
127 dma_play_data.size -= size; 212 dma_play_data.size -= size;
128 213
129 while (1) 214 if (LIKELY(dma_play_data.size != 0)) {
130 { 215 /* Begin next segment */
131 if (dma_play_data.size > 0) { 216 dma_tx_start(false);
132 size = MAX_DMA_CHUNK_SIZE; 217 }
133 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less 218 else if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_play_data.p_r,
134 * than a FIFO's worth of data after this transfer? */ 219 &dma_play_data.size)) {
135 if (size + 16*4 > dma_play_data.size) 220 dma_play_data.addr = dma_tx_buf_prepare(dma_play_data.p_r);
136 size = dma_play_data.size; 221 dma_tx_start(false);
137 222 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
138 /* Set the new DMA values and activate channel */ 223 }
139 DMA0_RAM_ADDR = dma_play_data.addr; 224}
140 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
141
142 if (new_buffer)
143 pcm_play_dma_started_callback();
144 return;
145 }
146 225
147 new_buffer = true; 226#else /* !defined (CPU_PP502x) */
148 227
149 /* Buffer empty. Try to get more. */ 228/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
150 pcm_play_get_more_callback((void **)&dma_play_data.addr, 229#define SAMPLE_SIZE 32
151 &dma_play_data.size);
152 230
153 if (dma_play_data.size == 0) { 231static void dma_tx_init(void)
154 /* No more data */ 232{
155 return; 233 /* Set up banked registers for FIQ mode */
156 }
157 234
158 if (dma_play_data.addr < UNCACHED_BASE_ADDR) { 235 /* Use non-banked registers for scratch. */
159 /* Flush any pending cache writes */ 236 register volatile void *iiscfg asm("r0") = &IISCONFIG;
160 dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr); 237 register volatile void *dmapd asm("r1") = &dma_play_data;
161 commit_dcache(); 238
162 } 239 asm volatile (
240 "mrs r2, cpsr \n" /* Save mode and interrupt status */
241 "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */
242 "mov r8, #0 \n"
243 "mov r9, #0 \n"
244 "mov r10, %[iiscfg] \n"
245 "mov r11, %[dmapd] \n"
246 "msr cpsr_c, r2 \n"
247 :
248 : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd)
249 : "r2");
250
251 /* FIQ priority for I2S */
252 CPU_INT_PRIORITY |= IIS_MASK;
253 CPU_INT_EN = IIS_MASK;
254}
255
256static inline void dma_tx_setup(void)
257{
258 /* Nothing to do */
259}
260
261static inline unsigned long dma_tx_buf_prepare(const void *addr)
262{
263 return (unsigned long)addr;
264}
265
266static inline void dma_tx_start(bool begin)
267{
268 if (begin) {
269 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
163 } 270 }
271
272 /* Fill the FIFO or start when data is used up */
273 while (IIS_TX_FREE_COUNT >= 2 && dma_play_data.size != 0) {
274 IISFIFO_WRH = *dma_play_data.p32++;
275 IISFIFO_WRH = *dma_play_data.p32++;
276 dma_play_data.size -= 4;
277 }
278
279 if (begin) {
280 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
281 }
282}
283
284static inline void dma_tx_stop(void)
285{
286 /* Disable TX interrupt */
287 IIS_IRQTX_REG &= ~IIS_IRQTX;
288}
289
290static inline void dma_tx_lock(void)
291{
292 IIS_IRQTX_REG &= ~IIS_IRQTX;
293}
294
295static inline void dma_tx_unlock(void)
296{
297 IIS_IRQTX_REG |= IIS_IRQTX;
164} 298}
165#else 299
166/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by 300/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by
167 * evalutation of free IISFIFO-slots against available source buffer words. 301 * evalutation of free IISFIFO-slots against available source buffer words.
168 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside 302 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside
@@ -173,150 +307,123 @@ void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void)
173 * ASM implementation (not used anymore): GCC fails to make use of the fact 307 * ASM implementation (not used anymore): GCC fails to make use of the fact
174 * that FIQ mode has registers r8-r14 banked, and so does not need to be saved. 308 * that FIQ mode has registers r8-r14 banked, and so does not need to be saved.
175 * This routine uses only these registers, and so will never touch the stack 309 * This routine uses only these registers, and so will never touch the stack
176 * unless it actually needs to do so when calling pcm_callback_for_more. 310 * unless it actually needs to do so when calling pcm_play_dma_complete_callback.
177 * C version is still included below for reference and testing. 311 * C version is still included below for reference and testing.
178 */ 312 */
179#if 1 313#if 1
180void fiq_playback(void) ICODE_ATTR __attribute__((naked)); 314void fiq_playback(void) ICODE_ATTR __attribute__((naked));
181void fiq_playback(void) 315void fiq_playback(void)
182{ 316{
183 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual 317 /* r8 and r9 contains local copies of p and size respectively.
184 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other 318 * r10 contains IISCONFIG address (set during PCM init to minimize code in
185 * addresses we need are generated by using offsets with these two. 319 * FIQ handler.Most other addresses we need are generated by using offsets
186 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG. 320 * from this.
187 * r8 and r9 contains local copies of p and size respectively. 321 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x1c is IISFIFO_CFG.
188 * r0-r3 and r12 is a working register. 322 * r11 contains address of dma_play_data
323 * r12 and r14 are working registers.
324 *
325 * Divided into two blocks: one where no external calls are needed and
326 * one where external callbacks are made
189 */ 327 */
190 asm volatile ( 328 asm volatile (
191 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ 329 /* No external calls */
192 330 "sub lr, lr, #4 \n" /* Prepare return address */
193 "mov r4, #0 \n" /* Was the callback called? */ 331 "stmfd sp!, { lr } \n" /* stack lr so we can use it */
194#if CONFIG_CPU == PP5002 332 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux ... */
195 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ 333 "ldr r12, [r12] \n" /* ... actually a DMA INT ack? */
196 "ldr r12, [r12] \n" 334 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
197#endif 335 "cmp r9, #0 \n" /* is size 0? */
198 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ 336 "beq 1f \n" /* if so, ask PCM for more data */
199 "cmp r9, #0 \n" /* is size 0? */ 337
200 "beq .more_data \n" /* if so, ask pcmbuf for more data */ 338 "ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */
201 339 "and r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */
202#if SAMPLE_SIZE == 16 340 "cmp r9, r14, lsr #22 \n" /* number of words from source */
203 ".check_fifo: \n" 341 "movlo r14, r9, lsl #22 \n" /* r14 = amount of allowed loops */
204 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ 342 "sub r9, r9, r14, lsr #22 \n" /* r14 words will be written in loop */
205 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 16 (PP502x) */ 343 "0: \n"
206 344 "ldr r12, [r8], #4 \n" /* load left-right pair */
207 "mov r1, r0, lsr #16 \n" /* number of free FIFO slots */ 345 "subs r14, r14, #(0x2<<23) \n" /* one more loop? ... */
208 "cmp r1, r9, lsr #2 \n" /* number of words from source */ 346 "strh r12, [r10, #0x40] \n" /* left sample to IISFIFO_WR */
209 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ 347 "mov r12, r12, lsr #16 \n" /* put right sample in bottom 16 bits */
210 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ 348 "strh r12, [r10, #0x40] \n" /* right sample to IISFIFO_WR */
211 349 "bhi 0b \n" /* ... yes, continue */
212 "subs r1, r1, #2 \n" 350
213 ".fifo_loop_2: \n" 351 "cmp r9, #0 \n" /* either FIFO full or size empty? */
214 "ldmgeia r8!, {r2, r12} \n" /* load four samples */ 352 "stmneia r11, { r8-r9 } \n" /* save p and size, if not empty */
215 "strge r2 , [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ 353 "ldmnefd sp!, { pc }^ \n" /* RFE if not empty */
216 "strge r12, [r10, %[wr]] \n" /* write sample 2-3 to IISFIFO_WR */ 354
217 "subges r1, r1, #2 \n" /* one more loop? */ 355 /* Making external calls */
218 "bge .fifo_loop_2 \n" /* yes, continue */ 356 "1: \n"
219 357 "stmfd sp!, { r0-r3 } \n" /* Must save volatiles */
220 "tst r1, #1 \n" /* two samples (one word) left? */ 358 "2: \n"
221 "ldrne r12, [r8], #4 \n" /* load two samples */ 359 "mov r0, %0 \n" /* r0 = status */
222 "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ 360 "mov r1, r11 \n" /* r1 = &dma_play_data.p_r */
223#elif SAMPLE_SIZE == 32 361 "add r2, r11, #4 \n" /* r2 = &dma_play_data.size */
224 ".check_fifo: \n" 362 "ldr r3, =pcm_play_dma_complete_callback \n"
225 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ 363 "mov lr, pc \n" /* long call (not in same section) */
226 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ 364 "bx r3 \n"
227 365 "cmp r0, #0 \n" /* more data? */
228 "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ 366 "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
229 "beq .fifo_fill_complete \n" /* no complete pair? -> exit */ 367
230 "cmp r1, r9, lsr #2 \n" /* number of words from source */ 368 "ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */
231 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ 369 "ands r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */
232 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ 370 "bne 4f \n"
233 371 "3: \n" /* inform of started status if registered */
234 ".fifo_loop: \n" 372 "ldr r1, =pcm_play_status_callback \n"
235 "ldr r12, [r8], #4 \n" /* load two samples */ 373 "ldr r1, [r1] \n"
236 "mov r2 , r12, lsl #16 \n" /* put left sample at the top bits */ 374 "cmp r1, #0 \n"
237 "str r2 , [r10, %[wr]] \n" /* write top sample to IISFIFO_WR */ 375 "movne r0, %1 \n"
238 "str r12, [r10, %[wr]] \n" /* write low sample to IISFIFO_WR*/ 376 "movne lr, pc \n"
239 "subs r1, r1, #1 \n" /* one more loop? */ 377 "bxne r1 \n"
240 "bgt .fifo_loop \n" /* yes, continue */ 378 "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
241 379 "4: \n"
242 ".fifo_fill_complete: \n" 380 "ldmia r11, { r8-r9 } \n" /* load new p and size */
243#endif 381 "cmp r9, r14, lsr #22 \n" /* number of words from source */
244 "cmp r4, #0 \n" /* If fill came after get_more... */ 382 "movlo r14, r9, lsl #22 \n" /* r14 = amount of allowed loops */
245 "beq .still_old_buffer \n" 383 "sub r9, r9, r14, lsr #22 \n" /* r14 words will be written in loop */
246 "mov r4, #0 \n" 384 "0: \n"
247 "ldr r2, =pcm_play_dma_started \n" 385 "ldr r12, [r8], #4 \n" /* load left-right pair */
248 "ldrne r2, [r2] \n" 386 "subs r14, r14, #(0x2<<23) \n" /* one more loop? ... */
249 "cmp r2, #0 \n" 387 "strh r12, [r10, #0x40] \n" /* left sample to IISFIFO_WR */
250 "movne lr, pc \n" 388 "mov r12, r12, lsr #16 \n" /* put right sample in bottom 16 bits */
251 "bxne r2 \n" 389 "strh r12, [r10, #0x40] \n" /* right sample to IISFIFO_WR */
252 390 "bhi 0b \n" /* ... yes, continue */
253 ".still_old_buffer: \n" 391 "stmia r11, { r8-r9 } \n" /* save p and size */
254 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ 392
255 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ 393 "cmp r9, #0 \n" /* used up data in FIFO fill? */
256 394 "bne 3b \n" /* no? -> go return */
257 ".more_data: \n" 395 "b 2b \n" /* yes -> get even more */
258 "mov r4, #1 \n" /* Remember we did this */ 396 ".ltorg \n"
259 "ldr r2, =pcm_play_get_more_callback \n"
260 "mov r0, r11 \n" /* r0 = &p */
261 "add r1, r11, #4 \n" /* r1 = &size */
262 "mov lr, pc \n" /* call pcm_play_get_more_callback */
263 "bx r2 \n"
264 "ldmia r11, { r8-r9 } \n" /* load new p and size */
265 "cmp r9, #0 \n"
266 "bne .check_fifo \n" /* size != 0? refill */
267
268 ".exit: \n" /* (r9=0 if stopping, look above) */
269 "stmia r11, { r8-r9 } \n" /* save p and size */
270 "ldmfd sp!, { r0-r4, lr } \n"
271 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
272 ".ltorg \n"
273 : /* These must only be integers! No regs */ 397 : /* These must only be integers! No regs */
274 : [mask]"i"(IIS_TX_FREE_MASK), 398 : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED));
275 [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG),
276 [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG)
277 );
278} 399}
400
279#else /* C version for reference */ 401#else /* C version for reference */
280void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; 402
281/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ 403/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
404void fiq_playback(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
282void fiq_playback(void) 405void fiq_playback(void)
283{ 406{
284 bool new_buffer = false;
285
286#if CONFIG_CPU == PP5002
287 inl(0xcf001040); 407 inl(0xcf001040);
288#endif
289 408
290 do { 409 if (LIKELY(dma_play_data.size != 0)) {
291 while (dma_play_data.size > 0) { 410 dma_tx_start(false);
292 if (IIS_TX_FREE_COUNT < 2) {
293 if (new_buffer) {
294 new_buffer = false;
295 pcm_play_dma_started_callback();
296 }
297 return;
298 }
299#if SAMPLE_SIZE == 16
300 IISFIFO_WR = *dma_play_data.p16++;
301#elif SAMPLE_SIZE == 32
302 IISFIFO_WR = *dma_play_data.p32++ << 16;
303 IISFIFO_WR = *dma_play_data.p32++ << 16;
304#endif
305 dma_play_data.size -= 4;
306 }
307 411
308 if (new_buffer) { 412 if (dma_play_data.size != 0) {
309 new_buffer = false; 413 /* Still more data */
310 pcm_play_dma_started_callback(); 414 return;
311 } 415 }
416 }
312 417
313 /* p is empty, get some more data */ 418 while (pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_play_data.p_r,
314 pcm_play_get_more_callback((void **)&dma_play_data.addr, 419 &dma_play_data.size)) {
315 &dma_play_data.size); 420 dma_tx_start(false);
316 new_buffer = true; 421 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
317 } while (dma_play_data.size);
318 422
319 /* No more data */ 423 if (dma_play_data.size != 0) {
424 return;
425 }
426 }
320} 427}
321#endif /* ASM / C selection */ 428#endif /* ASM / C selection */
322#endif /* CPU_PP502x */ 429#endif /* CPU_PP502x */
@@ -329,11 +436,7 @@ void pcm_play_lock(void)
329 int status = disable_fiq_save(); 436 int status = disable_fiq_save();
330 437
331 if (++dma_play_data.locked == 1) { 438 if (++dma_play_data.locked == 1) {
332#ifdef CPU_PP502x 439 dma_tx_lock();
333 CPU_INT_DIS = DMA_MASK;
334#else
335 IIS_IRQTX_REG &= ~IIS_IRQTX;
336#endif
337 } 440 }
338 441
339 restore_fiq(status); 442 restore_fiq(status);
@@ -341,89 +444,25 @@ void pcm_play_lock(void)
341 444
342void pcm_play_unlock(void) 445void pcm_play_unlock(void)
343{ 446{
344 int status = disable_fiq_save(); 447 int status = disable_fiq_save();
345 448
346 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { 449 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
347#ifdef CPU_PP502x 450 dma_tx_unlock();
348 CPU_INT_EN = DMA_MASK;
349#else
350 IIS_IRQTX_REG |= IIS_IRQTX;
351#endif
352 } 451 }
353 452
354 restore_fiq(status); 453 restore_fiq(status);
355} 454}
356 455
357static void play_start_pcm(void) 456static void play_start_pcm(void)
358{ 457{
359 fiq_function = fiq_playback; 458 fiq_function = fiq_playback;
360
361#ifdef CPU_PP502x
362 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a
363 * FIFO's worth of data after this transfer? */
364 size_t size = MAX_DMA_CHUNK_SIZE;
365 if (size + 16*4 > dma_play_data.size)
366 size = dma_play_data.size;
367
368 DMA0_RAM_ADDR = dma_play_data.addr;
369 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
370 dma_play_data.state = 1; 459 dma_play_data.state = 1;
371#else 460 dma_tx_start(true);
372 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
373
374 /* Fill the FIFO or start when data is used up */
375 while (1) {
376 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
377 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
378 dma_play_data.state = 1;
379 return;
380 }
381
382#if SAMPLE_SIZE == 16
383 IISFIFO_WR = *dma_play_data.p16++;
384#elif SAMPLE_SIZE == 32
385 IISFIFO_WR = *dma_play_data.p32++ << 16;
386 IISFIFO_WR = *dma_play_data.p32++ << 16;
387#endif
388 dma_play_data.size -= 4;
389 }
390#endif
391} 461}
392 462
393static void play_stop_pcm(void) 463static void play_stop_pcm(void)
394{ 464{
395#ifdef CPU_PP502x 465 dma_tx_stop();
396 unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */
397 unsigned long cmd = DMA0_CMD;
398 size_t size = 0;
399
400 /* Stop transfer */
401 DMA0_CMD = cmd & ~(DMA_CMD_START | DMA_CMD_INTR);
402
403 /* Wait for not busy + clear int */
404 while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
405
406 if (status & DMA_STATUS_BUSY) {
407 /* Transfer was interrupted - leave what's left */
408 size = (cmd & 0xfffc) - (status & 0xfffc);
409 }
410 else if (status & DMA_STATUS_INTR) {
411 /* Transfer was finished - DMA0_STATUS will have been reloaded
412 * automatically with size in DMA0_CMD. Setup to restart on next
413 * segment. */
414 size = (cmd & 0xfffc) + 4;
415 }
416 /* else not an active state - size = 0 */
417
418 dma_play_data.addr += size;
419 dma_play_data.size -= size;
420
421 if (dma_play_data.size == 0)
422 dma_play_data.addr = 0; /* Entire buffer has completed. */
423#else
424 /* Disable TX interrupt */
425 IIS_IRQTX_REG &= ~IIS_IRQTX;
426#endif
427 466
428 /* Wait for FIFO to empty */ 467 /* Wait for FIFO to empty */
429 while (!IIS_TX_IS_EMPTY); 468 while (!IIS_TX_IS_EMPTY);
@@ -433,30 +472,17 @@ static void play_stop_pcm(void)
433 472
434void pcm_play_dma_start(const void *addr, size_t size) 473void pcm_play_dma_start(const void *addr, size_t size)
435{ 474{
475 pcm_play_dma_stop();
476
436#if NUM_CORES > 1 477#if NUM_CORES > 1
437 /* This will become more important later - and different ! */ 478 /* This will become more important later - and different ! */
438 dma_play_data.core = processor_id(); /* save initiating core */ 479 dma_play_data.core = processor_id(); /* save initiating core */
439#endif 480#endif
440 481
441 pcm_play_dma_stop(); 482 dma_tx_setup();
442
443#ifdef CPU_PP502x
444 if ((unsigned long)addr < UNCACHED_BASE_ADDR) {
445 /* Flush any pending cache writes */
446 addr = UNCACHED_ADDR(addr);
447 commit_dcache();
448 }
449 483
450 dma_play_data.addr = (unsigned long)addr; 484 dma_play_data.addr = dma_tx_buf_prepare(addr);
451 dma_play_data.size = size; 485 dma_play_data.size = size;
452 DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR;
453 DMA0_FLAGS = DMA_FLAGS_UNK26;
454 DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT;
455#else
456 dma_play_data.addr = (unsigned long)addr;
457 dma_play_data.size = size;
458#endif
459
460 play_start_pcm(); 486 play_start_pcm();
461} 487}
462 488
@@ -490,39 +516,7 @@ void pcm_play_dma_init(void)
490 /* Initialize default register values. */ 516 /* Initialize default register values. */
491 audiohw_init(); 517 audiohw_init();
492 518
493#ifdef CPU_PP502x 519 dma_tx_init();
494 /* Enable DMA controller */
495 DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
496 /* FIQ priority for DMA */
497 CPU_INT_PRIORITY |= DMA_MASK;
498 /* Enable request?? Not setting or clearing everything doesn't seem to
499 * prevent it operating. Perhaps important for reliability (how requests
500 * are handled). */
501 DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS;
502 DMA0_STATUS;
503#else
504 /* Set up banked registers for FIQ mode */
505
506 /* Use non-banked registers for scratch. */
507 register volatile void *iiscfg asm("r0") = &IISCONFIG;
508 register volatile void *dmapd asm("r1") = &dma_play_data;
509
510 asm volatile (
511 "mrs r2, cpsr \n" /* Save mode and interrupt status */
512 "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */
513 "mov r8, #0 \n"
514 "mov r9, #0 \n"
515 "mov r10, %[iiscfg] \n"
516 "mov r11, %[dmapd] \n"
517 "msr cpsr_c, r2 \n"
518 :
519 : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd)
520 : "r2");
521
522 /* FIQ priority for I2S */
523 CPU_INT_PRIORITY |= IIS_MASK;
524 CPU_INT_EN = IIS_MASK;
525#endif
526 520
527 IISCONFIG |= IIS_TXFIFOEN; 521 IISCONFIG |= IIS_TXFIFOEN;
528} 522}
@@ -649,11 +643,15 @@ void fiq_record(void)
649 } 643 }
650 } 644 }
651 645
652 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, 646 if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, &dma_rec_data.p_w,
653 &dma_rec_data.size); 647 &dma_rec_data.size))
648 {
649 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
650 }
654} 651}
655 652
656#else 653#else /* !(SANSA_C200 || SANSA_E200) */
654
657void fiq_record(void) 655void fiq_record(void)
658{ 656{
659 while (dma_rec_data.size > 0) { 657 while (dma_rec_data.size > 0) {
@@ -664,17 +662,20 @@ void fiq_record(void)
664#if SAMPLE_SIZE == 16 662#if SAMPLE_SIZE == 16
665 *dma_rec_data.p16++ = IISFIFO_RD; 663 *dma_rec_data.p16++ = IISFIFO_RD;
666#elif SAMPLE_SIZE == 32 664#elif SAMPLE_SIZE == 32
667 *dma_rec_data.p32++ = IISFIFO_RD >> 16; 665 *dma_rec_data.p32++ = IISFIFO_RDH;
668 *dma_rec_data.p32++ = IISFIFO_RD >> 16; 666 *dma_rec_data.p32++ = IISFIFO_RDH;
669#endif 667#endif
670 dma_rec_data.size -= 4; 668 dma_rec_data.size -= 4;
671 } 669 }
672 670
673 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, 671 if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, &dma_rec_data.p_w,
674 &dma_rec_data.size); 672 &dma_rec_data.size))
673 {
674 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
675 }
675} 676}
676 677
677#endif /* SANSA_E200 */ 678#endif /* SANSA_C200 || SANSA_E200 */
678 679
679void pcm_rec_dma_stop(void) 680void pcm_rec_dma_stop(void)
680{ 681{
diff --git a/firmware/target/arm/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c
index 80a8d462ea..e4318de408 100644
--- a/firmware/target/arm/rk27xx/pcm-rk27xx.c
+++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c
@@ -273,15 +273,13 @@ size_t pcm_get_bytes_waiting(void)
273/* audio DMA ISR called when chunk from callers buffer has been transfered */ 273/* audio DMA ISR called when chunk from callers buffer has been transfered */
274void INT_HDMA(void) 274void INT_HDMA(void)
275{ 275{
276 void *start; 276 const void *start;
277 size_t size; 277 size_t size;
278 278
279 pcm_play_get_more_callback(&start, &size); 279 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
280
281 if (size != 0)
282 { 280 {
283 hdma_i2s_transfer(start, size); 281 hdma_i2s_transfer(start, size);
284 pcm_play_dma_started_callback(); 282 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
285 } 283 }
286} 284}
287 285
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
index 35905645dd..a1c854a0df 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -215,16 +215,14 @@ void pcm_play_dma_pause(bool pause)
215 215
216void fiq_handler(void) 216void fiq_handler(void)
217{ 217{
218 static void *start; 218 static const void *start;
219 static size_t size; 219 static size_t size;
220 220
221 /* clear any pending interrupt */ 221 /* clear any pending interrupt */
222 SRCPND = DMA2_MASK; 222 SRCPND = DMA2_MASK;
223 223
224 /* Buffer empty. Try to get more. */ 224 /* Buffer empty. Try to get more. */
225 pcm_play_get_more_callback(&start, &size); 225 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
226
227 if (size == 0)
228 return; 226 return;
229 227
230 /* Flush any pending cache writes */ 228 /* Flush any pending cache writes */
@@ -237,7 +235,7 @@ void fiq_handler(void)
237 /* Re-Activate the channel */ 235 /* Re-Activate the channel */
238 DMASKTRIG2 = 0x2; 236 DMASKTRIG2 = 0x2;
239 237
240 pcm_play_dma_started_callback(); 238 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
241} 239}
242 240
243size_t pcm_get_bytes_waiting(void) 241size_t pcm_get_bytes_waiting(void)
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
index a4f58a8e06..943cbb2ade 100644
--- a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
+++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
@@ -255,16 +255,14 @@ void pcm_play_dma_pause(bool pause)
255 255
256void fiq_handler(void) 256void fiq_handler(void)
257{ 257{
258 static void *start; 258 static const void *start;
259 static size_t size; 259 static size_t size;
260 260
261 /* clear any pending interrupt */ 261 /* clear any pending interrupt */
262 SRCPND = DMA2_MASK; 262 SRCPND = DMA2_MASK;
263 263
264 /* Buffer empty. Try to get more. */ 264 /* Buffer empty. Try to get more. */
265 pcm_play_get_more_callback(&start, &size); 265 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
266
267 if (size == 0)
268 return; 266 return;
269 267
270 /* Flush any pending cache writes */ 268 /* Flush any pending cache writes */
@@ -277,7 +275,7 @@ void fiq_handler(void)
277 /* Re-Activate the channel */ 275 /* Re-Activate the channel */
278 DMASKTRIG2 = 0x2; 276 DMASKTRIG2 = 0x2;
279 277
280 pcm_play_dma_started_callback(); 278 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
281} 279}
282 280
283size_t pcm_get_bytes_waiting(void) 281size_t pcm_get_bytes_waiting(void)
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c
index 7b4258fa68..c5a5bcf74f 100644
--- a/firmware/target/arm/s5l8700/pcm-s5l8700.c
+++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c
@@ -116,9 +116,10 @@ void INT_DMA(void)
116 { 116 {
117 if (!nextsize) 117 if (!nextsize)
118 { 118 {
119 pcm_play_get_more_callback((void**)&nextbuf, &nextsize); 119 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
120 if (!nextsize) break; 120 (const void**)&nextbuf, &nextsize);
121 new_buffer = true; 121 if (!new_buffer)
122 break;
122 } 123 }
123 queuedsize = MIN(sizeof(dblbuf), nextsize / 2); 124 queuedsize = MIN(sizeof(dblbuf), nextsize / 2);
124 nextsize -= queuedsize; 125 nextsize -= queuedsize;
@@ -133,7 +134,7 @@ void INT_DMA(void)
133 134
134 if (new_buffer) 135 if (new_buffer)
135 { 136 {
136 pcm_play_dma_started_callback(); 137 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
137 new_buffer = false; 138 new_buffer = false;
138 } 139 }
139 } 140 }
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c
index b58ef0f4d3..1442afa420 100644
--- a/firmware/target/arm/s5l8702/pcm-s5l8702.c
+++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c
@@ -65,7 +65,7 @@ void INT_DMAC0C0(void)
65 DMAC0INTTCCLR = 1; 65 DMAC0INTTCCLR = 1;
66 if (!pcm_remaining) 66 if (!pcm_remaining)
67 { 67 {
68 pcm_play_get_more_callback((void**)&dataptr, &pcm_remaining); 68 pcm_play_dma_complete_callback((const void**)&dataptr, &pcm_remaining);
69 pcm_chunksize = pcm_remaining; 69 pcm_chunksize = pcm_remaining;
70 } 70 }
71 if (!pcm_remaining) 71 if (!pcm_remaining)
@@ -115,7 +115,7 @@ void INT_DMAC0C0(void)
115 } 115 }
116 else DMAC0C0NEXTLLI = pcm_lli; 116 else DMAC0C0NEXTLLI = pcm_lli;
117 117
118 pcm_play_dma_started_callback(); 118 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
119} 119}
120 120
121void pcm_play_dma_start(const void* addr, size_t size) 121void pcm_play_dma_start(const void* addr, size_t size)
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
index 91d6b66130..d23c93de39 100644
--- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
+++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
@@ -33,7 +33,7 @@
33/* This is global to save some latency when pcm_play_dma_get_peak_buffer is 33/* This is global to save some latency when pcm_play_dma_get_peak_buffer is
34 * called. 34 * called.
35 */ 35 */
36static void *start; 36static const void *start;
37 37
38void pcm_play_dma_postinit(void) 38void pcm_play_dma_postinit(void)
39{ 39{
@@ -164,9 +164,7 @@ void DSPHINT(void)
164 164
165 case MSG_REFILL: 165 case MSG_REFILL:
166 /* Buffer empty. Try to get more. */ 166 /* Buffer empty. Try to get more. */
167 pcm_play_get_more_callback(&start, &size); 167 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
168
169 if (size != 0)
170 { 168 {
171 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; 169 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START;
172 /* Flush any pending cache writes */ 170 /* Flush any pending cache writes */
@@ -180,7 +178,7 @@ void DSPHINT(void)
180 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", 178 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx",
181 (unsigned long)start, (unsigned long)sdem_addr); 179 (unsigned long)start, (unsigned long)sdem_addr);
182 180
183 pcm_play_dma_started_callback(); 181 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
184 } 182 }
185 183
186 break; 184 break;
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
index 8b1fbf95e4..6e640bdf12 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
@@ -34,7 +34,7 @@
34/* This is global to save some latency when pcm_play_dma_get_peak_buffer is 34/* This is global to save some latency when pcm_play_dma_get_peak_buffer is
35 * called. 35 * called.
36 */ 36 */
37static void *start; 37static const void *start;
38static int dma_channel; 38static int dma_channel;
39 39
40void pcm_play_dma_postinit(void) 40void pcm_play_dma_postinit(void)
@@ -171,9 +171,7 @@ void DSPHINT(void)
171 171
172 case MSG_REFILL: 172 case MSG_REFILL:
173 /* Buffer empty. Try to get more. */ 173 /* Buffer empty. Try to get more. */
174 pcm_play_get_more_callback(&start, &size); 174 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
175
176 if (size != 0)
177 { 175 {
178 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; 176 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START;
179 /* Flush any pending cache writes */ 177 /* Flush any pending cache writes */
@@ -187,7 +185,7 @@ void DSPHINT(void)
187 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", 185 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx",
188 (unsigned long)start, (unsigned long)sdem_addr); 186 (unsigned long)start, (unsigned long)sdem_addr);
189 187
190 pcm_play_dma_started_callback(); 188 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
191 } 189 }
192 190
193 break; 191 break;
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c
index e95d445337..2e2312f7ae 100644
--- a/firmware/target/coldfire/pcm-coldfire.c
+++ b/firmware/target/coldfire/pcm-coldfire.c
@@ -294,8 +294,6 @@ void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
294void DMA0(void) 294void DMA0(void)
295{ 295{
296 unsigned long res = DSR0; 296 unsigned long res = DSR0;
297 void *start;
298 size_t size;
299 297
300 and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */ 298 and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */
301 DSR0 = 1; /* Clear interrupt and errors */ 299 DSR0 = 1; /* Clear interrupt and errors */
@@ -311,17 +309,18 @@ void DMA0(void)
311#endif 309#endif
312 } 310 }
313 311
314 /* Force stop on error */ 312 const void *addr;
315 pcm_play_get_more_callback((res & 0x70) ? NULL : &start, &size); 313 size_t size;
316 314
317 if (size != 0) 315 if (pcm_play_dma_complete_callback((res & 0x70) ?
316 PCM_DMAST_ERR_DMA : PCM_DMAST_OK,
317 &addr, &size))
318 { 318 {
319 SAR0 = (unsigned long)start; /* Source address */ 319 SAR0 = (unsigned long)addr; /* Source address */
320 BCR0 = size; /* Bytes to transfer */ 320 BCR0 = (unsigned long)size; /* Bytes to transfer */
321 or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */ 321 or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */
322 322
323 /* Call buffer callback */ 323 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
324 pcm_play_dma_started_callback();
325 } 324 }
326 /* else inished playing */ 325 /* else inished playing */
327} /* DMA0 */ 326} /* DMA0 */
@@ -368,7 +367,7 @@ void pcm_rec_unlock(void)
368 367
369void pcm_rec_dma_start(void *addr, size_t size) 368void pcm_rec_dma_start(void *addr, size_t size)
370{ 369{
371 /* stop any DMA in progress */ 370 /* Stop any DMA in progress */
372 pcm_rec_dma_stop(); 371 pcm_rec_dma_stop();
373 372
374 and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); 373 and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
@@ -430,16 +429,14 @@ void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
430void DMA1(void) 429void DMA1(void)
431{ 430{
432 unsigned long res = DSR1; 431 unsigned long res = DSR1;
433 int status = 0; 432 enum pcm_dma_status status = PCM_DMAST_OK;
434 void *start;
435 size_t size;
436 433
437 and_l(~(DMA_EEXT | DMA_INT), &DCR1); /* per request and int OFF */ 434 and_l(~(DMA_EEXT | DMA_INT), &DCR1); /* per request and int OFF */
438 DSR1 = 1; /* Clear interrupt and errors */ 435 DSR1 = 1; /* Clear interrupt and errors */
439 436
440 if (res & 0x70) 437 if (res & 0x70)
441 { 438 {
442 status = DMA_REC_ERROR_DMA; 439 status = PCM_DMAST_ERR_DMA;
443 logf("DMA1 err: %02x", res); 440 logf("DMA1 err: %02x", res);
444#if 0 441#if 0
445 logf(" SAR1: %08x", SAR1); 442 logf(" SAR1: %08x", SAR1);
@@ -456,19 +453,22 @@ void DMA1(void)
456 * Ignore valnogood since several sources don't set it properly. */ 453 * Ignore valnogood since several sources don't set it properly. */
457 /* clear: ebu1cnew, symbolerr, parityerr */ 454 /* clear: ebu1cnew, symbolerr, parityerr */
458 INTERRUPTCLEAR = (1 << 25) | (1 << 23) | (1 << 22); 455 INTERRUPTCLEAR = (1 << 25) | (1 << 23) | (1 << 22);
459 status = DMA_REC_ERROR_SPDIF; 456 status = PCM_DMAST_ERR_SPDIF;
460 logf("spdif err"); 457 logf("spdif err");
461 } 458 }
462#endif 459#endif
463 460
464 /* Inform PCM we have more data (or error) */ 461 /* Inform PCM we have more data (or error) */
465 pcm_rec_more_ready_callback(status, &start, &size); 462 void *addr;
463 size_t size;
466 464
467 if (size != 0) 465 if (pcm_rec_dma_complete_callback(status, &addr, &size))
468 { 466 {
469 DAR1 = (unsigned long)start; /* Destination address */ 467 DAR1 = (unsigned long)addr; /* Destination address */
470 BCR1 = (unsigned long)size; /* Bytes to transfer */ 468 BCR1 = (unsigned long)size; /* Bytes to transfer */
471 or_l(DMA_EEXT | DMA_INT, &DCR1); /* per request and int ON */ 469 or_l(DMA_EEXT | DMA_INT, &DCR1); /* per request and int ON */
470
471 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
472 } 472 }
473} /* DMA1 */ 473} /* DMA1 */
474 474
diff --git a/firmware/target/hosted/android/pcm-android.c b/firmware/target/hosted/android/pcm-android.c
index 4e58707d0a..7a0f28634e 100644
--- a/firmware/target/hosted/android/pcm-android.c
+++ b/firmware/target/hosted/android/pcm-android.c
@@ -80,8 +80,8 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this,
80 80
81 if (!pcm_data_size) /* get some initial data */ 81 if (!pcm_data_size) /* get some initial data */
82 { 82 {
83 new_buffer = true; 83 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
84 pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size); 84 (const void**)&pcm_data_start, &pcm_data_size);
85 } 85 }
86 86
87 while(left > 0 && pcm_data_size) 87 while(left > 0 && pcm_data_size)
@@ -99,7 +99,7 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this,
99 if (new_buffer) 99 if (new_buffer)
100 { 100 {
101 new_buffer = false; 101 new_buffer = false;
102 pcm_play_dma_started_callback(); 102 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
103 103
104 /* NOTE: might need to release the mutex and sleep here if the 104 /* NOTE: might need to release the mutex and sleep here if the
105 buffer is shorter than the required buffer (like pcm-sdl.c) to 105 buffer is shorter than the required buffer (like pcm-sdl.c) to
@@ -114,15 +114,15 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this,
114 114
115 if (pcm_data_size == 0) /* need new data */ 115 if (pcm_data_size == 0) /* need new data */
116 { 116 {
117 new_buffer = true; 117 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
118 pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size); 118 (const void**)&pcm_data_start, &pcm_data_size);
119 } 119 }
120 else /* increment data pointer and feed more */ 120 else /* increment data pointer and feed more */
121 pcm_data_start += transfer_size; 121 pcm_data_start += transfer_size;
122 } 122 }
123 123
124 if (new_buffer && pcm_data_size) 124 if (new_buffer)
125 pcm_play_dma_started_callback(); 125 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
126 126
127 unlock_audio(); 127 unlock_audio();
128 return max_size - left; 128 return max_size - left;
@@ -154,7 +154,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
154 154
155void pcm_play_dma_stop(void) 155void pcm_play_dma_stop(void)
156{ 156{
157 /* NOTE: due to how pcm_play_get_more_callback() works, this is 157 /* NOTE: due to how pcm_play_dma_complete_callback() works, this is
158 * possibly called from nativeWrite(), i.e. another (host) thread 158 * possibly called from nativeWrite(), i.e. another (host) thread
159 * => need to discover the appropriate JNIEnv* */ 159 * => need to discover the appropriate JNIEnv* */
160 JNIEnv* env = getJavaEnvironment(); 160 JNIEnv* env = getJavaEnvironment();
diff --git a/firmware/target/hosted/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c
index e5620d0702..61f33cbadd 100644
--- a/firmware/target/hosted/maemo/pcm-gstreamer.c
+++ b/firmware/target/hosted/maemo/pcm-gstreamer.c
@@ -189,9 +189,8 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused)
189 from inside gstreamer's stream thread as it will deadlock */ 189 from inside gstreamer's stream thread as it will deadlock */
190 inside_feed_data = 1; 190 inside_feed_data = 1;
191 191
192 pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); 192 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, (const void **)&pcm_data,
193 193 &pcm_data_size))
194 if (pcm_data_size != 0)
195 { 194 {
196 GstBuffer *buffer = gst_buffer_new (); 195 GstBuffer *buffer = gst_buffer_new ();
197 GstFlowReturn ret; 196 GstFlowReturn ret;
@@ -205,7 +204,7 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused)
205 if (ret != 0) 204 if (ret != 0)
206 DEBUGF("push-buffer error result: %d\n", ret); 205 DEBUGF("push-buffer error result: %d\n", ret);
207 206
208 pcm_play_dma_started_callback(); 207 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
209 } else 208 } else
210 { 209 {
211 DEBUGF("feed_data: No Data.\n"); 210 DEBUGF("feed_data: No Data.\n");
diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c
index b78993dd0a..1385f75b34 100644
--- a/firmware/target/hosted/pcm-alsa.c
+++ b/firmware/target/hosted/pcm-alsa.c
@@ -223,9 +223,11 @@ static bool fill_frames(void)
223 if (!pcm_size) 223 if (!pcm_size)
224 { 224 {
225 new_buffer = true; 225 new_buffer = true;
226 pcm_play_get_more_callback((void **)&pcm_data, &pcm_size); 226 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK,
227 if (!pcm_size || !pcm_data) 227 (const void **)&pcm_data, &pcm_size))
228 {
228 return false; 229 return false;
230 }
229 } 231 }
230 copy_n = MIN((ssize_t)pcm_size, frames_left*4); 232 copy_n = MIN((ssize_t)pcm_size, frames_left*4);
231 memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n); 233 memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n);
@@ -237,7 +239,7 @@ static bool fill_frames(void)
237 if (new_buffer) 239 if (new_buffer)
238 { 240 {
239 new_buffer = false; 241 new_buffer = false;
240 pcm_play_dma_started_callback(); 242 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
241 } 243 }
242 } 244 }
243 return true; 245 return true;
diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c
index 020928d572..2c535b2dc5 100644
--- a/firmware/target/hosted/sdl/pcm-sdl.c
+++ b/firmware/target/hosted/sdl/pcm-sdl.c
@@ -56,7 +56,7 @@ static int sim_volume = 0;
56#if CONFIG_CODEC == SWCODEC 56#if CONFIG_CODEC == SWCODEC
57static int cvt_status = -1; 57static int cvt_status = -1;
58 58
59static Uint8* pcm_data; 59static const Uint8* pcm_data;
60static size_t pcm_data_size; 60static size_t pcm_data_size;
61static size_t pcm_sample_bytes; 61static size_t pcm_sample_bytes;
62static size_t pcm_channel_bytes; 62static size_t pcm_channel_bytes;
@@ -109,7 +109,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
109{ 109{
110 pcm_dma_apply_settings_nolock(); 110 pcm_dma_apply_settings_nolock();
111 111
112 pcm_data = (Uint8 *) addr; 112 pcm_data = addr;
113 pcm_data_size = size; 113 pcm_data_size = size;
114 114
115 SDL_PauseAudio(0); 115 SDL_PauseAudio(0);
@@ -245,48 +245,48 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
245 245
246 /* Audio card wants more? Get some more then. */ 246 /* Audio card wants more? Get some more then. */
247 while (len > 0) { 247 while (len > 0) {
248 new_buffer = true; 248 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
249 pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); 249 (const void **)&pcm_data, &pcm_data_size);
250
251 if (!new_buffer) {
252 DEBUGF("sdl_audio_callback: No Data.\n");
253 break;
254 }
255
250 start: 256 start:
251 if (pcm_data_size != 0) { 257 udata->num_in = pcm_data_size / pcm_sample_bytes;
252 udata->num_in = pcm_data_size / pcm_sample_bytes; 258 udata->num_out = len / pcm_sample_bytes;
253 udata->num_out = len / pcm_sample_bytes;
254 259
255 write_to_soundcard(udata); 260 write_to_soundcard(udata);
256 261
257 udata->num_in *= pcm_sample_bytes; 262 udata->num_in *= pcm_sample_bytes;
258 udata->num_out *= pcm_sample_bytes; 263 udata->num_out *= pcm_sample_bytes;
259 264
265 if (new_buffer)
266 {
267 new_buffer = false;
268 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
260 269
261 if (new_buffer) 270 if ((size_t)len > udata->num_out)
262 { 271 {
263 new_buffer = false; 272 int delay = pcm_data_size*250 / pcm_sampr - 1;
264 pcm_play_dma_started_callback();
265 273
266 if ((size_t)len > udata->num_out) 274 if (delay > 0)
267 { 275 {
268 int delay = pcm_data_size*250 / pcm_sampr - 1; 276 SDL_UnlockMutex(audio_lock);
269 277 SDL_Delay(delay);
270 if (delay > 0) 278 SDL_LockMutex(audio_lock);
271 { 279
272 SDL_UnlockMutex(audio_lock); 280 if (!pcm_is_playing())
273 SDL_Delay(delay); 281 break;
274 SDL_LockMutex(audio_lock);
275
276 if (!pcm_is_playing())
277 break;
278 }
279 } 282 }
280 } 283 }
281
282 pcm_data += udata->num_in;
283 pcm_data_size -= udata->num_in;
284 udata->stream += udata->num_out;
285 len -= udata->num_out;
286 } else {
287 DEBUGF("sdl_audio_callback: No Data.\n");
288 break;
289 } 284 }
285
286 pcm_data += udata->num_in;
287 pcm_data_size -= udata->num_in;
288 udata->stream += udata->num_out;
289 len -= udata->num_out;
290 } 290 }
291 291
292 SDL_UnlockMutex(audio_lock); 292 SDL_UnlockMutex(audio_lock);
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
index 1ed413c9ae..83d3646ed1 100644
--- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
@@ -63,7 +63,7 @@ void pcm_dma_apply_settings(void)
63 audiohw_set_frequency(pcm_sampr); 63 audiohw_set_frequency(pcm_sampr);
64} 64}
65 65
66static void* playback_address; 66static const void* playback_address;
67static inline void set_dma(const void *addr, size_t size) 67static inline void set_dma(const void *addr, size_t size)
68{ 68{
69 int burst_size; 69 int burst_size;
@@ -96,21 +96,19 @@ static inline void set_dma(const void *addr, size_t size)
96 REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT; 96 REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT;
97 REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | burst_size | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE); 97 REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | burst_size | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE);
98 98
99 playback_address = (void*)addr; 99 playback_address = addr;
100} 100}
101 101
102static inline void play_dma_callback(void) 102static inline void play_dma_callback(void)
103{ 103{
104 void *start; 104 const void *start;
105 size_t size; 105 size_t size;
106 106
107 pcm_play_get_more_callback(&start, &size); 107 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
108
109 if (size != 0)
110 { 108 {
111 set_dma(start, size); 109 set_dma(start, size);
112 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; 110 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
113 pcm_play_dma_started_callback(); 111 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
114 } 112 }
115} 113}
116 114