FS#8046: H10 FM tuner support. Thanks to  Przemyslaw Holubowski for doing the hard work in figuring out how to communicate with the tuner.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15578 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/keymaps/keymap-h10.c b/apps/keymaps/keymap-h10.c
index 3e90f15..883b0d3 100644
--- a/apps/keymaps/keymap-h10.c
+++ b/apps/keymaps/keymap-h10.c
@@ -305,6 +305,26 @@
     LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
 }; /* button_context_recscreen */
 
+static const struct button_mapping button_context_radio[]  = {
+    { ACTION_FM_PRESET,      BUTTON_RIGHT | BUTTON_REL,             BUTTON_RIGHT },
+    { ACTION_FM_MENU,        BUTTON_RIGHT | BUTTON_REPEAT,          BUTTON_NONE },
+    { ACTION_FM_MODE,        BUTTON_LEFT,                           BUTTON_NONE },
+    { ACTION_FM_STOP,        BUTTON_PLAY | BUTTON_REPEAT,           BUTTON_PLAY },
+    { ACTION_FM_EXIT,        BUTTON_POWER,                          BUTTON_NONE },
+    { ACTION_FM_PLAY,        BUTTON_PLAY | BUTTON_REL,              BUTTON_PLAY },
+    { ACTION_SETTINGS_INC,       BUTTON_SCROLL_UP,                  BUTTON_NONE },
+    { ACTION_SETTINGS_INCREPEAT, BUTTON_SCROLL_UP|BUTTON_REPEAT,    BUTTON_NONE },
+    { ACTION_SETTINGS_DEC,       BUTTON_SCROLL_DOWN,                BUTTON_NONE },
+    { ACTION_SETTINGS_DECREPEAT, BUTTON_SCROLL_DOWN|BUTTON_REPEAT,  BUTTON_NONE },
+    { ACTION_STD_PREV,       BUTTON_REW,                            BUTTON_NONE },
+    { ACTION_STD_PREVREPEAT, BUTTON_REW|BUTTON_REPEAT,              BUTTON_NONE },
+    { ACTION_STD_NEXT,       BUTTON_FF,                             BUTTON_NONE },
+    { ACTION_STD_NEXTREPEAT, BUTTON_FF|BUTTON_REPEAT,               BUTTON_NONE },
+
+    LAST_ITEM_IN_LIST
+    
+};
+
 static const struct button_mapping* get_context_mapping_remote( int context )
 {
     context ^= CONTEXT_REMOTE;
@@ -385,7 +405,8 @@
             return button_context_keyboard;
         case CONTEXT_RECSCREEN:
             return button_context_recscreen;
-
+        case CONTEXT_FM:
+            return button_context_radio;
         default:
             return button_context_standard;
     } 
diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c
index a0cc10c..7a45027 100644
--- a/apps/recorder/radio.c
+++ b/apps/recorder/radio.c
@@ -81,6 +81,10 @@
 #define FM_NEXT_PRESET
 #define FM_PREV_PRESET
 
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define FM_PRESET
+#define FM_MODE
+
 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
 #define FM_PRESET
 #define FM_MODE
diff --git a/firmware/SOURCES b/firmware/SOURCES
index e78d74a..465d10e 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -557,6 +557,7 @@
 target/arm/iriver/h10/adc-h10.c
 target/arm/iriver/h10/backlight-h10.c
 target/arm/iriver/h10/button-h10.c
+target/arm/iriver/h10/fmradio_i2c-h10.c
 target/arm/iriver/h10/lcd-h10_20gb.c
 target/arm/iriver/h10/power-h10.c
 target/arm/iriver/h10/powermgmt-h10.c
@@ -573,6 +574,7 @@
 target/arm/iriver/h10/adc-h10.c
 target/arm/iriver/h10/backlight-h10.c
 target/arm/iriver/h10/button-h10.c
+target/arm/iriver/h10/fmradio_i2c-h10.c
 target/arm/iriver/h10/lcd-h10_5gb.c
 target/arm/iriver/h10/power-h10.c
 target/arm/iriver/h10/powermgmt-h10.c
diff --git a/firmware/drivers/audio/wm8731.c b/firmware/drivers/audio/wm8731.c
index 785cb2a..44aeefe 100644
--- a/firmware/drivers/audio/wm8731.c
+++ b/firmware/drivers/audio/wm8731.c
@@ -319,6 +319,15 @@
 
 void audiohw_set_monitor(int enable)
 {
-    /* TODO: Implement for FM monitoring */
-    (void)enable;
+    if(enable)
+    {
+        wm8731_regs[AAPCTRL] |=  AAPCTRL_BYPASS;
+        wm8731_regs[AAPCTRL] &=~ (AAPCTRL_DACSEL | AAPCTRL_SIDETONE);
+        wm8731_write(AAPCTRL, wm8731_regs[AAPCTRL]);
+    }
+    else {
+        wm8731_regs[AAPCTRL] &=~ AAPCTRL_BYPASS;
+        wm8731_regs[AAPCTRL] |=  AAPCTRL_DACSEL | AAPCTRL_SIDETONE;
+        wm8731_write(AAPCTRL, wm8731_regs[AAPCTRL]);
+    }
 }
