summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-04-27 10:30:54 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-04-27 10:30:54 +0000
commit27fac88548366f57c32931ed4d7c7dfc5b4f1627 (patch)
treee45132b551ce39a44a6fefa31e85971cea6df47e
parentfec6af659f809d47f6e750975289e220908396fe (diff)
downloadrockbox-27fac88548366f57c32931ed4d7c7dfc5b4f1627.zip
rockbox-27fac88548366f57c32931ed4d7c7dfc5b4f1627.tar.gz
rockbox-27fac88548366f57c32931ed4d7c7dfc5b4f1627.tar.bz2
rockbox-27fac88548366f57c32931ed4d7c7dfc5b4f1627.tar.xz
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
-rw-r--r--firmware/SOURCES4
-rw-r--r--firmware/drivers/audio/wm8978.c60
-rw-r--r--firmware/export/audiohw.h13
-rw-r--r--firmware/export/config-gigabeat-s.h6
-rwxr-xr-xfirmware/export/imx31l.h108
-rw-r--r--firmware/export/wm8978.h17
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c313
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h56
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c2
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c25
10 files changed, 571 insertions, 33 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 879800e..5083357 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 0000000..01f3d33
--- /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 73c50b8..190fcca 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 6eb57b2..3e36e0c 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 aea4b9e..777fa69 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 3a1eb62..aca1250 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 54ddaa7..ccd9efb 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 <stdlib.h>
+#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 c708ebb..d6de5c4 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 <stdbool.h>
+
+/* 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 4d2206b..e37f6bf 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 238ee3a..235ae54 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);
}