diff options
author | Thom Johansen <thomj@rockbox.org> | 2006-05-11 22:55:24 +0000 |
---|---|---|
committer | Thom Johansen <thomj@rockbox.org> | 2006-05-11 22:55:24 +0000 |
commit | a4bfe37c6cdcc2e70b2b6d5d486531fa2986370b (patch) | |
tree | 24f01ed56d3197d66e2f66cabbd4a9814ce14b72 /firmware/pcm_record.c | |
parent | 5725b8cbcbe11c006fe92874dcc5d834e006daa6 (diff) | |
download | rockbox-a4bfe37c6cdcc2e70b2b6d5d486531fa2986370b.tar.gz rockbox-a4bfe37c6cdcc2e70b2b6d5d486531fa2986370b.zip |
Optical S/PDIF recording and monitoring for Iriver H1x0. Removed unsupported recording options on Iriver. Sample rate displayed in recording screen reflects the real S/PDIF sample rate when doing S/PDIF recording. Testing would be appreciated. Thanks to Jens Arnold for fixing the DMA hang issue. Will reset settings!
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9916 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/pcm_record.c')
-rw-r--r-- | firmware/pcm_record.c | 188 |
1 files changed, 167 insertions, 21 deletions
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c index a7d8bc707c..f97995df72 100644 --- a/firmware/pcm_record.c +++ b/firmware/pcm_record.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "cpu.h" | 31 | #include "cpu.h" |
32 | #include "i2c.h" | 32 | #include "i2c.h" |
33 | #include "power.h" | ||
33 | #include "uda1380.h" | 34 | #include "uda1380.h" |
34 | #include "system.h" | 35 | #include "system.h" |
35 | #include "usb.h" | 36 | #include "usb.h" |
@@ -61,6 +62,8 @@ static volatile int error_count; /* Number of DMA errors */ | |||
61 | static long record_start_time; /* Value of current_tick when recording was started */ | 62 | static long record_start_time; /* Value of current_tick when recording was started */ |
62 | static long pause_start_time; /* Value of current_tick when pause was started */ | 63 | static long pause_start_time; /* Value of current_tick when pause was started */ |
63 | static volatile int buffered_chunks; /* number of valid chunks in buffer */ | 64 | static volatile int buffered_chunks; /* number of valid chunks in buffer */ |
65 | static unsigned int sample_rate; /* Sample rate at time of recording start */ | ||
66 | static int rec_source; /* Current recording source */ | ||
64 | 67 | ||
65 | static int wav_file; | 68 | static int wav_file; |
66 | static char recording_filename[MAX_PATH]; | 69 | static char recording_filename[MAX_PATH]; |
@@ -193,51 +196,144 @@ unsigned long audio_num_recorded_bytes(void) | |||
193 | return 0; | 196 | return 0; |
194 | } | 197 | } |
195 | 198 | ||
199 | #ifdef HAVE_SPDIF_IN | ||
200 | /* Only the last six of these are standard rates, but all sample rates are | ||
201 | * possible, so we support some other common ones as well. | ||
202 | */ | ||
203 | static unsigned long spdif_sample_rates[] = { | ||
204 | 8000, 11025, 12000, 16000, 22050, 24000, | ||
205 | 32000, 44100, 48000, 64000, 88200, 96000 | ||
206 | }; | ||
207 | |||
208 | /* Return SPDIF sample rate. Since we base our reading on the actual SPDIF | ||
209 | * sample rate (which might be a bit inaccurate), we round off to the closest | ||
210 | * sample rate that is supported by SPDIF. | ||
211 | */ | ||
212 | unsigned long audio_get_spdif_sample_rate(void) | ||
213 | { | ||
214 | int i = 0; | ||
215 | unsigned long measured_rate; | ||
216 | const int upper_bound = sizeof(spdif_sample_rates)/sizeof(long) - 1; | ||
217 | |||
218 | /* The following formula is specified in MCF5249 user's manual section | ||
219 | * 17.6.1. The 3*(1 << 13) part will need changing if the setup of the | ||
220 | * PHASECONFIG register is ever changed. The 128 divide is because of the | ||
221 | * fact that the SPDIF clock is the sample rate times 128. | ||
222 | */ | ||
223 | measured_rate = (unsigned long)((unsigned long long)FREQMEAS*CPU_FREQ/ | ||
224 | ((1 << 15)*3*(1 << 13))/128); | ||
225 | /* Find which SPDIF sample rate we're closest to. */ | ||
226 | while (spdif_sample_rates[i] < measured_rate && i < upper_bound) ++i; | ||
227 | if (i > 0 && i < upper_bound) | ||
228 | { | ||
229 | long diff1 = measured_rate - spdif_sample_rates[i - 1]; | ||
230 | long diff2 = spdif_sample_rates[i] - measured_rate; | ||
231 | |||
232 | if (diff2 > diff1) --i; | ||
233 | } | ||
234 | return spdif_sample_rates[i]; | ||
235 | } | ||
236 | #endif | ||
237 | |||
238 | #ifdef HAVE_SPDIF_POWER | ||
239 | static bool spdif_power_setting; | ||
240 | |||
241 | void audio_set_spdif_power_setting(bool on) | ||
242 | { | ||
243 | spdif_power_setting = on; | ||
244 | } | ||
245 | #endif | ||
196 | 246 | ||
197 | /** | 247 | /** |
198 | * Sets the audio source | 248 | * Sets the audio source |
199 | * | 249 | * |
200 | * This functions starts feeding the CPU with audio data over the I2S bus | 250 | * This functions starts feeding the CPU with audio data over the I2S bus |
201 | * | 251 | * |
202 | * @param source 0=mic, 1=line-in, (todo: 2=spdif) | 252 | * @param source 0=mic, 1=line-in, 2=spdif |
203 | */ | 253 | */ |
204 | void audio_set_recording_options(int frequency, int quality, | 254 | void audio_set_recording_options(int frequency, int quality, |
205 | int source, int channel_mode, | 255 | int source, int channel_mode, |
206 | bool editable, int prerecord_time) | 256 | bool editable, int prerecord_time) |
207 | { | 257 | { |
208 | /* TODO: */ | 258 | /* TODO: */ |
209 | (void)frequency; | ||
210 | (void)quality; | 259 | (void)quality; |
211 | (void)channel_mode; | 260 | (void)channel_mode; |
212 | (void)editable; | 261 | (void)editable; |
213 | 262 | ||
214 | /* WARNING: calculation below uses fixed frequency! */ | 263 | /* NOTE: Coldfire UDA based recording does not yet support anything other |
264 | * than 44.1kHz sampling rate, so we limit it to that case here now. SPDIF | ||
265 | * based recording will overwrite this value with the proper sample rate in | ||
266 | * audio_record(), and will not be affected by this. | ||
267 | */ | ||
268 | frequency = 44100; | ||
215 | pre_record_ticks = prerecord_time * HZ; | 269 | pre_record_ticks = prerecord_time * HZ; |
216 | pre_record_chunks = ((44100 * prerecord_time * 4)/CHUNK_SIZE)+1; | 270 | pre_record_chunks = ((frequency * prerecord_time * 4)/CHUNK_SIZE)+1; |
217 | if(pre_record_chunks >= (num_chunks-250)) | 271 | if(pre_record_chunks >= (num_chunks-250)) |
218 | { | 272 | { |
219 | /* we can't prerecord more than our buffersize minus treshold to write to disk! */ | 273 | /* we can't prerecord more than our buffersize minus treshold to write to disk! */ |
220 | pre_record_chunks = num_chunks-250; | 274 | pre_record_chunks = num_chunks-250; |
221 | /* don't forget to recalculate that time! */ | 275 | /* don't forget to recalculate that time! */ |
222 | pre_record_ticks = ((pre_record_chunks * CHUNK_SIZE)/(4*44100)) * HZ; | 276 | pre_record_ticks = ((pre_record_chunks * CHUNK_SIZE)/(4*frequency)) * HZ; |
223 | } | 277 | } |
224 | 278 | ||
225 | //logf("pcmrec: src=%d", source); | 279 | //logf("pcmrec: src=%d", source); |
226 | 280 | ||
281 | rec_source = source; | ||
282 | #ifdef HAVE_SPDIF_POWER | ||
283 | /* Check if S/PDIF output power should be switched off or on. NOTE: assumes | ||
284 | both optical in and out is controlled by the same power source, which is | ||
285 | the case on H1x0. */ | ||
286 | spdif_power_enable((source == 2) || spdif_power_setting); | ||
287 | #endif | ||
227 | switch (source) | 288 | switch (source) |
228 | { | 289 | { |
229 | /* mic */ | 290 | /* mic */ |
230 | case 0: | 291 | case 0: |
292 | /* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */ | ||
293 | DATAINCONTROL = 0xc020; | ||
231 | uda1380_enable_recording(true); | 294 | uda1380_enable_recording(true); |
232 | break; | 295 | break; |
233 | 296 | ||
234 | /* line-in */ | 297 | /* line-in */ |
235 | case 1: | 298 | case 1: |
299 | /* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */ | ||
300 | DATAINCONTROL = 0xc020; | ||
236 | uda1380_enable_recording(false); | 301 | uda1380_enable_recording(false); |
237 | break; | 302 | break; |
303 | #ifdef HAVE_SPDIF_IN | ||
304 | /* SPDIF */ | ||
305 | case 2: | ||
306 | /* Int. when 6 samples in FIFO. PDIR2 source = ebu1RcvData */ | ||
307 | DATAINCONTROL = 0xc038; | ||
308 | EBU1CONFIG = 0; /* Normal operation, source is EBU in 1 */ | ||
309 | /* We can't use the EBU clock to drive the IIS interface, so we | ||
310 | * need to use the clock the UDA provides, which is 44.1kHz as of | ||
311 | * now. This is the reason S/PDIF monitoring distorts for all other | ||
312 | * sample rates. Enable record to enable clock gen. | ||
313 | */ | ||
314 | uda1380_enable_recording(true); | ||
315 | break; | ||
316 | #endif | ||
238 | } | 317 | } |
239 | 318 | ||
319 | sample_rate = frequency; | ||
320 | |||
321 | #ifdef HAVE_SPDIF_IN | ||
322 | /* Turn on UDA based monitoring when UDA is used as input. */ | ||
323 | if (source == 2) { | ||
324 | uda1380_set_monitor(false); | ||
325 | IIS2CONFIG = 0x800; /* Reset before reprogram */ | ||
326 | /* SCLK follow IIS1 (UDA clock), TXSRC = EBU1rcv, 64 bclk/wclk */ | ||
327 | IIS2CONFIG = (8 << 12) | (7 << 8) | (4 << 2); | ||
328 | } | ||
329 | else | ||
330 | { | ||
331 | uda1380_set_monitor(true); | ||
332 | IIS2CONFIG = 0x800; /* Stop the S/PDIF monitoring if it's active */ | ||
333 | } | ||
334 | #else | ||
240 | uda1380_set_monitor(true); | 335 | uda1380_set_monitor(true); |
336 | #endif | ||
241 | } | 337 | } |
242 | 338 | ||
243 | 339 | ||
@@ -271,6 +367,11 @@ void audio_record(const char *filename) | |||
271 | strncpy(recording_filename, filename, MAX_PATH - 1); | 367 | strncpy(recording_filename, filename, MAX_PATH - 1); |
272 | recording_filename[MAX_PATH - 1] = 0; | 368 | recording_filename[MAX_PATH - 1] = 0; |
273 | 369 | ||
370 | #ifdef HAVE_SPDIF_IN | ||
371 | if (rec_source == 2) | ||
372 | sample_rate = audio_get_spdif_sample_rate(); | ||
373 | #endif | ||
374 | |||
274 | record_done = false; | 375 | record_done = false; |
275 | queue_post(&pcmrec_queue, PCMREC_START, 0); | 376 | queue_post(&pcmrec_queue, PCMREC_START, 0); |
276 | 377 | ||
@@ -381,7 +482,7 @@ void pcm_rec_get_peaks(int *left, int *right) | |||
381 | * | 482 | * |
382 | */ | 483 | */ |
383 | 484 | ||
384 | static void pcmrec_callback(bool flush) __attribute__ ((section (".icode"))); | 485 | static void pcmrec_callback(bool flush) ICODE_ATTR; |
385 | static void pcmrec_callback(bool flush) | 486 | static void pcmrec_callback(bool flush) |
386 | { | 487 | { |
387 | int num_ready, num_free, num_new; | 488 | int num_ready, num_free, num_new; |
@@ -493,6 +594,10 @@ static void pcmrec_dma_start(void) | |||
493 | /* Start the DMA transfer.. */ | 594 | /* Start the DMA transfer.. */ |
494 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; | 595 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; |
495 | 596 | ||
597 | #ifdef HAVE_SPDIF_IN | ||
598 | INTERRUPTCLEAR = 0x03c00000; | ||
599 | #endif | ||
600 | |||
496 | /* pre-recording: buffer count */ | 601 | /* pre-recording: buffer count */ |
497 | buffered_chunks = 0; | 602 | buffered_chunks = 0; |
498 | 603 | ||
@@ -512,14 +617,36 @@ void DMA1(void) | |||
512 | { | 617 | { |
513 | DCR1 = 0; /* Stop DMA transfer */ | 618 | DCR1 = 0; /* Stop DMA transfer */ |
514 | error_count++; | 619 | error_count++; |
515 | is_recording = false; | ||
516 | 620 | ||
517 | logf("dma1 err: 0x%x", res); | 621 | logf("dma1 err: 0x%x", res); |
518 | |||
519 | /* Flush recorded data to disk and stop recording */ | ||
520 | queue_post(&pcmrec_queue, PCMREC_STOP, NULL); | ||
521 | 622 | ||
522 | } else | 623 | DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ |
624 | BCR1 = CHUNK_SIZE; | ||
625 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; | ||
626 | } | ||
627 | #ifdef HAVE_SPDIF_IN | ||
628 | else if ((rec_source == 2) && (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */ | ||
629 | { | ||
630 | INTERRUPTCLEAR = 0x03c00000; | ||
631 | error_count++; | ||
632 | |||
633 | logf("spdif err"); | ||
634 | |||
635 | if (is_stopping) | ||
636 | { | ||
637 | DCR1 = 0; /* Stop DMA transfer */ | ||
638 | is_stopping = false; | ||
639 | |||
640 | logf("dma1 stopping"); | ||
641 | } | ||
642 | else | ||
643 | { | ||
644 | DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ | ||
645 | BCR1 = CHUNK_SIZE; | ||
646 | } | ||
647 | } | ||
648 | #endif | ||
649 | else | ||
523 | { | 650 | { |
524 | write_index++; | 651 | write_index++; |
525 | if (write_index >= num_chunks) | 652 | if (write_index >= num_chunks) |
@@ -535,15 +662,16 @@ void DMA1(void) | |||
535 | is_stopping = false; | 662 | is_stopping = false; |
536 | 663 | ||
537 | logf("dma1 stopping"); | 664 | logf("dma1 stopping"); |
538 | 665 | } | |
539 | } else if (write_index == read_index) | 666 | else if (write_index == read_index) |
540 | { | 667 | { |
541 | DCR1 = 0; /* Stop DMA transfer */ | 668 | DCR1 = 0; /* Stop DMA transfer */ |
542 | is_recording = false; | 669 | is_recording = false; |
543 | 670 | ||
544 | logf("dma1 overrun"); | 671 | logf("dma1 overrun"); |
545 | 672 | ||
546 | } else | 673 | } |
674 | else | ||
547 | { | 675 | { |
548 | DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ | 676 | DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ |
549 | BCR1 = CHUNK_SIZE; | 677 | BCR1 = CHUNK_SIZE; |
@@ -560,10 +688,11 @@ static int start_wave(void) | |||
560 | unsigned char header[44] = | 688 | unsigned char header[44] = |
561 | { | 689 | { |
562 | 'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ', | 690 | 'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ', |
563 | 0x10,0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0, | 691 | 0x10,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0, |
564 | 4,0,0x10,0,'d','a','t','a',0,0,0,0 | 692 | 4,0,0x10,0,'d','a','t','a',0,0,0,0 |
565 | }; | 693 | }; |
566 | 694 | unsigned long avg_bytes_per_sec; | |
695 | |||
567 | wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC); | 696 | wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC); |
568 | if (wav_file < 0) | 697 | if (wav_file < 0) |
569 | { | 698 | { |
@@ -572,7 +701,18 @@ static int start_wave(void) | |||
572 | is_error = true; | 701 | is_error = true; |
573 | return -1; | 702 | return -1; |
574 | } | 703 | } |
575 | 704 | /* Now set the sample rate field of the WAV header to what it should be */ | |
705 | header[24] = (unsigned char)(sample_rate & 0xff); | ||
706 | header[25] = (unsigned char)(sample_rate >> 8); | ||
707 | header[26] = (unsigned char)(sample_rate >> 16); | ||
708 | header[27] = (unsigned char)(sample_rate >> 24); | ||
709 | /* And then the average bytes per second field */ | ||
710 | avg_bytes_per_sec = sample_rate*4; /* Hard coded to 16 bit stereo */ | ||
711 | header[28] = (unsigned char)(avg_bytes_per_sec & 0xff); | ||
712 | header[29] = (unsigned char)(avg_bytes_per_sec >> 8); | ||
713 | header[30] = (unsigned char)(avg_bytes_per_sec >> 16); | ||
714 | header[31] = (unsigned char)(avg_bytes_per_sec >> 24); | ||
715 | |||
576 | if (sizeof(header) != write(wav_file, header, sizeof(header))) | 716 | if (sizeof(header) != write(wav_file, header, sizeof(header))) |
577 | { | 717 | { |
578 | close(wav_file); | 718 | close(wav_file); |
@@ -634,7 +774,7 @@ static void pcmrec_start(void) | |||
634 | { | 774 | { |
635 | /* not enough good chunks available - limit pre-record time */ | 775 | /* not enough good chunks available - limit pre-record time */ |
636 | pre_chunks = buffered_chunks; | 776 | pre_chunks = buffered_chunks; |
637 | pre_ticks = ((buffered_chunks * CHUNK_SIZE)/(4*44100)) * HZ; | 777 | pre_ticks = ((buffered_chunks * CHUNK_SIZE)/(4*sample_rate)) * HZ; |
638 | } | 778 | } |
639 | record_start_time = current_tick - pre_ticks; | 779 | record_start_time = current_tick - pre_ticks; |
640 | 780 | ||
@@ -792,7 +932,6 @@ static void pcmrec_resume(void) | |||
792 | logf("pcmrec_resume done"); | 932 | logf("pcmrec_resume done"); |
793 | } | 933 | } |
794 | 934 | ||
795 | |||
796 | /** | 935 | /** |
797 | * audio_init_recording calls this function using PCMREC_INIT | 936 | * audio_init_recording calls this function using PCMREC_INIT |
798 | * | 937 | * |
@@ -834,7 +973,6 @@ static void pcmrec_init(void) | |||
834 | IIS1CONFIG = 0x800; /* Stop any playback */ | 973 | IIS1CONFIG = 0x800; /* Stop any playback */ |
835 | AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */ | 974 | AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */ |
836 | DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */ | 975 | DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */ |
837 | DATAINCONTROL |= 0x20; /* PDIR2 source = IIS1recv */ | ||
838 | 976 | ||
839 | DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */ | 977 | DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */ |
840 | DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */ | 978 | DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */ |
@@ -842,6 +980,9 @@ static void pcmrec_init(void) | |||
842 | ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */ | 980 | ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */ |
843 | IMR &= ~(1<<15); /* bit 15 is DMA1 */ | 981 | IMR &= ~(1<<15); /* bit 15 is DMA1 */ |
844 | 982 | ||
983 | #ifdef HAVE_SPDIF_IN | ||
984 | PHASECONFIG = 0x34; /* Gain = 3*2^13, source = EBUIN */ | ||
985 | #endif | ||
845 | pcmrec_dma_start(); | 986 | pcmrec_dma_start(); |
846 | 987 | ||
847 | init_done = 1; | 988 | init_done = 1; |
@@ -851,10 +992,15 @@ static void pcmrec_close(void) | |||
851 | { | 992 | { |
852 | uda1380_disable_recording(); | 993 | uda1380_disable_recording(); |
853 | 994 | ||
995 | #ifdef HAVE_SPDIF_POWER | ||
996 | spdif_power_enable(spdif_power_setting); | ||
997 | #endif | ||
854 | DMAROUTE = (DMAROUTE & 0xffff00ff); | 998 | DMAROUTE = (DMAROUTE & 0xffff00ff); |
855 | ICR7 = 0x00; /* Disable interrupt */ | 999 | ICR7 = 0x00; /* Disable interrupt */ |
856 | IMR |= (1<<15); /* bit 15 is DMA1 */ | 1000 | IMR |= (1<<15); /* bit 15 is DMA1 */ |
857 | 1001 | ||
1002 | /* Reset PDIR2 data flow */ | ||
1003 | DATAINCONTROL = 0x200; | ||
858 | close_done = true; | 1004 | close_done = true; |
859 | } | 1005 | } |
860 | 1006 | ||