From 1fea6f6b22af74e904d918fff4cebec345529f4b Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 27 Dec 2008 19:18:50 +0000 Subject: Make si4700 tuner driver more sane with bit and field defines and entirely hide strange i2c interface from code with write/set/clear/masked functionality. On Gigabeat S use by-the-book busmode selection and GPIO lines. Implement some primitive station detection, debug registers in screen, and misc. changes to tie things together. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19600 a1c6a512-1295-4272-9138-f99709370657 --- apps/debug_menu.c | 28 +- firmware/drivers/tuner/si4700.c | 418 ++++++++++++++++----- firmware/export/imx31l.h | 100 ++--- firmware/export/power.h | 1 + firmware/export/si4700.h | 6 + firmware/target/arm/as3525/power-as3525.c | 16 +- .../arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c | 75 +++- firmware/target/arm/imx31/gigabeat-s/power-imx31.c | 43 +-- firmware/tuner.c | 2 +- 9 files changed, 513 insertions(+), 176 deletions(-) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index f390cced85..9be36df046 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -2293,12 +2293,12 @@ static int radio_callback(int btn, struct gui_synclist *lists) "if_set: %d Hz", lv24020lp_get(LV24020LP_IF_SET) ); simplelist_addline(SIMPLELIST_ADD_LINE, "sd_set: %d Hz", lv24020lp_get(LV24020LP_SD_SET) ); -#endif +#endif /* LV24020LP */ #if (CONFIG_TUNER & S1A0903X01) simplelist_addline(SIMPLELIST_ADD_LINE, "Samsung regs: %08X", s1a0903x01_get(RADIO_ALL)); /* This one doesn't return dynamic data atm */ -#endif +#endif /* S1A0903X01 */ #if (CONFIG_TUNER & TEA5767) struct tea5767_dbg_info nfo; tea5767_dbg_info(&nfo); @@ -2313,7 +2313,29 @@ static int radio_callback(int btn, struct gui_synclist *lists) (unsigned)nfo.write_regs[0], (unsigned)nfo.write_regs[1], (unsigned)nfo.write_regs[2], (unsigned)nfo.write_regs[3], (unsigned)nfo.write_regs[4]); -#endif +#endif /* TEA5767 */ +#if (CONFIG_TUNER & SI4700) + struct si4700_dbg_info nfo; + si4700_dbg_info(&nfo); + simplelist_addline(SIMPLELIST_ADD_LINE, "SI4700 regs:"); + /* Registers */ + simplelist_addline(SIMPLELIST_ADD_LINE, + " %04X %04X %04X %04X", + (unsigned)nfo.regs[0], (unsigned)nfo.regs[1], + (unsigned)nfo.regs[2], (unsigned)nfo.regs[3]); + simplelist_addline(SIMPLELIST_ADD_LINE, + " %04X %04X %04X %04X", + (unsigned)nfo.regs[4], (unsigned)nfo.regs[5], + (unsigned)nfo.regs[6], (unsigned)nfo.regs[7]); + simplelist_addline(SIMPLELIST_ADD_LINE, + " %04X %04X %04X %04X", + (unsigned)nfo.regs[8], (unsigned)nfo.regs[9], + (unsigned)nfo.regs[10], (unsigned)nfo.regs[11]); + simplelist_addline(SIMPLELIST_ADD_LINE, + " %04X %04X %04X %04X", + (unsigned)nfo.regs[12], (unsigned)nfo.regs[13], + (unsigned)nfo.regs[14], (unsigned)nfo.regs[15]); +#endif /* SI4700 */ return ACTION_REDRAW; } static bool dbg_fm_radio(void) 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 #include #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 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) */ -- cgit v1.2.3