From 291b2338c98c211794d55a68c9585d278fc86563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1stor=20Mu=C3=B1oz?= Date: Sat, 6 Dec 2014 19:00:34 +0100 Subject: 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 --- firmware/target/arm/s5l8702/debug-s5l8702.c | 5 + firmware/target/arm/s5l8702/gpio-s5l8702.c | 39 ++++- firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c | 32 ++++ firmware/target/arm/s5l8702/pcm-s5l8702.c | 176 +++++++++++++++++++++- firmware/target/arm/s5l8702/system-s5l8702.c | 2 +- 5 files changed, 242 insertions(+), 12 deletions(-) (limited to 'firmware/target') diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c index 74f765e447..c49737756c 100644 --- a/firmware/target/arm/s5l8702/debug-s5l8702.c +++ b/firmware/target/arm/s5l8702/debug-s5l8702.c @@ -39,6 +39,8 @@ #define _DEBUG_PRINTF(a, varargs...) lcd_putsf(0, line++, (a), ##varargs); extern int lcd_type; +extern int rec_hw_ver; + bool dbg_hw_info(void) { int line; @@ -70,6 +72,9 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("LCD type: %d", lcd_type); line++; + + _DEBUG_PRINTF("capture HW: %d", rec_hw_ver); + line++; } else if(state==1) { diff --git a/firmware/target/arm/s5l8702/gpio-s5l8702.c b/firmware/target/arm/s5l8702/gpio-s5l8702.c index d3ccb032bc..bd87005ad8 100644 --- a/firmware/target/arm/s5l8702/gpio-s5l8702.c +++ b/firmware/target/arm/s5l8702/gpio-s5l8702.c @@ -26,6 +26,40 @@ #include "gpio-s5l8702.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! @@ -47,11 +81,6 @@ static int n_handlers = 0; /* API */ -void INIT_ATTR gpio_init(void) -{ - /* TODO: initialize GPIO ports for minimum power consumption */ -} - uint32_t gpio_group_get(int group) { uint32_t pcon = PCON(group); diff --git a/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c index 6ede3d0c30..6a3bab06d6 100644 --- a/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c @@ -22,6 +22,9 @@ #include "cpu.h" #include "audio.h" #include "sound.h" +#include "pmu-target.h" + +extern int rec_hw_ver; #if INPUT_SRC_CAPS != 0 void audio_set_output_source(int source) @@ -46,13 +49,42 @@ void audio_input_mux(int source, unsigned flags) { audiohw_set_monitor(false); audiohw_disable_recording(); + + /* Vcodec = 1800mV (900mV + value*100mV) */ + pmu_ldo_set_voltage(3, 0x9); + + if (rec_hw_ver == 1) + GPIOCMD = 0xe070e; } #endif 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 case AUDIO_SRC_LINEIN: /* recording only */ 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_enable_recording(false); /* source line */ } diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c index a4ea56edce..446be24008 100644 --- a/firmware/target/arm/s5l8702/pcm-s5l8702.c +++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c @@ -250,37 +250,201 @@ void * pcm_dma_addr(void *addr) ** Recording DMA transfer **/ #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) { + if ((rec_locked++ == 0) && pcm_rec_initialized) + dmac_ch_lock_int(&dma_rec_ch); } 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) { + 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)addr; - (void)size; + SIZE_PANIC(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) { + pcm_rec_dma_stop(); } - 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) { - 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 */ diff --git a/firmware/target/arm/s5l8702/system-s5l8702.c b/firmware/target/arm/s5l8702/system-s5l8702.c index 6a06c29694..a9db7e1b8d 100644 --- a/firmware/target/arm/s5l8702/system-s5l8702.c +++ b/firmware/target/arm/s5l8702/system-s5l8702.c @@ -182,7 +182,7 @@ void fiq_dummy(void) void system_init(void) { - /*gpio_init();*/ + gpio_init(); pmu_init(); dma_init(); VIC0INTENABLE = 1 << IRQ_WHEEL; -- cgit v1.2.3