From 27fac88548366f57c32931ed4d7c7dfc5b4f1627 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sun, 27 Apr 2008 10:30:54 +0000 Subject: Gigabeat S: Implement i2c driver - transmit works but no testing of receiving which will get a chance later. Add some seeds for codec driver. Correct a few #defines. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17254 a1c6a512-1295-4272-9138-f99709370657 --- firmware/SOURCES | 4 +- firmware/drivers/audio/wm8978.c | 60 ++++ firmware/export/audiohw.h | 13 +- firmware/export/config-gigabeat-s.h | 6 +- firmware/export/imx31l.h | 108 ++++++- firmware/export/wm8978.h | 17 +- firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c | 313 ++++++++++++++++++++- firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h | 56 +++- firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 2 + .../target/arm/imx31/gigabeat-s/wmcodec-imx31.c | 25 +- 10 files changed, 571 insertions(+), 33 deletions(-) create mode 100644 firmware/drivers/audio/wm8978.c diff --git a/firmware/SOURCES b/firmware/SOURCES index 879800ec96..5083357732 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -210,7 +210,9 @@ drivers/mas.c drivers/audio/uda1380.c #elif defined(HAVE_WM8751) drivers/audio/wm8751.c -#elif defined(HAVE_WM8975) || defined(HAVE_WM8978) +#elif defined(HAVE_WM8978) +drivers/audio/wm8978.c +#elif defined(HAVE_WM8975) drivers/audio/wm8975.c #elif defined(HAVE_WM8985) drivers/audio/wm8985.c diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c new file mode 100644 index 0000000000..01f3d331bb --- /dev/null +++ b/firmware/drivers/audio/wm8978.c @@ -0,0 +1,60 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Michael Sevakis + * + * Driver for WM8978 audio codec + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "system.h" +#include "audiohw.h" +#include "wmcodec.h" +#include "audio.h" + +const struct sound_settings_info audiohw_settings[] = { + [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, + [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, + [SOUND_TREBLE] = {"dB", 0, 1, -12, 12, 0}, + [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, + [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, + [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, + [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, + [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, + [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, +#if 0 + [SOUND_BASS_CUTOFF] = {"", 0, 1, 1, 4, 1}, + [SOUND_TREBLE_CUTOFF] = {"", 0, 1, 1, 4, 1}, +#endif +}; + +void audiohw_preinit(void) +{ + wmcodec_write(WM8978_SOFTWARE_RESET, 0); +} + +void audiohw_postinit(void) +{ + sleep(HZ); +} + +void audiohw_close(void) +{ +} + +void audiohw_mute(bool mute) +{ + (void)mute; +} diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h index 73c50b8412..190fcca7a8 100644 --- a/firmware/export/audiohw.h +++ b/firmware/export/audiohw.h @@ -27,7 +27,9 @@ #include "uda1380.h" #elif defined(HAVE_WM8751) #include "wm8751.h" -#elif defined(HAVE_WM8975) || defined(HAVE_WM8978) +#elif defined(HAVE_WM8978) +#include "wm8978.h" +#elif defined(HAVE_WM8975) #include "wm8975.h" #elif defined(HAVE_WM8985) #include "wm8985.h" @@ -109,7 +111,14 @@ extern const struct sound_settings_info audiohw_settings[]; void audiohw_init(void); /** - * Do some stuff (codec related) after audiohw_init. + * Do initial audio codec setup. + */ +void audiohw_preinit(void); + +/** + * Do some stuff (codec related) after audiohw_init that needs to be + * delayed such as enabling outputs to prevent popping. This lets + * other inits in the system complete in the meantime. */ void audiohw_postinit(void); diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h index 6eb57b219c..3e36e0c7ea 100644 --- a/firmware/export/config-gigabeat-s.h +++ b/firmware/export/config-gigabeat-s.h @@ -66,10 +66,9 @@ /* The number of bytes reserved for loadable plugins */ #define PLUGIN_BUFFER_SIZE 0x80000 -/* Define this if you have the WM8975 audio codec */ +/* Define this if you have the WM8978 audio codec */ #define HAVE_WM8978 - #define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | \ SAMPR_CAP_11) @@ -85,8 +84,9 @@ /* Define this if you want to use coldfire's i2c interface */ #define CONFIG_I2C I2C_IMX31L -/* Define the bitmask of serial interface modules (CSPI) used */ +/* Define the bitmask of modules used */ #define SPI_MODULE_MASK (USE_CSPI2_MODULE) +#define I2C_MODULE_MASK (USE_I2C1_MODULE) /* Define this if target has an additional number of threads specific to it */ #define TARGET_EXTRA_THREADS 1 diff --git a/firmware/export/imx31l.h b/firmware/export/imx31l.h index aea4b9e151..777fa6981c 100755 --- a/firmware/export/imx31l.h +++ b/firmware/export/imx31l.h @@ -49,7 +49,7 @@ #define ETB_SLOT4_BASE_ADDR 0x43F10000 #define ETB_SLOT5_BASE_ADDR 0x43F14000 #define ECT_CTIO_BASE_ADDR 0x43F18000 -#define I2C_BASE_ADDR 0x43F80000 +#define I2C1_BASE_ADDR 0x43F80000 #define I2C3_BASE_ADDR 0x43F84000 #define OTG_BASE_ADDR 0x43F88000 #define ATA_BASE_ADDR 0x43F8C000 @@ -628,6 +628,112 @@ #define CSPI_TESTREG_SWAP (1 << 15) #define CSPI_TESTREG_LBC (1 << 14) +/* I2C */ +#define I2C_IADR1 (*(REG16_PTR_T)(I2C1_BASE_ADDR+0x0)) +#define I2C_IFDR1 (*(REG16_PTR_T)(I2C1_BASE_ADDR+0x4)) +#define I2C_I2CR1 (*(REG16_PTR_T)(I2C1_BASE_ADDR+0x8)) +#define I2C_I2SR1 (*(REG16_PTR_T)(I2C1_BASE_ADDR+0xC)) +#define I2C_I2DR1 (*(REG16_PTR_T)(I2C1_BASE_ADDR+0x10)) + +#define I2C_IADR2 (*(REG16_PTR_T)(I2C2_BASE_ADDR+0x0)) +#define I2C_IFDR2 (*(REG16_PTR_T)(I2C2_BASE_ADDR+0x4)) +#define I2C_I2CR2 (*(REG16_PTR_T)(I2C2_BASE_ADDR+0x8)) +#define I2C_I2SR2 (*(REG16_PTR_T)(I2C2_BASE_ADDR+0xC)) +#define I2C_I2DR2 (*(REG16_PTR_T)(I2C2_BASE_ADDR+0x10)) + +#define I2C_IADR3 (*(REG16_PTR_T)(I2C3_BASE_ADDR+0x0)) +#define I2C_IFDR3 (*(REG16_PTR_T)(I2C3_BASE_ADDR+0x4)) +#define I2C_I2CR3 (*(REG16_PTR_T)(I2C3_BASE_ADDR+0x8)) +#define I2C_I2SR3 (*(REG16_PTR_T)(I2C3_BASE_ADDR+0xC)) +#define I2C_I2DR3 (*(REG16_PTR_T)(I2C3_BASE_ADDR+0x10)) + + /* IADR - [7:1] Address */ + + /* IFDR */ +#define I2C_IFDR_DIV30 0x00 +#define I2C_IFDR_DIV32 0x01 +#define I2C_IFDR_DIV36 0x02 +#define I2C_IFDR_DIV42 0x03 +#define I2C_IFDR_DIV48 0x04 +#define I2C_IFDR_DIV52 0x05 +#define I2C_IFDR_DIV60 0x06 +#define I2C_IFDR_DIV72 0x07 +#define I2C_IFDR_DIV80 0x08 +#define I2C_IFDR_DIV88 0x09 +#define I2C_IFDR_DIV104 0x0a +#define I2C_IFDR_DIV128 0x0b +#define I2C_IFDR_DIV144 0x0c +#define I2C_IFDR_DIV160 0x0d +#define I2C_IFDR_DIV192 0x0e +#define I2C_IFDR_DIV240 0x0f +#define I2C_IFDR_DIV288 0x10 +#define I2C_IFDR_DIV320 0x11 +#define I2C_IFDR_DIV384 0x12 +#define I2C_IFDR_DIV480 0x13 +#define I2C_IFDR_DIV576 0x14 +#define I2C_IFDR_DIV640 0x15 +#define I2C_IFDR_DIV768 0x16 +#define I2C_IFDR_DIV960 0x17 +#define I2C_IFDR_DIV1152 0x18 +#define I2C_IFDR_DIV1280 0x19 +#define I2C_IFDR_DIV1536 0x1a +#define I2C_IFDR_DIV1920 0x1b +#define I2C_IFDR_DIV2304 0x1c +#define I2C_IFDR_DIV2560 0x1d +#define I2C_IFDR_DIV3072 0x1e +#define I2C_IFDR_DIV3840 0x1f +#define I2C_IFDR_DIV22 0x20 +#define I2C_IFDR_DIV24 0x21 +#define I2C_IFDR_DIV26 0x22 +#define I2C_IFDR_DIV28 0x23 +#define I2C_IFDR_DIV32_2 0x24 +#define I2C_IFDR_DIV36_2 0x25 +#define I2C_IFDR_DIV40 0x26 +#define I2C_IFDR_DIV44 0x27 +#define I2C_IFDR_DIV48_2 0x28 +#define I2C_IFDR_DIV56 0x29 +#define I2C_IFDR_DIV64 0x2a +#define I2C_IFDR_DIV72_2 0x2b +#define I2C_IFDR_DIV80_2 0x2c +#define I2C_IFDR_DIV96 0x2d +#define I2C_IFDR_DIV112 0x2e +#define I2C_IFDR_DIV128_2 0x2f +#define I2C_IFDR_DIV160_2 0x30 +#define I2C_IFDR_DIV192_2 0x31 +#define I2C_IFDR_DIV224 0x32 +#define I2C_IFDR_DIV256 0x33 +#define I2C_IFDR_DIV320_2 0x34 +#define I2C_IFDR_DIV384_2 0x35 +#define I2C_IFDR_DIV448 0x36 +#define I2C_IFDR_DIV512 0x37 +#define I2C_IFDR_DIV640_2 0x38 +#define I2C_IFDR_DIV768_2 0x39 +#define I2C_IFDR_DIV896 0x3a +#define I2C_IFDR_DIV1024 0x3b +#define I2C_IFDR_DIV1280_2 0x3c +#define I2C_IFDR_DIV1536_2 0x3d +#define I2C_IFDR_DIV1792 0x3e +#define I2C_IFDR_DIV2048 0x3f + + /* I2CR */ +#define I2C_I2CR_IEN (1 << 7) +#define I2C_I2CR_IIEN (1 << 6) +#define I2C_I2CR_MSTA (1 << 5) +#define I2C_I2CR_MTX (1 << 4) +#define I2C_I2CR_TXAK (1 << 3) +#define I2C_I2CR_RSATA (1 << 2) + + /* I2SR */ +#define I2C_I2SR_ICF (1 << 7) +#define I2C_I2SR_IAAS (1 << 6) +#define I2C_I2SR_IBB (1 << 5) +#define I2C_I2SR_IAL (1 << 4) +#define I2C_I2SR_SRW (1 << 2) +#define I2C_I2SR_IIF (1 << 1) +#define I2C_I2SR_RXAK (1 << 0) + + /* I2DR - [7:0] Data */ + /* RTC */ #define RTC_HOURMIN (*(REG32_PTR_T)(RTC_BASE_ADDR+0x00)) #define RTC_SECONDS (*(REG32_PTR_T)(RTC_BASE_ADDR+0x04)) diff --git a/firmware/export/wm8978.h b/firmware/export/wm8978.h index 3a1eb627df..aca1250665 100644 --- a/firmware/export/wm8978.h +++ b/firmware/export/wm8978.h @@ -21,6 +21,9 @@ #ifndef _WM8978_H #define _WM8978_H +#define VOLUME_MIN -570 +#define VOLUME_MAX 60 + #define WM8978_I2C_ADDR 0x34 /* Registers */ @@ -142,7 +145,7 @@ #define WM8978_DAC_COMP_U_LAW (2 << 3) #define WM8978_DAC_COMP_A_LAW (3 << 3) #define WM8978_ADC_COMP (3 << 1) - #define WM8978_DAC_COMP_OFF (0 << 1) + #define WM8978_ADC_COMP_OFF (0 << 1) #define WM8978_ADC_COMP_U_LAW (2 << 1) #define WM8978_ADC_COMP_A_LAW (3 << 1) #define WM8978_LOOPBACK (1 << 0) @@ -159,12 +162,12 @@ #define WM8978_MCLKDIV_8 (6 << 5) #define WM8978_MCLKDIV_12 (7 << 5) #define WM8978_BCLKDIV (7 << 2) - #define WM8978_MCLKDIV_1 (0 << 2) - #define WM8978_MCLKDIV_2 (1 << 2) - #define WM8978_MCLKDIV_4 (2 << 2) - #define WM8978_MCLKDIV_8 (3 << 2) - #define WM8978_MCLKDIV_16 (4 << 2) - #define WM8978_MCLKDIV_32 (5 << 2) + #define WM8978_BCLKDIV_1 (0 << 2) + #define WM8978_BCLKDIV_2 (1 << 2) + #define WM8978_BCLKDIV_4 (2 << 2) + #define WM8978_BCLKDIV_8 (3 << 2) + #define WM8978_BCLKDIV_16 (4 << 2) + #define WM8978_BCLKDIV_32 (5 << 2) #define WM8978_MS (1 << 0) /* WM8978_ADDITIONAL_CTRL (0x07) */ diff --git a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c b/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c index 54ddaa7a46..ccd9efb321 100644 --- a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2007 by Michael Sevakis + * Copyright (C) 2008 by Michael Sevakis * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. @@ -16,38 +16,321 @@ * KIND, either express or implied. * ****************************************************************************/ +#include +#include "config.h" #include "system.h" +#include "kernel.h" +#include "avic-imx31.h" +#include "clkctl-imx31.h" #include "i2c-imx31.h" -#if 0 -static int i2c_getack(void) +/* Forward interrupt handler declarations */ +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C1_HANDLER(void); +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C2_HANDLER(void); +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C3_HANDLER(void); +#endif + +static struct i2c_module_descriptor { - return 0; + struct i2c_map *base; /* Module base address */ + enum IMX31_CG_LIST cg; /* Clock gating index */ + enum IMX31_INT_LIST ints; /* Module interrupt number */ + int enable; /* Enable count */ + void (*handler)(void); /* Module interrupt handler */ + struct mutex m; /* Node mutual-exclusion */ + struct wakeup w; /* I2C done signal */ + unsigned char *addr_data; /* Additional addressing data */ + int addr_count; /* Addressing byte count */ + unsigned char *data; /* TX/RX buffer (actual data) */ + int data_count; /* TX/RX byte count */ + unsigned char addr; /* Address + r/w bit */ +} i2c_descs[I2C_NUM_I2C] = +{ +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) + { + .base = (struct i2c_map *)I2C1_BASE_ADDR, + .cg = CG_I2C1, + .ints = I2C1, + .handler = I2C1_HANDLER, + }, +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) + { + .base = (struct i2c_map *)I2C2_BASE_ADDR, + .cg = CG_I2C2, + .ints = I2C2, + .handler = I2C2_HANDLER, + }, +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) + { + .base = (struct i2c_map *)I2C3_BASE_ADDR, + .cg = CG_I2C3, + .ints = I2C3, + .handler = I2C3_HANDLER, + }, +#endif +}; + +static void i2c_interrupt(enum i2c_module_number i2c) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[i2c]; + struct i2c_map * const base = desc->base; + uint16_t i2sr = base->i2sr; + + base->i2sr = i2sr & ~I2C_I2SR_IIF; /* Clear IIF */ + + if (desc->addr_count >= 0) + { + /* ADDR cycle - either done or more to send */ + if ((i2sr & I2C_I2SR_RXAK) != 0) + { + goto i2c_stop; /* problem */ + } + + if (--desc->addr_count < 0) + { + /* Switching to data cycle */ + if (desc->addr & 0x1) + { + base->i2cr &= ~I2C_I2CR_MTX; /* Switch to RX mode */ + base->i2dr; /* Dummy read */ + return; + } + /* else remaining data is TX - handle below */ + goto i2c_transmit; + } + else + { + base->i2dr = *desc->addr_data++; /* Send next addressing byte */ + return; + } + } + + if (base->i2cr & I2C_I2CR_MTX) + { +i2c_transmit: + /* Transmitting data */ + if ((i2sr & I2C_I2SR_RXAK) == 0 && desc->data_count > 0) + { + /* More bytes to send, got ACK from previous byte */ + base->i2dr = *desc->data++; + desc->data_count--; + return; + } + /* else done or no ACK received */ + } + else + { + /* Receiving data */ + if (--desc->data_count > 0) + { + if (desc->data_count == 1) + { + /* 2nd to Last byte - NACK */ + base->i2cr |= I2C_I2CR_TXAK; + } + + *desc->data++ = base->i2dr; /* Read data from I2DR and store */ + return; + } + else + { + /* Generate STOP signal before reading data */ + base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); + *desc->data++ = base->i2dr; /* Read data from I2DR and store */ + goto i2c_done; + } + } + +i2c_stop: + /* Generate STOP signal */ + base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); +i2c_done: + /* Signal thread we're done */ + wakeup_signal(&desc->w); } -static int i2c_start(void) +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C1_HANDLER(void) { - return 0; + i2c_interrupt(I2C1_NUM); } +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C2_HANDLER(void) +{ + i2c_interrupt(I2C2_NUM); +} +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C3_HANDLER(void) +{ + i2c_interrupt(I2C3_NUM); +} +#endif -static void i2c_stop(void) +static int i2c_transfer(struct i2c_node * const node, + struct i2c_module_descriptor *const desc) { + struct i2c_map * const base = desc->base; + int count = desc->data_count; + uint16_t i2cr; + + /* Set speed */ + base->ifdr = node->ifdr; + + /* Enable module */ + base->i2cr = I2C_I2CR_IEN; + + /* Enable Interrupt, Master */ + i2cr = I2C_I2CR_IEN | I2C_I2CR_IIEN | I2C_I2CR_MTX; + + if ((desc->addr & 0x1) && desc->data_count < 2) + { + /* Receiving less than two bytes - disable ACK generation */ + i2cr |= I2C_I2CR_TXAK; + } + + /* Set config */ + base->i2cr = i2cr; + + /* Generate START */ + base->i2cr |= I2C_I2CR_MSTA; + + /* Address slave (first byte sent) and begin session. */ + base->i2dr = desc->addr; + + /* Wait for transfer to complete */ + count = (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) ? + -1 : (count - desc->data_count); + + /* Disable module - generate STOP if timeout */ + base->i2cr = 0; + + return count; } -static int i2c_outb(unsigned char byte) +int i2c_read(struct i2c_node *node, int reg, + unsigned char *data, int data_count) { - (void)byte; - return 0; + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + unsigned char ad[1]; + + mutex_lock(&desc->m); + + desc->addr = (node->addr & 0xfe) | 0x1; /* Slave address/rd */ + + if (reg >= 0) + { + /* Sub-address */ + desc->addr_count = 1; + desc->addr_data = ad; + ad[0] = reg; + } + /* else raw read from slave */ + + desc->data = data; + desc->data_count = data_count; + + data_count = i2c_transfer(node, desc); + + desc->addr_count = 0; /* To eliminate zeroing elsewhere */ + + mutex_unlock(&desc->m); + + return data_count; } -#endif -void i2c_write(int addr, const unsigned char *buf, int count) +int i2c_write(struct i2c_node *node, const unsigned char *data, int data_count) { - (void)addr; - (void)buf; - (void)count; + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + + mutex_lock(&desc->m); + + desc->addr = node->addr & 0xfe; /* Slave address/wr */ + desc->data = (unsigned char *)data; + desc->data_count = data_count; + + data_count = i2c_transfer(node, desc); + + mutex_unlock(&desc->m); + + return data_count; } void i2c_init(void) { + int i; + + /* Do one-time inits for each module that will be used - leave + * module disabled and unclocked until something wants it */ + for (i = 0; i < I2C_NUM_I2C; i++) + { + struct i2c_module_descriptor *const desc = &i2c_descs[i]; + imx31_clkctl_module_clock_gating(desc->cg, CGM_ON_ALL); + mutex_init(&desc->m); + wakeup_init(&desc->w); + desc->base->i2cr = 0; + imx31_clkctl_module_clock_gating(desc->cg, CGM_OFF); + } + +#if 0 + /* Pad config set up by OF bootloader doesn't agree with manual but + * TX works at the moment - probably would't do this here either */ + uint32_t reg = SW_PAD_CTL_CSI_PIXCLK_I2C_CLK_I2C_DAT; + reg &= ~0xfffff; + reg |= (1 << 19) | (3 << 17) | (1 << 15) | (1 << 14) | + (1 << 13) | (0 << 11); + + reg |= (1 << 9) | (3 << 7) | (1 << 5) | (1 << 4) | + (1 << 3) | (0 << 1); + + SW_PAD_CTL_CSI_PIXCLK_I2C_CLK_I2C_DAT = reg; +#endif +} + +void i2c_enable_node(struct i2c_node *node, bool enable) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + + mutex_lock(&desc->m); + + if (enable) + { + if (++desc->enable == 1) + { + /* First enable */ + imx31_clkctl_module_clock_gating(desc->cg, CGM_ON_ALL); + avic_enable_int(desc->ints, IRQ, 7, desc->handler); + } + } + else + { + if (desc->enable > 0 && --desc->enable == 0) + { + /* Last enable */ + avic_disable_int(desc->ints); + imx31_clkctl_module_clock_gating(desc->cg, CGM_OFF); + } + } + + mutex_unlock(&desc->m); +} + +void i2c_lock_node(struct i2c_node *node) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + mutex_lock(&desc->m); +} + +void i2c_unlock_node(struct i2c_node *node) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + mutex_unlock(&desc->m); } diff --git a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h b/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h index c708ebbfb4..d6de5c47e8 100644 --- a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h +++ b/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h @@ -16,7 +16,61 @@ * KIND, either express or implied. * ****************************************************************************/ +#ifndef _I2C_IMX31_H_ +#define _I2C_IMX31_H_ + +#include + +/* I2C module usage masks */ +#define USE_I2C1_MODULE (1 << 0) +#define USE_I2C2_MODULE (1 << 1) +#define USE_I2C3_MODULE (1 << 2) + +enum i2c_module_number +{ + __I2C_NUM_START = -1, +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) + I2C1_NUM, +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) + I2C2_NUM, +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) + I2C3_NUM, +#endif + I2C_NUM_I2C, +}; + +/* Module interface map structure */ +struct i2c_map +{ + volatile uint16_t iadr; /* 0x00 */ + volatile uint16_t unused1; + volatile uint16_t ifdr; /* 0x04 */ + volatile uint16_t unused2; + volatile uint16_t i2cr; /* 0x08 */ + volatile uint16_t unused3; + volatile uint16_t i2sr; /* 0x0C */ + volatile uint16_t unused4; + volatile uint16_t i2dr; /* 0x10 */ +}; + +struct i2c_node +{ + enum i2c_module_number num; /* Module that this node uses */ + unsigned int ifdr; /* Maximum frequency for node */ + unsigned char addr; /* Slave address on module */ +}; void i2c_init(void); -void i2c_write(int addr, const unsigned char *data, int count); +/* Enable or disable the node - modules will be switch on/off accordingly. */ +void i2c_enable_node(struct i2c_node *node, bool enable); +/* If addr < 0, then raw read */ +int i2c_read(struct i2c_node *node, int addr, unsigned char *data, int count); +int i2c_write(struct i2c_node *node, const unsigned char *data, int count); +/* Gain mutually-exclusive access to the node and module to perform multiple + * operations atomically */ +void i2c_lock_node(struct i2c_node *node); +void i2c_unlock_node(struct i2c_node *node); +#endif /* _I2C_IMX31_H_ */ diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c index 4d2206bd56..e37f6bfbe2 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c @@ -49,10 +49,12 @@ void pcm_apply_settings(void) void pcm_play_dma_init(void) { + audiohw_init(); } void pcm_postinit(void) { + audiohw_postinit(); } #if 0 diff --git a/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c b/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c index 238ee3aeb2..235ae54bad 100644 --- a/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Gigabeat specific code for the Wolfson codec + * Gigabeat S specific code for the WM8978 codec * * Based on code from the ipodlinux project - http://ipodlinux.org/ * Adapted for Rockbox in December 2005 @@ -28,12 +28,31 @@ #include "sound.h" #include "i2c-imx31.h" +/* NOTE: Some port-specific bits will have to be moved away (node and GPIO + * writes) for cleanest implementation. */ + +static struct i2c_node wm8978_i2c_node = +{ + .num = I2C1_NUM, + .ifdr = I2C_IFDR_DIV192, /* 66MHz/.4MHz = 165, closest = 192 = 343750Hz */ + /* Just hard-code for now - scaling may require + * updating */ + .addr = WM8978_I2C_ADDR, +}; + void audiohw_init(void) { + i2c_enable_node(&wm8978_i2c_node, true); + GPIO3_DR |= (1 << 21); /* Turn on analogue LDO */ + sleep(HZ/10); /* Wait for things to stabilize */ + audiohw_preinit(); } void wmcodec_write(int reg, int data) { - (void)reg; - (void)data; + unsigned char d[2]; + /* |aaaaaaad|dddddddd| */ + d[0] = (reg << 1) | ((data & 0x100) >> 8); + d[1] = data; + i2c_write(&wm8978_i2c_node, d, 2); } -- cgit v1.2.3