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:
parent
2c7379757c
commit
e42a3194de
17 changed files with 272 additions and 212 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
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)
|
||||
{
|
||||
if(--rec_locked == 0 && is_recording)
|
||||
int oldlevel = disable_irq_save();
|
||||
|
||||
if (--rec_locked == 0 && is_recording)
|
||||
{
|
||||
int old = disable_irq_save();
|
||||
if(rec_callback_pending)
|
||||
{
|
||||
rec_callback_pending = false;
|
||||
rec_dma_callback();
|
||||
}
|
||||
restore_irq(old);
|
||||
VIC_INT_ENABLE = INTERRUPT_I2SIN;
|
||||
I2SIN_MASK = (1<<2); /* I2SIN_MASK_POAF */
|
||||
}
|
||||
|
||||
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 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;
|
||||
if (audio_channels == 1)
|
||||
{
|
||||
/* RX is left-channel-only mono */
|
||||
while (rec_dma_size > 0)
|
||||
{
|
||||
if (I2SIN_RAW_STATUS & (1<<5))
|
||||
return; /* empty */
|
||||
|
||||
do {
|
||||
int16_t left = *samples++; // load 1 sample of the left-channel
|
||||
*samples++ = left; // copy it in the right-channel
|
||||
} while(samples != end);
|
||||
/* Discard every other sample since ADC clock is 1/2 LRCK */
|
||||
uint32_t value = *I2SIN_DATA;
|
||||
*I2SIN_DATA;
|
||||
|
||||
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 */
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
else
|
||||
#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)
|
||||
{
|
||||
|
||||
#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;
|
||||
|
||||
/* 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)
|
||||
/* RX is stereo */
|
||||
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;
|
||||
|
||||
/* 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, &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();
|
||||
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();
|
||||
|
||||
VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
|
||||
I2SIN_MASK = 0; /* disables all interrupts */
|
||||
|
||||
rec_dma_addr = NULL;
|
||||
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 |
|
||||
CGU_I2SOUT_APB_CLOCK_ENABLE);
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
|
|
|
@ -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;
|
||||
#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;
|
||||
|
||||
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 */
|
||||
|
|
Loading…
Reference in a new issue