Prepare new maemo release
[maemo-rb.git] / firmware / drivers / tuner / lv24020lp.c
blob533445f110d66c8a527fe7c3849bf515652b843c
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 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include "config.h"
26 #include "thread.h"
27 #include "kernel.h"
28 #include "tuner.h" /* tuner abstraction interface */
29 #include "power.h"
30 #include "fmradio.h" /* physical interface driver */
31 #include "sound.h"
32 #include "system.h"
34 #ifndef BOOTLOADER
36 static struct mutex tuner_mtx;
38 /* define RSSI range */
39 #define RSSI_MIN 5
40 #define RSSI_MAX 75
42 /* define to enable tuner logging */
43 #undef SANYO_TUNER_LOG_FILE
44 #undef SANYO_TUNER_LOGF
46 #ifdef SANYO_TUNER_LOG_FILE
47 #include "file.h"
49 static int fd_log = -1;
51 #define TUNER_LOG_OPEN() if (fd_log < 0) \
52 fd_log = creat("/tuner_dump.txt", 0666)
53 /* syncing required because close() is never called */
54 #define TUNER_LOG_SYNC() fsync(fd_log)
55 #define TUNER_LOG(s...) fdprintf(fd_log, s)
57 #elif defined(SANYO_TUNER_LOGF)
58 /*#define LOGF_ENABLE*/
59 #include "logf.h"
61 #define TUNER_LOG_OPEN()
62 #define TUNER_LOG_SYNC()
63 #define TUNER_LOG(s...) logf(s)
65 #else
66 #define TUNER_LOG_OPEN()
67 #define TUNER_LOG_SYNC()
68 #define TUNER_LOG(s...)
69 #endif /* SANYO_TUNER_LOG */
71 /** tuner register defines **/
73 #if defined(SANSA_E200) || defined(SANSA_C200)
74 #define TUNER_GPIO_INPUT_VAL GPIOH_INPUT_VAL
75 #define TUNER_GPIO_OUTPUT_EN_SET(mask) GPIO_SET_BITWISE(GPIOH_OUTPUT_EN, mask)
76 #define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) GPIO_CLEAR_BITWISE(GPIOH_OUTPUT_EN, mask)
77 #define TUNER_GPIO_OUTPUT_VAL_SET(mask) GPIO_SET_BITWISE(GPIOH_OUTPUT_VAL, mask)
78 #define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) GPIO_CLEAR_BITWISE(GPIOH_OUTPUT_VAL, mask)
79 #define FM_NRW_PIN 3
80 #define FM_CLOCK_PIN 4
81 #define FM_DATA_PIN 5
83 #elif defined(IAUDIO_7)
84 #define TUNER_GPIO_INPUT_VAL GPIOA
85 #define TUNER_GPIO_OUTPUT_EN_SET(mask) (GPIOA_DIR |= (mask))
86 #define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) (GPIOA_DIR &= ~(mask))
87 #define TUNER_GPIO_OUTPUT_VAL_SET(mask) (GPIOA |= (mask))
88 #define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) (GPIOA &= ~(mask))
89 #define FM_CLOCK_PIN 5
90 #define FM_DATA_PIN 6
91 #define FM_NRW_PIN 7
93 #elif defined(COWON_D2)
94 #define TUNER_GPIO_INPUT_VAL GPIOC
95 #define TUNER_GPIO_OUTPUT_EN_SET(mask) (GPIOC_DIR |= (mask))
96 #define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) (GPIOC_DIR &= ~(mask))
97 #define TUNER_GPIO_OUTPUT_VAL_SET(mask) (GPIOC |= (mask))
98 #define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) (GPIOC &= ~(mask))
99 #define FM_NRW_PIN 31
100 #define FM_CLOCK_PIN 29
101 #define FM_DATA_PIN 30
103 #else
104 #error GPIOs undefined for this target
105 #endif
107 #define FM_CLK_DELAY 1
109 /* block 1 registers */
111 /* R */
112 #define CHIP_ID 0x00
114 /* W */
115 #define BLK_SEL 0x01
116 #define BLK1 0x01
117 #define BLK2 0x02
119 /* W */
120 #define MSRC_SEL 0x02
121 #define MSR_O (1 << 7)
122 #define AFC_LVL (1 << 6)
123 #define AFC_SPD (1 << 5)
124 #define MSS_SD (1 << 2)
125 #define MSS_FM (1 << 1)
126 #define MSS_IF (1 << 0)
128 /* W */
129 #define FM_OSC 0x03
131 /* W */
132 #define SD_OSC 0x04
134 /* W */
135 #define IF_OSC 0x05
137 /* W */
138 #define CNT_CTRL 0x06
139 #define CNT1_CLR (1 << 7)
140 #define CTAB(x) ((x) & (0x7 << 4))
141 #define CTAB_STOP_2 (0x0 << 4)
142 #define CTAB_STOP_8 (0x1 << 4)
143 #define CTAB_STOP_32 (0x2 << 4)
144 #define CTAB_STOP_128 (0x3 << 4)
145 #define CTAB_STOP_512 (0x4 << 4)
146 #define CTAB_STOP_2048 (0x5 << 4)
147 #define CTAB_STOP_8192 (0x6 << 4)
148 #define CTAB_STOP_32768 (0x7 << 4)
149 #define SWP_CNT_L (1 << 3)
150 #define CNT_EN (1 << 2)
151 #define CNT_SEL (1 << 1)
152 #define CNT_SET (1 << 0)
154 /* W */
155 #define IRQ_MSK 0x08
156 #define IM_MS (1 << 6)
157 #define IRQ_LVL (1 << 3)
158 #define IM_AFC (1 << 2)
159 #define IM_FS (1 << 1)
160 #define IM_CNT2 (1 << 0)
162 /* W */
163 #define FM_CAP 0x09
165 /* R */
166 #define CNT_L 0x0a /* Counter register low value */
168 /* R */
169 #define CNT_H 0x0b /* Counter register high value */
171 /* R */
172 #define CTRL_STAT 0x0c
173 #define AFC_FLG (1 << 0)
175 /* R */
176 #define RADIO_STAT 0x0d
177 #define RSS_MS (1 << 7)
178 #define RSS_FS(x) ((x) & 0x7f)
179 #define RSS_FS_GET(x) ((x) & 0x7f)
180 #define RSS_FS_SET(x) (x)
181 /* Note: Reading this register will clear field strength and mono/stereo interrupt. */
183 /* R */
184 #define IRQ_ID 0x0e
185 #define II_CNT2 (1 << 5)
186 #define II_AFC (1 << 3)
187 #define II_FS_MS (1 << 0)
189 /* W */
190 #define IRQ_OUT 0x0f
192 /* block 2 registers - offset added in order to id and avoid manual
193 switching */
194 #define BLK2_START 0x10
196 /* W */
197 #define RADIO_CTRL1 (0x02 + BLK2_START)
198 #define EN_MEAS (1 << 7)
199 #define EN_AFC (1 << 6)
200 #define DIR_AFC (1 << 3)
201 #define RST_AFC (1 << 2)
203 /* W */
204 #define IF_CENTER (0x03 + BLK2_START)
206 /* W */
207 #define IF_BW (0x05 + BLK2_START)
209 /* W */
210 #define RADIO_CTRL2 (0x06 + BLK2_START)
211 #define VREF2 (1 << 7)
212 #define VREF (1 << 6)
213 #define STABI_BP (1 << 5)
214 #define IF_PM_L (1 << 4)
215 #define AGCSP (1 << 1)
216 #define AM_ANT_BSW (1 << 0) /* ?? */
218 /* W */
219 #define RADIO_CTRL3 (0x07 + BLK2_START)
220 #define AGC_SLVL (1 << 7)
221 #define VOLSH (1 << 6)
222 #define TB_ON (1 << 5)
223 #define AMUTE_L (1 << 4)
224 #define SE_FM (1 << 3)
225 #define SE_BE (1 << 1)
226 #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */
228 /* W */
229 #define STEREO_CTRL (0x08 + BLK2_START)
230 #define FRCST (1 << 7)
231 #define FMCS(x) ((x) & (0x7 << 4))
232 #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4)
233 #define FMCS_SET(x) ((x) << 4)
234 #define AUTOSSR (1 << 3)
235 #define PILTCA (1 << 2)
236 #define SD_PM (1 << 1)
237 #define ST_M (1 << 0)
239 /* W */
240 #define AUDIO_CTRL1 (0x09 + BLK2_START)
241 #define TONE_LVL(x) ((x) & (0xf << 4))
242 #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4)
243 #define TONE_LVL_SET(x) ((x) << 4)
244 #define VOL_LVL(x) ((x) & 0xf)
245 #define VOL_LVL_GET(x) ((x) & 0xf)
246 #define VOL_LVL_SET(x) ((x) << 4)
248 /* W */
249 #define AUDIO_CTRL2 (0x0a + BLK2_START)
250 #define BASS_PP (1 << 0)
251 #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */
252 #define BASS_N (1 << 2)
253 #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */
254 #define TREB_N (1 << 4)
255 #define DEEMP (1 << 5)
256 #define BPFREQ(x) ((x) & (0x3 << 6))
257 #define BPFREQ_2_0K (0x0 << 6)
258 #define BPFREQ_1_0K (0x1 << 6)
259 #define BPFREQ_0_5K (0x2 << 6)
260 #define BPFREQ_HIGH (0x3 << 6)
262 /* W */
263 #define PW_SCTRL (0x0b + BLK2_START)
264 #define SS_CTRL(x) ((x) & (0x7 << 5))
265 #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5)
266 #define SS_CTRL_SET(x) ((x) << 5)
267 #define SM_CTRL(x) ((x) & (0x7 << 2))
268 #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2)
269 #define SM_CTRL_SET(x) ((x) << 2)
270 #define PW_HPA (1 << 1) /* LV24002 only */
271 #define PW_RAD (1 << 0)
273 /* shadow for writeable registers */
274 #define TUNER_POWERED (1 << 0)
275 #define TUNER_PRESENT (1 << 1)
276 #define TUNER_AWAKE (1 << 2)
277 #define TUNER_PRESENCE_CHECKED (1 << 3)
278 static unsigned tuner_status = 0;
280 static unsigned char lv24020lp_regs[0x1c];
282 static const int sw_osc_low = 10; /* 30; */
283 static const int sw_osc_high = 240; /* 200; */
284 static const int sw_cap_low = 0;
285 static const int sw_cap_high = 191;
287 /* linear coefficients used for tuning */
288 static int coef_00, coef_01, coef_10, coef_11;
290 /* DAC control register set values */
291 static int if_set, sd_set;
293 static inline bool tuner_awake(void)
295 return (tuner_status & TUNER_AWAKE) != 0;
298 /* send a byte to the tuner - expects write mode to be current */
299 static void lv24020lp_send_byte(unsigned int byte)
301 int i;
303 for (i = 0; i < 8; i++)
305 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_CLOCK_PIN);
308 if (byte & 1)
309 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_DATA_PIN);
310 else
311 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_DATA_PIN);
313 udelay(FM_CLK_DELAY);
315 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_CLOCK_PIN);
316 udelay(FM_CLK_DELAY);
318 byte >>= 1;
322 /* end a write cycle on the tuner */
323 static void lv24020lp_end_write(void)
325 /* switch back to read mode */
326 TUNER_GPIO_OUTPUT_EN_CLEAR(1 << FM_DATA_PIN);
327 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_NRW_PIN);
328 udelay(FM_CLK_DELAY);
331 /* prepare a write cycle on the tuner */
332 static unsigned int lv24020lp_begin_write(unsigned int address)
334 /* Get register's block, translate address */
335 unsigned int blk = (address >= BLK2_START) ?
336 (address -= BLK2_START, BLK2) : BLK1;
338 for (;;)
340 /* Prepare 3-wire bus pins for write cycle */
341 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_NRW_PIN);
342 TUNER_GPIO_OUTPUT_EN_SET(1 << FM_DATA_PIN);
343 udelay(FM_CLK_DELAY);
345 /* current block == register block? */
346 if (blk == lv24020lp_regs[BLK_SEL])
347 return address;
349 /* switch block */
350 lv24020lp_regs[BLK_SEL] = blk;
352 /* data first */
353 lv24020lp_send_byte(blk);
354 /* then address */
355 lv24020lp_send_byte(BLK_SEL);
357 lv24020lp_end_write();
361 /* write a byte to a tuner register */
362 static void lv24020lp_write(unsigned int address, unsigned int data)
364 /* shadow logical values but do logical=>physical remappings on some
365 registers' data. */
366 lv24020lp_regs[address] = data;
368 switch (address)
370 case FM_OSC:
371 /* L: 000..255
372 * P: 255..000 */
373 data = 255 - data;
374 break;
375 case FM_CAP:
376 /* L: 000..063, 064..191
377 * P: 255..192, 127..000 */
378 data = ((data < 64) ? 255 : (255 - 64)) - data;
379 break;
380 case RADIO_CTRL1:
381 /* L: data
382 * P: data | always "1" bits */
383 data |= (1 << 4) | (1 << 1) | (1 << 0);
384 break;
387 /* Check if interface is turned on */
388 if (!(tuner_status & TUNER_POWERED))
389 return;
391 address = lv24020lp_begin_write(address);
393 /* data first */
394 lv24020lp_send_byte(data);
395 /* then address */
396 lv24020lp_send_byte(address);
398 lv24020lp_end_write();
401 /* helpers to set/clear register bits */
402 static void lv24020lp_write_set(unsigned int address, unsigned int bits)
404 lv24020lp_write(address, lv24020lp_regs[address] | bits);
407 static void lv24020lp_write_clear(unsigned int address, unsigned int bits)
409 lv24020lp_write(address, lv24020lp_regs[address] & ~bits);
412 /* read a byte from a tuner register */
413 static unsigned int lv24020lp_read(unsigned int address)
415 int i;
416 unsigned int toread;
418 /* Check if interface is turned on */
419 if (!(tuner_status & TUNER_POWERED))
420 return 0;
422 address = lv24020lp_begin_write(address);
424 /* address */
425 lv24020lp_send_byte(address);
427 lv24020lp_end_write();
429 /* data */
430 toread = 0;
431 for (i = 0; i < 8; i++)
433 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_CLOCK_PIN);
434 udelay(FM_CLK_DELAY);
436 if (TUNER_GPIO_INPUT_VAL & (1 << FM_DATA_PIN))
437 toread |= (1 << i);
439 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_CLOCK_PIN);
440 udelay(FM_CLK_DELAY);
443 return toread;
446 /* enables auto frequency centering */
447 static void enable_afc(bool enabled)
449 unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1];
451 if (enabled)
453 radio_ctrl1 &= ~RST_AFC;
454 radio_ctrl1 |= EN_AFC;
456 else
458 radio_ctrl1 |= RST_AFC;
459 radio_ctrl1 &= ~EN_AFC;
462 lv24020lp_write(RADIO_CTRL1, radio_ctrl1);
465 static int calculate_coef(unsigned fkhz)
467 /* Overflow below 66000kHz --
468 My tuner tunes down to a min of ~72600kHz but datasheet mentions
469 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */
470 return fkhz < 66000 ?
471 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz);
474 static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2)
476 return y1 == y2 ?
477 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1;
480 static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2)
482 return x1 == x2 ?
483 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1;
486 /* this performs measurements of IF, FM and Stereo frequencies
487 * Input can be: MSS_FM, MSS_IF, MSS_SD */
488 static int tuner_measure(unsigned char type, int scale, int duration)
490 int64_t finval;
492 /* enable measuring */
493 lv24020lp_write_set(MSRC_SEL, type);
494 lv24020lp_write_clear(CNT_CTRL, CNT_SEL);
495 lv24020lp_write_set(RADIO_CTRL1, EN_MEAS);
497 /* reset counter */
498 lv24020lp_write_set(CNT_CTRL, CNT1_CLR);
499 lv24020lp_write_clear(CNT_CTRL, CNT1_CLR);
501 /* start counter, delay for specified time and stop it */
502 lv24020lp_write_set(CNT_CTRL, CNT_EN);
504 #ifdef CPU_PP
505 /* obtain actual duration, including interrupts that occurred and
506 * the time to write the counter stop */
507 long usec = USEC_TIMER;
508 #endif
510 udelay(duration*1000);
512 lv24020lp_write_clear(CNT_CTRL, CNT_EN);
514 #ifdef CPU_PP
515 duration = (USEC_TIMER - usec) / 1000;
516 #endif
518 /* This function takes a loooong time and other stuff needs
519 running by now */
520 yield();
522 /* read tick count */
523 finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L);
525 /* restore measure mode */
526 lv24020lp_write_clear(RADIO_CTRL1, EN_MEAS);
527 lv24020lp_write_clear(MSRC_SEL, type);
529 /* convert value */
530 if (type == MSS_FM)
531 finval = scale*finval*256 / duration;
532 else
533 finval = scale*finval / duration;
535 return (int)finval;
538 /* set the FM oscillator frequency */
539 static void set_frequency(int freq)
541 int coef, cap_value, osc_value;
542 int f1, f2, x1, x2;
543 int count;
545 TUNER_LOG_OPEN();
547 TUNER_LOG("set_frequency(%d)\n", freq);
549 enable_afc(false);
551 /* For the LV2400x, the tuned frequency is the sum of the displayed
552 * frequency and the preset IF frequency, in formula:
553 * Tuned FM frequency = displayed frequency + preset IF frequency
555 * For example: when the IF frequency of LV2400x is preset at 110 kHz,
556 * it must be tuned at 88.51 MHz to receive the radio station at 88.4 MHz.
557 * -- AN2400S04@ – V0.4
559 freq += if_set;
561 /* MHz -> kHz */
562 freq /= 1000;
564 TUNER_LOG("Select cap:\n");
566 coef = calculate_coef(freq);
567 cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high,
568 coef_00, coef_01);
570 osc_value = sw_osc_low;
571 lv24020lp_write(FM_OSC, osc_value);
573 /* Just in case - don't go into infinite loop */
574 for (count = 0; count < 30; count++)
576 int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
577 coef_00, coef_01);
578 int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
579 coef_10, coef_11);
580 int coef_fcur, cap_new, coef_cor, range;
582 lv24020lp_write(FM_CAP, cap_value);
584 range = y1 - y0;
585 f1 = tuner_measure(MSS_FM, 1, 16);
586 coef_fcur = calculate_coef(f1);
587 coef_cor = calculate_coef((f1*1000 + 32*256) / 1000);
588 y0 = coef_cor;
589 y1 = y0 + range;
591 TUNER_LOG("%d %d %d %d %d %d %d %d\n",
592 f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range);
594 if (coef >= y0 && coef <= y1)
596 osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high,
597 y0, y1);
599 if (osc_value >= sw_osc_low && osc_value <= sw_osc_high)
600 break;
603 cap_new = interpolate_x(coef, cap_value, sw_cap_high,
604 coef_fcur, coef_01);
606 if (cap_new == cap_value)
608 if (coef < coef_fcur)
609 cap_value++;
610 else
611 cap_value--;
613 else
615 cap_value = cap_new;
619 TUNER_LOG("osc_value: %d\n", osc_value);
621 TUNER_LOG("Tune:\n");
623 x1 = sw_osc_low, x2 = sw_osc_high;
624 /* FM_OSC already at SW_OSC low and f1 is already the measured
625 frequency */
629 int x2_new;
631 lv24020lp_write(FM_OSC, x2);
632 f2 = tuner_measure(MSS_FM, 1, 16);
634 if (abs(f2 - freq) <= 16)
636 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
637 break;
640 x2_new = interpolate_x(freq, x1, x2, f1, f2);
642 x1 = x2, f1 = f2, x2 = x2_new;
643 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
645 while (x2 != 0);
647 if (x2 == 0)
649 /* May still be close enough */
650 TUNER_LOG("tuning failed - diff: %d\n", f2 - freq);
653 enable_afc(true);
655 TUNER_LOG("\n");
657 TUNER_LOG_SYNC();
660 #define TOO_SMALL (1 << 0)
661 #define TOO_BIG (1 << 1)
662 #define APPROACH_UP_1 (1 << 2)
663 #define APPROACH_DOWN_1 (1 << 3)
665 static void fine_step_tune(int (*setcmp)(int regval), int regval, int step)
667 /* Registers are not always stable, timeout if best fit not found soon
668 enough */
669 unsigned long abort = current_tick + HZ*2;
670 int flags = 0;
672 while (TIME_BEFORE(current_tick, abort))
674 int cmp;
676 regval = regval + step;
678 cmp = setcmp(regval);
680 if (cmp == 0)
681 break;
683 step = abs(step);
685 if (cmp < 0)
687 flags |= TOO_SMALL;
688 if (step == 1)
689 flags |= APPROACH_UP_1;
691 else
693 step = -step;
694 flags |= TOO_BIG;
695 if (step == -1)
696 step |= APPROACH_DOWN_1;
699 if ((flags & APPROACH_UP_1) && (flags & APPROACH_DOWN_1))
700 break; /* approached with step=1: best fit value found */
702 if ((flags & TOO_SMALL) && (flags & TOO_BIG))
704 step /= 2;
705 if (step == 0)
706 step = 1;
707 flags &= ~(TOO_SMALL | TOO_BIG);
712 static int if_setcmp(int regval)
714 lv24020lp_write(IF_OSC, regval);
715 lv24020lp_write(IF_CENTER, regval);
716 lv24020lp_write(IF_BW, 65*regval/100);
718 if_set = tuner_measure(MSS_IF, 1000, 32);
720 /* This register is bounces around by a few hundred Hz and doesn't seem
721 to be precisely tuneable. Just do 110000 +/- 500 since it's not very
722 critical it seems. */
723 if (abs(if_set - 110000) <= 500)
724 return 0;
726 return if_set < 110000 ? -1 : 1;
729 static int sd_setcmp(int regval)
731 lv24020lp_write(SD_OSC, regval);
733 sd_set = tuner_measure(MSS_SD, 1000, 32);
735 if (abs(sd_set - 38300) <= 31)
736 return 0;
738 return sd_set < 38300 ? -1 : 1;
741 static void set_sleep(bool sleep)
743 tuner_power(!sleep);
744 if (sleep || tuner_awake())
745 return;
747 if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) !=
748 (TUNER_PRESENT | TUNER_POWERED))
749 return;
751 enable_afc(false);
753 /* 2. Calibrate the IF frequency at 110 kHz: */
754 lv24020lp_write_clear(RADIO_CTRL2, IF_PM_L);
755 fine_step_tune(if_setcmp, 0x80, 8);
756 lv24020lp_write_set(RADIO_CTRL2, IF_PM_L);
758 /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */
759 lv24020lp_write_set(STEREO_CTRL, SD_PM);
760 fine_step_tune(sd_setcmp, 0x80, 8);
761 lv24020lp_write_clear(STEREO_CTRL, SD_PM);
763 /* calculate FM tuning coefficients */
764 lv24020lp_write(FM_CAP, sw_cap_low);
765 lv24020lp_write(FM_OSC, sw_osc_low);
766 coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
768 lv24020lp_write(FM_CAP, sw_cap_high);
769 coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
771 lv24020lp_write(FM_CAP, sw_cap_low);
772 lv24020lp_write(FM_OSC, sw_osc_high);
773 coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
775 lv24020lp_write(FM_CAP, sw_cap_high);
776 coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
778 /* set various audio level settings */
779 lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
780 lv24020lp_write_set(RADIO_CTRL2, AGCSP);
781 lv24020lp_write_set(RADIO_CTRL3, VOLSH);
782 lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
783 lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
784 PW_RAD);
786 tuner_status |= TUNER_AWAKE;
789 static int lp24020lp_tuned(void)
791 return RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f;
794 static int lv24020lp_debug_info(int setting)
796 int val = -1;
798 if (setting >= LV24020LP_DEBUG_FIRST && setting <= LV24020LP_DEBUG_LAST)
800 val = 0;
802 if (tuner_awake())
804 switch (setting)
806 /* tuner-specific debug info */
807 case LV24020LP_CTRL_STAT:
808 val = lv24020lp_read(CTRL_STAT);
809 break;
811 case LV24020LP_REG_STAT:
812 val = lv24020lp_read(RADIO_STAT);
813 break;
815 case LV24020LP_MSS_FM:
816 val = tuner_measure(MSS_FM, 1, 16);
817 break;
819 case LV24020LP_MSS_IF:
820 val = tuner_measure(MSS_IF, 1000, 16);
821 break;
823 case LV24020LP_MSS_SD:
824 val = tuner_measure(MSS_SD, 1000, 16);
825 break;
827 case LV24020LP_IF_SET:
828 val = if_set;
829 break;
831 case LV24020LP_SD_SET:
832 val = sd_set;
833 break;
838 return val;
841 /** Public interfaces **/
842 void lv24020lp_init(void)
844 mutex_init(&tuner_mtx);
847 void lv24020lp_lock(void)
849 mutex_lock(&tuner_mtx);
852 void lv24020lp_unlock(void)
854 mutex_unlock(&tuner_mtx);
857 /* This function expects the driver to be locked externally */
858 void lv24020lp_power(bool status)
860 static const unsigned char tuner_defaults[][2] =
862 /* Block 1 writeable registers */
863 { MSRC_SEL, AFC_LVL },
864 { FM_OSC, 0x80 },
865 { SD_OSC, 0x80 },
866 { IF_OSC, 0x80 },
867 { CNT_CTRL, CNT1_CLR | SWP_CNT_L },
868 { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */
869 { FM_CAP, 0x80 },
870 /* { IRQ_OUT, 0x00 }, No action on this register (skip) */
871 /* Block 2 writeable registers */
872 { RADIO_CTRL1, EN_AFC },
873 { IF_CENTER, 0x80 },
874 { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */
875 { RADIO_CTRL2, IF_PM_L },
876 { RADIO_CTRL3, AGC_SLVL | SE_FM },
877 { STEREO_CTRL, FMCS_SET(4) | AUTOSSR },
878 { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) },
879 { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */
880 { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD },
883 unsigned i;
885 if (status)
887 tuner_status |= (TUNER_PRESENCE_CHECKED | TUNER_POWERED);
889 /* if tuner is present, CHIP ID is 0x09 */
890 if (lv24020lp_read(CHIP_ID) == 0x09)
892 tuner_status |= TUNER_PRESENT;
894 /* After power-up, the LV2400x needs to be initialized as
895 follows: */
897 /* 1. Write default values to the registers: */
898 lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */
899 for (i = 0; i < ARRAYLEN(tuner_defaults); i++)
900 lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]);
902 /* Complete the startup calibration if the tuner is woken */
903 sleep(HZ/10);
906 else
908 /* Power off */
909 if (tuner_status & TUNER_PRESENT)
910 lv24020lp_write_clear(PW_SCTRL, PW_RAD);
912 tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE);
916 int lv24020lp_set(int setting, int value)
918 int val = 1;
920 mutex_lock(&tuner_mtx);
922 switch(setting)
924 case RADIO_SLEEP:
925 set_sleep(value);
926 break;
928 case RADIO_FREQUENCY:
929 set_frequency(value);
930 break;
932 case RADIO_SCAN_FREQUENCY:
933 /* TODO: really implement this */
934 set_frequency(value);
935 val = lp24020lp_tuned();
936 break;
938 case RADIO_MUTE:
939 if (value)
940 lv24020lp_write_clear(RADIO_CTRL3, AMUTE_L);
941 else
942 lv24020lp_write_set(RADIO_CTRL3, AMUTE_L);
943 break;
945 case RADIO_REGION:
947 const struct fm_region_data *rd = &fm_region_data[value];
948 if (rd->deemphasis == 75)
949 lv24020lp_write_set(AUDIO_CTRL2, DEEMP);
950 else
951 lv24020lp_write_clear(AUDIO_CTRL2, DEEMP);
952 break;
955 case RADIO_FORCE_MONO:
956 if (value)
957 lv24020lp_write_set(STEREO_CTRL, ST_M);
958 else
959 lv24020lp_write_clear(STEREO_CTRL, ST_M);
960 break;
962 default:
963 value = -1;
966 mutex_unlock(&tuner_mtx);
968 return val;
971 int lv24020lp_get(int setting)
973 int val = -1;
974 const unsigned char fst[7] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f};
975 unsigned char fst_ndx, fs;
977 mutex_lock(&tuner_mtx);
979 switch(setting)
981 case RADIO_TUNED:
982 /* TODO: really implement this */
983 val = lp24020lp_tuned();
984 break;
986 case RADIO_STEREO:
987 val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0;
988 break;
990 case RADIO_PRESENT:
992 bool fmstatus = true;
994 if (!(tuner_status & TUNER_PRESENCE_CHECKED))
995 fmstatus = tuner_power(true);
997 val = (tuner_status & TUNER_PRESENT) != 0;
999 if (!fmstatus)
1000 tuner_power(false);
1001 break;
1004 case RADIO_RSSI:
1005 fs = RSS_FS(lv24020lp_read(RADIO_STAT));
1006 for(fst_ndx=0; fst_ndx<7; fst_ndx++)
1007 if(fs == fst[fst_ndx])
1008 break;
1009 val = 75 - 10*fst_ndx;
1010 break;
1012 case RADIO_RSSI_MIN:
1013 val = RSSI_MIN;
1014 break;
1016 case RADIO_RSSI_MAX:
1017 val = RSSI_MAX;
1018 break;
1020 default:
1021 val = lv24020lp_debug_info(setting);
1024 mutex_unlock(&tuner_mtx);
1026 return val;
1028 #endif /* BOOTLOADER */