1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Michael Sevakis 2008-12-27 19:18:50 +00:00
parent d6bae6c858
commit 1fea6f6b22
9 changed files with 510 additions and 173 deletions

View file

@ -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)

View file

@ -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
#define RDSA 0xC /* x */
#define RDSB 0xD /* x */
#define RDSC 0xE /* x */
#define RDSD 0xF /* x */
/* 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
/* 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));
}
}

View file

@ -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_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_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_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 */
#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_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)
#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))

View file

@ -95,6 +95,7 @@ bool spdif_powered(void);
#if CONFIG_TUNER
bool tuner_power(bool status);
bool tuner_powered(void);
#endif
#endif /* _POWER_H_ */

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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)
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(true);
/* enable the fm chip */
imx31_regset32(&GPIO1_DR, (1 << 26));
/* enable CLK32KMCU clock */
mc13783_set(MC13783_POWER_CONTROL0, MC13783_CLK32KMCUEN);
}
else
{
/* 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();

View file

@ -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) */