summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBertrik Sikken <bertrik@sikken.nl>2010-06-15 20:57:48 +0000
committerBertrik Sikken <bertrik@sikken.nl>2010-06-15 20:57:48 +0000
commitfcea117d21102383b35124a9e2513a51971a3fb5 (patch)
treeed21491ee9b061dedd07908a939a2e3eff2f0149
parent267a446887dbeafe02a4e1991af4489979fbb044 (diff)
downloadrockbox-fcea117d21102383b35124a9e2513a51971a3fb5.tar.gz
rockbox-fcea117d21102383b35124a9e2513a51971a3fb5.zip
Support for mystery FM chip in some Sansa Clip+, FS #11403 by me
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26864 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c55
-rw-r--r--firmware/SOURCES4
-rw-r--r--firmware/drivers/tuner/fmclipplus.c328
-rw-r--r--firmware/drivers/tuner/si4700.c29
-rw-r--r--firmware/export/config.h1
-rw-r--r--firmware/export/config/sansaclipplus.h2
-rw-r--r--firmware/export/fmclipplus.h54
-rw-r--r--firmware/export/si4700.h1
-rw-r--r--firmware/export/tuner.h6
-rw-r--r--firmware/target/arm/as3525/sansa-clipplus/tuner-clipplus.c38
-rw-r--r--firmware/tuner.c18
11 files changed, 505 insertions, 31 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index fce3e7aaf8..c5bbd020ef 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -2324,6 +2324,14 @@ static bool dbg_save_roms(void)
2324 2324
2325#ifndef SIMULATOR 2325#ifndef SIMULATOR
2326#if CONFIG_TUNER 2326#if CONFIG_TUNER
2327
2328#ifdef CONFIG_TUNER_MULTI
2329static int tuner_type = 0;
2330#define IF_TUNER_TYPE(type) if(tuner_type==type)
2331#else
2332#define IF_TUNER_TYPE(type)
2333#endif
2334
2327static int radio_callback(int btn, struct gui_synclist *lists) 2335static int radio_callback(int btn, struct gui_synclist *lists)
2328{ 2336{
2329 (void)lists; 2337 (void)lists;
@@ -2368,32 +2376,39 @@ static int radio_callback(int btn, struct gui_synclist *lists)
2368 (unsigned)nfo.write_regs[4]); 2376 (unsigned)nfo.write_regs[4]);
2369#endif /* TEA5767 */ 2377#endif /* TEA5767 */
2370#if (CONFIG_TUNER & SI4700) 2378#if (CONFIG_TUNER & SI4700)
2371 struct si4700_dbg_info nfo; 2379 IF_TUNER_TYPE(SI4700)
2372 si4700_dbg_info(&nfo); 2380 {
2373 simplelist_addline(SIMPLELIST_ADD_LINE, "SI4700 regs:"); 2381 struct si4700_dbg_info nfo;
2374 /* Registers */ 2382 int i;
2375 simplelist_addline(SIMPLELIST_ADD_LINE, 2383 si4700_dbg_info(&nfo);
2376 "%04X %04X %04X %04X", 2384 simplelist_addline(SIMPLELIST_ADD_LINE, "SI4700 regs:");
2377 (unsigned)nfo.regs[0], (unsigned)nfo.regs[1], 2385 for (i = 0; i < 16; i += 4) {
2378 (unsigned)nfo.regs[2], (unsigned)nfo.regs[3]); 2386 simplelist_addline(SIMPLELIST_ADD_LINE,"%02X: %04X %04X %04X %04X",
2379 simplelist_addline(SIMPLELIST_ADD_LINE, 2387 i, nfo.regs[i], nfo.regs[i+1], nfo.regs[i+2], nfo.regs[i+3]);
2380 "%04X %04X %04X %04X", 2388 }
2381 (unsigned)nfo.regs[4], (unsigned)nfo.regs[5], 2389 }
2382 (unsigned)nfo.regs[6], (unsigned)nfo.regs[7]);
2383 simplelist_addline(SIMPLELIST_ADD_LINE,
2384 "%04X %04X %04X %04X",
2385 (unsigned)nfo.regs[8], (unsigned)nfo.regs[9],
2386 (unsigned)nfo.regs[10], (unsigned)nfo.regs[11]);
2387 simplelist_addline(SIMPLELIST_ADD_LINE,
2388 "%04X %04X %04X %04X",
2389 (unsigned)nfo.regs[12], (unsigned)nfo.regs[13],
2390 (unsigned)nfo.regs[14], (unsigned)nfo.regs[15]);
2391#endif /* SI4700 */ 2390#endif /* SI4700 */
2391#if (CONFIG_TUNER & FMCLIPPLUS)
2392 IF_TUNER_TYPE(FMCLIPPLUS)
2393 {
2394 struct fmclipplus_dbg_info nfo;
2395 int i;
2396 fmclipplus_dbg_info(&nfo);
2397 simplelist_addline(SIMPLELIST_ADD_LINE, "FM Clip+ regs:");
2398 for (i = 0; i < 32; i += 4) {
2399 simplelist_addline(SIMPLELIST_ADD_LINE,"%02X: %04X %04X %04X %04X",
2400 i, nfo.regs[i], nfo.regs[i+1], nfo.regs[i+2], nfo.regs[i+3]);
2401 }
2402 }
2403#endif /* FMCLIPPLUS */
2392 return ACTION_REDRAW; 2404 return ACTION_REDRAW;
2393} 2405}
2394static bool dbg_fm_radio(void) 2406static bool dbg_fm_radio(void)
2395{ 2407{
2396 struct simplelist_info info; 2408 struct simplelist_info info;
2409#ifdef CONFIG_TUNER_MULTI
2410 tuner_type = tuner_detect_type();
2411#endif
2397 info.scroll_all = true; 2412 info.scroll_all = true;
2398 simplelist_info_init(&info, "FM Radio", 1, NULL); 2413 simplelist_info_init(&info, "FM Radio", 1, NULL);
2399 simplelist_set_line_count(0); 2414 simplelist_set_line_count(0);
diff --git a/firmware/SOURCES b/firmware/SOURCES
index c716b78432..b49382a0e9 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -248,6 +248,9 @@ drivers/tuner/si4700.c
248#if (CONFIG_TUNER & IPOD_REMOTE_TUNER) 248#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
249drivers/tuner/ipod_remote_tuner.c 249drivers/tuner/ipod_remote_tuner.c
250#endif /* (CONFIG_TUNER & IPOD_REMOTE_TUNER) */ 250#endif /* (CONFIG_TUNER & IPOD_REMOTE_TUNER) */
251#if (CONFIG_TUNER & FMCLIPPLUS)
252drivers/tuner/fmclipplus.c
253#endif /* (CONFIG_TUNER & FMCLIPPLUS) */
251#endif /*SIMULATOR */ 254#endif /*SIMULATOR */
252#endif /* CONFIG_TUNER */ 255#endif /* CONFIG_TUNER */
253#endif /* BOOTLOADER */ 256#endif /* BOOTLOADER */
@@ -1251,6 +1254,7 @@ target/arm/as3525/sansa-clipplus/backlight-clip.c
1251target/arm/powermgmt-ascodec.c 1254target/arm/powermgmt-ascodec.c
1252target/arm/as3525/sansa-clipplus/powermgmt-clipplus.c 1255target/arm/as3525/sansa-clipplus/powermgmt-clipplus.c
1253target/arm/as3525/sansa-clipplus/lcd-as-clip-plus.S 1256target/arm/as3525/sansa-clipplus/lcd-as-clip-plus.S
1257target/arm/as3525/sansa-clipplus/tuner-clipplus.c
1254#endif /* !BOOTLOADER */ 1258#endif /* !BOOTLOADER */
1255#endif /* !SIMULATOR */ 1259#endif /* !SIMULATOR */
1256#endif /* SANSA_CLIPPLUS */ 1260#endif /* SANSA_CLIPPLUS */
diff --git a/firmware/drivers/tuner/fmclipplus.c b/firmware/drivers/tuner/fmclipplus.c
new file mode 100644
index 0000000000..4badfba08f
--- /dev/null
+++ b/firmware/drivers/tuner/fmclipplus.c
@@ -0,0 +1,328 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Tuner "middleware" for unidentified Silicon Labs chip present in some
11 * Sansa Clip+ players
12 *
13 * Copyright (C) 2010 Bertrik Sikken
14 * Copyright (C) 2008 Nils Wallménius (si4700 code that this was based on)
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
20 *
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
23 *
24 ****************************************************************************/
25#include "config.h"
26#include <stdbool.h>
27#include <string.h>
28#include <stdlib.h>
29#include "kernel.h"
30#include "tuner.h" /* tuner abstraction interface */
31#include "fmradio.h"
32#include "fmradio_i2c.h" /* physical interface driver */
33
34#define SEEK_THRESHOLD 0x10
35
36#define I2C_ADR 0x20
37
38/** Registers and bits **/
39#define POWERCFG 0x2
40#define CHANNEL 0x3
41#define SYSCONFIG1 0x4
42#define SYSCONFIG2 0x5
43#define SYSCONFIG3 0x6
44
45#define READCHAN 0xA
46#define STATUSRSSI 0xB
47#define IDENT 0xC
48
49
50/* POWERCFG (0x2) */
51#define POWERCFG_DMUTE (0x1 << 14)
52#define POWERCFG_MONO (0x1 << 13)
53#define POWERCFG_ENABLE (0x1 << 0)
54
55/* CHANNEL (0x3) */
56#define CHANNEL_CHAN (0x3ff << 6)
57 #define CHANNEL_CHANw(x) (((x) << 6) & CHANNEL_CHAN)
58#define CHANNEL_TUNE (0x1 << 4)
59#define CHANNEL_BAND (0x3 << 2)
60 #define CHANNEL_BANDw(x) (((x) << 2) & CHANNEL_BAND)
61 #define CHANNEL_BANDr(x) (((x) & CHANNEL_BAND) >> 2)
62 #define CHANNEL_BAND_875_1080 (0x0 << 2) /* tenth-megahertz */
63 #define CHANNEL_BAND_760_1080 (0x1 << 2)
64 #define CHANNEL_BAND_760_900 (0x2 << 2)
65#define CHANNEL_SPACE (0x3 << 0)
66 #define CHANNEL_SPACEw(x) (((x) << 0) & CHANNEL_SPACE)
67 #define CHANNEL_SPACEr(x) (((x) & CHANNEL_SPACE) >> 0)
68 #define CHANNEL_SPACE_200KHZ (0x0 << 0)
69 #define CHANNEL_SPACE_100KHZ (0x1 << 0)
70 #define CHANNEL_SPACE_50KHZ (0x2 << 0)
71
72/* SYSCONFIG1 (0x4) */
73#define SYSCONFIG1_DE (0x1 << 11)
74
75/* READCHAN (0xA) */
76#define READCHAN_READCHAN (0x3ff << 0)
77 #define READCHAN_READCHANr(x) (((x) & READCHAN_READCHAN) >> 0)
78#define READCHAN_STC (0x1 << 14)
79
80/* STATUSRSSI (0xB) */
81#define STATUSRSSI_RSSI (0x3F << 10)
82 #define STATUSRSSI_RSSIr(x) (((x) & STATUSRSSI_RSSI) >> 10)
83#define STATUSRSSI_AFCRL (0x1 << 8)
84
85static const uint16_t initvals[32] = {
86 0x8110, 0x4580, 0xC401, 0x1B90,
87 0x0400, 0x866F, 0x8000, 0x4712,
88 0x5EC6, 0x0000, 0x406E, 0x2D80,
89 0x5803, 0x5804, 0x5804, 0x5804,
90
91 0x0047, 0x9000, 0xF587, 0x0009,
92 0x00F1, 0x41C0, 0x41E0, 0x506F,
93 0x5592, 0x007D, 0x10A0, 0x0780,
94 0x311D, 0x4006, 0x1F9B, 0x4C2B
95};
96
97static bool tuner_present = false;
98static int curr_frequency = 87500000; /* Current station frequency (HZ) */
99static uint16_t cache[32];
100
101/* reads <len> registers from radio at offset 0x0A into cache */
102static void fmclipplus_read(int len)
103{
104 int i;
105 unsigned char buf[64];
106 unsigned char *ptr = buf;
107 uint16_t data;
108
109 fmradio_i2c_read(I2C_ADR, buf, len * 2);
110 for (i = 0; i < len; i++) {
111 data = ptr[0] << 8 | ptr[1];
112 cache[(i + READCHAN) & 0x1F] = data;
113 ptr += 2;
114 }
115}
116
117/* writes <len> registers from cache to radio at offset 0x02 */
118static void fmclipplus_write(int len)
119{
120 int i;
121 unsigned char buf[64];
122 unsigned char *ptr = buf;
123 uint16_t data;
124
125 for (i = 0; i < len; i++) {
126 data = cache[(i + POWERCFG) & 0x1F];
127 *ptr++ = (data >> 8) & 0xFF;
128 *ptr++ = data & 0xFF;
129 }
130 fmradio_i2c_write(I2C_ADR, buf, len * 2);
131}
132
133static uint16_t fmclipplus_read_reg(int reg)
134{
135 fmclipplus_read(((reg - READCHAN) & 0x1F) + 1);
136 return cache[reg];
137}
138
139static void fmclipplus_write_reg(int reg, uint16_t value)
140{
141 cache[reg] = value;
142}
143
144static void fmclipplus_write_cache(void)
145{
146 fmclipplus_write(5);
147}
148
149static void fmclipplus_write_masked(int reg, uint16_t bits, uint16_t mask)
150{
151 fmclipplus_write_reg(reg, (cache[reg] & ~mask) | (bits & mask));
152}
153
154static void fmclipplus_write_clear(int reg, uint16_t mask)
155{
156 fmclipplus_write_reg(reg, cache[reg] & ~mask);
157}
158
159static void fmclipplus_sleep(int snooze)
160{
161 if (snooze) {
162 fmclipplus_write_masked(POWERCFG, 0, 0xFF);
163 }
164 else {
165 fmclipplus_write_masked(POWERCFG, 1, 0xFF);
166 }
167 fmclipplus_write_cache();
168}
169
170bool fmclipplus_detect(void)
171{
172 return ((fmclipplus_read_reg(IDENT) & 0xFF00) == 0x5800);
173 }
174
175void fmclipplus_init(void)
176{
177 if (fmclipplus_detect()) {
178 tuner_present = true;
179
180 // send pre-initialisation value
181 fmclipplus_write_reg(POWERCFG, 0x200);
182 fmclipplus_write(2);
183 sleep(HZ * 10 / 100);
184
185 // write initialisation values
186 memcpy(cache, initvals, sizeof(cache));
187 fmclipplus_write(32);
188 sleep(HZ * 70 / 1000);
189 }
190}
191
192static void fmclipplus_set_frequency(int freq)
193{
194 int i;
195
196 /* check BAND and spacings */
197 fmclipplus_read_reg(STATUSRSSI);
198 int start = CHANNEL_BANDr(cache[CHANNEL]) & 1 ? 76000000 : 87000000;
199 int chan = (freq - start) / 50000;
200
201 curr_frequency = freq;
202
203 for (i = 0; i < 5; i++) {
204 /* tune and wait a bit */
205 fmclipplus_write_masked(CHANNEL, CHANNEL_CHANw(chan) | CHANNEL_TUNE,
206 CHANNEL_CHAN | CHANNEL_TUNE);
207 fmclipplus_write_cache();
208 sleep(HZ * 70 / 1000);
209 fmclipplus_write_clear(CHANNEL, CHANNEL_TUNE);
210 fmclipplus_write_cache();
211
212 /* check if tuning was successful */
213 fmclipplus_read_reg(STATUSRSSI);
214 if (cache[READCHAN] & READCHAN_STC) {
215 if (READCHAN_READCHANr(cache[READCHAN]) == chan) {
216 break;
217 }
218 }
219 }
220}
221
222static int fmclipplus_tuned(void)
223{
224 /* Primitive tuning check: sufficient level and AFC not railed */
225 uint16_t status = fmclipplus_read_reg(STATUSRSSI);
226 if (STATUSRSSI_RSSIr(status) >= SEEK_THRESHOLD &&
227 (status & STATUSRSSI_AFCRL) == 0) {
228 return 1;
229 }
230
231 return 0;
232}
233
234static void fmclipplus_set_region(int region)
235{
236 const struct fmclipplus_region_data *rd = &fmclipplus_region_data[region];
237 uint16_t bandspacing = CHANNEL_BANDw(rd->band) |
238 CHANNEL_SPACEw(CHANNEL_SPACE_50KHZ);
239 uint16_t oldbs = cache[CHANNEL] & (CHANNEL_BAND | CHANNEL_SPACE);
240
241 fmclipplus_write_masked(SYSCONFIG1, rd->deemphasis ? SYSCONFIG1_DE : 0,
242 SYSCONFIG1_DE);
243 fmclipplus_write_masked(CHANNEL, bandspacing, CHANNEL_BAND | CHANNEL_SPACE);
244 fmclipplus_write_cache();
245
246 /* Retune if this region change would change the channel number. */
247 if (oldbs != bandspacing) {
248 fmclipplus_set_frequency(curr_frequency);
249 }
250}
251
252static bool fmclipplus_st(void)
253{
254 /* TODO not implemented yet */
255 return false;
256}
257
258/* tuner abstraction layer: set something to the tuner */
259int fmclipplus_set(int setting, int value)
260{
261 switch (setting) {
262 case RADIO_SLEEP:
263 if (value != 2) {
264 fmclipplus_sleep(value);
265 }
266 break;
267
268 case RADIO_FREQUENCY:
269 fmclipplus_set_frequency(value);
270 break;
271
272 case RADIO_SCAN_FREQUENCY:
273 fmclipplus_set_frequency(value);
274 return fmclipplus_tuned();
275
276 case RADIO_MUTE:
277 fmclipplus_write_masked(POWERCFG, value ? 0 : POWERCFG_DMUTE,
278 POWERCFG_DMUTE);
279 fmclipplus_write_masked(SYSCONFIG1, (3 << 9), (3 << 9));
280 fmclipplus_write_masked(SYSCONFIG2, (0xF << 0), (0xF << 0));
281 fmclipplus_write_cache();
282 break;
283
284 case RADIO_REGION:
285 fmclipplus_set_region(value);
286 break;
287
288 case RADIO_FORCE_MONO:
289 fmclipplus_write_masked(POWERCFG, value ? POWERCFG_MONO : 0,
290 POWERCFG_MONO);
291 fmclipplus_write_cache();
292 break;
293
294 default:
295 return -1;
296 }
297
298 return 1;
299}
300
301/* tuner abstraction layer: read something from the tuner */
302int fmclipplus_get(int setting)
303{
304 int val = -1; /* default for unsupported query */
305
306 switch (setting) {
307 case RADIO_PRESENT:
308 val = tuner_present ? 1 : 0;
309 break;
310
311 case RADIO_TUNED:
312 val = fmclipplus_tuned();
313 break;
314
315 case RADIO_STEREO:
316 val = fmclipplus_st();
317 break;
318 }
319
320 return val;
321}
322
323void fmclipplus_dbg_info(struct fmclipplus_dbg_info *nfo)
324{
325 fmclipplus_read(32);
326 memcpy(nfo->regs, cache, sizeof (nfo->regs));
327}
328
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index 985659b77b..8e43fe6acc 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -316,30 +316,39 @@ static void si4700_sleep(int snooze)
316 } 316 }
317} 317}
318 318
319void si4700_init(void) 319bool si4700_detect(void)
320{ 320{
321 bool detected;
322
321 tuner_power(true); 323 tuner_power(true);
324 detected = (si4700_read_reg(DEVICEID) == 0x1242);
325 tuner_power(false);
322 326
323 /* read all registers */ 327 return detected;
324 si4700_read(16); 328}
325 si4700_sleep(0);
326 329
330void si4700_init(void)
331{
327 /* check device id */ 332 /* check device id */
328 if (cache[DEVICEID] == 0x1242) 333 if (si4700_detect()) {
329 {
330 tuner_present = true; 334 tuner_present = true;
331 335
336 tuner_power(true);
337
338 /* read all registers */
339 si4700_read(16);
340 si4700_sleep(0);
341
332#ifdef USE_INTERNAL_OSCILLATOR 342#ifdef USE_INTERNAL_OSCILLATOR
333 /* Enable the internal oscillator 343 /* Enable the internal oscillator
334 (Si4702-16 needs this register to be initialised to 0x100) */ 344 (Si4702-16 needs this register to be initialised to 0x100) */
335 si4700_write_set(TEST1, TEST1_XOSCEN | 0x100); 345 si4700_write_set(TEST1, TEST1_XOSCEN | 0x100);
336 sleep(HZ/2); 346 sleep(HZ/2);
337#endif 347#endif
338 }
339 348
340 si4700_sleep(1); 349 si4700_sleep(1);
341 350 tuner_power(false);
342 tuner_power(false); 351 }
343} 352}
344 353
345static void si4700_set_frequency(int freq) 354static void si4700_set_frequency(int freq)
diff --git a/firmware/export/config.h b/firmware/export/config.h
index a2ba3f9f98..7f77514116 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -41,6 +41,7 @@
41#define TEA5760 0x10 /* Philips */ 41#define TEA5760 0x10 /* Philips */
42#define LV240000 0x20 /* Sanyo */ 42#define LV240000 0x20 /* Sanyo */
43#define IPOD_REMOTE_TUNER 0x40 /* Apple */ 43#define IPOD_REMOTE_TUNER 0x40 /* Apple */
44#define FMCLIPPLUS 0x80 /* Mystery SiLabs FM tuner in some clip+ */
44 45
45/* CONFIG_CODEC */ 46/* CONFIG_CODEC */
46#define MAS3587F 3587 47#define MAS3587F 3587
diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index 9382b22cbd..883a71ea21 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -123,7 +123,7 @@
123#define AB_REPEAT_ENABLE 1 123#define AB_REPEAT_ENABLE 1
124 124
125/* FM Tuner */ 125/* FM Tuner */
126#define CONFIG_TUNER SI4700 /* in fact SI4702 */ 126#define CONFIG_TUNER (SI4700|FMCLIPPLUS) /* in fact SI4702 */
127//#define HAVE_TUNER_PWR_CTRL 127//#define HAVE_TUNER_PWR_CTRL
128 128
129/* Define this for LCD backlight available */ 129/* Define this for LCD backlight available */
diff --git a/firmware/export/fmclipplus.h b/firmware/export/fmclipplus.h
new file mode 100644
index 0000000000..20961f47be
--- /dev/null
+++ b/firmware/export/fmclipplus.h
@@ -0,0 +1,54 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * $Id$
10 *
11 * Tuner header for the Silicon Labs Mystery radio chip in some Sansa Clip+
12 *
13 * Copyright (C) 2010 Bertrik Sikken
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#ifndef _FMCLIPPLUS_H_
26#define _FMCLIPPLUS_H_
27
28#define HAVE_RADIO_REGION
29
30struct fmclipplus_region_data
31{
32 unsigned char deemphasis; /* 0: 75us, 1: 50us */
33 unsigned char band; /* 0: us/europe, 1: japan */
34} __attribute__((packed));
35
36extern const struct fmclipplus_region_data fmclipplus_region_data[TUNER_NUM_REGIONS];
37
38struct fmclipplus_dbg_info
39{
40 uint16_t regs[32]; /* Read registers */
41};
42
43bool fmclipplus_detect(void);
44void fmclipplus_init(void);
45int fmclipplus_set(int setting, int value);
46int fmclipplus_get(int setting);
47void fmclipplus_dbg_info(struct fmclipplus_dbg_info *nfo);
48
49#ifndef CONFIG_TUNER_MULTI
50#define tuner_set fmclipplus_set
51#define tuner_get fmclipplus_get
52#endif
53
54#endif /* _FMCLIPPLUS_H_ */
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index fcc71cb282..d6c4e73b6e 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -41,6 +41,7 @@ struct si4700_dbg_info
41 uint16_t regs[16]; /* Read registers */ 41 uint16_t regs[16]; /* Read registers */
42}; 42};
43 43
44bool si4700_detect(void);
44void si4700_init(void); 45void si4700_init(void);
45int si4700_set(int setting, int value); 46int si4700_set(int setting, int value);
46int si4700_get(int setting); 47int si4700_get(int setting);
diff --git a/firmware/export/tuner.h b/firmware/export/tuner.h
index 03b6bd79d1..9101bb9241 100644
--- a/firmware/export/tuner.h
+++ b/firmware/export/tuner.h
@@ -99,6 +99,7 @@ char* tuner_get_rds_info(int setting);
99#else 99#else
100 100
101#ifdef CONFIG_TUNER_MULTI 101#ifdef CONFIG_TUNER_MULTI
102extern int tuner_detect_type(void);
102extern int (*tuner_set)(int setting, int value); 103extern int (*tuner_set)(int setting, int value);
103extern int (*tuner_get)(int setting); 104extern int (*tuner_get)(int setting);
104#endif /* CONFIG_TUNER_MULTI */ 105#endif /* CONFIG_TUNER_MULTI */
@@ -131,6 +132,11 @@ extern int (*tuner_get)(int setting);
131#include "si4700.h" 132#include "si4700.h"
132#endif 133#endif
133 134
135/* Silicon Labs mystery radio chip in some Sansa Clip+ */
136#if (CONFIG_TUNER & FMCLIPPLUS)
137#include "fmclipplus.h"
138#endif
139
134/* Apple remote tuner */ 140/* Apple remote tuner */
135#if (CONFIG_TUNER & IPOD_REMOTE_TUNER) 141#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
136#include "ipod_remote_tuner.h" 142#include "ipod_remote_tuner.h"
diff --git a/firmware/target/arm/as3525/sansa-clipplus/tuner-clipplus.c b/firmware/target/arm/as3525/sansa-clipplus/tuner-clipplus.c
new file mode 100644
index 0000000000..6bc1294eb4
--- /dev/null
+++ b/firmware/target/arm/as3525/sansa-clipplus/tuner-clipplus.c
@@ -0,0 +1,38 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Multi-tuner detection module to select between the si4700 and a yet
11 * unidentified Silicon Labs FM tuner chip found in some Sansa Clip+ players.
12 *
13 * Copyright (C) 2010 Bertrik Sikken
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24#include "config.h"
25#include <stdint.h>
26#include "tuner.h"
27
28int tuner_detect_type(void)
29{
30 if (si4700_detect()) {
31 return SI4700;
32 } else if (fmclipplus_detect()) {
33 return FMCLIPPLUS;
34 } else {
35 return 0;
36 }
37}
38
diff --git a/firmware/tuner.c b/firmware/tuner.c
index cca5cf2491..4d3866dc8e 100644
--- a/firmware/tuner.c
+++ b/firmware/tuner.c
@@ -89,6 +89,18 @@ const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS] =
89}; 89};
90#endif /* (CONFIG_TUNER & SI4700) */ 90#endif /* (CONFIG_TUNER & SI4700) */
91 91
92#if (CONFIG_TUNER & FMCLIPPLUS)
93const struct fmclipplus_region_data fmclipplus_region_data[TUNER_NUM_REGIONS] =
94{
95 [REGION_EUROPE] = { 1, 0 }, /* 50uS, US/Europe band */
96 [REGION_US_CANADA] = { 0, 0 }, /* 75uS, US/Europe band */
97 [REGION_JAPAN] = { 1, 1 }, /* 50uS, Japanese band */
98 [REGION_KOREA] = { 1, 0 }, /* 50uS, US/Europe band */
99 [REGION_ITALY] = { 1, 0 }, /* 50uS, US/Europe band */
100 [REGION_OTHER] = { 1, 0 }, /* 50uS, US/Europe band */
101};
102#endif /* (CONFIG_TUNER & FMCLIPPLUS) */
103
92#if (CONFIG_TUNER & IPOD_REMOTE_TUNER) 104#if (CONFIG_TUNER & IPOD_REMOTE_TUNER)
93const struct rmt_tuner_region_data 105const struct rmt_tuner_region_data
94 rmt_tuner_region_data[TUNER_NUM_REGIONS] = 106 rmt_tuner_region_data[TUNER_NUM_REGIONS] =
@@ -151,6 +163,12 @@ void tuner_init(void)
151 si4700_get, 163 si4700_get,
152 si4700_init()) 164 si4700_init())
153 #endif 165 #endif
166 #if (CONFIG_TUNER & FMCLIPPLUS)
167 TUNER_TYPE_CASE(FMCLIPPLUS,
168 fmclipplus_set,
169 fmclipplus_get,
170 fmclipplus_init())
171 #endif
154 } 172 }
155} 173}
156#endif /* SIMULATOR */ 174#endif /* SIMULATOR */