diff options
| author | Bertrik Sikken <bertrik@sikken.nl> | 2011-12-17 20:24:19 +0000 |
|---|---|---|
| committer | Bertrik Sikken <bertrik@sikken.nl> | 2011-12-17 20:24:19 +0000 |
| commit | 8c19dcd598144d028ff1647d850d3a17483e6b9c (patch) | |
| tree | b1baea8ffcf26bedf0dea800c62ad33df9fa18f0 /firmware/drivers | |
| parent | 17ed3253fc98bcca59d70531a4d81b3be75dc7ea (diff) | |
| download | rockbox-8c19dcd598144d028ff1647d850d3a17483e6b9c.zip rockbox-8c19dcd598144d028ff1647d850d3a17483e6b9c.tar.gz rockbox-8c19dcd598144d028ff1647d850d3a17483e6b9c.tar.bz2 rockbox-8c19dcd598144d028ff1647d850d3a17483e6b9c.tar.xz | |
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
Diffstat (limited to 'firmware/drivers')
| -rw-r--r-- | firmware/drivers/rds.c | 192 | ||||
| -rw-r--r-- | firmware/drivers/tuner/si4700.c | 164 |
2 files changed, 323 insertions, 33 deletions
diff --git a/firmware/drivers/rds.c b/firmware/drivers/rds.c new file mode 100644 index 0000000..09bc338 --- /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 848d25e..bebbd0c 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 */ |