diff --git a/firmware/drivers/fmradio_i2c.c b/firmware/drivers/fmradio_i2c.c index 93c5d2fa91..d503980391 100644 --- a/firmware/drivers/fmradio_i2c.c +++ b/firmware/drivers/fmradio_i2c.c @@ -8,7 +8,7 @@ * $Id$ * Physical interface of the Philips TEA5767 in Archos Ondio * - * Copyright (C) 2004 by Jörg Hohensohn + * Copyright (C) 2002 by Linus Nielsen Feltzing * * 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. @@ -26,16 +26,181 @@ #ifdef CONFIG_TUNER -/* reads 5 byte */ -void fmradio_i2c_read(unsigned char* p_data) + +/* cute little functions, atomic read-modify-write */ +/* SDA is PB4 */ +#define SDA_LO and_b(~0x10, &PBDRL) +#define SDA_HI or_b(0x10, &PBDRL) +#define SDA_INPUT and_b(~0x10, &PBIORL) +#define SDA_OUTPUT or_b(0x10, &PBIORL) +#define SDA (PBDR & 0x0010) + +/* SCL is PB1 */ +#define SCL_INPUT and_b(~0x02, &PBIORL) +#define SCL_OUTPUT or_b(0x02, &PBIORL) +#define SCL_LO and_b(~0x02, &PBDRL) +#define SCL_HI or_b(0x02, &PBDRL) +#define SCL (PBDR & 0x0002) + +/* arbitrary delay loop */ +#define DELAY do { int _x; for(_x=0;_x<20;_x++);} while (0) + + +static void fmradio_i2c_start(void) { - (void)p_data; + SDA_OUTPUT; + SDA_HI; + SCL_HI; + SDA_LO; + DELAY; + SCL_LO; } -/* writes 5 bytes */ -void fmradio_i2c_set(const unsigned char* p_data) +static void fmradio_i2c_stop(void) { - (void)p_data; + SDA_LO; + SCL_HI; + DELAY; + SDA_HI; +} + + +static void fmradio_i2c_ack(int bit) +{ + /* Here's the deal. The slave is slow, and sometimes needs to wait + before it can receive the acknowledge. Therefore it forces the clock + low until it is ready. We need to poll the clock line until it goes + high before we release the ack. */ + + SCL_LO; /* Set the clock low */ + if ( bit ) + { + SDA_HI; + } + else + { + SDA_LO; + } + + SCL_INPUT; /* Set the clock to input */ + while(!SCL) /* and wait for the slave to release it */ + sleep_thread(); + wake_up_thread(); + + DELAY; + SCL_OUTPUT; + SCL_LO; +} + +static int fmradio_i2c_getack(void) +{ + int ret = 1; + + /* Here's the deal. The slave is slow, and sometimes needs to wait + before it can send the acknowledge. Therefore it forces the clock + low until it is ready. We need to poll the clock line until it goes + high before we read the ack. */ + + SDA_INPUT; /* And set to input */ + SCL_INPUT; /* Set the clock to input */ + while(!SCL) /* and wait for the slave to release it */ + sleep_thread(); + wake_up_thread(); + + if (SDA) + /* ack failed */ + ret = 0; + + SCL_OUTPUT; + SCL_LO; + SDA_HI; + SDA_OUTPUT; + return ret; +} + +static void fmradio_i2c_outb(unsigned char byte) +{ + int i; + + /* clock out each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + if ( i & byte ) + { + SDA_HI; + } + else + { + SDA_LO; + } + SCL_HI; + SCL_LO; + } + + SDA_HI; +} + +static unsigned char fmradio_i2c_inb(int ack) +{ + int i; + unsigned char byte = 0; + + /* clock in each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + SDA_INPUT; /* And set to input */ + SCL_HI; + if ( SDA ) + byte |= i; + SCL_LO; + SDA_OUTPUT; + } + + fmradio_i2c_ack(ack); + + return byte; +} + +int fmradio_i2c_write(int address, const unsigned char* buf, int count) +{ + int i,x=0; + + fmradio_i2c_start(); + fmradio_i2c_outb(address & 0xfe); + if (fmradio_i2c_getack()) + { + for (i=0; i +#include #include "tuner.h" /* tuner abstraction interface */ #include "fmradio_i2c.h" /* physical interface driver */ -/* FIXME: this is just a dummy */ +#define I2C_ADR 0xC0 +static unsigned char write_bytes[5]; /* tuner abstraction layer: set something to the tuner */ void philips_set(int setting, int value) @@ -31,12 +33,22 @@ void philips_set(int setting, int value) switch(setting) { case RADIO_INIT: + memset(write_bytes, 0, sizeof(write_bytes)); break; case RADIO_FREQUENCY: + { + int n; + n = (4 * (value - 225000)) / 50000; + write_bytes[0] = (write_bytes[0] & 0xC0) | (n >> 8); + write_bytes[1] = n & 0xFF; + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + } break; case RADIO_MUTE: + write_bytes[0] = (write_bytes[0] & 0x7F) | (value ? 0x80 : 0); + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); break; case RADIO_IF_MEASUREMENT: @@ -46,6 +58,8 @@ void philips_set(int setting, int value) break; case RADIO_FORCE_MONO: + write_bytes[2] = (write_bytes[2] & 0xF7) | (value ? 0x08 : 0); + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); break; } } @@ -53,17 +67,24 @@ void philips_set(int setting, int value) /* tuner abstraction layer: read something from the tuner */ int philips_get(int setting) { + unsigned char read_bytes[5]; int val = -1; + + fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); + switch(setting) { case RADIO_PRESENT: - val = 0; /* false */ + val = 1; /* true */ break; case RADIO_IF_MEASURED: + val = read_bytes[2] & 0x7F; + val = 1070 + (val-55)/2; break; case RADIO_STEREO: + val = read_bytes[2] >> 7; break; } return val;