summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2013-06-18 15:45:58 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2013-06-18 15:45:58 +0200
commit7fb0b893f9b6d167d4849e90fa923e76a68d72ce (patch)
tree36fbe6bb90943592a1e51989265cec88164ad71b
parentac4e76d0720eeca0dd00ec3bf1fcd80f76d543c0 (diff)
downloadrockbox-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
-rw-r--r--firmware/target/arm/imx233/pcm-imx233.c199
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
38static 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
43static int dac_locked = 0;
39static struct pcm_dma_command_t dac_dma; 44static struct pcm_dma_command_t dac_dma;
40static bool pcm_freezed = false; 45static bool dac_freezed = false;
46
47static const void *dac_buf; /* current buffer */
48static size_t dac_size; /* remaining size */
49
50/* for both recording and playback: maximum transfer size, see
51 * pcm_dma_apply_settings */
52static size_t dma_max_size = CACHEALIGN_UP(1600);
53
54enum
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
47static void play(const void *addr, size_t size) 67static 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
57void INT_DAC_DMA(void) 84void 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
71void INT_DAC_ERROR(void) 107void 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
76void pcm_play_lock(void) 114void 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
82void pcm_play_unlock(void) 120void 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
88void pcm_play_dma_stop(void) 126void 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
92void pcm_play_dma_start(const void *addr, size_t size) 138void 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
99void pcm_play_dma_pause(bool pause) 152void 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
105void pcm_play_dma_init(void) 158void pcm_play_dma_init(void)
@@ -117,7 +170,14 @@ void pcm_play_dma_postinit(void)
117 170
118void pcm_dma_apply_settings(void) 171void 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
123size_t pcm_get_bytes_waiting(void) 183size_t pcm_get_bytes_waiting(void)
@@ -128,10 +188,10 @@ size_t pcm_get_bytes_waiting(void)
128 188
129const void *pcm_play_dma_get_peak_buffer(int *count) 189const 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
209static int adc_locked = 0;
210static struct pcm_dma_command_t adc_dma;
211
212static void *adc_buf; /* current buffer */
213static size_t adc_size; /* remaining size */
214
215enum
216{
217 ADC_RECORDING,
218 ADC_STOP_PENDING,
219 ADC_STOPPED,
220}adc_state = ADC_STOPPED;
221
144void pcm_rec_lock(void) 222void 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
148void pcm_rec_unlock(void) 229void 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
152void pcm_rec_dma_init(void) 235void 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
156void pcm_rec_dma_close(void) 242void pcm_rec_dma_close(void)
157{ 243{
244 pcm_rec_dma_stop();
158} 245}
159 246
160void pcm_rec_dma_start(void *addr, size_t size) 247static 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/* 264void INT_ADC_DMA(void)
167void 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
287void 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
294void 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
172void pcm_rec_dma_stop(void) 308void 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
176const void *pcm_rec_dma_get_peak_buffer(void) 320const 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}