diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-01-19 09:15:23 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-01-19 09:15:23 +0000 |
commit | c04f1a23c79a4d09b4c04c149f0c5b7da9939254 (patch) | |
tree | 71208133e8a997ddb8302a5d2f5a2370fd404c87 | |
parent | 77075be27902a097be2ec2513bf913607df40997 (diff) | |
download | rockbox-c04f1a23c79a4d09b4c04c149f0c5b7da9939254.tar.gz rockbox-c04f1a23c79a4d09b4c04c149f0c5b7da9939254.zip |
AS3525: Implement a true audio pause and full-resolution audio tick. Take care of a few atomic hotspots.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29088 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | firmware/target/arm/as3525/dma-pl081.c | 30 | ||||
-rw-r--r-- | firmware/target/arm/as3525/dma-target.h | 2 | ||||
-rw-r--r-- | firmware/target/arm/as3525/pcm-as3525.c | 83 |
3 files changed, 85 insertions, 30 deletions
diff --git a/firmware/target/arm/as3525/dma-pl081.c b/firmware/target/arm/as3525/dma-pl081.c index a53fde99c5..f4cc07df30 100644 --- a/firmware/target/arm/as3525/dma-pl081.c +++ b/firmware/target/arm/as3525/dma-pl081.c | |||
@@ -33,7 +33,7 @@ void dma_retain(void) | |||
33 | if(++dma_used == 1) | 33 | if(++dma_used == 1) |
34 | { | 34 | { |
35 | bitset32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE); | 35 | bitset32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE); |
36 | DMAC_CONFIGURATION |= (1<<0); | 36 | bitset32(&DMAC_CONFIGURATION, 1<<0); |
37 | } | 37 | } |
38 | } | 38 | } |
39 | 39 | ||
@@ -41,7 +41,7 @@ void dma_release(void) | |||
41 | { | 41 | { |
42 | if(--dma_used == 0) | 42 | if(--dma_used == 0) |
43 | { | 43 | { |
44 | DMAC_CONFIGURATION &= ~(1<<0); | 44 | bitclr32(&DMAC_CONFIGURATION, 1<<0); |
45 | bitclr32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE); | 45 | bitclr32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE); |
46 | } | 46 | } |
47 | if (dma_used < 0) | 47 | if (dma_used < 0) |
@@ -56,11 +56,33 @@ void dma_init(void) | |||
56 | VIC_INT_ENABLE = INTERRUPT_DMAC; | 56 | VIC_INT_ENABLE = INTERRUPT_DMAC; |
57 | } | 57 | } |
58 | 58 | ||
59 | void dma_disable_channel(int channel) | 59 | void dma_pause_channel(int channel) |
60 | { | 60 | { |
61 | DMAC_CH_CONFIGURATION(channel) &= ~(1<<0); | 61 | /* Disable the channel - clears the FIFO after sending last word */ |
62 | bitclr32(&DMAC_CH_CONFIGURATION(channel), 1<<0); | ||
63 | /* Wait for it to go inactive */ | ||
64 | while (DMAC_CH_CONFIGURATION(channel) & (1<<17)); | ||
62 | } | 65 | } |
63 | 66 | ||
67 | void dma_resume_channel(int channel) | ||
68 | { | ||
69 | /* Resume - must reinit to where it left off (so the docs say) */ | ||
70 | unsigned long control = DMAC_CH_CONTROL(channel); | ||
71 | if ((control & 0x7ff) == 0) | ||
72 | return; /* empty */ | ||
73 | |||
74 | DMAC_INT_TC_CLEAR = (1<<channel); | ||
75 | DMAC_INT_ERR_CLEAR = (1<<channel); | ||
76 | DMAC_CH_SRC_ADDR(channel) = DMAC_CH_SRC_ADDR(channel); | ||
77 | DMAC_CH_DST_ADDR(channel) = DMAC_CH_DST_ADDR(channel); | ||
78 | DMAC_CH_LLI(channel) = DMAC_CH_LLI(channel); | ||
79 | DMAC_CH_CONTROL(channel) = control; | ||
80 | bitset32(&DMAC_CH_CONFIGURATION(channel), (1<<0)); | ||
81 | } | ||
82 | |||
83 | void dma_disable_channel(int channel) \ | ||
84 | __attribute__((alias("dma_pause_channel"))); | ||
85 | |||
64 | void dma_enable_channel(int channel, void *src, void *dst, int peri, | 86 | void dma_enable_channel(int channel, void *src, void *dst, int peri, |
65 | int flow_controller, bool src_inc, bool dst_inc, | 87 | int flow_controller, bool src_inc, bool dst_inc, |
66 | size_t size, int nwords, void (*callback)(void)) | 88 | size_t size, int nwords, void (*callback)(void)) |
diff --git a/firmware/target/arm/as3525/dma-target.h b/firmware/target/arm/as3525/dma-target.h index 2024fab73a..2ed6e36bd7 100644 --- a/firmware/target/arm/as3525/dma-target.h +++ b/firmware/target/arm/as3525/dma-target.h | |||
@@ -38,6 +38,8 @@ void dma_enable_channel(int channel, void *src, void *dst, int peri, | |||
38 | int flow_controller, bool src_inc, bool dst_inc, | 38 | int flow_controller, bool src_inc, bool dst_inc, |
39 | size_t size, int nwords, void (*callback)(void)); | 39 | size_t size, int nwords, void (*callback)(void)); |
40 | void dma_disable_channel(int channel); | 40 | void dma_disable_channel(int channel); |
41 | void dma_pause_channel(int channel); | ||
42 | void dma_resume_channel(int channel); | ||
41 | 43 | ||
42 | void dma_retain(void); | 44 | void dma_retain(void); |
43 | void dma_release(void); | 45 | void dma_release(void); |
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index 5c53c60fe1..469833b05c 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c | |||
@@ -35,9 +35,11 @@ | |||
35 | * and the number of 32bits words has to | 35 | * and the number of 32bits words has to |
36 | * fit in 11 bits of DMA register */ | 36 | * fit in 11 bits of DMA register */ |
37 | 37 | ||
38 | static void *dma_start_addr; | 38 | static void *dma_start_addr; /* Pointer to callback buffer */ |
39 | static size_t dma_size; /* in 4*32 bits */ | 39 | static size_t dma_start_size; /* Size of callback buffer */ |
40 | static size_t play_sub_size; /* size of subtransfer */ | 40 | static void *dma_sub_addr; /* Pointer to sub buffer */ |
41 | static size_t dma_rem_size; /* Remaining size - in 4*32 bits */ | ||
42 | static size_t play_sub_size; /* size of current subtransfer */ | ||
41 | static void dma_callback(void); | 43 | static void dma_callback(void); |
42 | static int locked = 0; | 44 | static int locked = 0; |
43 | static bool is_playing = false; | 45 | static bool is_playing = false; |
@@ -66,23 +68,22 @@ void pcm_play_unlock(void) | |||
66 | 68 | ||
67 | static void play_start_pcm(void) | 69 | static void play_start_pcm(void) |
68 | { | 70 | { |
69 | const unsigned char* addr = dma_start_addr; | 71 | const void *addr = dma_sub_addr; |
70 | size_t size = dma_size; | 72 | size_t size = dma_rem_size; |
71 | if(size > MAX_TRANSFER) | 73 | if(size > MAX_TRANSFER) |
72 | size = MAX_TRANSFER; | 74 | size = MAX_TRANSFER; |
73 | 75 | ||
74 | play_sub_size = size; | 76 | play_sub_size = size; |
75 | 77 | ||
76 | clean_dcache_range((void*)addr, size); /* force write back */ | ||
77 | dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT, | 78 | dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT, |
78 | DMAC_FLOWCTRL_DMAC_MEM_TO_PERI, true, false, size >> 2, DMA_S1, | 79 | DMAC_FLOWCTRL_DMAC_MEM_TO_PERI, true, false, size >> 2, |
79 | dma_callback); | 80 | DMA_S1, dma_callback); |
80 | } | 81 | } |
81 | 82 | ||
82 | static void dma_callback(void) | 83 | static void dma_callback(void) |
83 | { | 84 | { |
84 | dma_size -= play_sub_size; | 85 | dma_sub_addr += play_sub_size; |
85 | dma_start_addr += play_sub_size; | 86 | dma_rem_size -= play_sub_size; |
86 | play_sub_size = 0; /* Might get called again if locked */ | 87 | play_sub_size = 0; /* Might get called again if locked */ |
87 | 88 | ||
88 | if(locked) | 89 | if(locked) |
@@ -91,12 +92,18 @@ static void dma_callback(void) | |||
91 | return; | 92 | return; |
92 | } | 93 | } |
93 | 94 | ||
94 | if(!dma_size) | 95 | if(!dma_rem_size) |
95 | { | 96 | { |
96 | pcm_play_get_more_callback(&dma_start_addr, &dma_size); | 97 | pcm_play_get_more_callback(&dma_start_addr, &dma_start_size); |
97 | 98 | ||
98 | if (!dma_size) | 99 | if (!dma_start_size) |
99 | return; | 100 | return; |
101 | |||
102 | dma_sub_addr = dma_start_addr; | ||
103 | dma_rem_size = dma_start_size; | ||
104 | |||
105 | /* force writeback */ | ||
106 | clean_dcache_range(dma_start_addr, dma_start_size); | ||
100 | } | 107 | } |
101 | 108 | ||
102 | play_start_pcm(); | 109 | play_start_pcm(); |
@@ -104,8 +111,10 @@ static void dma_callback(void) | |||
104 | 111 | ||
105 | void pcm_play_dma_start(const void *addr, size_t size) | 112 | void pcm_play_dma_start(const void *addr, size_t size) |
106 | { | 113 | { |
107 | dma_size = size; | 114 | dma_start_addr = (void*)addr; |
108 | dma_start_addr = (unsigned char*)addr; | 115 | dma_start_size = size; |
116 | dma_sub_addr = dma_start_addr; | ||
117 | dma_rem_size = size; | ||
109 | 118 | ||
110 | bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); | 119 | bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); |
111 | CGU_AUDIO |= (1<<11); | 120 | CGU_AUDIO |= (1<<11); |
@@ -114,6 +123,8 @@ void pcm_play_dma_start(const void *addr, size_t size) | |||
114 | 123 | ||
115 | is_playing = true; | 124 | is_playing = true; |
116 | 125 | ||
126 | /* force writeback */ | ||
127 | clean_dcache_range(dma_start_addr, dma_start_size); | ||
117 | play_start_pcm(); | 128 | play_start_pcm(); |
118 | } | 129 | } |
119 | 130 | ||
@@ -121,7 +132,12 @@ void pcm_play_dma_stop(void) | |||
121 | { | 132 | { |
122 | is_playing = false; | 133 | is_playing = false; |
123 | dma_disable_channel(1); | 134 | dma_disable_channel(1); |
124 | dma_size = 0; | 135 | |
136 | /* Ensure byte counts read back 0 */ | ||
137 | DMAC_CH_SRC_ADDR(1) = 0; | ||
138 | dma_start_addr = NULL; | ||
139 | dma_start_size = 0; | ||
140 | dma_rem_size = 0; | ||
125 | 141 | ||
126 | dma_release(); | 142 | dma_release(); |
127 | 143 | ||
@@ -133,15 +149,21 @@ void pcm_play_dma_stop(void) | |||
133 | 149 | ||
134 | void pcm_play_dma_pause(bool pause) | 150 | void pcm_play_dma_pause(bool pause) |
135 | { | 151 | { |
152 | is_playing = !pause; | ||
153 | |||
136 | if(pause) | 154 | if(pause) |
137 | { | 155 | { |
138 | is_playing = false; | 156 | dma_pause_channel(1); |
139 | dma_disable_channel(1); | 157 | |
140 | play_callback_pending = false; | 158 | /* if producer's buffer finished, upper layer starts anew */ |
159 | if (dma_rem_size == 0) | ||
160 | play_callback_pending = false; | ||
141 | } | 161 | } |
142 | else | 162 | else |
143 | { | 163 | { |
144 | play_start_pcm(); | 164 | if (play_sub_size != 0) |
165 | dma_resume_channel(1); | ||
166 | /* else unlock calls the callback if sub buffers remain */ | ||
145 | } | 167 | } |
146 | } | 168 | } |
147 | 169 | ||
@@ -194,16 +216,25 @@ void pcm_dma_apply_settings(void) | |||
194 | 216 | ||
195 | size_t pcm_get_bytes_waiting(void) | 217 | size_t pcm_get_bytes_waiting(void) |
196 | { | 218 | { |
197 | return dma_size; | 219 | int oldstatus = disable_irq_save(); |
220 | size_t addr = DMAC_CH_SRC_ADDR(1); | ||
221 | size_t start_addr = (size_t)dma_start_addr; | ||
222 | size_t start_size = dma_start_size; | ||
223 | restore_interrupt(oldstatus); | ||
224 | |||
225 | return start_size - addr + start_addr; | ||
198 | } | 226 | } |
199 | 227 | ||
200 | const void * pcm_play_dma_get_peak_buffer(int *count) | 228 | const void * pcm_play_dma_get_peak_buffer(int *count) |
201 | { | 229 | { |
202 | pcm_play_lock(); | 230 | int oldstatus = disable_irq_save(); |
203 | void *addr = (void*)DMAC_CH_SRC_ADDR(1); | 231 | size_t addr = DMAC_CH_SRC_ADDR(1); |
204 | *count = (dma_size - (addr - dma_start_addr)) >> 2; | 232 | size_t start_addr = (size_t)dma_start_addr; |
205 | pcm_play_unlock(); | 233 | size_t start_size = dma_start_size; |
206 | return AS3525_UNCACHED_ADDR(addr); | 234 | restore_interrupt(oldstatus); |
235 | |||
236 | *count = (start_size - addr + start_addr) >> 2; | ||
237 | return (void*)AS3525_UNCACHED_ADDR(addr); | ||
207 | } | 238 | } |
208 | 239 | ||
209 | #ifdef HAVE_PCM_DMA_ADDRESS | 240 | #ifdef HAVE_PCM_DMA_ADDRESS |