si4700/rda5802 tuners: prevent hang when changing radio region. Probably fixes bug...
[maemo-rb.git] / firmware / drivers / tuner / rda5802.c
blobfe2b25f21379c99213b7866ecdf9fe8a316cfb08
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Tuner "middleware" for RDA5802 chip present in some Sansa Clip+ players
12 * Copyright (C) 2010 Bertrik Sikken
13 * Copyright (C) 2008 Nils Wallménius (si4700 code that this was based on)
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.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
24 #include "config.h"
25 #include <stdbool.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include "kernel.h"
29 #include "tuner.h" /* tuner abstraction interface */
30 #include "fmradio.h"
31 #include "fmradio_i2c.h" /* physical interface driver */
33 #define SEEK_THRESHOLD 0x16
35 #define I2C_ADR 0x20
37 /* define RSSI range */
38 #define RSSI_MIN 0
39 #define RSSI_MAX 70
41 /** Registers and bits **/
42 #define POWERCFG 0x2
43 #define CHANNEL 0x3
44 #define SYSCONFIG1 0x4
45 #define SYSCONFIG2 0x5
46 #define SYSCONFIG3 0x6
48 #define READCHAN 0xA
49 #define STATUSRSSI 0xB
50 #define IDENT 0xC
53 /* POWERCFG (0x2) */
54 #define POWERCFG_DMUTE (0x1 << 14)
55 #define POWERCFG_MONO (0x1 << 13)
56 #define POWERCFG_SOFT_RESET (0x1 << 1)
57 #define POWERCFG_ENABLE (0x1 << 0)
59 /* CHANNEL (0x3) */
60 #define CHANNEL_CHAN (0x3ff << 6)
61 #define CHANNEL_CHANw(x) (((x) << 6) & CHANNEL_CHAN)
62 #define CHANNEL_TUNE (0x1 << 4)
63 #define CHANNEL_BAND (0x3 << 2)
64 #define CHANNEL_BANDw(x) (((x) << 2) & CHANNEL_BAND)
65 #define CHANNEL_BANDr(x) (((x) & CHANNEL_BAND) >> 2)
66 #define CHANNEL_BAND_870_1080 (0x0) /* tenth-megahertz */
67 #define CHANNEL_BAND_760_1080 (0x1)
68 #define CHANNEL_BAND_760_900 (0x2)
69 #define CHANNEL_BAND_650_760 (0x3)
70 #define CHANNEL_SPACE (0x3 << 0)
71 #define CHANNEL_SPACEw(x) (((x) << 0) & CHANNEL_SPACE)
72 #define CHANNEL_SPACEr(x) (((x) & CHANNEL_SPACE) >> 0)
73 #define CHANNEL_SPACE_100KHZ (0x0)
74 #define CHANNEL_SPACE_200KHZ (0x1)
75 #define CHANNEL_SPACE_50KHZ (0x2)
77 /* SYSCONFIG1 (0x4) */
78 #define SYSCONFIG1_DE (0x1 << 11)
79 #define SYSCONFIG1_SMUTE (0x1 << 9)
81 /* SYSCONFIG2 (0x5) */
82 #define SYSCONFIG2_VOLUME (0xF << 0)
84 /* READCHAN (0xA) */
85 #define READCHAN_READCHAN (0x3ff << 0)
86 #define READCHAN_READCHANr(x) (((x) & READCHAN_READCHAN) >> 0)
87 #define READCHAN_STC (0x1 << 14)
88 #define READCHAN_ST (0x1 << 10)
90 /* STATUSRSSI (0xB) */
91 #define STATUSRSSI_RSSI (0x7F << 9)
92 #define STATUSRSSI_RSSIr(x) (((x) & STATUSRSSI_RSSI) >> 9)
93 #define STATUSRSSI_FM_TRUE (0x1 << 8)
95 static const uint16_t initvals[16] = {
96 0x0000, 0x0000, 0xC401, 0x1B90,
97 0x0400, 0x866F, 0x8000, 0x4712,
98 0x5EC6, 0x0000, 0x406E, 0x2D80,
99 0x5803, 0x5804, 0x5804, 0x5804
102 static bool tuner_present = false;
103 static uint16_t cache[16];
105 /* reads <len> registers from radio at offset 0x0A into cache */
106 static void rda5802_read(int len)
108 int i;
109 unsigned char buf[128];
110 unsigned char *ptr = buf;
111 uint16_t data;
113 fmradio_i2c_read(I2C_ADR, buf, len * 2);
114 for (i = 0; i < len; i++) {
115 data = ptr[0] << 8 | ptr[1];
116 cache[READCHAN + i] = data;
117 ptr += 2;
121 /* writes <len> registers from cache to radio at offset 0x02 */
122 static void rda5802_write(int len)
124 int i;
125 unsigned char buf[64];
126 unsigned char *ptr = buf;
127 uint16_t data;
129 for (i = 0; i < len; i++) {
130 data = cache[POWERCFG + i];
131 *ptr++ = (data >> 8) & 0xFF;
132 *ptr++ = data & 0xFF;
134 fmradio_i2c_write(I2C_ADR, buf, len * 2);
137 static uint16_t rda5802_read_reg(int reg)
139 rda5802_read((reg - READCHAN) + 1);
140 return cache[reg];
143 static void rda5802_write_reg(int reg, uint16_t value)
145 cache[reg] = value;
148 static void rda5802_write_cache(void)
150 rda5802_write(5);
153 static void rda5802_write_masked(int reg, uint16_t bits, uint16_t mask)
155 rda5802_write_reg(reg, (cache[reg] & ~mask) | (bits & mask));
158 static void rda5802_write_clear(int reg, uint16_t mask)
160 rda5802_write_reg(reg, cache[reg] & ~mask);
163 static void rda5802_write_set(int reg, uint16_t mask)
165 rda5802_write_reg(reg, cache[reg] | mask);
168 static void rda5802_sleep(int snooze)
170 if (snooze) {
171 rda5802_write_clear(POWERCFG, POWERCFG_ENABLE);
173 else {
174 rda5802_write_set(POWERCFG, POWERCFG_ENABLE);
176 rda5802_write_cache();
179 bool rda5802_detect(void)
181 return ((rda5802_read_reg(IDENT) & 0xFF00) == 0x5800);
184 void rda5802_init(void)
186 if (rda5802_detect()) {
187 tuner_present = true;
189 // soft-reset
190 rda5802_write_reg(POWERCFG, POWERCFG_SOFT_RESET);
191 rda5802_write(1);
192 sleep(HZ * 10 / 1000);
194 // write initialisation values
195 memcpy(cache, initvals, sizeof(cache));
196 rda5802_write(14);
197 sleep(HZ * 70 / 1000);
201 static void rda5802_set_frequency(int freq)
203 int i;
204 uint16_t readchan;
206 /* check BAND and spacings */
207 int start = CHANNEL_BANDr(cache[CHANNEL]) & 1 ? 76000000 : 87000000;
208 int chan = (freq - start) / 50000;
210 for (i = 0; i < 5; i++) {
211 /* tune and wait a bit */
212 rda5802_write_masked(CHANNEL, CHANNEL_CHANw(chan) | CHANNEL_TUNE,
213 CHANNEL_CHAN | CHANNEL_TUNE);
214 rda5802_write_cache();
215 sleep(HZ * 70 / 1000);
216 rda5802_write_clear(CHANNEL, CHANNEL_TUNE);
217 rda5802_write_cache();
219 /* check if tuning was successful */
220 readchan = rda5802_read_reg(READCHAN);
221 if (readchan & READCHAN_STC) {
222 if (READCHAN_READCHANr(readchan) == chan) {
223 break;
229 static int rda5802_tuned(void)
231 /* Primitive tuning check: sufficient level and AFC not railed */
232 uint16_t status = rda5802_read_reg(STATUSRSSI);
233 if (STATUSRSSI_RSSIr(status) >= SEEK_THRESHOLD &&
234 (status & STATUSRSSI_FM_TRUE)) {
235 return 1;
238 return 0;
241 static void rda5802_set_region(int region)
243 const struct fm_region_data *rd = &fm_region_data[region];
244 int band = (rd->freq_min == 76000000) ?
245 CHANNEL_BAND_760_900 : CHANNEL_BAND_870_1080;
246 int deemphasis = (rd->deemphasis == 50) ? SYSCONFIG1_DE : 0;
248 uint16_t bandspacing = CHANNEL_BANDw(band) |
249 CHANNEL_SPACEw(CHANNEL_SPACE_50KHZ);
250 rda5802_write_masked(SYSCONFIG1, deemphasis, SYSCONFIG1_DE);
251 rda5802_write_masked(CHANNEL, bandspacing, CHANNEL_BAND | CHANNEL_SPACE);
252 rda5802_write_cache();
255 static bool rda5802_st(void)
257 return (rda5802_read_reg(READCHAN) & READCHAN_ST);
260 static int rda5802_rssi(void)
262 uint16_t status = rda5802_read_reg(STATUSRSSI);
263 return STATUSRSSI_RSSIr(status);
266 /* tuner abstraction layer: set something to the tuner */
267 int rda5802_set(int setting, int value)
269 switch (setting) {
270 case RADIO_SLEEP:
271 if (value != 2) {
272 rda5802_sleep(value);
274 break;
276 case RADIO_FREQUENCY:
277 rda5802_set_frequency(value);
278 break;
280 case RADIO_SCAN_FREQUENCY:
281 rda5802_set_frequency(value);
282 return rda5802_tuned();
284 case RADIO_MUTE:
285 rda5802_write_masked(POWERCFG, value ? 0 : POWERCFG_DMUTE,
286 POWERCFG_DMUTE);
287 rda5802_write_masked(SYSCONFIG1, (3 << 9), (3 << 9));
288 rda5802_write_set(SYSCONFIG2, SYSCONFIG2_VOLUME);
289 rda5802_write_cache();
290 break;
292 case RADIO_REGION:
293 rda5802_set_region(value);
294 break;
296 case RADIO_FORCE_MONO:
297 rda5802_write_masked(POWERCFG, value ? POWERCFG_MONO : 0,
298 POWERCFG_MONO);
299 rda5802_write_cache();
300 break;
302 default:
303 return -1;
306 return 1;
309 /* tuner abstraction layer: read something from the tuner */
310 int rda5802_get(int setting)
312 int val = -1; /* default for unsupported query */
314 switch (setting) {
315 case RADIO_PRESENT:
316 val = tuner_present ? 1 : 0;
317 break;
319 case RADIO_TUNED:
320 val = rda5802_tuned();
321 break;
323 case RADIO_STEREO:
324 val = rda5802_st();
325 break;
327 case RADIO_RSSI:
328 val = rda5802_rssi();
329 break;
331 case RADIO_RSSI_MIN:
332 val = RSSI_MIN;
333 break;
335 case RADIO_RSSI_MAX:
336 val = RSSI_MAX;
337 break;
340 return val;
343 void rda5802_dbg_info(struct rda5802_dbg_info *nfo)
345 rda5802_read(6);
346 memcpy(nfo->regs, cache, sizeof (nfo->regs));