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