diff --git a/firmware/export/config-h10.h b/firmware/export/config-h10.h
index 039ebc5..e1e0b54 100644
--- a/firmware/export/config-h10.h
+++ b/firmware/export/config-h10.h
@@ -12,7 +12,7 @@
 #define HAVE_RECORDING
 /* Define bitmask of input sources - recordable bitmask can be defined
    explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
+#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
 
 /* define the bitmask of hardware sample rates */
 #define HW_SAMPR_CAPS   (SAMPR_CAP_44)
@@ -88,8 +88,8 @@
 #define AB_REPEAT_ENABLE 1
 
 /* FM Tuner */
-/*#define CONFIG_TUNER TEA5767
-#define CONFIG_TUNER_XTAL  32768 *//* TODO: what is this? */
+#define CONFIG_TUNER TEA5767
+#define CONFIG_TUNER_XTAL  32768
 
 /* Define this for LCD backlight available */
 #define HAVE_BACKLIGHT
diff --git a/firmware/export/config-h10_5gb.h b/firmware/export/config-h10_5gb.h
index 3d10af2..b719522 100644
--- a/firmware/export/config-h10_5gb.h
+++ b/firmware/export/config-h10_5gb.h
@@ -12,7 +12,7 @@
 #define HAVE_RECORDING
 /* Define bitmask of input sources - recordable bitmask can be defined
    explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN)
+#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
 
 /* define the bitmask of hardware sample rates */
 #define HW_SAMPR_CAPS   (SAMPR_CAP_44)
@@ -74,8 +74,8 @@
 #define AB_REPEAT_ENABLE 1
 
 /* FM Tuner */
-/*#define CONFIG_TUNER TEA5767
-#define CONFIG_TUNER_XTAL  32768 *//* TODO: what is this? */
+#define CONFIG_TUNER TEA5767
+#define CONFIG_TUNER_XTAL  32768
 
 /* Define this for LCD backlight available */
 #define HAVE_BACKLIGHT
diff --git a/firmware/target/arm/audio-pp.c b/firmware/target/arm/audio-pp.c
index 9fff197..783342e 100644
--- a/firmware/target/arm/audio-pp.c
+++ b/firmware/target/arm/audio-pp.c
@@ -35,6 +35,7 @@
     /* Prevent pops from unneeded switching */
     static int last_source = AUDIO_SRC_PLAYBACK;
 #ifdef HAVE_FMRADIO_REC
+    bool recording = flags & SRCF_RECORDING;
     static bool last_recording = false;
 #endif
 
@@ -62,6 +63,10 @@
 #endif
 #ifdef HAVE_LINEIN_REC
         case AUDIO_SRC_LINEIN:          /* recording only */
+#if defined(IRIVER_H10) || defined(IRIVER_H10_5GB)
+            /* Switch line in source to line-in */
+            GPIO_SET_BITWISE(GPIOB_OUTPUT_VAL, 0x04);
+#endif
             if (source != last_source)
             {
                 audiohw_enable_recording(false); /* source line */
@@ -71,17 +76,20 @@
 #endif
 #ifdef HAVE_FMRADIO_REC
         case AUDIO_SRC_FMRADIO:         /* recording and playback */
+#if defined(IRIVER_H10) || defined(IRIVER_H10_5GB)
+            /* Switch line in source to tuner */
+            GPIO_CLEAR_BITWISE(GPIOB_OUTPUT_VAL, 0x04);
+#endif            /* Set line-in vol to 0dB*/
             if (!recording)
-                audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN);
+                audiohw_set_recvol(0x17, 0x17, AUDIO_GAIN_LINEIN);
 
             if (source == last_source && recording == last_recording)
                 break;
 
             last_recording = recording;
 
-            /* I2S recording and playback */
-            audiohw_enable_recording(false);    /* source line */
-            audiohw_set_monitor(!recording);
+            audiohw_enable_recording(false);    /* select line-in source */
+            audiohw_set_monitor(!recording);    /* enable bypass mode */
         break;
 #endif
     } /* end switch */
