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
This commit is contained in:
Michael Sevakis 2011-12-08 19:20:00 +00:00
parent 2c7379757c
commit e42a3194de
17 changed files with 272 additions and 212 deletions

View file

@ -1078,7 +1078,7 @@ static void record_and_get_pitch(void)
} }
} }
rb->pcm_close_recording(); 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 #ifdef HAVE_SCHEDULER_BOOSTCTRL
rb->cancel_cpu_boost(); rb->cancel_cpu_boost();
#endif #endif

View file

@ -290,7 +290,7 @@ static void pcm_rec_have_more(int status, void **start, size_t *size)
static void reset_hardware(void) static void reset_hardware(void)
{ {
/* reset pcm to defaults */ /* 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); audio_set_output_source(AUDIO_SRC_PLAYBACK);
pcm_apply_settings(); pcm_apply_settings();
} }

View file

@ -526,7 +526,6 @@ CE lines
/* PCM addresses for obtaining buffers will be what DMA is using (physical) */ /* PCM addresses for obtaining buffers will be what DMA is using (physical) */
#define HAVE_PCM_DMA_ADDRESS #define HAVE_PCM_DMA_ADDRESS
#define HAVE_PCM_REC_DMA_ADDRESS
/* Timer frequency */ /* Timer frequency */
#define TIMER_FREQ (24000000 / 16) #define TIMER_FREQ (24000000 / 16)

View file

@ -14,7 +14,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -14,7 +14,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -21,7 +21,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -14,7 +14,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -21,7 +21,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -12,7 +12,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -12,7 +12,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 */ /* Default recording levels */
#define DEFAULT_REC_MIC_GAIN 23 #define DEFAULT_REC_MIC_GAIN 23

View file

@ -12,7 +12,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 */ /* Default recording levels */
#define DEFAULT_REC_MIC_GAIN 23 #define DEFAULT_REC_MIC_GAIN 23

View file

@ -16,7 +16,14 @@
/* define this if you have recording possibility */ /* define this if you have recording possibility */
#define HAVE_RECORDING #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 /* Define bitmask of input sources - recordable bitmask can be defined
explicitly if different */ explicitly if different */

View file

@ -305,6 +305,8 @@ enum rec_freq_indexes
#define REC_SAMPR_DEFAULT SAMPR_44 #define REC_SAMPR_DEFAULT SAMPR_44
#endif #endif
#define HW_SAMPR_RESET 0
#define REC_FREQ_CFG_VAL_LIST &REC_HAVE_96_(",96") REC_HAVE_88_(",88") \ #define REC_FREQ_CFG_VAL_LIST &REC_HAVE_96_(",96") REC_HAVE_88_(",88") \
REC_HAVE_64_(",64") REC_HAVE_48_(",48") \ REC_HAVE_64_(",64") REC_HAVE_48_(",48") \
REC_HAVE_44_(",44") REC_HAVE_32_(",32") \ 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) #define SAMPR_TYPE_REC (0x01 << 24)
#endif #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 */ #else /* ndef CONFIG_SAMPR_TYPES */

View file

@ -393,21 +393,14 @@ void pcm_set_frequency(unsigned int samplerate)
int index; int index;
#ifdef CONFIG_SAMPR_TYPES #ifdef CONFIG_SAMPR_TYPES
#ifdef HAVE_RECORDING
unsigned int type = samplerate & SAMPR_TYPE_MASK; unsigned int type = samplerate & SAMPR_TYPE_MASK;
#endif
samplerate &= ~SAMPR_TYPE_MASK; samplerate &= ~SAMPR_TYPE_MASK;
#ifdef HAVE_RECORDING
#if SAMPR_TYPE_REC != 0
/* For now, supported targets have direct conversion when configured with /* For now, supported targets have direct conversion when configured with
* CONFIG_SAMPR_TYPES. * CONFIG_SAMPR_TYPES.
* Some hypothetical target with independent rates would need slightly * Some hypothetical target with independent rates would need slightly
* different handling throughout this source. */ * different handling throughout this source. */
if (type == SAMPR_TYPE_REC) samplerate = pcm_sampr_to_hw_sampr(samplerate, type);
samplerate = pcm_sampr_type_rec_to_play(samplerate);
#endif
#endif /* HAVE_RECORDING */
#endif /* CONFIG_SAMPR_TYPES */ #endif /* CONFIG_SAMPR_TYPES */
index = round_value_to_list32(samplerate, hw_freq_sampr, index = round_value_to_list32(samplerate, hw_freq_sampr,

View file

@ -24,16 +24,33 @@
#include "audio.h" #include "audio.h"
#include "audiohw.h" #include "audiohw.h"
#include "sound.h" #include "sound.h"
#include "general.h"
int audio_channels = 2; int audio_channels = 2;
#if CONFIG_CPU == AS3525
int audio_output_source = AUDIO_SRC_PLAYBACK;
#endif
void audio_set_output_source(int source) void audio_set_output_source(int source)
{ {
bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 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 else
I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ I2SOUT_CONTROL &= ~(1<<5); /* normal i2sout */
} }
void audio_input_mux(int source, unsigned flags) void audio_input_mux(int source, unsigned flags)
@ -108,4 +125,33 @@ void audio_input_mux(int source, unsigned flags)
} }
last_source = source; 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 */

View file

@ -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 size_t play_sub_size; /* size of current subtransfer */
static void dma_callback(void); static void dma_callback(void);
static int locked = 0; static int locked = 0;
static bool is_playing = false; static bool volatile is_playing = false;
static bool play_callback_pending = 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 */ /* Mask the DMA interrupt */
void pcm_play_lock(void) 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) void pcm_play_dma_start(const void *addr, size_t size)
{ {
is_playing = true;
dma_start_addr = (void*)addr; dma_start_addr = (void*)addr;
dma_start_size = size; dma_start_size = size;
dma_sub_addr = dma_start_addr; dma_sub_addr = dma_start_addr;
dma_rem_size = size; dma_rem_size = size;
bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE);
CGU_AUDIO |= (1<<11);
dma_retain(); dma_retain();
is_playing = true;
/* force writeback */ /* force writeback */
clean_dcache_range(dma_start_addr, dma_start_size); clean_dcache_range(dma_start_addr, dma_start_size);
bitset32(&CGU_AUDIO, (1<<11));
play_start_pcm(); play_start_pcm();
} }
void pcm_play_dma_stop(void) void pcm_play_dma_stop(void)
{ {
is_playing = false; is_playing = false;
dma_disable_channel(1); dma_disable_channel(1);
/* Ensure byte counts read back 0 */ /* Ensure byte counts read back 0 */
@ -146,8 +152,10 @@ void pcm_play_dma_stop(void)
dma_release(); dma_release();
bitclr32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); #ifdef HAVE_RECORDING
CGU_AUDIO &= ~(1<<11); if (!is_recording)
bitclr32(&CGU_AUDIO, (1<<11));
#endif
play_callback_pending = false; play_callback_pending = false;
} }
@ -175,10 +183,10 @@ void pcm_play_dma_pause(bool pause)
void pcm_play_dma_init(void) void pcm_play_dma_init(void)
{ {
bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 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(); audiohw_preinit();
pcm_dma_apply_settings();
} }
void pcm_play_dma_postinit(void) void pcm_play_dma_postinit(void)
@ -209,14 +217,15 @@ static inline unsigned char mclk_divider(void)
void pcm_dma_apply_settings(void) void pcm_dma_apply_settings(void)
{ {
int cgu_audio = CGU_AUDIO; /* read register */ bitmod32(&CGU_AUDIO,
cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */ (0<<24) | /* I2SI_MCLK2PAD_EN = disabled */
cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */ (0<<23) | /* I2SI_MCLK_EN = disabled */
cgu_audio &= ~(0x1ff << 2); /* clear i2sout divider */ (0<<14) | /* I2SI_MCLK_DIV_SEL = unused */
cgu_audio |= mclk_divider() << 2; /* set new i2sout divider */ (0<<12) | /* I2SI_MCLK_SEL = clk_main */
cgu_audio &= ~(1 << 23); /* clear I2SI_MCLK_EN */ /* I2SO_MCLK_EN = unchanged */
cgu_audio &= ~(1 << 24); /* clear I2SI_MCLK2PAD_EN */ (mclk_divider() << 2) | /* I2SO_MCLK_DIV_SEL */
CGU_AUDIO = cgu_audio; /* write back register */ (AS3525_MCLK_SEL << 0), /* I2SO_MCLK_SEL */
0x01fff7ff);
} }
size_t pcm_get_bytes_waiting(void) size_t pcm_get_bytes_waiting(void)
@ -258,220 +267,158 @@ void * pcm_dma_addr(void *addr)
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
static int rec_locked = 0; static int rec_locked = 0;
static bool is_recording = false; static uint32_t *rec_dma_addr;
static bool rec_callback_pending = false; static size_t rec_dma_size;
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
void pcm_rec_lock(void) void pcm_rec_lock(void)
{ {
++rec_locked; int oldlevel = disable_irq_save();
if (++rec_locked == 1)
{
bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
I2SIN_MASK = 0; /* disables all interrupts */
}
restore_irq(oldlevel);
} }
void pcm_rec_unlock(void) void pcm_rec_unlock(void)
{ {
if(--rec_locked == 0 && is_recording) int oldlevel = disable_irq_save();
if (--rec_locked == 0 && is_recording)
{ {
int old = disable_irq_save(); VIC_INT_ENABLE = INTERRUPT_I2SIN;
if(rec_callback_pending) I2SIN_MASK = (1<<2); /* I2SIN_MASK_POAF */
{
rec_callback_pending = false;
rec_dma_callback();
}
restore_irq(old);
} }
restore_irq(oldlevel);
} }
static void rec_dma_start(void) void INT_I2SIN(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);
}
#if CONFIG_CPU == AS3525 #if CONFIG_CPU == AS3525
/* if needed, duplicate samples of the working channel until the given bound */ if (audio_channels == 1)
static inline void mono2stereo(int16_t *end) {
{ /* RX is left-channel-only mono */
if(audio_channels != 1) /* only for microphone */ while (rec_dma_size > 0)
return; {
#if 0 if (I2SIN_RAW_STATUS & (1<<5))
/* load pointer in a register and avoid updating it in each loop */ return; /* empty */
register int16_t *samples = mono_samples;
do { /* Discard every other sample since ADC clock is 1/2 LRCK */
int16_t left = *samples++; // load 1 sample of the left-channel uint32_t value = *I2SIN_DATA;
*samples++ = left; // copy it in the right-channel *I2SIN_DATA;
} while(samples != end);
mono_samples = samples; /* update pointer */ /* Data is in left channel only - copy to right channel
#else 14-bit => 16-bit samples */
/* gcc doesn't use pre indexing : let's save 1 cycle */ value = (uint16_t)(value << 2) | (value << 18);
int16_t left;
asm ( if (audio_output_source != AUDIO_SRC_PLAYBACK && !is_playing)
"1: ldrh %0, [%1], #2 \n" // load 1 sample of the left-channel {
" strh %0, [%1], #2 \n" // copy it in the right-channel /* In this case, loopback is manual so that both output
" cmp %1, %2 \n" // are we finished? channels have audio */
" bne 1b \n" if (I2SOUT_RAW_STATUS & (1<<5))
: "=&r"(left), "+r"(mono_samples) {
: "r"(end) /* Sync output fifo so it goes empty not before input is
: "memory" filled */
); for (unsigned i = 0; i < 4; i++)
#endif /* C / ASM */ *I2SOUT_DATA = 0;
} }
*I2SOUT_DATA = value;
*I2SOUT_DATA = value;
}
*rec_dma_addr++ = value;
rec_dma_size -= 4;
}
}
else
#endif /* CONFIG_CPU == AS3525 */ #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)
{
if(rec_dma_transfer_size)
{ {
/* RX is stereo */
#if CONFIG_CPU == AS3525v2 while (rec_dma_size > 0)
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;
/* don't act like we just transferred data when we are called from
* pcm_rec_unlock() */
rec_dma_transfer_size = 0;
#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; if (I2SIN_RAW_STATUS & (1<<5))
return; return; /* empty */
/* Discard every other sample since ADC clock is 1/2 LRCK */
uint32_t value = *I2SIN_DATA;
*I2SIN_DATA;
/* Loopback is in I2S hardware */
/* 14-bit => 16-bit samples */
*rec_dma_addr++ = (value << 2) & ~0x00030000;
rec_dma_size -= 4;
} }
} }
if(!rec_dma_size) pcm_rec_more_ready_callback(0, (void *)&rec_dma_addr, &rec_dma_size);
{
pcm_rec_more_ready_callback(0, &rec_dma_start_addr,
&rec_dma_size);
if(rec_dma_size == 0)
return;
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
}
rec_dma_start();
} }
void pcm_rec_dma_stop(void) void pcm_rec_dma_stop(void)
{ {
is_recording = false; is_recording = false;
dma_disable_channel(1);
dma_release(); VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
I2SIN_MASK = 0; /* disables all interrupts */
rec_dma_addr = NULL;
rec_dma_size = 0; rec_dma_size = 0;
I2SIN_CONTROL &= ~(1<<11); /* disable dma */ if (!is_playing)
bitclr32(&CGU_AUDIO, (1<<11));
CGU_AUDIO &= ~(1<<11); bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE |
CGU_I2SOUT_APB_CLOCK_ENABLE);
rec_callback_pending = false;
} }
void pcm_rec_dma_start(void *addr, size_t size) 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();
bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE |
CGU_I2SOUT_APB_CLOCK_ENABLE);
CGU_AUDIO |= (1<<11);
I2SIN_CONTROL |= (1<<11)|(1<<5); /* enable dma, 14bits samples */
is_recording = true; is_recording = true;
rec_dma_start(); bitset32(&CGU_AUDIO, (1<<11));
rec_dma_addr = addr;
rec_dma_size = size;
/* ensure empty FIFO */
while (!(I2SIN_RAW_STATUS & (1<<5)))
*I2SIN_DATA;
I2SIN_CLEAR = (1<<6) | (1<<0); /* push error, pop error */
} }
void pcm_rec_dma_close(void) void pcm_rec_dma_close(void)
{ {
bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
pcm_rec_dma_stop();
} }
void pcm_rec_dma_init(void) void pcm_rec_dma_init(void)
{ {
/* i2c clk src = I2SOUTIF, sdata src = AFE, bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
* data valid at positive edge of SCLK */
I2SIN_CONTROL = (1<<2);
I2SIN_MASK = 0; /* disables all interrupts */ 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) const void * pcm_rec_dma_get_peak_buffer(void)
{ {
#if CONFIG_CPU == AS3525 return rec_dma_addr;
/*
* 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
} }
#endif /* HAVE_RECORDING */ #endif /* HAVE_RECORDING */

View file

@ -186,16 +186,25 @@ void audiohw_set_sampr_dividers(int fsel)
IISDIV = (IISDIV & ~0xc000003f) | regvals[fsel].iisdiv; IISDIV = (IISDIV & ~0xc000003f) | regvals[fsel].iisdiv;
} }
#ifdef HAVE_RECORDING #ifdef CONFIG_SAMPR_TYPES
unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate) unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate,
unsigned int type)
{ {
/* Check if the samplerate is in the list of recordable rates. #ifdef HAVE_RECORDING
* Fail to default if not */ if (samplerate != HW_SAMPR_RESET && type == SAMPR_TYPE_REC)
int index = round_value_to_list32(samplerate, rec_freq_sampr, {
REC_NUM_FREQ, false); /* Check if the samplerate is in the list of recordable rates.
if (samplerate != rec_freq_sampr[index]) * Fail to default if not */
return HW_SAMPR_DEFAULT; int index = round_value_to_list32(samplerate, rec_freq_sampr,
REC_NUM_FREQ, false);
if (samplerate != rec_freq_sampr[index])
samplerate = REC_SAMPR_DEFAULT;
return samplerate * 2; /* Recording rates are 1/2 the codec clock */ samplerate *= 2; /* Recording rates are 1/2 the codec clock */
}
#endif /* HAVE_RECORDING */
return samplerate;
(void)type;
} }
#endif #endif /* CONFIG_SAMPR_TYPES */