summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2014-12-06 19:00:34 +0100
committerCástor Muñoz <cmvidal@gmail.com>2015-10-07 06:15:03 +0200
commit291b2338c98c211794d55a68c9585d278fc86563 (patch)
treec894f7d5db4e93b94c153b8125bec344e590e93c
parent42abc6a49670cd546737a4dc7542f9f3f62e3831 (diff)
downloadrockbox-291b233.tar.gz
rockbox-291b233.zip
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
-rw-r--r--firmware/export/config/ipod6g.h6
-rw-r--r--firmware/target/arm/s5l8702/debug-s5l8702.c5
-rw-r--r--firmware/target/arm/s5l8702/gpio-s5l8702.c39
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c32
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c176
-rw-r--r--firmware/target/arm/s5l8702/system-s5l8702.c2
6 files changed, 246 insertions, 14 deletions
diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h
index d38f604f9f..3eae08edce 100644
--- a/firmware/export/config/ipod6g.h
+++ b/firmware/export/config/ipod6g.h
@@ -20,11 +20,13 @@
#define HAVE_LBA48
/* 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
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 HW_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 \
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;