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