Sansa AMS recording support (Microphone and FM)

Still disabled on all targets:
- Fuze and e200v2 see spurious interrupts with no source defined
- Clip/m200v4 deadlock instantly when starting recording (perhaps due to low memory size)

Having the code in SVN will make working on this feature easier

Also add keymaps for Fuze, and correct Frequency section of recording
options : the 22.05kHz limitation of e200v1 and c200v1 doesn't apply to
Sansa AMS (different I2S hardware, unrelated to as3514)

Flyspray: FS#10371
Authors: Fred Bauer and myself

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23476 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Rafaël Carré 2009-11-01 22:51:31 +00:00
parent 6cdb80d7df
commit 9b4057bbd4
11 changed files with 186 additions and 79 deletions

View file

@ -25,44 +25,71 @@
#include "audiohw.h"
#include "sound.h"
int audio_channels = 2;
int audio_output_source = AUDIO_SRC_PLAYBACK;
void audio_set_output_source(int source)
{
if ((unsigned)source >= AUDIO_NUM_SOURCES)
source = AUDIO_SRC_PLAYBACK;
audio_output_source = source;
} /* audio_set_output_source */
(void)source;
}
void audio_input_mux(int source, unsigned flags)
{
static int last_source = AUDIO_SRC_PLAYBACK;
(void)flags;
#ifdef HAVE_RECORDING
static bool last_recording = false;
const bool recording = flags & SRCF_RECORDING;
#else
(void) flags;
#endif
switch (source)
{
default: /* playback - no recording */
source = AUDIO_SRC_PLAYBACK;
case AUDIO_SRC_PLAYBACK:
audio_channels = 2;
if (source != last_source)
{
audiohw_set_monitor(false);
#ifdef HAVE_RECORDING
audiohw_disable_recording();
#endif
}
break;
#ifdef HAVE_RECORDING
case AUDIO_SRC_MIC: /* recording only */
if (source != last_source)
{
audiohw_set_monitor(false);
audiohw_enable_recording(true); /* source mic */
}
break;
#endif
case AUDIO_SRC_FMRADIO: /* recording and playback */
audio_channels = 2;
if (source == last_source)
if (source == last_source
#ifdef HAVE_RECORDING
&& recording == last_recording
#endif
)
break;
audiohw_set_monitor(true);
#ifdef HAVE_RECORDING
last_recording = recording;
if (recording)
{
audiohw_set_monitor(false);
audiohw_enable_recording(false);
}
else
#endif
{
#ifdef HAVE_RECORDING
audiohw_disable_recording();
#endif
audiohw_set_monitor(true); /* line 2 analog audio path */
}
break;
} /* end switch */
}
last_source = source;
} /* audio_input_mux */
}

View file