diff --git a/firmware/target/arm/iriver/h10/fmradio_i2c-h10.c b/firmware/target/arm/iriver/h10/fmradio_i2c-h10.c
new file mode 100644
index 0000000..f3d3075
--- /dev/null
+++ b/firmware/target/arm/iriver/h10/fmradio_i2c-h10.c
@@ -0,0 +1,206 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ * Physical interface of the Philips TEA5767 in iriver H10 series
+ *
+ * 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include "cpu.h"
+#include "logf.h"
+#include "system.h"
+
+/* cute little functions, atomic read-modify-write */
+
+#define SDA_OUTINIT GPIO_CLEAR_BITWISE(GPIOD_OUTPUT_VAL, 0x08)
+#define SDA_HI_IN   GPIO_CLEAR_BITWISE(GPIOD_OUTPUT_EN, 0x08)
+#define SDA_LO_OUT  GPIO_SET_BITWISE(GPIOD_OUTPUT_EN, 0x08)
+#define SDA         (GPIOD_INPUT_VAL & 0x08)
+
+#define SCL_INPUT   GPIO_CLEAR_BITWISE(GPIOD_OUTPUT_EN, 0x10)
+#define SCL_OUTPUT  GPIO_SET_BITWISE(GPIOD_OUTPUT_EN, 0x10)
+#define SCL_LO      GPIO_CLEAR_BITWISE(GPIOD_OUTPUT_VAL, 0x10)
+#define SCL_HI      GPIO_SET_BITWISE(GPIOD_OUTPUT_VAL,0x10)
+#define SCL         (GPIOD_INPUT_VAL & 0x10)
+
+#define DELAY   udelay(2)
+
+static void fmradio_i2c_start(void)
+{
+    SCL_HI;
+    SCL_OUTPUT;
+    SDA_HI_IN;
+    SDA_OUTINIT;
+    DELAY;
+    SDA_LO_OUT;
+    DELAY;
+    SCL_LO;
+}
+
+static void fmradio_i2c_stop(void)
+{
+   SDA_LO_OUT;
+   DELAY;
+   SCL_HI;
+   DELAY;
+   SDA_HI_IN;
+}
+
+/* Generate ACK or NACK */
+static void fmradio_i2c_ack(bool nack)
+{
+    /* 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.
+
+       In their infinite wisdom, iriver didn't pull up the SCL line, so
+       we have to drive the SCL high repeatedly to simulate a pullup. */
+    
+    if (nack)
+        SDA_HI_IN;
+    else
+        SDA_LO_OUT;
+    DELAY;
+
+    SCL_HI;
+    do
+    {
+        SCL_OUTPUT;  /* Set the clock to output */
+        SCL_INPUT;   /* Set the clock to input */
+        DELAY;
+    }
+    while(!SCL);  /* and wait for the slave to release it */
+
+    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.
+
+       In their infinite wisdom, iriver didn't pull up the SCL line, so
+       we have to drive the SCL high repeatedly to simulate a pullup. */
+
+    SDA_HI_IN;
+    DELAY;
+
+    SCL_HI;          /* set clock to high */
+    do
+    {
+        SCL_OUTPUT;  /* Set the clock to output */
+        SCL_INPUT;   /* Set the clock to input */
+        DELAY;
+    }
+    while(!SCL);     /* and wait for the slave to release it */
+
+    if (SDA)
+        ret = 0;    /* ack failed */
+    
+    SCL_OUTPUT;
+    SCL_LO;
+
+    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_IN;
+      else
+         SDA_LO_OUT;
+      DELAY;
+      SCL_HI;
+      DELAY;
+      SCL_LO;
+   }
+}
+
+static unsigned char fmradio_i2c_inb(void)
+{
+   int i;
+   unsigned char byte = 0;
+
+   SDA_HI_IN;
+   /* clock in each bit, MSB first */
+   for ( i=0x80; i; i>>=1 ) {
+       DELAY;
+       SCL_HI;
+       DELAY;
+       if ( SDA )
+           byte |= i;
+       SCL_LO;
+   }
+
+   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
+    {
+        logf("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=count; i>0; i--)
+        {
+            *buf++ = fmradio_i2c_inb();
+            fmradio_i2c_ack(i == 1);
+        }
+    }
+    else
+        x=-1;
+    fmradio_i2c_stop();
+    return x;
+}
diff --git a/firmware/target/arm/iriver/h10/power-h10.c b/firmware/target/arm/iriver/h10/power-h10.c
index 7f3fb40..f4d5be4 100644
--- a/firmware/target/arm/iriver/h10/power-h10.c
+++ b/firmware/target/arm/iriver/h10/power-h10.c
@@ -35,7 +35,6 @@
 bool charger_enabled;
 #endif
 
-#if 0
 #if CONFIG_TUNER
 
 bool tuner_power(bool status)
@@ -46,7 +45,6 @@
 }
 
 #endif /* #if CONFIG_TUNER */
-#endif
 
 void power_init(void)
 {