Philips tuner supported


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5300 a1c6a512-1295-4272-9138-f99709370657
diff --git a/firmware/drivers/fmradio_i2c.c b/firmware/drivers/fmradio_i2c.c
index 93c5d2f..d503980 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<count; i++)
+        {
+            fmradio_i2c_outb(buf[i]);
+            if (!fmradio_i2c_getack())
+            {
+                x=-2;
+                break;
+            }
+        }
+    }
+    else
+    {
+        debugf("fmradio_i2c_write() - no ack\n");
+        x=-1;
+    }
+    fmradio_i2c_stop();
+    return x;
+}
+
+int fmradio_i2c_read(int address, unsigned char* buf, int count)
+{
+    int i,x=0;
+    
+    fmradio_i2c_start();
+    fmradio_i2c_outb(address | 1);
+    if (fmradio_i2c_getack()) {
+        for (i=0; i<count; i++) {
+            buf[i] = fmradio_i2c_inb(0);
+        }
+    }
+    else
+        x=-1;
+    fmradio_i2c_stop();
+    return x;
 }
 
 #endif
diff --git a/firmware/export/fmradio_i2c.h b/firmware/export/fmradio_i2c.h
index 06b7f9f..b7007a8 100644
--- a/firmware/export/fmradio_i2c.h
+++ b/firmware/export/fmradio_i2c.h
@@ -20,7 +20,7 @@
 #ifndef FMRADIO_I2C_H
 #define FMRADIO_I2C_H
 
-void fmradio_i2c_read(unsigned char* p_data); /* reads 5 byte */
-void fmradio_i2c_set(const unsigned char* p_data); /* writes 5 bytes */
+int fmradio_i2c_write(int address, const unsigned char* buf, int count);
+int fmradio_i2c_read(int address, unsigned char* buf, int count);
 
 #endif
diff --git a/firmware/tuner_philips.c b/firmware/tuner_philips.c
index 50559af..e63d063 100644
--- a/firmware/tuner_philips.c
+++ b/firmware/tuner_philips.c
@@ -19,10 +19,12 @@
  ****************************************************************************/
 
 #include <stdbool.h>
+#include <string.h>
 #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 @@
     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 @@
             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 @@
 /* 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;