@ -69,6 +69,10 @@ static void play_start_pcm(void)
CGU_PERI |= CGU_I2SOUT_APB_CLOCK_ENABLE;
CGU_AUDIO |= (1<<11);
#ifdef HAVE_RECORDING
CGU_PERI &= ~CGU_I2SIN_APB_CLOCK_ENABLE;
CGU_AUDIO &= ~(1<<23);
#endif
clean_dcache_range((void*)addr, size); /* force write back */
dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT,
@ -130,9 +134,7 @@ void pcm_play_dma_init(void)
/* clock source PLLA, minimal frequency */
CGU_AUDIO |= (511<<2) | (1<<0);
I2SOUT_CONTROL |= (1<<6) ; /* enable dma */
I2SOUT_CONTROL |= (1<<3) ; /* stereo */
I2SOUT_CONTROL &= ~(1<<2); /* 16 bit samples */
I2SOUT_CONTROL = (1<<6)|(1<<3) /* enable dma, stereo */;
audiohw_preinit();
}
@ -153,7 +155,7 @@ void pcm_dma_apply_settings(void)
int cgu_audio = CGU_AUDIO; /* read register */
cgu_audio &= ~(511 << 2); /* clear i2sout divider */
cgu_audio |= divider << 2; /* set new i2sout divider */
#if 0
#ifdef HAVE_RECORDING
cgu_audio &= ~(511 << 14); /* clear i2sin divider */
cgu_audio |= divider << 14; /* set new i2sin divider */
#endif
@ -185,43 +187,130 @@ void * pcm_dma_addr(void *addr)
** Recording DMA transfer
**/
#ifdef HAVE_RECORDING
static int rec_locked = 0;
static unsigned int *rec_start_addr;
static size_t rec_size;
void pcm_rec_lock(void)
{
if(++rec_locked == 1)
VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
}
void pcm_rec_unlock(void)
{
if(--rec_locked == 0)
VIC_INT_ENABLE |= INTERRUPT_I2SIN;
}
void pcm_record_more(void *start, size_t size)
{
(void)start;
(void)size;
rec_start_addr = start;
rec_size = size;
}
void pcm_rec_dma_stop(void)
{
VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
I2SOUT_CONTROL &= ~(1<<5); /* source = i2soutif fifo */
CGU_AUDIO &= ~((1<<23)|(1<<11));
CGU_PERI &= ~(CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE);
}
void INT_I2SIN(void)
{
register int status;
register pcm_more_callback_type2 more_ready;
status = I2SIN_STATUS;
#if 0 /* FIXME */
if ( status & ((1<<6)|(1<<0)) ) /* errors */
panicf("i2sin error: 0x%x = %s %s", status,
(status & (1<<6)) ? "push" : "",
(status & (1<<0)) ? "pop" : ""
);
#endif
while (((I2SIN_RAW_STATUS & (1<<5)) == 0) && rec_size)
{
/* 14 bits per sample = 1 32 bits word */
*rec_start_addr++ = *I2SIN_DATA;
rec_size -= 4;
}
I2SIN_CLEAR = status;
if(!rec_size)
{
more_ready = pcm_callback_more_ready;
if(!more_ready || more_ready(0) < 0)
{
/* Finished recording */
pcm_rec_dma_stop();
pcm_rec_dma_stopped_callback();
}
}
}
void pcm_rec_dma_start(void *addr, size_t size)
{
(void)addr;
(void)size;
rec_start_addr = addr;
rec_size = size;
if((unsigned int)addr & 3)
panicf("unaligned pointer!");
CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE;
CGU_AUDIO |= ((1<<23)|(1<<11));
I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */
/* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE,
* data valid at positive edge of SCLK */
I2SIN_CONTROL = (1<<5) | (1<<2);
unsigned long tmp;
while ( ( I2SIN_RAW_STATUS & ( 1<<5 ) ) == 0 )
tmp = *I2SIN_DATA; /* FLUSH FIFO */
I2SIN_CLEAR = (1<<6)|(1<<0); /* push error, pop error */
I2SIN_MASK = (1<<6) | (1<<0) |
(1<<3) | (1<<2) | (1<<1); /* half full, almost full, full */
VIC_INT_ENABLE |= INTERRUPT_I2SIN;
}
void pcm_rec_dma_close(void)
{
pcm_rec_dma_stop();
}
void pcm_rec_dma_init(void)
{
pcm_dma_apply_settings();
}
const void * pcm_rec_dma_get_peak_buffer(int *count)
{
(void)count;
const void *peak_buffer;
pcm_rec_lock();
*count = rec_size >> 2;
peak_buffer = (const void*)rec_start_addr;
pcm_rec_unlock();
return peak_buffer;
}
#endif /* HAVE_RECORDING */

View file

@ -20,6 +20,7 @@
*
****************************************************************************/
#include "config.h"
#include "kernel.h"
#include "system.h"
#include "panic.h"
@ -113,6 +114,9 @@ struct vec_int_src vec_int_srcs[] =
{ INT_SRC_MCI0, INT_MCI0 },
{ INT_SRC_GPIOA, INT_GPIOA, },
{ INT_SRC_GPIOB, INT_GPIOB, },
#ifdef HAVE_RECORDING
{ INT_SRC_I2SIN, INT_I2SIN, },
#endif
};
static void setup_vic(void)