summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/drivers/tuner/si4700.c418
-rwxr-xr-xfirmware/export/imx31l.h100
-rw-r--r--firmware/export/power.h1
-rw-r--r--firmware/export/si4700.h6
-rw-r--r--firmware/target/arm/as3525/power-as3525.c16
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c75
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-imx31.c43
-rw-r--r--firmware/tuner.c2
8 files changed, 488 insertions, 173 deletions
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index f68ea5a750..fb8fc49749 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -25,12 +25,33 @@
#include <string.h>
#include <stdlib.h>
#include "kernel.h"
+#include "power.h"
#include "tuner.h" /* tuner abstraction interface */
#include "fmradio.h"
#include "fmradio_i2c.h" /* physical interface driver */
+/* some models use the internal 32 kHz oscillator which needs special attention
+ during initialisation, power-up and power-down.
+*/
+#if defined(SANSA_CLIP) || defined(SANSA_E200V2) || defined(SANSA_FUZE)
+#define USE_INTERNAL_OSCILLATOR
+#elif defined(TOSHIBA_GIGABEAT_S)
+#define SI4700_GPIO_SETUP (SYSCONFIG1_GPIO1_HI_Z | \
+ SYSCONFIG1_GPIO2_HI_Z | \
+ SYSCONFIG1_GPIO3_MO_ST_I)
+extern int si4700_st(void);
+#endif
+
+#ifndef SI4700_GPIO_SETUP
+#define SI4700_GPIO_SETUP 0
+#endif
+
+#define SEEK_THRESHOLD 0x16
+#define TUNER_VOLUME 0xC
+
#define I2C_ADR 0x20
+/** Registers and bits - "x" denotes Si4702/03 only (so they say) **/
#define DEVICEID 0x0
#define CHIPID 0x1
#define POWERCFG 0x2
@@ -43,20 +64,154 @@
#define BOOTCONFIG 0x9
#define STATUSRSSI 0xA
#define READCHAN 0xB
-#define RDSA 0xC
-#define RDSB 0xD
-#define RDSC 0xE
-#define RDSD 0xF
-
-/* some models use the internal 32 kHz oscillator which needs special attention
- during initialisation, power-up and power-down.
-*/
-#if defined(SANSA_CLIP) || defined(SANSA_E200V2) || defined(SANSA_FUZE)
-#define USE_INTERNAL_OSCILLATOR
-#endif
+#define RDSA 0xC /* x */
+#define RDSB 0xD /* x */
+#define RDSC 0xE /* x */
+#define RDSD 0xF /* x */
+
+/* DEVICEID (0x0) */
+#define DEVICEID_PN (0xf << 12)
+ /* 0x01 = Si4700/01 */
+ /* 0x01 = Si4702/03 */
+#define DEVICEID_MFGID (0xfff << 0)
+ /* always 0x242 */
+
+/* CHIPID (0x1) */
+
+#if 0 /* Informational */
+/* Si4700/01 */
+#define CHIPID_REV (0x3f << 10)
+#define CHIPID_DEV (0x1 << 9)
+ /* 0 before powerup */
+ /* 0 after powerup = Si4700 */
+ /* 1 after powerup = Si4701 */
+#define CHIPID_FIRMWARE (0xff << 0)
+
+/* Si4702/03 */
+#define CHIPID_REV (0x3f << 10)
+#define CHIPID_DEV (0xf << 6)
+ /* 0000 before PU = Si4702 */
+ /* 0001 after PU = Si4702 */
+ /* 1000 before PU = Si4703 */
+ /* 1001 after PU = Si4703 */
+#define CHIPID_FIRMWARE (0x3f << 0)
+#endif /* 0 */
+
+/* POWERCFG (0x2) */
+#define POWERCFG_DSMUTE (0x1 << 15)
+#define POWERCFG_DMUTE (0x1 << 14)
+#define POWERCFG_MONO (0x1 << 13)
+#define POWERCFG_RDSM (0x1 << 11) /* x */
+#define POWERCFG_SKMODE (0x1 << 10)
+#define POWERCFG_SEEKUP (0x1 << 9)
+#define POWERCFG_SEEK (0x1 << 8)
+#define POWERCFG_DISABLE (0x1 << 6)
+#define POWERCFG_ENABLE (0x1 << 0)
+
+/* CHANNEL (0x3) */
+#define CHANNEL_TUNE (0x1 << 15)
+#define CHANNEL_CHAN (0x3ff << 0)
+ #define CHANNEL_CHANw(x) ((x) & CHANNEL_CHAN)
+
+/* SYSCONFIG1 (0x4) */
+#define SYSCONFIG1_RDSIEN (0x1 << 15) /* x */
+#define SYSCONFIG1_STCIEN (0x1 << 14)
+#define SYSCONFIG1_RDS (0x1 << 12) /* x */
+#define SYSCONFIG1_DE (0x1 << 11)
+#define SYSCONFIG1_AGCD (0x1 << 10)
+#define SYSCONFIG1_BLNDADJ (0x3 << 6)
+ #define SYSCONFIG1_BLNDADJ_31_39_RSSI (0x0 << 6)
+ #define SYSCONFIG1_BLNDADJ_37_55_RSSI (0x1 << 6)
+ #define SYSCONFIG1_BLNDADJ_19_37_RSSI (0x2 << 6)
+ #define SYSCONFIG1_BLNDADJ_25_43_RSSI (0x3 << 6)
+#define SYSCONFIG1_GPIO3 (0x3 << 4)
+ #define SYSCONFIG1_GPIO3_HI_Z (0x0 << 4)
+ #define SYSCONFIG1_GPIO3_MO_ST_I (0x1 << 4)
+ #define SYSCONFIG1_GPIO3_LOW (0x2 << 4)
+ #define SYSCONFIG1_GPIO3_HI (0x3 << 4)
+#define SYSCONFIG1_GPIO2 (0x3 << 2)
+ #define SYSCONFIG1_GPIO2_HI_Z (0x0 << 2)
+ #define SYSCONFIG1_GPIO2_STC_RDS_I (0x1 << 2)
+ #define SYSCONFIG1_GPIO2_LOW (0x2 << 2)
+ #define SYSCONFIG1_GPIO2_HI (0x3 << 2)
+#define SYSCONFIG1_GPIO1 (0x3 << 0)
+ #define SYSCONFIG1_GPIO1_HI_Z (0x0 << 0)
+ #define SYSCONFIG1_GPIO1_LOW (0x2 << 0)
+ #define SYSCONFIG1_GPIO1_HI (0x3 << 0)
+
+/* SYSCONFIG2 (0x5) */
+#define SYSCONFIG2_SEEKTH (0xff << 8)
+ #define SYSCONFIG2_SKEETHw(x) (((x) << 8) & SYSCONFIG2_SEEKTH)
+#define SYSCONFIG2_BAND (0x3 << 6)
+ #define SYSCONFIG2_BANDw(x) (((x) << 6) & SYSCONFIG2_BAND)
+ #define SYSCONFIG2_BANDr(x) (((x) & SYSCONFIG2_BAND) >> 6)
+ #define SYSCONFIG2_BAND_875_1080 (0x0 << 6) /* tenth-megahertz */
+ #define SYSCONFIG2_BAND_760_1080 (0x1 << 6)
+ #define SYSCONFIG2_BAND_760_900 (0x2 << 6)
+#define SYSCONFIG2_SPACE (0x3 << 4)
+ #define SYSCONFIG2_SPACEw(x) (((x) << 4) & SYSCONFIG2_SPACE)
+ #define SYSCONFIG2_SPACEr(x) (((x) & SYSCONFIG2_SPACE) >> 4)
+ #define SYSCONFIG2_SPACE_200KHZ (0x0 << 4)
+ #define SYSCONFIG2_SPACE_100KHZ (0x1 << 4)
+ #define SYSCONFIG2_SPACE_50KHZ (0x2 << 4)
+/* 4700/01 0000=mute,0001=-28dBFS..2dB steps..1111= +0dBFS */
+/* 4702/03: VOLEXT=0: 0000=mute,0001=-28dBFS..2dB steps..1111= +0dBFS */
+/* VOLEXT=1: 0000=mute,0001=-58dBFS..2dB steps..1111=-30dBFS */
+#define SYSCONFIG2_VOLUME (0xf << 0)
+ #define SYSCONFIG2_VOLUMEw(x) ((x) & SYSCONFIG2_VOLUME)
+
+/* SYSCONFIG3 (0x6) */
+#define SYSCONFIG3_SMUTER (0x3 << 14)
+ #define SYSCONFIG3_SMUTER_FASTEST (0x0 << 14)
+ #define SYSCONFIG3_SMUTER_FAST (0x1 << 14)
+ #define SYSCONFIG3_SMUTER_SLOW (0x2 << 14)
+ #define SYSCONFIG3_SMUTER_SLOWEST (0x3 << 14)
+#define SYSCONFIG3_SMUTEA (0x3 << 12)
+ #define SYSCONFIG3_SMUTEA_16DB (0x0 << 12)
+ #define SYSCONFIG3_SMUTEA_14DB (0x1 << 12)
+ #define SYSCONFIG3_SMUTEA_12DB (0x2 << 12)
+ #define SYSCONFIG3_SMUTEA_10DB (0x3 << 12)
+#define SYSCONFIG3_VOLEXT (0x1 << 8) /* x */
+#define SYSCONFIG3_SKSNR (0xf << 4)
+ #define SYSCONFIG3_SKSNRw(x) (((x) << 4) & SYSCONFIG3_SKSNR)
+#define SYSCONFIG3_SKCNT (0xf << 0)
+ #define SYSCONFIG3_SKCNTw(x) (((x) << 0) & SYSCONFIG3_SKCNT)
+
+/* TEST1 (0x7) */
+/* 4700/01: 15=always 0, 13:0 = write with preexisting values! */
+/* 4702/03: 13:0 = write with preexisting values! */
+#define TEST1_XOSCEN (0x1 << 15) /* x */
+#define TEST1_AHIZEN (0x1 << 14)
+
+/* TEST2 (0x8) */
+/* 15:0 = write with preexisting values! */
+
+/* BOOTCONFIG (0x9) */
+/* 15:0 = write with preexisting values! */
+
+/* STATUSRSSI (0xA) */
+#define STATUSRSSI_RDSR (0x1 << 15) /* x */
+#define STATUSRSSI_STC (0x1 << 14)
+#define STATUSRSSI_SFBL (0x1 << 13)
+#define STATUSRSSI_AFCRL (0x1 << 12)
+#define STATUSRSSI_RDSS (0x1 << 11) /* x */
+#define STATUSRSSI_BLERA (0x3 << 9) /* x */
+#define STATUSRSSI_ST (0x1 << 8)
+#define STATUSRSSI_RSSI (0xff << 0)
+ #define STATUSRSSI_RSSIr(x) ((x) & 0xff)
+
+/* READCHAN (0xB) */
+#define READCHAN_BLERB (0x3 << 14) /* x */
+#define READCHAN_BLERC (0x3 << 12) /* x */
+#define READCHAN_BLERD (0x3 << 10) /* x */
+#define READCHAN_READCHAN (0x3ff << 0)
+
+/* RDSA-D (0xC-0xF) */
+/* 4702/03: RDS Block A-D data */
static bool tuner_present = false;
-static unsigned short cache[16];
+static int curr_frequency = 87500000; /* Current station frequency (HZ) */
+static uint16_t cache[16];
/* reads <len> registers from radio at offset 0x0A into cache */
static void si4700_read(int len)
@@ -64,7 +219,7 @@ static void si4700_read(int len)
int i;
unsigned char buf[32];
unsigned char *ptr = buf;
- unsigned short data;
+ uint16_t data;
fmradio_i2c_read(I2C_ADR, buf, len * 2);
for (i = 0; i < len; i++) {
@@ -80,8 +235,8 @@ static void si4700_write(int len)
int i;
unsigned char buf[32];
unsigned char *ptr = buf;
- unsigned short data;
-
+ uint16_t data;
+
for (i = 0; i < len; i++) {
data = cache[(i + POWERCFG) & 0xF];
*ptr++ = (data >> 8) & 0xFF;
@@ -90,6 +245,43 @@ static void si4700_write(int len)
fmradio_i2c_write(I2C_ADR, buf, len * 2);
}
+/* Hide silly, wrapped and continuous register reading and make interface
+ * appear sane and normal. This also makes the driver compatible with
+ * using the 3-wire interface. */
+static uint16_t si4700_read_reg(int reg)
+{
+ si4700_read(((reg - STATUSRSSI) & 0xF) + 1);
+ return cache[reg];
+}
+
+static void si4700_write_reg(int reg, uint16_t value)
+{
+ cache[reg] = value;
+ si4700_write(((reg - POWERCFG) & 0xF) + 1);
+}
+
+static void si4700_write_masked(int reg, uint16_t bits, uint16_t mask)
+{
+ si4700_write_reg(reg, (cache[reg] & ~mask) | (bits & mask));
+}
+
+static void si4700_write_set(int reg, uint16_t mask)
+{
+ si4700_write_reg(reg, cache[reg] | mask);
+}
+
+static void si4700_write_clear(int reg, uint16_t mask)
+{
+ si4700_write_reg(reg, cache[reg] & ~mask);
+}
+
+#if (SI4700_GPIO_SETUP & SYSCONFIG1_GPIO3) != SYSCONFIG1_GPIO3_MO_ST_I
+/* Poll i2c for the stereo status */
+static inline int si4700_st(void)
+{
+ return (si4700_read_reg(STATUSRSSI) & STATUSRSSI_ST) >> 8;
+}
+#endif
void si4700_init(void)
{
@@ -105,8 +297,7 @@ void si4700_init(void)
#ifdef USE_INTERNAL_OSCILLATOR
/* enable the internal oscillator */
- cache[TEST1] |= (1 << 15); /* XOSCEN */
- si4700_write(6);
+ si4700_write_set(TEST1, TEST1_XOSCEN);
sleep(HZ/2);
#endif
}
@@ -114,21 +305,102 @@ void si4700_init(void)
tuner_power(false);
}
-static void si4700_tune(void)
+static void si4700_sleep(int snooze)
{
- cache[CHANNEL] |= (1 << 15); /* Set TUNE high to start tuning */
- si4700_write(2);
+ if (snooze)
+ {
+ /** power down **/
+ /* ENABLE high, DISABLE high */
+ si4700_write_set(POWERCFG,
+ POWERCFG_DISABLE | POWERCFG_ENABLE);
+ /* Bits self-clear once placed in powerdown. */
+ cache[POWERCFG] &= ~(POWERCFG_DISABLE | POWERCFG_ENABLE);
+ }
+ else
+ {
+ /** power up **/
+ /* ENABLE high, DISABLE low */
+ si4700_write_masked(POWERCFG, POWERCFG_ENABLE,
+ POWERCFG_DISABLE | POWERCFG_ENABLE);
+ sleep(110 * HZ / 1000);
+
+ /* init register cache */
+ si4700_read(16);
+
+#if SI4700_GPIO_SETUP != 0
+ si4700_write_masked(SYSCONFIG1, SI4700_GPIO_SETUP,
+ SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 |
+ SYSCONFIG1_GPIO3);
+#endif
+ /* -6dB volume, seek threshold 12 */
+ si4700_write_masked(SYSCONFIG2,
+ SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) |
+ SYSCONFIG2_VOLUMEw(TUNER_VOLUME),
+ SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH);
+ }
+}
+
+static void si4700_set_frequency(int freq)
+{
+ static const unsigned int spacings[3] =
+ {
+ 200000, /* SYSCONFIG2_SPACE_200KHZ */
+ 100000, /* SYSCONFIG2_SPACE_100KHZ */
+ 50000, /* SYSCONFIG2_SPACE_50KHZ */
+ };
+ static const unsigned int bands[3] =
+ {
+ 87500000, /* SYSCONFIG2_BAND_875_1080 */
+ 76000000, /* SYSCONFIG2_BAND_760_1080 */
+ 76000000, /* SYSCONFIG2_BAND_760_900 */
+ };
+
+ /* check BAND and spacings */
+ int space = SYSCONFIG2_SPACEr(cache[SYSCONFIG2]);
+ int band = SYSCONFIG2_BANDr(cache[SYSCONFIG2]);
+ int chan = (freq - bands[band]) / spacings[space];
+
+ curr_frequency = freq;
+
+ si4700_write_reg(CHANNEL, CHANNEL_CHANw(chan) | CHANNEL_TUNE);
do
{
/* tuning should be done within 60 ms according to the datasheet */
sleep(HZ * 60 / 1000);
- si4700_read(2);
}
- while (!(cache[STATUSRSSI] & (1 << 14))); /* STC high */
+ while ((si4700_read_reg(STATUSRSSI) & STATUSRSSI_STC) == 0); /* STC high? */
- cache[CHANNEL] &= ~(1 << 15); /* Set TUNE low */
- si4700_write(2);
+ si4700_write_clear(CHANNEL, CHANNEL_TUNE); /* Set TUNE low */
+}
+
+static int si4700_tuned(void)
+{
+ /* Primitive tuning check: sufficient level and AFC not railed */
+ uint16_t status = si4700_read_reg(STATUSRSSI);
+ if (STATUSRSSI_RSSIr(status) >= SEEK_THRESHOLD &&
+ (status & STATUSRSSI_AFCRL) == 0)
+ return 1;
+
+ return 0;
+}
+
+static void si4700_set_region(int region)
+{
+ const struct si4700_region_data *rd = &si4700_region_data[region];
+ uint16_t bandspacing = SYSCONFIG2_BANDw(rd->band) |
+ SYSCONFIG2_SPACEw(rd->spacing);
+ uint16_t oldbs = cache[SYSCONFIG2] & (SYSCONFIG2_BAND | SYSCONFIG2_SPACE);
+
+ si4700_write_masked(SYSCONFIG1,
+ rd->deemphasis ? SYSCONFIG1_DE : 0,
+ SYSCONFIG1_DE);
+ si4700_write_masked(SYSCONFIG2, bandspacing,
+ SYSCONFIG2_BAND | SYSCONFIG2_SPACE);
+
+ /* Retune if this region change would change the channel number. */
+ if (oldbs != bandspacing)
+ si4700_set_frequency(curr_frequency);
}
/* tuner abstraction layer: set something to the tuner */
@@ -137,95 +409,35 @@ int si4700_set(int setting, int value)
switch(setting)
{
case RADIO_SLEEP:
- if (value)
- {
- /* power down */
- cache[POWERCFG] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */
- si4700_write(1);
- }
- else
- {
- /* power up */
- cache[POWERCFG] = 1; /* ENABLE high, DISABLE low */
- si4700_write(1);
- sleep(110 * HZ / 1000);
-
- /* update register cache */
- si4700_read(16);
-
- /* -6dB volume, keep everything else as default */
- cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~0xF) | 0xC;
- si4700_write(5);
- }
- return 1;
+ si4700_sleep(value);
+ break;
case RADIO_FREQUENCY:
- {
- static const unsigned int spacings[3] =
- {
- 200000, 100000, 50000
- };
- unsigned int chan;
- unsigned int spacing = spacings[(cache[5] >> 4) & 3] ;
-
- if (cache[SYSCONFIG2] & (3 << 6)) /* check BAND */
- {
- chan = (value - 76000000) / spacing;
- }
- else
- {
- chan = (value - 87500000) / spacing;
- }
-
- cache[CHANNEL] = (cache[CHANNEL] & ~0x3FF) | chan;
- si4700_tune();
- return 1;
- }
+ si4700_set_frequency(value);
+ break;
case RADIO_SCAN_FREQUENCY:
- si4700_set(RADIO_FREQUENCY, value);
- return 1;
+ si4700_set_frequency(value);
+ return si4700_tuned();
case RADIO_MUTE:
- if (value)
- {
- /* mute */
- cache[POWERCFG] &= ~(1 << 14);
- }
- else
- {
- /* unmute */
- cache[POWERCFG] |= (1 << 14);
- }
+ si4700_write_masked(POWERCFG, value ? 0 : POWERCFG_DMUTE,
+ POWERCFG_DMUTE);
break;
case RADIO_REGION:
- {
- const struct si4700_region_data *rd =
- &si4700_region_data[value];
-
- cache[SYSCONFIG1] = (cache[SYSCONFIG1] & ~(1 << 11)) | (rd->deemphasis << 11);
- cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~(3 << 6)) | (rd->band << 6);
- cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~(3 << 4)) | (rd->spacing << 4);
+ si4700_set_region(value);
break;
- }
case RADIO_FORCE_MONO:
- if (value)
- {
- cache[POWERCFG] |= (1 << 13);
- }
- else
- {
- cache[POWERCFG] &= ~(1 << 13);
- }
+ si4700_write_masked(POWERCFG, value ? POWERCFG_MONO : 0,
+ POWERCFG_MONO);
break;
default:
return -1;
}
- si4700_write(5);
return 1;
}
@@ -241,15 +453,25 @@ int si4700_get(int setting)
break;
case RADIO_TUNED:
- val = 1;
+ val = si4700_tuned();
break;
case RADIO_STEREO:
- si4700_read(1);
- val = (cache[STATUSRSSI] & (1 << 8)); /* ST high == Stereo */
+ val = si4700_st();
break;
}
return val;
}
+void si4700_dbg_info(struct si4700_dbg_info *nfo)
+{
+ memset(nfo->regs, 0, sizeof (nfo->regs));
+
+ if (tuner_powered())
+ {
+ si4700_read(16);
+ memcpy(nfo->regs, cache, sizeof (nfo->regs));
+ }
+}
+
diff --git a/firmware/export/imx31l.h b/firmware/export/imx31l.h
index 6780a5a43f..5a964b8e2e 100755
--- a/firmware/export/imx31l.h
+++ b/firmware/export/imx31l.h
@@ -202,26 +202,33 @@
#define SW_MUX_CTL_GPIO1_0_GPIO1_1_GPIO1_2_GPIO1_3 IOMUXC_(0x14C)
#define SW_MUX_CTL_CAPTURE_COMPARE_WATCHDOG_RST_PWMO IOMUXC_(0x150)
-#define SW_MUX_OUT_EN_GPIO_DR 0x0
-#define SW_MUX_OUT_FUNCTIONAL 0x1
-#define SW_MUX_OUT_ALTERNATE_1 0x2
-#define SW_MUX_OUT_ALTERNATE_2 0x3
-#define SW_MUX_OUT_ALTERNATE_3 0x4
-#define SW_MUX_OUT_ALTERNATE_4 0x5
-#define SW_MUX_OUT_ALTERNATE_5 0x6
-#define SW_MUX_OUT_ALTERNATE_6 0x7
-
-#define SW_MUX_IN_NO_INPUTS 0x0
-#define SW_MUX_IN_GPIO_PSR_ISR 0x1
-#define SW_MUX_IN_FUNCTIONAL 0x2
-#define SW_MUX_IN_ALTERNATE_1 0x3
-#define SW_MUX_IN_ALTERNATE_2 0x4
-
+#define SW_MUX_OUT (0x7 << 4)
+#define SW_MUX_OUT_GPIO_DR (0x0 << 4)
+#define SW_MUX_OUT_FUNCTIONAL (0x1 << 4)
+#define SW_MUX_OUT_ALT1 (0x2 << 4)
+#define SW_MUX_OUT_ALT2 (0x3 << 4)
+#define SW_MUX_OUT_ALT3 (0x4 << 4)
+#define SW_MUX_OUT_ALT4 (0x5 << 4)
+#define SW_MUX_OUT_ALT5 (0x6 << 4)
+#define SW_MUX_OUT_ALT6 (0x7 << 4)
+
+#define SW_MUX_IN (0xf << 0)
+#define SW_MUX_IN_NO_INPUTS (0x0 << 0)
+#define SW_MUX_IN_GPIO_PSR_ISR (0x1 << 0)
+#define SW_MUX_IN_FUNCTIONAL (0x2 << 0)
+#define SW_MUX_IN_ALT1 (0x4 << 0)
+#define SW_MUX_IN_ALT2 (0x8 << 0)
+
+/* Masks for each signal field */
+#define SW_MUX_CTL_SIG1 (0x7f << 0)
+#define SW_MUX_CTL_SIG2 (0x7f << 8)
+#define SW_MUX_CTL_SIG3 (0x7f << 16)
+#define SW_MUX_CTL_SIG4 (0x7f << 24)
/* Shift above flags into one of the four fields in each register */
-#define SW_MUX_CTL_FLD_0(x) ((x) << 0)
-#define SW_MUX_CTL_FLD_1(x) ((x) << 8)
-#define SW_MUX_CTL_FLD_2(x) ((x) << 16)
-#define SW_MUX_CTL_FLD_3(x) ((x) << 24)
+#define SW_MUX_CTL_SIG1w(x) (((x) << 0) & SW_MUX_CTL_SIG1)
+#define SW_MUX_CTL_SIG2w(x) (((x) << 8) & SW_MUX_CTL_SIG2)
+#define SW_MUX_CTL_SIG3w(x) (((x) << 16) & SW_MUX_CTL_SIG3)
+#define SW_MUX_CTL_SIG4w(x) (((x) << 24) & SW_MUX_CTL_SIG4)
/* SW_PAD_CTL */
#define SW_PAD_CTL_TTM_PAD__X__X IOMUXC_(0x154)
@@ -336,36 +343,39 @@
#define SW_PAD_CTL_CAPTURE_COMPARE_WATCHDOG_RST IOMUXC_(0x308)
/* SW_PAD_CTL flags */
-#define SW_PAD_CTL_LOOPBACK (1 << 9)
-#define SW_PAD_CTL_DISABLE_PULL_UP_DOWN_AND_KEEPER (0 << 7)
-#if 0 /* Same as 0 */
-#define SW_PAD_CTL_DISABLE_PULL_UP_DOWN_AND_KEEPER (1 << 7)
-#endif
-#define SW_PAD_CTL_ENABLE_KEEPER (2 << 7)
-#define SW_PAD_CTL_ENABLE_PULL_UP_OR_PULL_DOWN (3 << 7)
-#define SW_PAD_CTL_100K_PULL_DOWN (0 << 5)
-#define SW_PAD_CTL_100K_PULL_UP (1 << 5)
+#define SW_PAD_CTL_LOOPBACK (0x1 << 9) /* Route output to input */
+/* Pullup, pulldown and keeper enable */
+#define SW_PAD_CTL_PUE_PKE (0x3 << 7)
+#define SW_PAD_CTL_PUE_PKE_DISABLE (0x0 << 7)
+#define SW_PAD_CTL_PUE_PKE_DISABLE_2 (0x1 << 7) /* Same as 0x0 */
+#define SW_PAD_CTL_PUE_PKE_KEEPER (0x2 << 7)
+#define SW_PAD_CTL_PUE_PKE_PULLUPDOWN (0x3 << 7) /* Enb. Pull up or down */
+/* Pullup/down resistance */
+#define SW_PAD_CTL_PUS (0x3 << 5)
+#define SW_PAD_CTL_PUS_DOWN_100K (0x0 << 5)
+#define SW_PAD_CTL_PUS_UP_100K (0x1 << 5)
#if 0 /* Completeness */
-#define SW_PAD_CTL_47K_PULL_UP (2 << 5) /* Not in IMX31/L */
-#define SW_PAD_CTL_22K_PULL_UP (3 << 5) /* Not in IMX31/L */
-#endif
-#define SW_PAD_CTL_IPP_HYS_STD (0 << 4)
-#define SW_PAD_CTL_IPP_HYS_SCHIMDT (1 << 4)
-#define SW_PAD_CTL_IPP_ODE_CMOS (0 << 3)
-#define SW_PAD_CTL_IPP_ODE_OPEN (1 << 3)
-#define SW_PAD_CTL_IPP_DSE_STD (0 << 1)
-#define SW_PAD_CTL_IPP_DSE_HIGH (1 << 1)
-#define SW_PAD_CTL_IPP_DSE_MAX (2 << 1)
-#if 0 /* Same as 2 */
-#define SW_PAD_CTL_IPP_DSE_MAX (3 << 1)
+#define SW_PAD_CTL_PUS_UP_47K (0x2 << 5) /* Not in IMX31/L */
+#define SW_PAD_CTL_PUS_UP_22K (0x3 << 5) /* Not in IMX31/L */
#endif
-#define SW_PAD_CTL_IPP_SRE_SLOW (0 << 0)
-#define SW_PAD_CTL_IPP_SRE_FAST (1 << 0)
+#define SW_PAD_CTL_HYS (0x1 << 4) /* Schmitt trigger input */
+#define SW_PAD_CTL_ODE (0x1 << 3) /* Open drain output 0=CMOS pushpull*/
+#define SW_PAD_CTL_DSE (0x3 << 1)
+#define SW_PAD_CTL_DSE_STD (0x0 << 1) /* Drive strength */
+#define SW_PAD_CTL_DSE_HIGH (0x1 << 1)
+#define SW_PAD_CTL_DSE_MAX (0x2 << 1)
+#define SW_PAD_CTL_DSE_MAX_2 (0x3 << 1) /* Same as 0x2 */
+#define SW_PAD_CTL_SRE (0x1 << 0) /* Slew rate, 1=fast */
+
+/* Masks for each IO field */
+#define SW_PAD_CTL_IO1 (0x3ff << 0)
+#define SW_PAD_CTL_IO2 (0x3ff << 10)
+#define SW_PAD_CTL_IO3 (0x3ff << 20)
/* Shift above flags into one of the three fields in each register */
-#define SW_PAD_CTL_FLD_0(x) ((x) << 0)
-#define SW_PAD_CTL_FLD_1(x) ((x) << 10)
-#define SW_PAD_CTL_FLD_2(x) ((x) << 20)
+#define SW_PAD_CTL_IO1w(x) (((x) << 0) & SW_PAD_CTL_IO1)
+#define SW_PAD_CTL_IO2w(x) (((x) << 10) & SW_PAD_CTL_IO2)
+#define SW_PAD_CTL_IO3w(x) (((x) << 20) & SW_PAD_CTL_IO3)
/* RNGA */
#define RNGA_CONTROL (*(REG32_PTR_T)(RNGA_BASE_ADDR+0x00))
diff --git a/firmware/export/power.h b/firmware/export/power.h
index edf43f8cc8..5cd6996eff 100644
--- a/firmware/export/power.h
+++ b/firmware/export/power.h
@@ -95,6 +95,7 @@ bool spdif_powered(void);
#if CONFIG_TUNER
bool tuner_power(bool status);
+bool tuner_powered(void);
#endif
#endif /* _POWER_H_ */
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index 89905ee155..fcc71cb282 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -36,9 +36,15 @@ struct si4700_region_data
extern const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS];
+struct si4700_dbg_info
+{
+ uint16_t regs[16]; /* Read registers */
+};
+
void si4700_init(void);
int si4700_set(int setting, int value);
int si4700_get(int setting);
+void si4700_dbg_info(struct si4700_dbg_info *nfo);
#ifndef CONFIG_TUNER_MULTI
#define tuner_set si4700_set
diff --git a/firmware/target/arm/as3525/power-as3525.c b/firmware/target/arm/as3525/power-as3525.c
index 07867546c2..2fbeab0bc6 100644
--- a/firmware/target/arm/as3525/power-as3525.c
+++ b/firmware/target/arm/as3525/power-as3525.c
@@ -54,9 +54,21 @@ void ide_power_enable(bool on)
}
#if CONFIG_TUNER
+static bool tuner_on = false;
+
bool tuner_power(bool status)
{
- (void)status;
- return false;
+ if (status != tuner_on)
+ {
+ tuner_on = status;
+ status = !status;
+ }
+
+ return status;
+}
+
+bool tuner_powered(void)
+{
+ return tuner_on; /* No debug info */
}
#endif
diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
index 154e13f9a7..7646402435 100644
--- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
@@ -21,6 +21,7 @@
****************************************************************************/
#include "config.h"
#include "system.h"
+#include "mc13783.h"
#include "i2c-imx31.h"
#include "fmradio_i2c.h"
@@ -33,9 +34,77 @@ static struct i2c_node si4700_i2c_node =
.addr = (0x20),
};
+void fmradio_i2c_init(void)
+{
+ /* RST: LOW */
+ imx31_regclr32(&GPIO1_DR, (1 << 26));
+ /* RST: OUT */
+ imx31_regset32(&GPIO1_GDIR, (1 << 26));
+
+ /* I2C2 SCL: IN, I2C2: SDA IN */
+ imx31_regclr32(&GPIO2_GDIR, (3 << 14));
+ /* I2C2 SCL LO, I2C2 SDA LO */
+ imx31_regclr32(&GPIO2_DR, (3 << 14));
+
+ /* open-drain pins - external pullups on PCB. Pullup default but
+ * disabled */
+ imx31_regmod32(&SW_PAD_CTL_DSR_DTE1_RI_DTE1_DCD_DTE1,
+ /* RI_DTE1 (I2C2_SCLK) */
+ SW_PAD_CTL_IO2w(SW_PAD_CTL_PUE_PKE_DISABLE |
+ SW_PAD_CTL_PUS_UP_100K |
+ SW_PAD_CTL_HYS |
+ SW_PAD_CTL_ODE) |
+ /* DCD_DTE1 (I2C2_SDA) */
+ SW_PAD_CTL_IO1w(SW_PAD_CTL_PUE_PKE_DISABLE |
+ SW_PAD_CTL_PUS_UP_100K |
+ SW_PAD_CTL_HYS |
+ SW_PAD_CTL_ODE),
+ SW_PAD_CTL_IO2 | SW_PAD_CTL_IO1);
+ /* set outputs to I2C2 */
+ imx31_regmod32(&SW_MUX_CTL_RI_DTE1_DCD_DTE1_DTR_DCE2_RXD2,
+ /* RI_DTE1 => I2C2_SCLK */
+ SW_MUX_CTL_SIG4w(SW_MUX_OUT_ALT2 | SW_MUX_IN_ALT2) |
+ /* DCD_DTE1 => I2C2_SDA */
+ SW_MUX_CTL_SIG3w(SW_MUX_OUT_ALT2 | SW_MUX_IN_ALT2),
+ SW_MUX_CTL_SIG4 | SW_MUX_CTL_SIG3);
+}
+
void fmradio_i2c_enable(bool enable)
{
- i2c_enable_node(&si4700_i2c_node, enable);
+ if (enable)
+ {
+ uint32_t io_pin_mux = SW_MUX_CTL_RI_DTE1_DCD_DTE1_DTR_DCE2_RXD2;
+ /* place in GPIO mode to hold SDIO low during RESET release,
+ * SEN1 should be high already (pullup) and GPIO3 left alone */
+ imx31_regset32(&GPIO2_GDIR, (1 << 15)); /* SDIO OUT */
+ /* I2C2_SDA => MCU2_15 */
+ imx31_regmod32(&SW_MUX_CTL_RI_DTE1_DCD_DTE1_DTR_DCE2_RXD2,
+ SW_MUX_CTL_SIG3w(SW_MUX_OUT_GPIO_DR | SW_MUX_IN_GPIO_PSR_ISR),
+ SW_MUX_CTL_SIG3);
+ /* enable CLK32KMCU clock */
+ mc13783_set(MC13783_POWER_CONTROL0, MC13783_CLK32KMCUEN);
+ /* enable the fm chip (release RESET) */
+ imx31_regset32(&GPIO1_DR, (1 << 26));
+ sleep(HZ/100);
+ /* busmode should be selected - OK to release SDIO */
+ imx31_regclr32(&GPIO2_GDIR, (1 << 15)); /* SDIO IN */
+ /* restore pin mux (DCD_DTE1 => I2C2_SDA) */
+ imx31_regmod32(&SW_MUX_CTL_RI_DTE1_DCD_DTE1_DTR_DCE2_RXD2,
+ io_pin_mux, SW_MUX_CTL_SIG3);
+ /* the si4700 is the only thing connected to i2c2 so
+ we can diable the i2c module when not in use */
+ i2c_enable_node(&si4700_i2c_node, true);
+ }
+ else
+ {
+ /* the si4700 is the only thing connected to i2c2 so
+ we can diable the i2c module when not in use */
+ i2c_enable_node(&si4700_i2c_node, false);
+ /* disable the fm chip */
+ imx31_regclr32(&GPIO1_DR, (1 << 26));
+ /* disable CLK32KMCU clock */
+ mc13783_clear(MC13783_POWER_CONTROL0, MC13783_CLK32KMCUEN);
+ }
}
int fmradio_i2c_write(unsigned char address, const unsigned char* buf, int count)
@@ -52,3 +121,7 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
return 0;
}
+int si4700_st(void)
+{
+ return (GPIO1_DR & (1 << 28)) >> 28;
+}
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
index 727b38bd96..b29d3cd0fb 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
@@ -98,37 +98,24 @@ bool ide_powered(void)
}
#if CONFIG_TUNER
+static bool tuner_on = false;
+
bool tuner_power(bool status)
{
- static bool tuner_powered = false;
-
- if (status == tuner_powered)
- return status;
-
- tuner_powered = status;
-
- if (status)
- {
- /* the si4700 is the only thing connected to i2c2 so
- we can diable the i2c module when not in use */
- fmradio_i2c_enable(true);
- /* enable the fm chip */
- imx31_regset32(&GPIO1_DR, (1 << 26));
- /* enable CLK32KMCU clock */
- mc13783_set(MC13783_POWER_CONTROL0, MC13783_CLK32KMCUEN);
- }
- else
+ if (status != tuner_on)
{
- /* the si4700 is the only thing connected to i2c2 so
- we can diable the i2c module when not in use */
- fmradio_i2c_enable(false);
- /* disable the fm chip */
- imx31_regclr32(&GPIO1_DR, (1 << 26));
- /* disable CLK32KMCU clock */
- mc13783_clear(MC13783_POWER_CONTROL0, MC13783_CLK32KMCUEN);
+ tuner_on = status;
+ /* Handle power and pin setup */
+ fmradio_i2c_enable(status);
+ status = !status;
}
- return !status;
+ return status;
+}
+
+bool tuner_powered(void)
+{
+ return tuner_on;
}
#endif /* #if CONFIG_TUNER */
@@ -151,6 +138,10 @@ void power_off(void)
void power_init(void)
{
+#if CONFIG_TUNER
+ fmradio_i2c_init();
+#endif
+
/* Poll initial state */
charger_main_detect_event();
diff --git a/firmware/tuner.c b/firmware/tuner.c
index c694031c45..c5da27079c 100644
--- a/firmware/tuner.c
+++ b/firmware/tuner.c
@@ -64,7 +64,7 @@ const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS] =
{
[REGION_EUROPE] = { 1, 0, 2 }, /* 50uS, US/Europe band, 50kHz spacing */
[REGION_US_CANADA] = { 0, 0, 0 }, /* 75uS, US/Europe band, 200kHz spacing */
- [REGION_JAPAN] = { 1, 1, 1 }, /* 50uS, Japanese band, 100kHz spacing */
+ [REGION_JAPAN] = { 1, 2, 1 }, /* 50uS, Japanese band, 100kHz spacing */
[REGION_KOREA] = { 1, 0, 1 }, /* 50uS, US/Europe band, 100kHz spacing */
};
#endif /* (CONFIG_TUNER & SI4700) */