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