From e42a3194de3b4fb9cd3e7cbd2e0ff17fea804b72 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Thu, 8 Dec 2011 19:20:00 +0000 Subject: AS3525v1/v2: Fix problems with volume of recorded material by converting 14-bit samples to 16-bit. Remove duplicate samples from recorded data and support proper samplerate since ADC runs 1/2 the codec clock. Support monitoring mono on both output channels by feeding data manually to I2SOUT under the right conditions. DMA is no longer used for recording since frames must be processed as described above but it does allow full-duplex audio. Miscellaneous change includes a proper constant (HW_SAMPR_DEFAULT) to reset the hardware samplerate when recording is closed. PP5024 and AS3525 have different default recording rates (22kHz and 44kHz respectively) but both have half-speed ADC. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31180 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/pitch_detector.c | 2 +- apps/recorder/pcm_record.c | 2 +- firmware/export/as3525.h | 1 - firmware/export/config/sansac200v2.h | 9 +- firmware/export/config/sansaclip.h | 9 +- firmware/export/config/sansaclipplus.h | 9 +- firmware/export/config/sansaclipv2.h | 9 +- firmware/export/config/sansaclipzip.h | 9 +- firmware/export/config/sansae200v2.h | 9 +- firmware/export/config/sansafuze.h | 9 +- firmware/export/config/sansafuzev2.h | 9 +- firmware/export/config/sansam200v4.h | 9 +- firmware/export/pcm_sampr.h | 5 +- firmware/pcm.c | 9 +- firmware/target/arm/as3525/audio-as3525.c | 52 ++++- firmware/target/arm/as3525/pcm-as3525.c | 295 +++++++++++--------------- firmware/target/arm/sandisk/audio-c200_e200.c | 31 ++- 17 files changed, 269 insertions(+), 209 deletions(-) diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c index ec208268ab..c30d48a025 100644 --- a/apps/plugins/pitch_detector.c +++ b/apps/plugins/pitch_detector.c @@ -1078,7 +1078,7 @@ static void record_and_get_pitch(void) } } rb->pcm_close_recording(); - rb->pcm_set_frequency(REC_SAMPR_DEFAULT | SAMPR_TYPE_REC); + rb->pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC); #ifdef HAVE_SCHEDULER_BOOSTCTRL rb->cancel_cpu_boost(); #endif diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c index e83db0f418..30b681ab45 100644 --- a/apps/recorder/pcm_record.c +++ b/apps/recorder/pcm_record.c @@ -290,7 +290,7 @@ static void pcm_rec_have_more(int status, void **start, size_t *size) static void reset_hardware(void) { /* reset pcm to defaults */ - pcm_set_frequency(REC_SAMPR_DEFAULT | SAMPR_TYPE_REC); + pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC); audio_set_output_source(AUDIO_SRC_PLAYBACK); pcm_apply_settings(); } diff --git a/firmware/export/as3525.h b/firmware/export/as3525.h index 87e3fc43d7..c5e01d5bc8 100644 --- a/firmware/export/as3525.h +++ b/firmware/export/as3525.h @@ -526,7 +526,6 @@ CE lines /* 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) diff --git a/firmware/export/config/sansac200v2.h b/firmware/export/config/sansac200v2.h index 4223d41b92..a5b857fe81 100644 --- a/firmware/export/config/sansac200v2.h +++ b/firmware/export/config/sansac200v2.h @@ -14,7 +14,14 @@ /* 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 */ diff --git a/firmware/export/config/sansaclip.h b/firmware/export/config/sansaclip.h index 7427d623fb..4e214669b1 100644 --- a/firmware/export/config/sansaclip.h +++ b/firmware/export/config/sansaclip.h @@ -14,7 +14,14 @@ /* 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 */ diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h index 8a0a0403f3..0ae67b70d0 100644 --- a/firmware/export/config/sansaclipplus.h +++ b/firmware/export/config/sansaclipplus.h @@ -21,7 +21,14 @@ /* 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 */ diff --git a/firmware/export/config/sansaclipv2.h b/firmware/export/config/sansaclipv2.h index a39fe3e12c..d7a50afa20 100644 --- a/firmware/export/config/sansaclipv2.h +++ b/firmware/export/config/sansaclipv2.h @@ -14,7 +14,14 @@ /* 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 */ diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h index 3df9ed57fd..e0f39afcf1 100644 --- a/firmware/export/config/sansaclipzip.h +++ b/firmware/export/config/sansaclipzip.h @@ -21,7 +21,14 @@ /* 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 */ diff --git a/firmware/export/config/sansae200v2.h b/firmware/export/config/sansae200v2.h index 34c2cd98ad..a03dfe05a6 100644 --- a/firmware/export/config/sansae200v2.h +++ b/firmware/export/config/sansae200v2.h @@ -12,7 +12,14 @@ /* 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 */ diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h index 8563664cb8..2e9c261a04 100644 --- a/firmware/export/config/sansafuze.h +++ b/firmware/export/config/sansafuze.h @@ -12,7 +12,14 @@ /* 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 diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h index 1c0f8a99dc..7465010166 100644 --- a/firmware/export/config/sansafuzev2.h +++ b/firmware/export/config/sansafuzev2.h @@ -12,7 +12,14 @@ /* 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 diff --git a/firmware/export/config/sansam200v4.h b/firmware/export/config/sansam200v4.h index 88679c1a78..52b10a4431 100644 --- a/firmware/export/config/sansam200v4.h +++ b/firmware/export/config/sansam200v4.h @@ -16,7 +16,14 @@ /* 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 */ diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h index 62bfd0068b..01a8ed428e 100644 --- a/firmware/export/pcm_sampr.h +++ b/firmware/export/pcm_sampr.h @@ -305,6 +305,8 @@ enum rec_freq_indexes #define REC_SAMPR_DEFAULT SAMPR_44 #endif +#define HW_SAMPR_RESET 0 + #define REC_FREQ_CFG_VAL_LIST &REC_HAVE_96_(",96") REC_HAVE_88_(",88") \ REC_HAVE_64_(",64") REC_HAVE_48_(",48") \ REC_HAVE_44_(",44") REC_HAVE_32_(",32") \ @@ -324,7 +326,8 @@ extern const unsigned long rec_freq_sampr[REC_NUM_FREQ]; #define SAMPR_TYPE_REC (0x01 << 24) #endif -unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate); +unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate, + unsigned int type); #else /* ndef CONFIG_SAMPR_TYPES */ diff --git a/firmware/pcm.c b/firmware/pcm.c index f5efb4f84e..d1a897dcab 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -393,21 +393,14 @@ void pcm_set_frequency(unsigned int samplerate) int index; #ifdef CONFIG_SAMPR_TYPES -#ifdef HAVE_RECORDING unsigned int type = samplerate & SAMPR_TYPE_MASK; -#endif samplerate &= ~SAMPR_TYPE_MASK; -#ifdef HAVE_RECORDING -#if SAMPR_TYPE_REC != 0 /* For now, supported targets have direct conversion when configured with * CONFIG_SAMPR_TYPES. * Some hypothetical target with independent rates would need slightly * different handling throughout this source. */ - if (type == SAMPR_TYPE_REC) - samplerate = pcm_sampr_type_rec_to_play(samplerate); -#endif -#endif /* HAVE_RECORDING */ + samplerate = pcm_sampr_to_hw_sampr(samplerate, type); #endif /* CONFIG_SAMPR_TYPES */ index = round_value_to_list32(samplerate, hw_freq_sampr, diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c index 10c6161881..e4bb39b406 100644 --- a/firmware/target/arm/as3525/audio-as3525.c +++ b/firmware/target/arm/as3525/audio-as3525.c @@ -24,16 +24,33 @@ #include "audio.h" #include "audiohw.h" #include "sound.h" +#include "general.h" int audio_channels = 2; +#if CONFIG_CPU == AS3525 +int audio_output_source = AUDIO_SRC_PLAYBACK; +#endif + void audio_set_output_source(int source) { bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - if (source == AUDIO_SRC_PLAYBACK) - I2SOUT_CONTROL &= ~(1<<5); + + if ((unsigned)source >= AUDIO_NUM_SOURCES) + source = AUDIO_SRC_PLAYBACK; + + bool loopback = source != AUDIO_SRC_PLAYBACK; + +#if CONFIG_CPU == AS3525 + loopback = loopback && audio_channels > 1; + + audio_output_source = source; +#endif + + if (loopback) + I2SOUT_CONTROL |= (1<<5); /* loopback from i2sin fifo */ else - I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ + I2SOUT_CONTROL &= ~(1<<5); /* normal i2sout */ } void audio_input_mux(int source, unsigned flags) @@ -108,4 +125,33 @@ void audio_input_mux(int source, unsigned flags) } last_source = source; + +#if CONFIG_CPU == AS3525 + /* Sync on behalf of change in number of channels */ + audio_set_output_source(audio_output_source); +#endif } + +#ifdef CONFIG_SAMPR_TYPES +unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate, + unsigned int type) +{ +#ifdef HAVE_RECORDING + if (samplerate != HW_SAMPR_RESET && type == SAMPR_TYPE_REC) + { + /* 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]) + samplerate = REC_SAMPR_DEFAULT; + + samplerate *= 2; /* Recording rates are 1/2 the codec clock */ + } +#endif /* HAVE_RECORDING */ + + return samplerate; + (void)type; +} +#endif /* CONFIG_SAMPR_TYPES */ + diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index f82b373ade..8b42bbd5b2 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c @@ -43,9 +43,14 @@ static size_t dma_rem_size; /* Remaining size - in 4*32 bits */ static size_t play_sub_size; /* size of current subtransfer */ static void dma_callback(void); static int locked = 0; -static bool is_playing = false; +static bool volatile is_playing = false; static bool play_callback_pending = false; +#ifdef HAVE_RECORDING +/* Stopping playback gates clock if not recording */ +static bool volatile is_recording = false; +#endif + /* Mask the DMA interrupt */ void pcm_play_lock(void) { @@ -116,26 +121,27 @@ static void dma_callback(void) void pcm_play_dma_start(const void *addr, size_t size) { + is_playing = true; + dma_start_addr = (void*)addr; dma_start_size = size; dma_sub_addr = dma_start_addr; dma_rem_size = size; - bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - CGU_AUDIO |= (1<<11); - dma_retain(); - is_playing = true; - /* force writeback */ clean_dcache_range(dma_start_addr, dma_start_size); + + bitset32(&CGU_AUDIO, (1<<11)); + play_start_pcm(); } void pcm_play_dma_stop(void) { is_playing = false; + dma_disable_channel(1); /* Ensure byte counts read back 0 */ @@ -146,8 +152,10 @@ void pcm_play_dma_stop(void) dma_release(); - bitclr32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - CGU_AUDIO &= ~(1<<11); +#ifdef HAVE_RECORDING + if (!is_recording) + bitclr32(&CGU_AUDIO, (1<<11)); +#endif play_callback_pending = false; } @@ -175,10 +183,10 @@ void pcm_play_dma_pause(bool pause) void pcm_play_dma_init(void) { bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - - I2SOUT_CONTROL = (1<<6)|(1<<3) /* enable dma, stereo */; + I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */ audiohw_preinit(); + pcm_dma_apply_settings(); } void pcm_play_dma_postinit(void) @@ -209,14 +217,15 @@ static inline unsigned char mclk_divider(void) 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, + (0<<24) | /* I2SI_MCLK2PAD_EN = disabled */ + (0<<23) | /* I2SI_MCLK_EN = disabled */ + (0<<14) | /* I2SI_MCLK_DIV_SEL = unused */ + (0<<12) | /* I2SI_MCLK_SEL = clk_main */ + /* I2SO_MCLK_EN = unchanged */ + (mclk_divider() << 2) | /* I2SO_MCLK_DIV_SEL */ + (AS3525_MCLK_SEL << 0), /* I2SO_MCLK_SEL */ + 0x01fff7ff); } size_t pcm_get_bytes_waiting(void) @@ -258,220 +267,158 @@ void * pcm_dma_addr(void *addr) #ifdef HAVE_RECORDING 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); + bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE); + VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; + I2SIN_MASK = 0; /* disables all interrupts */ } -} - -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) -{ - 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; - - do { - int16_t left = *samples++; // load 1 sample of the left-channel - *samples++ = left; // copy it in the right-channel - } while(samples != end); - - 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 */ -} -#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) +void pcm_rec_unlock(void) { - 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 */ + int oldlevel = disable_irq_save(); -static void rec_dma_callback(void) -{ - if(rec_dma_transfer_size) + if (--rec_locked == 0 && is_recording) { + VIC_INT_ENABLE = INTERRUPT_I2SIN; + I2SIN_MASK = (1<<2); /* I2SIN_MASK_POAF */ + } -#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; + restore_irq(oldlevel); +} - /* don't act like we just transferred data when we are called from - * pcm_rec_unlock() */ - rec_dma_transfer_size = 0; +void INT_I2SIN(void) +{ #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) + if (audio_channels == 1) + { + /* RX is left-channel-only mono */ + while (rec_dma_size > 0) { - rec_callback_pending = is_recording; - return; + if (I2SIN_RAW_STATUS & (1<<5)) + return; /* empty */ + + /* Discard every other sample since ADC clock is 1/2 LRCK */ + uint32_t value = *I2SIN_DATA; + *I2SIN_DATA; + + /* Data is in left channel only - copy to right channel + 14-bit => 16-bit samples */ + value = (uint16_t)(value << 2) | (value << 18); + + if (audio_output_source != AUDIO_SRC_PLAYBACK && !is_playing) + { + /* In this case, loopback is manual so that both output + channels have audio */ + if (I2SOUT_RAW_STATUS & (1<<5)) + { + /* Sync output fifo so it goes empty not before input is + filled */ + for (unsigned i = 0; i < 4; i++) + *I2SOUT_DATA = 0; + } + + *I2SOUT_DATA = value; + *I2SOUT_DATA = value; + } + + *rec_dma_addr++ = value; + rec_dma_size -= 4; } } - - if(!rec_dma_size) + else +#endif /* CONFIG_CPU == 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 + /* Loopback is in I2S hardware */ + + /* 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_size = 0; - I2SIN_CONTROL &= ~(1<<11); /* disable dma */ + VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; + I2SIN_MASK = 0; /* disables all interrupts */ + + rec_dma_addr = NULL; + rec_dma_size = 0; - CGU_AUDIO &= ~(1<<11); - bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE | - CGU_I2SOUT_APB_CLOCK_ENABLE); + if (!is_playing) + bitclr32(&CGU_AUDIO, (1<<11)); - rec_callback_pending = false; + 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_size = size; - - dma_retain(); + is_recording = true; - bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE | - CGU_I2SOUT_APB_CLOCK_ENABLE); - CGU_AUDIO |= (1<<11); + bitset32(&CGU_AUDIO, (1<<11)); - I2SIN_CONTROL |= (1<<11)|(1<<5); /* enable dma, 14bits samples */ + rec_dma_addr = addr; + rec_dma_size = size; - is_recording = true; + /* ensure empty FIFO */ + while (!(I2SIN_RAW_STATUS & (1<<5))) + *I2SIN_DATA; - rec_dma_start(); + I2SIN_CLEAR = (1<<6) | (1<<0); /* push error, pop error */ } void pcm_rec_dma_close(void) { + bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE); + pcm_rec_dma_stop(); } void pcm_rec_dma_init(void) { - /* i2c clk src = I2SOUTIF, sdata src = AFE, - * data valid at positive edge of SCLK */ - I2SIN_CONTROL = (1<<2); + bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE); + I2SIN_MASK = 0; /* disables all interrupts */ + + /* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE, + * data valid at positive edge of SCLK */ + I2SIN_CONTROL = (1<<5) | (1<<2); } 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 */ diff --git a/firmware/target/arm/sandisk/audio-c200_e200.c b/firmware/target/arm/sandisk/audio-c200_e200.c index 2f6bde1b98..4de7aa62c3 100644 --- a/firmware/target/arm/sandisk/audio-c200_e200.c +++ b/firmware/target/arm/sandisk/audio-c200_e200.c @@ -186,16 +186,25 @@ void audiohw_set_sampr_dividers(int fsel) IISDIV = (IISDIV & ~0xc000003f) | regvals[fsel].iisdiv; } -#ifdef HAVE_RECORDING -unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate) +#ifdef CONFIG_SAMPR_TYPES +unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate, + unsigned int type) { - /* 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 */ +#ifdef HAVE_RECORDING + if (samplerate != HW_SAMPR_RESET && type == SAMPR_TYPE_REC) + { + /* 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]) + samplerate = REC_SAMPR_DEFAULT; + + samplerate *= 2; /* Recording rates are 1/2 the codec clock */ + } +#endif /* HAVE_RECORDING */ + + return samplerate; + (void)type; } -#endif +#endif /* CONFIG_SAMPR_TYPES */ -- cgit v1.2.3