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 | |
parent | 17ed3253fc98bcca59d70531a4d81b3be75dc7ea (diff) | |
download | rockbox-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/SOURCES | 3 | ||||
-rw-r--r-- | firmware/drivers/rds.c | 192 | ||||
-rw-r--r-- | firmware/drivers/tuner/si4700.c | 164 | ||||
-rw-r--r-- | firmware/export/config/gigabeats.h | 4 | ||||
-rw-r--r-- | firmware/export/config/sansaclipzip.h | 5 | ||||
-rw-r--r-- | firmware/export/rds.h | 33 | ||||
-rw-r--r-- | firmware/export/si4700.h | 14 | ||||
-rw-r--r-- | firmware/target/arm/as3525/fmradio-i2c-as3525.c | 59 | ||||
-rw-r--r-- | firmware/target/arm/as3525/system-as3525.c | 4 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c | 56 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c | 9 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/gpio-target.h | 10 |
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) |
303 | drivers/tuner/rda5802.c | 303 | drivers/tuner/rda5802.c |
304 | #endif /* (CONFIG_TUNER & RDA5802) */ | 304 | #endif /* (CONFIG_TUNER & RDA5802) */ |
305 | #if defined(HAVE_RDS_CAP) | ||
306 | drivers/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 */ | ||
28 | static uint16_t pi; | ||
29 | /* program service name */ | ||
30 | static char ps_data[9]; | ||
31 | static char ps_copy[9]; | ||
32 | static int ps_segment; | ||
33 | /* radio text */ | ||
34 | static char rt_data[65]; | ||
35 | static char rt_copy[65]; | ||
36 | static int rt_segment; | ||
37 | static int rt_abflag; | ||
38 | |||
39 | /* resets the rds parser */ | ||
40 | void 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 */ | ||
50 | void rds_init(void) | ||
51 | { | ||
52 | rds_reset(); | ||
53 | } | ||
54 | |||
55 | /* handles a group 0 packet, returns true if a new message was received */ | ||
56 | static 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 */ | ||
86 | static 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 */ | ||
104 | static 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 */ | ||
147 | bool 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 */ | ||
176 | uint16_t rds_get_pi(void) | ||
177 | { | ||
178 | return pi; | ||
179 | } | ||
180 | |||
181 | /* returns the most recent valid programme service name */ | ||
182 | char* rds_get_ps(void) | ||
183 | { | ||
184 | return ps_copy; | ||
185 | } | ||
186 | |||
187 | /* returns the most recent valid RadioText message */ | ||
188 | char* 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) | ||
42 | extern 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 | ||
215 | static bool tuner_present = false; | 213 | static bool tuner_present = false; |
216 | static uint16_t cache[16]; | 214 | static uint16_t cache[16]; |
215 | static struct mutex fmr_mutex SHAREDBSS_ATTR; | ||
216 | #ifdef HAVE_RDS_CAP | ||
217 | static 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 */ |
219 | static void si4700_read(int len) | 221 | static 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 */ |
282 | static inline int si4700_st(void) | 284 | bool 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 | ||
288 | static void si4700_sleep(int snooze) | 290 | static 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 | ||
326 | bool si4700_detect(void) | 345 | bool 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 | ||
337 | void si4700_init(void) | 355 | void 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 */ |
422 | int si4700_set(int setting, int value) | 444 | int 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 */ | ||
560 | bool 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 */ | ||
579 | void si4700_rds_set_event(void) | ||
580 | { | ||
581 | mutex_lock(&fmr_mutex); | ||
582 | rds_event = 1; | ||
583 | mutex_unlock(&fmr_mutex); | ||
584 | } | ||
585 | |||
586 | char * 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 | |||
24 | void rds_init(void); | ||
25 | |||
26 | void rds_reset(void); | ||
27 | bool rds_process(uint16_t data[4]); | ||
28 | |||
29 | uint16_t rds_get_pi(void); | ||
30 | char* rds_get_ps(void); | ||
31 | char* 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); | |||
39 | int si4700_set(int setting, int value); | 39 | int si4700_set(int setting, int value); |
40 | int si4700_get(int setting); | 40 | int si4700_get(int setting); |
41 | void si4700_dbg_info(struct si4700_dbg_info *nfo); | 41 | void si4700_dbg_info(struct si4700_dbg_info *nfo); |
42 | /* For interrupt-based mono/stereo indicator */ | ||
43 | bool si4700_st(void); | ||
44 | |||
45 | /** RDS support **/ | ||
46 | void si4700_rds_init(void); | ||
47 | /* Read raw RDS info for processing */ | ||
48 | bool si4700_rds_read_raw(uint16_t data[4]); | ||
49 | /* Radio is fully powered up or about to be powered down */ | ||
50 | void si4700_rds_powerup(bool on); | ||
51 | /* Obtain specified string */ | ||
52 | char* si4700_get_rds_info(int setting); | ||
53 | /* Set the event flag */ | ||
54 | void 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 */ | ||
189 | static struct semaphore rds_sema; | ||
190 | static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)]; | ||
191 | |||
192 | /* RDS GPIO interrupt handler */ | ||
193 | void 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 */ | ||
203 | static 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 */ | ||
217 | void 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 */ | ||
232 | void 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 | ||
160 | void irq_handler(void) | 164 | 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 @@ | |||
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 | ||
29 | static struct i2c_node si4700_i2c_node = | 33 | static 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 | ||
120 | int si4700_st(void) | 124 | bool 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 */ | ||
131 | static struct semaphore rds_sema; | ||
132 | static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)]; | ||
133 | |||
134 | /* RDS GPIO interrupt handler */ | ||
135 | void 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 */ | ||
144 | void 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 */ | ||
157 | static 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 */ | ||
171 | void 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 | ||
46 | void mc13783_event(void); | 53 | void mc13783_event(void); |
54 | void si4700_stc_rds_event(void); | ||
47 | 55 | ||
48 | #endif /* GPIO_TARGET_H */ | 56 | #endif /* GPIO_TARGET_H */ |