Fix the pesky write corruption bug in 4bit mode.
[kugel-rb.git] / firmware / drivers / tuner / lv24020lp.c
blob86f8c3959392c576d517cda3d986bbec1a32e931
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 to enable tuner logging */
39 #undef SANYO_TUNER_LOG_FILE
40 #undef SANYO_TUNER_LOGF
42 #ifdef SANYO_TUNER_LOG_FILE
43 #include "file.h"
45 static int fd_log = -1;
47 #define TUNER_LOG_OPEN() if (fd_log < 0) \
48 fd_log = creat("/tuner_dump.txt", 0666)
49 /* syncing required because close() is never called */
50 #define TUNER_LOG_SYNC() fsync(fd_log)
51 #define TUNER_LOG(s...) fdprintf(fd_log, s)
53 #elif defined(SANYO_TUNER_LOGF)
54 /*#define LOGF_ENABLE*/
55 #include "logf.h"
57 #define TUNER_LOG_OPEN()
58 #define TUNER_LOG_SYNC()
59 #define TUNER_LOG(s...) logf(s)
61 #else
62 #define TUNER_LOG_OPEN()
63 #define TUNER_LOG_SYNC()
64 #define TUNER_LOG(s...)
65 #endif /* SANYO_TUNER_LOG */
67 /** tuner register defines **/
69 #if defined(SANSA_E200) || defined(SANSA_C200)
70 #define TUNER_GPIO_INPUT_VAL GPIOH_INPUT_VAL
71 #define TUNER_GPIO_OUTPUT_EN_SET(mask) GPIO_SET_BITWISE(GPIOH_OUTPUT_EN, mask)
72 #define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) GPIO_CLEAR_BITWISE(GPIOH_OUTPUT_EN, mask)
73 #define TUNER_GPIO_OUTPUT_VAL_SET(mask) GPIO_SET_BITWISE(GPIOH_OUTPUT_VAL, mask)
74 #define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) GPIO_CLEAR_BITWISE(GPIOH_OUTPUT_VAL, mask)
75 #define FM_NRW_PIN 3
76 #define FM_CLOCK_PIN 4
77 #define FM_DATA_PIN 5
79 #elif defined(IAUDIO_7)
80 #define TUNER_GPIO_INPUT_VAL GPIOA
81 #define TUNER_GPIO_OUTPUT_EN_SET(mask) (GPIOA_DIR |= (mask))
82 #define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) (GPIOA_DIR &= ~(mask))
83 #define TUNER_GPIO_OUTPUT_VAL_SET(mask) (GPIOA |= (mask))
84 #define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) (GPIOA &= ~(mask))
85 #define FM_CLOCK_PIN 5
86 #define FM_DATA_PIN 6
87 #define FM_NRW_PIN 7
89 #elif defined(COWON_D2)
90 #define TUNER_GPIO_INPUT_VAL GPIOC
91 #define TUNER_GPIO_OUTPUT_EN_SET(mask) (GPIOC_DIR |= (mask))
92 #define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) (GPIOC_DIR &= ~(mask))
93 #define TUNER_GPIO_OUTPUT_VAL_SET(mask) (GPIOC |= (mask))
94 #define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) (GPIOC &= ~(mask))
95 #define FM_NRW_PIN 31
96 #define FM_CLOCK_PIN 29
97 #define FM_DATA_PIN 30
99 #else
100 #error GPIOs undefined for this target
101 #endif
103 #define FM_CLK_DELAY 1
105 /* block 1 registers */
107 /* R */
108 #define CHIP_ID 0x00
110 /* W */
111 #define BLK_SEL 0x01
112 #define BLK1 0x01
113 #define BLK2 0x02
115 /* W */
116 #define MSRC_SEL 0x02
117 #define MSR_O (1 << 7)
118 #define AFC_LVL (1 << 6)
119 #define AFC_SPD (1 << 5)
120 #define MSS_SD (1 << 2)
121 #define MSS_FM (1 << 1)
122 #define MSS_IF (1 << 0)
124 /* W */
125 #define FM_OSC 0x03
127 /* W */
128 #define SD_OSC 0x04
130 /* W */
131 #define IF_OSC 0x05
133 /* W */
134 #define CNT_CTRL 0x06
135 #define CNT1_CLR (1 << 7)
136 #define CTAB(x) ((x) & (0x7 << 4))
137 #define CTAB_STOP_2 (0x0 << 4)
138 #define CTAB_STOP_8 (0x1 << 4)
139 #define CTAB_STOP_32 (0x2 << 4)
140 #define CTAB_STOP_128 (0x3 << 4)
141 #define CTAB_STOP_512 (0x4 << 4)
142 #define CTAB_STOP_2048 (0x5 << 4)
143 #define CTAB_STOP_8192 (0x6 << 4)
144 #define CTAB_STOP_32768 (0x7 << 4)
145 #define SWP_CNT_L (1 << 3)
146 #define CNT_EN (1 << 2)
147 #define CNT_SEL (1 << 1)
148 #define CNT_SET (1 << 0)
150 /* W */
151 #define IRQ_MSK 0x08
152 #define IM_MS (1 << 6)
153 #define IRQ_LVL (1 << 3)
154 #define IM_AFC (1 << 2)
155 #define IM_FS (1 << 1)
156 #define IM_CNT2 (1 << 0)
158 /* W */
159 #define FM_CAP 0x09
161 /* R */
162 #define CNT_L 0x0a /* Counter register low value */
164 /* R */
165 #define CNT_H 0x0b /* Counter register high value */
167 /* R */
168 #define CTRL_STAT 0x0c
169 #define AFC_FLG (1 << 0)
171 /* R */
172 #define RADIO_STAT 0x0d
173 #define RSS_MS (1 << 7)
174 #define RSS_FS(x) ((x) & 0x7f)
175 #define RSS_FS_GET(x) ((x) & 0x7f)
176 #define RSS_FS_SET(x) (x)
177 /* Note: Reading this register will clear field strength and mono/stereo interrupt. */
179 /* R */
180 #define IRQ_ID 0x0e
181 #define II_CNT2 (1 << 5)
182 #define II_AFC (1 << 3)
183 #define II_FS_MS (1 << 0)
185 /* W */
186 #define IRQ_OUT 0x0f
188 /* block 2 registers - offset added in order to id and avoid manual
189 switching */
190 #define BLK2_START 0x10
192 /* W */
193 #define RADIO_CTRL1 (0x02 + BLK2_START)
194 #define EN_MEAS (1 << 7)
195 #define EN_AFC (1 << 6)
196 #define DIR_AFC (1 << 3)
197 #define RST_AFC (1 << 2)
199 /* W */
200 #define IF_CENTER (0x03 + BLK2_START)
202 /* W */
203 #define IF_BW (0x05 + BLK2_START)
205 /* W */
206 #define RADIO_CTRL2 (0x06 + BLK2_START)
207 #define VREF2 (1 << 7)
208 #define VREF (1 << 6)
209 #define STABI_BP (1 << 5)
210 #define IF_PM_L (1 << 4)
211 #define AGCSP (1 << 1)
212 #define AM_ANT_BSW (1 << 0) /* ?? */
214 /* W */
215 #define RADIO_CTRL3 (0x07 + BLK2_START)
216 #define AGC_SLVL (1 << 7)
217 #define VOLSH (1 << 6)
218 #define TB_ON (1 << 5)
219 #define AMUTE_L (1 << 4)
220 #define SE_FM (1 << 3)
221 #define SE_BE (1 << 1)
222 #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */
224 /* W */
225 #define STEREO_CTRL (0x08 + BLK2_START)
226 #define FRCST (1 << 7)
227 #define FMCS(x) ((x) & (0x7 << 4))
228 #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4)
229 #define FMCS_SET(x) ((x) << 4)
230 #define AUTOSSR (1 << 3)
231 #define PILTCA (1 << 2)
232 #define SD_PM (1 << 1)
233 #define ST_M (1 << 0)
235 /* W */
236 #define AUDIO_CTRL1 (0x09 + BLK2_START)
237 #define TONE_LVL(x) ((x) & (0xf << 4))
238 #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4)
239 #define TONE_LVL_SET(x) ((x) << 4)
240 #define VOL_LVL(x) ((x) & 0xf)
241 #define VOL_LVL_GET(x) ((x) & 0xf)
242 #define VOL_LVL_SET(x) ((x) << 4)
244 /* W */
245 #define AUDIO_CTRL2 (0x0a + BLK2_START)
246 #define BASS_PP (1 << 0)
247 #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */
248 #define BASS_N (1 << 2)
249 #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */
250 #define TREB_N (1 << 4)
251 #define DEEMP (1 << 5)
252 #define BPFREQ(x) ((x) & (0x3 << 6))
253 #define BPFREQ_2_0K (0x0 << 6)
254 #define BPFREQ_1_0K (0x1 << 6)
255 #define BPFREQ_0_5K (0x2 << 6)
256 #define BPFREQ_HIGH (0x3 << 6)
258 /* W */
259 #define PW_SCTRL (0x0b + BLK2_START)
260 #define SS_CTRL(x) ((x) & (0x7 << 5))
261 #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5)
262 #define SS_CTRL_SET(x) ((x) << 5)
263 #define SM_CTRL(x) ((x) & (0x7 << 2))
264 #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2)
265 #define SM_CTRL_SET(x) ((x) << 2)
266 #define PW_HPA (1 << 1) /* LV24002 only */
267 #define PW_RAD (1 << 0)
269 /* shadow for writeable registers */
270 #define TUNER_POWERED (1 << 0)
271 #define TUNER_PRESENT (1 << 1)
272 #define TUNER_AWAKE (1 << 2)
273 #define TUNER_PRESENCE_CHECKED (1 << 3)
274 static unsigned tuner_status = 0;
276 static unsigned char lv24020lp_regs[0x1c];
278 static const int sw_osc_low = 10; /* 30; */
279 static const int sw_osc_high = 240; /* 200; */
280 static const int sw_cap_low = 0;
281 static const int sw_cap_high = 191;
283 /* linear coefficients used for tuning */
284 static int coef_00, coef_01, coef_10, coef_11;
286 /* DAC control register set values */
287 static int if_set, sd_set;
289 static inline bool tuner_awake(void)
291 return (tuner_status & TUNER_AWAKE) != 0;
294 /* send a byte to the tuner - expects write mode to be current */
295 static void lv24020lp_send_byte(unsigned int byte)
297 int i;
299 for (i = 0; i < 8; i++)
301 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_CLOCK_PIN);
304 if (byte & 1)
305 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_DATA_PIN);
306 else
307 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_DATA_PIN);
309 udelay(FM_CLK_DELAY);
311 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_CLOCK_PIN);
312 udelay(FM_CLK_DELAY);
314 byte >>= 1;
318 /* end a write cycle on the tuner */
319 static void lv24020lp_end_write(void)
321 /* switch back to read mode */
322 TUNER_GPIO_OUTPUT_EN_CLEAR(1 << FM_DATA_PIN);
323 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_NRW_PIN);
324 udelay(FM_CLK_DELAY);
327 /* prepare a write cycle on the tuner */
328 static unsigned int lv24020lp_begin_write(unsigned int address)
330 /* Get register's block, translate address */
331 unsigned int blk = (address >= BLK2_START) ?
332 (address -= BLK2_START, BLK2) : BLK1;
334 for (;;)
336 /* Prepare 3-wire bus pins for write cycle */
337 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_NRW_PIN);
338 TUNER_GPIO_OUTPUT_EN_SET(1 << FM_DATA_PIN);
339 udelay(FM_CLK_DELAY);
341 /* current block == register block? */
342 if (blk == lv24020lp_regs[BLK_SEL])
343 return address;
345 /* switch block */
346 lv24020lp_regs[BLK_SEL] = blk;
348 /* data first */
349 lv24020lp_send_byte(blk);
350 /* then address */
351 lv24020lp_send_byte(BLK_SEL);
353 lv24020lp_end_write();
357 /* write a byte to a tuner register */
358 static void lv24020lp_write(unsigned int address, unsigned int data)
360 /* shadow logical values but do logical=>physical remappings on some
361 registers' data. */
362 lv24020lp_regs[address] = data;
364 switch (address)
366 case FM_OSC:
367 /* L: 000..255
368 * P: 255..000 */
369 data = 255 - data;
370 break;
371 case FM_CAP:
372 /* L: 000..063, 064..191
373 * P: 255..192, 127..000 */
374 data = ((data < 64) ? 255 : (255 - 64)) - data;
375 break;
376 case RADIO_CTRL1:
377 /* L: data
378 * P: data | always "1" bits */
379 data |= (1 << 4) | (1 << 1) | (1 << 0);
380 break;
383 /* Check if interface is turned on */
384 if (!(tuner_status & TUNER_POWERED))
385 return;
387 address = lv24020lp_begin_write(address);
389 /* data first */
390 lv24020lp_send_byte(data);
391 /* then address */
392 lv24020lp_send_byte(address);
394 lv24020lp_end_write();
397 /* helpers to set/clear register bits */
398 static void lv24020lp_write_set(unsigned int address, unsigned int bits)
400 lv24020lp_write(address, lv24020lp_regs[address] | bits);
403 static void lv24020lp_write_clear(unsigned int address, unsigned int bits)
405 lv24020lp_write(address, lv24020lp_regs[address] & ~bits);
408 /* read a byte from a tuner register */
409 static unsigned int lv24020lp_read(unsigned int address)
411 int i;
412 unsigned int toread;
414 /* Check if interface is turned on */
415 if (!(tuner_status & TUNER_POWERED))
416 return 0;
418 address = lv24020lp_begin_write(address);
420 /* address */
421 lv24020lp_send_byte(address);
423 lv24020lp_end_write();
425 /* data */
426 toread = 0;
427 for (i = 0; i < 8; i++)
429 TUNER_GPIO_OUTPUT_VAL_CLEAR(1 << FM_CLOCK_PIN);
430 udelay(FM_CLK_DELAY);
432 if (TUNER_GPIO_INPUT_VAL & (1 << FM_DATA_PIN))
433 toread |= (1 << i);
435 TUNER_GPIO_OUTPUT_VAL_SET(1 << FM_CLOCK_PIN);
436 udelay(FM_CLK_DELAY);
439 return toread;
442 /* enables auto frequency centering */
443 static void enable_afc(bool enabled)
445 unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1];
447 if (enabled)
449 radio_ctrl1 &= ~RST_AFC;
450 radio_ctrl1 |= EN_AFC;
452 else
454 radio_ctrl1 |= RST_AFC;
455 radio_ctrl1 &= ~EN_AFC;
458 lv24020lp_write(RADIO_CTRL1, radio_ctrl1);
461 static int calculate_coef(unsigned fkhz)
463 /* Overflow below 66000kHz --
464 My tuner tunes down to a min of ~72600kHz but datasheet mentions
465 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */
466 return fkhz < 66000 ?
467 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz);
470 static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2)
472 return y1 == y2 ?
473 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1;
476 static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2)
478 return x1 == x2 ?
479 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1;
482 /* this performs measurements of IF, FM and Stereo frequencies
483 * Input can be: MSS_FM, MSS_IF, MSS_SD */
484 static int tuner_measure(unsigned char type, int scale, int duration)
486 int64_t finval;
488 /* enable measuring */
489 lv24020lp_write_set(MSRC_SEL, type);
490 lv24020lp_write_clear(CNT_CTRL, CNT_SEL);
491 lv24020lp_write_set(RADIO_CTRL1, EN_MEAS);
493 /* reset counter */
494 lv24020lp_write_set(CNT_CTRL, CNT1_CLR);
495 lv24020lp_write_clear(CNT_CTRL, CNT1_CLR);
497 /* start counter, delay for specified time and stop it */
498 lv24020lp_write_set(CNT_CTRL, CNT_EN);
500 #ifdef CPU_PP
501 /* obtain actual duration, including interrupts that occurred and
502 * the time to write the counter stop */
503 long usec = USEC_TIMER;
504 #endif
506 udelay(duration*1000);
508 lv24020lp_write_clear(CNT_CTRL, CNT_EN);
510 #ifdef CPU_PP
511 duration = (USEC_TIMER - usec) / 1000;
512 #endif
514 /* This function takes a loooong time and other stuff needs
515 running by now */
516 yield();
518 /* read tick count */
519 finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L);
521 /* restore measure mode */
522 lv24020lp_write_clear(RADIO_CTRL1, EN_MEAS);
523 lv24020lp_write_clear(MSRC_SEL, type);
525 /* convert value */
526 if (type == MSS_FM)
527 finval = scale*finval*256 / duration;
528 else
529 finval = scale*finval / duration;
531 return (int)finval;
534 /* set the FM oscillator frequency */
535 static void set_frequency(int freq)
537 int coef, cap_value, osc_value;
538 int f1, f2, x1, x2;
539 int count;
541 TUNER_LOG_OPEN();
543 TUNER_LOG("set_frequency(%d)\n", freq);
545 enable_afc(false);
547 /* For the LV2400x, the tuned frequency is the sum of the displayed
548 * frequency and the preset IF frequency, in formula:
549 * Tuned FM frequency = displayed frequency + preset IF frequency
551 * For example: when the IF frequency of LV2400x is preset at 110 kHz,
552 * it must be tuned at 88.51 MHz to receive the radio station at 88.4 MHz.
553 * -- AN2400S04@ – V0.4
555 freq += if_set;
557 /* MHz -> kHz */
558 freq /= 1000;
560 TUNER_LOG("Select cap:\n");
562 coef = calculate_coef(freq);
563 cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high,
564 coef_00, coef_01);
566 osc_value = sw_osc_low;
567 lv24020lp_write(FM_OSC, osc_value);
569 /* Just in case - don't go into infinite loop */
570 for (count = 0; count < 30; count++)
572 int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
573 coef_00, coef_01);
574 int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
575 coef_10, coef_11);
576 int coef_fcur, cap_new, coef_cor, range;
578 lv24020lp_write(FM_CAP, cap_value);
580 range = y1 - y0;
581 f1 = tuner_measure(MSS_FM, 1, 16);
582 coef_fcur = calculate_coef(f1);
583 coef_cor = calculate_coef((f1*1000 + 32*256) / 1000);
584 y0 = coef_cor;
585 y1 = y0 + range;
587 TUNER_LOG("%d %d %d %d %d %d %d %d\n",
588 f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range);
590 if (coef >= y0 && coef <= y1)
592 osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high,
593 y0, y1);
595 if (osc_value >= sw_osc_low && osc_value <= sw_osc_high)
596 break;
599 cap_new = interpolate_x(coef, cap_value, sw_cap_high,
600 coef_fcur, coef_01);
602 if (cap_new == cap_value)
604 if (coef < coef_fcur)
605 cap_value++;
606 else
607 cap_value--;
609 else
611 cap_value = cap_new;
615 TUNER_LOG("osc_value: %d\n", osc_value);
617 TUNER_LOG("Tune:\n");
619 x1 = sw_osc_low, x2 = sw_osc_high;
620 /* FM_OSC already at SW_OSC low and f1 is already the measured
621 frequency */
625 int x2_new;
627 lv24020lp_write(FM_OSC, x2);
628 f2 = tuner_measure(MSS_FM, 1, 16);
630 if (abs(f2 - freq) <= 16)
632 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
633 break;
636 x2_new = interpolate_x(freq, x1, x2, f1, f2);
638 x1 = x2, f1 = f2, x2 = x2_new;
639 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
641 while (x2 != 0);
643 if (x2 == 0)
645 /* May still be close enough */
646 TUNER_LOG("tuning failed - diff: %d\n", f2 - freq);
649 enable_afc(true);
651 TUNER_LOG("\n");
653 TUNER_LOG_SYNC();
656 #define TOO_SMALL (1 << 0)
657 #define TOO_BIG (1 << 1)
658 #define APPROACH_UP_1 (1 << 2)
659 #define APPROACH_DOWN_1 (1 << 3)
661 static void fine_step_tune(int (*setcmp)(int regval), int regval, int step)
663 /* Registers are not always stable, timeout if best fit not found soon
664 enough */
665 unsigned long abort = current_tick + HZ*2;
666 int flags = 0;
668 while (TIME_BEFORE(current_tick, abort))
670 int cmp;
672 regval = regval + step;
674 cmp = setcmp(regval);
676 if (cmp == 0)
677 break;
679 step = abs(step);
681 if (cmp < 0)
683 flags |= TOO_SMALL;
684 if (step == 1)
685 flags |= APPROACH_UP_1;
687 else
689 step = -step;
690 flags |= TOO_BIG;
691 if (step == -1)
692 step |= APPROACH_DOWN_1;
695 if ((flags & APPROACH_UP_1) && (flags & APPROACH_DOWN_1))
696 break; /* approached with step=1: best fit value found */
698 if ((flags & TOO_SMALL) && (flags & TOO_BIG))
700 step /= 2;
701 if (step == 0)
702 step = 1;
703 flags &= ~(TOO_SMALL | TOO_BIG);
708 static int if_setcmp(int regval)
710 lv24020lp_write(IF_OSC, regval);
711 lv24020lp_write(IF_CENTER, regval);
712 lv24020lp_write(IF_BW, 65*regval/100);
714 if_set = tuner_measure(MSS_IF, 1000, 32);
716 /* This register is bounces around by a few hundred Hz and doesn't seem
717 to be precisely tuneable. Just do 110000 +/- 500 since it's not very
718 critical it seems. */
719 if (abs(if_set - 110000) <= 500)
720 return 0;
722 return if_set < 110000 ? -1 : 1;
725 static int sd_setcmp(int regval)
727 lv24020lp_write(SD_OSC, regval);
729 sd_set = tuner_measure(MSS_SD, 1000, 32);
731 if (abs(sd_set - 38300) <= 31)
732 return 0;
734 return sd_set < 38300 ? -1 : 1;
737 static void set_sleep(bool sleep)
739 if (sleep || tuner_awake())
740 return;
742 if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) !=
743 (TUNER_PRESENT | TUNER_POWERED))
744 return;
746 enable_afc(false);
748 /* 2. Calibrate the IF frequency at 110 kHz: */
749 lv24020lp_write_clear(RADIO_CTRL2, IF_PM_L);
750 fine_step_tune(if_setcmp, 0x80, 8);
751 lv24020lp_write_set(RADIO_CTRL2, IF_PM_L);
753 /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */
754 lv24020lp_write_set(STEREO_CTRL, SD_PM);
755 fine_step_tune(sd_setcmp, 0x80, 8);
756 lv24020lp_write_clear(STEREO_CTRL, SD_PM);
758 /* calculate FM tuning coefficients */
759 lv24020lp_write(FM_CAP, sw_cap_low);
760 lv24020lp_write(FM_OSC, sw_osc_low);
761 coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
763 lv24020lp_write(FM_CAP, sw_cap_high);
764 coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
766 lv24020lp_write(FM_CAP, sw_cap_low);
767 lv24020lp_write(FM_OSC, sw_osc_high);
768 coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
770 lv24020lp_write(FM_CAP, sw_cap_high);
771 coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
773 /* set various audio level settings */
774 lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
775 lv24020lp_write_set(RADIO_CTRL2, AGCSP);
776 lv24020lp_write_set(RADIO_CTRL3, VOLSH);
777 lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
778 lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
779 PW_RAD);
781 tuner_status |= TUNER_AWAKE;
784 static int lp24020lp_tuned(void)
786 return RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f;
789 static int lv24020lp_debug_info(int setting)
791 int val = -1;
793 if (setting >= LV24020LP_DEBUG_FIRST && setting <= LV24020LP_DEBUG_LAST)
795 val = 0;
797 if (tuner_awake())
799 switch (setting)
801 /* tuner-specific debug info */
802 case LV24020LP_CTRL_STAT:
803 val = lv24020lp_read(CTRL_STAT);
804 break;
806 case LV24020LP_REG_STAT:
807 val = lv24020lp_read(RADIO_STAT);
808 break;
810 case LV24020LP_MSS_FM:
811 val = tuner_measure(MSS_FM, 1, 16);
812 break;
814 case LV24020LP_MSS_IF:
815 val = tuner_measure(MSS_IF, 1000, 16);
816 break;
818 case LV24020LP_MSS_SD:
819 val = tuner_measure(MSS_SD, 1000, 16);
820 break;
822 case LV24020LP_IF_SET:
823 val = if_set;
824 break;
826 case LV24020LP_SD_SET:
827 val = sd_set;
828 break;
833 return val;
836 /** Public interfaces **/
837 void lv24020lp_init(void)
839 mutex_init(&tuner_mtx);
842 void lv24020lp_lock(void)
844 mutex_lock(&tuner_mtx);
847 void lv24020lp_unlock(void)
849 mutex_unlock(&tuner_mtx);
852 /* This function expects the driver to be locked externally */
853 void lv24020lp_power(bool status)
855 static const unsigned char tuner_defaults[][2] =
857 /* Block 1 writeable registers */
858 { MSRC_SEL, AFC_LVL },
859 { FM_OSC, 0x80 },
860 { SD_OSC, 0x80 },
861 { IF_OSC, 0x80 },
862 { CNT_CTRL, CNT1_CLR | SWP_CNT_L },
863 { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */
864 { FM_CAP, 0x80 },
865 /* { IRQ_OUT, 0x00 }, No action on this register (skip) */
866 /* Block 2 writeable registers */
867 { RADIO_CTRL1, EN_AFC },
868 { IF_CENTER, 0x80 },
869 { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */
870 { RADIO_CTRL2, IF_PM_L },
871 { RADIO_CTRL3, AGC_SLVL | SE_FM },
872 { STEREO_CTRL, FMCS_SET(4) | AUTOSSR },
873 { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) },
874 { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */
875 { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD },
878 unsigned i;
880 if (status)
882 tuner_status |= (TUNER_PRESENCE_CHECKED | TUNER_POWERED);
884 /* if tuner is present, CHIP ID is 0x09 */
885 if (lv24020lp_read(CHIP_ID) == 0x09)
887 tuner_status |= TUNER_PRESENT;
889 /* After power-up, the LV2400x needs to be initialized as
890 follows: */
892 /* 1. Write default values to the registers: */
893 lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */
894 for (i = 0; i < ARRAYLEN(tuner_defaults); i++)
895 lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]);
897 /* Complete the startup calibration if the tuner is woken */
898 sleep(HZ/10);
901 else
903 /* Power off */
904 if (tuner_status & TUNER_PRESENT)
905 lv24020lp_write_clear(PW_SCTRL, PW_RAD);
907 tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE);
911 int lv24020lp_set(int setting, int value)
913 int val = 1;
915 mutex_lock(&tuner_mtx);
917 switch(setting)
919 case RADIO_SLEEP:
920 set_sleep(value);
921 break;
923 case RADIO_FREQUENCY:
924 set_frequency(value);
925 break;
927 case RADIO_SCAN_FREQUENCY:
928 /* TODO: really implement this */
929 set_frequency(value);
930 val = lp24020lp_tuned();
931 break;
933 case RADIO_MUTE:
934 if (value)
935 lv24020lp_write_clear(RADIO_CTRL3, AMUTE_L);
936 else
937 lv24020lp_write_set(RADIO_CTRL3, AMUTE_L);
938 break;
940 case RADIO_REGION:
941 if (lv24020lp_region_data[value])
942 lv24020lp_write_set(AUDIO_CTRL2, DEEMP);
943 else
944 lv24020lp_write_clear(AUDIO_CTRL2, DEEMP);
945 break;
947 case RADIO_FORCE_MONO:
948 if (value)
949 lv24020lp_write_set(STEREO_CTRL, ST_M);
950 else
951 lv24020lp_write_clear(STEREO_CTRL, ST_M);
952 break;
954 default:
955 value = -1;
958 mutex_unlock(&tuner_mtx);
960 return val;
963 int lv24020lp_get(int setting)
965 int val = -1;
967 mutex_lock(&tuner_mtx);
969 switch(setting)
971 case RADIO_TUNED:
972 /* TODO: really implement this */
973 val = lp24020lp_tuned();
974 break;
976 case RADIO_STEREO:
977 val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0;
978 break;
980 case RADIO_PRESENT:
982 bool fmstatus = true;
984 if (!(tuner_status & TUNER_PRESENCE_CHECKED))
985 fmstatus = tuner_power(true);
987 val = (tuner_status & TUNER_PRESENT) != 0;
989 if (!fmstatus)
990 tuner_power(false);
991 break;
994 default:
995 val = lv24020lp_debug_info(setting);
998 mutex_unlock(&tuner_mtx);
1000 return val;
1002 #endif /* BOOTLOADER */