diff options
-rw-r--r-- | apps/debug_menu.c | 55 | ||||
-rw-r--r-- | firmware/SOURCES | 4 | ||||
-rw-r--r-- | firmware/drivers/tuner/fmclipplus.c | 328 | ||||
-rw-r--r-- | firmware/drivers/tuner/si4700.c | 29 | ||||
-rw-r--r-- | firmware/export/config.h | 1 | ||||
-rw-r--r-- | firmware/export/config/sansaclipplus.h | 2 | ||||
-rw-r--r-- | firmware/export/fmclipplus.h | 54 | ||||
-rw-r--r-- | firmware/export/si4700.h | 1 | ||||
-rw-r--r-- | firmware/export/tuner.h | 6 | ||||
-rw-r--r-- | firmware/target/arm/as3525/sansa-clipplus/tuner-clipplus.c | 38 | ||||
-rw-r--r-- | firmware/tuner.c | 18 |
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 | ||
2329 | static 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 | |||
2327 | static int radio_callback(int btn, struct gui_synclist *lists) | 2335 | static 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 | } |
2394 | static bool dbg_fm_radio(void) | 2406 | static 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) |
249 | drivers/tuner/ipod_remote_tuner.c | 249 | drivers/tuner/ipod_remote_tuner.c |
250 | #endif /* (CONFIG_TUNER & IPOD_REMOTE_TUNER) */ | 250 | #endif /* (CONFIG_TUNER & IPOD_REMOTE_TUNER) */ |
251 | #if (CONFIG_TUNER & FMCLIPPLUS) | ||
252 | drivers/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 | |||
1251 | target/arm/powermgmt-ascodec.c | 1254 | target/arm/powermgmt-ascodec.c |
1252 | target/arm/as3525/sansa-clipplus/powermgmt-clipplus.c | 1255 | target/arm/as3525/sansa-clipplus/powermgmt-clipplus.c |
1253 | target/arm/as3525/sansa-clipplus/lcd-as-clip-plus.S | 1256 | target/arm/as3525/sansa-clipplus/lcd-as-clip-plus.S |
1257 | target/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 | |||
85 | static 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 | |||
97 | static bool tuner_present = false; | ||
98 | static int curr_frequency = 87500000; /* Current station frequency (HZ) */ | ||
99 | static uint16_t cache[32]; | ||
100 | |||
101 | /* reads <len> registers from radio at offset 0x0A into cache */ | ||
102 | static 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 */ | ||
118 | static 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 | |||
133 | static uint16_t fmclipplus_read_reg(int reg) | ||
134 | { | ||
135 | fmclipplus_read(((reg - READCHAN) & 0x1F) + 1); | ||
136 | return cache[reg]; | ||
137 | } | ||
138 | |||
139 | static void fmclipplus_write_reg(int reg, uint16_t value) | ||
140 | { | ||
141 | cache[reg] = value; | ||
142 | } | ||
143 | |||
144 | static void fmclipplus_write_cache(void) | ||
145 | { | ||
146 | fmclipplus_write(5); | ||
147 | } | ||
148 | |||
149 | static 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 | |||
154 | static void fmclipplus_write_clear(int reg, uint16_t mask) | ||
155 | { | ||
156 | fmclipplus_write_reg(reg, cache[reg] & ~mask); | ||
157 | } | ||
158 | |||
159 | static 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 | |||
170 | bool fmclipplus_detect(void) | ||
171 | { | ||
172 | return ((fmclipplus_read_reg(IDENT) & 0xFF00) == 0x5800); | ||
173 | } | ||
174 | |||
175 | void 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 | |||
192 | static 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 | |||
222 | static 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 | |||
234 | static 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 | |||
252 | static bool fmclipplus_st(void) | ||
253 | { | ||
254 | /* TODO not implemented yet */ | ||
255 | return false; | ||
256 | } | ||
257 | |||
258 | /* tuner abstraction layer: set something to the tuner */ | ||
259 | int 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 */ | ||
302 | int 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 | |||
323 | void 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 | ||
319 | void si4700_init(void) | 319 | bool 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 | ||
330 | void 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 | ||
345 | static void si4700_set_frequency(int freq) | 354 | static 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 | |||
30 | struct 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 | |||
36 | extern const struct fmclipplus_region_data fmclipplus_region_data[TUNER_NUM_REGIONS]; | ||
37 | |||
38 | struct fmclipplus_dbg_info | ||
39 | { | ||
40 | uint16_t regs[32]; /* Read registers */ | ||
41 | }; | ||
42 | |||
43 | bool fmclipplus_detect(void); | ||
44 | void fmclipplus_init(void); | ||
45 | int fmclipplus_set(int setting, int value); | ||
46 | int fmclipplus_get(int setting); | ||
47 | void 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 | ||
44 | bool si4700_detect(void); | ||
44 | void si4700_init(void); | 45 | void si4700_init(void); |
45 | int si4700_set(int setting, int value); | 46 | int si4700_set(int setting, int value); |
46 | int si4700_get(int setting); | 47 | int 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 |
102 | extern int tuner_detect_type(void); | ||
102 | extern int (*tuner_set)(int setting, int value); | 103 | extern int (*tuner_set)(int setting, int value); |
103 | extern int (*tuner_get)(int setting); | 104 | extern 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 | |||
28 | int 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) | ||
93 | const 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) |
93 | const struct rmt_tuner_region_data | 105 | const 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 */ |