summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBertrik Sikken <bertrik@sikken.nl>2011-12-17 20:24:19 +0000
committerBertrik Sikken <bertrik@sikken.nl>2011-12-17 20:24:19 +0000
commit8c19dcd598144d028ff1647d850d3a17483e6b9c (patch)
treeb1baea8ffcf26bedf0dea800c62ad33df9fa18f0
parent17ed3253fc98bcca59d70531a4d81b3be75dc7ea (diff)
downloadrockbox-8c19dcd598144d028ff1647d850d3a17483e6b9c.tar.gz
rockbox-8c19dcd598144d028ff1647d850d3a17483e6b9c.zip
FS#12370: Initial RDS support for Si4701/Si4703 tuner (beast and clip zip)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31346 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c9
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/drivers/rds.c192
-rw-r--r--firmware/drivers/tuner/si4700.c164
-rw-r--r--firmware/export/config/gigabeats.h4
-rw-r--r--firmware/export/config/sansaclipzip.h5
-rw-r--r--firmware/export/rds.h33
-rw-r--r--firmware/export/si4700.h14
-rw-r--r--firmware/target/arm/as3525/fmradio-i2c-as3525.c59
-rw-r--r--firmware/target/arm/as3525/system-as3525.c4
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c56
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/gpio-target.h10
13 files changed, 525 insertions, 37 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 79a0752c4a..e0343572b7 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1735,6 +1735,15 @@ static int radio_callback(int btn, struct gui_synclist *lists)
simplelist_addline(SIMPLELIST_ADD_LINE,"%02X: %04X %04X %04X %04X",
i, nfo.regs[i], nfo.regs[i+1], nfo.regs[i+2], nfo.regs[i+3]);
}
+#ifdef HAVE_RDS_CAP
+ simplelist_addline(SIMPLELIST_ADD_LINE, "");
+ simplelist_addline(SIMPLELIST_ADD_LINE, "RDS Info:");
+ simplelist_addline(SIMPLELIST_ADD_LINE,
+ si4700_get_rds_info(RADIO_RDS_NAME));
+
+ simplelist_addline(SIMPLELIST_ADD_LINE,
+ si4700_get_rds_info(RADIO_RDS_TEXT));
+#endif
}
#endif /* SI4700 */
#if (CONFIG_TUNER & RDA5802)
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 85e6fe3757..b4e9c5ca2b 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -302,6 +302,9 @@ drivers/tuner/ipod_remote_tuner.c
#if (CONFIG_TUNER & RDA5802)
drivers/tuner/rda5802.c
#endif /* (CONFIG_TUNER & RDA5802) */
+#if defined(HAVE_RDS_CAP)
+drivers/rds.c
+#endif /* HAVE_RDS_CAP */
#endif /* PLATFORM_NATIVE */
#endif /* CONFIG_TUNER */
#endif /* BOOTLOADER */
diff --git a/firmware/drivers/rds.c b/firmware/drivers/rds.c
new file mode 100644
index 0000000000..09bc33807c
--- /dev/null
+++ b/firmware/drivers/rds.c
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2011 by Bertrik Sikken
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <strlcpy.h>
+#include "rds.h"
+
+/* programme identification */
+static uint16_t pi;
+/* program service name */
+static char ps_data[9];
+static char ps_copy[9];
+static int ps_segment;
+/* radio text */
+static char rt_data[65];
+static char rt_copy[65];
+static int rt_segment;
+static int rt_abflag;
+
+/* resets the rds parser */
+void rds_reset(void)
+{
+ ps_copy[0] = '\0';
+ ps_segment = 0;
+ rt_copy[0] = '\0';
+ rt_segment = 0;
+ pi = 0;
+}
+
+/* initialises the rds parser */
+void rds_init(void)
+{
+ rds_reset();
+}
+
+/* handles a group 0 packet, returns true if a new message was received */
+static bool handle_group0(uint16_t data[4])
+{
+ int segment, pos;
+
+ segment = data[1] & 3;
+
+ /* reset parsing if not in expected order */
+ if (segment != ps_segment) {
+ ps_segment = 0;
+ if (segment != 0) {
+ return false;
+ }
+ }
+
+ /* store data */
+ pos = segment * 2;
+ ps_data[pos++] = (data[3] >> 8) & 0xFF;
+ ps_data[pos++] = (data[3] >> 0) & 0xFF;
+ if (++ps_segment == 4) {
+ ps_data[pos] = '\0';
+ if (strcmp(ps_copy, ps_data) != 0) {
+ /* we got an updated message */
+ strcpy(ps_copy, ps_data);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* handles a radio text characters, returns true if end-of-line found */
+static bool handle_rt(int pos, char c)
+{
+ switch (c) {
+ case 0x0A:
+ /* line break hint */
+ rt_data[pos] = ' ';
+ return false;
+ case 0x0D:
+ /* end of line */
+ rt_data[pos] = '\0';
+ return true;
+ default:
+ rt_data[pos] = c;
+ return false;
+ }
+}
+
+/* handles a group 2 packet, returns true if a new message was received */
+static bool handle_group2(uint16_t data[4])
+{
+ int abflag, segment, version, pos;
+ bool done;
+
+ /* reset parsing if not in expected order */
+ abflag = (data[1] >> 4) & 1;
+ segment = data[1] & 0xF;
+ if ((abflag != rt_abflag) || (segment != rt_segment)) {
+ rt_abflag = abflag;
+ rt_segment = 0;
+ if (segment != 0) {
+ return false;
+ }
+ }
+
+ /* store data */
+ version = (data[1] >> 11) & 1;
+ done = false;
+ if (version == 0) {
+ pos = segment * 4;
+ done = done || handle_rt(pos++, (data[2] >> 8) & 0xFF);
+ done = done || handle_rt(pos++, (data[2] >> 0) & 0xFF);
+ done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF);
+ done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF);
+ } else {
+ pos = segment * 2;
+ done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF);
+ done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF);
+ }
+ if ((++rt_segment == 16) || done) {
+ rt_data[pos] = '\0';
+ if (strcmp(rt_copy, rt_data) != 0) {
+ /* we got an updated message */
+ strcpy(rt_copy, rt_data);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* processes one rds packet, returns true if a new message was received */
+bool rds_process(uint16_t data[4])
+{
+ int group;
+
+ /* get programme identification */
+ if (pi == 0) {
+ pi = data[0];
+ }
+
+ /* handle rds data based on group */
+ group = (data[1] >> 11) & 0x1F;
+ switch (group) {
+
+ case 0: /* group 0A: basic info */
+ case 1: /* group 0B: basic info */
+ return handle_group0(data);
+
+ case 4: /* group 2A: radio text */
+ case 5: /* group 2B: radio text */
+ return handle_group2(data);
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/* returns the programme identification code */
+uint16_t rds_get_pi(void)
+{
+ return pi;
+}
+
+/* returns the most recent valid programme service name */
+char* rds_get_ps(void)
+{
+ return ps_copy;
+}
+
+/* returns the most recent valid RadioText message */
+char* rds_get_rt(void)
+{
+ return rt_copy;
+}
+
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index 848d25eeb0..bebbd0c881 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -29,21 +29,15 @@
#include "tuner.h" /* tuner abstraction interface */
#include "fmradio.h"
#include "fmradio_i2c.h" /* physical interface driver */
+#include "rds.h"
-/* 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) || defined(SANSA_C200V2)
-#define USE_INTERNAL_OSCILLATOR
+/* some models use the internal 32 kHz oscillator which needs special attention
+ during initialisation, power-up and power-down. */
+#define SI4700_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
+/* gigabeat S uses the GPIO for stereo/mono detection */
+#define SI4700_USE_MO_ST_I
#endif
#define SEEK_THRESHOLD 0x16
@@ -81,7 +75,8 @@ extern int si4700_st(void);
/* CHIPID (0x1) */
-#if 0 /* Informational */
+#if 0
+/* Informational */
/* Si4700/01 */
#define CHIPID_REV (0x3f << 10)
#define CHIPID_DEV (0x1 << 9)
@@ -98,7 +93,10 @@ extern int si4700_st(void);
/* 1000 before PU = Si4703 */
/* 1001 after PU = Si4703 */
#define CHIPID_FIRMWARE (0x3f << 0)
-#endif /* 0 */
+#endif
+
+/* Indicates Si4701/2/3 after powerup */
+#define CHIPID_DEV_0 (0x1 << 9)
/* POWERCFG (0x2) */
#define POWERCFG_DSMUTE (0x1 << 15)
@@ -214,6 +212,10 @@ extern int si4700_st(void);
static bool tuner_present = false;
static uint16_t cache[16];
+static struct mutex fmr_mutex SHAREDBSS_ATTR;
+#ifdef HAVE_RDS_CAP
+static int rds_event = 0;
+#endif
/* reads <len> registers from radio at offset 0x0A into cache */
static void si4700_read(int len)
@@ -277,19 +279,26 @@ 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
+#ifndef SI4700_USE_MO_ST_I
/* Poll i2c for the stereo status */
-static inline int si4700_st(void)
+bool si4700_st(void)
{
return (si4700_read_reg(STATUSRSSI) & STATUSRSSI_ST) >> 8;
}
-#endif
+#endif /* ndef SI4700_USE_MO_ST_I */
static void si4700_sleep(int snooze)
{
if (snooze)
{
/** power down **/
+#ifdef HAVE_RDS_CAP
+ if (cache[CHIPID] & CHIPID_DEV_0) {
+ si4700_rds_powerup(false);
+ si4700_write_clear(SYSCONFIG1, SYSCONFIG1_RDS | SYSCONFIG1_RDSIEN);
+ }
+#endif
+
/* ENABLE high, DISABLE high */
si4700_write_set(POWERCFG,
POWERCFG_DISABLE | POWERCFG_ENABLE);
@@ -307,9 +316,8 @@ static void si4700_sleep(int snooze)
/* init register cache */
si4700_read(16);
-#if SI4700_GPIO_SETUP != 0
- si4700_write_masked(SYSCONFIG1, SI4700_GPIO_SETUP,
- SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 |
+#ifdef SI4700_USE_MO_ST_I
+ si4700_write_masked(SYSCONFIG1, SYSCONFIG1_GPIO3_MO_ST_I,
SYSCONFIG1_GPIO3);
#endif
/* set mono->stereo switching RSSI range to lowest setting */
@@ -320,33 +328,43 @@ static void si4700_sleep(int snooze)
SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) |
SYSCONFIG2_VOLUMEw(0xF),
SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH);
+
+#ifdef HAVE_RDS_CAP
+ /* enable RDS and RDS interrupt if supported (bit 9 of CHIPID) */
+ if (cache[CHIPID] & CHIPID_DEV_0) {
+ /* Is Si4701/2/3 - Enable RDS and interrupt */
+ si4700_write_set(SYSCONFIG1, SYSCONFIG1_RDS | SYSCONFIG1_RDSIEN);
+ si4700_write_masked(SYSCONFIG1, SYSCONFIG1_GPIO2_STC_RDS_I,
+ SYSCONFIG1_GPIO2);
+ si4700_rds_powerup(true);
+ }
+#endif
}
}
bool si4700_detect(void)
{
- bool detected;
-
- tuner_power(true);
- detected = (si4700_read_reg(DEVICEID) == 0x1242);
- tuner_power(false);
-
- return detected;
+ if (!tuner_present) {
+ tuner_power(true);
+ tuner_present = (si4700_read_reg(DEVICEID) == 0x1242);
+ tuner_power(false);
+ }
+ return tuner_present;
}
void si4700_init(void)
{
/* check device id */
if (si4700_detect()) {
- tuner_present = true;
-
+ mutex_init(&fmr_mutex);
+
tuner_power(true);
/* read all registers */
si4700_read(16);
si4700_sleep(0);
-#ifdef USE_INTERNAL_OSCILLATOR
+#ifdef SI4700_USE_INTERNAL_OSCILLATOR
/* Enable the internal oscillator
(Si4702-16 needs this register to be initialised to 0x100) */
si4700_write_set(TEST1, TEST1_XOSCEN | 0x100);
@@ -355,6 +373,10 @@ void si4700_init(void)
si4700_sleep(1);
tuner_power(false);
+
+#ifdef HAVE_RDS_CAP
+ si4700_rds_init();
+#endif
}
}
@@ -421,6 +443,10 @@ static void si4700_set_region(int region)
/* tuner abstraction layer: set something to the tuner */
int si4700_set(int setting, int value)
{
+ int val = 1;
+
+ mutex_lock(&fmr_mutex);
+
switch(setting)
{
case RADIO_SLEEP:
@@ -430,12 +456,19 @@ int si4700_set(int setting, int value)
break;
case RADIO_FREQUENCY:
+#ifdef HAVE_RDS_CAP
+ rds_reset();
+#endif
si4700_set_frequency(value);
break;
case RADIO_SCAN_FREQUENCY:
+#ifdef HAVE_RDS_CAP
+ rds_reset();
+#endif
si4700_set_frequency(value);
- return si4700_tuned();
+ val = si4700_tuned();
+ break;
case RADIO_MUTE:
si4700_write_masked(POWERCFG, value ? 0 : POWERCFG_DMUTE,
@@ -452,10 +485,13 @@ int si4700_set(int setting, int value)
break;
default:
- return -1;
+ val = -1;
+ break;
}
- return 1;
+ mutex_unlock(&fmr_mutex);
+
+ return val;
}
/* tuner abstraction layer: read something from the tuner */
@@ -463,6 +499,8 @@ int si4700_get(int setting)
{
int val = -1; /* default for unsupported query */
+ mutex_lock(&fmr_mutex);
+
switch(setting)
{
case RADIO_PRESENT:
@@ -488,8 +526,17 @@ int si4700_get(int setting)
case RADIO_RSSI_MAX:
val = RSSI_MAX;
break;
+
+#ifdef HAVE_RDS_CAP
+ case RADIO_EVENT:
+ val = rds_event;
+ rds_event = 0;
+ break;
+#endif
}
+ mutex_unlock(&fmr_mutex);
+
return val;
}
@@ -497,10 +544,61 @@ void si4700_dbg_info(struct si4700_dbg_info *nfo)
{
memset(nfo->regs, 0, sizeof (nfo->regs));
+ mutex_lock(&fmr_mutex);
+
if (tuner_powered())
{
si4700_read(16);
memcpy(nfo->regs, cache, sizeof (nfo->regs));
}
+
+ mutex_unlock(&fmr_mutex);
+}
+
+#ifdef HAVE_RDS_CAP
+/* Read raw RDS info for processing */
+bool si4700_rds_read_raw(uint16_t data[4])
+{
+ bool retval = false;
+
+ mutex_lock(&fmr_mutex);
+
+ if (tuner_powered())
+ {
+ si4700_read_reg(RDSD);
+ memcpy(data, &cache[RDSA], 4 * sizeof (uint16_t));
+ retval = true;
+ }
+
+ mutex_unlock(&fmr_mutex);
+
+ return retval;
+}
+
+/* Set the event flag */
+void si4700_rds_set_event(void)
+{
+ mutex_lock(&fmr_mutex);
+ rds_event = 1;
+ mutex_unlock(&fmr_mutex);
+}
+
+char * si4700_get_rds_info(int setting)
+{
+ char *text = NULL;
+
+ switch(setting)
+ {
+ case RADIO_RDS_NAME:
+ text = rds_get_ps();
+ break;
+
+ case RADIO_RDS_TEXT:
+ text = rds_get_rt();
+ break;
+ }
+
+ return text;
}
+#endif /* HAVE_RDS_CAP */
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h
index 0171dd8942..04e7b156b6 100644
--- a/firmware/export/config/gigabeats.h
+++ b/firmware/export/config/gigabeats.h
@@ -89,6 +89,8 @@
/* Define this if you have a SI4700 fm radio tuner */
#define CONFIG_TUNER SI4700
+#define HAVE_RDS_CAP
+
/* Define this if you have the WM8978 audio codec */
#define HAVE_WM8978
@@ -161,7 +163,7 @@
#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS)
/* Define this if target has an additional number of threads specific to it */
-#define TARGET_EXTRA_THREADS 2
+#define TARGET_EXTRA_THREADS 3
/* Type of mobile power - check this out */
#define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */
diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h
index 1eaa67fed0..1ff41a00ec 100644
--- a/firmware/export/config/sansaclipzip.h
+++ b/firmware/export/config/sansaclipzip.h
@@ -34,6 +34,8 @@
explicitly if different */
#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO)
+#define HAVE_RDS_CAP
+
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP
/* define this if you have a colour LCD */
@@ -138,6 +140,9 @@
/* define this if the flash memory uses the SecureDigital Memory Card protocol */
#define CONFIG_STORAGE STORAGE_SD
+/* Define this if target has an additional number of threads specific to it */
+#define TARGET_EXTRA_THREADS 1 /* RDS thread */
+
#define BATTERY_CAPACITY_DEFAULT 300 /* default battery capacity */
#define BATTERY_CAPACITY_MIN 300 /* min. capacity selectable */
#define BATTERY_CAPACITY_MAX 300 /* max. capacity selectable */
diff --git a/firmware/export/rds.h b/firmware/export/rds.h
new file mode 100644
index 0000000000..990f9b47f3
--- /dev/null
+++ b/firmware/export/rds.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2011 by Bertrik Sikken
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdint.h>
+#include <stdbool.h>
+
+void rds_init(void);
+
+void rds_reset(void);
+bool rds_process(uint16_t data[4]);
+
+uint16_t rds_get_pi(void);
+char* rds_get_ps(void);
+char* rds_get_rt(void);
+
+
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index 06a87c484f..761ad1ca24 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -39,10 +39,24 @@ 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);
+/* For interrupt-based mono/stereo indicator */
+bool si4700_st(void);
+
+/** RDS support **/
+void si4700_rds_init(void);
+/* Read raw RDS info for processing */
+bool si4700_rds_read_raw(uint16_t data[4]);
+/* Radio is fully powered up or about to be powered down */
+void si4700_rds_powerup(bool on);
+/* Obtain specified string */
+char* si4700_get_rds_info(int setting);
+/* Set the event flag */
+void si4700_rds_set_event(void);
#ifndef CONFIG_TUNER_MULTI
#define tuner_set si4700_set
#define tuner_get si4700_get
+#define tuner_get_rds_info si4700_get_rds_info
#endif
#endif /* _SI4700_H_ */
diff --git a/firmware/target/arm/as3525/fmradio-i2c-as3525.c b/firmware/target/arm/as3525/fmradio-i2c-as3525.c
index 1dbf2fde24..3da42e31b2 100644
--- a/firmware/target/arm/as3525/fmradio-i2c-as3525.c
+++ b/firmware/target/arm/as3525/fmradio-i2c-as3525.c
@@ -27,10 +27,14 @@
I2C with a couple of GPIO pins.
*/
+#include "config.h"
#include "as3525.h"
#include "system.h"
+#include "tuner.h"
#include "generic_i2c.h"
#include "fmradio_i2c.h"
+#include "thread.h"
+#include "rds.h"
#if defined(SANSA_CLIP) || defined(SANSA_C200V2)
#define I2C_SCL_GPIO(x) GPIOB_PIN(x)
@@ -179,3 +183,58 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
#endif
return ret;
}
+
+#ifdef HAVE_RDS_CAP
+/* Low-level RDS Support */
+static struct semaphore rds_sema;
+static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
+
+/* RDS GPIO interrupt handler */
+void tuner_isr(void)
+{
+ /* read and clear the interrupt */
+ if (GPIOA_MIS & (1<<4)) {
+ semaphore_release(&rds_sema);
+ }
+ GPIOA_IC = (1<<4);
+}
+
+/* Captures RDS data and processes it */
+static void NORETURN_ATTR rds_thread(void)
+{
+ uint16_t rds_data[4];
+
+ while (true) {
+ semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
+ if (si4700_rds_read_raw(rds_data) && rds_process(rds_data)) {
+ si4700_rds_set_event();
+ }
+ }
+}
+
+/* Called with on=true after full radio power up, and with on=false before
+ powering down */
+void si4700_rds_powerup(bool on)
+{
+ GPIOA_IE &= ~(1<<4); /* disable GPIO interrupt */
+
+ if (on) {
+ GPIOA_DIR &= ~(1<<4); /* input */
+ GPIOA_IS &= ~(1<<4); /* edge detect */
+ GPIOA_IBE &= ~(1<<4); /* only one edge */
+ GPIOA_IEV &= ~(1<<4); /* falling edge */
+ GPIOA_IC = (1<<4); /* clear any pending interrupt */
+ GPIOA_IE |= (1<<4); /* enable GPIO interrupt */
+ }
+}
+
+/* One-time RDS init at startup */
+void si4700_rds_init(void)
+{
+ semaphore_init(&rds_sema, 1, 0);
+ rds_init();
+ create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
+ IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
+}
+#endif /* HAVE_RDS_CAP */
+
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index 7e2e480eda..965030ecc3 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -155,6 +155,10 @@ void INT_GPIOA(void)
void button_gpioa_isr(void);
button_gpioa_isr();
#endif
+#ifdef HAVE_RDS_CAP
+ void tuner_isr(void);
+ tuner_isr();
+#endif
}
void irq_handler(void)
diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
index 39a2cdab04..e0457e367a 100644
--- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
@@ -23,8 +23,12 @@
#include "system.h"
#include "mc13783.h"
#include "iomuxc-imx31.h"
+#include "gpio-imx31.h"
#include "i2c-imx31.h"
#include "fmradio_i2c.h"
+#include "thread.h"
+#include "rds.h"
+#include "tuner.h"
static struct i2c_node si4700_i2c_node =
{
@@ -117,7 +121,57 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
return 0;
}
-int si4700_st(void)
+bool si4700_st(void)
{
return (GPIO1_DR & (1 << 28)) >> 28;
}
+
+
+/* Low-level RDS Support */
+static struct semaphore rds_sema;
+static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
+
+/* RDS GPIO interrupt handler */
+void si4700_stc_rds_event(void)
+{
+ /* read and clear the interrupt */
+ SI4700_GPIO_STC_RDS_ISR = (1ul << SI4700_GPIO_STC_RDS_LINE);
+ semaphore_release(&rds_sema);
+}
+
+/* Called with on=true after full radio power up, and with on=false before
+ powering down */
+void si4700_rds_powerup(bool on)
+{
+ gpio_disable_event(SI4700_STC_RDS_EVENT_ID);
+
+ if (on)
+ {
+ SI4700_GPIO_STC_RDS_ISR = (1ul << SI4700_GPIO_STC_RDS_LINE);
+ gpio_enable_event(SI4700_STC_RDS_EVENT_ID);
+ }
+}
+
+/* Captures RDS data and processes it */
+/* Use of a thread here is likely temporary */
+static void NORETURN_ATTR rds_thread(void)
+{
+ uint16_t rds_data[4];
+
+ while (1)
+ {
+ semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
+
+ if (si4700_rds_read_raw(rds_data) && rds_process(rds_data))
+ si4700_rds_set_event();
+ }
+}
+
+/* One-time RDS init at startup */
+void si4700_rds_init(void)
+{
+ semaphore_init(&rds_sema, 1, 0);
+ rds_init();
+ create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
+ IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
+}
diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c
index f4402d5617..e6bbb2345d 100644
--- a/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c
@@ -39,5 +39,12 @@ const struct gpio_event gpio1_events[] =
.mask = 1 << MC13783_GPIO_LINE,
.sense = GPIO_SENSE_HIGH_LEVEL,
.callback = mc13783_event,
- }
+ },
+ /* Generates a 5ms low pulse on the line - detect the falling edge */
+ [SI4700_STC_RDS_EVENT_ID] =
+ {
+ .mask = 1 << SI4700_GPIO_STC_RDS_LINE,
+ .sense = GPIO_SENSE_FALLING,
+ .callback = si4700_stc_rds_event,
+ },
};
diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-target.h b/firmware/target/arm/imx31/gigabeat-s/gpio-target.h
index 2eea27c3be..4903d0f631 100644
--- a/firmware/target/arm/imx31/gigabeat-s/gpio-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/gpio-target.h
@@ -29,6 +29,12 @@
#define MC13783_GPIO_ISR GPIO1_ISR
#define MC13783_GPIO_LINE 31
+/* SI4700 GPIO STC/RDS pin info for this target */
+#define SI4700_GPIO_STC_RDS_IMR GPIO1_IMR
+#define SI4700_GPIO_STC_RDS_NUM GPIO1_NUM
+#define SI4700_GPIO_STC_RDS_ISR GPIO1_ISR
+#define SI4700_GPIO_STC_RDS_LINE 27
+
#define GPIO1_INT_PRIO INT_PRIO_DEFAULT
/* Declare event indexes in priority order in a packed array */
@@ -36,7 +42,8 @@ enum gpio_event_ids
{
/* GPIO1 event IDs */
MC13783_EVENT_ID = GPIO1_EVENT_FIRST,
- GPIO1_NUM_EVENTS = 1,
+ SI4700_STC_RDS_EVENT_ID,
+ GPIO1_NUM_EVENTS = 2,
/* GPIO2 event IDs */
/* none defined */
/* GPIO3 event IDs */
@@ -44,5 +51,6 @@ enum gpio_event_ids
};
void mc13783_event(void);
+void si4700_stc_rds_event(void);
#endif /* GPIO_TARGET_H */