summaryrefslogtreecommitdiff
path: root/firmware/drivers/tuner/lv24020lp.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-07-14 11:20:31 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-07-14 11:20:31 +0000
commit7d759f6b9ca96a4a64c71ac301eb59cb9702e74c (patch)
treefbaf5104716ccfed888c7d8506dbee15a881d4a2 /firmware/drivers/tuner/lv24020lp.c
parentb51a20fb9b1cc57b813d1c0b90ad8c010b9eab84 (diff)
downloadrockbox-7d759f6b9ca96a4a64c71ac301eb59cb9702e74c.tar.gz
rockbox-7d759f6b9ca96a4a64c71ac301eb59cb9702e74c.zip
Do some planned radio interface cleanup since adding in the LV24020LP.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13880 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/tuner/lv24020lp.c')
-rw-r--r--firmware/drivers/tuner/lv24020lp.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/firmware/drivers/tuner/lv24020lp.c b/firmware/drivers/tuner/lv24020lp.c
new file mode 100644
index 0000000000..9ec68f9a27
--- /dev/null
+++ b/firmware/drivers/tuner/lv24020lp.c
@@ -0,0 +1,870 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 * Tuner driver for the Sanyo LV24020LP
10 *
11 * Copyright (C) 2007 Ivan Zupan
12 *
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.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
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 "fmradio.h" /* physical interface driver */
28#include "sound.h"
29#include "pp5024.h"
30#include "system.h"
31
32#ifndef BOOTLOADER
33
34#if 0
35/* define to enable tuner logging */
36#define SANYO_TUNER_LOG
37#endif
38
39#ifdef SANYO_TUNER_LOG
40#include "sprintf.h"
41#include "file.h"
42
43static int fd_log = -1;
44
45#define TUNER_LOG_OPEN() if (fd_log < 0) \
46 fd_log = creat("/tuner_dump.txt")
47/* syncing required because close() is never called */
48#define TUNER_LOG_SYNC() fsync(fd_log)
49#define TUNER_LOG(s...) fdprintf(fd_log, s)
50#else
51#define TUNER_LOG_OPEN()
52#define TUNER_LOG_SYNC()
53#define TUNER_LOG(s...)
54#endif /* SANYO_TUNER_LOG */
55
56/** tuner register defines **/
57
58/* pins on GPIOH port */
59#define FM_NRW_PIN 3
60#define FM_CLOCK_PIN 4
61#define FM_DATA_PIN 5
62#define FM_CLK_DELAY 1
63
64/* block 1 registers */
65
66/* R */
67#define CHIP_ID 0x00
68
69/* W */
70#define BLK_SEL 0x01
71 #define BLK1 0x01
72 #define BLK2 0x02
73
74/* W */
75#define MSRC_SEL 0x02
76 #define MSR_O (1 << 7)
77 #define AFC_LVL (1 << 6)
78 #define AFC_SPD (1 << 5)
79 #define MSS_SD (1 << 2)
80 #define MSS_FM (1 << 1)
81 #define MSS_IF (1 << 0)
82
83/* W */
84#define FM_OSC 0x03
85
86/* W */
87#define SD_OSC 0x04
88
89/* W */
90#define IF_OSC 0x05
91
92/* W */
93#define CNT_CTRL 0x06
94 #define CNT1_CLR (1 << 7)
95 #define CTAB(x) ((x) & (0x7 << 4))
96 #define CTAB_STOP_2 (0x0 << 4)
97 #define CTAB_STOP_8 (0x1 << 4)
98 #define CTAB_STOP_32 (0x2 << 4)
99 #define CTAB_STOP_128 (0x3 << 4)
100 #define CTAB_STOP_512 (0x4 << 4)
101 #define CTAB_STOP_2048 (0x5 << 4)
102 #define CTAB_STOP_8192 (0x6 << 4)
103 #define CTAB_STOP_32768 (0x7 << 4)
104 #define SWP_CNT_L (1 << 3)
105 #define CNT_EN (1 << 2)
106 #define CNT_SEL (1 << 1)
107 #define CNT_SET (1 << 0)
108
109/* W */
110#define IRQ_MSK 0x08
111 #define IM_MS (1 << 6)
112 #define IRQ_LVL (1 << 3)
113 #define IM_AFC (1 << 2)
114 #define IM_FS (1 << 1)
115 #define IM_CNT2 (1 << 0)
116
117/* W */
118#define FM_CAP 0x09
119
120/* R */
121#define CNT_L 0x0a /* Counter register low value */
122
123/* R */
124#define CNT_H 0x0b /* Counter register high value */
125
126/* R */
127#define CTRL_STAT 0x0c
128 #define AFC_FLG (1 << 0)
129
130/* R */
131#define RADIO_STAT 0x0d
132 #define RSS_MS (1 << 7)
133 #define RSS_FS(x) ((x) & 0x7f)
134 #define RSS_FS_GET(x) ((x) & 0x7f)
135 #define RSS_FS_SET(x) (x)
136/* Note: Reading this register will clear field strength and mono/stereo interrupt. */
137
138/* R */
139#define IRQ_ID 0x0e
140 #define II_CNT2 (1 << 5)
141 #define II_AFC (1 << 3)
142 #define II_FS_MS (1 << 0)
143
144/* W */
145#define IRQ_OUT 0x0f
146
147/* block 2 registers - offset added in order to id and avoid manual
148 switching */
149#define BLK2_START 0x10
150
151/* W */
152#define RADIO_CTRL1 (0x02 + BLK2_START)
153 #define EN_MEAS (1 << 7)
154 #define EN_AFC (1 << 6)
155 #define DIR_AFC (1 << 3)
156 #define RST_AFC (1 << 2)
157
158/* W */
159#define IF_CENTER (0x03 + BLK2_START)
160
161/* W */
162#define IF_BW (0x05 + BLK2_START)
163
164/* W */
165#define RADIO_CTRL2 (0x06 + BLK2_START)
166 #define VREF2 (1 << 7)
167 #define VREF (1 << 6)
168 #define STABI_BP (1 << 5)
169 #define IF_PM_L (1 << 4)
170 #define AGCSP (1 << 1)
171 #define AM_ANT_BSW (1 << 0) /* ?? */
172
173/* W */
174#define RADIO_CTRL3 (0x07 + BLK2_START)
175 #define AGC_SLVL (1 << 7)
176 #define VOLSH (1 << 6)
177 #define TB_ON (1 << 5)
178 #define AMUTE_L (1 << 4)
179 #define SE_FM (1 << 3)
180 #define SE_BE (1 << 1)
181 #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */
182
183/* W */
184#define STEREO_CTRL (0x08 + BLK2_START)
185 #define FRCST (1 << 7)
186 #define FMCS(x) ((x) & (0x7 << 4))
187 #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4)
188 #define FMCS_SET(x) ((x) << 4)
189 #define AUTOSSR (1 << 3)
190 #define PILTCA (1 << 2)
191 #define SD_PM (1 << 1)
192 #define ST_M (1 << 0)
193
194/* W */
195#define AUDIO_CTRL1 (0x09 + BLK2_START)
196 #define TONE_LVL(x) ((x) & (0xf << 4))
197 #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4)
198 #define TONE_LVL_SET(x) ((x) << 4)
199 #define VOL_LVL(x) ((x) & 0xf)
200 #define VOL_LVL_GET(x) ((x) & 0xf)
201 #define VOL_LVL_SET(x) ((x) << 4)
202
203/* W */
204#define AUDIO_CTRL2 (0x0a + BLK2_START)
205 #define BASS_PP (1 << 0)
206 #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */
207 #define BASS_N (1 << 2)
208 #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */
209 #define TREB_N (1 << 4)
210 #define DEEMP (1 << 5)
211 #define BPFREQ(x) ((x) & (0x3 << 6))
212 #define BPFREQ_2_0K (0x0 << 6)
213 #define BPFREQ_1_0K (0x1 << 6)
214 #define BPFREQ_0_5K (0x2 << 6)
215 #define BPFREQ_HIGH (0x3 << 6)
216
217/* W */
218#define PW_SCTRL (0x0b + BLK2_START)
219 #define SS_CTRL(x) ((x) & (0x7 << 5))
220 #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5)
221 #define SS_CTRL_SET(x) ((x) << 5)
222 #define SM_CTRL(x) ((x) & (0x7 << 2))
223 #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2)
224 #define SM_CTRL_SET(x) ((x) << 2)
225 #define PW_HPA (1 << 1) /* LV24002 only */
226 #define PW_RAD (1 << 0)
227
228/* shadow for writeable registers */
229#define TUNER_POWERED (1 << 0)
230#define TUNER_PRESENT (1 << 1)
231#define TUNER_AWAKE (1 << 2)
232#define TUNER_PRESENCE_CHECKED (1 << 3)
233static unsigned tuner_status = 0;
234
235static unsigned char lv24020lp_regs[0x1c];
236
237static const int sw_osc_low = 10; /* 30; */
238static const int sw_osc_high = 240; /* 200; */
239static const int sw_cap_low = 0;
240static const int sw_cap_high = 191;
241
242/* linear coefficients used for tuning */
243static int coef_00, coef_01, coef_10, coef_11;
244
245/* DAC control register set values */
246int if_set, sd_set;
247
248static inline bool tuner_awake(void)
249{
250 return (tuner_status & TUNER_AWAKE) != 0;
251}
252
253/* send a byte to the tuner - expects write mode to be current */
254static void lv24020lp_send_byte(unsigned int byte)
255{
256 int i;
257
258 byte <<= FM_DATA_PIN;
259
260 for (i = 0; i < 8; i++)
261 {
262 GPIOH_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN);
263
264 GPIOH_OUTPUT_VAL = (GPIOH_OUTPUT_VAL & ~(1 << FM_DATA_PIN)) |
265 (byte & (1 << FM_DATA_PIN));
266
267 GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN);
268 udelay(FM_CLK_DELAY);
269
270 byte >>= 1;
271 }
272}
273
274/* end a write cycle on the tuner */
275static void lv24020lp_end_write(void)
276{
277 /* switch back to read mode */
278 GPIOH_OUTPUT_EN &= ~(1 << FM_DATA_PIN);
279 GPIOH_OUTPUT_VAL &= ~(1 << FM_NRW_PIN);
280}
281
282/* prepare a write cycle on the tuner */
283static unsigned int lv24020lp_begin_write(unsigned int address)
284{
285 /* Get register's block, translate address */
286 unsigned int blk = (address >= BLK2_START) ?
287 (address -= BLK2_START, BLK2) : BLK1;
288
289 for (;;)
290 {
291 /* Prepare 3-wire bus pins for write cycle */
292 GPIOH_OUTPUT_VAL |= (1 << FM_NRW_PIN);
293 GPIOH_OUTPUT_EN |= (1 << FM_DATA_PIN);
294
295 udelay(FM_CLK_DELAY);
296
297 /* current block == register block? */
298 if (blk == lv24020lp_regs[BLK_SEL])
299 return address;
300
301 /* switch block */
302 lv24020lp_regs[BLK_SEL] = blk;
303
304 /* data first */
305 lv24020lp_send_byte(blk);
306 /* then address */
307 lv24020lp_send_byte(BLK_SEL);
308
309 lv24020lp_end_write();
310
311 udelay(FM_CLK_DELAY);
312 }
313}
314
315/* write a byte to a tuner register */
316static void lv24020lp_write(unsigned int address, unsigned int data)
317{
318 /* shadow logical values but do logical=>physical remappings on some
319 registers' data. */
320 lv24020lp_regs[address] = data;
321
322 switch (address)
323 {
324 case FM_OSC:
325 /* L: 000..255
326 * P: 255..000 */
327 data = 255 - data;
328 break;
329 case FM_CAP:
330 /* L: 000..063, 064..191
331 * P: 255..192, 127..000 */
332 data = ((data < 64) ? 255 : (255 - 64)) - data;
333 break;
334 case RADIO_CTRL1:
335 /* L: data
336 * P: data | always "1" bits */
337 data |= (1 << 4) | (1 << 1) | (1 << 0);
338 break;
339 }
340
341 address = lv24020lp_begin_write(address);
342
343 /* data first */
344 lv24020lp_send_byte(data);
345 /* then address */
346 lv24020lp_send_byte(address);
347
348 lv24020lp_end_write();
349}
350
351/* helpers to set/clear register bits */
352static void lv24020lp_write_or(unsigned int address, unsigned int bits)
353{
354 lv24020lp_write(address, lv24020lp_regs[address] | bits);
355}
356
357static void lv24020lp_write_and(unsigned int address, unsigned int bits)
358{
359 lv24020lp_write(address, lv24020lp_regs[address] & bits);
360}
361
362/* read a byte from a tuner register */
363static unsigned int lv24020lp_read(unsigned int address)
364{
365 int i;
366 unsigned int toread;
367
368 address = lv24020lp_begin_write(address);
369
370 /* address */
371 lv24020lp_send_byte(address);
372
373 lv24020lp_end_write();
374
375 /* data */
376 toread = 0;
377 for (i = 0; i < 8; i++)
378 {
379 GPIOH_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN);
380 udelay(FM_CLK_DELAY);
381
382 toread |= (GPIOH_INPUT_VAL & (1 << FM_DATA_PIN)) << i;
383
384 GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN);
385 }
386
387 return toread >> FM_DATA_PIN;
388}
389
390/* enables auto frequency centering */
391static void enable_afc(bool enabled)
392{
393 unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1];
394
395 if (enabled)
396 {
397 radio_ctrl1 &= ~RST_AFC;
398 radio_ctrl1 |= EN_AFC;
399 }
400 else
401 {
402 radio_ctrl1 |= RST_AFC;
403 radio_ctrl1 &= ~EN_AFC;
404 }
405
406 lv24020lp_write(RADIO_CTRL1, radio_ctrl1);
407}
408
409static int calculate_coef(unsigned fkhz)
410{
411 /* Overflow below 66000kHz --
412 My tuner tunes down to a min of ~72600kHz but datasheet mentions
413 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */
414 return fkhz < 66000 ?
415 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz);
416}
417
418static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2)
419{
420 return y1 == y2 ?
421 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1;
422}
423
424static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2)
425{
426 return x1 == x2 ?
427 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1;
428}
429
430/* this performs measurements of IF, FM and Stereo frequencies
431 * Input can be: MSS_FM, MSS_IF, MSS_SD */
432static int tuner_measure(unsigned char type, int scale, int duration)
433{
434 int64_t finval;
435
436 if (!tuner_awake())
437 return 0;
438
439 /* enable measuring */
440 lv24020lp_write_or(MSRC_SEL, type);
441 lv24020lp_write_and(CNT_CTRL, ~CNT_SEL);
442 lv24020lp_write_or(RADIO_CTRL1, EN_MEAS);
443
444 /* reset counter */
445 lv24020lp_write_or(CNT_CTRL, CNT1_CLR);
446 lv24020lp_write_and(CNT_CTRL, ~CNT1_CLR);
447
448 /* start counter, delay for specified time and stop it */
449 lv24020lp_write_or(CNT_CTRL, CNT_EN);
450 udelay(duration*1000 - 16);
451 lv24020lp_write_and(CNT_CTRL, ~CNT_EN);
452
453 /* read tick count */
454 finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L);
455
456 /* restore measure mode */
457 lv24020lp_write_and(RADIO_CTRL1, ~EN_MEAS);
458 lv24020lp_write_and(MSRC_SEL, ~type);
459
460 /* convert value */
461 if (type == MSS_FM)
462 finval = scale*finval*256 / duration;
463 else
464 finval = scale*finval / duration;
465
466 return (int)finval;
467}
468
469/* set the FM oscillator frequency */
470static void set_frequency(int freq)
471{
472 int coef, cap_value, osc_value;
473 int f1, f2, x1, x2;
474 int count;
475
476 if (!tuner_awake())
477 return;
478
479 TUNER_LOG_OPEN();
480
481 TUNER_LOG("set_frequency(%d)\n", freq);
482
483 enable_afc(false);
484
485 /* MHz -> kHz */
486 freq /= 1000;
487
488 TUNER_LOG("Select cap:\n");
489
490 coef = calculate_coef(freq);
491 cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high,
492 coef_00, coef_01);
493
494 osc_value = sw_osc_low;
495 lv24020lp_write(FM_OSC, osc_value);
496
497 /* Just in case - don't go into infinite loop */
498 for (count = 0; count < 30; count++)
499 {
500 int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
501 coef_00, coef_01);
502 int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high,
503 coef_10, coef_11);
504 int coef_fcur, cap_new, coef_cor, range;
505
506 lv24020lp_write(FM_CAP, cap_value);
507
508 range = y1 - y0;
509 f1 = tuner_measure(MSS_FM, 1, 16);
510 coef_fcur = calculate_coef(f1);
511 coef_cor = calculate_coef((f1*1000 + 32*256) / 1000);
512 y0 = coef_cor;
513 y1 = y0 + range;
514
515 TUNER_LOG("%d %d %d %d %d %d %d %d\n",
516 f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range);
517
518 if (coef >= y0 && coef <= y1)
519 {
520 osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high,
521 y0, y1);
522
523 if (osc_value >= sw_osc_low && osc_value <= sw_osc_high)
524 break;
525 }
526
527 cap_new = interpolate_x(coef, cap_value, sw_cap_high,
528 coef_fcur, coef_01);
529
530 if (cap_new == cap_value)
531 {
532 if (coef < coef_fcur)
533 cap_value++;
534 else
535 cap_value--;
536 }
537 else
538 {
539 cap_value = cap_new;
540 }
541 }
542
543 TUNER_LOG("osc_value: %d\n", osc_value);
544
545 TUNER_LOG("Tune:\n");
546
547 x1 = sw_osc_low, x2 = sw_osc_high;
548 /* FM_OSC already at SW_OSC low and f1 is already the measured
549 frequency */
550
551 do
552 {
553 int x2_new;
554
555 lv24020lp_write(FM_OSC, x2);
556 f2 = tuner_measure(MSS_FM, 1, 16);
557
558 if (abs(f2 - freq) <= 16)
559 {
560 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
561 break;
562 }
563
564 x2_new = interpolate_x(freq, x1, x2, f1, f2);
565
566 x1 = x2, f1 = f2, x2 = x2_new;
567 TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2);
568 }
569 while (x2 != 0);
570
571 if (x2 == 0)
572 {
573 /* May still be close enough */
574 TUNER_LOG("tuning failed - diff: %d\n", f2 - freq);
575 }
576
577 enable_afc(true);
578
579 TUNER_LOG("\n");
580
581 TUNER_LOG_SYNC();
582}
583
584static void fine_step_tune(int (*setcmp)(int regval), int regval, int step)
585{
586 /* Registers are not always stable, timeout if best fit not found soon
587 enough */
588 unsigned long abort = current_tick + HZ*2;
589 int flags = 0;
590
591 while (TIME_BEFORE(current_tick, abort))
592 {
593 int cmp;
594
595 regval = regval + step;
596
597 cmp = setcmp(regval);
598
599 if (cmp == 0)
600 break;
601
602 step = abs(step);
603
604 if (cmp < 0)
605 {
606 flags |= 1;
607 if (step == 1)
608 flags |= 4;
609 }
610 else
611 {
612 step = -step;
613 flags |= 2;
614 if (step == -1)
615 step |= 8;
616 }
617
618 if ((flags & 0xc) == 0xc)
619 break;
620
621 if ((flags & 0x3) == 0x3)
622 {
623 step /= 2;
624 if (step == 0)
625 step = 1;
626 flags &= ~3;
627 }
628 }
629}
630
631static int if_setcmp(int regval)
632{
633 lv24020lp_write(IF_OSC, regval);
634 lv24020lp_write(IF_CENTER, regval);
635 lv24020lp_write(IF_BW, 65*regval/100);
636
637 if_set = tuner_measure(MSS_IF, 1000, 32);
638
639 /* This register is bounces around by a few hundred Hz and doesn't seem
640 to be precisely tuneable. Just do 110000 +/- 500 since it's not very
641 critical it seems. */
642 if (abs(if_set - 109500) <= 500)
643 return 0;
644
645 return if_set < 109500 ? -1 : 1;
646}
647
648static int sd_setcmp(int regval)
649{
650 lv24020lp_write(SD_OSC, regval);
651
652 sd_set = tuner_measure(MSS_SD, 1000, 32);
653
654 if (abs(sd_set - 38300) <= 31)
655 return 0;
656
657 return sd_set < 38300 ? -1 : 1;
658}
659
660static void set_sleep(bool sleep)
661{
662 if (sleep || tuner_awake())
663 return;
664
665 if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) !=
666 (TUNER_PRESENT | TUNER_POWERED))
667 return;
668
669 tuner_status |= TUNER_AWAKE;
670
671 enable_afc(false);
672
673 /* 2. Calibrate the IF frequency at 110 kHz: */
674 lv24020lp_write_and(RADIO_CTRL2, ~IF_PM_L);
675 fine_step_tune(if_setcmp, 0x80, 8);
676 lv24020lp_write_or(RADIO_CTRL2, IF_PM_L);
677
678 /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */
679 lv24020lp_write_or(STEREO_CTRL, SD_PM);
680 fine_step_tune(sd_setcmp, 0x80, 8);
681 lv24020lp_write_and(STEREO_CTRL, ~SD_PM);
682
683 /* calculate FM tuning coefficients */
684 lv24020lp_write(FM_CAP, sw_cap_low);
685 lv24020lp_write(FM_OSC, sw_osc_low);
686 coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
687
688 lv24020lp_write(FM_CAP, sw_cap_high);
689 coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
690
691 lv24020lp_write(FM_CAP, sw_cap_low);
692 lv24020lp_write(FM_OSC, sw_osc_high);
693 coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
694
695 lv24020lp_write(FM_CAP, sw_cap_high);
696 coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
697
698 /* set various audio level settings */
699 lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
700 lv24020lp_write_or(RADIO_CTRL2, AGCSP);
701 lv24020lp_write_or(RADIO_CTRL3, VOLSH);
702 lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
703 lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
704 PW_RAD);
705}
706
707/** Public interfaces **/
708void lv24020lp_power(bool status)
709{
710 static const unsigned char tuner_defaults[][2] =
711 {
712 /* Block 1 writeable registers */
713 { MSRC_SEL, AFC_LVL },
714 { FM_OSC, 0x80 },
715 { SD_OSC, 0x80 },
716 { IF_OSC, 0x80 },
717 { CNT_CTRL, CNT1_CLR | SWP_CNT_L },
718 { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */
719 { FM_CAP, 0x80 },
720 /* { IRQ_OUT, 0x00 }, No action on this register (skip) */
721 /* Block 2 writeable registers */
722 { RADIO_CTRL1, EN_AFC },
723 { IF_CENTER, 0x80 },
724 { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */
725 { RADIO_CTRL2, IF_PM_L },
726 { RADIO_CTRL3, AGC_SLVL | SE_FM },
727 { STEREO_CTRL, FMCS_SET(4) | AUTOSSR },
728 { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) },
729 { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */
730 { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD },
731 };
732
733 unsigned i;
734
735 if (status)
736 {
737 tuner_status |= TUNER_POWERED | TUNER_PRESENCE_CHECKED;
738
739 /* if tuner is present, CHIP ID is 0x09 */
740 if (lv24020lp_read(CHIP_ID) == 0x09)
741 {
742 tuner_status |= TUNER_PRESENT;
743
744 /* After power-up, the LV2400x needs to be initialized as
745 follows: */
746
747 /* 1. Write default values to the registers: */
748 lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */
749 for (i = 0; i < ARRAYLEN(tuner_defaults); i++)
750 lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]);
751
752 /* Complete the startup calibration if the tuner is woken */
753 udelay(100000);
754 }
755 }
756 else
757 {
758 /* Power off */
759 if (tuner_status & TUNER_PRESENT)
760 lv24020lp_write_and(PW_SCTRL, ~PW_RAD);
761
762 tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE);
763 }
764}
765
766int lv24020lp_set(int setting, int value)
767{
768 int val = 1;
769
770 switch(setting)
771 {
772 case RADIO_SLEEP:
773 set_sleep(value);
774 break;
775
776 case RADIO_FREQUENCY:
777 set_frequency(value);
778 break;
779
780 case RADIO_SCAN_FREQUENCY:
781 /* TODO: really implement this */
782 set_frequency(value);
783 val = lv24020lp_get(RADIO_TUNED);
784 break;
785
786 case RADIO_MUTE:
787 if (value)
788 lv24020lp_write_and(RADIO_CTRL3, ~AMUTE_L);
789 else
790 lv24020lp_write_or(RADIO_CTRL3, AMUTE_L);
791 break;
792
793 case RADIO_REGION:
794 {
795 if (lv24020lp_region_data[value])
796 lv24020lp_write_or(AUDIO_CTRL2, DEEMP);
797 else
798 lv24020lp_write_and(AUDIO_CTRL2, ~DEEMP);
799 break;
800 }
801
802 case RADIO_FORCE_MONO:
803 if (value)
804 lv24020lp_write_or(STEREO_CTRL, ST_M);
805 else
806 lv24020lp_write_and(STEREO_CTRL, ~ST_M);
807 break;
808
809 default:
810 val = -1;
811 }
812
813 return val;
814}
815
816int lv24020lp_get(int setting)
817{
818 int val = -1;
819
820 switch(setting)
821 {
822 case RADIO_TUNED:
823 /* TODO: really implement this */
824 val = RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f;
825 break;
826
827 case RADIO_STEREO:
828 val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0;
829 break;
830
831 case RADIO_PRESENT:
832 {
833 bool fmstatus = true;
834
835 if (!(tuner_status & TUNER_PRESENCE_CHECKED))
836 fmstatus = tuner_power(true);
837
838 val = (tuner_status & TUNER_PRESENT) != 0;
839
840 if (!fmstatus)
841 tuner_power(false);
842 break;
843 }
844
845 /* tuner-specific debug info */
846 case LV24020LP_CTRL_STAT:
847 return lv24020lp_read(CTRL_STAT);
848
849 case LV24020LP_REG_STAT:
850 return lv24020lp_read(RADIO_STAT);
851
852 case LV24020LP_MSS_FM:
853 return tuner_measure(MSS_FM, 1, 16);
854
855 case LV24020LP_MSS_IF:
856 return tuner_measure(MSS_IF, 1000, 16);
857
858 case LV24020LP_MSS_SD:
859 return tuner_measure(MSS_SD, 1000, 16);
860
861 case LV24020LP_IF_SET:
862 return if_set;
863
864 case LV24020LP_SD_SET:
865 return sd_set;
866 }
867
868 return val;
869}
870#endif /* BOOTLOADER */