Index: firmware/export/as3525.h =================================================================== --- firmware/export/as3525.h (revision 31155) +++ firmware/export/as3525.h (working copy) @@ -526,7 +526,6 @@ /* PCM addresses for obtaining buffers will be what DMA is using (physical) */ #define HAVE_PCM_DMA_ADDRESS -#define HAVE_PCM_REC_DMA_ADDRESS /* Timer frequency */ #define TIMER_FREQ (24000000 / 16) Index: firmware/export/config/sansaclipzip.h =================================================================== --- firmware/export/config/sansaclipzip.h (revision 31155) +++ firmware/export/config/sansaclipzip.h (working copy) @@ -21,8 +21,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/export/config/sansafuzev2.h =================================================================== --- firmware/export/config/sansafuzev2.h (revision 31155) +++ firmware/export/config/sansafuzev2.h (working copy) @@ -12,8 +12,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Default recording levels */ #define DEFAULT_REC_MIC_GAIN 23 #define DEFAULT_REC_LEFT_GAIN 23 Index: firmware/export/config/sansaclip.h =================================================================== --- firmware/export/config/sansaclip.h (revision 31155) +++ firmware/export/config/sansaclip.h (working copy) @@ -14,8 +14,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/export/config/sansafuze.h =================================================================== --- firmware/export/config/sansafuze.h (revision 31155) +++ firmware/export/config/sansafuze.h (working copy) @@ -12,8 +12,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Default recording levels */ #define DEFAULT_REC_MIC_GAIN 23 #define DEFAULT_REC_LEFT_GAIN 23 Index: firmware/export/config/sansac200v2.h =================================================================== --- firmware/export/config/sansac200v2.h (revision 31155) +++ firmware/export/config/sansac200v2.h (working copy) @@ -14,8 +14,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/export/config/sansae200v2.h =================================================================== --- firmware/export/config/sansae200v2.h (revision 31155) +++ firmware/export/config/sansae200v2.h (working copy) @@ -12,8 +12,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/export/config/sansam200v4.h =================================================================== --- firmware/export/config/sansam200v4.h (revision 31155) +++ firmware/export/config/sansam200v4.h (working copy) @@ -16,8 +16,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/export/config/sansaclipplus.h =================================================================== --- firmware/export/config/sansaclipplus.h (revision 31155) +++ firmware/export/config/sansaclipplus.h (working copy) @@ -21,8 +21,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/export/config/sansaclipv2.h =================================================================== --- firmware/export/config/sansaclipv2.h (revision 31155) +++ firmware/export/config/sansaclipv2.h (working copy) @@ -14,8 +14,15 @@ /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ + SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ + SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) Index: firmware/target/arm/as3525/pcm-as3525.c =================================================================== --- firmware/target/arm/as3525/pcm-as3525.c (revision 31155) +++ firmware/target/arm/as3525/pcm-as3525.c (working copy) @@ -122,7 +122,7 @@ dma_rem_size = size; bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - CGU_AUDIO |= (1<<11); + bitset32(&CGU_AUDIO, (1<<11)); dma_retain(); @@ -146,9 +146,6 @@ dma_release(); - bitclr32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - CGU_AUDIO &= ~(1<<11); - play_callback_pending = false; } @@ -209,14 +206,12 @@ void pcm_dma_apply_settings(void) { - int cgu_audio = CGU_AUDIO; /* read register */ - cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */ - cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */ - cgu_audio &= ~(0x1ff << 2); /* clear i2sout divider */ - cgu_audio |= mclk_divider() << 2; /* set new i2sout divider */ - cgu_audio &= ~(1 << 23); /* clear I2SI_MCLK_EN */ - cgu_audio &= ~(1 << 24); /* clear I2SI_MCLK2PAD_EN */ - CGU_AUDIO = cgu_audio; /* write back register */ + bitmod32(&CGU_AUDIO, + (AS3525_MCLK_SEL << 0) | /* set i2sout MCLK_SEL */ + (mclk_divider() << 2) | /* set new i2sout divider */ + (0 << 23) | /* clear I2SI_MCLK_EN */ + (0 << 24), /* clear I2SI_MCLK2PAD_EN */ + (3 << 0) | (0x1ff << 2) | (1 << 23) | (1 << 24)); } size_t pcm_get_bytes_waiting(void) @@ -259,219 +254,126 @@ static int rec_locked = 0; static bool is_recording = false; -static bool rec_callback_pending = false; -static void *rec_dma_start_addr; -static size_t rec_dma_size, rec_dma_transfer_size; -static void rec_dma_callback(void); -#if CONFIG_CPU == AS3525 -/* points to the samples which need to be duplicated into the right channel */ -static int16_t *mono_samples; -#endif +static uint32_t *rec_dma_addr; +static size_t rec_dma_size; - void pcm_rec_lock(void) { - ++rec_locked; -} + int oldlevel = disable_irq_save(); - -void pcm_rec_unlock(void) -{ - if(--rec_locked == 0 && is_recording) + if (++rec_locked == 1) { - int old = disable_irq_save(); - if(rec_callback_pending) - { - rec_callback_pending = false; - rec_dma_callback(); - } - restore_irq(old); + VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; + I2SIN_MASK = 0; } -} - -static void rec_dma_start(void) -{ - rec_dma_transfer_size = rec_dma_size; - - /* We are limited to 8188 DMA transfers, and the recording core asks for - * 8192 bytes. Avoid splitting 8192 bytes transfers in 8188 + 4 */ - if(rec_dma_transfer_size > 4096) - rec_dma_transfer_size = 4096; - - dma_enable_channel(1, (void*)I2SIN_DATA, rec_dma_start_addr, DMA_PERI_I2SIN, - DMAC_FLOWCTRL_DMAC_PERI_TO_MEM, false, true, - rec_dma_transfer_size >> 2, DMA_S4, rec_dma_callback); + restore_irq(oldlevel); } -#if CONFIG_CPU == AS3525 -/* if needed, duplicate samples of the working channel until the given bound */ -static inline void mono2stereo(int16_t *end) +void pcm_rec_unlock(void) { - if(audio_channels != 1) /* only for microphone */ - return; -#if 0 - /* load pointer in a register and avoid updating it in each loop */ - register int16_t *samples = mono_samples; + int oldlevel = disable_irq_save(); - do { - int16_t left = *samples++; // load 1 sample of the left-channel - *samples++ = left; // copy it in the right-channel - } while(samples != end); + if (--rec_locked == 0 && is_recording) + { + VIC_INT_ENABLE = INTERRUPT_I2SIN; + I2SIN_MASK = (1<<2); /* I2SIN_MASK_POAF */ + } - mono_samples = samples; /* update pointer */ -#else - /* gcc doesn't use pre indexing : let's save 1 cycle */ - int16_t left; - asm ( - "1: ldrh %0, [%1], #2 \n" // load 1 sample of the left-channel - " strh %0, [%1], #2 \n" // copy it in the right-channel - " cmp %1, %2 \n" // are we finished? - " bne 1b \n" - : "=&r"(left), "+r"(mono_samples) - : "r"(end) - : "memory" - ); -#endif /* C / ASM */ + restore_irq(oldlevel); } -#endif /* CONFIG_CPU == AS3525 */ -#if CONFIG_CPU == AS3525v2 -/* scale microphone audio by 2 bits due to 14 bit ADC */ -static inline void scalevolume(int16_t *end, int size) -{ - if(audio_channels != 1) /* only for microphone */ - return; - /* load pointer in a register and avoid updating it in each loop */ - register int16_t *samples = end; - - do { - *samples++ <<=2; - - } while(samples != end+size); - -} -#endif /* CONFIG_CPU == AS3525v2 */ - -static void rec_dma_callback(void) +void INT_I2SIN(void) { - if(rec_dma_transfer_size) +#if CONFIG_CPU == AS3525 + if (audio_channels == 1) { + /* RX is left channel mono */ + while (rec_dma_size > 0) + { + if (I2SIN_RAW_STATUS & (1<<5)) + return; /* empty */ -#if CONFIG_CPU == AS3525v2 - scalevolume(AS3525_UNCACHED_ADDR((int16_t*)rec_dma_start_addr), rec_dma_transfer_size); -#endif - rec_dma_size -= rec_dma_transfer_size; - rec_dma_start_addr += rec_dma_transfer_size; + /* Discard every other sample since ADC clock is 1/2 LRCK */ + uint32_t value = *I2SIN_DATA; + *I2SIN_DATA; - /* don't act like we just transferred data when we are called from - * pcm_rec_unlock() */ - rec_dma_transfer_size = 0; + /* Data is in left channel only - copy to right channel + 14-bit => 16-bit samples */ + value = (uint16_t)(value << 2) | (value << 18); -#if CONFIG_CPU == AS3525 - /* the 2nd channel is silent when recording microphone on as3525v1 */ - mono2stereo(AS3525_UNCACHED_ADDR((int16_t*)rec_dma_start_addr)); -#endif - - if(locked) - { - rec_callback_pending = is_recording; - return; + *rec_dma_addr++ = value; + rec_dma_size -= 4; } } - - if(!rec_dma_size) + else +#endif /* AS3525 */ { - pcm_rec_more_ready_callback(0, &rec_dma_start_addr, - &rec_dma_size); + /* RX is stereo */ + while (rec_dma_size > 0) + { + if (I2SIN_RAW_STATUS & (1<<5)) + return; /* empty */ - if(rec_dma_size == 0) - return; + /* Discard every other sample since ADC clock is 1/2 LRCK */ + uint32_t value = *I2SIN_DATA; + *I2SIN_DATA; - dump_dcache_range(rec_dma_start_addr, rec_dma_size); -#if CONFIG_CPU == AS3525 - mono_samples = AS3525_UNCACHED_ADDR((int16_t*)rec_dma_start_addr); -#endif + /* 14-bit => 16-bit samples */ + *rec_dma_addr++ = (value << 2) & ~0x00030000; + rec_dma_size -= 4; + } } - rec_dma_start(); + pcm_rec_more_ready_callback(0, (void *)&rec_dma_addr, &rec_dma_size); } + void pcm_rec_dma_stop(void) { is_recording = false; - dma_disable_channel(1); - dma_release(); + + rec_dma_addr = NULL; rec_dma_size = 0; - I2SIN_CONTROL &= ~(1<<11); /* disable dma */ - - CGU_AUDIO &= ~(1<<11); - bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE | - CGU_I2SOUT_APB_CLOCK_ENABLE); - - rec_callback_pending = false; + bitclr32(&CGU_AUDIO, (1<<23)); + bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE); } void pcm_rec_dma_start(void *addr, size_t size) { - dump_dcache_range(addr, size); - rec_dma_start_addr = addr; -#if CONFIG_CPU == AS3525 - mono_samples = AS3525_UNCACHED_ADDR(addr); -#endif + rec_dma_addr = addr; rec_dma_size = size; - dma_retain(); + bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE); + bitset32(&CGU_AUDIO, (1<<23)); - bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE | - CGU_I2SOUT_APB_CLOCK_ENABLE); - CGU_AUDIO |= (1<<11); + I2SIN_CONTROL |= (1<<5); /* 14bits samples */ - I2SIN_CONTROL |= (1<<11)|(1<<5); /* enable dma, 14bits samples */ - + I2SIN_CLEAR = (1<<6) | (1<<0); /* push error, pop error */ is_recording = true; - - rec_dma_start(); } - void pcm_rec_dma_close(void) { + pcm_rec_dma_stop(); } - void pcm_rec_dma_init(void) { - /* i2c clk src = I2SOUTIF, sdata src = AFE, + /* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE, * data valid at positive edge of SCLK */ - I2SIN_CONTROL = (1<<2); + I2SIN_CONTROL = (1<<5) | (1<<2); I2SIN_MASK = 0; /* disables all interrupts */ } const void * pcm_rec_dma_get_peak_buffer(void) { -#if CONFIG_CPU == AS3525 - /* - * We need to prevent the DMA callback from kicking in while we are - * faking the right channel with data from left channel. - */ - - int old = disable_irq_save(); - int16_t *addr = AS3525_UNCACHED_ADDR((int16_t *)DMAC_CH_DST_ADDR(1)); - mono2stereo(addr); - restore_irq(old); - - return addr; - -#else - /* Microphone recording is stereo on as3525v2 */ - return AS3525_UNCACHED_ADDR((int16_t *)DMAC_CH_DST_ADDR(1)); -#endif + return rec_dma_addr; } #endif /* HAVE_RECORDING */ Index: firmware/target/arm/as3525/audio-as3525.c =================================================================== --- firmware/target/arm/as3525/audio-as3525.c (revision 31155) +++ firmware/target/arm/as3525/audio-as3525.c (working copy) @@ -24,6 +24,7 @@ #include "audio.h" #include "audiohw.h" #include "sound.h" +#include "general.h" int audio_channels = 2; @@ -109,3 +110,17 @@ last_source = source; } + +#ifdef HAVE_RECORDING +unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate) +{ + /* Check if the samplerate is in the list of recordable rates. + * Fail to default if not */ + int index = round_value_to_list32(samplerate, rec_freq_sampr, + REC_NUM_FREQ, false); + if (samplerate != rec_freq_sampr[index]) + return HW_SAMPR_DEFAULT; + + return samplerate * 2; /* Recording rates are 1/2 the codec clock */ +} +#endif