summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-01-19 09:15:23 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-01-19 09:15:23 +0000
commitc04f1a23c79a4d09b4c04c149f0c5b7da9939254 (patch)
tree71208133e8a997ddb8302a5d2f5a2370fd404c87
parent77075be27902a097be2ec2513bf913607df40997 (diff)
downloadrockbox-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.c30
-rw-r--r--firmware/target/arm/as3525/dma-target.h2
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c83
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
59void dma_disable_channel(int channel) 59void 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
67void 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
83void dma_disable_channel(int channel) \
84 __attribute__((alias("dma_pause_channel")));
85
64void dma_enable_channel(int channel, void *src, void *dst, int peri, 86void 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));
40void dma_disable_channel(int channel); 40void dma_disable_channel(int channel);
41void dma_pause_channel(int channel);
42void dma_resume_channel(int channel);
41 43
42void dma_retain(void); 44void dma_retain(void);
43void dma_release(void); 45void 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
38static void *dma_start_addr; 38static void *dma_start_addr; /* Pointer to callback buffer */
39static size_t dma_size; /* in 4*32 bits */ 39static size_t dma_start_size; /* Size of callback buffer */
40static size_t play_sub_size; /* size of subtransfer */ 40static void *dma_sub_addr; /* Pointer to sub buffer */
41static size_t dma_rem_size; /* Remaining size - in 4*32 bits */
42static size_t play_sub_size; /* size of current subtransfer */
41static void dma_callback(void); 43static void dma_callback(void);
42static int locked = 0; 44static int locked = 0;
43static bool is_playing = false; 45static bool is_playing = false;
@@ -66,23 +68,22 @@ void pcm_play_unlock(void)
66 68
67static void play_start_pcm(void) 69static 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
82static void dma_callback(void) 83static 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
105void pcm_play_dma_start(const void *addr, size_t size) 112void 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
134void pcm_play_dma_pause(bool pause) 150void 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
195size_t pcm_get_bytes_waiting(void) 217size_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
200const void * pcm_play_dma_get_peak_buffer(int *count) 228const 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