diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2013-06-18 15:45:58 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2013-06-18 15:45:58 +0200 |
commit | 7fb0b893f9b6d167d4849e90fa923e76a68d72ce (patch) | |
tree | 36fbe6bb90943592a1e51989265cec88164ad71b /firmware/target/arm/imx233/pcm-imx233.c | |
parent | ac4e76d0720eeca0dd00ec3bf1fcd80f76d543c0 (diff) | |
download | rockbox-7fb0b893f9b6d167d4849e90fa923e76a68d72ce.tar.gz rockbox-7fb0b893f9b6d167d4849e90fa923e76a68d72ce.zip |
imx233: implement recording side of pcm
Although everything is implemented, recording still doesn't work,
dma is stuck. Add code for reference until this get a proper fix.
Change-Id: Ifc016b00876230c6d337a5cd4f8bb90b856efac8
Diffstat (limited to 'firmware/target/arm/imx233/pcm-imx233.c')
-rw-r--r-- | firmware/target/arm/imx233/pcm-imx233.c | 199 |
1 files changed, 172 insertions, 27 deletions
diff --git a/firmware/target/arm/imx233/pcm-imx233.c b/firmware/target/arm/imx233/pcm-imx233.c index 2c5033471f..07af090f38 100644 --- a/firmware/target/arm/imx233/pcm-imx233.c +++ b/firmware/target/arm/imx233/pcm-imx233.c | |||
@@ -35,34 +35,70 @@ struct pcm_dma_command_t | |||
35 | 35 | ||
36 | __ENSURE_STRUCT_CACHE_FRIENDLY(struct pcm_dma_command_t) | 36 | __ENSURE_STRUCT_CACHE_FRIENDLY(struct pcm_dma_command_t) |
37 | 37 | ||
38 | static int locked = 0; | 38 | /* Because we have no way of stopping the DMA properly (see below), we can only |
39 | * let the tranfer finish on stop. However if the transfer is very long it could | ||
40 | * take a while. We work around this by splitting big transfers into small burst | ||
41 | * to make sure we can stop quickly. */ | ||
42 | |||
43 | static int dac_locked = 0; | ||
39 | static struct pcm_dma_command_t dac_dma; | 44 | static struct pcm_dma_command_t dac_dma; |
40 | static bool pcm_freezed = false; | 45 | static bool dac_freezed = false; |
46 | |||
47 | static const void *dac_buf; /* current buffer */ | ||
48 | static size_t dac_size; /* remaining size */ | ||
49 | |||
50 | /* for both recording and playback: maximum transfer size, see | ||
51 | * pcm_dma_apply_settings */ | ||
52 | static size_t dma_max_size = CACHEALIGN_UP(1600); | ||
53 | |||
54 | enum | ||
55 | { | ||
56 | DAC_PLAYING, | ||
57 | DAC_STOP_PENDING, | ||
58 | DAC_STOPPED, | ||
59 | }dac_state = DAC_STOPPED; | ||
41 | 60 | ||
42 | /** | 61 | /** |
43 | * WARNING ! | 62 | * WARNING ! |
44 | * Never reset the dma channel, otherwise it will halt the DAC for some reason | 63 | * Never reset the dma channel, otherwise it will halt the DAC for some reason |
64 | * and I don't know how to recover from this state | ||
45 | * */ | 65 | * */ |
46 | 66 | ||
47 | static void play(const void *addr, size_t size) | 67 | static void play(void) |
48 | { | 68 | { |
69 | /* split transfer if needed */ | ||
70 | size_t xfer = MIN(dac_size, dma_max_size); | ||
71 | |||
49 | dac_dma.dma.next = NULL; | 72 | dac_dma.dma.next = NULL; |
50 | dac_dma.dma.buffer = (void *)addr; | 73 | dac_dma.dma.buffer = (void *)dac_buf; |
51 | dac_dma.dma.cmd = BF_OR4(APB_CHx_CMD, COMMAND_V(READ), | 74 | dac_dma.dma.cmd = BF_OR4(APB_CHx_CMD, COMMAND_V(READ), |
52 | IRQONCMPLT(1), SEMAPHORE(1), XFER_COUNT(size)); | 75 | IRQONCMPLT(1), SEMAPHORE(1), XFER_COUNT(xfer)); |
53 | /* dma subsystem will make sure cached stuff is written to memory */ | 76 | /* dma subsystem will make sure cached stuff is written to memory */ |
77 | dac_state = DAC_PLAYING; | ||
54 | imx233_dma_start_command(APB_AUDIO_DAC, &dac_dma.dma); | 78 | imx233_dma_start_command(APB_AUDIO_DAC, &dac_dma.dma); |
79 | /* advance buffer */ | ||
80 | dac_buf += xfer; | ||
81 | dac_size -= xfer; | ||
55 | } | 82 | } |
56 | 83 | ||
57 | void INT_DAC_DMA(void) | 84 | void INT_DAC_DMA(void) |
58 | { | 85 | { |
59 | const void *start; | 86 | /* if stop is pending, ackonowledge stop |
60 | size_t size; | 87 | * otherwise try to get some more and stop if there is none */ |
61 | 88 | if(dac_state == DAC_STOP_PENDING) | |
62 | if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) | 89 | { |
90 | dac_state = DAC_STOPPED; | ||
91 | } | ||
92 | else if(dac_state == DAC_PLAYING) | ||
63 | { | 93 | { |
64 | play(start, size); | 94 | /* continue if buffer is not done, otherwise try to get some new data */ |
65 | pcm_play_dma_status_callback(PCM_DMAST_STARTED); | 95 | if(dac_size != 0 || pcm_play_dma_complete_callback(PCM_DMAST_OK, &dac_buf, &dac_size)) |
96 | { | ||
97 | play(); | ||
98 | pcm_play_dma_status_callback(PCM_DMAST_STARTED); | ||
99 | } | ||
100 | else | ||
101 | dac_state = DAC_STOPPED; | ||
66 | } | 102 | } |
67 | 103 | ||
68 | imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); | 104 | imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); |
@@ -70,36 +106,53 @@ void INT_DAC_DMA(void) | |||
70 | 106 | ||
71 | void INT_DAC_ERROR(void) | 107 | void INT_DAC_ERROR(void) |
72 | { | 108 | { |
73 | /* TODO: Inform of error through pcm_play_dma_complete_callback */ | 109 | dac_state = DAC_STOPPED; |
110 | pcm_play_dma_status_callback(PCM_DMAST_ERR_DMA); | ||
111 | imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); | ||
74 | } | 112 | } |
75 | 113 | ||
76 | void pcm_play_lock(void) | 114 | void pcm_play_lock(void) |
77 | { | 115 | { |
78 | if(locked++ == 0) | 116 | if(dac_locked++ == 0) |
79 | imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, false); | 117 | imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, false); |
80 | } | 118 | } |
81 | 119 | ||
82 | void pcm_play_unlock(void) | 120 | void pcm_play_unlock(void) |
83 | { | 121 | { |
84 | if(--locked == 0) | 122 | if(--dac_locked == 0) |
85 | imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, true); | 123 | imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, true); |
86 | } | 124 | } |
87 | 125 | ||
88 | void pcm_play_dma_stop(void) | 126 | void pcm_play_dma_stop(void) |
89 | { | 127 | { |
128 | /* do not interrupt the current transaction because resetting the dma | ||
129 | * would halt the DAC and clearing RUN causes sound havoc so simply | ||
130 | * wait for the end of transfer */ | ||
131 | pcm_play_lock(); | ||
132 | dac_buf = NULL; | ||
133 | dac_size = 0; | ||
134 | dac_state = DAC_STOP_PENDING; | ||
135 | pcm_play_unlock(); | ||
90 | } | 136 | } |
91 | 137 | ||
92 | void pcm_play_dma_start(const void *addr, size_t size) | 138 | void pcm_play_dma_start(const void *addr, size_t size) |
93 | { | 139 | { |
94 | pcm_play_dma_stop(); | 140 | pcm_play_lock(); |
95 | 141 | /* update pending buffer */ | |
96 | play(addr, size); | 142 | dac_buf = addr; |
143 | dac_size = size; | ||
144 | /* if we are stopped restart playback, otherwise IRQ will pick up */ | ||
145 | if(dac_state == DAC_STOPPED) | ||
146 | play(); | ||
147 | else | ||
148 | dac_state = DAC_PLAYING; | ||
149 | pcm_play_unlock(); | ||
97 | } | 150 | } |
98 | 151 | ||
99 | void pcm_play_dma_pause(bool pause) | 152 | void pcm_play_dma_pause(bool pause) |
100 | { | 153 | { |
101 | imx233_dma_freeze_channel(APB_AUDIO_DAC, pause); | 154 | imx233_dma_freeze_channel(APB_AUDIO_DAC, pause); |
102 | pcm_freezed = pause; | 155 | dac_freezed = pause; |
103 | } | 156 | } |
104 | 157 | ||
105 | void pcm_play_dma_init(void) | 158 | void pcm_play_dma_init(void) |
@@ -117,7 +170,14 @@ void pcm_play_dma_postinit(void) | |||
117 | 170 | ||
118 | void pcm_dma_apply_settings(void) | 171 | void pcm_dma_apply_settings(void) |
119 | { | 172 | { |
173 | pcm_play_lock(); | ||
174 | /* update frequency */ | ||
120 | audiohw_set_frequency(pcm_fsel); | 175 | audiohw_set_frequency(pcm_fsel); |
176 | /* compute maximum transfer size: aim at ~1/100s stop time maximum, make sure | ||
177 | * the resulting value is a multiple of cache line. At sample rate F we | ||
178 | * transfer two samples (2 x 2 bytes) F times per second = 4F b/s */ | ||
179 | dma_max_size = CACHEALIGN_UP(4 * pcm_sampr / 100); | ||
180 | pcm_play_unlock(); | ||
121 | } | 181 | } |
122 | 182 | ||
123 | size_t pcm_get_bytes_waiting(void) | 183 | size_t pcm_get_bytes_waiting(void) |
@@ -128,10 +188,10 @@ size_t pcm_get_bytes_waiting(void) | |||
128 | 188 | ||
129 | const void *pcm_play_dma_get_peak_buffer(int *count) | 189 | const void *pcm_play_dma_get_peak_buffer(int *count) |
130 | { | 190 | { |
131 | if(!pcm_freezed) | 191 | if(!dac_freezed) |
132 | imx233_dma_freeze_channel(APB_AUDIO_DAC, true); | 192 | imx233_dma_freeze_channel(APB_AUDIO_DAC, true); |
133 | struct imx233_dma_info_t info = imx233_dma_get_info(APB_AUDIO_DAC, DMA_INFO_AHB_BYTES | DMA_INFO_BAR); | 193 | struct imx233_dma_info_t info = imx233_dma_get_info(APB_AUDIO_DAC, DMA_INFO_AHB_BYTES | DMA_INFO_BAR); |
134 | if(!pcm_freezed) | 194 | if(!dac_freezed) |
135 | imx233_dma_freeze_channel(APB_AUDIO_DAC, false); | 195 | imx233_dma_freeze_channel(APB_AUDIO_DAC, false); |
136 | *count = info.ahb_bytes; | 196 | *count = info.ahb_bytes; |
137 | return (void *)info.bar; | 197 | return (void *)info.bar; |
@@ -141,39 +201,124 @@ const void *pcm_play_dma_get_peak_buffer(int *count) | |||
141 | * Recording | 201 | * Recording |
142 | */ | 202 | */ |
143 | 203 | ||
204 | /* Because we have no way of stopping the DMA properly (like for the DAC), | ||
205 | * we can only let the tranfer finish on stop. However if the transfer is very | ||
206 | * long it could take a while. We work around this by splitting big transfers | ||
207 | * into small burst to make sure we can stop quickly. */ | ||
208 | |||
209 | static int adc_locked = 0; | ||
210 | static struct pcm_dma_command_t adc_dma; | ||
211 | |||
212 | static void *adc_buf; /* current buffer */ | ||
213 | static size_t adc_size; /* remaining size */ | ||
214 | |||
215 | enum | ||
216 | { | ||
217 | ADC_RECORDING, | ||
218 | ADC_STOP_PENDING, | ||
219 | ADC_STOPPED, | ||
220 | }adc_state = ADC_STOPPED; | ||
221 | |||
144 | void pcm_rec_lock(void) | 222 | void pcm_rec_lock(void) |
145 | { | 223 | { |
224 | if(adc_locked++ == 0) | ||
225 | imx233_dma_enable_channel_interrupt(APB_AUDIO_ADC, false); | ||
146 | } | 226 | } |
147 | 227 | ||
228 | |||
148 | void pcm_rec_unlock(void) | 229 | void pcm_rec_unlock(void) |
149 | { | 230 | { |
231 | if(--adc_locked == 0) | ||
232 | imx233_dma_enable_channel_interrupt(APB_AUDIO_ADC, true); | ||
150 | } | 233 | } |
151 | 234 | ||
152 | void pcm_rec_dma_init(void) | 235 | void pcm_rec_dma_init(void) |
153 | { | 236 | { |
237 | imx233_icoll_enable_interrupt(INT_SRC_ADC_DMA, true); | ||
238 | imx233_icoll_enable_interrupt(INT_SRC_ADC_ERROR, true); | ||
239 | imx233_dma_enable_channel_interrupt(APB_AUDIO_ADC, true); | ||
154 | } | 240 | } |
155 | 241 | ||
156 | void pcm_rec_dma_close(void) | 242 | void pcm_rec_dma_close(void) |
157 | { | 243 | { |
244 | pcm_rec_dma_stop(); | ||
158 | } | 245 | } |
159 | 246 | ||
160 | void pcm_rec_dma_start(void *addr, size_t size) | 247 | static void rec(void) |
161 | { | 248 | { |
162 | (void) addr; | 249 | /* split transfer if needed */ |
163 | (void) size; | 250 | size_t xfer = MIN(adc_size, dma_max_size); |
251 | |||
252 | adc_dma.dma.next = NULL; | ||
253 | adc_dma.dma.buffer = (void *)adc_buf; | ||
254 | adc_dma.dma.cmd = BF_OR4(APB_CHx_CMD, COMMAND_V(WRITE), | ||
255 | IRQONCMPLT(1), SEMAPHORE(1), XFER_COUNT(xfer)); | ||
256 | /* dma subsystem will make sure cached stuff is written to memory */ | ||
257 | adc_state = ADC_RECORDING; | ||
258 | imx233_dma_start_command(APB_AUDIO_ADC, &adc_dma.dma); | ||
259 | /* advance buffer */ | ||
260 | adc_buf += xfer; | ||
261 | adc_size -= xfer; | ||
164 | } | 262 | } |
165 | 263 | ||
166 | /* | 264 | void INT_ADC_DMA(void) |
167 | void pcm_rec_dma_record_more(void *start, size_t size) | ||
168 | { | 265 | { |
266 | /* if stop is pending, ackonowledge stop | ||
267 | * otherwise try to get some more and stop if there is none */ | ||
268 | if(adc_state == ADC_STOP_PENDING) | ||
269 | { | ||
270 | adc_state = ADC_STOPPED; | ||
271 | } | ||
272 | else if(adc_state == ADC_RECORDING) | ||
273 | { | ||
274 | /* continue if buffer is not done, otherwise try to get some new data */ | ||
275 | if(adc_size != 0 || pcm_rec_dma_complete_callback(PCM_DMAST_OK, &adc_buf, &adc_size)) | ||
276 | { | ||
277 | rec(); | ||
278 | pcm_rec_dma_status_callback(PCM_DMAST_STARTED); | ||
279 | } | ||
280 | else | ||
281 | adc_state = ADC_STOPPED; | ||
282 | } | ||
283 | |||
284 | imx233_dma_clear_channel_interrupt(APB_AUDIO_ADC); | ||
285 | } | ||
286 | |||
287 | void INT_ADC_ERROR(void) | ||
288 | { | ||
289 | adc_state = ADC_STOPPED; | ||
290 | pcm_rec_dma_status_callback(PCM_DMAST_ERR_DMA); | ||
291 | imx233_dma_clear_channel_interrupt(APB_AUDIO_ADC); | ||
292 | } | ||
293 | |||
294 | void pcm_rec_dma_start(void *addr, size_t size) | ||
295 | { | ||
296 | pcm_rec_lock(); | ||
297 | /* update pending buffer */ | ||
298 | adc_buf = addr; | ||
299 | adc_size = size; | ||
300 | /* if we are stopped restart recording, otherwise IRQ will pick up */ | ||
301 | if(adc_state == ADC_STOPPED) | ||
302 | rec(); | ||
303 | else | ||
304 | adc_state = ADC_RECORDING; | ||
305 | pcm_rec_unlock(); | ||
169 | } | 306 | } |
170 | */ | ||
171 | 307 | ||
172 | void pcm_rec_dma_stop(void) | 308 | void pcm_rec_dma_stop(void) |
173 | { | 309 | { |
310 | /* do not interrupt the current transaction because resetting the dma | ||
311 | * would halt the ADC and clearing RUN causes sound havoc so simply | ||
312 | * wait for the end of transfer */ | ||
313 | pcm_rec_lock(); | ||
314 | adc_buf = NULL; | ||
315 | adc_size = 0; | ||
316 | adc_state = ADC_STOP_PENDING; | ||
317 | pcm_rec_unlock(); | ||
174 | } | 318 | } |
175 | 319 | ||
176 | const void *pcm_rec_dma_get_peak_buffer(void) | 320 | const void *pcm_rec_dma_get_peak_buffer(void) |
177 | { | 321 | { |
178 | return NULL; | 322 | struct imx233_dma_info_t info = imx233_dma_get_info(APB_AUDIO_ADC, DMA_INFO_BAR); |
323 | return (void *)info.bar; | ||
179 | } | 324 | } |