Implement udelay() for D2 and remove the old hacks from USB & FM drivers.
[kugel-rb.git] / firmware / drivers / tuner / lv24020lp.c
blob769be8ac774daff4f4f0b47c2346bb6b0568203f
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 "pp5024.h"
33 #include "system.h"
35 #ifndef BOOTLOADER
37 static struct mutex tuner_mtx;
39 #if 0
40 /* define to enable tuner logging */
41 #define SANYO_TUNER_LOG
42 #endif
44 #ifdef SANYO_TUNER_LOG
45 #include "sprintf.h"
46 #include "file.h"
48 static int fd_log = -1;
50 #define TUNER_LOG_OPEN() if (fd_log < 0) \
51 fd_log = creat("/tuner_dump.txt")
52 /* syncing required because close() is never called */
53 #define TUNER_LOG_SYNC() fsync(fd_log)
54 #define TUNER_LOG(s...) fdprintf(fd_log, s)
55 #else
56 #define TUNER_LOG_OPEN()
57 #define TUNER_LOG_SYNC()
58 #define TUNER_LOG(s...)
59 #endif /* SANYO_TUNER_LOG */
61 /** tuner register defines **/
63 #if defined(SANSA_E200) || defined(SANSA_C200)
64 #define TUNER_GPIO_OUTPUT_EN GPIOH_OUTPUT_EN
65 #define TUNER_GPIO_OUTPUT_VAL GPIOH_OUTPUT_VAL
66 #define TUNER_GPIO_INPUT_VAL GPIOH_INPUT_VAL
67 #define FM_NRW_PIN 3
68 #define FM_CLOCK_PIN 4
69 #define FM_DATA_PIN 5
71 #elif defined(IAUDIO_7)
72 #define TUNER_GPIO_OUTPUT_EN GPIOA_DIR
73 #define TUNER_GPIO_OUTPUT_VAL GPIOA
74 #define TUNER_GPIO_INPUT_VAL GPIOA
75 #define FM_CLOCK_PIN 5
76 #define FM_DATA_PIN 6
77 #define FM_NRW_PIN 7
78 /* Remove hack when i7 has udelay */
79 static void udelay(int usecs)
81 while (usecs--)
82 asm("nop;nop;");
85 #elif defined(COWON_D2)
86 #define TUNER_GPIO_OUTPUT_EN GPIOC_DIR
87 #define TUNER_GPIO_OUTPUT_VAL GPIOC
88 #define TUNER_GPIO_INPUT_VAL GPIOC
89 #define FM_NRW_PIN 31
90 #define FM_CLOCK_PIN 29
91 #define FM_DATA_PIN 30
93 #else
94 #error GPIOs undefined for this target
95 #endif
97 #define FM_CLK_DELAY 1
99 /* block 1 registers */
101 /* R */
102 #define CHIP_ID 0x00
104 /* W */
105 #define BLK_SEL 0x01
106 #define BLK1 0x01
107 #define BLK2 0x02
109 /* W */
110 #define MSRC_SEL 0x02
111 #define MSR_O (1 << 7)
112 #define AFC_LVL (1 << 6)
113 #define AFC_SPD (1 << 5)
114 #define MSS_SD (1 << 2)
115 #define MSS_FM (1 << 1)
116 #define MSS_IF (1 << 0)
118 /* W */
119 #define FM_OSC 0x03
121 /* W */
122 #define SD_OSC 0x04
124 /* W */
125 #define IF_OSC 0x05
127 /* W */
128 #define CNT_CTRL 0x06
129 #define CNT1_CLR (1 << 7)
130 #define CTAB(x) ((x) & (0x7 << 4))
131 #define CTAB_STOP_2 (0x0 << 4)
132 #define CTAB_STOP_8 (0x1 << 4)
133 #define CTAB_STOP_32 (0x2 << 4)
134 #define CTAB_STOP_128 (0x3 << 4)
135 #define CTAB_STOP_512 (0x4 << 4)
136 #define CTAB_STOP_2048 (0x5 << 4)
137 #define CTAB_STOP_8192 (0x6 << 4)
138 #define CTAB_STOP_32768 (0x7 << 4)
139 #define SWP_CNT_L (1 << 3)
140 #define CNT_EN (1 << 2)
141 #define CNT_SEL (1 << 1)
142 #define CNT_SET (1 << 0)
144 /* W */
145 #define IRQ_MSK 0x08
146 #define IM_MS (1 << 6)
147 #define IRQ_LVL (1 << 3)
148 #define IM_AFC (1 << 2)
149 #define IM_FS (1 << 1)
150 #define IM_CNT2 (1 << 0)
152 /* W */
153 #define FM_CAP 0x09
155 /* R */
156 #define CNT_L 0x0a /* Counter register low value */
158 /* R */
159 #define CNT_H 0x0b /* Counter register high value */
161 /* R */
162 #define CTRL_STAT 0x0c
163 #define AFC_FLG (1 << 0)
165 /* R */
166 #define RADIO_STAT 0x0d
167 #define RSS_MS (1 << 7)
168 #define RSS_FS(x) ((x) & 0x7f)
169 #define RSS_FS_GET(x) ((x) & 0x7f)
170 #define RSS_FS_SET(x) (x)
171 /* Note: Reading this register will clear field strength and mono/stereo interrupt. */
173 /* R */
174 #define IRQ_ID 0x0e
175 #define II_CNT2 (1 << 5)
176 #define II_AFC (1 << 3)
177 #define II_FS_MS (1 << 0)
179 /* W */
180 #define IRQ_OUT 0x0f
182 /* block 2 registers - offset added in order to id and avoid manual
183 switching */
184 #define BLK2_START 0x10
186 /* W */
187 #define RADIO_CTRL1 (0x02 + BLK2_START)
188 #define EN_MEAS (1 << 7)
189 #define EN_AFC (1 << 6)
190 #define DIR_AFC (1 << 3)
191 #define RST_AFC (1 << 2)
193 /* W */
194 #define IF_CENTER (0x03 + BLK2_START)
196 /* W */
197 #define IF_BW (0x05 + BLK2_START)
199 /* W */
200 #define RADIO_CTRL2 (0x06 + BLK2_START)
201 #define VREF2 (1 << 7)
202 #define VREF (1 << 6)
203 #define STABI_BP (1 << 5)
204 #define IF_PM_L (1 << 4)
205 #define AGCSP (1 << 1)
206 #define AM_ANT_BSW (1 << 0) /* ?? */
208 /* W */
209 #define RADIO_CTRL3 (0x07 + BLK2_START)
210 #define AGC_SLVL (1 << 7)
211 #define VOLSH (1 << 6)
212 #define TB_ON (1 << 5)
213 #define AMUTE_L (1 << 4)
214 #define SE_FM (1 << 3)
215 #define SE_BE (1 << 1)
216 #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */
218 /* W */
219 #define STEREO_CTRL (0x08 + BLK2_START)
220 #define FRCST (1 << 7)
221 #define FMCS(x) ((x) & (0x7 << 4))
222 #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4)
223 #define FMCS_SET(x) ((x) << 4)
224 #define AUTOSSR (1 << 3)
225 #define PILTCA (1 << 2)
226 #define SD_PM (1 << 1)
227 #define ST_M (1 << 0)
229 /* W */
230 #define AUDIO_CTRL1 (0x09 + BLK2_START)
231 #define TONE_LVL(x) ((x) & (0xf << 4))
232 #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4)
233 #define TONE_LVL_SET(x) ((x) << 4)
234 #define VOL_LVL(x) ((x) & 0xf)
235 #define VOL_LVL_GET(x) ((x) & 0xf)
236 #define VOL_LVL_SET(x) ((x) << 4)
238 /* W */
239 #define AUDIO_CTRL2 (0x0a + BLK2_START)
240 #define BASS_PP (1 << 0)
241 #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */
242 #define BASS_N (1 << 2)
243 #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */
244 #define TREB_N (1 << 4)
245 #define DEEMP (1 << 5)
246 #define BPFREQ(x) ((x) & (0x3 << 6))
247 #define BPFREQ_2_0K (0x0 << 6)
248 #define BPFREQ_1_0K (0x1 << 6)
249 #define BPFREQ_0_5K (0x2 << 6)
250 #define BPFREQ_HIGH (0x3 << 6)
252 /* W */
253 #define PW_SCTRL (0x0b + BLK2_START)
254 #define SS_CTRL(x) ((x) & (0x7 << 5))
255 #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5)
256 #define SS_CTRL_SET(x) ((x) << 5)
257 #define SM_CTRL(x) ((x) & (0x7 << 2))
258 #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2)
259 #define SM_CTRL_SET(x) ((x) << 2)
260 #define PW_HPA (1 << 1) /* LV24002 only */
261 #define PW_RAD (1 << 0)
263 /* shadow for writeable registers */
264 #define TUNER_POWERED (1 << 0)
265 #define TUNER_PRESENT (1 << 1)
266 #define TUNER_AWAKE (1 << 2)
267 #define TUNER_PRESENCE_CHECKED (1 << 3)
268 static unsigned tuner_status = 0;
270 static unsigned char lv24020lp_regs[0x1c];
272 static const int sw_osc_low = 10; /* 30; */
273 static const int sw_osc_high = 240; /* 200; */
274 static const int sw_cap_low = 0;
275 static const int sw_cap_high = 191;
277 /* linear coefficients used for tuning */
278 static int coef_00, coef_01, coef_10, coef_11;
280 /* DAC control register set values */
281 static int if_set, sd_set;
283 static inline bool tuner_awake(void)
285 return (tuner_status & TUNER_AWAKE) != 0;
288 /* send a byte to the tuner - expects write mode to be current */
289 static void lv24020lp_send_byte(unsigned int byte)
291 int i;
293 for (i = 0; i < 8; i++)
295 TUNER_GPIO_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN);
297 if (byte & 1)
298 TUNER_GPIO_OUTPUT_VAL |= (1 << FM_DATA_PIN);
299 else
300 TUNER_GPIO_OUTPUT_VAL &= ~(1 << FM_DATA_PIN);
302 udelay(FM_CLK_DELAY);
304 TUNER_GPIO_OUTPUT_VAL |= (1 << FM_CLOCK_PIN);
305 udelay(FM_CLK_DELAY);
307 byte >>= 1;
311 /* end a write cycle on the tuner */
312 static void lv24020lp_end_write(void)
314 /* switch back to read mode */
315 TUNER_GPIO_OUTPUT_EN &= ~(1 << FM_DATA_PIN);
316 TUNER_GPIO_OUTPUT_VAL &= ~(1 << FM_NRW_PIN);
317 udelay(FM_CLK_DELAY);
320 /* prepare a write cycle on the tuner */
321 static unsigned int lv24020lp_begin_write(unsigned int address)
323 /* Get register's block, translate address */
324 unsigned int blk = (address >= BLK2_START) ?
325 (address -= BLK2_START, BLK2) : BLK1;
327 for (;;)
329 /* Prepare 3-wire bus pins for write cycle */
330 TUNER_GPIO_OUTPUT_VAL |= (1 << FM_NRW_PIN);
331 TUNER_GPIO_OUTPUT_EN |= (1 << FM_DATA_PIN);
332 udelay(FM_CLK_DELAY);
334 /* current block == register block? */
335 if (blk == lv24020lp_regs[BLK_SEL])
336 return address;
338 /* switch block */
339 lv24020lp_regs[BLK_SEL] = blk;
341 /* data first */
342 lv24020lp_send_byte(blk);
343 /* then address */
344 lv24020lp_send_byte(BLK_SEL);
346 lv24020lp_end_write();
350 /* write a byte to a tuner register */
351 static void lv24020lp_write(unsigned int address, unsigned int data)
353 /* shadow logical values but do logical=>physical remappings on some
354 registers' data. */
355 lv24020lp_regs[address] = data;
357 switch (address)
359 case FM_OSC:
360 /* L: 000..255
361 * P: 255..000 */
362 data = 255 - data;
363 break;
364 case FM_CAP:
365 /* L: 000..063, 064..191
366 * P: 255..192, 127..000 */
367 data = ((data < 64) ? 255 : (255 - 64)) - data;
368 break;
369 case RADIO_CTRL1:
370 /* L: data
371 * P: data | always "1" bits */
372 data |= (1 << 4) | (1 << 1) | (1 << 0);
373 break;
376 /* Check if interface is turned on */
377 if (!(tuner_status & TUNER_POWERED))
378 return;
380 address = lv24020lp_begin_write(address);
382 /* data first */
383 lv24020lp_send_byte(data);
384 /* then address */
385 lv24020lp_send_byte(address);
387 lv24020lp_end_write();
390 /* helpers to set/clear register bits */
391 static void lv24020lp_write_set(unsigned int address, unsigned int bits)
393 lv24020lp_write(address, lv24020lp_regs[address] | bits);
396 static void lv24020lp_write_clear(unsigned int address, unsigned int bits)
398 lv24020lp_write(address, lv24020lp_regs[address] & ~bits);
401 /* read a byte from a tuner register */
402 static unsigned int lv24020lp_read(unsigned int address)
404 int i;
405 unsigned int toread;
407 /* Check if interface is turned on */
408 if (!(tuner_status & TUNER_POWERED))
409 return 0;
411 address = lv24020lp_begin_write(address);
413 /* address */
414 lv24020lp_send_byte(address);
416 lv24020lp_end_write();
418 /* data */
419 toread = 0;
420 for (i = 0; i < 8; i++)
422 TUNER_GPIO_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN);
423 udelay(FM_CLK_DELAY);
425 if (TUNER_GPIO_INPUT_VAL & (1 << FM_DATA_PIN))
426 toread |= (1 << i);
428 TUNER_GPIO_OUTPUT_VAL |= (1 << FM_CLOCK_PIN);
429 udelay(FM_CLK_DELAY);
432 return toread;
435 /* enables auto frequency centering */
436 static void enable_afc(bool enabled)
438 unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1];
440 if (enabled)
442 radio_ctrl1 &= ~RST_AFC;
443 radio_ctrl1 |= EN_AFC;
445 else
447 radio_ctrl1 |= RST_AFC;
448 radio_ctrl1 &= ~EN_AFC;
451 lv24020lp_write(RADIO_CTRL1, radio_ctrl1);
454 static int calculate_coef(unsigned fkhz)
456 /* Overflow below 66000kHz --
457 My tuner tunes down to a min of ~72600kHz but datasheet mentions
458 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */
459 return fkhz < 66000 ?
460 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz);
463 static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2)
465 return y1 == y2 ?
466 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1;
469 static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2)
471 return x1 == x2 ?
472 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1;
475 /* this performs measurements of IF, FM and Stereo frequencies
476 * Input can be: MSS_FM, MSS_IF, MSS_SD */
477 static int tuner_measure(unsigned char type, int scale, int duration)
479 int64_t finval;
481 /* enable measuring */
482 lv24020lp_write_set(MSRC_SEL, type);
483 lv24020lp_write_clear(CNT_CTRL, CNT_SEL);
484 lv24020lp_write_set(RADIO_CTRL1, EN_MEAS);
486 /* reset counter */
487 lv24020lp_write_set(CNT_CTRL, CNT1_CLR);
488 lv24020lp_write_clear(CNT_CTRL, CNT1_CLR);
490 /* start counter, delay for specified time and stop it */
491 lv24020lp_write_set(CNT_CTRL, CNT_EN);
492 udelay(duration*1000 - 16);
493 lv24020lp_write_clear(CNT_CTRL, CNT_EN);
495 /* read tick count */
496 finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L);
498 /* restore measure mode */
499 lv24020lp_write_clear(RADIO_CTRL1, EN_MEAS);
500 lv24020lp_write_clear(MSRC_SEL, type);
502 /* convert value */
503 if (type == MSS_FM)
504 finval = scale*finval*256 / duration;
505 else
506 finval = scale*finval / duration;
508 /* This function takes a loooong time and other stuff needs
509 running by now */
510 yield();
512 return (int)finval;
515 /* set the FM oscillator frequency */
516 static void set_frequency(int freq)
518 int coef, cap_value, osc_value;
519 int f1, f2, x1, x2;
520 int count;
522 TUNER_LOG_OPEN();
524 TUNER_LOG("set_frequency(%d)\n", freq);
526 enable_afc(false);
528 /* MHz -> kHz */
529 freq /= 1000;
531 TUNER_LOG("Select cap:\n");
533 coef = calculate_coef(freq);
534 cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high,
535 coef_00, coef_01);
537 osc_value = sw_osc_low;
538 lv24020lp_write(FM_OSC, osc_value);
540 /* Just in case - don't go into infinite loop */
541 for (count = 0; count < 30; count++)
543 int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
544 coef_00, coef_01);
545 int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
546 coef_10, coef_11);
547 int coef_fcur, cap_new, coef_cor, range;
549 lv24020lp_write(FM_CAP, cap_value);
551 range = y1 - y0;
552 f1 = tuner_measure(MSS_FM, 1, 16);
553 coef_fcur = calculate_coef(f1);
554 coef_cor = calculate_coef((f1*1000 + 32*256) / 1000);
555 y0 = coef_cor;
556 y1 = y0 + range;
558 TUNER_LOG("%d %d %d %d %d %d %d %d\n",
559 f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range);
561 if (coef >= y0 && coef <= y1)
563 osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high,
564 y0, y1);
566 if (osc_value >= sw_osc_low && osc_value <= sw_osc_high)
567 break;
570 cap_new = interpolate_x(coef, cap_value, sw_cap_high,
571 coef_fcur, coef_01);
573 if (cap_new == cap_value)
575 if (coef < coef_fcur)
576 cap_value++;
577 else
578 cap_value--;
580 else
582 cap_value = cap_new;
586 TUNER_LOG("osc_value: %d\n", osc_value);
588 TUNER_LOG("Tune:\n");
590 x1 = sw_osc_low, x2 = sw_osc_high;
591 /* FM_OSC already at SW_OSC low and f1 is already the measured
592 frequency */
596 int x2_new;
598 lv24020lp_write(FM_OSC, x2);
599 f2 = tuner_measure(MSS_FM, 1, 16);
601 if (abs(f2 - freq) <= 16)
603 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
604 break;
607 x2_new = interpolate_x(freq, x1, x2, f1, f2);
609 x1 = x2, f1 = f2, x2 = x2_new;
610 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
612 while (x2 != 0);
614 if (x2 == 0)
616 /* May still be close enough */
617 TUNER_LOG("tuning failed - diff: %d\n", f2 - freq);
620 enable_afc(true);
622 TUNER_LOG("\n");
624 TUNER_LOG_SYNC();
627 #define TOO_SMALL (1 << 0)
628 #define TOO_BIG (1 << 1)
629 #define APPROACH_UP_1 (1 << 2)
630 #define APPROACH_DOWN_1 (1 << 3)
632 static void fine_step_tune(int (*setcmp)(int regval), int regval, int step)
634 /* Registers are not always stable, timeout if best fit not found soon
635 enough */
636 unsigned long abort = current_tick + HZ*2;
637 int flags = 0;
639 while (TIME_BEFORE(current_tick, abort))
641 int cmp;
643 regval = regval + step;
645 cmp = setcmp(regval);
647 if (cmp == 0)
648 break;
650 step = abs(step);
652 if (cmp < 0)
654 flags |= TOO_SMALL;
655 if (step == 1)
656 flags |= APPROACH_UP_1;
658 else
660 step = -step;
661 flags |= TOO_BIG;
662 if (step == -1)
663 step |= APPROACH_DOWN_1;
666 if ((flags & APPROACH_UP_1) && (flags & APPROACH_DOWN_1))
667 break; /* approached with step=1: best fit value found */
669 if ((flags & TOO_SMALL) && (flags & TOO_BIG))
671 step /= 2;
672 if (step == 0)
673 step = 1;
674 flags &= ~(TOO_SMALL | TOO_BIG);
679 static int if_setcmp(int regval)
681 lv24020lp_write(IF_OSC, regval);
682 lv24020lp_write(IF_CENTER, regval);
683 lv24020lp_write(IF_BW, 65*regval/100);
685 if_set = tuner_measure(MSS_IF, 1000, 32);
687 /* This register is bounces around by a few hundred Hz and doesn't seem
688 to be precisely tuneable. Just do 110000 +/- 500 since it's not very
689 critical it seems. */
690 if (abs(if_set - 110000) <= 500)
691 return 0;
693 return if_set < 110000 ? -1 : 1;
696 static int sd_setcmp(int regval)
698 lv24020lp_write(SD_OSC, regval);
700 sd_set = tuner_measure(MSS_SD, 1000, 32);
702 if (abs(sd_set - 38300) <= 31)
703 return 0;
705 return sd_set < 38300 ? -1 : 1;
708 static void set_sleep(bool sleep)
710 if (sleep || tuner_awake())
711 return;
713 if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) !=
714 (TUNER_PRESENT | TUNER_POWERED))
715 return;
717 enable_afc(false);
719 /* 2. Calibrate the IF frequency at 110 kHz: */
720 lv24020lp_write_clear(RADIO_CTRL2, IF_PM_L);
721 fine_step_tune(if_setcmp, 0x80, 8);
722 lv24020lp_write_set(RADIO_CTRL2, IF_PM_L);
724 /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */
725 lv24020lp_write_set(STEREO_CTRL, SD_PM);
726 fine_step_tune(sd_setcmp, 0x80, 8);
727 lv24020lp_write_clear(STEREO_CTRL, SD_PM);
729 /* calculate FM tuning coefficients */
730 lv24020lp_write(FM_CAP, sw_cap_low);
731 lv24020lp_write(FM_OSC, sw_osc_low);
732 coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
734 lv24020lp_write(FM_CAP, sw_cap_high);
735 coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
737 lv24020lp_write(FM_CAP, sw_cap_low);
738 lv24020lp_write(FM_OSC, sw_osc_high);
739 coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
741 lv24020lp_write(FM_CAP, sw_cap_high);
742 coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
744 /* set various audio level settings */
745 lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
746 lv24020lp_write_set(RADIO_CTRL2, AGCSP);
747 lv24020lp_write_set(RADIO_CTRL3, VOLSH);
748 lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
749 lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
750 PW_RAD);
752 tuner_status |= TUNER_AWAKE;
755 static int lp24020lp_tuned(void)
757 return RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f;
760 static int lv24020lp_debug_info(int setting)
762 int val = -1;
764 if (setting >= LV24020LP_DEBUG_FIRST && setting <= LV24020LP_DEBUG_LAST)
766 val = 0;
768 if (tuner_awake())
770 switch (setting)
772 /* tuner-specific debug info */
773 case LV24020LP_CTRL_STAT:
774 val = lv24020lp_read(CTRL_STAT);
775 break;
777 case LV24020LP_REG_STAT:
778 val = lv24020lp_read(RADIO_STAT);
779 break;
781 case LV24020LP_MSS_FM:
782 val = tuner_measure(MSS_FM, 1, 16);
783 break;
785 case LV24020LP_MSS_IF:
786 val = tuner_measure(MSS_IF, 1000, 16);
787 break;
789 case LV24020LP_MSS_SD:
790 val = tuner_measure(MSS_SD, 1000, 16);
791 break;
793 case LV24020LP_IF_SET:
794 val = if_set;
795 break;
797 case LV24020LP_SD_SET:
798 val = sd_set;
799 break;
804 return val;
807 /** Public interfaces **/
808 void lv24020lp_init(void)
810 mutex_init(&tuner_mtx);
813 void lv24020lp_lock(void)
815 mutex_lock(&tuner_mtx);
818 void lv24020lp_unlock(void)
820 mutex_unlock(&tuner_mtx);
823 /* This function expects the driver to be locked externally */
824 void lv24020lp_power(bool status)
826 static const unsigned char tuner_defaults[][2] =
828 /* Block 1 writeable registers */
829 { MSRC_SEL, AFC_LVL },
830 { FM_OSC, 0x80 },
831 { SD_OSC, 0x80 },
832 { IF_OSC, 0x80 },
833 { CNT_CTRL, CNT1_CLR | SWP_CNT_L },
834 { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */
835 { FM_CAP, 0x80 },
836 /* { IRQ_OUT, 0x00 }, No action on this register (skip) */
837 /* Block 2 writeable registers */
838 { RADIO_CTRL1, EN_AFC },
839 { IF_CENTER, 0x80 },
840 { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */
841 { RADIO_CTRL2, IF_PM_L },
842 { RADIO_CTRL3, AGC_SLVL | SE_FM },
843 { STEREO_CTRL, FMCS_SET(4) | AUTOSSR },
844 { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) },
845 { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */
846 { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD },
849 unsigned i;
851 if (status)
853 tuner_status |= (TUNER_PRESENCE_CHECKED | TUNER_POWERED);
855 /* if tuner is present, CHIP ID is 0x09 */
856 if (lv24020lp_read(CHIP_ID) == 0x09)
858 tuner_status |= TUNER_PRESENT;
860 /* After power-up, the LV2400x needs to be initialized as
861 follows: */
863 /* 1. Write default values to the registers: */
864 lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */
865 for (i = 0; i < ARRAYLEN(tuner_defaults); i++)
866 lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]);
868 /* Complete the startup calibration if the tuner is woken */
869 sleep(HZ/10);
872 else
874 /* Power off */
875 if (tuner_status & TUNER_PRESENT)
876 lv24020lp_write_clear(PW_SCTRL, PW_RAD);
878 tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE);
882 int lv24020lp_set(int setting, int value)
884 int val = 1;
886 mutex_lock(&tuner_mtx);
888 switch(setting)
890 case RADIO_SLEEP:
891 set_sleep(value);
892 break;
894 case RADIO_FREQUENCY:
895 set_frequency(value);
896 break;
898 case RADIO_SCAN_FREQUENCY:
899 /* TODO: really implement this */
900 set_frequency(value);
901 val = lp24020lp_tuned();
902 break;
904 case RADIO_MUTE:
905 if (value)
906 lv24020lp_write_clear(RADIO_CTRL3, AMUTE_L);
907 else
908 lv24020lp_write_set(RADIO_CTRL3, AMUTE_L);
909 break;
911 case RADIO_REGION:
912 if (lv24020lp_region_data[value])
913 lv24020lp_write_set(AUDIO_CTRL2, DEEMP);
914 else
915 lv24020lp_write_clear(AUDIO_CTRL2, DEEMP);
916 break;
918 case RADIO_FORCE_MONO:
919 if (value)
920 lv24020lp_write_set(STEREO_CTRL, ST_M);
921 else
922 lv24020lp_write_clear(STEREO_CTRL, ST_M);
923 break;
925 default:
926 value = -1;
929 mutex_unlock(&tuner_mtx);
931 return val;
934 int lv24020lp_get(int setting)
936 int val = -1;
938 mutex_lock(&tuner_mtx);
940 switch(setting)
942 case RADIO_TUNED:
943 /* TODO: really implement this */
944 val = lp24020lp_tuned();
945 break;
947 case RADIO_STEREO:
948 val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0;
949 break;
951 case RADIO_PRESENT:
953 bool fmstatus = true;
955 if (!(tuner_status & TUNER_PRESENCE_CHECKED))
956 fmstatus = tuner_power(true);
958 val = (tuner_status & TUNER_PRESENT) != 0;
960 if (!fmstatus)
961 tuner_power(false);
962 break;
965 default:
966 val = lv24020lp_debug_info(setting);
969 mutex_unlock(&tuner_mtx);
971 return val;
973 #endif /* BOOTLOADER */