From 9b4057bbd43dfeaf69785605407e4625f01be069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?= Date: Sun, 1 Nov 2009 22:51:31 +0000 Subject: [PATCH] Sansa AMS recording support (Microphone and FM) Still disabled on all targets: - Fuze and e200v2 see spurious interrupts with no source defined - Clip/m200v4 deadlock instantly when starting recording (perhaps due to low memory size) Having the code in SVN will make working on this feature easier Also add keymaps for Fuze, and correct Frequency section of recording options : the 22.05kHz limitation of e200v1 and c200v1 doesn't apply to Sansa AMS (different I2S hardware, unrelated to as3514) Flyspray: FS#10371 Authors: Fred Bauer and myself git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23476 a1c6a512-1295-4272-9138-f99709370657 --- firmware/export/as3525.h | 11 ++ firmware/export/config-c200v2.h | 12 +- firmware/export/config-clip.h | 12 +- firmware/export/config-e200v2.h | 10 +- firmware/export/config-fuze.h | 10 +- firmware/export/config-m200v4.h | 12 +- firmware/target/arm/as3525/audio-as3525.c | 61 +++++++--- firmware/target/arm/as3525/pcm-as3525.c | 107 ++++++++++++++++-- firmware/target/arm/as3525/system-as3525.c | 4 + .../configure_rockbox/recording_settings.tex | 10 +- manual/platform/keymap-fuze.tex | 16 +-- 11 files changed, 186 insertions(+), 79 deletions(-) diff --git a/firmware/export/as3525.h b/firmware/export/as3525.h index d2b5a4fef2..07f78dafcf 100644 --- a/firmware/export/as3525.h +++ b/firmware/export/as3525.h @@ -507,4 +507,15 @@ interface */ #define USB_NUM_ENDPOINTS 4 #define USB_DEVBSS_ATTR IBSS_ATTR +/* I2SIN registers */ + +#define I2SIN_CONTROL (*(volatile unsigned long*)(I2SIN_BASE+0x00)) +#define I2SIN_MASK (*(volatile unsigned char*)(I2SIN_BASE+0x04)) +#define I2SIN_RAW_STATUS (*(volatile unsigned char*)(I2SIN_BASE+0x08)) +#define I2SIN_STATUS (*(volatile unsigned char*)(I2SIN_BASE+0x0C)) +#define I2SIN_CLEAR (*(volatile unsigned char*)(I2SIN_BASE+0x10)) +#define I2SIN_DATA (volatile unsigned long*)(I2SIN_BASE+0x14) +#define I2SIN_SPDIF_STATUS (*(volatile unsigned long*)(I2SIN_BASE+0x18)) + + #endif /*__AS3525_H__*/ diff --git a/firmware/export/config-c200v2.h b/firmware/export/config-c200v2.h index d6c2159e8c..2441fe01c5 100644 --- a/firmware/export/config-c200v2.h +++ b/firmware/export/config-c200v2.h @@ -9,18 +9,12 @@ #define FIRMWARE_OFFSET_FILE_DATA 8 #define FIRMWARE_OFFSET_FILE_CRC 0 -#if 0 - -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS SAMPR_CAP_ALL /* define this if you have recording possibility */ -#define HAVE_RECORDING +//#define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) -#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ -#define REC_SAMPR_DEFAULT SAMPR_22 - -#endif +#define REC_SAMPR_CAPS SAMPR_CAP_ALL /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ diff --git a/firmware/export/config-clip.h b/firmware/export/config-clip.h index 3b82a25159..b57881f794 100644 --- a/firmware/export/config-clip.h +++ b/firmware/export/config-clip.h @@ -9,18 +9,12 @@ #define FIRMWARE_OFFSET_FILE_DATA 8 #define FIRMWARE_OFFSET_FILE_CRC 0 -#if 0 /* disabled since there is no driver (yet) */ - -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS SAMPR_CAP_ALL /* define this if you have recording possibility */ -#define HAVE_RECORDING +//#define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) -#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ -#define REC_SAMPR_DEFAULT SAMPR_22 - -#endif +#define REC_SAMPR_CAPS SAMPR_CAP_ALL /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ diff --git a/firmware/export/config-e200v2.h b/firmware/export/config-e200v2.h index b108decce6..4dc3e43f2d 100644 --- a/firmware/export/config-e200v2.h +++ b/firmware/export/config-e200v2.h @@ -7,17 +7,13 @@ #define MODEL_NUMBER 41 #define MODEL_NAME "Sandisk Sansa e200v2 series" -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS SAMPR_CAP_ALL -#if 0 /* define this if you have recording possibility */ -#define HAVE_RECORDING +//#define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) -#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ -#define REC_SAMPR_DEFAULT SAMPR_22 +#define REC_SAMPR_CAPS SAMPR_CAP_ALL -#endif /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) diff --git a/firmware/export/config-fuze.h b/firmware/export/config-fuze.h index def343c680..2a438ec91b 100644 --- a/firmware/export/config-fuze.h +++ b/firmware/export/config-fuze.h @@ -7,15 +7,12 @@ #define MODEL_NUMBER 43 #define MODEL_NAME "Sandisk Sansa Fuze" -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS SAMPR_CAP_ALL -#if 0 /* define this if you have recording possibility */ -#define HAVE_RECORDING +//#define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) -#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ -#define REC_SAMPR_DEFAULT SAMPR_22 +#define REC_SAMPR_CAPS SAMPR_CAP_ALL /* Default recording levels */ #define DEFAULT_REC_MIC_GAIN 23 @@ -24,7 +21,6 @@ /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ -#endif #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) /* define this if you have a bitmap LCD display */ diff --git a/firmware/export/config-m200v4.h b/firmware/export/config-m200v4.h index 21038349fd..b734f3e368 100644 --- a/firmware/export/config-m200v4.h +++ b/firmware/export/config-m200v4.h @@ -11,18 +11,12 @@ /* Enable FAT16 support */ #define HAVE_FAT16SUPPORT -#if 0 /* disabled since there is no driver (yet) */ - -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS SAMPR_CAP_ALL /* define this if you have recording possibility */ -#define HAVE_RECORDING +//#define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) -#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ -#define REC_SAMPR_DEFAULT SAMPR_22 - -#endif +#define REC_SAMPR_CAPS SAMPR_CAP_ALL /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c index 410fdfad79..b616153eae 100644 --- a/firmware/target/arm/as3525/audio-as3525.c +++ b/firmware/target/arm/as3525/audio-as3525.c @@ -25,44 +25,71 @@ #include "audiohw.h" #include "sound.h" -int audio_channels = 2; -int audio_output_source = AUDIO_SRC_PLAYBACK; - void audio_set_output_source(int source) { - if ((unsigned)source >= AUDIO_NUM_SOURCES) - source = AUDIO_SRC_PLAYBACK; - - audio_output_source = source; -} /* audio_set_output_source */ + (void)source; +} void audio_input_mux(int source, unsigned flags) { static int last_source = AUDIO_SRC_PLAYBACK; - - (void)flags; +#ifdef HAVE_RECORDING + static bool last_recording = false; + const bool recording = flags & SRCF_RECORDING; +#else + (void) flags; +#endif switch (source) { default: /* playback - no recording */ source = AUDIO_SRC_PLAYBACK; case AUDIO_SRC_PLAYBACK: - audio_channels = 2; if (source != last_source) { audiohw_set_monitor(false); +#ifdef HAVE_RECORDING + audiohw_disable_recording(); +#endif } break; +#ifdef HAVE_RECORDING + case AUDIO_SRC_MIC: /* recording only */ + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_enable_recording(true); /* source mic */ + } + break; +#endif + case AUDIO_SRC_FMRADIO: /* recording and playback */ - audio_channels = 2; - if (source == last_source) + if (source == last_source +#ifdef HAVE_RECORDING + && recording == last_recording +#endif + ) break; - audiohw_set_monitor(true); +#ifdef HAVE_RECORDING + last_recording = recording; + + if (recording) + { + audiohw_set_monitor(false); + audiohw_enable_recording(false); + } + else +#endif + { +#ifdef HAVE_RECORDING + audiohw_disable_recording(); +#endif + audiohw_set_monitor(true); /* line 2 analog audio path */ + } break; - } /* end switch */ + } last_source = source; -} /* audio_input_mux */ - +} diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index 20579806c0..f648908474 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c @@ -69,6 +69,10 @@ static void play_start_pcm(void) CGU_PERI |= CGU_I2SOUT_APB_CLOCK_ENABLE; CGU_AUDIO |= (1<<11); +#ifdef HAVE_RECORDING + CGU_PERI &= ~CGU_I2SIN_APB_CLOCK_ENABLE; + CGU_AUDIO &= ~(1<<23); +#endif clean_dcache_range((void*)addr, size); /* force write back */ dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT, @@ -130,9 +134,7 @@ void pcm_play_dma_init(void) /* clock source PLLA, minimal frequency */ CGU_AUDIO |= (511<<2) | (1<<0); - I2SOUT_CONTROL |= (1<<6) ; /* enable dma */ - I2SOUT_CONTROL |= (1<<3) ; /* stereo */ - I2SOUT_CONTROL &= ~(1<<2); /* 16 bit samples */ + I2SOUT_CONTROL = (1<<6)|(1<<3) /* enable dma, stereo */; audiohw_preinit(); } @@ -153,7 +155,7 @@ void pcm_dma_apply_settings(void) int cgu_audio = CGU_AUDIO; /* read register */ cgu_audio &= ~(511 << 2); /* clear i2sout divider */ cgu_audio |= divider << 2; /* set new i2sout divider */ -#if 0 +#ifdef HAVE_RECORDING cgu_audio &= ~(511 << 14); /* clear i2sin divider */ cgu_audio |= divider << 14; /* set new i2sin divider */ #endif @@ -185,43 +187,130 @@ void * pcm_dma_addr(void *addr) ** Recording DMA transfer **/ #ifdef HAVE_RECORDING + +static int rec_locked = 0; +static unsigned int *rec_start_addr; +static size_t rec_size; + + void pcm_rec_lock(void) { + if(++rec_locked == 1) + VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; } + void pcm_rec_unlock(void) { + if(--rec_locked == 0) + VIC_INT_ENABLE |= INTERRUPT_I2SIN; } + void pcm_record_more(void *start, size_t size) { - (void)start; - (void)size; + rec_start_addr = start; + rec_size = size; } + void pcm_rec_dma_stop(void) { + VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; + + I2SOUT_CONTROL &= ~(1<<5); /* source = i2soutif fifo */ + CGU_AUDIO &= ~((1<<23)|(1<<11)); + CGU_PERI &= ~(CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE); } + +void INT_I2SIN(void) +{ + register int status; + register pcm_more_callback_type2 more_ready; + + status = I2SIN_STATUS; + +#if 0 /* FIXME */ + if ( status & ((1<<6)|(1<<0)) ) /* errors */ + panicf("i2sin error: 0x%x = %s %s", status, + (status & (1<<6)) ? "push" : "", + (status & (1<<0)) ? "pop" : "" + ); +#endif + + while (((I2SIN_RAW_STATUS & (1<<5)) == 0) && rec_size) + { + /* 14 bits per sample = 1 32 bits word */ + *rec_start_addr++ = *I2SIN_DATA; + rec_size -= 4; + } + + I2SIN_CLEAR = status; + + if(!rec_size) + { + more_ready = pcm_callback_more_ready; + if(!more_ready || more_ready(0) < 0) + { + /* Finished recording */ + pcm_rec_dma_stop(); + pcm_rec_dma_stopped_callback(); + } + } +} + + void pcm_rec_dma_start(void *addr, size_t size) { - (void)addr; - (void)size; + rec_start_addr = addr; + rec_size = size; + + if((unsigned int)addr & 3) + panicf("unaligned pointer!"); + + CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE; + CGU_AUDIO |= ((1<<23)|(1<<11)); + + I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ + + /* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE, + * data valid at positive edge of SCLK */ + I2SIN_CONTROL = (1<<5) | (1<<2); + + unsigned long tmp; + while ( ( I2SIN_RAW_STATUS & ( 1<<5 ) ) == 0 ) + tmp = *I2SIN_DATA; /* FLUSH FIFO */ + I2SIN_CLEAR = (1<<6)|(1<<0); /* push error, pop error */ + I2SIN_MASK = (1<<6) | (1<<0) | + (1<<3) | (1<<2) | (1<<1); /* half full, almost full, full */ + + VIC_INT_ENABLE |= INTERRUPT_I2SIN; } + void pcm_rec_dma_close(void) { + pcm_rec_dma_stop(); } void pcm_rec_dma_init(void) { + pcm_dma_apply_settings(); } const void * pcm_rec_dma_get_peak_buffer(int *count) { - (void)count; + const void *peak_buffer; + + pcm_rec_lock(); + *count = rec_size >> 2; + peak_buffer = (const void*)rec_start_addr; + pcm_rec_unlock(); + + return peak_buffer; } #endif /* HAVE_RECORDING */ diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c index dc335dfaad..5f01ed2dee 100644 --- a/firmware/target/arm/as3525/system-as3525.c +++ b/firmware/target/arm/as3525/system-as3525.c @@ -20,6 +20,7 @@ * ****************************************************************************/ +#include "config.h" #include "kernel.h" #include "system.h" #include "panic.h" @@ -113,6 +114,9 @@ struct vec_int_src vec_int_srcs[] = { INT_SRC_MCI0, INT_MCI0 }, { INT_SRC_GPIOA, INT_GPIOA, }, { INT_SRC_GPIOB, INT_GPIOB, }, +#ifdef HAVE_RECORDING + { INT_SRC_I2SIN, INT_I2SIN, }, +#endif }; static void setup_vic(void) diff --git a/manual/configure_rockbox/recording_settings.tex b/manual/configure_rockbox/recording_settings.tex index 2c9847a7ae..036450accd 100644 --- a/manual/configure_rockbox/recording_settings.tex +++ b/manual/configure_rockbox/recording_settings.tex @@ -42,11 +42,13 @@ } \section{Frequency} - \nopt{sansa,sansaAMS}{ + \nopt{sansa}{ Choose the recording frequency (sample rate). \opt{masf}{48kHz, 44.1kHz, 32kHz, 24kHz, 22.05kHz, 16kHz} \opt{h100,h300}{44.1kHz, 22.05kHz and 11.025kHz} \opt{x5}{88.2kHz, 44.1kHz, 22.05kHz and 11.025kHz} + \opt{sansaAMS}{96kHz, 88.2kHz, 64kHz, 48kHz, 44.1kHz, 32kHz, 24kHz, % + 22.05kHz, 16kHz, 12kHz, 11.025kHz and 8kHz} are available. Higher sample rates use up more disk space, but give better sound quality. \opt{swcodec}{\note{The 11.025kHz setting is not available when using% @@ -61,11 +63,11 @@ \opt{recorder,recorderv2fm,h100} {\note{You cannot change the sample rate for digital recordings.} } - } % nopt e200 - \opt{e200,e200v2}{ + } % nopt sansa + \opt{sansa}{ Recordings can only be made at a 22.05kHz frequency (sample rate) on this \dap. - } % opt e200 + } % opt sansa \section{Source} Choose the source of the recording. The options are: diff --git a/manual/platform/keymap-fuze.tex b/manual/platform/keymap-fuze.tex index 351db621b0..46ddc8ba62 100644 --- a/manual/platform/keymap-fuze.tex +++ b/manual/platform/keymap-fuze.tex @@ -84,14 +84,14 @@ \newcommand{\ActionPsExit}{Long \ButtonHome{} or \ButtonUp} %Button actions, Recording screen context -%\newcommand{\ActionRecPause}{\ButtonUp} -%\newcommand{\ActionRecExit}{\ButtonDown} -%\newcommand{\ActionRecNewfile}{\ButtonHome} -%\newcommand{\ActionRecMenu}{Long \ButtonSelect} -%\newcommand{\ActionRecSettingsInc}{\ButtonRight} -%\newcommand{\ActionRecSettingsDec}{\ButtonLeft} -%\newcommand{\ActionRecPrev}{\ButtonScrollBack} -%\newcommand{\ActionRecNext}{\ButtonScrollFwd} +\newcommand{\ActionRecPause}{\ButtonUp} +\newcommand{\ActionRecExit}{\ButtonDown} +\newcommand{\ActionRecNewfile}{\ButtonHome} +\newcommand{\ActionRecMenu}{Long \ButtonSelect} +\newcommand{\ActionRecSettingsInc}{\ButtonRight} +\newcommand{\ActionRecSettingsDec}{\ButtonLeft} +\newcommand{\ActionRecPrev}{\ButtonScrollBack} +\newcommand{\ActionRecNext}{\ButtonScrollFwd} %Button actions, FM radio context \newcommand{\ActionFMPreset}{\ButtonSelect}