summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/rds.c192
-rw-r--r--firmware/drivers/tuner/si4700.c164
2 files changed, 323 insertions, 33 deletions
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