1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Tuner "middleware" for unidentified Silicon Labs chip present in some
13 * Copyright (C) 2010 Bertrik Sikken
14 * Copyright (C) 2008 Nils Wallménius (si4700 code that this was based on)
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.
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
24 ****************************************************************************/
30 #include "tuner.h" /* tuner abstraction interface */
32 #include "fmradio_i2c.h" /* physical interface driver */
34 #define SEEK_THRESHOLD 0x10
38 /** Registers and bits **/
41 #define SYSCONFIG1 0x4
42 #define SYSCONFIG2 0x5
43 #define SYSCONFIG3 0x6
46 #define STATUSRSSI 0xB
51 #define POWERCFG_DMUTE (0x1 << 14)
52 #define POWERCFG_MONO (0x1 << 13)
53 #define POWERCFG_ENABLE (0x1 << 0)
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)
72 /* SYSCONFIG1 (0x4) */
73 #define SYSCONFIG1_DE (0x1 << 11)
76 #define READCHAN_READCHAN (0x3ff << 0)
77 #define READCHAN_READCHANr(x) (((x) & READCHAN_READCHAN) >> 0)
78 #define READCHAN_STC (0x1 << 14)
79 #define READCHAN_ST (0x1 << 10)
81 /* STATUSRSSI (0xB) */
82 #define STATUSRSSI_RSSI (0x3F << 10)
83 #define STATUSRSSI_RSSIr(x) (((x) & STATUSRSSI_RSSI) >> 10)
84 #define STATUSRSSI_AFCRL (0x1 << 8)
86 static const uint16_t initvals
[32] = {
87 0x8110, 0x4580, 0xC401, 0x1B90,
88 0x0400, 0x866F, 0x8000, 0x4712,
89 0x5EC6, 0x0000, 0x406E, 0x2D80,
90 0x5803, 0x5804, 0x5804, 0x5804,
92 0x0047, 0x9000, 0xF587, 0x0009,
93 0x00F1, 0x41C0, 0x41E0, 0x506F,
94 0x5592, 0x007D, 0x10A0, 0x0780,
95 0x311D, 0x4006, 0x1F9B, 0x4C2B
98 static bool tuner_present
= false;
99 static int curr_frequency
= 87500000; /* Current station frequency (HZ) */
100 static uint16_t cache
[32];
102 /* reads <len> registers from radio at offset 0x0A into cache */
103 static void fmclipplus_read(int len
)
106 unsigned char buf
[64];
107 unsigned char *ptr
= buf
;
110 fmradio_i2c_read(I2C_ADR
, buf
, len
* 2);
111 for (i
= 0; i
< len
; i
++) {
112 data
= ptr
[0] << 8 | ptr
[1];
113 cache
[(i
+ READCHAN
) & 0x1F] = data
;
118 /* writes <len> registers from cache to radio at offset 0x02 */
119 static void fmclipplus_write(int len
)
122 unsigned char buf
[64];
123 unsigned char *ptr
= buf
;
126 for (i
= 0; i
< len
; i
++) {
127 data
= cache
[(i
+ POWERCFG
) & 0x1F];
128 *ptr
++ = (data
>> 8) & 0xFF;
129 *ptr
++ = data
& 0xFF;
131 fmradio_i2c_write(I2C_ADR
, buf
, len
* 2);
134 static uint16_t fmclipplus_read_reg(int reg
)
136 fmclipplus_read(((reg
- READCHAN
) & 0x1F) + 1);
140 static void fmclipplus_write_reg(int reg
, uint16_t value
)
145 static void fmclipplus_write_cache(void)
150 static void fmclipplus_write_masked(int reg
, uint16_t bits
, uint16_t mask
)
152 fmclipplus_write_reg(reg
, (cache
[reg
] & ~mask
) | (bits
& mask
));
155 static void fmclipplus_write_clear(int reg
, uint16_t mask
)
157 fmclipplus_write_reg(reg
, cache
[reg
] & ~mask
);
160 static void fmclipplus_sleep(int snooze
)
163 fmclipplus_write_masked(POWERCFG
, 0, 0xFF);
166 fmclipplus_write_masked(POWERCFG
, 1, 0xFF);
168 fmclipplus_write_cache();
171 bool fmclipplus_detect(void)
173 return ((fmclipplus_read_reg(IDENT
) & 0xFF00) == 0x5800);
176 void fmclipplus_init(void)
178 if (fmclipplus_detect()) {
179 tuner_present
= true;
181 // send pre-initialisation value
182 fmclipplus_write_reg(POWERCFG
, 0x200);
184 sleep(HZ
* 10 / 100);
186 // write initialisation values
187 memcpy(cache
, initvals
, sizeof(cache
));
188 fmclipplus_write(32);
189 sleep(HZ
* 70 / 1000);
193 static void fmclipplus_set_frequency(int freq
)
197 /* check BAND and spacings */
198 fmclipplus_read_reg(STATUSRSSI
);
199 int start
= CHANNEL_BANDr(cache
[CHANNEL
]) & 1 ? 76000000 : 87000000;
200 int chan
= (freq
- start
) / 50000;
202 curr_frequency
= freq
;
204 for (i
= 0; i
< 5; i
++) {
205 /* tune and wait a bit */
206 fmclipplus_write_masked(CHANNEL
, CHANNEL_CHANw(chan
) | CHANNEL_TUNE
,
207 CHANNEL_CHAN
| CHANNEL_TUNE
);
208 fmclipplus_write_cache();
209 sleep(HZ
* 70 / 1000);
210 fmclipplus_write_clear(CHANNEL
, CHANNEL_TUNE
);
211 fmclipplus_write_cache();
213 /* check if tuning was successful */
214 fmclipplus_read_reg(STATUSRSSI
);
215 if (cache
[READCHAN
] & READCHAN_STC
) {
216 if (READCHAN_READCHANr(cache
[READCHAN
]) == chan
) {
223 static int fmclipplus_tuned(void)
225 /* Primitive tuning check: sufficient level and AFC not railed */
226 uint16_t status
= fmclipplus_read_reg(STATUSRSSI
);
227 if (STATUSRSSI_RSSIr(status
) >= SEEK_THRESHOLD
&&
228 (status
& STATUSRSSI_AFCRL
) == 0) {
235 static void fmclipplus_set_region(int region
)
237 const struct fmclipplus_region_data
*rd
= &fmclipplus_region_data
[region
];
238 uint16_t bandspacing
= CHANNEL_BANDw(rd
->band
) |
239 CHANNEL_SPACEw(CHANNEL_SPACE_50KHZ
);
240 uint16_t oldbs
= cache
[CHANNEL
] & (CHANNEL_BAND
| CHANNEL_SPACE
);
242 fmclipplus_write_masked(SYSCONFIG1
, rd
->deemphasis
? SYSCONFIG1_DE
: 0,
244 fmclipplus_write_masked(CHANNEL
, bandspacing
, CHANNEL_BAND
| CHANNEL_SPACE
);
245 fmclipplus_write_cache();
247 /* Retune if this region change would change the channel number. */
248 if (oldbs
!= bandspacing
) {
249 fmclipplus_set_frequency(curr_frequency
);
253 static bool fmclipplus_st(void)
255 return (fmclipplus_read_reg(READCHAN
) & READCHAN_ST
);
258 /* tuner abstraction layer: set something to the tuner */
259 int fmclipplus_set(int setting
, int value
)
264 fmclipplus_sleep(value
);
268 case RADIO_FREQUENCY
:
269 fmclipplus_set_frequency(value
);
272 case RADIO_SCAN_FREQUENCY
:
273 fmclipplus_set_frequency(value
);
274 return fmclipplus_tuned();
277 fmclipplus_write_masked(POWERCFG
, value
? 0 : POWERCFG_DMUTE
,
279 fmclipplus_write_masked(SYSCONFIG1
, (3 << 9), (3 << 9));
280 fmclipplus_write_masked(SYSCONFIG2
, (0xF << 0), (0xF << 0));
281 fmclipplus_write_cache();
285 fmclipplus_set_region(value
);
288 case RADIO_FORCE_MONO
:
289 fmclipplus_write_masked(POWERCFG
, value
? POWERCFG_MONO
: 0,
291 fmclipplus_write_cache();
301 /* tuner abstraction layer: read something from the tuner */
302 int fmclipplus_get(int setting
)
304 int val
= -1; /* default for unsupported query */
308 val
= tuner_present
? 1 : 0;
312 val
= fmclipplus_tuned();
316 val
= fmclipplus_st();
323 void fmclipplus_dbg_info(struct fmclipplus_dbg_info
*nfo
)
326 memcpy(nfo
->regs
, cache
, sizeof (nfo
->regs
));