Update the LV24020 FM driver so that it can be used on the D2. Register read/write...
[Rockbox.git] / firmware / drivers / tuner / lv24020lp.c
bloba1af38c89d30a7215759e35884f5caa27403a789
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 * Tuner driver for the Sanyo LV24020LP
11 * Copyright (C) 2007 Ivan Zupan
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include "config.h"
24 #include "thread.h"
25 #include "kernel.h"
26 #include "tuner.h" /* tuner abstraction interface */
27 #include "power.h"
28 #include "fmradio.h" /* physical interface driver */
29 #include "sound.h"
30 #include "pp5024.h"
31 #include "system.h"
33 #ifndef BOOTLOADER
35 static struct mutex tuner_mtx;
37 #if 0
38 /* define to enable tuner logging */
39 #define SANYO_TUNER_LOG
40 #endif
42 #ifdef SANYO_TUNER_LOG
43 #include "sprintf.h"
44 #include "file.h"
46 static int fd_log = -1;
48 #define TUNER_LOG_OPEN() if (fd_log < 0) \
49 fd_log = creat("/tuner_dump.txt")
50 /* syncing required because close() is never called */
51 #define TUNER_LOG_SYNC() fsync(fd_log)
52 #define TUNER_LOG(s...) fdprintf(fd_log, s)
53 #else
54 #define TUNER_LOG_OPEN()
55 #define TUNER_LOG_SYNC()
56 #define TUNER_LOG(s...)
57 #endif /* SANYO_TUNER_LOG */
59 /** tuner register defines **/
61 #if defined(SANSA_E200) || defined(SANSA_C200)
62 #define GPIO_OUTPUT_EN GPIOH_OUTPUT_EN
63 #define GPIO_OUTPUT_VAL GPIOH_OUTPUT_VAL
64 #define GPIO_INPUT_VAL GPIOH_INPUT_VAL
65 #define FM_NRW_PIN 3
66 #define FM_CLOCK_PIN 4
67 #define FM_DATA_PIN 5
68 #elif defined(COWON_D2)
69 #define GPIO_OUTPUT_EN GPIOC_DIR
70 #define GPIO_OUTPUT_VAL GPIOC
71 #define GPIO_INPUT_VAL GPIOC
72 #define FM_NRW_PIN 31
73 #define FM_CLOCK_PIN 29
74 #define FM_DATA_PIN 30
75 #define udelay(x) /* Remove hack when D2 has udelay */
76 #else
77 #error GPIOs undefined for this target
78 #endif
80 #define FM_CLK_DELAY 1
82 /* block 1 registers */
84 /* R */
85 #define CHIP_ID 0x00
87 /* W */
88 #define BLK_SEL 0x01
89 #define BLK1 0x01
90 #define BLK2 0x02
92 /* W */
93 #define MSRC_SEL 0x02
94 #define MSR_O (1 << 7)
95 #define AFC_LVL (1 << 6)
96 #define AFC_SPD (1 << 5)
97 #define MSS_SD (1 << 2)
98 #define MSS_FM (1 << 1)
99 #define MSS_IF (1 << 0)
101 /* W */
102 #define FM_OSC 0x03
104 /* W */
105 #define SD_OSC 0x04
107 /* W */
108 #define IF_OSC 0x05
110 /* W */
111 #define CNT_CTRL 0x06
112 #define CNT1_CLR (1 << 7)
113 #define CTAB(x) ((x) & (0x7 << 4))
114 #define CTAB_STOP_2 (0x0 << 4)
115 #define CTAB_STOP_8 (0x1 << 4)
116 #define CTAB_STOP_32 (0x2 << 4)
117 #define CTAB_STOP_128 (0x3 << 4)
118 #define CTAB_STOP_512 (0x4 << 4)
119 #define CTAB_STOP_2048 (0x5 << 4)
120 #define CTAB_STOP_8192 (0x6 << 4)
121 #define CTAB_STOP_32768 (0x7 << 4)
122 #define SWP_CNT_L (1 << 3)
123 #define CNT_EN (1 << 2)
124 #define CNT_SEL (1 << 1)
125 #define CNT_SET (1 << 0)
127 /* W */
128 #define IRQ_MSK 0x08
129 #define IM_MS (1 << 6)
130 #define IRQ_LVL (1 << 3)
131 #define IM_AFC (1 << 2)
132 #define IM_FS (1 << 1)
133 #define IM_CNT2 (1 << 0)
135 /* W */
136 #define FM_CAP 0x09
138 /* R */
139 #define CNT_L 0x0a /* Counter register low value */
141 /* R */
142 #define CNT_H 0x0b /* Counter register high value */
144 /* R */
145 #define CTRL_STAT 0x0c
146 #define AFC_FLG (1 << 0)
148 /* R */
149 #define RADIO_STAT 0x0d
150 #define RSS_MS (1 << 7)
151 #define RSS_FS(x) ((x) & 0x7f)
152 #define RSS_FS_GET(x) ((x) & 0x7f)
153 #define RSS_FS_SET(x) (x)
154 /* Note: Reading this register will clear field strength and mono/stereo interrupt. */
156 /* R */
157 #define IRQ_ID 0x0e
158 #define II_CNT2 (1 << 5)
159 #define II_AFC (1 << 3)
160 #define II_FS_MS (1 << 0)
162 /* W */
163 #define IRQ_OUT 0x0f
165 /* block 2 registers - offset added in order to id and avoid manual
166 switching */
167 #define BLK2_START 0x10
169 /* W */
170 #define RADIO_CTRL1 (0x02 + BLK2_START)
171 #define EN_MEAS (1 << 7)
172 #define EN_AFC (1 << 6)
173 #define DIR_AFC (1 << 3)
174 #define RST_AFC (1 << 2)
176 /* W */
177 #define IF_CENTER (0x03 + BLK2_START)
179 /* W */
180 #define IF_BW (0x05 + BLK2_START)
182 /* W */
183 #define RADIO_CTRL2 (0x06 + BLK2_START)
184 #define VREF2 (1 << 7)
185 #define VREF (1 << 6)
186 #define STABI_BP (1 << 5)
187 #define IF_PM_L (1 << 4)
188 #define AGCSP (1 << 1)
189 #define AM_ANT_BSW (1 << 0) /* ?? */
191 /* W */
192 #define RADIO_CTRL3 (0x07 + BLK2_START)
193 #define AGC_SLVL (1 << 7)
194 #define VOLSH (1 << 6)
195 #define TB_ON (1 << 5)
196 #define AMUTE_L (1 << 4)
197 #define SE_FM (1 << 3)
198 #define SE_BE (1 << 1)
199 #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */
201 /* W */
202 #define STEREO_CTRL (0x08 + BLK2_START)
203 #define FRCST (1 << 7)
204 #define FMCS(x) ((x) & (0x7 << 4))
205 #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4)
206 #define FMCS_SET(x) ((x) << 4)
207 #define AUTOSSR (1 << 3)
208 #define PILTCA (1 << 2)
209 #define SD_PM (1 << 1)
210 #define ST_M (1 << 0)
212 /* W */
213 #define AUDIO_CTRL1 (0x09 + BLK2_START)
214 #define TONE_LVL(x) ((x) & (0xf << 4))
215 #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4)
216 #define TONE_LVL_SET(x) ((x) << 4)
217 #define VOL_LVL(x) ((x) & 0xf)
218 #define VOL_LVL_GET(x) ((x) & 0xf)
219 #define VOL_LVL_SET(x) ((x) << 4)
221 /* W */
222 #define AUDIO_CTRL2 (0x0a + BLK2_START)
223 #define BASS_PP (1 << 0)
224 #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */
225 #define BASS_N (1 << 2)
226 #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */
227 #define TREB_N (1 << 4)
228 #define DEEMP (1 << 5)
229 #define BPFREQ(x) ((x) & (0x3 << 6))
230 #define BPFREQ_2_0K (0x0 << 6)
231 #define BPFREQ_1_0K (0x1 << 6)
232 #define BPFREQ_0_5K (0x2 << 6)
233 #define BPFREQ_HIGH (0x3 << 6)
235 /* W */
236 #define PW_SCTRL (0x0b + BLK2_START)
237 #define SS_CTRL(x) ((x) & (0x7 << 5))
238 #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5)
239 #define SS_CTRL_SET(x) ((x) << 5)
240 #define SM_CTRL(x) ((x) & (0x7 << 2))
241 #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2)
242 #define SM_CTRL_SET(x) ((x) << 2)
243 #define PW_HPA (1 << 1) /* LV24002 only */
244 #define PW_RAD (1 << 0)
246 /* shadow for writeable registers */
247 #define TUNER_POWERED (1 << 0)
248 #define TUNER_PRESENT (1 << 1)
249 #define TUNER_AWAKE (1 << 2)
250 #define TUNER_PRESENCE_CHECKED (1 << 3)
251 static unsigned tuner_status = 0;
253 static unsigned char lv24020lp_regs[0x1c];
255 static const int sw_osc_low = 10; /* 30; */
256 static const int sw_osc_high = 240; /* 200; */
257 static const int sw_cap_low = 0;
258 static const int sw_cap_high = 191;
260 /* linear coefficients used for tuning */
261 static int coef_00, coef_01, coef_10, coef_11;
263 /* DAC control register set values */
264 static int if_set, sd_set;
266 static inline bool tuner_awake(void)
268 return (tuner_status & TUNER_AWAKE) != 0;
271 /* send a byte to the tuner - expects write mode to be current */
272 static void lv24020lp_send_byte(unsigned int byte)
274 int i;
276 for (i = 0; i < 8; i++)
278 GPIO_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN);
280 if (byte & 1)
281 GPIO_OUTPUT_VAL |= (1 << FM_DATA_PIN);
282 else
283 GPIO_OUTPUT_VAL &= ~(1 << FM_DATA_PIN);
285 udelay(FM_CLK_DELAY);
287 GPIO_OUTPUT_VAL |= (1 << FM_CLOCK_PIN);
288 udelay(FM_CLK_DELAY);
290 byte >>= 1;
294 /* end a write cycle on the tuner */
295 static void lv24020lp_end_write(void)
297 /* switch back to read mode */
298 GPIO_OUTPUT_EN &= ~(1 << FM_DATA_PIN);
299 GPIO_OUTPUT_VAL &= ~(1 << FM_NRW_PIN);
300 udelay(FM_CLK_DELAY);
303 /* prepare a write cycle on the tuner */
304 static unsigned int lv24020lp_begin_write(unsigned int address)
306 /* Get register's block, translate address */
307 unsigned int blk = (address >= BLK2_START) ?
308 (address -= BLK2_START, BLK2) : BLK1;
310 for (;;)
312 /* Prepare 3-wire bus pins for write cycle */
313 GPIO_OUTPUT_VAL |= (1 << FM_NRW_PIN);
314 GPIO_OUTPUT_EN |= (1 << FM_DATA_PIN);
315 udelay(FM_CLK_DELAY);
317 /* current block == register block? */
318 if (blk == lv24020lp_regs[BLK_SEL])
319 return address;
321 /* switch block */
322 lv24020lp_regs[BLK_SEL] = blk;
324 /* data first */
325 lv24020lp_send_byte(blk);
326 /* then address */
327 lv24020lp_send_byte(BLK_SEL);
329 lv24020lp_end_write();
333 /* write a byte to a tuner register */
334 static void lv24020lp_write(unsigned int address, unsigned int data)
336 /* shadow logical values but do logical=>physical remappings on some
337 registers' data. */
338 lv24020lp_regs[address] = data;
340 switch (address)
342 case FM_OSC:
343 /* L: 000..255
344 * P: 255..000 */
345 data = 255 - data;
346 break;
347 case FM_CAP:
348 /* L: 000..063, 064..191
349 * P: 255..192, 127..000 */
350 data = ((data < 64) ? 255 : (255 - 64)) - data;
351 break;
352 case RADIO_CTRL1:
353 /* L: data
354 * P: data | always "1" bits */
355 data |= (1 << 4) | (1 << 1) | (1 << 0);
356 break;
359 /* Check if interface is turned on */
360 if (!(tuner_status & TUNER_POWERED))
361 return;
363 address = lv24020lp_begin_write(address);
365 /* data first */
366 lv24020lp_send_byte(data);
367 /* then address */
368 lv24020lp_send_byte(address);
370 lv24020lp_end_write();
373 /* helpers to set/clear register bits */
374 static void lv24020lp_write_set(unsigned int address, unsigned int bits)
376 lv24020lp_write(address, lv24020lp_regs[address] | bits);
379 static void lv24020lp_write_clear(unsigned int address, unsigned int bits)
381 lv24020lp_write(address, lv24020lp_regs[address] & ~bits);
384 /* read a byte from a tuner register */
385 static unsigned int lv24020lp_read(unsigned int address)
387 int i;
388 unsigned int toread;
390 /* Check if interface is turned on */
391 if (!(tuner_status & TUNER_POWERED))
392 return 0;
394 address = lv24020lp_begin_write(address);
396 /* address */
397 lv24020lp_send_byte(address);
399 lv24020lp_end_write();
401 /* data */
402 toread = 0;
403 for (i = 0; i < 8; i++)
405 GPIO_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN);
406 udelay(FM_CLK_DELAY);
408 if (GPIO_INPUT_VAL & (1 << FM_DATA_PIN))
409 toread |= (1 << i);
411 GPIO_OUTPUT_VAL |= (1 << FM_CLOCK_PIN);
412 udelay(FM_CLK_DELAY);
415 return toread;
418 /* enables auto frequency centering */
419 static void enable_afc(bool enabled)
421 unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1];
423 if (enabled)
425 radio_ctrl1 &= ~RST_AFC;
426 radio_ctrl1 |= EN_AFC;
428 else
430 radio_ctrl1 |= RST_AFC;
431 radio_ctrl1 &= ~EN_AFC;
434 lv24020lp_write(RADIO_CTRL1, radio_ctrl1);
437 static int calculate_coef(unsigned fkhz)
439 /* Overflow below 66000kHz --
440 My tuner tunes down to a min of ~72600kHz but datasheet mentions
441 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */
442 return fkhz < 66000 ?
443 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz);
446 static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2)
448 return y1 == y2 ?
449 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1;
452 static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2)
454 return x1 == x2 ?
455 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1;
458 /* this performs measurements of IF, FM and Stereo frequencies
459 * Input can be: MSS_FM, MSS_IF, MSS_SD */
460 static int tuner_measure(unsigned char type, int scale, int duration)
462 int64_t finval;
464 /* enable measuring */
465 lv24020lp_write_set(MSRC_SEL, type);
466 lv24020lp_write_clear(CNT_CTRL, CNT_SEL);
467 lv24020lp_write_set(RADIO_CTRL1, EN_MEAS);
469 /* reset counter */
470 lv24020lp_write_set(CNT_CTRL, CNT1_CLR);
471 lv24020lp_write_clear(CNT_CTRL, CNT1_CLR);
473 /* start counter, delay for specified time and stop it */
474 lv24020lp_write_set(CNT_CTRL, CNT_EN);
475 udelay(duration*1000 - 16);
476 lv24020lp_write_clear(CNT_CTRL, CNT_EN);
478 /* read tick count */
479 finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L);
481 /* restore measure mode */
482 lv24020lp_write_clear(RADIO_CTRL1, EN_MEAS);
483 lv24020lp_write_clear(MSRC_SEL, type);
485 /* convert value */
486 if (type == MSS_FM)
487 finval = scale*finval*256 / duration;
488 else
489 finval = scale*finval / duration;
491 /* This function takes a loooong time and other stuff needs
492 running by now */
493 yield();
495 return (int)finval;
498 /* set the FM oscillator frequency */
499 static void set_frequency(int freq)
501 int coef, cap_value, osc_value;
502 int f1, f2, x1, x2;
503 int count;
505 TUNER_LOG_OPEN();
507 TUNER_LOG("set_frequency(%d)\n", freq);
509 enable_afc(false);
511 /* MHz -> kHz */
512 freq /= 1000;
514 TUNER_LOG("Select cap:\n");
516 coef = calculate_coef(freq);
517 cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high,
518 coef_00, coef_01);
520 osc_value = sw_osc_low;
521 lv24020lp_write(FM_OSC, osc_value);
523 /* Just in case - don't go into infinite loop */
524 for (count = 0; count < 30; count++)
526 int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
527 coef_00, coef_01);
528 int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
529 coef_10, coef_11);
530 int coef_fcur, cap_new, coef_cor, range;
532 lv24020lp_write(FM_CAP, cap_value);
534 range = y1 - y0;
535 f1 = tuner_measure(MSS_FM, 1, 16);
536 coef_fcur = calculate_coef(f1);
537 coef_cor = calculate_coef((f1*1000 + 32*256) / 1000);
538 y0 = coef_cor;
539 y1 = y0 + range;
541 TUNER_LOG("%d %d %d %d %d %d %d %d\n",
542 f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range);
544 if (coef >= y0 && coef <= y1)
546 osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high,
547 y0, y1);
549 if (osc_value >= sw_osc_low && osc_value <= sw_osc_high)
550 break;
553 cap_new = interpolate_x(coef, cap_value, sw_cap_high,
554 coef_fcur, coef_01);
556 if (cap_new == cap_value)
558 if (coef < coef_fcur)
559 cap_value++;
560 else
561 cap_value--;
563 else
565 cap_value = cap_new;
569 TUNER_LOG("osc_value: %d\n", osc_value);
571 TUNER_LOG("Tune:\n");
573 x1 = sw_osc_low, x2 = sw_osc_high;
574 /* FM_OSC already at SW_OSC low and f1 is already the measured
575 frequency */
579 int x2_new;
581 lv24020lp_write(FM_OSC, x2);
582 f2 = tuner_measure(MSS_FM, 1, 16);
584 if (abs(f2 - freq) <= 16)
586 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
587 break;
590 x2_new = interpolate_x(freq, x1, x2, f1, f2);
592 x1 = x2, f1 = f2, x2 = x2_new;
593 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
595 while (x2 != 0);
597 if (x2 == 0)
599 /* May still be close enough */
600 TUNER_LOG("tuning failed - diff: %d\n", f2 - freq);
603 enable_afc(true);
605 TUNER_LOG("\n");
607 TUNER_LOG_SYNC();
610 #define TOO_SMALL (1 << 0)
611 #define TOO_BIG (1 << 1)
612 #define APPROACH_UP_1 (1 << 2)
613 #define APPROACH_DOWN_1 (1 << 3)
615 static void fine_step_tune(int (*setcmp)(int regval), int regval, int step)
617 /* Registers are not always stable, timeout if best fit not found soon
618 enough */
619 unsigned long abort = current_tick + HZ*2;
620 int flags = 0;
622 while (TIME_BEFORE(current_tick, abort))
624 int cmp;
626 regval = regval + step;
628 cmp = setcmp(regval);
630 if (cmp == 0)
631 break;
633 step = abs(step);
635 if (cmp < 0)
637 flags |= TOO_SMALL;
638 if (step == 1)
639 flags |= APPROACH_UP_1;
641 else
643 step = -step;
644 flags |= TOO_BIG;
645 if (step == -1)
646 step |= APPROACH_DOWN_1;
649 if ((flags & APPROACH_UP_1) && (flags & APPROACH_DOWN_1))
650 break; /* approached with step=1: best fit value found */
652 if ((flags & TOO_SMALL) && (flags & TOO_BIG))
654 step /= 2;
655 if (step == 0)
656 step = 1;
657 flags &= ~(TOO_SMALL | TOO_BIG);
662 static int if_setcmp(int regval)
664 lv24020lp_write(IF_OSC, regval);
665 lv24020lp_write(IF_CENTER, regval);
666 lv24020lp_write(IF_BW, 65*regval/100);
668 if_set = tuner_measure(MSS_IF, 1000, 32);
670 /* This register is bounces around by a few hundred Hz and doesn't seem
671 to be precisely tuneable. Just do 110000 +/- 500 since it's not very
672 critical it seems. */
673 if (abs(if_set - 110000) <= 500)
674 return 0;
676 return if_set < 110000 ? -1 : 1;
679 static int sd_setcmp(int regval)
681 lv24020lp_write(SD_OSC, regval);
683 sd_set = tuner_measure(MSS_SD, 1000, 32);
685 if (abs(sd_set - 38300) <= 31)
686 return 0;
688 return sd_set < 38300 ? -1 : 1;
691 static void set_sleep(bool sleep)
693 if (sleep || tuner_awake())
694 return;
696 if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) !=
697 (TUNER_PRESENT | TUNER_POWERED))
698 return;
700 enable_afc(false);
702 /* 2. Calibrate the IF frequency at 110 kHz: */
703 lv24020lp_write_clear(RADIO_CTRL2, IF_PM_L);
704 fine_step_tune(if_setcmp, 0x80, 8);
705 lv24020lp_write_set(RADIO_CTRL2, IF_PM_L);
707 /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */
708 lv24020lp_write_set(STEREO_CTRL, SD_PM);
709 fine_step_tune(sd_setcmp, 0x80, 8);
710 lv24020lp_write_clear(STEREO_CTRL, SD_PM);
712 /* calculate FM tuning coefficients */
713 lv24020lp_write(FM_CAP, sw_cap_low);
714 lv24020lp_write(FM_OSC, sw_osc_low);
715 coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
717 lv24020lp_write(FM_CAP, sw_cap_high);
718 coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
720 lv24020lp_write(FM_CAP, sw_cap_low);
721 lv24020lp_write(FM_OSC, sw_osc_high);
722 coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
724 lv24020lp_write(FM_CAP, sw_cap_high);
725 coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
727 /* set various audio level settings */
728 lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
729 lv24020lp_write_set(RADIO_CTRL2, AGCSP);
730 lv24020lp_write_set(RADIO_CTRL3, VOLSH);
731 lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
732 lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
733 PW_RAD);
735 tuner_status |= TUNER_AWAKE;
738 static int lp24020lp_tuned(void)
740 return RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f;
743 static int lv24020lp_debug_info(int setting)
745 int val = -1;
747 if (setting >= LV24020LP_DEBUG_FIRST && setting <= LV24020LP_DEBUG_LAST)
749 val = 0;
751 if (tuner_awake())
753 switch (setting)
755 /* tuner-specific debug info */
756 case LV24020LP_CTRL_STAT:
757 val = lv24020lp_read(CTRL_STAT);
758 break;
760 case LV24020LP_REG_STAT:
761 val = lv24020lp_read(RADIO_STAT);
762 break;
764 case LV24020LP_MSS_FM:
765 val = tuner_measure(MSS_FM, 1, 16);
766 break;
768 case LV24020LP_MSS_IF:
769 val = tuner_measure(MSS_IF, 1000, 16);
770 break;
772 case LV24020LP_MSS_SD:
773 val = tuner_measure(MSS_SD, 1000, 16);
774 break;
776 case LV24020LP_IF_SET:
777 val = if_set;
778 break;
780 case LV24020LP_SD_SET:
781 val = sd_set;
782 break;
787 return val;
790 /** Public interfaces **/
791 void lv24020lp_init(void)
793 mutex_init(&tuner_mtx);
796 void lv24020lp_lock(void)
798 mutex_lock(&tuner_mtx);
801 void lv24020lp_unlock(void)
803 mutex_unlock(&tuner_mtx);
806 /* This function expects the driver to be locked externally */
807 void lv24020lp_power(bool status)
809 static const unsigned char tuner_defaults[][2] =
811 /* Block 1 writeable registers */
812 { MSRC_SEL, AFC_LVL },
813 { FM_OSC, 0x80 },
814 { SD_OSC, 0x80 },
815 { IF_OSC, 0x80 },
816 { CNT_CTRL, CNT1_CLR | SWP_CNT_L },
817 { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */
818 { FM_CAP, 0x80 },
819 /* { IRQ_OUT, 0x00 }, No action on this register (skip) */
820 /* Block 2 writeable registers */
821 { RADIO_CTRL1, EN_AFC },
822 { IF_CENTER, 0x80 },
823 { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */
824 { RADIO_CTRL2, IF_PM_L },
825 { RADIO_CTRL3, AGC_SLVL | SE_FM },
826 { STEREO_CTRL, FMCS_SET(4) | AUTOSSR },
827 { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) },
828 { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */
829 { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD },
832 unsigned i;
834 if (status)
836 tuner_status |= (TUNER_PRESENCE_CHECKED | TUNER_POWERED);
838 /* if tuner is present, CHIP ID is 0x09 */
839 if (lv24020lp_read(CHIP_ID) == 0x09)
841 tuner_status |= TUNER_PRESENT;
843 /* After power-up, the LV2400x needs to be initialized as
844 follows: */
846 /* 1. Write default values to the registers: */
847 lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */
848 for (i = 0; i < ARRAYLEN(tuner_defaults); i++)
849 lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]);
851 /* Complete the startup calibration if the tuner is woken */
852 sleep(HZ/10);
855 else
857 /* Power off */
858 if (tuner_status & TUNER_PRESENT)
859 lv24020lp_write_clear(PW_SCTRL, PW_RAD);
861 tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE);
865 int lv24020lp_set(int setting, int value)
867 int val = 1;
869 mutex_lock(&tuner_mtx);
871 switch(setting)
873 case RADIO_SLEEP:
874 set_sleep(value);
875 break;
877 case RADIO_FREQUENCY:
878 set_frequency(value);
879 break;
881 case RADIO_SCAN_FREQUENCY:
882 /* TODO: really implement this */
883 set_frequency(value);
884 val = lp24020lp_tuned();
885 break;
887 case RADIO_MUTE:
888 if (value)
889 lv24020lp_write_clear(RADIO_CTRL3, AMUTE_L);
890 else
891 lv24020lp_write_set(RADIO_CTRL3, AMUTE_L);
892 break;
894 case RADIO_REGION:
895 if (lv24020lp_region_data[value])
896 lv24020lp_write_set(AUDIO_CTRL2, DEEMP);
897 else
898 lv24020lp_write_clear(AUDIO_CTRL2, DEEMP);
899 break;
901 case RADIO_FORCE_MONO:
902 if (value)
903 lv24020lp_write_set(STEREO_CTRL, ST_M);
904 else
905 lv24020lp_write_clear(STEREO_CTRL, ST_M);
906 break;
908 default:
909 value = -1;
912 mutex_unlock(&tuner_mtx);
914 return val;
917 int lv24020lp_get(int setting)
919 int val = -1;
921 mutex_lock(&tuner_mtx);
923 switch(setting)
925 case RADIO_TUNED:
926 /* TODO: really implement this */
927 val = lp24020lp_tuned();
928 break;
930 case RADIO_STEREO:
931 val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0;
932 break;
934 case RADIO_PRESENT:
936 bool fmstatus = true;
938 if (!(tuner_status & TUNER_PRESENCE_CHECKED))
939 fmstatus = tuner_power(true);
941 val = (tuner_status & TUNER_PRESENT) != 0;
943 if (!fmstatus)
944 tuner_power(false);
945 break;
948 default:
949 val = lv24020lp_debug_info(setting);
952 mutex_unlock(&tuner_mtx);
954 return val;
956 #endif /* BOOTLOADER */