forked from len0rd/rockbox
ipod Classic: implement HAVE_RECORDING
This patch has been tested on iPod 80 and 160slim, actually it works but some updates must be done to the final version: - unlimitted input buffer - decrease CHUNK_SIZE - use non-cached addresses instead of discard d-cache ??? Capture hardware versions: Ver iPod models capture support --- ----------- --------------- 0 80/160fat dock line-in 1 120/160slim dock line-in + jack mic HW version 1 includes an amplifier for the jack plug mic. Capture HW detection only tested on iPod 80 and 160slim. CODEC power: AFAIK, OF powers CS42L55 at VA=2.4V for capture (1.8V for playback) and turns on the ADC charge pump. CODEC datasheet recommmends to disable the charge pump for VA>2.1V. CS42L55 DS, s4.13 (Required Initialization Settings): for VA>2.1V, some adjustments "must" be done using undocummented "control port compensation" registers. OF does not modifies these registers when VA=2.4V. This patch configures capture HW in the same way as OF does. TODO: - ADC full scale voltage depends on VA, perform tests to find clipping levels for VA=1.8V and VA=2.4V Change-Id: I7e20fd3ecaa83b1c58d5c746f5153fe5c3891d75
This commit is contained in:
parent
42abc6a496
commit
291b2338c9
6 changed files with 246 additions and 14 deletions
|
@ -20,11 +20,13 @@
|
||||||
#define HAVE_LBA48
|
#define HAVE_LBA48
|
||||||
|
|
||||||
/* define this if you have recording possibility */
|
/* define this if you have recording possibility */
|
||||||
//#define HAVE_RECORDING
|
#define HAVE_RECORDING
|
||||||
|
//#define HAVE_AGC
|
||||||
|
//#define HAVE_HISTOGRAM
|
||||||
|
|
||||||
/* 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 */
|
||||||
#define INPUT_SRC_CAPS (SRC_CAP_LINEIN)
|
#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
|
||||||
|
|
||||||
/* define the bitmask of hardware sample rates */
|
/* define the bitmask of hardware sample rates */
|
||||||
#define HW_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 \
|
#define HW_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 \
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#define _DEBUG_PRINTF(a, varargs...) lcd_putsf(0, line++, (a), ##varargs);
|
#define _DEBUG_PRINTF(a, varargs...) lcd_putsf(0, line++, (a), ##varargs);
|
||||||
|
|
||||||
extern int lcd_type;
|
extern int lcd_type;
|
||||||
|
extern int rec_hw_ver;
|
||||||
|
|
||||||
bool dbg_hw_info(void)
|
bool dbg_hw_info(void)
|
||||||
{
|
{
|
||||||
int line;
|
int line;
|
||||||
|
@ -70,6 +72,9 @@ bool dbg_hw_info(void)
|
||||||
|
|
||||||
_DEBUG_PRINTF("LCD type: %d", lcd_type);
|
_DEBUG_PRINTF("LCD type: %d", lcd_type);
|
||||||
line++;
|
line++;
|
||||||
|
|
||||||
|
_DEBUG_PRINTF("capture HW: %d", rec_hw_ver);
|
||||||
|
line++;
|
||||||
}
|
}
|
||||||
else if(state==1)
|
else if(state==1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,6 +26,40 @@
|
||||||
#include "gpio-s5l8702.h"
|
#include "gpio-s5l8702.h"
|
||||||
#include "panic.h"
|
#include "panic.h"
|
||||||
|
|
||||||
|
int rec_hw_ver;
|
||||||
|
|
||||||
|
void INIT_ATTR gpio_init(void)
|
||||||
|
{
|
||||||
|
/* Capture hardware versions:
|
||||||
|
*
|
||||||
|
* HW version 1 includes an amplifier for the jack plug
|
||||||
|
* microphone, it is activated configuring GPIO E7 as output
|
||||||
|
* high. It is posible to detect capture HW version (even
|
||||||
|
* when HP are not plugged) reading GPIO E7:
|
||||||
|
*
|
||||||
|
* Ver GPIO E7 models capture support
|
||||||
|
* --- ------- ------ ---------------
|
||||||
|
* 0 1 80/160fat dock line-in
|
||||||
|
* 1 0 120/160slim dock line-in + jack mic
|
||||||
|
*/
|
||||||
|
GPIOCMD = 0xe0700;
|
||||||
|
rec_hw_ver = (PDAT(14) & (1 << 7)) ? 0 : 1;
|
||||||
|
|
||||||
|
/* default GPIO configuration */
|
||||||
|
GPIOCMD = 0xe070e;
|
||||||
|
if (rec_hw_ver == 0) {
|
||||||
|
GPIOCMD = 0xe060e;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* GPIO E6 is connected to mikey IRQ line (active low),
|
||||||
|
configure it as pull-up input */
|
||||||
|
GPIOCMD = 0xe0600;
|
||||||
|
PUNB(14) |= (1 << 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: initialize GPIO ports for minimum power consumption */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: disabled, not used and never tested!
|
* XXX: disabled, not used and never tested!
|
||||||
|
@ -47,11 +81,6 @@ static int n_handlers = 0;
|
||||||
|
|
||||||
|
|
||||||
/* API */
|
/* API */
|
||||||
void INIT_ATTR gpio_init(void)
|
|
||||||
{
|
|
||||||
/* TODO: initialize GPIO ports for minimum power consumption */
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t gpio_group_get(int group)
|
uint32_t gpio_group_get(int group)
|
||||||
{
|
{
|
||||||
uint32_t pcon = PCON(group);
|
uint32_t pcon = PCON(group);
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
|
#include "pmu-target.h"
|
||||||
|
|
||||||
|
extern int rec_hw_ver;
|
||||||
|
|
||||||
#if INPUT_SRC_CAPS != 0
|
#if INPUT_SRC_CAPS != 0
|
||||||
void audio_set_output_source(int source)
|
void audio_set_output_source(int source)
|
||||||
|
@ -46,13 +49,42 @@ void audio_input_mux(int source, unsigned flags)
|
||||||
{
|
{
|
||||||
audiohw_set_monitor(false);
|
audiohw_set_monitor(false);
|
||||||
audiohw_disable_recording();
|
audiohw_disable_recording();
|
||||||
|
|
||||||
|
/* Vcodec = 1800mV (900mV + value*100mV) */
|
||||||
|
pmu_ldo_set_voltage(3, 0x9);
|
||||||
|
|
||||||
|
if (rec_hw_ver == 1)
|
||||||
|
GPIOCMD = 0xe070e;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifdef HAVE_MIC_REC
|
||||||
|
case AUDIO_SRC_MIC: /* recording only */
|
||||||
|
if (source != last_source)
|
||||||
|
{
|
||||||
|
if (rec_hw_ver == 1)
|
||||||
|
GPIOCMD = 0xe070f;
|
||||||
|
|
||||||
|
/* Vcodec = 2400mV (900mV + value*100mV) */
|
||||||
|
pmu_ldo_set_voltage(3, 0xf);
|
||||||
|
|
||||||
|
audiohw_set_monitor(false);
|
||||||
|
audiohw_enable_recording(true); /* source mic */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LINE_REC
|
#ifdef HAVE_LINE_REC
|
||||||
case AUDIO_SRC_LINEIN: /* recording only */
|
case AUDIO_SRC_LINEIN: /* recording only */
|
||||||
if (source != last_source)
|
if (source != last_source)
|
||||||
{
|
{
|
||||||
|
if (rec_hw_ver == 1)
|
||||||
|
GPIOCMD = 0xe070e;
|
||||||
|
|
||||||
|
/* Vcodec = 2400mV (900mV + value*100mV) */
|
||||||
|
pmu_ldo_set_voltage(3, 0xf);
|
||||||
|
|
||||||
audiohw_set_monitor(false);
|
audiohw_set_monitor(false);
|
||||||
audiohw_enable_recording(false); /* source line */
|
audiohw_enable_recording(false); /* source line */
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,37 +250,201 @@ void * pcm_dma_addr(void *addr)
|
||||||
** Recording DMA transfer
|
** Recording DMA transfer
|
||||||
**/
|
**/
|
||||||
#ifdef HAVE_RECORDING
|
#ifdef HAVE_RECORDING
|
||||||
|
static volatile int rec_locked = 0;
|
||||||
|
static void *rec_dma_addr;
|
||||||
|
static size_t rec_dma_size;
|
||||||
|
static bool pcm_rec_initialized = false;
|
||||||
|
static int completed_task;
|
||||||
|
|
||||||
|
/* ahead capture buffer */
|
||||||
|
#define PCM_AHEADBUF_SAMPLES 128
|
||||||
|
#define AHEADBUF_SZ (PCM_AHEADBUF_SAMPLES * 4)
|
||||||
|
static unsigned char ahead_buf[AHEADBUF_SZ] CACHEALIGN_ATTR;
|
||||||
|
|
||||||
|
/* DMA configuration */
|
||||||
|
static void dma_rec_callback(void *cb_data) ICODE_ATTR;
|
||||||
|
|
||||||
|
enum { /* cb_data */
|
||||||
|
TASK_AHEADBUF,
|
||||||
|
TASK_RECBUF
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DMA_REC_TSKBUF_SZ 2 /* N tasks, MUST be pow2 */
|
||||||
|
#define DMA_REC_LLIBUF_SZ 8 /* N LLIs, MUST be pow2 */
|
||||||
|
static struct dmac_tsk dma_rec_tskbuf[DMA_REC_TSKBUF_SZ];
|
||||||
|
static struct dmac_lli volatile \
|
||||||
|
dma_rec_llibuf[DMA_REC_LLIBUF_SZ] CACHEALIGN_ATTR;
|
||||||
|
|
||||||
|
static struct dmac_ch dma_rec_ch = {
|
||||||
|
.dmac = &s5l8702_dmac0,
|
||||||
|
.prio = DMAC_CH_PRIO(1),
|
||||||
|
.cb_fn = dma_rec_callback,
|
||||||
|
|
||||||
|
.llibuf = dma_rec_llibuf,
|
||||||
|
.llibuf_mask = DMA_REC_LLIBUF_SZ - 1,
|
||||||
|
.llibuf_bus = DMAC_MASTER_AHB1,
|
||||||
|
|
||||||
|
.tskbuf = dma_rec_tskbuf,
|
||||||
|
.tskbuf_mask = DMA_REC_TSKBUF_SZ - 1,
|
||||||
|
.queue_mode = QUEUE_LINK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct dmac_ch_cfg dma_rec_ch_cfg = {
|
||||||
|
.srcperi = S5L8702_DMAC0_PERI_IIS0_RX,
|
||||||
|
.dstperi = S5L8702_DMAC0_PERI_MEM,
|
||||||
|
.sbsize = DMACCxCONTROL_BSIZE_4,
|
||||||
|
.dbsize = DMACCxCONTROL_BSIZE_4,
|
||||||
|
.swidth = DMACCxCONTROL_WIDTH_16,
|
||||||
|
.dwidth = DMACCxCONTROL_WIDTH_16,
|
||||||
|
.sbus = DMAC_MASTER_AHB1,
|
||||||
|
.dbus = DMAC_MASTER_AHB1,
|
||||||
|
.sinc = DMACCxCONTROL_INC_DISABLE,
|
||||||
|
.dinc = DMACCxCONTROL_INC_ENABLE,
|
||||||
|
.prot = DMAC_PROT_CACH | DMAC_PROT_BUFF | DMAC_PROT_PRIV,
|
||||||
|
/* align LLI transfers to L-R pairs (samples) */
|
||||||
|
.lli_xfer_max_count = DMAC_LLI_MAX_COUNT & ~1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* maximum and minimum supported block sizes in bytes */
|
||||||
|
#define MIN_SIZE ((size_t) (AHEADBUF_SZ * 2))
|
||||||
|
#define MAX_SIZE ((size_t) (AHEADBUF_SZ + ((DMA_REC_LLIBUF_SZ - 1) * \
|
||||||
|
(dma_rec_ch_cfg.lli_xfer_max_count << dma_rec_ch_cfg.swidth))))
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define SIZE_PANIC(sz) { \
|
||||||
|
if (((sz) < MIN_SIZE) || ((sz) > MAX_SIZE)) \
|
||||||
|
panicf("pcm record: unsupported size: %d", (sz)); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define SIZE_PANIC(sz) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void rec_dmac_ch_queue(void *addr, size_t size, int cb_data)
|
||||||
|
{
|
||||||
|
discard_dcache_range(addr, size);
|
||||||
|
dmac_ch_queue(&dma_rec_ch, (void*)S5L8702_DADDR_PERI_IIS0_RX,
|
||||||
|
addr, size, (void *)cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dma_rec_callback(void *cb_data)
|
||||||
|
{
|
||||||
|
completed_task = (int)cb_data;
|
||||||
|
|
||||||
|
if (completed_task == TASK_AHEADBUF)
|
||||||
|
{
|
||||||
|
/* safety check */
|
||||||
|
if (rec_dma_addr == NULL)
|
||||||
|
return; /* capture finished */
|
||||||
|
|
||||||
|
/* move ahead buffer to record buffer and queue
|
||||||
|
next capture-ahead task */
|
||||||
|
memcpy(rec_dma_addr, ahead_buf, AHEADBUF_SZ);
|
||||||
|
rec_dmac_ch_queue(ahead_buf, AHEADBUF_SZ, TASK_AHEADBUF);
|
||||||
|
}
|
||||||
|
else /* TASK_RECBUF */
|
||||||
|
{
|
||||||
|
/* Inform middle layer */
|
||||||
|
if (pcm_rec_dma_complete_callback(
|
||||||
|
PCM_DMAST_OK, &rec_dma_addr, &rec_dma_size))
|
||||||
|
{
|
||||||
|
SIZE_PANIC(rec_dma_size);
|
||||||
|
rec_dmac_ch_queue(rec_dma_addr + AHEADBUF_SZ,
|
||||||
|
rec_dma_size - AHEADBUF_SZ, TASK_RECBUF);
|
||||||
|
pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pcm_rec_lock(void)
|
void pcm_rec_lock(void)
|
||||||
{
|
{
|
||||||
|
if ((rec_locked++ == 0) && pcm_rec_initialized)
|
||||||
|
dmac_ch_lock_int(&dma_rec_ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_rec_unlock(void)
|
void pcm_rec_unlock(void)
|
||||||
{
|
{
|
||||||
|
if ((--rec_locked == 0) && pcm_rec_initialized)
|
||||||
|
dmac_ch_unlock_int(&dma_rec_ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_rec_dma_stop(void)
|
void pcm_rec_dma_stop(void)
|
||||||
{
|
{
|
||||||
|
if (!pcm_rec_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dmac_ch_stop(&dma_rec_ch);
|
||||||
|
|
||||||
|
I2SRXCOM = 0x2; /* stop Rx I2S */
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_rec_dma_start(void *addr, size_t size)
|
void pcm_rec_dma_start(void *addr, size_t size)
|
||||||
{
|
{
|
||||||
(void)addr;
|
SIZE_PANIC(size);
|
||||||
(void)size;
|
|
||||||
|
pcm_rec_dma_stop();
|
||||||
|
|
||||||
|
rec_dma_addr = addr;
|
||||||
|
rec_dma_size = size;
|
||||||
|
completed_task = -1;
|
||||||
|
|
||||||
|
/* launch first DMA transfer to capture into ahead buffer,
|
||||||
|
link the second task to capture into record buffer */
|
||||||
|
rec_dmac_ch_queue(ahead_buf, AHEADBUF_SZ, TASK_AHEADBUF);
|
||||||
|
rec_dmac_ch_queue(addr + AHEADBUF_SZ, size - AHEADBUF_SZ, TASK_RECBUF);
|
||||||
|
|
||||||
|
I2SRXCOM = 0x6; /* start Rx I2S */
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcm_rec_dma_close(void)
|
void pcm_rec_dma_close(void)
|
||||||
{
|
{
|
||||||
|
pcm_rec_dma_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void pcm_rec_dma_init(void)
|
void pcm_rec_dma_init(void)
|
||||||
{
|
{
|
||||||
}
|
if (pcm_rec_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PWRCON(0) &= ~(1 << 4);
|
||||||
|
PWRCON(0) &= ~(1 << 7);
|
||||||
|
|
||||||
|
dmac_ch_init(&dma_rec_ch, &dma_rec_ch_cfg);
|
||||||
|
|
||||||
|
/* synchronize lock status */
|
||||||
|
if (rec_locked)
|
||||||
|
dmac_ch_lock_int(&dma_rec_ch);
|
||||||
|
|
||||||
|
I2SRXCON = 0x1000;
|
||||||
|
I2SCLKCON = 1;
|
||||||
|
|
||||||
|
pcm_rec_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
const void * pcm_rec_dma_get_peak_buffer(void)
|
const void * pcm_rec_dma_get_peak_buffer(void)
|
||||||
{
|
{
|
||||||
return NULL;
|
void *dstaddr;
|
||||||
|
|
||||||
|
pcm_rec_lock();
|
||||||
|
|
||||||
|
if (completed_task == TASK_AHEADBUF) {
|
||||||
|
dstaddr = dmac_ch_get_info(&dma_rec_ch, NULL, NULL);
|
||||||
|
|
||||||
|
if ((dstaddr < rec_dma_addr) ||
|
||||||
|
(dstaddr > rec_dma_addr + rec_dma_size))
|
||||||
|
/* At this moment, interrupt for TASK_RECBUF is waiting to
|
||||||
|
be handled. TASK_RECBUF is already finished and HW is
|
||||||
|
transfering next TASK_AHEADBUF. Return whole block. */
|
||||||
|
dstaddr = rec_dma_addr + rec_dma_size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Ahead buffer not yet captured _and_ moved to
|
||||||
|
record buffer. Return nothing. */
|
||||||
|
dstaddr = rec_dma_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcm_rec_unlock();
|
||||||
|
|
||||||
|
return CACHEALIGN_DOWN(dstaddr);
|
||||||
|
}
|
||||||
#endif /* HAVE_RECORDING */
|
#endif /* HAVE_RECORDING */
|
||||||
|
|
|
@ -182,7 +182,7 @@ void fiq_dummy(void)
|
||||||
|
|
||||||
void system_init(void)
|
void system_init(void)
|
||||||
{
|
{
|
||||||
/*gpio_init();*/
|
gpio_init();
|
||||||
pmu_init();
|
pmu_init();
|
||||||
dma_init();
|
dma_init();
|
||||||
VIC0INTENABLE = 1 << IRQ_WHEEL;
|
VIC0INTENABLE = 1 << IRQ_WHEEL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue