summaryrefslogtreecommitdiff
path: root/firmware
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 /firmware
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
Diffstat (limited to 'firmware')
-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
12 files changed, 516 insertions, 37 deletions
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
302#if (CONFIG_TUNER & RDA5802) 302#if (CONFIG_TUNER & RDA5802)
303drivers/tuner/rda5802.c 303drivers/tuner/rda5802.c
304#endif /* (CONFIG_TUNER & RDA5802) */ 304#endif /* (CONFIG_TUNER & RDA5802) */
305#if defined(HAVE_RDS_CAP)
306drivers/rds.c
307#endif /* HAVE_RDS_CAP */
305#endif /* PLATFORM_NATIVE */ 308#endif /* PLATFORM_NATIVE */
306#endif /* CONFIG_TUNER */ 309#endif /* CONFIG_TUNER */
307#endif /* BOOTLOADER */ 310#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2011 by Bertrik Sikken
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdbool.h>
22#include <stdint.h>
23#include <string.h>
24#include <strlcpy.h>
25#include "rds.h"
26
27/* programme identification */
28static uint16_t pi;
29/* program service name */
30static char ps_data[9];
31static char ps_copy[9];
32static int ps_segment;
33/* radio text */
34static char rt_data[65];
35static char rt_copy[65];
36static int rt_segment;
37static int rt_abflag;
38
39/* resets the rds parser */
40void rds_reset(void)
41{
42 ps_copy[0] = '\0';
43 ps_segment = 0;
44 rt_copy[0] = '\0';
45 rt_segment = 0;
46 pi = 0;
47}
48
49/* initialises the rds parser */
50void rds_init(void)
51{
52 rds_reset();
53}
54
55/* handles a group 0 packet, returns true if a new message was received */
56static bool handle_group0(uint16_t data[4])
57{
58 int segment, pos;
59
60 segment = data[1] & 3;
61
62 /* reset parsing if not in expected order */
63 if (segment != ps_segment) {
64 ps_segment = 0;
65 if (segment != 0) {
66 return false;
67 }
68 }
69
70 /* store data */
71 pos = segment * 2;
72 ps_data[pos++] = (data[3] >> 8) & 0xFF;
73 ps_data[pos++] = (data[3] >> 0) & 0xFF;
74 if (++ps_segment == 4) {
75 ps_data[pos] = '\0';
76 if (strcmp(ps_copy, ps_data) != 0) {
77 /* we got an updated message */
78 strcpy(ps_copy, ps_data);
79 return true;
80 }
81 }
82 return false;
83}
84
85/* handles a radio text characters, returns true if end-of-line found */
86static bool handle_rt(int pos, char c)
87{
88 switch (c) {
89 case 0x0A:
90 /* line break hint */
91 rt_data[pos] = ' ';
92 return false;
93 case 0x0D:
94 /* end of line */
95 rt_data[pos] = '\0';
96 return true;
97 default:
98 rt_data[pos] = c;
99 return false;
100 }
101}
102
103/* handles a group 2 packet, returns true if a new message was received */
104static bool handle_group2(uint16_t data[4])
105{
106 int abflag, segment, version, pos;
107 bool done;
108
109 /* reset parsing if not in expected order */
110 abflag = (data[1] >> 4) & 1;
111 segment = data[1] & 0xF;
112 if ((abflag != rt_abflag) || (segment != rt_segment)) {
113 rt_abflag = abflag;
114 rt_segment = 0;
115 if (segment != 0) {
116 return false;
117 }
118 }
119
120 /* store data */
121 version = (data[1] >> 11) & 1;
122 done = false;
123 if (version == 0) {
124 pos = segment * 4;
125 done = done || handle_rt(pos++, (data[2] >> 8) & 0xFF);
126 done = done || handle_rt(pos++, (data[2] >> 0) & 0xFF);
127 done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF);
128 done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF);
129 } else {
130 pos = segment * 2;
131 done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF);
132 done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF);
133 }
134 if ((++rt_segment == 16) || done) {
135 rt_data[pos] = '\0';
136 if (strcmp(rt_copy, rt_data) != 0) {
137 /* we got an updated message */
138 strcpy(rt_copy, rt_data);
139 return true;
140 }
141 }
142
143 return false;
144}
145
146/* processes one rds packet, returns true if a new message was received */
147bool rds_process(uint16_t data[4])
148{
149 int group;
150
151 /* get programme identification */
152 if (pi == 0) {
153 pi = data[0];
154 }
155
156 /* handle rds data based on group */
157 group = (data[1] >> 11) & 0x1F;
158 switch (group) {
159
160 case 0: /* group 0A: basic info */
161 case 1: /* group 0B: basic info */
162 return handle_group0(data);
163
164 case 4: /* group 2A: radio text */
165 case 5: /* group 2B: radio text */
166 return handle_group2(data);
167
168 default:
169 break;
170 }
171
172 return false;
173}
174
175/* returns the programme identification code */
176uint16_t rds_get_pi(void)
177{
178 return pi;
179}
180
181/* returns the most recent valid programme service name */
182char* rds_get_ps(void)
183{
184 return ps_copy;
185}
186
187/* returns the most recent valid RadioText message */
188char* rds_get_rt(void)
189{
190 return rt_copy;
191}
192
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 @@
29#include "tuner.h" /* tuner abstraction interface */ 29#include "tuner.h" /* tuner abstraction interface */
30#include "fmradio.h" 30#include "fmradio.h"
31#include "fmradio_i2c.h" /* physical interface driver */ 31#include "fmradio_i2c.h" /* physical interface driver */
32#include "rds.h"
32 33
33/* some models use the internal 32 kHz oscillator which needs special attention
34 during initialisation, power-up and power-down.
35*/
36#if defined(SANSA_CLIP) || defined(SANSA_E200V2) || defined(SANSA_FUZE) || defined(SANSA_C200V2) 34#if defined(SANSA_CLIP) || defined(SANSA_E200V2) || defined(SANSA_FUZE) || defined(SANSA_C200V2)
37#define USE_INTERNAL_OSCILLATOR 35/* some models use the internal 32 kHz oscillator which needs special attention
36 during initialisation, power-up and power-down. */
37#define SI4700_USE_INTERNAL_OSCILLATOR
38#elif defined(TOSHIBA_GIGABEAT_S) 38#elif defined(TOSHIBA_GIGABEAT_S)
39#define SI4700_GPIO_SETUP (SYSCONFIG1_GPIO1_HI_Z | \ 39/* gigabeat S uses the GPIO for stereo/mono detection */
40 SYSCONFIG1_GPIO2_HI_Z | \ 40#define SI4700_USE_MO_ST_I
41 SYSCONFIG1_GPIO3_MO_ST_I)
42extern int si4700_st(void);
43#endif
44
45#ifndef SI4700_GPIO_SETUP
46#define SI4700_GPIO_SETUP 0
47#endif 41#endif
48 42
49#define SEEK_THRESHOLD 0x16 43#define SEEK_THRESHOLD 0x16
@@ -81,7 +75,8 @@ extern int si4700_st(void);
81 75
82/* CHIPID (0x1) */ 76/* CHIPID (0x1) */
83 77
84#if 0 /* Informational */ 78#if 0
79/* Informational */
85/* Si4700/01 */ 80/* Si4700/01 */
86#define CHIPID_REV (0x3f << 10) 81#define CHIPID_REV (0x3f << 10)
87#define CHIPID_DEV (0x1 << 9) 82#define CHIPID_DEV (0x1 << 9)
@@ -98,7 +93,10 @@ extern int si4700_st(void);
98 /* 1000 before PU = Si4703 */ 93 /* 1000 before PU = Si4703 */
99 /* 1001 after PU = Si4703 */ 94 /* 1001 after PU = Si4703 */
100#define CHIPID_FIRMWARE (0x3f << 0) 95#define CHIPID_FIRMWARE (0x3f << 0)
101#endif /* 0 */ 96#endif
97
98/* Indicates Si4701/2/3 after powerup */
99#define CHIPID_DEV_0 (0x1 << 9)
102 100
103/* POWERCFG (0x2) */ 101/* POWERCFG (0x2) */
104#define POWERCFG_DSMUTE (0x1 << 15) 102#define POWERCFG_DSMUTE (0x1 << 15)
@@ -214,6 +212,10 @@ extern int si4700_st(void);
214 212
215static bool tuner_present = false; 213static bool tuner_present = false;
216static uint16_t cache[16]; 214static uint16_t cache[16];
215static struct mutex fmr_mutex SHAREDBSS_ATTR;
216#ifdef HAVE_RDS_CAP
217static int rds_event = 0;
218#endif
217 219
218/* reads <len> registers from radio at offset 0x0A into cache */ 220/* reads <len> registers from radio at offset 0x0A into cache */
219static void si4700_read(int len) 221static void si4700_read(int len)
@@ -277,19 +279,26 @@ static void si4700_write_clear(int reg, uint16_t mask)
277 si4700_write_reg(reg, cache[reg] & ~mask); 279 si4700_write_reg(reg, cache[reg] & ~mask);
278} 280}
279 281
280#if (SI4700_GPIO_SETUP & SYSCONFIG1_GPIO3) != SYSCONFIG1_GPIO3_MO_ST_I 282#ifndef SI4700_USE_MO_ST_I
281/* Poll i2c for the stereo status */ 283/* Poll i2c for the stereo status */
282static inline int si4700_st(void) 284bool si4700_st(void)
283{ 285{
284 return (si4700_read_reg(STATUSRSSI) & STATUSRSSI_ST) >> 8; 286 return (si4700_read_reg(STATUSRSSI) & STATUSRSSI_ST) >> 8;
285} 287}
286#endif 288#endif /* ndef SI4700_USE_MO_ST_I */
287 289
288static void si4700_sleep(int snooze) 290static void si4700_sleep(int snooze)
289{ 291{
290 if (snooze) 292 if (snooze)
291 { 293 {
292 /** power down **/ 294 /** power down **/
295#ifdef HAVE_RDS_CAP
296 if (cache[CHIPID] & CHIPID_DEV_0) {
297 si4700_rds_powerup(false);
298 si4700_write_clear(SYSCONFIG1, SYSCONFIG1_RDS | SYSCONFIG1_RDSIEN);
299 }
300#endif
301
293 /* ENABLE high, DISABLE high */ 302 /* ENABLE high, DISABLE high */
294 si4700_write_set(POWERCFG, 303 si4700_write_set(POWERCFG,
295 POWERCFG_DISABLE | POWERCFG_ENABLE); 304 POWERCFG_DISABLE | POWERCFG_ENABLE);
@@ -307,9 +316,8 @@ static void si4700_sleep(int snooze)
307 /* init register cache */ 316 /* init register cache */
308 si4700_read(16); 317 si4700_read(16);
309 318
310#if SI4700_GPIO_SETUP != 0 319#ifdef SI4700_USE_MO_ST_I
311 si4700_write_masked(SYSCONFIG1, SI4700_GPIO_SETUP, 320 si4700_write_masked(SYSCONFIG1, SYSCONFIG1_GPIO3_MO_ST_I,
312 SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 |
313 SYSCONFIG1_GPIO3); 321 SYSCONFIG1_GPIO3);
314#endif 322#endif
315 /* set mono->stereo switching RSSI range to lowest setting */ 323 /* set mono->stereo switching RSSI range to lowest setting */
@@ -320,33 +328,43 @@ static void si4700_sleep(int snooze)
320 SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) | 328 SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) |
321 SYSCONFIG2_VOLUMEw(0xF), 329 SYSCONFIG2_VOLUMEw(0xF),
322 SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH); 330 SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH);
331
332#ifdef HAVE_RDS_CAP
333 /* enable RDS and RDS interrupt if supported (bit 9 of CHIPID) */
334 if (cache[CHIPID] & CHIPID_DEV_0) {
335 /* Is Si4701/2/3 - Enable RDS and interrupt */
336 si4700_write_set(SYSCONFIG1, SYSCONFIG1_RDS | SYSCONFIG1_RDSIEN);
337 si4700_write_masked(SYSCONFIG1, SYSCONFIG1_GPIO2_STC_RDS_I,
338 SYSCONFIG1_GPIO2);
339 si4700_rds_powerup(true);
340 }
341#endif
323 } 342 }
324} 343}
325 344
326bool si4700_detect(void) 345bool si4700_detect(void)
327{ 346{
328 bool detected; 347 if (!tuner_present) {
329 348 tuner_power(true);
330 tuner_power(true); 349 tuner_present = (si4700_read_reg(DEVICEID) == 0x1242);
331 detected = (si4700_read_reg(DEVICEID) == 0x1242); 350 tuner_power(false);
332 tuner_power(false); 351 }
333 352 return tuner_present;
334 return detected;
335} 353}
336 354
337void si4700_init(void) 355void si4700_init(void)
338{ 356{
339 /* check device id */ 357 /* check device id */
340 if (si4700_detect()) { 358 if (si4700_detect()) {
341 tuner_present = true; 359 mutex_init(&fmr_mutex);
342 360
343 tuner_power(true); 361 tuner_power(true);
344 362
345 /* read all registers */ 363 /* read all registers */
346 si4700_read(16); 364 si4700_read(16);
347 si4700_sleep(0); 365 si4700_sleep(0);
348 366
349#ifdef USE_INTERNAL_OSCILLATOR 367#ifdef SI4700_USE_INTERNAL_OSCILLATOR
350 /* Enable the internal oscillator 368 /* Enable the internal oscillator
351 (Si4702-16 needs this register to be initialised to 0x100) */ 369 (Si4702-16 needs this register to be initialised to 0x100) */
352 si4700_write_set(TEST1, TEST1_XOSCEN | 0x100); 370 si4700_write_set(TEST1, TEST1_XOSCEN | 0x100);
@@ -355,6 +373,10 @@ void si4700_init(void)
355 373
356 si4700_sleep(1); 374 si4700_sleep(1);
357 tuner_power(false); 375 tuner_power(false);
376
377#ifdef HAVE_RDS_CAP
378 si4700_rds_init();
379#endif
358 } 380 }
359} 381}
360 382
@@ -421,6 +443,10 @@ static void si4700_set_region(int region)
421/* tuner abstraction layer: set something to the tuner */ 443/* tuner abstraction layer: set something to the tuner */
422int si4700_set(int setting, int value) 444int si4700_set(int setting, int value)
423{ 445{
446 int val = 1;
447
448 mutex_lock(&fmr_mutex);
449
424 switch(setting) 450 switch(setting)
425 { 451 {
426 case RADIO_SLEEP: 452 case RADIO_SLEEP:
@@ -430,12 +456,19 @@ int si4700_set(int setting, int value)
430 break; 456 break;
431 457
432 case RADIO_FREQUENCY: 458 case RADIO_FREQUENCY:
459#ifdef HAVE_RDS_CAP
460 rds_reset();
461#endif
433 si4700_set_frequency(value); 462 si4700_set_frequency(value);
434 break; 463 break;
435 464
436 case RADIO_SCAN_FREQUENCY: 465 case RADIO_SCAN_FREQUENCY:
466#ifdef HAVE_RDS_CAP
467 rds_reset();
468#endif
437 si4700_set_frequency(value); 469 si4700_set_frequency(value);
438 return si4700_tuned(); 470 val = si4700_tuned();
471 break;
439 472
440 case RADIO_MUTE: 473 case RADIO_MUTE:
441 si4700_write_masked(POWERCFG, value ? 0 : POWERCFG_DMUTE, 474 si4700_write_masked(POWERCFG, value ? 0 : POWERCFG_DMUTE,
@@ -452,10 +485,13 @@ int si4700_set(int setting, int value)
452 break; 485 break;
453 486
454 default: 487 default:
455 return -1; 488 val = -1;
489 break;
456 } 490 }
457 491
458 return 1; 492 mutex_unlock(&fmr_mutex);
493
494 return val;
459} 495}
460 496
461/* tuner abstraction layer: read something from the tuner */ 497/* tuner abstraction layer: read something from the tuner */
@@ -463,6 +499,8 @@ int si4700_get(int setting)
463{ 499{
464 int val = -1; /* default for unsupported query */ 500 int val = -1; /* default for unsupported query */
465 501
502 mutex_lock(&fmr_mutex);
503
466 switch(setting) 504 switch(setting)
467 { 505 {
468 case RADIO_PRESENT: 506 case RADIO_PRESENT:
@@ -488,8 +526,17 @@ int si4700_get(int setting)
488 case RADIO_RSSI_MAX: 526 case RADIO_RSSI_MAX:
489 val = RSSI_MAX; 527 val = RSSI_MAX;
490 break; 528 break;
529
530#ifdef HAVE_RDS_CAP
531 case RADIO_EVENT:
532 val = rds_event;
533 rds_event = 0;
534 break;
535#endif
491 } 536 }
492 537
538 mutex_unlock(&fmr_mutex);
539
493 return val; 540 return val;
494} 541}
495 542
@@ -497,10 +544,61 @@ void si4700_dbg_info(struct si4700_dbg_info *nfo)
497{ 544{
498 memset(nfo->regs, 0, sizeof (nfo->regs)); 545 memset(nfo->regs, 0, sizeof (nfo->regs));
499 546
547 mutex_lock(&fmr_mutex);
548
500 if (tuner_powered()) 549 if (tuner_powered())
501 { 550 {
502 si4700_read(16); 551 si4700_read(16);
503 memcpy(nfo->regs, cache, sizeof (nfo->regs)); 552 memcpy(nfo->regs, cache, sizeof (nfo->regs));
504 } 553 }
554
555 mutex_unlock(&fmr_mutex);
556}
557
558#ifdef HAVE_RDS_CAP
559/* Read raw RDS info for processing */
560bool si4700_rds_read_raw(uint16_t data[4])
561{
562 bool retval = false;
563
564 mutex_lock(&fmr_mutex);
565
566 if (tuner_powered())
567 {
568 si4700_read_reg(RDSD);
569 memcpy(data, &cache[RDSA], 4 * sizeof (uint16_t));
570 retval = true;
571 }
572
573 mutex_unlock(&fmr_mutex);
574
575 return retval;
576}
577
578/* Set the event flag */
579void si4700_rds_set_event(void)
580{
581 mutex_lock(&fmr_mutex);
582 rds_event = 1;
583 mutex_unlock(&fmr_mutex);
584}
585
586char * si4700_get_rds_info(int setting)
587{
588 char *text = NULL;
589
590 switch(setting)
591 {
592 case RADIO_RDS_NAME:
593 text = rds_get_ps();
594 break;
595
596 case RADIO_RDS_TEXT:
597 text = rds_get_rt();
598 break;
599 }
600
601 return text;
505} 602}
603#endif /* HAVE_RDS_CAP */
506 604
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 @@
89/* Define this if you have a SI4700 fm radio tuner */ 89/* Define this if you have a SI4700 fm radio tuner */
90#define CONFIG_TUNER SI4700 90#define CONFIG_TUNER SI4700
91 91
92#define HAVE_RDS_CAP
93
92/* Define this if you have the WM8978 audio codec */ 94/* Define this if you have the WM8978 audio codec */
93#define HAVE_WM8978 95#define HAVE_WM8978
94 96
@@ -161,7 +163,7 @@
161#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) 163#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS)
162 164
163/* Define this if target has an additional number of threads specific to it */ 165/* Define this if target has an additional number of threads specific to it */
164#define TARGET_EXTRA_THREADS 2 166#define TARGET_EXTRA_THREADS 3
165 167
166/* Type of mobile power - check this out */ 168/* Type of mobile power - check this out */
167#define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */ 169#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 @@
34 explicitly if different */ 34 explicitly if different */
35#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) 35#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO)
36 36
37#define HAVE_RDS_CAP
38
37/* define this if you have a bitmap LCD display */ 39/* define this if you have a bitmap LCD display */
38#define HAVE_LCD_BITMAP 40#define HAVE_LCD_BITMAP
39/* define this if you have a colour LCD */ 41/* define this if you have a colour LCD */
@@ -138,6 +140,9 @@
138/* define this if the flash memory uses the SecureDigital Memory Card protocol */ 140/* define this if the flash memory uses the SecureDigital Memory Card protocol */
139#define CONFIG_STORAGE STORAGE_SD 141#define CONFIG_STORAGE STORAGE_SD
140 142
143/* Define this if target has an additional number of threads specific to it */
144#define TARGET_EXTRA_THREADS 1 /* RDS thread */
145
141#define BATTERY_CAPACITY_DEFAULT 300 /* default battery capacity */ 146#define BATTERY_CAPACITY_DEFAULT 300 /* default battery capacity */
142#define BATTERY_CAPACITY_MIN 300 /* min. capacity selectable */ 147#define BATTERY_CAPACITY_MIN 300 /* min. capacity selectable */
143#define BATTERY_CAPACITY_MAX 300 /* max. capacity selectable */ 148#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2011 by Bertrik Sikken
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdint.h>
22#include <stdbool.h>
23
24void rds_init(void);
25
26void rds_reset(void);
27bool rds_process(uint16_t data[4]);
28
29uint16_t rds_get_pi(void);
30char* rds_get_ps(void);
31char* rds_get_rt(void);
32
33
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);
39int si4700_set(int setting, int value); 39int si4700_set(int setting, int value);
40int si4700_get(int setting); 40int si4700_get(int setting);
41void si4700_dbg_info(struct si4700_dbg_info *nfo); 41void si4700_dbg_info(struct si4700_dbg_info *nfo);
42/* For interrupt-based mono/stereo indicator */
43bool si4700_st(void);
44
45/** RDS support **/
46void si4700_rds_init(void);
47/* Read raw RDS info for processing */
48bool si4700_rds_read_raw(uint16_t data[4]);
49/* Radio is fully powered up or about to be powered down */
50void si4700_rds_powerup(bool on);
51/* Obtain specified string */
52char* si4700_get_rds_info(int setting);
53/* Set the event flag */
54void si4700_rds_set_event(void);
42 55
43#ifndef CONFIG_TUNER_MULTI 56#ifndef CONFIG_TUNER_MULTI
44#define tuner_set si4700_set 57#define tuner_set si4700_set
45#define tuner_get si4700_get 58#define tuner_get si4700_get
59#define tuner_get_rds_info si4700_get_rds_info
46#endif 60#endif
47 61
48#endif /* _SI4700_H_ */ 62#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 @@
27 I2C with a couple of GPIO pins. 27 I2C with a couple of GPIO pins.
28 */ 28 */
29 29
30#include "config.h"
30#include "as3525.h" 31#include "as3525.h"
31#include "system.h" 32#include "system.h"
33#include "tuner.h"
32#include "generic_i2c.h" 34#include "generic_i2c.h"
33#include "fmradio_i2c.h" 35#include "fmradio_i2c.h"
36#include "thread.h"
37#include "rds.h"
34 38
35#if defined(SANSA_CLIP) || defined(SANSA_C200V2) 39#if defined(SANSA_CLIP) || defined(SANSA_C200V2)
36#define I2C_SCL_GPIO(x) GPIOB_PIN(x) 40#define I2C_SCL_GPIO(x) GPIOB_PIN(x)
@@ -179,3 +183,58 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
179#endif 183#endif
180 return ret; 184 return ret;
181} 185}
186
187#ifdef HAVE_RDS_CAP
188/* Low-level RDS Support */
189static struct semaphore rds_sema;
190static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
191
192/* RDS GPIO interrupt handler */
193void tuner_isr(void)
194{
195 /* read and clear the interrupt */
196 if (GPIOA_MIS & (1<<4)) {
197 semaphore_release(&rds_sema);
198 }
199 GPIOA_IC = (1<<4);
200}
201
202/* Captures RDS data and processes it */
203static void NORETURN_ATTR rds_thread(void)
204{
205 uint16_t rds_data[4];
206
207 while (true) {
208 semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
209 if (si4700_rds_read_raw(rds_data) && rds_process(rds_data)) {
210 si4700_rds_set_event();
211 }
212 }
213}
214
215/* Called with on=true after full radio power up, and with on=false before
216 powering down */
217void si4700_rds_powerup(bool on)
218{
219 GPIOA_IE &= ~(1<<4); /* disable GPIO interrupt */
220
221 if (on) {
222 GPIOA_DIR &= ~(1<<4); /* input */
223 GPIOA_IS &= ~(1<<4); /* edge detect */
224 GPIOA_IBE &= ~(1<<4); /* only one edge */
225 GPIOA_IEV &= ~(1<<4); /* falling edge */
226 GPIOA_IC = (1<<4); /* clear any pending interrupt */
227 GPIOA_IE |= (1<<4); /* enable GPIO interrupt */
228 }
229}
230
231/* One-time RDS init at startup */
232void si4700_rds_init(void)
233{
234 semaphore_init(&rds_sema, 1, 0);
235 rds_init();
236 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
237 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
238}
239#endif /* HAVE_RDS_CAP */
240
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)
155 void button_gpioa_isr(void); 155 void button_gpioa_isr(void);
156 button_gpioa_isr(); 156 button_gpioa_isr();
157#endif 157#endif
158#ifdef HAVE_RDS_CAP
159 void tuner_isr(void);
160 tuner_isr();
161#endif
158} 162}
159 163
160void irq_handler(void) 164void 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 @@
23#include "system.h" 23#include "system.h"
24#include "mc13783.h" 24#include "mc13783.h"
25#include "iomuxc-imx31.h" 25#include "iomuxc-imx31.h"
26#include "gpio-imx31.h"
26#include "i2c-imx31.h" 27#include "i2c-imx31.h"
27#include "fmradio_i2c.h" 28#include "fmradio_i2c.h"
29#include "thread.h"
30#include "rds.h"
31#include "tuner.h"
28 32
29static struct i2c_node si4700_i2c_node = 33static struct i2c_node si4700_i2c_node =
30{ 34{
@@ -117,7 +121,57 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
117 return 0; 121 return 0;
118} 122}
119 123
120int si4700_st(void) 124bool si4700_st(void)
121{ 125{
122 return (GPIO1_DR & (1 << 28)) >> 28; 126 return (GPIO1_DR & (1 << 28)) >> 28;
123} 127}
128
129
130/* Low-level RDS Support */
131static struct semaphore rds_sema;
132static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
133
134/* RDS GPIO interrupt handler */
135void si4700_stc_rds_event(void)
136{
137 /* read and clear the interrupt */
138 SI4700_GPIO_STC_RDS_ISR = (1ul << SI4700_GPIO_STC_RDS_LINE);
139 semaphore_release(&rds_sema);
140}
141
142/* Called with on=true after full radio power up, and with on=false before
143 powering down */
144void si4700_rds_powerup(bool on)
145{
146 gpio_disable_event(SI4700_STC_RDS_EVENT_ID);
147
148 if (on)
149 {
150 SI4700_GPIO_STC_RDS_ISR = (1ul << SI4700_GPIO_STC_RDS_LINE);
151 gpio_enable_event(SI4700_STC_RDS_EVENT_ID);
152 }
153}
154
155/* Captures RDS data and processes it */
156/* Use of a thread here is likely temporary */
157static void NORETURN_ATTR rds_thread(void)
158{
159 uint16_t rds_data[4];
160
161 while (1)
162 {
163 semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
164
165 if (si4700_rds_read_raw(rds_data) && rds_process(rds_data))
166 si4700_rds_set_event();
167 }
168}
169
170/* One-time RDS init at startup */
171void si4700_rds_init(void)
172{
173 semaphore_init(&rds_sema, 1, 0);
174 rds_init();
175 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
176 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
177}
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[] =
39 .mask = 1 << MC13783_GPIO_LINE, 39 .mask = 1 << MC13783_GPIO_LINE,
40 .sense = GPIO_SENSE_HIGH_LEVEL, 40 .sense = GPIO_SENSE_HIGH_LEVEL,
41 .callback = mc13783_event, 41 .callback = mc13783_event,
42 } 42 },
43 /* Generates a 5ms low pulse on the line - detect the falling edge */
44 [SI4700_STC_RDS_EVENT_ID] =
45 {
46 .mask = 1 << SI4700_GPIO_STC_RDS_LINE,
47 .sense = GPIO_SENSE_FALLING,
48 .callback = si4700_stc_rds_event,
49 },
43}; 50};
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 @@
29#define MC13783_GPIO_ISR GPIO1_ISR 29#define MC13783_GPIO_ISR GPIO1_ISR
30#define MC13783_GPIO_LINE 31 30#define MC13783_GPIO_LINE 31
31 31
32/* SI4700 GPIO STC/RDS pin info for this target */
33#define SI4700_GPIO_STC_RDS_IMR GPIO1_IMR
34#define SI4700_GPIO_STC_RDS_NUM GPIO1_NUM
35#define SI4700_GPIO_STC_RDS_ISR GPIO1_ISR
36#define SI4700_GPIO_STC_RDS_LINE 27
37
32#define GPIO1_INT_PRIO INT_PRIO_DEFAULT 38#define GPIO1_INT_PRIO INT_PRIO_DEFAULT
33 39
34/* Declare event indexes in priority order in a packed array */ 40/* Declare event indexes in priority order in a packed array */
@@ -36,7 +42,8 @@ enum gpio_event_ids
36{ 42{
37 /* GPIO1 event IDs */ 43 /* GPIO1 event IDs */
38 MC13783_EVENT_ID = GPIO1_EVENT_FIRST, 44 MC13783_EVENT_ID = GPIO1_EVENT_FIRST,
39 GPIO1_NUM_EVENTS = 1, 45 SI4700_STC_RDS_EVENT_ID,
46 GPIO1_NUM_EVENTS = 2,
40 /* GPIO2 event IDs */ 47 /* GPIO2 event IDs */
41 /* none defined */ 48 /* none defined */
42 /* GPIO3 event IDs */ 49 /* GPIO3 event IDs */
@@ -44,5 +51,6 @@ enum gpio_event_ids
44}; 51};
45 52
46void mc13783_event(void); 53void mc13783_event(void);
54void si4700_stc_rds_event(void);
47 55
48#endif /* GPIO_TARGET_H */ 56#endif /* GPIO_TARGET_H */