summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-06-06 19:23:48 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-06-06 19:23:48 +0000
commit21a4a87ca2553834b3f7b0c8f95f1b0889d8cb2c (patch)
treeb3c7693a1c1b2e24d5aeee3e3316bbcc404f7dbc
parentaf4cd0a84cc8ead0e3b9c32684c55235c792fb12 (diff)
downloadrockbox-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
-rw-r--r--apps/debug_menu.c34
-rw-r--r--apps/keymaps/keymap-e200.c15
-rw-r--r--apps/recorder/radio.c34
-rw-r--r--docs/CREDITS1
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/drivers/audio/as3514.c4
-rw-r--r--firmware/export/config-e200.h6
-rw-r--r--firmware/export/config.h1
-rw-r--r--firmware/export/tuner.h58
-rw-r--r--firmware/target/arm/pcm-pp.c4
-rw-r--r--firmware/target/arm/sandisk/sansa-e200/audio-e200.c9
-rw-r--r--firmware/tuner_sanyo.c909
12 files changed, 1039 insertions, 39 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 2279a27c13..32ab46cbda 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1990,16 +1990,48 @@ static bool dbg_fm_radio(void)
1990 1990
1991 lcd_setmargins(0, 0); 1991 lcd_setmargins(0, 0);
1992 1992
1993 fm_detected = radio_hardware_present();
1994
1993 while(1) 1995 while(1)
1994 { 1996 {
1995 int row = 0; 1997 int row = 0;
1996 1998
1997 lcd_clear_display(); 1999 lcd_clear_display();
1998 fm_detected = radio_hardware_present();
1999 2000
2000 snprintf(buf, sizeof buf, "HW detected: %s", fm_detected?"yes":"no"); 2001 snprintf(buf, sizeof buf, "HW detected: %s", fm_detected?"yes":"no");
2001 lcd_puts(0, row++, buf); 2002 lcd_puts(0, row++, buf);
2003#if (CONFIG_TUNER & LV24020LP)
2004 if (fm_detected)
2005 {
2006 snprintf(buf, sizeof buf, "CTRL_STAT: %02X",
2007 sanyo_get(RADIO_ALL) );
2008 lcd_puts(0, row++, buf);
2009
2010 snprintf(buf, sizeof buf, "RADIO_STAT: %02X",
2011 sanyo_get(RADIO_REG_STAT));
2012 lcd_puts(0, row++, buf);
2013
2014 snprintf(buf, sizeof buf, "MSS_FM: %d kHz",
2015 (sanyo_get(RADIO_MSS_FM) ) );
2016 lcd_puts(0, row++, buf);
2017
2018 snprintf(buf, sizeof buf, "MSS_IF: %d Hz",
2019 (sanyo_get(RADIO_MSS_IF) ) );
2020 lcd_puts(0, row++, buf);
2021
2022 snprintf(buf, sizeof buf, "MSS_SD: %d Hz",
2023 (sanyo_get(RADIO_MSS_SD) ) );
2024 lcd_puts(0, row++, buf);
2025
2026 snprintf(buf, sizeof buf, "if_set: %d Hz",
2027 (sanyo_get(RADIO_IF_SET) ) );
2028 lcd_puts(0, row++, buf);
2002 2029
2030 snprintf(buf, sizeof buf, "sd_set: %d Hz",
2031 (sanyo_get(RADIO_SD_SET) ) );
2032 lcd_puts(0, row++, buf);
2033 }
2034#endif
2003#if (CONFIG_TUNER & S1A0903X01) 2035#if (CONFIG_TUNER & S1A0903X01)
2004 snprintf(buf, sizeof buf, "Samsung regs: %08X", 2036 snprintf(buf, sizeof buf, "Samsung regs: %08X",
2005 samsung_get(RADIO_ALL)); 2037 samsung_get(RADIO_ALL));
diff --git a/apps/keymaps/keymap-e200.c b/apps/keymaps/keymap-e200.c
index 55e30ca258..318f495abe 100644
--- a/apps/keymaps/keymap-e200.c
+++ b/apps/keymaps/keymap-e200.c
@@ -219,6 +219,17 @@ static const struct button_mapping button_context_recscreen[] = {
219 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 219 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
220}; /* button_context_recscreen */ 220}; /* button_context_recscreen */
221 221
222/** FM Radio Screen **/
223static const struct button_mapping button_context_radio[] = {
224 { ACTION_FM_MENU, BUTTON_DOWN, BUTTON_NONE },
225 { ACTION_FM_PRESET, BUTTON_SELECT, BUTTON_NONE },
226 { ACTION_FM_STOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP },
227 { ACTION_FM_MODE, BUTTON_REC, BUTTON_NONE },
228 { ACTION_FM_EXIT, BUTTON_POWER, BUTTON_NONE },
229 { ACTION_FM_PLAY, BUTTON_UP|BUTTON_REL, BUTTON_UP },
230 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
231}; /* button_context_radio */
232
222static const struct button_mapping button_context_keyboard[] = { 233static const struct button_mapping button_context_keyboard[] = {
223 { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE }, 234 { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE },
224 { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, 235 { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
@@ -283,7 +294,9 @@ const struct button_mapping* get_context_mapping(int context)
283 return button_context_settings_time; 294 return button_context_settings_time;
284 295
285 case CONTEXT_YESNOSCREEN: 296 case CONTEXT_YESNOSCREEN:
286 return button_context_yesno; 297 return button_context_yesno;
298 case CONTEXT_FM:
299 return button_context_radio;
287 case CONTEXT_BOOKMARKSCREEN: 300 case CONTEXT_BOOKMARKSCREEN:
288 return button_context_bmark; 301 return button_context_bmark;
289 case CONTEXT_QUICKSCREEN: 302 case CONTEXT_QUICKSCREEN:
diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c
index a0e6c81e3a..9f3228be1f 100644
--- a/apps/recorder/radio.c
+++ b/apps/recorder/radio.c
@@ -90,6 +90,13 @@
90#elif CONFIG_KEYPAD == ONDIO_PAD 90#elif CONFIG_KEYPAD == ONDIO_PAD
91#define FM_RECORD_DBLPRE 91#define FM_RECORD_DBLPRE
92#define FM_RECORD 92#define FM_RECORD
93#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
94#define FM_MENU
95#define FM_PRESET
96#define FM_STOP
97#define FM_MODE
98#define FM_EXIT
99#define FM_PLAY
93#endif 100#endif
94 101
95#define RADIO_SCAN_MODE 0 102#define RADIO_SCAN_MODE 0
@@ -97,10 +104,14 @@
97 104
98static const struct fm_region_setting fm_region[] = { 105static const struct fm_region_setting fm_region[] = {
99 /* Note: Desriptive strings are just for display atm and are not compiled. */ 106 /* Note: Desriptive strings are just for display atm and are not compiled. */
100 FM_REGION_ENTRY("Europe", 87500000, 108000000, 50000, 0, 0), 107 [REGION_EUROPE] =
101 FM_REGION_ENTRY("US/Canada", 87900000, 107900000, 200000, 1, 0), 108 FM_REGION_ENTRY("Europe", 87500000, 108000000, 50000, 0, 0),
102 FM_REGION_ENTRY("Japan", 76000000, 90000000, 100000, 0, 1), 109 [REGION_US_CANADA] =
103 FM_REGION_ENTRY("Korea", 87500000, 108000000, 100000, 0, 0), 110 FM_REGION_ENTRY("US/Canada", 87900000, 107900000, 200000, 1, 0),
111 [REGION_JAPAN] =
112 FM_REGION_ENTRY("Japan", 76000000, 90000000, 100000, 0, 1),
113 [REGION_KOREA] =
114 FM_REGION_ENTRY("Korea", 87500000, 108000000, 100000, 0, 0),
104 }; 115 };
105 116
106static int curr_preset = -1; 117static int curr_preset = -1;
@@ -158,13 +169,18 @@ bool in_radio_screen(void)
158 return in_screen; 169 return in_screen;
159} 170}
160 171
172/* TODO: Move some more of the control functionality to an HAL and clean up the
173 mess */
174
161/* secret flag for starting paused - prevents unmute */ 175/* secret flag for starting paused - prevents unmute */
162#define FMRADIO_START_PAUSED 0x8000 176#define FMRADIO_START_PAUSED 0x8000
163void radio_start(void) 177void radio_start(void)
164{ 178{
165 const struct fm_region_setting *fmr; 179 const struct fm_region_setting *fmr;
166 bool start_paused; 180 bool start_paused;
181#if CONFIG_TUNER != LV24020LP
167 int mute_timeout; 182 int mute_timeout;
183#endif
168 184
169 if(radio_status == FMRADIO_PLAYING) 185 if(radio_status == FMRADIO_PLAYING)
170 return; 186 return;
@@ -182,8 +198,14 @@ void radio_start(void)
182 * fmr->freq_step + fmr->freq_min; 198 * fmr->freq_step + fmr->freq_min;
183 199
184 radio_set(RADIO_SLEEP, 0); /* wake up the tuner */ 200 radio_set(RADIO_SLEEP, 0); /* wake up the tuner */
201#if (CONFIG_TUNER & LV24020LP)
202 radio_set(RADIO_REGION, global_settings.fm_region);
203 radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
204#endif
185 radio_set(RADIO_FREQUENCY, curr_freq); 205 radio_set(RADIO_FREQUENCY, curr_freq);
186 206
207#if CONFIG_TUNER != LV24020LP
208
187 if(radio_status == FMRADIO_OFF) 209 if(radio_status == FMRADIO_OFF)
188 { 210 {
189#if (CONFIG_TUNER & S1A0903X01) 211#if (CONFIG_TUNER & S1A0903X01)
@@ -209,6 +231,7 @@ void radio_start(void)
209 break; 231 break;
210 yield(); 232 yield();
211 } 233 }
234#endif /* CONFIG_TUNER != LV24020LP */
212 235
213 /* keep radio from sounding initially */ 236 /* keep radio from sounding initially */
214 if(!start_paused) 237 if(!start_paused)
@@ -1311,6 +1334,9 @@ void toggle_mono_mode(bool mono)
1311 1334
1312void set_radio_region(int region) 1335void set_radio_region(int region)
1313{ 1336{
1337#if (CONFIG_TUNER & LV24020LP)
1338 radio_set(RADIO_REGION, global_settings.fm_region);
1339#endif
1314#if (CONFIG_TUNER & TEA5767) 1340#if (CONFIG_TUNER & TEA5767)
1315 radio_set(RADIO_SET_DEEMPHASIS, 1341 radio_set(RADIO_SET_DEEMPHASIS,
1316 fm_region[region].deemphasis); 1342 fm_region[region].deemphasis);
diff --git a/docs/CREDITS b/docs/CREDITS
index d5ad63b3ef..5ac1fabcf9 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -297,6 +297,7 @@ Akio Idehara
297Dagni McPhee 297Dagni McPhee
298Alex Gerchanovsky 298Alex Gerchanovsky
299Gerhard Dirschl 299Gerhard Dirschl
300Ivan Zupan
300The libmad team 301The libmad team
301The wavpack team 302The wavpack team
302The ffmpeg team 303The ffmpeg team
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
156drivers/fmradio_i2c.c 156drivers/fmradio_i2c.c
157tuner_philips.c 157tuner_philips.c
158#endif /* (CONFIG_TUNER & TEA5767) */ 158#endif /* (CONFIG_TUNER & TEA5767) */
159#if (CONFIG_TUNER & LV24020LP)
160tuner_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
58int (*_radio_set)(int setting, int value); 75int (*_radio_set)(int setting, int value);
59int (*_radio_get)(int setting); 76int (*_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)
64int samsung_set(int setting, int value); 81int samsung_set(int setting, int value);
65int samsung_get(int setting); 82int samsung_get(int setting);
66#endif /* CONFIG_TUNER & S1A0903X01 */ 83#endif /* CONFIG_TUNER & S1A0903X01 */
67 84
85#if (CONFIG_TUNER & LV24020LP)
86int sanyo_set(int setting, int value);
87int sanyo_get(int setting);
88#endif /* CONFIG_TUNER & LV24020LP */
89
68#if (CONFIG_TUNER & TEA5767) 90#if (CONFIG_TUNER & TEA5767)
69struct philips_dbg_info 91struct 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)
42void audio_set_source(int source, unsigned flags) 42void 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
45static 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)
235static unsigned tuner_status = 0;
236
237static unsigned char sanyo_regs[0x1c];
238
239static const int sw_osc_low = 10; /* 30; */
240static const int sw_osc_high = 240; /* 200; */
241static const int sw_cap_low = 0;
242static const int sw_cap_high = 191;
243
244/* linear coefficients used for tuning */
245static int coef_00, coef_01, coef_10, coef_11;
246
247/* DAC control register set values */
248int if_set, sd_set;
249
250static 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 */
256static 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 */
277static 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 */
285static 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 */
318static 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 */
354static void tuner_sanyo_write_or(unsigned int address, unsigned int bits)
355{
356 tuner_sanyo_write(address, sanyo_regs[address] | bits);
357}
358
359static 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 */
365static 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 */
393static 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
411static 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
420static 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
426static 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 */
434static 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 */
472static 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
586static 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
633static 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
650static 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
662static 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 **/
710bool 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
803bool radio_powered(void)
804{
805 return (tuner_status & TUNER_POWERED) != 0;
806}
807
808int 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
865int 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 */