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))); /* Callback to generate the beep frames - also don't want inlining of call below in beep_play */ static void __attribute__((noinline)) -beep_get_more(unsigned char **start, size_t *size) +beep_get_more(const void **start, size_t *size) { int count = beep_count; @@ -87,7 +87,7 @@ void beep_play(unsigned int frequency, unsigned int duration, #endif /* If it fits - avoid cb overhead */ - unsigned char *start; + const void *start; size_t size; /* 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) /** Playback */ /* PCM driver callback */ -static void pcmbuf_pcm_callback(unsigned char **start, size_t *size) +static void pcmbuf_pcm_callback(const void **start, size_t *size) { /*- Process the chunk that just finished -*/ 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); #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 216 +#define PLUGIN_API_VERSION 217 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 216 +#define PLUGIN_MIN_API_VERSION 217 /* plugin return codes */ /* internal returns start at 0x100 to make exit(1..255) work */ @@ -651,7 +651,8 @@ struct plugin_api { const unsigned long *hw_freq_sampr; void (*pcm_apply_settings)(void); void (*pcm_play_data)(pcm_play_callback_type get_more, - unsigned char* start, size_t size); + pcm_status_callback_type status_cb, + const void *start, size_t size); void (*pcm_play_stop)(void); void (*pcm_set_frequency)(unsigned int frequency); bool (*pcm_is_playing)(void); @@ -669,6 +670,7 @@ struct plugin_api { void (*pcm_init_recording)(void); void (*pcm_close_recording)(void); void (*pcm_record_data)(pcm_rec_callback_type more_ready, + pcm_status_callback_type status_cb, void *start, size_t size); void (*pcm_stop_recording)(void); void (*pcm_calculate_rec_peaks)(int *left, int *right); @@ -689,12 +691,13 @@ struct plugin_api { int (*dsp_output_count)(struct dsp_config *dsp, int count); enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel); - void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, int *count); + const void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, + int *count); void (*mixer_channel_calculate_peaks)(enum pcm_mixer_channel channel, int *left, int *right); void (*mixer_channel_play_data)(enum pcm_mixer_channel channel, pcm_play_callback_type get_more, - unsigned char *start, size_t size); + const void *start, size_t size); void (*mixer_channel_play_pause)(enum pcm_mixer_channel channel, bool play); void (*mixer_channel_stop)(enum pcm_mixer_channel channel); 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) rb->lcd_update(); } -void get_more(unsigned char** start, size_t* size) +void get_more(const void** start, size_t* size) { #ifndef SYNC if(lastswap!=swap) @@ -523,10 +523,10 @@ void get_more(unsigned char** start, size_t* size) *size = BUF_SIZE*sizeof(short); #ifndef SYNC - *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); + *start = swap ? gmbuf : gmbuf + BUF_SIZE; swap=!swap; #else - *start = (unsigned char*)(gmbuf); + *start = gmbuf; #endif } @@ -537,7 +537,7 @@ int beatboxmain() numberOfSamples=44100/10; synthbuf(); - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); rb->lcd_set_background(0x000000); 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 ) // only output be done asynchronous? // -void get_more(unsigned char** start, size_t* size) +void get_more(const void** start, size_t* size) { I_UpdateSound(); // Force sound update - *start = (unsigned char*)(mixbuffer); + *start = mixbuffer; *size = SAMPLECOUNT*2*sizeof(short); } @@ -471,7 +471,7 @@ void I_SubmitSound(void) if (!enable_sound) return; - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); } void 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) static inline bool fft_get_fft(void) { int count; - int16_t *value = - (int16_t *) rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count); + const int16_t *value = + rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count); /* This block can introduce discontinuities in our data. Meaning, the * FFT will not be done a continuous segment of the signal. Which can * 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) static void play_tock(void) { - rb->pcm_play_data(NULL,(unsigned char *)sndbuf,sizeof(sndbuf)); + rb->pcm_play_data(NULL, NULL, sndbuf, sizeof(sndbuf)); } #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) samples_in_buf = BUF_SIZE-i; } -static void get_more(unsigned char** start, size_t* size) +static void get_more(const void** start, size_t* size) { #ifndef SYNC if(lastswap != swap) @@ -333,10 +333,10 @@ static void get_more(unsigned char** start, size_t* size) *size = samples_in_buf*sizeof(int32_t); #ifndef SYNC - *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); + *start = swap ? gmbuf : gmbuf + BUF_SIZE; swap = !swap; #else - *start = (unsigned char*)(gmbuf); + *start = gmbuf; #endif } @@ -396,7 +396,7 @@ static int midimain(const void * filename) samples_this_second = 0; synthbuf(); - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); while (!quit) { @@ -445,7 +445,7 @@ static int midimain(const void * filename) seekBackward(5); midi_debug("Rewind to %d:%02d\n", playing_time/60, playing_time%60); if (is_playing) - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); break; } @@ -455,7 +455,7 @@ static int midimain(const void * filename) seekForward(5); midi_debug("Skip to %d:%02d\n", playing_time/60, playing_time%60); if (is_playing) - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); break; } @@ -470,7 +470,7 @@ static int midimain(const void * filename) { midi_debug("Playing from %d:%02d\n", playing_time/60, playing_time%60); is_playing = true; - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); } break; } 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) VC_WriteBytes(outptr, BUF_SIZE); } -void get_more(unsigned char** start, size_t* size) +void get_more(const void** start, size_t* size) { #ifndef SYNC if (lastswap != swap) @@ -282,10 +282,10 @@ void get_more(unsigned char** start, size_t* size) *size = BUF_SIZE; #ifndef SYNC - *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE)); + *start = swap ? gmbuf : gmbuf + BUF_SIZE; swap = !swap; #else - *start = (unsigned char*)(gmbuf); + *start = gmbuf; #endif } @@ -660,7 +660,7 @@ int playfile(char* filename) { display = DISPLAY_INFO; Player_Start(module); - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); } #ifdef HAVE_ADJUSTABLE_CPU_FREQ @@ -804,7 +804,7 @@ int playfile(char* filename) } else { - rb->pcm_play_data(&get_more, NULL, 0); + rb->pcm_play_data(&get_more, NULL, NULL, 0); } Player_TogglePause(); 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) } /* Audio DMA handler */ -static void get_more(unsigned char **start, size_t *size) +static void get_more(const void **start, size_t *size) { ssize_t sz; 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; /* Audio callback */ -static void get_more(unsigned char **start, size_t *size) +static void get_more(const void **start, size_t *size) { int32_t *out, *outend; int16_t *raw; @@ -306,7 +306,7 @@ static void get_more(unsigned char **start, size_t *size) } while (out < outend); - *start = (unsigned char *)sound_buf; + *start = sound_buf; *size = NBSAMPLES*sizeof(sound_buf[0]); } @@ -339,7 +339,7 @@ static void start_sound(void) wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]); rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]); - rb->pcm_play_data(get_more, NULL, 0); + rb->pcm_play_data(get_more, NULL, NULL, 0); sound_playing = true; } 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 @@ #include "s_stuff.h" /* Declare functions that go to IRAM. */ -void pdbox_get_more(unsigned char** start, size_t* size) ICODE_ATTR; +void pdbox_get_more(const void** start, size_t* size) ICODE_ATTR; int rockbox_send_dacs(void) ICODE_ATTR; /* Extern variables. */ @@ -90,12 +90,12 @@ void rockbox_close_audio(void) } /* Rockbox audio callback. */ -void pdbox_get_more(unsigned char** start, size_t* size) +void pdbox_get_more(const void** start, size_t* size) { if(outbuf_fill > 0) { /* Store output data address and size. */ - *start = (unsigned char*) outbuf[outbuf_tail].data; + *start = outbuf[outbuf_tail].data; *size = sizeof(outbuf[outbuf_tail].data); /* Free this part of output buffer. */ @@ -116,8 +116,6 @@ void pdbox_get_more(unsigned char** start, size_t* size) playing = false; /* Nothing to play. */ - *start = NULL; - *size = 0; } } @@ -185,7 +183,7 @@ int rockbox_send_dacs(void) if(!playing && outbuf_fill > 0) { /* Start playing. */ - rb->pcm_play_data(pdbox_get_more, NULL, 0); + rb->pcm_play_data(pdbox_get_more, NULL, NULL, 0); /* Set status flag. */ 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) } /* Stop the recording when the buffer is full */ -static void recording_callback(int status, void **start, size_t *size) +static void recording_callback(void **start, size_t *size) { int tail = audio_tail ^ 1; @@ -963,8 +963,6 @@ static void recording_callback(int status, void **start, size_t *size) /* Always record full buffer, even if not required */ *start = audio_data[tail]; *size = BUFFER_SIZE * sizeof (int16_t); - - (void)status; } #endif /* SIMULATOR */ @@ -973,7 +971,8 @@ static void record_data(void) { #ifndef SIMULATOR /* Always record full buffer, even if not required */ - rb->pcm_record_data(recording_callback, audio_data[audio_tail], + rb->pcm_record_data(recording_callback, NULL, + audio_data[audio_tail], BUFFER_SIZE * sizeof (int16_t)); #endif } 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; static bool newly_started; -static void get_more(unsigned char** start, size_t* size) +static void get_more(const void** start, size_t* size) { memcpy(hwbuf, &buf[pcm.len*doneplay], BUF_SIZE*sizeof(short)); - *start = (unsigned char*)(hwbuf); + *start = hwbuf; *size = BUF_SIZE*sizeof(short); doneplay=1; } @@ -76,7 +76,7 @@ int rockboy_pcm_submit(void) if(newly_started) { - rb->pcm_play_data(&get_more,NULL,0); + rb->pcm_play_data(&get_more, NULL, NULL,0); newly_started = false; } 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) } /* ISR handler to get next block of data */ -static void get_more(unsigned char **start, size_t *size) +static void get_more(const void **start, size_t *size) { /* Free previous buffer */ output_head += output_step; output_step = 0; - *start = (unsigned char *)output_buf[output_head & OUTPUT_CHUNK_MASK]; + *start = output_buf[output_head & OUTPUT_CHUNK_MASK]; *size = OUTPUT_CHUNK_SIZE; /* Keep repeating previous if source runs low */ @@ -236,7 +236,7 @@ static void play_tone(bool volume_set) IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); - rb->pcm_play_data(get_more, NULL, 0); + rb->pcm_play_data(get_more, NULL, NULL, 0); #ifndef HAVE_VOLUME_IN_LIST 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) } #endif } -static void get_more(unsigned char** start, size_t* size) +static void get_more(const void** start, size_t* size) { doneplay = 1; - rb->pcm_play_stop(); - (void)*start; - (void)*size; + (void)start; + (void)size; } /* sp_sound_buf is Unsigned 8 bit, Rate 8000 Hz, Mono */ @@ -219,7 +218,7 @@ static void write_buf(void){ = my_buf[j+10] = my_buf[j+11] \ = (((byte)sp_sound_buf[i])<<8) >> settings.volume; - rb->pcm_play_data(&get_more,(unsigned char*)(my_buf),TMNUM*4*3*2); + rb->pcm_play_data(&get_more,NULL,(unsigned char*)(my_buf),TMNUM*4*3*2); #if 0 /* 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 /*******************************************************************/ /* Callback for when more data is ready - called in interrupt context */ -static void pcm_rec_have_more(int status, void **start, size_t *size) +static void pcm_rec_have_more(void **start, size_t *size) { - if (status < 0) - { - /* some error condition */ - if (status == DMA_REC_ERROR_DMA) - { - /* Flush recorded data to disk and stop recording */ - queue_post(&pcmrec_queue, PCMREC_STOP, 0); - return; - } - /* else try again next transmission - frame is invalid */ - } - else if (!dma_lock) + if (!dma_lock) { /* advance write position */ 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) *size = PCM_CHUNK_SIZE; } /* pcm_rec_have_more */ +static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status) +{ + if (status < PCM_DMAST_OK) + { + /* some error condition */ + if (status == PCM_DMAST_ERR_DMA) + { + /* Flush recorded data to disk and stop recording */ + queue_post(&pcmrec_queue, PCMREC_STOP, 0); + return status; + } + /* else try again next transmission - frame is invalid */ + } + + return PCM_DMAST_OK; +} /* pcm_rec_status_callback */ + static void reset_hardware(void) { /* reset pcm to defaults */ @@ -1239,8 +1245,8 @@ static void pcmrec_set_recording_options( { /* start DMA transfer */ dma_lock = pre_record_ticks == 0; - pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos), - PCM_CHUNK_SIZE); + pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback, + GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE); } else { 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 /* Structure to store clip data callback info */ struct voice_info { - pcm_play_callback_type get_more; /* Callback to get more clips */ - unsigned char *start; /* Start of clip */ - size_t size; /* Size of clip */ + /* Callback to get more clips */ + void (*get_more)(unsigned char** start, size_t* size); + /* Start of clip */ + unsigned char *start; + /* Size of clip */ + size_t size; }; /* Private thread data for its current state that must be passed to its @@ -148,14 +151,14 @@ static inline int voice_unplayed_frames(void) } /* Mixer channel callback */ -static void voice_pcm_callback(unsigned char **start, size_t *size) +static void voice_pcm_callback(const void **start, size_t *size) { if (voice_unplayed_frames() == 0) return; /* Done! */ unsigned int i = ++cur_buf_out % VOICE_FRAMES; - *start = (unsigned char *)voicebuf[i]; + *start = voicebuf[i]; *size = voicebuf_sizes[i]; } @@ -167,7 +170,7 @@ static void voice_start_playback(void) unsigned int i = cur_buf_out % VOICE_FRAMES; mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback, - (unsigned char *)voicebuf[i], voicebuf_sizes[i]); + voicebuf[i], voicebuf_sizes[i]); } /* Stop the voice channel */ @@ -198,7 +201,7 @@ static void voice_buf_commit(size_t size) /* Stop any current clip and start playing a new one */ void mp3_play_data(const unsigned char* start, int size, - pcm_play_callback_type get_more) + void (*get_more)(unsigned char** start, size_t* size)) { if (get_more != NULL && start != NULL && (ssize_t)size > 0) { 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 @@ /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, - void *src0, + const void *src0, int32_t src0_amp, - void *src1, + const void *src1, int32_t src1_amp, size_t size) { @@ -96,7 +96,7 @@ static FORCE_INLINE void mix_samples(void *out, if (src0_amp != MIX_AMP_UNITY) { /* Keep unity in src0, amp0 */ - int16_t *src_tmp = src0; + const void *src_tmp = src0; src0 = src1; src1 = src_tmp; src1_amp = src0_amp; @@ -133,7 +133,7 @@ static FORCE_INLINE void mix_samples(void *out, /* Write channel's samples and apply gain factor */ static FORCE_INLINE void write_samples(void *out, - void *src, + const void *src, int32_t amp, size_t size) { 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 @@ /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, - void *src0, + const void *src0, int32_t src0_amp, - void *src1, + const void *src1, int32_t src1_amp, size_t size) { @@ -57,7 +57,7 @@ static FORCE_INLINE void mix_samples(void *out, /* Write channel's samples and apply gain factor */ static FORCE_INLINE void write_samples(void *out, - void *src, + const void *src, int32_t amp, size_t size) { 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 @@ /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, - void *src0, + const void *src0, int32_t src0_amp, - void *src1, + const void *src1, int32_t src1_amp, size_t size) { @@ -71,7 +71,7 @@ static FORCE_INLINE void mix_samples(void *out, /* Write channel's samples and apply gain factor */ static FORCE_INLINE void write_samples(void *out, - void *src, + const void *src, int32_t amp, size_t size) { 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) /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(void *out, - void *src0, + const void *src0, int32_t src0_amp, - void *src1, + const void *src1, int32_t src1_amp, size_t size) { @@ -94,7 +94,7 @@ static FORCE_INLINE void mix_samples(void *out, /* Write channel's samples and apply gain factor */ static FORCE_INLINE void write_samples(void *out, - void *src, + const void *src, int32_t amp, size_t size) { 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 @@ #include "dsp-util.h" /* for clip_sample_16 */ /* Mix channels' samples and apply gain factors */ static FORCE_INLINE void mix_samples(int16_t *out, - int16_t *src0, + const int16_t *src0, int32_t src0_amp, - int16_t *src1, + const int16_t *src1, int32_t src1_amp, size_t size) { @@ -64,7 +64,7 @@ static FORCE_INLINE void mix_samples(int16_t *out, if (src0_amp != MIX_AMP_UNITY) { /* Keep unity in src0, amp0 */ - int16_t *src_tmp = src0; + const int16_t *src_tmp = src0; src0 = src1; src1 = src_tmp; src1_amp = src0_amp; @@ -84,7 +84,7 @@ static FORCE_INLINE void mix_samples(int16_t *out, /* Write channel's samples and apply gain factor */ static FORCE_INLINE void write_samples(int16_t *out, - int16_t *src, + const int16_t *src, int32_t amp, size_t size) { 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, /** The following are for internal use between pcm.c and target- specific portion **/ - -/* Called by the bottom layer ISR when more data is needed. Returns non- - * zero size if more data is to be played. Setting start to NULL - * forces stop. */ -void pcm_play_get_more_callback(void **start, size_t *size); - -/* Called by the bottom layer ISR after next transfer has begun in order - to fill more data for next "get more" callback to implement double-buffered - callbacks - except for a couple ASM handlers, help drivers to implement - this functionality with minimal overhead */ -static FORCE_INLINE void pcm_play_dma_started_callback(void) +static FORCE_INLINE enum pcm_dma_status pcm_call_status_cb( + pcm_status_callback_type callback, enum pcm_dma_status status) { - extern void (* pcm_play_dma_started)(void); - void (* callback)(void) = pcm_play_dma_started; - if (callback) - callback(); + if (!callback) + return status; + + return callback(status); } +static FORCE_INLINE enum pcm_dma_status +pcm_play_dma_status_callback(enum pcm_dma_status status) +{ + extern enum pcm_dma_status + (* volatile pcm_play_status_callback)(enum pcm_dma_status); + return pcm_call_status_cb(pcm_play_status_callback, status); +} + +/* Called by the bottom layer ISR when more data is needed. Returns true + * if a new buffer is available, false otherwise. */ +bool pcm_play_dma_complete_callback(enum pcm_dma_status status, + const void **addr, size_t *size); + extern unsigned long pcm_curr_sampr; extern unsigned long pcm_sampr; extern int pcm_fsel; @@ -90,10 +94,29 @@ extern volatile bool pcm_recording; void pcm_rec_dma_init(void); void pcm_rec_dma_close(void); void pcm_rec_dma_start(void *addr, size_t size); -void pcm_rec_dma_record_more(void *start, size_t size); void pcm_rec_dma_stop(void); const void * pcm_rec_dma_get_peak_buffer(void); +static FORCE_INLINE enum pcm_dma_status +pcm_rec_dma_status_callback(enum pcm_dma_status status) +{ + extern enum pcm_dma_status + (* volatile pcm_rec_status_callback)(enum pcm_dma_status); + return pcm_call_status_cb(pcm_rec_status_callback, status); +} + + +/* Called by the bottom layer ISR when more data is needed. Returns true + * if a new buffer is available, false otherwise. */ +bool pcm_rec_dma_complete_callback(enum pcm_dma_status status, + void **addr, size_t *size); + +#ifdef HAVE_PCM_REC_DMA_ADDRESS +#define pcm_rec_dma_addr(addr) pcm_dma_addr(addr) +#else +#define pcm_rec_dma_addr(addr) addr +#endif + #endif /* HAVE_RECORDING */ #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 @@ #include /* size_t */ #include "config.h" -#define DMA_REC_ERROR_DMA (-1) +enum pcm_dma_status +{ #ifdef HAVE_SPDIF_REC -#define DMA_REC_ERROR_SPDIF (-2) + PCM_DMAST_ERR_SPDIF = -2, #endif + PCM_DMAST_ERR_DMA = -1, + PCM_DMAST_OK = 0, + PCM_DMAST_STARTED = 1, +}; /** RAW PCM routines used with playback and recording **/ -/* Typedef for registered callbacks */ -typedef void (*pcm_play_callback_type)(unsigned char **start, - size_t *size); -typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size); +/* Typedef for registered data callback */ +typedef void (*pcm_play_callback_type)(const void **start, size_t *size); + +/* Typedef for registered status callback */ +typedef enum pcm_dma_status (*pcm_status_callback_type)(enum pcm_dma_status status); /* set the pcm frequency - use values in hw_sampr_list * when CONFIG_SAMPR_TYPES is #defined, or-in SAMPR_TYPE_* fields with @@ -62,7 +68,8 @@ bool pcm_is_initialized(void); /* This is for playing "raw" PCM data */ void pcm_play_data(pcm_play_callback_type get_more, - unsigned char* start, size_t size); + pcm_status_callback_type status_cb, + const void *start, size_t size); void pcm_calculate_peaks(int *left, int *right); const void* pcm_get_peak_buffer(int* count); @@ -73,12 +80,13 @@ void pcm_play_pause(bool play); bool pcm_is_paused(void); bool pcm_is_playing(void); -void pcm_play_set_dma_started_callback(void (* callback)(void)); - #ifdef HAVE_RECORDING /** RAW PCM recording routines **/ +/* Typedef for registered data callback */ +typedef void (*pcm_rec_callback_type)(void **start, size_t *size); + /* Reenterable locks for locking and unlocking the recording interrupt */ void pcm_rec_lock(void); void pcm_rec_unlock(void); @@ -90,6 +98,7 @@ void pcm_close_recording(void); /* Start recording "raw" PCM data */ void pcm_record_data(pcm_rec_callback_type more_ready, + pcm_status_callback_type status_cb, void *start, size_t size); /* Stop tranferring data into supplied buffer */ @@ -98,10 +107,6 @@ void pcm_stop_recording(void); /* Is pcm currently recording? */ bool pcm_is_recording(void); -/* Called by bottom layer ISR when transfer is complete. Returns non-zero - * size if successful. Setting start to NULL forces stop. */ -void pcm_rec_more_ready_callback(int status, void **start, size_t *size); - void pcm_calculate_rec_peaks(int *left, int *right); #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 /* Start playback on a channel */ void mixer_channel_play_data(enum pcm_mixer_channel channel, pcm_play_callback_type get_more, - unsigned char *start, size_t size); + const void *start, size_t size); /* Pause or resume a channel (when started) */ void 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); size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel); /* Return pointer to channel's playing audio data and the size remaining */ -void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count); +const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count); /* Calculate peak values for channel */ void 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 @@ #define IISCONFIG (*(volatile unsigned long *)(0xc0002500)) #define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c)) #define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540)) +#define IISFIFO_WRH (*(volatile unsigned short *)(0xc0002540)) #define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580)) +#define IISFIFO_RDH (*(volatile unsigned short *)(0xc0002540)) /** * 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 @@ * pcm_play_lock * pcm_play_unlock * Semi-private - - * pcm_play_get_more_callback + * pcm_play_dma_complete_callback + * pcm_play_dma_status_callback * pcm_play_dma_init * pcm_play_dma_postinit * pcm_play_dma_start @@ -66,7 +67,8 @@ * pcm_rec_lock * pcm_rec_unlock * Semi-private - - * pcm_rec_more_ready_callback + * pcm_rec_dma_complete_callback + * pcm_rec_dma_status_callback * pcm_rec_dma_init * pcm_rec_dma_close * pcm_rec_dma_start @@ -83,9 +85,12 @@ /* 'true' when all stages of pcm initialization have completed */ static bool pcm_is_ready = false; -/* the registered callback function to ask for more mp3 data */ -static pcm_play_callback_type pcm_callback_for_more SHAREDBSS_ATTR = NULL; -void (* pcm_play_dma_started)(void) SHAREDBSS_ATTR = NULL; +/* The registered callback function to ask for more mp3 data */ +static volatile pcm_play_callback_type + pcm_callback_for_more SHAREDBSS_ATTR = NULL; +/* The registered callback function to inform of DMA status */ +volatile pcm_status_callback_type + pcm_play_status_callback SHAREDBSS_ATTR = NULL; /* PCM playback state */ volatile bool pcm_playing SHAREDBSS_ATTR = false; /* PCM paused state. paused implies playing */ @@ -104,7 +109,7 @@ static struct pcm_peaks global_peaks; static void pcm_play_stopped(void) { pcm_callback_for_more = NULL; - pcm_play_dma_started = NULL; + pcm_play_status_callback = NULL; pcm_paused = false; pcm_playing = false; } @@ -258,27 +263,29 @@ bool pcm_is_initialized(void) } /* Common code to pcm_play_data and pcm_play_pause */ -static void pcm_play_data_start(unsigned char *start, size_t size) +static void pcm_play_data_start(const void *addr, size_t size) { - ALIGN_AUDIOBUF(start, size); + ALIGN_AUDIOBUF(addr, size); - if (!(start && size)) + if (!(addr && size)) { pcm_play_callback_type get_more = pcm_callback_for_more; + addr = NULL; size = 0; + if (get_more) { logf(" get_more"); - get_more(&start, &size); - ALIGN_AUDIOBUF(start, size); + get_more(&addr, &size); + ALIGN_AUDIOBUF(addr, size); } } - if (start && size) + if (addr && size) { logf(" pcm_play_dma_start"); pcm_apply_settings(); - pcm_play_dma_start(start, size); + pcm_play_dma_start(addr, size); pcm_playing = true; pcm_paused = false; return; @@ -291,13 +298,15 @@ static void pcm_play_data_start(unsigned char *start, size_t size) } void pcm_play_data(pcm_play_callback_type get_more, - unsigned char *start, size_t size) + pcm_status_callback_type status_cb, + const void *start, size_t size) { logf("pcm_play_data"); pcm_play_lock(); pcm_callback_for_more = get_more; + pcm_play_status_callback = status_cb; logf(" pcm_play_data_start"); pcm_play_data_start(start, size); @@ -305,26 +314,33 @@ void pcm_play_data(pcm_play_callback_type get_more, pcm_play_unlock(); } -void pcm_play_get_more_callback(void **start, size_t *size) +bool pcm_play_dma_complete_callback(enum pcm_dma_status status, + const void **addr, size_t *size) { + /* Check status callback first if error */ + if (status < PCM_DMAST_OK) + status = pcm_play_dma_status_callback(status); + pcm_play_callback_type get_more = pcm_callback_for_more; - *size = 0; - - if (get_more && start) + if (get_more && status >= PCM_DMAST_OK) { - /* Call registered callback */ - get_more((unsigned char **)start, size); + *addr = NULL; + *size = 0; - ALIGN_AUDIOBUF(*start, *size); + /* Call registered callback to obtain next buffer */ + get_more(addr, size); + ALIGN_AUDIOBUF(*addr, *size); - if (*start && *size) - return; + if (*addr && *size) + return true; } /* Error, callback missing or no more DMA to do */ pcm_play_dma_stop(); pcm_play_stopped(); + + return false; } void pcm_play_pause(bool play) @@ -428,12 +444,6 @@ void pcm_apply_settings(void) } } -/* register callback to buffer more data */ -void pcm_play_set_dma_started_callback(void (* callback)(void)) -{ - pcm_play_dma_started = callback; -} - #ifdef HAVE_RECORDING /** Low level pcm recording apis **/ @@ -442,6 +452,8 @@ static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL; /* the registered callback function for when more data is available */ static volatile pcm_rec_callback_type pcm_callback_more_ready SHAREDBSS_ATTR = NULL; +volatile pcm_status_callback_type + pcm_rec_status_callback SHAREDBSS_ATTR = NULL; /* DMA transfer in is currently active */ volatile bool pcm_recording SHAREDBSS_ATTR = false; @@ -450,6 +462,7 @@ static void pcm_recording_stopped(void) { pcm_recording = false; pcm_callback_more_ready = NULL; + pcm_rec_status_callback = NULL; } /** @@ -542,13 +555,14 @@ void pcm_close_recording(void) } void pcm_record_data(pcm_rec_callback_type more_ready, - void *start, size_t size) + pcm_status_callback_type status_cb, + void *addr, size_t size) { logf("pcm_record_data"); - ALIGN_AUDIOBUF(start, size); + ALIGN_AUDIOBUF(addr, size); - if (!(start && size)) + if (!(addr && size)) { logf(" no buffer"); return; @@ -557,17 +571,14 @@ void pcm_record_data(pcm_rec_callback_type more_ready, pcm_rec_lock(); pcm_callback_more_ready = more_ready; + pcm_rec_status_callback = status_cb; -#ifdef HAVE_PCM_REC_DMA_ADDRESS /* Need a physical DMA address translation, if not already physical. */ - pcm_rec_peak_addr = pcm_dma_addr(start); -#else - pcm_rec_peak_addr = start; -#endif + pcm_rec_peak_addr = pcm_rec_dma_addr(addr); logf(" pcm_rec_dma_start"); pcm_apply_settings(); - pcm_rec_dma_start(start, size); + pcm_rec_dma_start(addr, size); pcm_recording = true; pcm_rec_unlock(); @@ -589,33 +600,35 @@ void pcm_stop_recording(void) pcm_rec_unlock(); } /* pcm_stop_recording */ -void pcm_rec_more_ready_callback(int status, void **start, size_t *size) +bool pcm_rec_dma_complete_callback(enum pcm_dma_status status, + void **addr, size_t *size) { + /* Check status callback first if error */ + if (status < PCM_DMAST_OK) + status = pcm_rec_dma_status_callback(status); + pcm_rec_callback_type have_more = pcm_callback_more_ready; - *size = 0; - - if (have_more && start) + if (have_more && status >= PCM_DMAST_OK) { - have_more(status, start, size); - ALIGN_AUDIOBUF(*start, *size); + /* Call registered callback to obtain next buffer */ + have_more(addr, size); + ALIGN_AUDIOBUF(*addr, *size); - if (*start && *size) + if (*addr && *size) { - #ifdef HAVE_PCM_REC_DMA_ADDRESS /* Need a physical DMA address translation, if not already * physical. */ - pcm_rec_peak_addr = pcm_dma_addr(*start); - #else - pcm_rec_peak_addr = *start; - #endif - return; + pcm_rec_peak_addr = pcm_rec_dma_addr(*addr); + return true; } } /* Error, callback missing or no more DMA to do */ pcm_rec_dma_stop(); pcm_recording_stopped(); + + return false; } #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 @@ /* Descriptor for each channel */ struct mixer_channel { - unsigned char *start; /* Buffer pointer */ + const void *start; /* Buffer pointer */ size_t size; /* Bytes remaining */ size_t last_size; /* Size of consumed data in prev. cycle */ pcm_play_callback_type get_more; /* Registered callback */ @@ -103,16 +103,20 @@ static void channel_stopped(struct mixer_channel *chan) } /* Main PCM callback - sends the current prepared frame to play */ -static void mixer_pcm_callback(unsigned char **start, size_t *size) +static void mixer_pcm_callback(const void **addr, size_t *size) { - *start = (unsigned char *)downmix_buf[downmix_index]; + *addr = downmix_buf[downmix_index]; *size = next_size; } /* Buffering callback - calls sub-callbacks and mixes the data for next buffer to be sent from mixer_pcm_callback() */ -static void MIXER_CALLBACK_ICODE mixer_buffer_callback(void) +static enum pcm_dma_status MIXER_CALLBACK_ICODE +mixer_buffer_callback(enum pcm_dma_status status) { + if (status != PCM_DMAST_STARTED) + return status; + downmix_index ^= 1; /* Next buffer */ void *mixptr = downmix_buf[downmix_index]; @@ -169,12 +173,11 @@ fill_frame: if (LIKELY(!*chan_p)) { - write_samples(mixptr, (void *)chan->start, - chan->amplitude, mixsize); + write_samples(mixptr, chan->start, chan->amplitude, mixsize); } else { - void *src0, *src1; + const void *src0, *src1; unsigned int amp0, amp1; /* Mix first two channels with each other as the downmix */ @@ -228,6 +231,8 @@ fill_frame: if (next_size) *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000; #endif + + return PCM_DMAST_OK; } /* Start PCM driver if it's not currently playing */ @@ -245,15 +250,15 @@ static void mixer_start_pcm(void) pcm_set_frequency(NATIVE_FREQUENCY); /* Prepare initial frames and set up the double buffer */ - mixer_buffer_callback(); + mixer_buffer_callback(PCM_DMAST_STARTED); /* Save the previous call's output */ void *start = downmix_buf[downmix_index]; - mixer_buffer_callback(); + mixer_buffer_callback(PCM_DMAST_STARTED); - pcm_play_set_dma_started_callback(mixer_buffer_callback); - pcm_play_data(mixer_pcm_callback, start, MIX_FRAME_SIZE); + pcm_play_data(mixer_pcm_callback, mixer_buffer_callback, + start, MIX_FRAME_SIZE); } /** Public interfaces **/ @@ -261,7 +266,7 @@ static void mixer_start_pcm(void) /* Start playback on a channel */ void mixer_channel_play_data(enum pcm_mixer_channel channel, pcm_play_callback_type get_more, - unsigned char *start, size_t size) + const void *start, size_t size) { struct mixer_channel *chan = &channels[channel]; @@ -360,12 +365,12 @@ size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel) } /* Return pointer to channel's playing audio data and the size remaining */ -void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count) +const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count) { struct mixer_channel *chan = &channels[channel]; - void * buf = *(unsigned char * volatile *)&chan->start; + const void * buf = *(const void * volatile *)&chan->start; size_t size = *(size_t volatile *)&chan->size; - void * buf2 = *(unsigned char * volatile *)&chan->start; + const void * buf2 = *(const void * volatile *)&chan->start; /* Still same buffer? */ 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 @@ * and the number of 32bits words has to * fit in 11 bits of DMA register */ -static void *dma_start_addr; /* Pointer to callback buffer */ +static const void *dma_start_addr; /* Pointer to callback buffer */ static size_t dma_start_size; /* Size of callback buffer */ -static void *dma_sub_addr; /* Pointer to sub buffer */ +static const void *dma_sub_addr; /* Pointer to sub buffer */ static size_t dma_rem_size; /* Remaining size - in 4*32 bits */ static size_t play_sub_size; /* size of current subtransfer */ static void dma_callback(void); @@ -100,9 +100,8 @@ static void dma_callback(void) if(!dma_rem_size) { - pcm_play_get_more_callback(&dma_start_addr, &dma_start_size); - - if (!dma_start_size) + if(!pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_start_addr, + &dma_start_size)) return; dma_sub_addr = dma_start_addr; @@ -111,7 +110,7 @@ static void dma_callback(void) /* force writeback */ commit_dcache_range(dma_start_addr, dma_start_size); play_start_pcm(); - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } else { @@ -123,7 +122,7 @@ void pcm_play_dma_start(const void *addr, size_t size) { is_playing = true; - dma_start_addr = (void*)addr; + dma_start_addr = addr; dma_start_size = size; dma_sub_addr = dma_start_addr; dma_rem_size = size; @@ -368,7 +367,12 @@ void INT_I2SIN(void) } } - pcm_rec_more_ready_callback(0, (void *)&rec_dma_addr, &rec_dma_size); + /* Inform middle layer */ + if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, (void **)&rec_dma_addr, + &rec_dma_size)) + { + pcm_rec_dma_status_callback(PCM_DMAST_STARTED); + } } 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) void INT_DAC_DMA(void) { - void *start; + const void *start; size_t size; - pcm_play_get_more_callback(&start, &size); - - if(size != 0) + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) { play(start, size); - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); @@ -65,6 +63,7 @@ void INT_DAC_DMA(void) void INT_DAC_ERROR(void) { + /* TODO: Inform of error through pcm_play_dma_complete_callback */ } void 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 = .state = 0 }; +static void play_start_dma(const void *addr, size_t size) +{ + commit_dcache_range(addr, size); + + dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)addr); + dma_play_bd.mode.count = size; + dma_play_bd.mode.command = TRANSFER_16BIT; + dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; + + sdma_channel_run(DMA_PLAY_CH_NUM); +} + static void play_dma_callback(void) { - void *start; - size_t size; - bool rror; - if (dma_play_data.locked != 0) { /* Callback is locked out */ @@ -91,22 +99,17 @@ static void play_dma_callback(void) return; } - rror = dma_play_bd.mode.status & BD_RROR; - - pcm_play_get_more_callback(rror ? NULL : &start, &size); - - if (size == 0) - return; - - /* Flush any pending cache writes */ - commit_dcache_range(start, size); - dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start); - dma_play_bd.mode.count = size; - dma_play_bd.mode.command = TRANSFER_16BIT; - dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - sdma_channel_run(DMA_PLAY_CH_NUM); - - pcm_play_dma_started_callback(); + /* Inform of status and get new buffer */ + enum pcm_dma_status status = (dma_play_bd.mode.status & BD_RROR) ? + PCM_DMAST_ERR_DMA : PCM_DMAST_OK; + const void *addr; + size_t size; + + if (pcm_play_dma_complete_callback(status, &addr, &size)) + { + play_start_dma(addr, size); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + } } void pcm_play_lock(void) @@ -221,15 +224,11 @@ void pcm_play_dma_start(const void *addr, size_t size) if (!sdma_channel_reset(DMA_PLAY_CH_NUM)) return; - commit_dcache_range(addr, size); - dma_play_bd.buf_addr = - (void *)addr_virt_to_phys((unsigned long)(void *)addr); - dma_play_bd.mode.count = size; - dma_play_bd.mode.command = TRANSFER_16BIT; - dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - + /* Begin I2S transmission */ play_start_pcm(); - sdma_channel_run(DMA_PLAY_CH_NUM); + + /* Begin DMA transfer */ + play_start_dma(addr, size); } void pcm_play_dma_stop(void) @@ -332,37 +331,39 @@ static struct dma_data dma_rec_data = .state = 0 }; +static void rec_start_dma(void *addr, size_t size) +{ + discard_dcache_range(addr, size); + + addr = (void *)addr_virt_to_phys((unsigned long)addr); + + dma_rec_bd.buf_addr = addr; + dma_rec_bd.mode.count = size; + dma_rec_bd.mode.command = TRANSFER_16BIT; + dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; + + sdma_channel_run(DMA_REC_CH_NUM); +} + static void rec_dma_callback(void) { - int status = 0; - void *start; - size_t size; - if (dma_rec_data.locked != 0) { dma_rec_data.callback_pending = dma_rec_data.state; return; /* Callback is locked out */ } - if (dma_rec_bd.mode.status & BD_RROR) - status = DMA_REC_ERROR_DMA; + /* Inform middle layer */ + enum pcm_dma_status status = (dma_rec_bd.mode.status & BD_RROR) ? + PCM_DMAST_ERR_DMA : PCM_DMAST_OK; + void *addr; + size_t size; - pcm_rec_more_ready_callback(status, &start, &size); - - if (size == 0) - return; - - /* Invalidate - buffer must be coherent */ - discard_dcache_range(start, size); - - start = (void *)addr_virt_to_phys((unsigned long)start); - - dma_rec_bd.buf_addr = start; - dma_rec_bd.mode.count = size; - dma_rec_bd.mode.command = TRANSFER_16BIT; - dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - - sdma_channel_run(DMA_REC_CH_NUM); + if (pcm_rec_dma_complete_callback(status, &addr, &size)) + { + rec_start_dma(addr, size); + pcm_rec_dma_status_callback(PCM_DMAST_STARTED); + } } void pcm_rec_lock(void) @@ -426,29 +427,21 @@ void pcm_rec_dma_start(void *addr, size_t size) if (!sdma_channel_reset(DMA_REC_CH_NUM)) return; - - /* Invalidate - buffer must be coherent */ - discard_dcache_range(addr, size); - - addr = (void *)addr_virt_to_phys((unsigned long)addr); - dma_rec_bd.buf_addr = addr; - dma_rec_bd.mode.count = size; - dma_rec_bd.mode.command = TRANSFER_16BIT; - dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - - dma_rec_data.state = 1; /* Check callback on unlock */ - - SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ /* Ensure clear FIFO */ while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0) SSI_SRX0_1; + dma_rec_data.state = 1; /* Check callback on unlock */ + + SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ + /* Enable receive */ SSI_SCR1 |= SSI_SCR_RE; SSI_SIER1 |= SSI_SIER_RDMAE; /* Enable DMA req. */ - sdma_channel_run(DMA_REC_CH_NUM); + /* Begin DMA transfer */ + rec_start_dma(addr, size); } void 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 { /* NOTE: The order of size and p is important if you use assembler optimised fiq handler, so don't change it. */ - uint16_t *p; + union + { + uint16_t *p; + const void *p_r; + void *p_w; + }; size_t size; #if NUM_CORES > 1 unsigned core; @@ -143,7 +148,7 @@ static void play_stop_pcm(void) void pcm_play_dma_start(const void *addr, size_t size) { - dma_play_data.p = (uint16_t*)addr; + dma_play_data.p_r = addr; dma_play_data.size = size; #if NUM_CORES > 1 @@ -248,8 +253,9 @@ void fiq_handler(void) * r0-r3 and r12 is a working register. */ asm volatile ( - "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ - "mov r4, #0 \n" /* Was the callback called? */ + "sub lr, lr, #4 \n" + "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ + "mov r14, #0 \n" /* Was the callback called? */ #if defined(CPU_TCC780X) "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ "ldr r9, =0xf3001004 \n" /* CREQ */ @@ -260,7 +266,7 @@ void fiq_handler(void) "str r8, [r9] \n" /* clear DAI IRQs */ "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ "cmp r9, #0x10 \n" /* is size <16? */ - "blt .more_data \n" /* if so, ask pcmbuf for more data */ + "blo .more_data \n" /* if so, ask pcmbuf for more data */ ".fill_fifo: \n" "ldr r12, [r8], #4 \n" /* load two samples */ @@ -282,29 +288,30 @@ void fiq_handler(void) "sub r9, r9, #0x10 \n" /* 4 words written */ "stmia r11, { r8-r9 } \n" /* save p and size */ - "cmp r4, #0 \n" /* Callback called? */ - "beq .exit \n" - /* "mov r4, #0 \n" If get_more could be called multiple times! */ - "ldr r2, =pcm_play_dma_started\n" - "ldr r2, [r2] \n" - "cmp r2, #0 \n" - "blxne r2 \n" + "cmp r14, #0 \n" /* Callback called? */ + "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */ - ".exit: \n" - "ldmfd sp!, { r0-r4, lr } \n" - "subs pc, lr, #4 \n" /* FIQ specific return sequence */ + "ldr r1, =pcm_play_status_callback \n" + "ldr r1, [r1] \n" + "cmp r1, #0 \n" + "movne r0, %1 \n" + "blxne r1 \n" + "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */ ".more_data: \n" - "mov r4, #1 \n" /* Remember we got more data in this FIQ */ - "ldr r2, =pcm_play_get_more_callback \n" - "mov r0, r11 \n" /* r0 = &p */ - "add r1, r11, #4 \n" /* r1 = &size */ - "blx r2 \n" /* call pcm_play_get_more_callback */ - "ldmia r11, { r8-r9 } \n" /* load new p and size */ - "cmp r9, #0x10 \n" /* did we actually get enough data? */ - "bpl .fill_fifo \n" /* not stop and enough? refill */ - "b .exit \n" + "mov r14, #1 \n" /* Remember we got more data in this FIQ */ + "mov r0, %0 \n" /* r0 = status */ + "mov r1, r11 \n" /* r1 = &dma_play_data.p_r */ + "add r2, r11, #4 \n" /* r2 = &dma_play_data.size */ + "mov lr, pc \n" + "ldr pc, =pcm_play_dma_complete_callback \n" + "cmp r0, #0 \n" /* any more to play? */ + "ldmneia r11, { r8-r9 } \n" /* load new p and size */ + "cmpne r9, #0x0f \n" /* did we actually get enough data? */ + "bhi .fill_fifo \n" /* not stop and enough? refill */ + "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */ ".ltorg \n" + : : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED) ); } #else /* C version for reference */ @@ -316,9 +323,8 @@ void fiq_handler(void) if (dma_play_data.size < 16) { /* p is empty, get some more data */ - new_buffer = true; - pcm_play_get_more_callback((void**)&dma_play_data.p, - &dma_play_data.size); + new_buffer = pcm_play_dma_complete_callback(&dma_play_data.p_r, + &dma_play_data.size); } if (dma_play_data.size >= 16) @@ -339,7 +345,7 @@ void fiq_handler(void) CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; if (new_buffer) - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } #endif 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 @@ short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; -unsigned short* p IBSS_ATTR; +const int16_t* p IBSS_ATTR; size_t p_size IBSS_ATTR; void pcm_play_lock(void) @@ -41,7 +41,7 @@ void pcm_play_unlock(void) void pcm_play_dma_start(const void *addr, size_t size) { - p = (unsigned short*)addr; + p = addr; p_size = size; } @@ -69,7 +69,7 @@ static inline void fill_dma_buf(int offset) do { int count; - unsigned short *tmp_p; + const int16_t *tmp_p; count = MIN(p_size / 4, (size_t)(lend - l)); tmp_p = p; p_size -= count * 4; @@ -109,16 +109,14 @@ static inline void fill_dma_buf(int offset) if (new_buffer) { new_buffer = false; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } if (l >= lend) return; - pcm_play_get_more_callback((void**)&p, &p_size); - - if (p_size) - new_buffer = true; + new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK, + &p, &p_size); } while (p_size); } 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 @@ /** DMA **/ -#ifdef CPU_PP502x -/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ -#define SAMPLE_SIZE 16 -/* DMA Requests from IIS, Memory to peripheral, single transfer, - wait for DMA request, interrupt on complete */ -#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \ - DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \ - DMA_CMD_WAIT_REQ | DMA_CMD_INTR) -/* DMA status cannot be viewed from outside code in control because that can - * clear the interrupt from outside the handler and prevent the handler from - * from being called. Split up transfers to a reasonable size that is good as - * a timer, obtaining a keyclick position and peaking yet still keeps the - * FIQ count low. - */ -#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */ -#else -/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */ -#define SAMPLE_SIZE 32 -#endif - struct dma_data { /* NOTE: The order of size and p is important if you use assembler @@ -57,6 +37,8 @@ struct dma_data union { unsigned long addr; + const void *p_r; + void *p_w; uint32_t *p16; /* For packed 16-16 stereo pairs */ uint16_t *p32; /* For individual samples converted to 32-bit */ }; @@ -113,286 +95,72 @@ void pcm_dma_apply_settings(void) } #if defined(CPU_PP502x) -/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ -void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) -{ - bool new_buffer = false; - register size_t size; - - DMA0_STATUS; /* Clear any pending interrupt */ - - size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this - interrupt */ - dma_play_data.addr += size; - dma_play_data.size -= size; - - while (1) - { - if (dma_play_data.size > 0) { - size = MAX_DMA_CHUNK_SIZE; - /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less - * than a FIFO's worth of data after this transfer? */ - if (size + 16*4 > dma_play_data.size) - size = dma_play_data.size; - - /* Set the new DMA values and activate channel */ - DMA0_RAM_ADDR = dma_play_data.addr; - DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; - - if (new_buffer) - pcm_play_dma_started_callback(); - return; - } - - new_buffer = true; - - /* Buffer empty. Try to get more. */ - pcm_play_get_more_callback((void **)&dma_play_data.addr, - &dma_play_data.size); - - if (dma_play_data.size == 0) { - /* No more data */ - return; - } - - if (dma_play_data.addr < UNCACHED_BASE_ADDR) { - /* Flush any pending cache writes */ - dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr); - commit_dcache(); - } - } -} -#else -/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by - * evalutation of free IISFIFO-slots against available source buffer words. - * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside - * the loop and do some further optimization. Right after the loops (source - * buffer -> IISFIFO) are done we need to check whether we have to exit FIQ - * handler (this must be done, if all free FIFO slots were filled) or we will - * have to get some new source data. Important information kept from former - * ASM implementation (not used anymore): GCC fails to make use of the fact - * that FIQ mode has registers r8-r14 banked, and so does not need to be saved. - * This routine uses only these registers, and so will never touch the stack - * unless it actually needs to do so when calling pcm_callback_for_more. - * C version is still included below for reference and testing. +/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ +#define SAMPLE_SIZE 16 +/* DMA Requests from IIS, Memory to peripheral, single transfer, + wait for DMA request, interrupt on complete */ +#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \ + DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \ + DMA_CMD_WAIT_REQ | DMA_CMD_INTR) +/* DMA status cannot be viewed from outside code in control because that can + * clear the interrupt from outside the handler and prevent the handler from + * from being called. Split up transfers to a reasonable size that is good as + * a timer and peaking yet still keeps the FIQ count low. */ -#if 1 -void fiq_playback(void) ICODE_ATTR __attribute__((naked)); -void fiq_playback(void) +#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */ + +static inline void dma_tx_init(void) { - /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual - * FIQ handler. r11 contains address of p (also set in crt0.S). Most other - * addresses we need are generated by using offsets with these two. - * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG. - * r8 and r9 contains local copies of p and size respectively. - * r0-r3 and r12 is a working register. - */ - asm volatile ( - "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ - - "mov r4, #0 \n" /* Was the callback called? */ -#if CONFIG_CPU == PP5002 - "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ - "ldr r12, [r12] \n" -#endif - "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ - "cmp r9, #0 \n" /* is size 0? */ - "beq .more_data \n" /* if so, ask pcmbuf for more data */ - -#if SAMPLE_SIZE == 16 - ".check_fifo: \n" - "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ - "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 16 (PP502x) */ - - "mov r1, r0, lsr #16 \n" /* number of free FIFO slots */ - "cmp r1, r9, lsr #2 \n" /* number of words from source */ - "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ - "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ - - "subs r1, r1, #2 \n" - ".fifo_loop_2: \n" - "ldmgeia r8!, {r2, r12} \n" /* load four samples */ - "strge r2 , [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ - "strge r12, [r10, %[wr]] \n" /* write sample 2-3 to IISFIFO_WR */ - "subges r1, r1, #2 \n" /* one more loop? */ - "bge .fifo_loop_2 \n" /* yes, continue */ - - "tst r1, #1 \n" /* two samples (one word) left? */ - "ldrne r12, [r8], #4 \n" /* load two samples */ - "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ -#elif SAMPLE_SIZE == 32 - ".check_fifo: \n" - "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ - "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ - - "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ - "beq .fifo_fill_complete \n" /* no complete pair? -> exit */ - "cmp r1, r9, lsr #2 \n" /* number of words from source */ - "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ - "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ - - ".fifo_loop: \n" - "ldr r12, [r8], #4 \n" /* load two samples */ - "mov r2 , r12, lsl #16 \n" /* put left sample at the top bits */ - "str r2 , [r10, %[wr]] \n" /* write top sample to IISFIFO_WR */ - "str r12, [r10, %[wr]] \n" /* write low sample to IISFIFO_WR*/ - "subs r1, r1, #1 \n" /* one more loop? */ - "bgt .fifo_loop \n" /* yes, continue */ - - ".fifo_fill_complete: \n" -#endif - "cmp r4, #0 \n" /* If fill came after get_more... */ - "beq .still_old_buffer \n" - "mov r4, #0 \n" - "ldr r2, =pcm_play_dma_started \n" - "ldrne r2, [r2] \n" - "cmp r2, #0 \n" - "movne lr, pc \n" - "bxne r2 \n" - - ".still_old_buffer: \n" - "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ - "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ - - ".more_data: \n" - "mov r4, #1 \n" /* Remember we did this */ - "ldr r2, =pcm_play_get_more_callback \n" - "mov r0, r11 \n" /* r0 = &p */ - "add r1, r11, #4 \n" /* r1 = &size */ - "mov lr, pc \n" /* call pcm_play_get_more_callback */ - "bx r2 \n" - "ldmia r11, { r8-r9 } \n" /* load new p and size */ - "cmp r9, #0 \n" - "bne .check_fifo \n" /* size != 0? refill */ - - ".exit: \n" /* (r9=0 if stopping, look above) */ - "stmia r11, { r8-r9 } \n" /* save p and size */ - "ldmfd sp!, { r0-r4, lr } \n" - "subs pc, lr, #4 \n" /* FIQ specific return sequence */ - ".ltorg \n" - : /* These must only be integers! No regs */ - : [mask]"i"(IIS_TX_FREE_MASK), - [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG), - [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG) - ); + /* Enable DMA controller */ + DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN; + /* FIQ priority for DMA */ + CPU_INT_PRIORITY |= DMA_MASK; + /* Enable request?? Not setting or clearing everything doesn't seem to + * prevent it operating. Perhaps important for reliability (how requests + * are handled). */ + DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS; + DMA0_STATUS; } -#else /* C version for reference */ -void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; -/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ -void fiq_playback(void) + +static inline void dma_tx_setup(void) { - bool new_buffer = false; - -#if CONFIG_CPU == PP5002 - inl(0xcf001040); -#endif - - do { - while (dma_play_data.size > 0) { - if (IIS_TX_FREE_COUNT < 2) { - if (new_buffer) { - new_buffer = false; - pcm_play_dma_started_callback(); - } - return; - } -#if SAMPLE_SIZE == 16 - IISFIFO_WR = *dma_play_data.p16++; -#elif SAMPLE_SIZE == 32 - IISFIFO_WR = *dma_play_data.p32++ << 16; - IISFIFO_WR = *dma_play_data.p32++ << 16; -#endif - dma_play_data.size -= 4; - } - - if (new_buffer) { - new_buffer = false; - pcm_play_dma_started_callback(); - } - - /* p is empty, get some more data */ - pcm_play_get_more_callback((void **)&dma_play_data.addr, - &dma_play_data.size); - new_buffer = true; - } while (dma_play_data.size); - - /* No more data */ + /* Setup DMA controller */ + DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR; + DMA0_FLAGS = DMA_FLAGS_UNK26; + DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT; } -#endif /* ASM / C selection */ -#endif /* CPU_PP502x */ -/* For the locks, FIQ must be disabled because the handler manipulates - IISCONFIG and the operation is not atomic - dual core support - will require other measures */ -void pcm_play_lock(void) +static inline unsigned long dma_tx_buf_prepare(const void *addr) { - int status = disable_fiq_save(); + unsigned long a = (unsigned long)addr; - if (++dma_play_data.locked == 1) { -#ifdef CPU_PP502x - CPU_INT_DIS = DMA_MASK; -#else - IIS_IRQTX_REG &= ~IIS_IRQTX; -#endif + if (a < UNCACHED_BASE_ADDR) { + /* VA in DRAM - writeback all data and get PA */ + a = UNCACHED_ADDR(a); + commit_dcache(); } - restore_fiq(status); + return a; } -void pcm_play_unlock(void) +static inline void dma_tx_start(bool begin) { - int status = disable_fiq_save(); - - if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { -#ifdef CPU_PP502x - CPU_INT_EN = DMA_MASK; -#else - IIS_IRQTX_REG |= IIS_IRQTX; -#endif - } - - restore_fiq(status); -} - -static void play_start_pcm(void) -{ - fiq_function = fiq_playback; - -#ifdef CPU_PP502x - /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a - * FIFO's worth of data after this transfer? */ size_t size = MAX_DMA_CHUNK_SIZE; + + /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less + * than a FIFO's worth of data after this transfer? */ if (size + 16*4 > dma_play_data.size) size = dma_play_data.size; + /* Set the new DMA values and activate channel */ DMA0_RAM_ADDR = dma_play_data.addr; DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; - dma_play_data.state = 1; -#else - IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ - /* Fill the FIFO or start when data is used up */ - while (1) { - if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { - IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ - dma_play_data.state = 1; - return; - } - -#if SAMPLE_SIZE == 16 - IISFIFO_WR = *dma_play_data.p16++; -#elif SAMPLE_SIZE == 32 - IISFIFO_WR = *dma_play_data.p32++ << 16; - IISFIFO_WR = *dma_play_data.p32++ << 16; -#endif - dma_play_data.size -= 4; - } -#endif + (void)begin; } -static void play_stop_pcm(void) +static void dma_tx_stop(void) { -#ifdef CPU_PP502x unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */ unsigned long cmd = DMA0_CMD; size_t size = 0; @@ -420,10 +188,281 @@ static void play_stop_pcm(void) if (dma_play_data.size == 0) dma_play_data.addr = 0; /* Entire buffer has completed. */ -#else +} + +static inline void dma_tx_lock(void) +{ + CPU_INT_DIS = DMA_MASK; +} + +static inline void dma_tx_unlock(void) +{ + CPU_INT_EN = DMA_MASK; +} + +/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ +void fiq_playback(void) ICODE_ATTR __attribute__((interrupt("FIQ"))); +void fiq_playback(void) +{ + DMA0_STATUS; /* Clear any pending interrupt */ + + size_t size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused + this interrupt */ + dma_play_data.addr += size; + dma_play_data.size -= size; + + if (LIKELY(dma_play_data.size != 0)) { + /* Begin next segment */ + dma_tx_start(false); + } + else if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_play_data.p_r, + &dma_play_data.size)) { + dma_play_data.addr = dma_tx_buf_prepare(dma_play_data.p_r); + dma_tx_start(false); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + } +} + +#else /* !defined (CPU_PP502x) */ + +/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */ +#define SAMPLE_SIZE 32 + +static void dma_tx_init(void) +{ + /* Set up banked registers for FIQ mode */ + + /* Use non-banked registers for scratch. */ + register volatile void *iiscfg asm("r0") = &IISCONFIG; + register volatile void *dmapd asm("r1") = &dma_play_data; + + asm volatile ( + "mrs r2, cpsr \n" /* Save mode and interrupt status */ + "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */ + "mov r8, #0 \n" + "mov r9, #0 \n" + "mov r10, %[iiscfg] \n" + "mov r11, %[dmapd] \n" + "msr cpsr_c, r2 \n" + : + : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd) + : "r2"); + + /* FIQ priority for I2S */ + CPU_INT_PRIORITY |= IIS_MASK; + CPU_INT_EN = IIS_MASK; +} + +static inline void dma_tx_setup(void) +{ + /* Nothing to do */ +} + +static inline unsigned long dma_tx_buf_prepare(const void *addr) +{ + return (unsigned long)addr; +} + +static inline void dma_tx_start(bool begin) +{ + if (begin) { + IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ + } + + /* Fill the FIFO or start when data is used up */ + while (IIS_TX_FREE_COUNT >= 2 && dma_play_data.size != 0) { + IISFIFO_WRH = *dma_play_data.p32++; + IISFIFO_WRH = *dma_play_data.p32++; + dma_play_data.size -= 4; + } + + if (begin) { + IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ + } +} + +static inline void dma_tx_stop(void) +{ /* Disable TX interrupt */ IIS_IRQTX_REG &= ~IIS_IRQTX; -#endif +} + +static inline void dma_tx_lock(void) +{ + IIS_IRQTX_REG &= ~IIS_IRQTX; +} + +static inline void dma_tx_unlock(void) +{ + IIS_IRQTX_REG |= IIS_IRQTX; +} + +/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by + * evalutation of free IISFIFO-slots against available source buffer words. + * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside + * the loop and do some further optimization. Right after the loops (source + * buffer -> IISFIFO) are done we need to check whether we have to exit FIQ + * handler (this must be done, if all free FIFO slots were filled) or we will + * have to get some new source data. Important information kept from former + * ASM implementation (not used anymore): GCC fails to make use of the fact + * that FIQ mode has registers r8-r14 banked, and so does not need to be saved. + * This routine uses only these registers, and so will never touch the stack + * unless it actually needs to do so when calling pcm_play_dma_complete_callback. + * C version is still included below for reference and testing. + */ +#if 1 +void fiq_playback(void) ICODE_ATTR __attribute__((naked)); +void fiq_playback(void) +{ + /* r8 and r9 contains local copies of p and size respectively. + * r10 contains IISCONFIG address (set during PCM init to minimize code in + * FIQ handler.Most other addresses we need are generated by using offsets + * from this. + * r10 + 0x40 is IISFIFO_WR, and r10 + 0x1c is IISFIFO_CFG. + * r11 contains address of dma_play_data + * r12 and r14 are working registers. + * + * Divided into two blocks: one where no external calls are needed and + * one where external callbacks are made + */ + asm volatile ( + /* No external calls */ + "sub lr, lr, #4 \n" /* Prepare return address */ + "stmfd sp!, { lr } \n" /* stack lr so we can use it */ + "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux ... */ + "ldr r12, [r12] \n" /* ... actually a DMA INT ack? */ + "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ + "cmp r9, #0 \n" /* is size 0? */ + "beq 1f \n" /* if so, ask PCM for more data */ + + "ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */ + "and r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */ + "cmp r9, r14, lsr #22 \n" /* number of words from source */ + "movlo r14, r9, lsl #22 \n" /* r14 = amount of allowed loops */ + "sub r9, r9, r14, lsr #22 \n" /* r14 words will be written in loop */ + "0: \n" + "ldr r12, [r8], #4 \n" /* load left-right pair */ + "subs r14, r14, #(0x2<<23) \n" /* one more loop? ... */ + "strh r12, [r10, #0x40] \n" /* left sample to IISFIFO_WR */ + "mov r12, r12, lsr #16 \n" /* put right sample in bottom 16 bits */ + "strh r12, [r10, #0x40] \n" /* right sample to IISFIFO_WR */ + "bhi 0b \n" /* ... yes, continue */ + + "cmp r9, #0 \n" /* either FIFO full or size empty? */ + "stmneia r11, { r8-r9 } \n" /* save p and size, if not empty */ + "ldmnefd sp!, { pc }^ \n" /* RFE if not empty */ + + /* Making external calls */ + "1: \n" + "stmfd sp!, { r0-r3 } \n" /* Must save volatiles */ + "2: \n" + "mov r0, %0 \n" /* r0 = status */ + "mov r1, r11 \n" /* r1 = &dma_play_data.p_r */ + "add r2, r11, #4 \n" /* r2 = &dma_play_data.size */ + "ldr r3, =pcm_play_dma_complete_callback \n" + "mov lr, pc \n" /* long call (not in same section) */ + "bx r3 \n" + "cmp r0, #0 \n" /* more data? */ + "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */ + + "ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */ + "ands r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */ + "bne 4f \n" + "3: \n" /* inform of started status if registered */ + "ldr r1, =pcm_play_status_callback \n" + "ldr r1, [r1] \n" + "cmp r1, #0 \n" + "movne r0, %1 \n" + "movne lr, pc \n" + "bxne r1 \n" + "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */ + "4: \n" + "ldmia r11, { r8-r9 } \n" /* load new p and size */ + "cmp r9, r14, lsr #22 \n" /* number of words from source */ + "movlo r14, r9, lsl #22 \n" /* r14 = amount of allowed loops */ + "sub r9, r9, r14, lsr #22 \n" /* r14 words will be written in loop */ + "0: \n" + "ldr r12, [r8], #4 \n" /* load left-right pair */ + "subs r14, r14, #(0x2<<23) \n" /* one more loop? ... */ + "strh r12, [r10, #0x40] \n" /* left sample to IISFIFO_WR */ + "mov r12, r12, lsr #16 \n" /* put right sample in bottom 16 bits */ + "strh r12, [r10, #0x40] \n" /* right sample to IISFIFO_WR */ + "bhi 0b \n" /* ... yes, continue */ + "stmia r11, { r8-r9 } \n" /* save p and size */ + + "cmp r9, #0 \n" /* used up data in FIFO fill? */ + "bne 3b \n" /* no? -> go return */ + "b 2b \n" /* yes -> get even more */ + ".ltorg \n" + : /* These must only be integers! No regs */ + : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED)); +} + +#else /* C version for reference */ + +/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ +void fiq_playback(void) ICODE_ATTR __attribute__((interrupt ("FIQ"))); +void fiq_playback(void) +{ + inl(0xcf001040); + + if (LIKELY(dma_play_data.size != 0)) { + dma_tx_start(false); + + if (dma_play_data.size != 0) { + /* Still more data */ + return; + } + } + + while (pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_play_data.p_r, + &dma_play_data.size)) { + dma_tx_start(false); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + + if (dma_play_data.size != 0) { + return; + } + } +} +#endif /* ASM / C selection */ +#endif /* CPU_PP502x */ + +/* For the locks, FIQ must be disabled because the handler manipulates + IISCONFIG and the operation is not atomic - dual core support + will require other measures */ +void pcm_play_lock(void) +{ + int status = disable_fiq_save(); + + if (++dma_play_data.locked == 1) { + dma_tx_lock(); + } + + restore_fiq(status); +} + +void pcm_play_unlock(void) +{ + int status = disable_fiq_save(); + + if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { + dma_tx_unlock(); + } + + restore_fiq(status); +} + +static void play_start_pcm(void) +{ + fiq_function = fiq_playback; + dma_play_data.state = 1; + dma_tx_start(true); +} + +static void play_stop_pcm(void) +{ + dma_tx_stop(); /* Wait for FIFO to empty */ while (!IIS_TX_IS_EMPTY); @@ -433,30 +472,17 @@ static void play_stop_pcm(void) void pcm_play_dma_start(const void *addr, size_t size) { + pcm_play_dma_stop(); + #if NUM_CORES > 1 /* This will become more important later - and different ! */ dma_play_data.core = processor_id(); /* save initiating core */ #endif - pcm_play_dma_stop(); + dma_tx_setup(); -#ifdef CPU_PP502x - if ((unsigned long)addr < UNCACHED_BASE_ADDR) { - /* Flush any pending cache writes */ - addr = UNCACHED_ADDR(addr); - commit_dcache(); - } - - dma_play_data.addr = (unsigned long)addr; + dma_play_data.addr = dma_tx_buf_prepare(addr); dma_play_data.size = size; - DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR; - DMA0_FLAGS = DMA_FLAGS_UNK26; - DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT; -#else - dma_play_data.addr = (unsigned long)addr; - dma_play_data.size = size; -#endif - play_start_pcm(); } @@ -490,39 +516,7 @@ void pcm_play_dma_init(void) /* Initialize default register values. */ audiohw_init(); -#ifdef CPU_PP502x - /* Enable DMA controller */ - DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN; - /* FIQ priority for DMA */ - CPU_INT_PRIORITY |= DMA_MASK; - /* Enable request?? Not setting or clearing everything doesn't seem to - * prevent it operating. Perhaps important for reliability (how requests - * are handled). */ - DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS; - DMA0_STATUS; -#else - /* Set up banked registers for FIQ mode */ - - /* Use non-banked registers for scratch. */ - register volatile void *iiscfg asm("r0") = &IISCONFIG; - register volatile void *dmapd asm("r1") = &dma_play_data; - - asm volatile ( - "mrs r2, cpsr \n" /* Save mode and interrupt status */ - "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */ - "mov r8, #0 \n" - "mov r9, #0 \n" - "mov r10, %[iiscfg] \n" - "mov r11, %[dmapd] \n" - "msr cpsr_c, r2 \n" - : - : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd) - : "r2"); - - /* FIQ priority for I2S */ - CPU_INT_PRIORITY |= IIS_MASK; - CPU_INT_EN = IIS_MASK; -#endif + dma_tx_init(); IISCONFIG |= IIS_TXFIFOEN; } @@ -649,11 +643,15 @@ void fiq_record(void) } } - pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, - &dma_rec_data.size); + if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, &dma_rec_data.p_w, + &dma_rec_data.size)) + { + pcm_rec_dma_status_callback(PCM_DMAST_STARTED); + } } -#else +#else /* !(SANSA_C200 || SANSA_E200) */ + void fiq_record(void) { while (dma_rec_data.size > 0) { @@ -664,17 +662,20 @@ void fiq_record(void) #if SAMPLE_SIZE == 16 *dma_rec_data.p16++ = IISFIFO_RD; #elif SAMPLE_SIZE == 32 - *dma_rec_data.p32++ = IISFIFO_RD >> 16; - *dma_rec_data.p32++ = IISFIFO_RD >> 16; + *dma_rec_data.p32++ = IISFIFO_RDH; + *dma_rec_data.p32++ = IISFIFO_RDH; #endif dma_rec_data.size -= 4; } - pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, - &dma_rec_data.size); + if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, &dma_rec_data.p_w, + &dma_rec_data.size)) + { + pcm_rec_dma_status_callback(PCM_DMAST_STARTED); + } } -#endif /* SANSA_E200 */ +#endif /* SANSA_C200 || SANSA_E200 */ void pcm_rec_dma_stop(void) { 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) /* audio DMA ISR called when chunk from callers buffer has been transfered */ void INT_HDMA(void) { - void *start; + const void *start; size_t size; - pcm_play_get_more_callback(&start, &size); - - if (size != 0) + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) { hdma_i2s_transfer(start, size); - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } } 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) void fiq_handler(void) { - static void *start; + static const void *start; static size_t size; /* clear any pending interrupt */ SRCPND = DMA2_MASK; /* Buffer empty. Try to get more. */ - pcm_play_get_more_callback(&start, &size); - - if (size == 0) + if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) return; /* Flush any pending cache writes */ @@ -237,7 +235,7 @@ void fiq_handler(void) /* Re-Activate the channel */ DMASKTRIG2 = 0x2; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } size_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) void fiq_handler(void) { - static void *start; + static const void *start; static size_t size; /* clear any pending interrupt */ SRCPND = DMA2_MASK; /* Buffer empty. Try to get more. */ - pcm_play_get_more_callback(&start, &size); - - if (size == 0) + if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) return; /* Flush any pending cache writes */ @@ -277,7 +275,7 @@ void fiq_handler(void) /* Re-Activate the channel */ DMASKTRIG2 = 0x2; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } size_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) { if (!nextsize) { - pcm_play_get_more_callback((void**)&nextbuf, &nextsize); - if (!nextsize) break; - new_buffer = true; + new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK, + (const void**)&nextbuf, &nextsize); + if (!new_buffer) + break; } queuedsize = MIN(sizeof(dblbuf), nextsize / 2); nextsize -= queuedsize; @@ -133,7 +134,7 @@ void INT_DMA(void) if (new_buffer) { - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); new_buffer = false; } } 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) DMAC0INTTCCLR = 1; if (!pcm_remaining) { - pcm_play_get_more_callback((void**)&dataptr, &pcm_remaining); + pcm_play_dma_complete_callback((const void**)&dataptr, &pcm_remaining); pcm_chunksize = pcm_remaining; } if (!pcm_remaining) @@ -115,7 +115,7 @@ void INT_DMAC0C0(void) } else DMAC0C0NEXTLLI = pcm_lli; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } void 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 @@ /* This is global to save some latency when pcm_play_dma_get_peak_buffer is * called. */ -static void *start; +static const void *start; void pcm_play_dma_postinit(void) { @@ -164,9 +164,7 @@ void DSPHINT(void) case MSG_REFILL: /* Buffer empty. Try to get more. */ - pcm_play_get_more_callback(&start, &size); - - if (size != 0) + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) { unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; /* Flush any pending cache writes */ @@ -180,7 +178,7 @@ void DSPHINT(void) DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", (unsigned long)start, (unsigned long)sdem_addr); - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } 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 @@ /* This is global to save some latency when pcm_play_dma_get_peak_buffer is * called. */ -static void *start; +static const void *start; static int dma_channel; void pcm_play_dma_postinit(void) @@ -171,9 +171,7 @@ void DSPHINT(void) case MSG_REFILL: /* Buffer empty. Try to get more. */ - pcm_play_get_more_callback(&start, &size); - - if (size != 0) + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) { unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; /* Flush any pending cache writes */ @@ -187,7 +185,7 @@ void DSPHINT(void) DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", (unsigned long)start, (unsigned long)sdem_addr); - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } 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"))); void DMA0(void) { unsigned long res = DSR0; - void *start; - size_t size; and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */ DSR0 = 1; /* Clear interrupt and errors */ @@ -311,17 +309,18 @@ void DMA0(void) #endif } - /* Force stop on error */ - pcm_play_get_more_callback((res & 0x70) ? NULL : &start, &size); + const void *addr; + size_t size; - if (size != 0) + if (pcm_play_dma_complete_callback((res & 0x70) ? + PCM_DMAST_ERR_DMA : PCM_DMAST_OK, + &addr, &size)) { - SAR0 = (unsigned long)start; /* Source address */ - BCR0 = size; /* Bytes to transfer */ + SAR0 = (unsigned long)addr; /* Source address */ + BCR0 = (unsigned long)size; /* Bytes to transfer */ or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */ - /* Call buffer callback */ - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } /* else inished playing */ } /* DMA0 */ @@ -368,7 +367,7 @@ void pcm_rec_unlock(void) void pcm_rec_dma_start(void *addr, size_t size) { - /* stop any DMA in progress */ + /* Stop any DMA in progress */ pcm_rec_dma_stop(); and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); @@ -430,16 +429,14 @@ void DMA1(void) __attribute__ ((interrupt_handler, section(".icode"))); void DMA1(void) { unsigned long res = DSR1; - int status = 0; - void *start; - size_t size; + enum pcm_dma_status status = PCM_DMAST_OK; and_l(~(DMA_EEXT | DMA_INT), &DCR1); /* per request and int OFF */ DSR1 = 1; /* Clear interrupt and errors */ if (res & 0x70) { - status = DMA_REC_ERROR_DMA; + status = PCM_DMAST_ERR_DMA; logf("DMA1 err: %02x", res); #if 0 logf(" SAR1: %08x", SAR1); @@ -456,19 +453,22 @@ void DMA1(void) * Ignore valnogood since several sources don't set it properly. */ /* clear: ebu1cnew, symbolerr, parityerr */ INTERRUPTCLEAR = (1 << 25) | (1 << 23) | (1 << 22); - status = DMA_REC_ERROR_SPDIF; + status = PCM_DMAST_ERR_SPDIF; logf("spdif err"); } #endif /* Inform PCM we have more data (or error) */ - pcm_rec_more_ready_callback(status, &start, &size); + void *addr; + size_t size; - if (size != 0) + if (pcm_rec_dma_complete_callback(status, &addr, &size)) { - DAR1 = (unsigned long)start; /* Destination address */ + DAR1 = (unsigned long)addr; /* Destination address */ BCR1 = (unsigned long)size; /* Bytes to transfer */ or_l(DMA_EEXT | DMA_INT, &DCR1); /* per request and int ON */ + + pcm_rec_dma_status_callback(PCM_DMAST_STARTED); } } /* DMA1 */ 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, if (!pcm_data_size) /* get some initial data */ { - new_buffer = true; - pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size); + new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK, + (const void**)&pcm_data_start, &pcm_data_size); } while(left > 0 && pcm_data_size) @@ -99,7 +99,7 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, if (new_buffer) { new_buffer = false; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); /* NOTE: might need to release the mutex and sleep here if the 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, if (pcm_data_size == 0) /* need new data */ { - new_buffer = true; - pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size); + new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK, + (const void**)&pcm_data_start, &pcm_data_size); } else /* increment data pointer and feed more */ pcm_data_start += transfer_size; } - if (new_buffer && pcm_data_size) - pcm_play_dma_started_callback(); + if (new_buffer) + pcm_play_dma_status_callback(PCM_DMAST_STARTED); unlock_audio(); return max_size - left; @@ -154,7 +154,7 @@ void pcm_play_dma_start(const void *addr, size_t size) void pcm_play_dma_stop(void) { - /* NOTE: due to how pcm_play_get_more_callback() works, this is + /* NOTE: due to how pcm_play_dma_complete_callback() works, this is * possibly called from nativeWrite(), i.e. another (host) thread * => need to discover the appropriate JNIEnv* */ 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) from inside gstreamer's stream thread as it will deadlock */ inside_feed_data = 1; - pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); - - if (pcm_data_size != 0) + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, (const void **)&pcm_data, + &pcm_data_size)) { GstBuffer *buffer = gst_buffer_new (); GstFlowReturn ret; @@ -205,7 +204,7 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused) if (ret != 0) DEBUGF("push-buffer error result: %d\n", ret); - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } else { 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) if (!pcm_size) { new_buffer = true; - pcm_play_get_more_callback((void **)&pcm_data, &pcm_size); - if (!pcm_size || !pcm_data) + if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, + (const void **)&pcm_data, &pcm_size)) + { return false; + } } copy_n = MIN((ssize_t)pcm_size, frames_left*4); memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n); @@ -237,7 +239,7 @@ static bool fill_frames(void) if (new_buffer) { new_buffer = false; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } } 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; #if CONFIG_CODEC == SWCODEC static int cvt_status = -1; -static Uint8* pcm_data; +static const Uint8* pcm_data; static size_t pcm_data_size; static size_t pcm_sample_bytes; static size_t pcm_channel_bytes; @@ -109,7 +109,7 @@ void pcm_play_dma_start(const void *addr, size_t size) { pcm_dma_apply_settings_nolock(); - pcm_data = (Uint8 *) addr; + pcm_data = addr; pcm_data_size = size; SDL_PauseAudio(0); @@ -245,48 +245,48 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) /* Audio card wants more? Get some more then. */ while (len > 0) { - new_buffer = true; - pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); - start: - if (pcm_data_size != 0) { - udata->num_in = pcm_data_size / pcm_sample_bytes; - udata->num_out = len / pcm_sample_bytes; + new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK, + (const void **)&pcm_data, &pcm_data_size); - write_to_soundcard(udata); - - udata->num_in *= pcm_sample_bytes; - udata->num_out *= pcm_sample_bytes; - - - if (new_buffer) - { - new_buffer = false; - pcm_play_dma_started_callback(); - - if ((size_t)len > udata->num_out) - { - int delay = pcm_data_size*250 / pcm_sampr - 1; - - if (delay > 0) - { - SDL_UnlockMutex(audio_lock); - SDL_Delay(delay); - SDL_LockMutex(audio_lock); - - if (!pcm_is_playing()) - break; - } - } - } - - pcm_data += udata->num_in; - pcm_data_size -= udata->num_in; - udata->stream += udata->num_out; - len -= udata->num_out; - } else { + if (!new_buffer) { DEBUGF("sdl_audio_callback: No Data.\n"); break; } + + start: + udata->num_in = pcm_data_size / pcm_sample_bytes; + udata->num_out = len / pcm_sample_bytes; + + write_to_soundcard(udata); + + udata->num_in *= pcm_sample_bytes; + udata->num_out *= pcm_sample_bytes; + + if (new_buffer) + { + new_buffer = false; + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + + if ((size_t)len > udata->num_out) + { + int delay = pcm_data_size*250 / pcm_sampr - 1; + + if (delay > 0) + { + SDL_UnlockMutex(audio_lock); + SDL_Delay(delay); + SDL_LockMutex(audio_lock); + + if (!pcm_is_playing()) + break; + } + } + } + + pcm_data += udata->num_in; + pcm_data_size -= udata->num_in; + udata->stream += udata->num_out; + len -= udata->num_out; } 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) audiohw_set_frequency(pcm_sampr); } -static void* playback_address; +static const void* playback_address; static inline void set_dma(const void *addr, size_t size) { int burst_size; @@ -96,21 +96,19 @@ static inline void set_dma(const void *addr, size_t size) REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT; REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | burst_size | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE); - playback_address = (void*)addr; + playback_address = addr; } static inline void play_dma_callback(void) { - void *start; + const void *start; size_t size; - pcm_play_get_more_callback(&start, &size); - - if (size != 0) + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) { set_dma(start, size); REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; - pcm_play_dma_started_callback(); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); } }