From 7d759f6b9ca96a4a64c71ac301eb59cb9702e74c Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 14 Jul 2007 11:20:31 +0000 Subject: 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 --- apps/debug_menu.c | 20 +- apps/recorder/radio.c | 144 ++-- apps/recorder/radio.h | 21 +- firmware/SOURCES | 11 +- firmware/drivers/power.c | 4 +- firmware/drivers/tuner/lv24020lp.c | 870 ++++++++++++++++++++ firmware/drivers/tuner/s1a0903x01.c | 172 ++++ firmware/drivers/tuner/tea5767.c | 135 +++ firmware/export/config.h | 5 + firmware/export/hwcompat.h | 7 + firmware/export/lv24020lp.h | 51 ++ firmware/export/power.h | 4 +- firmware/export/s1a0903x01.h | 40 + firmware/export/tea5767.h | 50 ++ firmware/export/tuner.h | 173 ++-- firmware/target/arm/archos/av300/power-av300.c | 4 +- firmware/target/arm/iriver/h10/power-h10.c | 4 +- .../arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c | 4 +- .../target/arm/sandisk/sansa-e200/power-e200.c | 58 ++ firmware/target/coldfire/iaudio/x5/power-x5.c | 4 +- firmware/target/coldfire/iriver/h100/power-h100.c | 4 +- firmware/target/coldfire/iriver/h300/power-h300.c | 4 +- firmware/tuner_philips.c | 123 --- firmware/tuner_samsung.c | 153 ---- firmware/tuner_sanyo.c | 908 --------------------- uisimulator/common/fmradio.c | 6 +- 26 files changed, 1570 insertions(+), 1409 deletions(-) create mode 100644 firmware/drivers/tuner/lv24020lp.c create mode 100644 firmware/drivers/tuner/s1a0903x01.c create mode 100644 firmware/drivers/tuner/tea5767.c create mode 100644 firmware/export/lv24020lp.h create mode 100644 firmware/export/s1a0903x01.h create mode 100644 firmware/export/tea5767.h delete mode 100644 firmware/tuner_philips.c delete mode 100644 firmware/tuner_samsung.c delete mode 100644 firmware/tuner_sanyo.c diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 20dbf76..9c426c2 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -1955,27 +1955,27 @@ static int radio_callback(int btn, struct gui_synclist *lists) "HW detected: yes"); #if (CONFIG_TUNER & LV24020LP) snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "CTRL_STAT: %02X", sanyo_get(RADIO_ALL) ); + "CTRL_STAT: %02X", lv24020lp_get(LV24020LP_CTRL_STAT) ); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "RADIO_STAT: %02X", sanyo_get(RADIO_REG_STAT)); + "RADIO_STAT: %02X", lv24020lp_get(LV24020LP_REG_STAT)); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "MSS_FM: %d kHz", sanyo_get(RADIO_MSS_FM) ); + "MSS_FM: %d kHz", lv24020lp_get(LV24020LP_MSS_FM) ); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "MSS_IF: %d Hz", (sanyo_get(RADIO_MSS_IF) ) ); + "MSS_IF: %d Hz", (lv24020lp_get(LV24020LP_MSS_IF) ) ); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "MSS_SD: %d Hz", (sanyo_get(RADIO_MSS_SD) ) ); + "MSS_SD: %d Hz", (lv24020lp_get(LV24020LP_MSS_SD) ) ); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "if_set: %d Hz", (sanyo_get(RADIO_IF_SET) ) ); + "if_set: %d Hz", (lv24020lp_get(LV24020LP_IF_SET) ) ); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "sd_set: %d Hz", (sanyo_get(RADIO_SD_SET) ) ); + "sd_set: %d Hz", (lv24020lp_get(LV24020LP_SD_SET) ) ); #endif #if (CONFIG_TUNER & S1A0903X01) snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, - "Samsung regs: %08X", samsung_get(RADIO_ALL)); + "Samsung regs: %08X", s1a0903x01_get(RADIO_ALL)); #endif #if (CONFIG_TUNER & TEA5767) - struct philips_dbg_info info; - philips_dbg_info(&info); + struct tea5767_dbg_info info; + tea5767_dbg_info(&info); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, "Philips regs:"); snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, " Read: %02X %02X %02X %02X %02X", diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c index 0633289..c15f519 100644 --- a/apps/recorder/radio.c +++ b/apps/recorder/radio.c @@ -102,18 +102,6 @@ #define RADIO_SCAN_MODE 0 #define RADIO_PRESET_MODE 1 -static const struct fm_region_setting fm_region[] = { - /* Note: Desriptive strings are just for display atm and are not compiled. */ - [REGION_EUROPE] = - FM_REGION_ENTRY("Europe", 87500000, 108000000, 50000, 0, 0), - [REGION_US_CANADA] = - FM_REGION_ENTRY("US/Canada", 87900000, 107900000, 200000, 1, 0), - [REGION_JAPAN] = - FM_REGION_ENTRY("Japan", 76000000, 90000000, 100000, 0, 1), - [REGION_KOREA] = - FM_REGION_ENTRY("Korea", 87500000, 108000000, 100000, 0, 0), - }; - static int curr_preset = -1; static int curr_freq; static int radio_mode = RADIO_SCAN_MODE; @@ -176,66 +164,57 @@ bool in_radio_screen(void) #define FMRADIO_START_PAUSED 0x8000 void radio_start(void) { - const struct fm_region_setting *fmr; + const struct fm_region_data *fmr; bool start_paused; -#if CONFIG_TUNER != LV24020LP - int mute_timeout; -#endif if(radio_status == FMRADIO_PLAYING) return; - fmr = &fm_region[global_settings.fm_region]; + fmr = &fm_region_data[global_settings.fm_region]; start_paused = radio_status & FMRADIO_START_PAUSED; /* clear flag before any yielding */ radio_status &= ~FMRADIO_START_PAUSED; if(radio_status == FMRADIO_OFF) - radio_power(true); + tuner_power(true); curr_freq = global_status.last_frequency * fmr->freq_step + fmr->freq_min; - radio_set(RADIO_SLEEP, 0); /* wake up the tuner */ -#if (CONFIG_TUNER & LV24020LP) - radio_set(RADIO_REGION, global_settings.fm_region); - radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono); -#endif - radio_set(RADIO_FREQUENCY, curr_freq); - -#if CONFIG_TUNER != LV24020LP + tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */ if(radio_status == FMRADIO_OFF) { -#if (CONFIG_TUNER & S1A0903X01) - radio_set(RADIO_IF_MEASUREMENT, 0); - radio_set(RADIO_SENSITIVITY, 0); -#endif - radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono); -#if (CONFIG_TUNER & TEA5767) - radio_set(RADIO_SET_DEEMPHASIS, fmr->deemphasis); - radio_set(RADIO_SET_BAND, fmr->band); +#ifdef HAVE_RADIO_REGION + tuner_set(RADIO_REGION, global_settings.fm_region); #endif - mute_timeout = current_tick + 1*HZ; - } - else - { - /* paused */ - mute_timeout = current_tick + 2*HZ; + tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono); } - while(!radio_get(RADIO_STEREO) && !radio_get(RADIO_TUNED)) + tuner_set(RADIO_FREQUENCY, curr_freq); + +#ifdef HAVE_RADIO_MUTE_TIMEOUT { - if(TIME_AFTER(current_tick, mute_timeout)) - break; - yield(); + unsigned long mute_timeout = current_tick + HZ; + if (radio_status != FMRADIO_OFF) + { + /* paused */ + mute_timeout += HZ; + } + + while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED)) + { + if(TIME_AFTER(current_tick, mute_timeout)) + break; + yield(); + } } -#endif /* CONFIG_TUNER != LV24020LP */ +#endif /* keep radio from sounding initially */ if(!start_paused) - radio_set(RADIO_MUTE, 0); + tuner_set(RADIO_MUTE, 0); radio_status = FMRADIO_PLAYING; } /* radio_start */ @@ -251,8 +230,8 @@ void radio_pause(void) radio_start(); } - radio_set(RADIO_MUTE, 1); - radio_set(RADIO_SLEEP, 1); + tuner_set(RADIO_MUTE, 1); + tuner_set(RADIO_SLEEP, 1); radio_status = FMRADIO_PAUSED; } /* radio_pause */ @@ -262,30 +241,22 @@ void radio_stop(void) if(radio_status == FMRADIO_OFF) return; - radio_set(RADIO_MUTE, 1); - radio_set(RADIO_SLEEP, 1); /* low power mode, if available */ + tuner_set(RADIO_MUTE, 1); + tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */ radio_status = FMRADIO_OFF; - radio_power(false); /* status update, power off if avail. */ + tuner_power(false); /* status update, power off if avail. */ } /* radio_stop */ bool radio_hardware_present(void) { -#ifdef HAVE_TUNER_PWR_CTRL - bool ret; - bool fmstatus = radio_power(true); /* power it up */ - ret = radio_get(RADIO_PRESENT); - radio_power(fmstatus); /* restore previous state */ - return ret; -#else - return radio_get(RADIO_PRESENT); -#endif + return tuner_get(RADIO_PRESENT); } /* Keep freq on the grid for the current region */ static int snap_freq_to_grid(int freq) { - const struct fm_region_setting * const fmr = - &fm_region[global_settings.fm_region]; + const struct fm_region_data * const fmr = + &fm_region_data[global_settings.fm_region]; /* Range clamp if out of range or just round to nearest */ if (freq < fmr->freq_min) @@ -346,9 +317,8 @@ static int find_closest_preset(int freq, int direction) static void remember_frequency(void) { - const struct fm_region_setting * const fmr = - &fm_region[global_settings.fm_region]; - + const struct fm_region_data * const fmr = + &fm_region_data[global_settings.fm_region]; global_status.last_frequency = (curr_freq - fmr->freq_min) / fmr->freq_step; status_save(); @@ -367,15 +337,15 @@ static void next_preset(int direction) /* Must stay on the current grid for the region */ curr_freq = snap_freq_to_grid(presets[curr_preset].frequency); - radio_set(RADIO_FREQUENCY, curr_freq); + tuner_set(RADIO_FREQUENCY, curr_freq); remember_frequency(); } /* Step to the next or previous frequency */ static int step_freq(int freq, int direction) { - const struct fm_region_setting * const fmr = - &fm_region[global_settings.fm_region]; + const struct fm_region_data * const fmr = + &fm_region_data[global_settings.fm_region]; freq += direction*fmr->freq_step; @@ -402,12 +372,12 @@ static void next_station(int direction) curr_freq = step_freq(curr_freq, direction); if (radio_status == FMRADIO_PLAYING) - radio_set(RADIO_MUTE, 1); + tuner_set(RADIO_MUTE, 1); - radio_set(RADIO_FREQUENCY, curr_freq); + tuner_set(RADIO_FREQUENCY, curr_freq); if (radio_status == FMRADIO_PLAYING) - radio_set(RADIO_MUTE, 0); + tuner_set(RADIO_MUTE, 0); curr_preset = find_preset(curr_freq); remember_frequency(); @@ -417,7 +387,7 @@ static void next_station(int direction) static void end_search(void) { if (search_dir != 0 && radio_status == FMRADIO_PLAYING) - radio_set(RADIO_MUTE, 0); + tuner_set(RADIO_MUTE, 0); search_dir = 0; } @@ -540,7 +510,7 @@ int radio_screen(void) curr_freq = step_freq(curr_freq, search_dir); update_screen = true; - if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq)) + if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq)) { curr_preset = find_preset(curr_freq); remember_frequency(); @@ -671,7 +641,7 @@ int radio_screen(void) else if (dir == 0) { /* Starting auto scan */ - radio_set(RADIO_MUTE, 1); + tuner_set(RADIO_MUTE, 1); update_screen = true; } break; @@ -847,7 +817,7 @@ int radio_screen(void) /* keep "mono" from always being displayed when paused */ if (radio_status != FMRADIO_PAUSED) { - stereo = radio_get(RADIO_STEREO) && + stereo = tuner_get(RADIO_STEREO) && !global_settings.fm_force_mono; if(stereo != last_stereo) @@ -1329,21 +1299,12 @@ static int handle_radio_presets(void) void toggle_mono_mode(bool mono) { - radio_set(RADIO_FORCE_MONO, mono); + tuner_set(RADIO_FORCE_MONO, mono); } void set_radio_region(int region) { -#if (CONFIG_TUNER & LV24020LP) - radio_set(RADIO_REGION, global_settings.fm_region); -#endif -#if (CONFIG_TUNER & TEA5767) - radio_set(RADIO_SET_DEEMPHASIS, - fm_region[region].deemphasis); - radio_set(RADIO_SET_BAND, fm_region[region].band); -#else - (void)region; -#endif + tuner_set(RADIO_REGION, region); next_station(0); remember_frequency(); } @@ -1381,15 +1342,16 @@ static int scan_presets(void) if(do_scan) { - const struct fm_region_setting * const fmr = - &fm_region[global_settings.fm_region]; + const struct fm_region_data * const fmr = + &fm_region_data[global_settings.fm_region]; + char buf[MAX_FMPRESET_LEN + 1]; int i; curr_freq = fmr->freq_min; num_presets = 0; memset(presets, 0, sizeof(presets)); - radio_set(RADIO_MUTE, 1); + tuner_set(RADIO_MUTE, 1); while(curr_freq <= fmr->freq_max) { @@ -1404,7 +1366,7 @@ static int scan_presets(void) snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac); gui_syncsplash(0, buf); - if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq)) + if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq)) { /* add preset */ snprintf(buf, MAX_FMPRESET_LEN, @@ -1418,7 +1380,7 @@ static int scan_presets(void) } if (radio_status == FMRADIO_PLAYING) - radio_set(RADIO_MUTE, 0); + tuner_set(RADIO_MUTE, 0); presets_changed = true; diff --git a/apps/recorder/radio.h b/apps/recorder/radio.h index f04c14d..c456d3a 100644 --- a/apps/recorder/radio.h +++ b/apps/recorder/radio.h @@ -44,23 +44,6 @@ struct fmstation char name[MAX_FMPRESET_LEN+1]; }; -struct fm_region_setting -{ - int freq_min; - int freq_max; - int freq_step; -#if (CONFIG_TUNER & TEA5767) - char deemphasis; /* 0: 50us, 1: 75us */ - char band; /* 0: europe, 1: japan (BL in TEA spec)*/ - /* Note: "region" parameter is just for display atm and is not compiled. */ - #define FM_REGION_ENTRY(region, fmin, fmax, fstep, deemph, band) \ - { fmin, fmax, fstep, deemph, band } -#else - #define FM_REGION_ENTRY(region, fmin, fmax, fstep, deemph, band) \ - { fmin, fmax, fstep } -#endif -}; +#endif /* CONFIG_TUNER */ -#endif - -#endif +#endif /* RADIO_H */ diff --git a/firmware/SOURCES b/firmware/SOURCES index 7e26ca0..2db8e9e 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -151,17 +151,18 @@ drivers/rtc/rtc_as3514.c /* Tuner */ #if CONFIG_TUNER #ifndef SIMULATOR +tuner.c +#if (CONFIG_TUNER & LV24020LP) +drivers/tuner/lv24020lp.c +#endif /* (CONFIG_TUNER & LV24020LP) */ #if (CONFIG_TUNER & S1A0903X01) drivers/fmradio.c -tuner_samsung.c +drivers/tuner/s1a0903x01.c #endif /* (CONFIG_TUNER & S1A0903X01) */ #if (CONFIG_TUNER & TEA5767) drivers/fmradio_i2c.c -tuner_philips.c +drivers/tuner/tea5767.c #endif /* (CONFIG_TUNER & TEA5767) */ -#if (CONFIG_TUNER & LV24020LP) -tuner_sanyo.c -#endif /* (CONFIG_TUNER & LV24020LP) */ #endif /*SIMULATOR */ #endif /* CONFIG_TUNER */ diff --git a/firmware/drivers/power.c b/firmware/drivers/power.c index eb69fce..50117dd 100644 --- a/firmware/drivers/power.c +++ b/firmware/drivers/power.c @@ -37,12 +37,12 @@ bool charger_enabled; static bool powered = false; -bool radio_powered(void) +bool tuner_powered(void) { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/drivers/tuner/lv24020lp.c b/firmware/drivers/tuner/lv24020lp.c new file mode 100644 index 0000000..9ec68f9 --- /dev/null +++ b/firmware/drivers/tuner/lv24020lp.c @@ -0,0 +1,870 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner driver for the Sanyo LV24020LP + * + * Copyright (C) 2007 Ivan Zupan + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include "config.h" +#include "thread.h" +#include "kernel.h" +#include "tuner.h" /* tuner abstraction interface */ +#include "fmradio.h" /* physical interface driver */ +#include "sound.h" +#include "pp5024.h" +#include "system.h" + +#ifndef BOOTLOADER + +#if 0 +/* define to enable tuner logging */ +#define SANYO_TUNER_LOG +#endif + +#ifdef SANYO_TUNER_LOG +#include "sprintf.h" +#include "file.h" + +static int fd_log = -1; + +#define TUNER_LOG_OPEN() if (fd_log < 0) \ + fd_log = creat("/tuner_dump.txt") +/* syncing required because close() is never called */ +#define TUNER_LOG_SYNC() fsync(fd_log) +#define TUNER_LOG(s...) fdprintf(fd_log, s) +#else +#define TUNER_LOG_OPEN() +#define TUNER_LOG_SYNC() +#define TUNER_LOG(s...) +#endif /* SANYO_TUNER_LOG */ + +/** tuner register defines **/ + +/* pins on GPIOH port */ +#define FM_NRW_PIN 3 +#define FM_CLOCK_PIN 4 +#define FM_DATA_PIN 5 +#define FM_CLK_DELAY 1 + +/* block 1 registers */ + +/* R */ +#define CHIP_ID 0x00 + +/* W */ +#define BLK_SEL 0x01 + #define BLK1 0x01 + #define BLK2 0x02 + +/* W */ +#define MSRC_SEL 0x02 + #define MSR_O (1 << 7) + #define AFC_LVL (1 << 6) + #define AFC_SPD (1 << 5) + #define MSS_SD (1 << 2) + #define MSS_FM (1 << 1) + #define MSS_IF (1 << 0) + +/* W */ +#define FM_OSC 0x03 + +/* W */ +#define SD_OSC 0x04 + +/* W */ +#define IF_OSC 0x05 + +/* W */ +#define CNT_CTRL 0x06 + #define CNT1_CLR (1 << 7) + #define CTAB(x) ((x) & (0x7 << 4)) + #define CTAB_STOP_2 (0x0 << 4) + #define CTAB_STOP_8 (0x1 << 4) + #define CTAB_STOP_32 (0x2 << 4) + #define CTAB_STOP_128 (0x3 << 4) + #define CTAB_STOP_512 (0x4 << 4) + #define CTAB_STOP_2048 (0x5 << 4) + #define CTAB_STOP_8192 (0x6 << 4) + #define CTAB_STOP_32768 (0x7 << 4) + #define SWP_CNT_L (1 << 3) + #define CNT_EN (1 << 2) + #define CNT_SEL (1 << 1) + #define CNT_SET (1 << 0) + +/* W */ +#define IRQ_MSK 0x08 + #define IM_MS (1 << 6) + #define IRQ_LVL (1 << 3) + #define IM_AFC (1 << 2) + #define IM_FS (1 << 1) + #define IM_CNT2 (1 << 0) + +/* W */ +#define FM_CAP 0x09 + +/* R */ +#define CNT_L 0x0a /* Counter register low value */ + +/* R */ +#define CNT_H 0x0b /* Counter register high value */ + +/* R */ +#define CTRL_STAT 0x0c + #define AFC_FLG (1 << 0) + +/* R */ +#define RADIO_STAT 0x0d + #define RSS_MS (1 << 7) + #define RSS_FS(x) ((x) & 0x7f) + #define RSS_FS_GET(x) ((x) & 0x7f) + #define RSS_FS_SET(x) (x) +/* Note: Reading this register will clear field strength and mono/stereo interrupt. */ + +/* R */ +#define IRQ_ID 0x0e + #define II_CNT2 (1 << 5) + #define II_AFC (1 << 3) + #define II_FS_MS (1 << 0) + +/* W */ +#define IRQ_OUT 0x0f + +/* block 2 registers - offset added in order to id and avoid manual + switching */ +#define BLK2_START 0x10 + +/* W */ +#define RADIO_CTRL1 (0x02 + BLK2_START) + #define EN_MEAS (1 << 7) + #define EN_AFC (1 << 6) + #define DIR_AFC (1 << 3) + #define RST_AFC (1 << 2) + +/* W */ +#define IF_CENTER (0x03 + BLK2_START) + +/* W */ +#define IF_BW (0x05 + BLK2_START) + +/* W */ +#define RADIO_CTRL2 (0x06 + BLK2_START) + #define VREF2 (1 << 7) + #define VREF (1 << 6) + #define STABI_BP (1 << 5) + #define IF_PM_L (1 << 4) + #define AGCSP (1 << 1) + #define AM_ANT_BSW (1 << 0) /* ?? */ + +/* W */ +#define RADIO_CTRL3 (0x07 + BLK2_START) + #define AGC_SLVL (1 << 7) + #define VOLSH (1 << 6) + #define TB_ON (1 << 5) + #define AMUTE_L (1 << 4) + #define SE_FM (1 << 3) + #define SE_BE (1 << 1) + #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */ + +/* W */ +#define STEREO_CTRL (0x08 + BLK2_START) + #define FRCST (1 << 7) + #define FMCS(x) ((x) & (0x7 << 4)) + #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4) + #define FMCS_SET(x) ((x) << 4) + #define AUTOSSR (1 << 3) + #define PILTCA (1 << 2) + #define SD_PM (1 << 1) + #define ST_M (1 << 0) + +/* W */ +#define AUDIO_CTRL1 (0x09 + BLK2_START) + #define TONE_LVL(x) ((x) & (0xf << 4)) + #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4) + #define TONE_LVL_SET(x) ((x) << 4) + #define VOL_LVL(x) ((x) & 0xf) + #define VOL_LVL_GET(x) ((x) & 0xf) + #define VOL_LVL_SET(x) ((x) << 4) + +/* W */ +#define AUDIO_CTRL2 (0x0a + BLK2_START) + #define BASS_PP (1 << 0) + #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */ + #define BASS_N (1 << 2) + #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */ + #define TREB_N (1 << 4) + #define DEEMP (1 << 5) + #define BPFREQ(x) ((x) & (0x3 << 6)) + #define BPFREQ_2_0K (0x0 << 6) + #define BPFREQ_1_0K (0x1 << 6) + #define BPFREQ_0_5K (0x2 << 6) + #define BPFREQ_HIGH (0x3 << 6) + +/* W */ +#define PW_SCTRL (0x0b + BLK2_START) + #define SS_CTRL(x) ((x) & (0x7 << 5)) + #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5) + #define SS_CTRL_SET(x) ((x) << 5) + #define SM_CTRL(x) ((x) & (0x7 << 2)) + #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2) + #define SM_CTRL_SET(x) ((x) << 2) + #define PW_HPA (1 << 1) /* LV24002 only */ + #define PW_RAD (1 << 0) + +/* shadow for writeable registers */ +#define TUNER_POWERED (1 << 0) +#define TUNER_PRESENT (1 << 1) +#define TUNER_AWAKE (1 << 2) +#define TUNER_PRESENCE_CHECKED (1 << 3) +static unsigned tuner_status = 0; + +static unsigned char lv24020lp_regs[0x1c]; + +static const int sw_osc_low = 10; /* 30; */ +static const int sw_osc_high = 240; /* 200; */ +static const int sw_cap_low = 0; +static const int sw_cap_high = 191; + +/* linear coefficients used for tuning */ +static int coef_00, coef_01, coef_10, coef_11; + +/* DAC control register set values */ +int if_set, sd_set; + +static inline bool tuner_awake(void) +{ + return (tuner_status & TUNER_AWAKE) != 0; +} + +/* send a byte to the tuner - expects write mode to be current */ +static void lv24020lp_send_byte(unsigned int byte) +{ + int i; + + byte <<= FM_DATA_PIN; + + for (i = 0; i < 8; i++) + { + GPIOH_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN); + + GPIOH_OUTPUT_VAL = (GPIOH_OUTPUT_VAL & ~(1 << FM_DATA_PIN)) | + (byte & (1 << FM_DATA_PIN)); + + GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN); + udelay(FM_CLK_DELAY); + + byte >>= 1; + } +} + +/* end a write cycle on the tuner */ +static void lv24020lp_end_write(void) +{ + /* switch back to read mode */ + GPIOH_OUTPUT_EN &= ~(1 << FM_DATA_PIN); + GPIOH_OUTPUT_VAL &= ~(1 << FM_NRW_PIN); +} + +/* prepare a write cycle on the tuner */ +static unsigned int lv24020lp_begin_write(unsigned int address) +{ + /* Get register's block, translate address */ + unsigned int blk = (address >= BLK2_START) ? + (address -= BLK2_START, BLK2) : BLK1; + + for (;;) + { + /* Prepare 3-wire bus pins for write cycle */ + GPIOH_OUTPUT_VAL |= (1 << FM_NRW_PIN); + GPIOH_OUTPUT_EN |= (1 << FM_DATA_PIN); + + udelay(FM_CLK_DELAY); + + /* current block == register block? */ + if (blk == lv24020lp_regs[BLK_SEL]) + return address; + + /* switch block */ + lv24020lp_regs[BLK_SEL] = blk; + + /* data first */ + lv24020lp_send_byte(blk); + /* then address */ + lv24020lp_send_byte(BLK_SEL); + + lv24020lp_end_write(); + + udelay(FM_CLK_DELAY); + } +} + +/* write a byte to a tuner register */ +static void lv24020lp_write(unsigned int address, unsigned int data) +{ + /* shadow logical values but do logical=>physical remappings on some + registers' data. */ + lv24020lp_regs[address] = data; + + switch (address) + { + case FM_OSC: + /* L: 000..255 + * P: 255..000 */ + data = 255 - data; + break; + case FM_CAP: + /* L: 000..063, 064..191 + * P: 255..192, 127..000 */ + data = ((data < 64) ? 255 : (255 - 64)) - data; + break; + case RADIO_CTRL1: + /* L: data + * P: data | always "1" bits */ + data |= (1 << 4) | (1 << 1) | (1 << 0); + break; + } + + address = lv24020lp_begin_write(address); + + /* data first */ + lv24020lp_send_byte(data); + /* then address */ + lv24020lp_send_byte(address); + + lv24020lp_end_write(); +} + +/* helpers to set/clear register bits */ +static void lv24020lp_write_or(unsigned int address, unsigned int bits) +{ + lv24020lp_write(address, lv24020lp_regs[address] | bits); +} + +static void lv24020lp_write_and(unsigned int address, unsigned int bits) +{ + lv24020lp_write(address, lv24020lp_regs[address] & bits); +} + +/* read a byte from a tuner register */ +static unsigned int lv24020lp_read(unsigned int address) +{ + int i; + unsigned int toread; + + address = lv24020lp_begin_write(address); + + /* address */ + lv24020lp_send_byte(address); + + lv24020lp_end_write(); + + /* data */ + toread = 0; + for (i = 0; i < 8; i++) + { + GPIOH_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN); + udelay(FM_CLK_DELAY); + + toread |= (GPIOH_INPUT_VAL & (1 << FM_DATA_PIN)) << i; + + GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN); + } + + return toread >> FM_DATA_PIN; +} + +/* enables auto frequency centering */ +static void enable_afc(bool enabled) +{ + unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1]; + + if (enabled) + { + radio_ctrl1 &= ~RST_AFC; + radio_ctrl1 |= EN_AFC; + } + else + { + radio_ctrl1 |= RST_AFC; + radio_ctrl1 &= ~EN_AFC; + } + + lv24020lp_write(RADIO_CTRL1, radio_ctrl1); +} + +static int calculate_coef(unsigned fkhz) +{ + /* Overflow below 66000kHz -- + My tuner tunes down to a min of ~72600kHz but datasheet mentions + 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */ + return fkhz < 66000 ? + 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz); +} + +static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2) +{ + return y1 == y2 ? + 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1; +} + +static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2) +{ + return x1 == x2 ? + 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1; +} + +/* this performs measurements of IF, FM and Stereo frequencies + * Input can be: MSS_FM, MSS_IF, MSS_SD */ +static int tuner_measure(unsigned char type, int scale, int duration) +{ + int64_t finval; + + if (!tuner_awake()) + return 0; + + /* enable measuring */ + lv24020lp_write_or(MSRC_SEL, type); + lv24020lp_write_and(CNT_CTRL, ~CNT_SEL); + lv24020lp_write_or(RADIO_CTRL1, EN_MEAS); + + /* reset counter */ + lv24020lp_write_or(CNT_CTRL, CNT1_CLR); + lv24020lp_write_and(CNT_CTRL, ~CNT1_CLR); + + /* start counter, delay for specified time and stop it */ + lv24020lp_write_or(CNT_CTRL, CNT_EN); + udelay(duration*1000 - 16); + lv24020lp_write_and(CNT_CTRL, ~CNT_EN); + + /* read tick count */ + finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L); + + /* restore measure mode */ + lv24020lp_write_and(RADIO_CTRL1, ~EN_MEAS); + lv24020lp_write_and(MSRC_SEL, ~type); + + /* convert value */ + if (type == MSS_FM) + finval = scale*finval*256 / duration; + else + finval = scale*finval / duration; + + return (int)finval; +} + +/* set the FM oscillator frequency */ +static void set_frequency(int freq) +{ + int coef, cap_value, osc_value; + int f1, f2, x1, x2; + int count; + + if (!tuner_awake()) + return; + + TUNER_LOG_OPEN(); + + TUNER_LOG("set_frequency(%d)\n", freq); + + enable_afc(false); + + /* MHz -> kHz */ + freq /= 1000; + + TUNER_LOG("Select cap:\n"); + + coef = calculate_coef(freq); + cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high, + coef_00, coef_01); + + osc_value = sw_osc_low; + lv24020lp_write(FM_OSC, osc_value); + + /* Just in case - don't go into infinite loop */ + for (count = 0; count < 30; count++) + { + int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high, + coef_00, coef_01); + int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high, + coef_10, coef_11); + int coef_fcur, cap_new, coef_cor, range; + + lv24020lp_write(FM_CAP, cap_value); + + range = y1 - y0; + f1 = tuner_measure(MSS_FM, 1, 16); + coef_fcur = calculate_coef(f1); + coef_cor = calculate_coef((f1*1000 + 32*256) / 1000); + y0 = coef_cor; + y1 = y0 + range; + + TUNER_LOG("%d %d %d %d %d %d %d %d\n", + f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range); + + if (coef >= y0 && coef <= y1) + { + osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high, + y0, y1); + + if (osc_value >= sw_osc_low && osc_value <= sw_osc_high) + break; + } + + cap_new = interpolate_x(coef, cap_value, sw_cap_high, + coef_fcur, coef_01); + + if (cap_new == cap_value) + { + if (coef < coef_fcur) + cap_value++; + else + cap_value--; + } + else + { + cap_value = cap_new; + } + } + + TUNER_LOG("osc_value: %d\n", osc_value); + + TUNER_LOG("Tune:\n"); + + x1 = sw_osc_low, x2 = sw_osc_high; + /* FM_OSC already at SW_OSC low and f1 is already the measured + frequency */ + + do + { + int x2_new; + + lv24020lp_write(FM_OSC, x2); + f2 = tuner_measure(MSS_FM, 1, 16); + + if (abs(f2 - freq) <= 16) + { + TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2); + break; + } + + x2_new = interpolate_x(freq, x1, x2, f1, f2); + + x1 = x2, f1 = f2, x2 = x2_new; + TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2); + } + while (x2 != 0); + + if (x2 == 0) + { + /* May still be close enough */ + TUNER_LOG("tuning failed - diff: %d\n", f2 - freq); + } + + enable_afc(true); + + TUNER_LOG("\n"); + + TUNER_LOG_SYNC(); +} + +static void fine_step_tune(int (*setcmp)(int regval), int regval, int step) +{ + /* Registers are not always stable, timeout if best fit not found soon + enough */ + unsigned long abort = current_tick + HZ*2; + int flags = 0; + + while (TIME_BEFORE(current_tick, abort)) + { + int cmp; + + regval = regval + step; + + cmp = setcmp(regval); + + if (cmp == 0) + break; + + step = abs(step); + + if (cmp < 0) + { + flags |= 1; + if (step == 1) + flags |= 4; + } + else + { + step = -step; + flags |= 2; + if (step == -1) + step |= 8; + } + + if ((flags & 0xc) == 0xc) + break; + + if ((flags & 0x3) == 0x3) + { + step /= 2; + if (step == 0) + step = 1; + flags &= ~3; + } + } +} + +static int if_setcmp(int regval) +{ + lv24020lp_write(IF_OSC, regval); + lv24020lp_write(IF_CENTER, regval); + lv24020lp_write(IF_BW, 65*regval/100); + + if_set = tuner_measure(MSS_IF, 1000, 32); + + /* This register is bounces around by a few hundred Hz and doesn't seem + to be precisely tuneable. Just do 110000 +/- 500 since it's not very + critical it seems. */ + if (abs(if_set - 109500) <= 500) + return 0; + + return if_set < 109500 ? -1 : 1; +} + +static int sd_setcmp(int regval) +{ + lv24020lp_write(SD_OSC, regval); + + sd_set = tuner_measure(MSS_SD, 1000, 32); + + if (abs(sd_set - 38300) <= 31) + return 0; + + return sd_set < 38300 ? -1 : 1; +} + +static void set_sleep(bool sleep) +{ + if (sleep || tuner_awake()) + return; + + if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) != + (TUNER_PRESENT | TUNER_POWERED)) + return; + + tuner_status |= TUNER_AWAKE; + + enable_afc(false); + + /* 2. Calibrate the IF frequency at 110 kHz: */ + lv24020lp_write_and(RADIO_CTRL2, ~IF_PM_L); + fine_step_tune(if_setcmp, 0x80, 8); + lv24020lp_write_or(RADIO_CTRL2, IF_PM_L); + + /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */ + lv24020lp_write_or(STEREO_CTRL, SD_PM); + fine_step_tune(sd_setcmp, 0x80, 8); + lv24020lp_write_and(STEREO_CTRL, ~SD_PM); + + /* calculate FM tuning coefficients */ + lv24020lp_write(FM_CAP, sw_cap_low); + lv24020lp_write(FM_OSC, sw_osc_low); + coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); + + lv24020lp_write(FM_CAP, sw_cap_high); + coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); + + lv24020lp_write(FM_CAP, sw_cap_low); + lv24020lp_write(FM_OSC, sw_osc_high); + coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); + + lv24020lp_write(FM_CAP, sw_cap_high); + coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); + + /* set various audio level settings */ + lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0)); + lv24020lp_write_or(RADIO_CTRL2, AGCSP); + lv24020lp_write_or(RADIO_CTRL3, VOLSH); + lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR); + lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) | + PW_RAD); +} + +/** Public interfaces **/ +void lv24020lp_power(bool status) +{ + static const unsigned char tuner_defaults[][2] = + { + /* Block 1 writeable registers */ + { MSRC_SEL, AFC_LVL }, + { FM_OSC, 0x80 }, + { SD_OSC, 0x80 }, + { IF_OSC, 0x80 }, + { CNT_CTRL, CNT1_CLR | SWP_CNT_L }, + { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */ + { FM_CAP, 0x80 }, + /* { IRQ_OUT, 0x00 }, No action on this register (skip) */ + /* Block 2 writeable registers */ + { RADIO_CTRL1, EN_AFC }, + { IF_CENTER, 0x80 }, + { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */ + { RADIO_CTRL2, IF_PM_L }, + { RADIO_CTRL3, AGC_SLVL | SE_FM }, + { STEREO_CTRL, FMCS_SET(4) | AUTOSSR }, + { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) }, + { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */ + { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD }, + }; + + unsigned i; + + if (status) + { + tuner_status |= TUNER_POWERED | TUNER_PRESENCE_CHECKED; + + /* if tuner is present, CHIP ID is 0x09 */ + if (lv24020lp_read(CHIP_ID) == 0x09) + { + tuner_status |= TUNER_PRESENT; + + /* After power-up, the LV2400x needs to be initialized as + follows: */ + + /* 1. Write default values to the registers: */ + lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */ + for (i = 0; i < ARRAYLEN(tuner_defaults); i++) + lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]); + + /* Complete the startup calibration if the tuner is woken */ + udelay(100000); + } + } + else + { + /* Power off */ + if (tuner_status & TUNER_PRESENT) + lv24020lp_write_and(PW_SCTRL, ~PW_RAD); + + tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE); + } +} + +int lv24020lp_set(int setting, int value) +{ + int val = 1; + + switch(setting) + { + case RADIO_SLEEP: + set_sleep(value); + break; + + case RADIO_FREQUENCY: + set_frequency(value); + break; + + case RADIO_SCAN_FREQUENCY: + /* TODO: really implement this */ + set_frequency(value); + val = lv24020lp_get(RADIO_TUNED); + break; + + case RADIO_MUTE: + if (value) + lv24020lp_write_and(RADIO_CTRL3, ~AMUTE_L); + else + lv24020lp_write_or(RADIO_CTRL3, AMUTE_L); + break; + + case RADIO_REGION: + { + if (lv24020lp_region_data[value]) + lv24020lp_write_or(AUDIO_CTRL2, DEEMP); + else + lv24020lp_write_and(AUDIO_CTRL2, ~DEEMP); + break; + } + + case RADIO_FORCE_MONO: + if (value) + lv24020lp_write_or(STEREO_CTRL, ST_M); + else + lv24020lp_write_and(STEREO_CTRL, ~ST_M); + break; + + default: + val = -1; + } + + return val; +} + +int lv24020lp_get(int setting) +{ + int val = -1; + + switch(setting) + { + case RADIO_TUNED: + /* TODO: really implement this */ + val = RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f; + break; + + case RADIO_STEREO: + val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0; + break; + + case RADIO_PRESENT: + { + bool fmstatus = true; + + if (!(tuner_status & TUNER_PRESENCE_CHECKED)) + fmstatus = tuner_power(true); + + val = (tuner_status & TUNER_PRESENT) != 0; + + if (!fmstatus) + tuner_power(false); + break; + } + + /* tuner-specific debug info */ + case LV24020LP_CTRL_STAT: + return lv24020lp_read(CTRL_STAT); + + case LV24020LP_REG_STAT: + return lv24020lp_read(RADIO_STAT); + + case LV24020LP_MSS_FM: + return tuner_measure(MSS_FM, 1, 16); + + case LV24020LP_MSS_IF: + return tuner_measure(MSS_IF, 1000, 16); + + case LV24020LP_MSS_SD: + return tuner_measure(MSS_SD, 1000, 16); + + case LV24020LP_IF_SET: + return if_set; + + case LV24020LP_SD_SET: + return sd_set; + } + + return val; +} +#endif /* BOOTLOADER */ diff --git a/firmware/drivers/tuner/s1a0903x01.c b/firmware/drivers/tuner/s1a0903x01.c new file mode 100644 index 0000000..cdeba2b --- /dev/null +++ b/firmware/drivers/tuner/s1a0903x01.c @@ -0,0 +1,172 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner "middleware" for Samsung S1A0903X01 chip + * + * Copyright (C) 2003 Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include "config.h" +#include "kernel.h" +#include "tuner.h" /* tuner abstraction interface */ +#include "fmradio.h" /* physical interface driver */ +#include "mpeg.h" +#include "sound.h" + +#define DEFAULT_IN1 0x100003 /* Mute */ +#define DEFAULT_IN2 0x140884 /* 5kHz, 7.2MHz crystal */ +#define PLL_FREQ_STEP 10000 + +static int fm_in1; +static int fm_in2; +static int fm_present = -1; /* unknown */ + +/* tuner abstraction layer: set something to the tuner */ +int s1a0903x01_set(int setting, int value) +{ + int val = 1; + + switch(setting) + { + case RADIO_SLEEP: + if (!value) + { /* wakeup: just unit */ + fm_in1 = DEFAULT_IN1; + fm_in2 = DEFAULT_IN2; + fmradio_set(1, fm_in1); + fmradio_set(2, fm_in2); + } + /* else we have no sleep mode? */ + break; + + case RADIO_FREQUENCY: + { + int pll_cnt; +#if CONFIG_CODEC == MAS3587F + /* Shift the MAS internal clock away for certain frequencies to + * avoid interference. */ + int pitch = 1000; + + /* 4th harmonic falls in the FM frequency range */ + int if_freq = 4 * mpeg_get_mas_pllfreq(); + + /* shift the mas harmonic >= 300 kHz away using the direction + * which needs less shifting. */ + if (value < if_freq) + { + if (if_freq - value < 300000) + pitch = 1003 - (if_freq - value) / 100000; + } + else + { + if (value - if_freq < 300000) + pitch = 997 + (value - if_freq) / 100000; + } + sound_set_pitch(pitch); +#endif + /* We add the standard Intermediate Frequency 10.7MHz + ** before calculating the divisor + ** The reference frequency is set to 50kHz, and the VCO + ** output is prescaled by 2. + */ + + pll_cnt = (value + 10700000) / (PLL_FREQ_STEP/2) / 2; + + /* 0x100000 == FM mode + ** 0x000002 == Microprocessor controlled Mute + */ + fm_in1 = (fm_in1 & 0xfff00007) | (pll_cnt << 3); + fmradio_set(1, fm_in1); + break; + } + + case RADIO_SCAN_FREQUENCY: + /* Tune in and delay */ + s1a0903x01_set(RADIO_FREQUENCY, value); + sleep(1); + /* Start IF measurement */ + fm_in1 |= 4; + fmradio_set(1, fm_in1); + sleep(1); + val = s1a0903x01_get(RADIO_TUNED); + break; + + case RADIO_MUTE: + fm_in1 = (fm_in1 & 0xfffffffe) | (value?1:0); + fmradio_set(1, fm_in1); + break; + + case RADIO_FORCE_MONO: + fm_in2 = (fm_in2 & 0xfffffffb) | (value?0:4); + fmradio_set(2, fm_in2); + break; + /* NOTE: These were only zeroed when starting the tuner from OFF + but the default values already set them to 0. */ +#if 0 + case S1A0903X01_IF_MEASUREMENT: + fm_in1 = (fm_in1 & 0xfffffffb) | (value?4:0); + fmradio_set(1, fm_in1); + break; + + case S1A0903X01_SENSITIVITY: + fm_in2 = (fm_in2 & 0xffff9fff) | ((value & 3) << 13); + fmradio_set(2, fm_in2); + break; +#endif + default: + val = -1; + } + + return val; +} + +/* tuner abstraction layer: read something from the tuner */ +int s1a0903x01_get(int setting) +{ + int val = -1; + switch(setting) + { + case RADIO_PRESENT: + if (fm_present == -1) + { +#ifdef HAVE_TUNER_PWR_CTRL + bool fmstatus = tuner_power(true); +#endif + /* 5kHz, 7.2MHz crystal, test mode 1 */ + fmradio_set(2, 0x140885); + fm_present = (fmradio_read(0) == 0x140885); +#ifdef HAVE_TUNER_PWR_CTRL + if (!fmstatus) + tuner_power(false); +#endif + } + + val = fm_present; + break; + + case RADIO_TUNED: + val = fmradio_read(3); + val = abs(10700 - ((val & 0x7ffff) / 8)) < 50; /* convert to kHz */ + break; + + case RADIO_STEREO: + val = fmradio_read(3); + val = ((val & 0x100000) ? true : false); + break; + } + return val; +} diff --git a/firmware/drivers/tuner/tea5767.c b/firmware/drivers/tuner/tea5767.c new file mode 100644 index 0000000..da7cdfb --- /dev/null +++ b/firmware/drivers/tuner/tea5767.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner "middleware" for Philips TEA5767 chip + * + * Copyright (C) 2004 Jörg Hohensohn + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include +#include +#include +#include "kernel.h" +#include "tuner.h" /* tuner abstraction interface */ +#include "fmradio.h" +#include "fmradio_i2c.h" /* physical interface driver */ + +#define I2C_ADR 0xC0 +static unsigned char write_bytes[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static void tea5767_set_clear(int byte, unsigned char bits, int set) +{ + write_bytes[byte] &= ~bits; + if (set) + write_bytes[byte] |= bits; +} + +/* tuner abstraction layer: set something to the tuner */ +int tea5767_set(int setting, int value) +{ + switch(setting) + { + case RADIO_SLEEP: + /* init values */ + write_bytes[0] |= (1<<7); /* mute */ +#if CONFIG_TUNER_XTAL == 32768 + /* 32.768kHz, soft mute, stereo noise cancelling */ + write_bytes[3] |= (1<<4) | (1<<3) | (1<<1); +#else + /* soft mute, stereo noise cancelling */ + write_bytes[3] |= (1<<3) | (1<<1); +#endif + /* sleep / standby mode */ + tea5767_set_clear(3, (1<<6), value); + break; + + case RADIO_FREQUENCY: + { + int n; +#if CONFIG_TUNER_XTAL == 32768 + n = (4 * (value - 225000) + 16384) / 32768; +#else + n = (4 * (value - 225000)) / 50000; +#endif + write_bytes[0] = (write_bytes[0] & 0xC0) | (n >> 8); + write_bytes[1] = n; + } + break; + + case RADIO_SCAN_FREQUENCY: + tea5767_set(RADIO_FREQUENCY, value); + sleep(HZ/30); + return tea5767_get(RADIO_TUNED); + + case RADIO_MUTE: + tea5767_set_clear(0, 0x80, value); + break; + + case RADIO_REGION: + { + const struct tea5767_region_data *rd = + &tea5767_region_data[value]; + + tea5767_set_clear(4, (1<<6), rd->deemphasis); + tea5767_set_clear(3, (1<<5), rd->band); + break; + } + case RADIO_FORCE_MONO: + tea5767_set_clear(2, 0x08, value); + break; + default: + return -1; + } + + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + return 1; +} + +/* tuner abstraction layer: read something from the tuner */ +int tea5767_get(int setting) +{ + unsigned char read_bytes[5]; + int val = -1; /* default for unsupported query */ + + fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); + + switch(setting) + { + case RADIO_PRESENT: + val = 1; /* true */ + break; + + case RADIO_TUNED: + val = 0; + if (read_bytes[0] & 0x80) /* ready */ + { + val = read_bytes[2] & 0x7F; /* IF counter */ + val = (abs(val - 0x36) < 2); /* close match */ + } + break; + + case RADIO_STEREO: + val = read_bytes[2] >> 7; + break; + } + + return val; +} + +void tea5767_dbg_info(struct tea5767_dbg_info *info) +{ + fmradio_i2c_read(I2C_ADR, info->read_regs, 5); + memcpy(info->write_regs, write_bytes, 5); +} diff --git a/firmware/export/config.h b/firmware/export/config.h index 86e27d0..056b985 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -245,6 +245,11 @@ #define CONFIG_REMOTE_DEFAULT_ICON_WIDTH 6 #endif +#if (CONFIG_TUNER & (CONFIG_TUNER - 1)) != 0 +/* Multiple possible tuners */ +#define CONFIG_TUNER_MULTI +#endif + /* Enable the directory cache and tagcache in RAM if we have * plenty of RAM. Both features can be enabled independently. */ #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8) && \ diff --git a/firmware/export/hwcompat.h b/firmware/export/hwcompat.h index bffb76e..bebca68 100644 --- a/firmware/export/hwcompat.h +++ b/firmware/export/hwcompat.h @@ -46,4 +46,11 @@ bool is_new_player(void); #endif +#ifdef CONFIG_TUNER_MULTI +static inline int tuner_detect_type(void) +{ + return (HW_MASK & TUNER_MODEL) ? TEA5767 : S1A0903X01; +} +#endif + #endif /* HWCOMPAT_H */ diff --git a/firmware/export/lv24020lp.h b/firmware/export/lv24020lp.h new file mode 100644 index 0000000..0fc39b1 --- /dev/null +++ b/firmware/export/lv24020lp.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner header for the Sanyo LV24020LP + * + * Copyright (C) 2007 Michael Sevakis + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _LV24020LP_H_ +#define _LV24020LP_H_ + +/* Define additional tuner messages here */ +#define HAVE_RADIO_REGION + +#define LV24020LP_CTRL_STAT (RADIO_GET_CHIP_FIRST+0) +#define LV24020LP_REG_STAT (RADIO_GET_CHIP_FIRST+1) +#define LV24020LP_MSS_FM (RADIO_GET_CHIP_FIRST+2) +#define LV24020LP_MSS_IF (RADIO_GET_CHIP_FIRST+3) +#define LV24020LP_MSS_SD (RADIO_GET_CHIP_FIRST+4) +#define LV24020LP_IF_SET (RADIO_GET_CHIP_FIRST+5) +#define LV24020LP_SD_SET (RADIO_GET_CHIP_FIRST+6) + +struct lv24020lp_region_data +{ + unsigned char deemphasis; +} __attribute__((packed)); + +const unsigned char lv24020lp_region_data[TUNER_NUM_REGIONS]; + +int lv24020lp_set(int setting, int value); +int lv24020lp_get(int setting); +void lv24020lp_power(bool status); + +#ifndef CONFIG_TUNER_MULTI +#define tuner_set lv24020lp_set +#define tuner_get lv24020lp_get +#endif + +#endif /* _LV24020LP_H_ */ diff --git a/firmware/export/power.h b/firmware/export/power.h index cafd6f8..8ecff6c 100644 --- a/firmware/export/power.h +++ b/firmware/export/power.h @@ -48,8 +48,8 @@ bool spdif_powered(void); #endif #if CONFIG_TUNER -extern bool radio_power(bool status); -extern bool radio_powered(void); +extern bool tuner_power(bool status); +extern bool tuner_powered(void); #endif #endif diff --git a/firmware/export/s1a0903x01.h b/firmware/export/s1a0903x01.h new file mode 100644 index 0000000..bf49762 --- /dev/null +++ b/firmware/export/s1a0903x01.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner header for the Samsung S1A0903X01 + * + * Copyright (C) 2007 Michael Sevakis + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _S1A0903X01_H_ +#define _S1A0903X01_H_ + +/* Define additional tuner messages here */ +#define HAVE_RADIO_MUTE_TIMEOUT + +#if 0 +#define S1A0903X01_IF_MEASUREMENT (RADIO_SET_CHIP_FIRST+0) +#define S1A0903X01_SENSITIVITY (RADIO_SET_CHIP_FIRST+1) +#endif + +int s1a0903x01_set(int setting, int value); +int s1a0903x01_get(int setting); + +#ifndef CONFIG_TUNER_MULTI +#define tuner_set s1a0903x01_get +#define tuner_get s1a0903x01_set +#endif + +#endif /* _S1A0903X01_H_ */ diff --git a/firmware/export/tea5767.h b/firmware/export/tea5767.h new file mode 100644 index 0000000..dfa6149 --- /dev/null +++ b/firmware/export/tea5767.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner header for the Philips TEA5767 + * + * Copyright (C) 2007 Michael Sevakis + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _TEA5767_H_ +#define _TEA5767_H_ + +#define HAVE_RADIO_REGION +#define HAVE_RADIO_MUTE_TIMEOUT + +struct tea5767_region_data +{ + unsigned char deemphasis; /* 0: 50us, 1: 75us */ + unsigned char band; /* 0: europe, 1: japan (BL in TEA spec)*/ +} __attribute__((packed)); + +const struct tea5767_region_data tea5767_region_data[TUNER_NUM_REGIONS]; + +struct tea5767_dbg_info +{ + unsigned char read_regs[5]; + unsigned char write_regs[5]; +}; + +int tea5767_set(int setting, int value); +int tea5767_get(int setting); +void tea5767_dbg_info(struct tea5767_dbg_info *info); + +#ifndef CONFIG_TUNER_MULTI +#define tuner_set tea5767_set +#define tuner_get tea5767_get +#endif + +#endif /* _TEA5767_H_ */ diff --git a/firmware/export/tuner.h b/firmware/export/tuner.h index 9f6d29f..cf18102 100644 --- a/firmware/export/tuner.h +++ b/firmware/export/tuner.h @@ -22,101 +22,112 @@ #include "hwcompat.h" -/* settings to the tuner layer */ -#define RADIO_ALL -1 /* debug */ -#define RADIO_SLEEP 0 -#define RADIO_FREQUENCY 1 -#define RADIO_MUTE 2 -#define RADIO_IF_MEASUREMENT 3 -#define RADIO_SENSITIVITY 4 -#define RADIO_FORCE_MONO 5 -#define RADIO_SCAN_FREQUENCY 6 -#if (CONFIG_TUNER & TEA5767) -#define RADIO_SET_DEEMPHASIS 7 -#define RADIO_SET_BAND 8 -#endif -#if (CONFIG_TUNER & LV24020LP) -#define RADIO_REGION 9 /* to be used for all tuners */ -#define RADIO_REG_STAT 100 -#define RADIO_MSS_FM 101 -#define RADIO_MSS_IF 102 -#define RADIO_MSS_SD 103 -#define RADIO_IF_SET 104 -#define RADIO_SD_SET 105 -#endif -/* readback from the tuner layer */ -#define RADIO_PRESENT 0 -#define RADIO_TUNED 1 -#define RADIO_STEREO 2 +/** Settings to the tuner layer **/ +enum +{ + RADIO_ALL = -1, /* debug */ + RADIO_SLEEP, + RADIO_FREQUENCY, + RADIO_MUTE, + RADIO_FORCE_MONO, + RADIO_SCAN_FREQUENCY, + + /* Put new general-purpose settings above this line */ + __RADIO_SET_STANDARD_LAST +}; + +/** Readback from the tuner layer **/ +enum +{ + RADIO_PRESENT = 0, + RADIO_TUNED, + RADIO_STEREO, + + /* Put new general-purpose readback values above this line */ + __RADIO_GET_STANDARD_LAST +}; + +/** Tuner regions **/ + +/* Basic region information */ +enum +{ + REGION_EUROPE = 0, + REGION_US_CANADA, + REGION_JAPAN, + REGION_KOREA, + + /* Add new regions above this line */ + TUNER_NUM_REGIONS +}; -#define REGION_EUROPE 0 -#define REGION_US_CANADA 1 -#define REGION_JAPAN 2 -#define REGION_KOREA 3 +struct fm_region_data +{ + int freq_min; + int freq_max; + int freq_step; +}; + +extern const struct fm_region_data fm_region_data[TUNER_NUM_REGIONS]; #if CONFIG_TUNER #ifdef SIMULATOR -int radio_set(int setting, int value); -int radio_get(int setting); +int tuner_set(int setting, int value); +int tuner_get(int setting); #else -#if CONFIG_TUNER == S1A0903X01 /* FM recorder */ -#define radio_set samsung_set -#define radio_get samsung_get -#elif CONFIG_TUNER == LV24020LP /* Sansa */ -#define radio_set sanyo_set -#define radio_get sanyo_get -#elif CONFIG_TUNER == TEA5767 /* iRiver, iAudio */ -#define radio_set philips_set -#define radio_get philips_get -#elif CONFIG_TUNER == (S1A0903X01 | TEA5767) /* OndioFM */ -#define radio_set _radio_set -#define radio_get _radio_get -int (*_radio_set)(int setting, int value); -int (*_radio_get)(int setting); -#endif /* CONFIG_TUNER == */ -#endif /* SIMULATOR */ -#if (CONFIG_TUNER & S1A0903X01) -int samsung_set(int setting, int value); -int samsung_get(int setting); -#endif /* CONFIG_TUNER & S1A0903X01 */ +#ifdef CONFIG_TUNER_MULTI +extern int (*tuner_set)(int setting, int value); +extern int (*tuner_get)(int setting); +#endif /* CONFIG_TUNER_MULTI */ +/** Sanyo LV24020LP **/ #if (CONFIG_TUNER & LV24020LP) -int sanyo_set(int setting, int value); -int sanyo_get(int setting); -#endif /* CONFIG_TUNER & LV24020LP */ +/* Sansa e200 Series */ +#include "lv24020lp.h" +#endif + +/** Samsung S1A0903X01 **/ +#if (CONFIG_TUNER & S1A0903X01) +/* Ondio FM, FM Recorder */ +#include "s1a0903x01.h" +#endif +/** Philips TEA5767 **/ #if (CONFIG_TUNER & TEA5767) -struct philips_dbg_info +/* Ondio FM, FM Recorder, Recorder V2, iRiver h100/h300, iAudio x5 */ +#include "tea5767.h" +#endif + +#endif /* SIMULATOR */ + +/* Additional messages that get enumerated after tuner driver headers */ + +/* for tuner_set */ +enum { - unsigned char read_regs[5]; - unsigned char write_regs[5]; + __RADIO_SET_ADDITIONAL_START = __RADIO_SET_STANDARD_LAST-1, +#ifdef HAVE_RADIO_REGION + RADIO_REGION, +#endif + + RADIO_SET_CHIP_FIRST }; -int philips_set(int setting, int value); -int philips_get(int setting); -void philips_dbg_info(struct philips_dbg_info *info); -#endif /* CONFIG_TUNER & TEA5767 */ - -/* Just inline here since only radio screen needs this atm and - there's no tuner.c. */ -static inline void tuner_init(void) + +/* for tuner_get */ +enum { -#ifndef SIMULATOR -#if CONFIG_TUNER == (S1A0903X01 | TEA5767) - if (HW_MASK & TUNER_MODEL) - { - _radio_set = philips_set; - _radio_get = philips_get; - } - else - { - _radio_set = samsung_set; - _radio_get = samsung_get; - } -#endif -#endif -} + __RADIO_GET_ADDITIONAL_START = __RADIO_SET_STANDARD_LAST-1, + + RADIO_GET_CHIP_FIRST +}; + +/** **/ + +void tuner_init(void); +bool tuner_power(bool power); +bool tuner_powered(void); #endif /* #if CONFIG_TUNER */ diff --git a/firmware/target/arm/archos/av300/power-av300.c b/firmware/target/arm/archos/av300/power-av300.c index cfdce69..6cc8b4f 100644 --- a/firmware/target/arm/archos/av300/power-av300.c +++ b/firmware/target/arm/archos/av300/power-av300.c @@ -84,12 +84,12 @@ void ide_power_enable(bool on) static bool powered = false; -bool radio_powered() +bool tuner_powered() { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/target/arm/iriver/h10/power-h10.c b/firmware/target/arm/iriver/h10/power-h10.c index 63eb2dc..d0d3ce5 100644 --- a/firmware/target/arm/iriver/h10/power-h10.c +++ b/firmware/target/arm/iriver/h10/power-h10.c @@ -40,12 +40,12 @@ bool charger_enabled; static bool powered = false; -bool radio_powered() +bool tuner_powered() { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c b/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c index 4e4f585..dfdcb1d 100644 --- a/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c +++ b/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c @@ -30,12 +30,12 @@ static bool powered = false; -bool radio_powered(void) +bool tuner_powered(void) { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/target/arm/sandisk/sansa-e200/power-e200.c b/firmware/target/arm/sandisk/sansa-e200/power-e200.c index dfa4211..002dcb8 100644 --- a/firmware/target/arm/sandisk/sansa-e200/power-e200.c +++ b/firmware/target/arm/sandisk/sansa-e200/power-e200.c @@ -21,6 +21,7 @@ #include "system.h" #include "cpu.h" #include "i2c-pp.h" +#include "tuner.h" void power_init(void) { @@ -61,3 +62,60 @@ void ide_power_enable(bool on) { (void)on; } + +/** Tuner **/ +static bool powered = false; + +bool tuner_power(bool status) +{ + bool old_status = powered; + + if (status != old_status) + { + if (status) + { + /* init mystery amplification device */ + outl(inl(0x70000084) | 0x1, 0x70000084); + udelay(5); + + /* When power up, host should initialize the 3-wire bus + in host read mode: */ + + /* 1. Set direction of the DATA-line to input-mode. */ + GPIOH_OUTPUT_EN &= ~(1 << 5); + GPIOH_ENABLE |= (1 << 5); + + /* 2. Drive NR_W low */ + GPIOH_OUTPUT_VAL &= ~(1 << 3); + GPIOH_OUTPUT_EN |= (1 << 3); + GPIOH_ENABLE |= (1 << 3); + + /* 3. Drive CLOCK high */ + GPIOH_OUTPUT_VAL |= (1 << 4); + GPIOH_OUTPUT_EN |= (1 << 4); + GPIOH_ENABLE |= (1 << 4); + + lv24020lp_power(true); + } + else + { + lv24020lp_power(false); + + /* set all as inputs */ + GPIOH_OUTPUT_EN &= ~((1 << 5) | (1 << 3) | (1 << 4)); + GPIOH_ENABLE &= ~((1 << 5) | (1 << 3) | (1 << 4)); + + /* turn off mystery amplification device */ + outl(inl(0x70000084) & ~0x1, 0x70000084); + } + + powered = status; + } + + return old_status; +} + +bool tuner_powered(void) +{ + return powered; +} diff --git a/firmware/target/coldfire/iaudio/x5/power-x5.c b/firmware/target/coldfire/iaudio/x5/power-x5.c index c646570..5c6c388 100644 --- a/firmware/target/coldfire/iaudio/x5/power-x5.c +++ b/firmware/target/coldfire/iaudio/x5/power-x5.c @@ -90,12 +90,12 @@ void ide_power_enable(bool on) static bool powered = false; -bool radio_powered() +bool tuner_powered() { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/target/coldfire/iriver/h100/power-h100.c b/firmware/target/coldfire/iriver/h100/power-h100.c index 9431689..c184126 100644 --- a/firmware/target/coldfire/iriver/h100/power-h100.c +++ b/firmware/target/coldfire/iriver/h100/power-h100.c @@ -29,12 +29,12 @@ static bool powered = false; -bool radio_powered(void) +bool tuner_powered(void) { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/target/coldfire/iriver/h300/power-h300.c b/firmware/target/coldfire/iriver/h300/power-h300.c index 5e57326..b687b29 100644 --- a/firmware/target/coldfire/iriver/h300/power-h300.c +++ b/firmware/target/coldfire/iriver/h300/power-h300.c @@ -29,12 +29,12 @@ static bool powered = false; -bool radio_powered(void) +bool tuner_powered(void) { return powered; } -bool radio_power(bool status) +bool tuner_power(bool status) { bool old_status = powered; powered = status; diff --git a/firmware/tuner_philips.c b/firmware/tuner_philips.c deleted file mode 100644 index 8520fdb..0000000 --- a/firmware/tuner_philips.c +++ /dev/null @@ -1,123 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * Tuner "middleware" for Philips TEA5767 chip - * - * Copyright (C) 2004 Jörg Hohensohn - * - * All files in this archive are subject to the GNU General Public License. - * See the file COPYING in the source tree root for full license agreement. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "config.h" -#include -#include -#include -#include "kernel.h" -#include "tuner.h" /* tuner abstraction interface */ -#include "fmradio_i2c.h" /* physical interface driver */ - -#define I2C_ADR 0xC0 -static unsigned char write_bytes[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; - -/* tuner abstraction layer: set something to the tuner */ -int philips_set(int setting, int value) -{ - switch(setting) - { - case RADIO_SLEEP: - /* init values */ - write_bytes[0] |= (1<<7); /* mute */ -#if CONFIG_TUNER_XTAL == 32768 - /* 32.768kHz, soft mute, stereo noise cancelling */ - write_bytes[3] |= (1<<4) | (1<<3) | (1<<1); -#else - /* soft mute, stereo noise cancelling */ - write_bytes[3] |= (1<<3) | (1<<1); -#endif - /* sleep / standby mode */ - write_bytes[3] &= ~(1<<6) | (value ? (1<<6) : 0); - break; - - case RADIO_FREQUENCY: - { - int n; -#if CONFIG_TUNER_XTAL == 32768 - n = (4 * (value - 225000) + 16384) / 32768; -#else - n = (4 * (value - 225000)) / 50000; -#endif - write_bytes[0] = (write_bytes[0] & 0xC0) | (n >> 8); - write_bytes[1] = n & 0xFF; - } - break; - - case RADIO_SCAN_FREQUENCY: - philips_set(RADIO_FREQUENCY, value); - sleep(HZ/30); - return philips_get(RADIO_TUNED); - - case RADIO_MUTE: - write_bytes[0] = (write_bytes[0] & 0x7F) | (value ? 0x80 : 0); - break; - - case RADIO_FORCE_MONO: - write_bytes[2] = (write_bytes[2] & 0xF7) | (value ? 0x08 : 0); - break; - - case RADIO_SET_DEEMPHASIS: - write_bytes[4] = (write_bytes[4] & ~(1<<6)) | (value ? (1<<6) : 0); - break; - - case RADIO_SET_BAND: - write_bytes[3] = (write_bytes[3] & ~(1<<5)) | (value ? (1<<5) : 0); - default: - return -1; - } - fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); - return 1; -} - -/* tuner abstraction layer: read something from the tuner */ -int philips_get(int setting) -{ - unsigned char read_bytes[5]; - int val = -1; /* default for unsupported query */ - - fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); - - switch(setting) - { - case RADIO_PRESENT: - val = 1; /* true */ - break; - - case RADIO_TUNED: - val = 0; - if (read_bytes[0] & 0x80) /* ready */ - { - val = read_bytes[2] & 0x7F; /* IF counter */ - val = (abs(val - 0x36) < 2); /* close match */ - } - break; - - case RADIO_STEREO: - val = read_bytes[2] >> 7; - break; - } - return val; -} - -void philips_dbg_info(struct philips_dbg_info *info) -{ - fmradio_i2c_read(I2C_ADR, info->read_regs, 5); - memcpy(info->write_regs, write_bytes, 5); -} diff --git a/firmware/tuner_samsung.c b/firmware/tuner_samsung.c deleted file mode 100644 index 82934d7..0000000 --- a/firmware/tuner_samsung.c +++ /dev/null @@ -1,153 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * Tuner "middleware" for Samsung S1A0903X01 chip - * - * Copyright (C) 2003 Linus Nielsen Feltzing - * - * All files in this archive are subject to the GNU General Public License. - * See the file COPYING in the source tree root for full license agreement. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include -#include -#include "config.h" -#include "kernel.h" -#include "tuner.h" /* tuner abstraction interface */ -#include "fmradio.h" /* physical interface driver */ -#include "mpeg.h" -#include "sound.h" - -#define DEFAULT_IN1 0x100003 /* Mute */ -#define DEFAULT_IN2 0x140884 /* 5kHz, 7.2MHz crystal */ -#define PLL_FREQ_STEP 10000 - -static int fm_in1; -static int fm_in2; - -/* tuner abstraction layer: set something to the tuner */ -int samsung_set(int setting, int value) -{ - int val = 1; - - switch(setting) - { - case RADIO_SLEEP: - if (!value) - { /* wakeup: just unit */ - fm_in1 = DEFAULT_IN1; - fm_in2 = DEFAULT_IN2; - fmradio_set(1, fm_in1); - fmradio_set(2, fm_in2); - } - /* else we have no sleep mode? */ - break; - - case RADIO_FREQUENCY: - { - int pll_cnt; -#if CONFIG_CODEC == MAS3587F - /* Shift the MAS internal clock away for certain frequencies to - * avoid interference. */ - int pitch = 1000; - - /* 4th harmonic falls in the FM frequency range */ - int if_freq = 4 * mpeg_get_mas_pllfreq(); - - /* shift the mas harmonic >= 300 kHz away using the direction - * which needs less shifting. */ - if (value < if_freq) - { - if (if_freq - value < 300000) - pitch = 1003 - (if_freq - value) / 100000; - } - else - { - if (value - if_freq < 300000) - pitch = 997 + (value - if_freq) / 100000; - } - sound_set_pitch(pitch); -#endif - /* We add the standard Intermediate Frequency 10.7MHz - ** before calculating the divisor - ** The reference frequency is set to 50kHz, and the VCO - ** output is prescaled by 2. - */ - - pll_cnt = (value + 10700000) / (PLL_FREQ_STEP/2) / 2; - - /* 0x100000 == FM mode - ** 0x000002 == Microprocessor controlled Mute - */ - fm_in1 = (fm_in1 & 0xfff00007) | (pll_cnt << 3); - fmradio_set(1, fm_in1); - break; - } - - case RADIO_SCAN_FREQUENCY: - /* Tune in and delay */ - samsung_set(RADIO_FREQUENCY, value); - sleep(1); - /* Start IF measurement */ - samsung_set(RADIO_IF_MEASUREMENT, 1); - sleep(1); - val = samsung_get(RADIO_TUNED); - break; - - case RADIO_MUTE: - fm_in1 = (fm_in1 & 0xfffffffe) | (value?1:0); - fmradio_set(1, fm_in1); - break; - - case RADIO_IF_MEASUREMENT: - fm_in1 = (fm_in1 & 0xfffffffb) | (value?4:0); - fmradio_set(1, fm_in1); - break; - - case RADIO_SENSITIVITY: - fm_in2 = (fm_in2 & 0xffff9fff) | ((value & 3) << 13); - fmradio_set(2, fm_in2); - break; - - case RADIO_FORCE_MONO: - fm_in2 = (fm_in2 & 0xfffffffb) | (value?0:4); - fmradio_set(2, fm_in2); - break; - default: - val = -1; - } - - return val; -} - -/* tuner abstraction layer: read something from the tuner */ -int samsung_get(int setting) -{ - int val = -1; - switch(setting) - { - case RADIO_PRESENT: - fmradio_set(2, 0x140885); /* 5kHz, 7.2MHz crystal, test mode 1 */ - val = (fmradio_read(0) == 0x140885); - break; - - case RADIO_TUNED: - val = fmradio_read(3); - val = abs(10700 - ((val & 0x7ffff) / 8)) < 50; /* convert to kHz */ - break; - - case RADIO_STEREO: - val = fmradio_read(3); - val = ((val & 0x100000) ? true : false); - } - return val; -} diff --git a/firmware/tuner_sanyo.c b/firmware/tuner_sanyo.c deleted file mode 100644 index 6e73297..0000000 --- a/firmware/tuner_sanyo.c +++ /dev/null @@ -1,908 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * Tuner driver for the Sanyo LV24020LP - * - * Copyright (C) 2007 Ivan Zupan - * - * All files in this archive are subject to the GNU General Public License. - * See the file COPYING in the source tree root for full license agreement. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include -#include -#include "config.h" -#include "thread.h" -#include "kernel.h" -#include "tuner.h" /* tuner abstraction interface */ -#include "fmradio.h" /* physical interface driver */ -#include "mpeg.h" -#include "sound.h" -#include "pp5024.h" -#include "system.h" -#include "as3514.h" - -#ifndef BOOTLOADER - -#if 0 -/* define to enable tuner logging */ -#define SANYO_TUNER_LOG -#endif - -#ifdef SANYO_TUNER_LOG -#include "sprintf.h" -#include "file.h" - -static int fd_log = -1; - -#define TUNER_LOG_OPEN() if (fd_log < 0) \ - fd_log = creat("/tuner_dump.txt") -/* syncing required because close() is never called */ -#define TUNER_LOG_SYNC() fsync(fd_log) -#define TUNER_LOG(s...) fdprintf(fd_log, s) -#else -#define TUNER_LOG_OPEN() -#define TUNER_LOG_SYNC() -#define TUNER_LOG(s...) -#endif /* SANYO_TUNER_LOG */ - -/** tuner register defines **/ - -/* pins on GPIOH port */ -#define FM_NRW_PIN 3 -#define FM_CLOCK_PIN 4 -#define FM_DATA_PIN 5 -#define FM_CLK_DELAY 1 - -/* block 1 registers */ - -/* R */ -#define CHIP_ID 0x00 - -/* W */ -#define BLK_SEL 0x01 - #define BLK1 0x01 - #define BLK2 0x02 - -/* W */ -#define MSRC_SEL 0x02 - #define MSR_O (1 << 7) - #define AFC_LVL (1 << 6) - #define AFC_SPD (1 << 5) - #define MSS_SD (1 << 2) - #define MSS_FM (1 << 1) - #define MSS_IF (1 << 0) - -/* W */ -#define FM_OSC 0x03 - -/* W */ -#define SD_OSC 0x04 - -/* W */ -#define IF_OSC 0x05 - -/* W */ -#define CNT_CTRL 0x06 - #define CNT1_CLR (1 << 7) - #define CTAB(x) ((x) & (0x7 << 4)) - #define CTAB_STOP_2 (0x0 << 4) - #define CTAB_STOP_8 (0x1 << 4) - #define CTAB_STOP_32 (0x2 << 4) - #define CTAB_STOP_128 (0x3 << 4) - #define CTAB_STOP_512 (0x4 << 4) - #define CTAB_STOP_2048 (0x5 << 4) - #define CTAB_STOP_8192 (0x6 << 4) - #define CTAB_STOP_32768 (0x7 << 4) - #define SWP_CNT_L (1 << 3) - #define CNT_EN (1 << 2) - #define CNT_SEL (1 << 1) - #define CNT_SET (1 << 0) - -/* W */ -#define IRQ_MSK 0x08 - #define IM_MS (1 << 6) - #define IRQ_LVL (1 << 3) - #define IM_AFC (1 << 2) - #define IM_FS (1 << 1) - #define IM_CNT2 (1 << 0) - -/* W */ -#define FM_CAP 0x09 - -/* R */ -#define CNT_L 0x0a /* Counter register low value */ - -/* R */ -#define CNT_H 0x0b /* Counter register high value */ - -/* R */ -#define CTRL_STAT 0x0c - #define AFC_FLG (1 << 0) - -/* R */ -#define RADIO_STAT 0x0d - #define RSS_MS (1 << 7) - #define RSS_FS(x) ((x) & 0x7f) - #define RSS_FS_GET(x) ((x) & 0x7f) - #define RSS_FS_SET(x) (x) -/* Note: Reading this register will clear field strength and mono/stereo interrupt. */ - -/* R */ -#define IRQ_ID 0x0e - #define II_CNT2 (1 << 5) - #define II_AFC (1 << 3) - #define II_FS_MS (1 << 0) - -/* W */ -#define IRQ_OUT 0x0f - -/* block 2 registers - offset added in order to id and avoid manual - switching */ -#define BLK2_START 0x10 - -/* W */ -#define RADIO_CTRL1 (0x02 + BLK2_START) - #define EN_MEAS (1 << 7) - #define EN_AFC (1 << 6) - #define DIR_AFC (1 << 3) - #define RST_AFC (1 << 2) - -/* W */ -#define IF_CENTER (0x03 + BLK2_START) - -/* W */ -#define IF_BW (0x05 + BLK2_START) - -/* W */ -#define RADIO_CTRL2 (0x06 + BLK2_START) - #define VREF2 (1 << 7) - #define VREF (1 << 6) - #define STABI_BP (1 << 5) - #define IF_PM_L (1 << 4) - #define AGCSP (1 << 1) - #define AM_ANT_BSW (1 << 0) /* ?? */ - -/* W */ -#define RADIO_CTRL3 (0x07 + BLK2_START) - #define AGC_SLVL (1 << 7) - #define VOLSH (1 << 6) - #define TB_ON (1 << 5) - #define AMUTE_L (1 << 4) - #define SE_FM (1 << 3) - #define SE_BE (1 << 1) - #define SE_EXT (1 << 0) /* For LV24000=0, LV24001/24002=Ext source enab. */ - -/* W */ -#define STEREO_CTRL (0x08 + BLK2_START) - #define FRCST (1 << 7) - #define FMCS(x) ((x) & (0x7 << 4)) - #define FMCS_GET(x) (((x) & (0x7 << 4)) >> 4) - #define FMCS_SET(x) ((x) << 4) - #define AUTOSSR (1 << 3) - #define PILTCA (1 << 2) - #define SD_PM (1 << 1) - #define ST_M (1 << 0) - -/* W */ -#define AUDIO_CTRL1 (0x09 + BLK2_START) - #define TONE_LVL(x) ((x) & (0xf << 4)) - #define TONE_LVL_GET(x) (((x) & (0xf << 4)) >> 4) - #define TONE_LVL_SET(x) ((x) << 4) - #define VOL_LVL(x) ((x) & 0xf) - #define VOL_LVL_GET(x) ((x) & 0xf) - #define VOL_LVL_SET(x) ((x) << 4) - -/* W */ -#define AUDIO_CTRL2 (0x0a + BLK2_START) - #define BASS_PP (1 << 0) - #define BASS_P (1 << 1) /* BASS_P, BASS_N are mutually-exclusive */ - #define BASS_N (1 << 2) - #define TREB_P (1 << 3) /* TREB_P, TREB_N are mutually-exclusive */ - #define TREB_N (1 << 4) - #define DEEMP (1 << 5) - #define BPFREQ(x) ((x) & (0x3 << 6)) - #define BPFREQ_2_0K (0x0 << 6) - #define BPFREQ_1_0K (0x1 << 6) - #define BPFREQ_0_5K (0x2 << 6) - #define BPFREQ_HIGH (0x3 << 6) - -/* W */ -#define PW_SCTRL (0x0b + BLK2_START) - #define SS_CTRL(x) ((x) & (0x7 << 5)) - #define SS_CTRL_GET(x) (((x) & (0x7 << 5)) >> 5) - #define SS_CTRL_SET(x) ((x) << 5) - #define SM_CTRL(x) ((x) & (0x7 << 2)) - #define SM_CTRL_GET(x) (((x) & (0x7 << 2)) >> 2) - #define SM_CTRL_SET(x) ((x) << 2) - #define PW_HPA (1 << 1) /* LV24002 only */ - #define PW_RAD (1 << 0) - -/* shadow for writeable registers */ -#define TUNER_POWERED (1 << 0) -#define TUNER_PRESENT (1 << 1) -#define TUNER_AWAKE (1 << 2) -#define TUNER_PRESENCE_CHECKED (1 << 3) -static unsigned tuner_status = 0; - -static unsigned char sanyo_regs[0x1c]; - -static const int sw_osc_low = 10; /* 30; */ -static const int sw_osc_high = 240; /* 200; */ -static const int sw_cap_low = 0; -static const int sw_cap_high = 191; - -/* linear coefficients used for tuning */ -static int coef_00, coef_01, coef_10, coef_11; - -/* DAC control register set values */ -int if_set, sd_set; - -static inline bool tuner_awake(void) -{ - return (tuner_status & TUNER_AWAKE) != 0; -} - -/* send a byte to the tuner - expects write mode to be current */ -static void tuner_sanyo_send_byte(unsigned int byte) -{ - int i; - - byte <<= FM_DATA_PIN; - - for (i = 0; i < 8; i++) - { - GPIOH_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN); - - GPIOH_OUTPUT_VAL = (GPIOH_OUTPUT_VAL & ~(1 << FM_DATA_PIN)) | - (byte & (1 << FM_DATA_PIN)); - - GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN); - udelay(FM_CLK_DELAY); - - byte >>= 1; - } -} - -/* end a write cycle on the tuner */ -static void tuner_sanyo_end_write(void) -{ - /* switch back to read mode */ - GPIOH_OUTPUT_EN &= ~(1 << FM_DATA_PIN); - GPIOH_OUTPUT_VAL &= ~(1 << FM_NRW_PIN); -} - -/* prepare a write cycle on the tuner */ -static unsigned int tuner_sanyo_begin_write(unsigned int address) -{ - /* Get register's block, translate address */ - unsigned int blk = (address >= BLK2_START) ? - (address -= BLK2_START, BLK2) : BLK1; - - for (;;) - { - /* Prepare 3-wire bus pins for write cycle */ - GPIOH_OUTPUT_VAL |= (1 << FM_NRW_PIN); - GPIOH_OUTPUT_EN |= (1 << FM_DATA_PIN); - - udelay(FM_CLK_DELAY); - - /* current block == register block? */ - if (blk == sanyo_regs[BLK_SEL]) - return address; - - /* switch block */ - sanyo_regs[BLK_SEL] = blk; - - /* data first */ - tuner_sanyo_send_byte(blk); - /* then address */ - tuner_sanyo_send_byte(BLK_SEL); - - tuner_sanyo_end_write(); - - udelay(FM_CLK_DELAY); - } -} - -/* write a byte to a tuner register */ -static void tuner_sanyo_write(unsigned int address, unsigned int data) -{ - /* shadow logical values but do logical=>physical remappings on some - registers' data. */ - sanyo_regs[address] = data; - - switch (address) - { - case FM_OSC: - /* L: 000..255 - * P: 255..000 */ - data = 255 - data; - break; - case FM_CAP: - /* L: 000..063, 064..191 - * P: 255..192, 127..000 */ - data = ((data < 64) ? 255 : (255 - 64)) - data; - break; - case RADIO_CTRL1: - /* L: data - * P: data | always "1" bits */ - data |= (1 << 4) | (1 << 1) | (1 << 0); - break; - } - - address = tuner_sanyo_begin_write(address); - - /* data first */ - tuner_sanyo_send_byte(data); - /* then address */ - tuner_sanyo_send_byte(address); - - tuner_sanyo_end_write(); -} - -/* helpers to set/clear register bits */ -static void tuner_sanyo_write_or(unsigned int address, unsigned int bits) -{ - tuner_sanyo_write(address, sanyo_regs[address] | bits); -} - -static void tuner_sanyo_write_and(unsigned int address, unsigned int bits) -{ - tuner_sanyo_write(address, sanyo_regs[address] & bits); -} - -/* read a byte from a tuner register */ -static unsigned int tuner_sanyo_read(unsigned int address) -{ - int i; - unsigned int toread; - - address = tuner_sanyo_begin_write(address); - - /* address */ - tuner_sanyo_send_byte(address); - - tuner_sanyo_end_write(); - - /* data */ - toread = 0; - for (i = 0; i < 8; i++) - { - GPIOH_OUTPUT_VAL &= ~(1 << FM_CLOCK_PIN); - udelay(FM_CLK_DELAY); - - toread |= (GPIOH_INPUT_VAL & (1 << FM_DATA_PIN)) << i; - - GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN); - } - - return toread >> FM_DATA_PIN; -} - -/* enables auto frequency centering */ -static void enable_afc(bool enabled) -{ - unsigned int radio_ctrl1 = sanyo_regs[RADIO_CTRL1]; - - if (enabled) - { - radio_ctrl1 &= ~RST_AFC; - radio_ctrl1 |= EN_AFC; - } - else - { - radio_ctrl1 |= RST_AFC; - radio_ctrl1 &= ~EN_AFC; - } - - tuner_sanyo_write(RADIO_CTRL1, radio_ctrl1); -} - -static int calculate_coef(unsigned fkhz) -{ - /* Overflow below 66000kHz -- - My tuner tunes down to a min of ~72600kHz but datasheet mentions - 66000kHz as the minimum. ?? Perhaps 76000kHz was intended? */ - return fkhz < 66000 ? - 0x7fffffff : 0x81d1a47efc5cb700ull / ((uint64_t)fkhz*fkhz); -} - -static int interpolate_x(int expected_y, int x1, int x2, int y1, int y2) -{ - return y1 == y2 ? - 0 : (int64_t)(expected_y - y1)*(x2 - x1) / (y2 - y1) + x1; -} - -static int interpolate_y(int expected_x, int x1, int x2, int y1, int y2) -{ - return x1 == x2 ? - 0 : (int64_t)(expected_x - x1)*(y2 - y1) / (x2 - x1) + y1; -} - -/* this performs measurements of IF, FM and Stereo frequencies - * Input can be: MSS_FM, MSS_IF, MSS_SD */ -static int tuner_measure(unsigned char type, int scale, int duration) -{ - int64_t finval; - - if (!tuner_awake()) - return 0; - - /* enable measuring */ - tuner_sanyo_write_or(MSRC_SEL, type); - tuner_sanyo_write_and(CNT_CTRL, ~CNT_SEL); - tuner_sanyo_write_or(RADIO_CTRL1, EN_MEAS); - - /* reset counter */ - tuner_sanyo_write_or(CNT_CTRL, CNT1_CLR); - tuner_sanyo_write_and(CNT_CTRL, ~CNT1_CLR); - - /* start counter, delay for specified time and stop it */ - tuner_sanyo_write_or(CNT_CTRL, CNT_EN); - udelay(duration*1000 - 16); - tuner_sanyo_write_and(CNT_CTRL, ~CNT_EN); - - /* read tick count */ - finval = (tuner_sanyo_read(CNT_H) << 8) | tuner_sanyo_read(CNT_L); - - /* restore measure mode */ - tuner_sanyo_write_and(RADIO_CTRL1, ~EN_MEAS); - tuner_sanyo_write_and(MSRC_SEL, ~type); - - /* convert value */ - if (type == MSS_FM) - finval = scale*finval*256 / duration; - else - finval = scale*finval / duration; - - return (int)finval; -} - -/* set the FM oscillator frequency */ -static void sanyo_set_frequency(int freq) -{ - int coef, cap_value, osc_value; - int f1, f2, x1, x2; - int count; - - if (!tuner_awake()) - return; - - TUNER_LOG_OPEN(); - - TUNER_LOG("set_frequency(%d)\n", freq); - - enable_afc(false); - - /* MHz -> kHz */ - freq /= 1000; - - TUNER_LOG("Select cap:\n"); - - coef = calculate_coef(freq); - cap_value = interpolate_x(coef, sw_cap_low, sw_cap_high, - coef_00, coef_01); - - osc_value = sw_osc_low; - tuner_sanyo_write(FM_OSC, osc_value); - - /* Just in case - don't go into infinite loop */ - for (count = 0; count < 30; count++) - { - int y0 = interpolate_y(cap_value, sw_cap_low, sw_cap_high, - coef_00, coef_01); - int y1 = interpolate_y(cap_value, sw_cap_low, sw_cap_high, - coef_10, coef_11); - int coef_fcur, cap_new, coef_cor, range; - - tuner_sanyo_write(FM_CAP, cap_value); - - range = y1 - y0; - f1 = tuner_measure(MSS_FM, 1, 16); - coef_fcur = calculate_coef(f1); - coef_cor = calculate_coef((f1*1000 + 32*256) / 1000); - y0 = coef_cor; - y1 = y0 + range; - - TUNER_LOG("%d %d %d %d %d %d %d %d\n", - f1, cap_value, coef, coef_fcur, coef_cor, y0, y1, range); - - if (coef >= y0 && coef <= y1) - { - osc_value = interpolate_x(coef, sw_osc_low, sw_osc_high, - y0, y1); - - if (osc_value >= sw_osc_low && osc_value <= sw_osc_high) - break; - } - - cap_new = interpolate_x(coef, cap_value, sw_cap_high, - coef_fcur, coef_01); - - if (cap_new == cap_value) - { - if (coef < coef_fcur) - cap_value++; - else - cap_value--; - } - else - { - cap_value = cap_new; - } - } - - TUNER_LOG("osc_value: %d\n", osc_value); - - TUNER_LOG("Tune:\n"); - - x1 = sw_osc_low, x2 = sw_osc_high; - /* FM_OSC already at SW_OSC low and f1 is already the measured - frequency */ - - do - { - int x2_new; - - tuner_sanyo_write(FM_OSC, x2); - f2 = tuner_measure(MSS_FM, 1, 16); - - if (abs(f2 - freq) <= 16) - { - TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2); - break; - } - - x2_new = interpolate_x(freq, x1, x2, f1, f2); - - x1 = x2, f1 = f2, x2 = x2_new; - TUNER_LOG("%d %d %d %d\n", f1, f2, x1, x2); - } - while (x2 != 0); - - if (x2 == 0) - { - /* May still be close enough */ - TUNER_LOG("tuning failed - diff: %d\n", f2 - freq); - } - - enable_afc(true); - - TUNER_LOG("\n"); - - TUNER_LOG_SYNC(); -} - -static void fine_step_tune(int (*setcmp)(int regval), int regval, int step) -{ - /* Registers are not always stable, timeout if best fit not found soon - enough */ - unsigned long abort = current_tick + HZ*2; - int flags = 0; - - while (TIME_BEFORE(current_tick, abort)) - { - int cmp; - - regval = regval + step; - - cmp = setcmp(regval); - - if (cmp == 0) - break; - - step = abs(step); - - if (cmp < 0) - { - flags |= 1; - if (step == 1) - flags |= 4; - } - else - { - step = -step; - flags |= 2; - if (step == -1) - step |= 8; - } - - if ((flags & 0xc) == 0xc) - break; - - if ((flags & 0x3) == 0x3) - { - step /= 2; - if (step == 0) - step = 1; - flags &= ~3; - } - } -} - -static int if_setcmp(int regval) -{ - tuner_sanyo_write(IF_OSC, regval); - tuner_sanyo_write(IF_CENTER, regval); - tuner_sanyo_write(IF_BW, 65*regval/100); - - if_set = tuner_measure(MSS_IF, 1000, 32); - - /* This register is bounces around by a few hundred Hz and doesn't seem - to be precisely tuneable. Just do 110000 +/- 500 since it's not very - critical it seems. */ - if (abs(if_set - 109500) <= 500) - return 0; - - return if_set < 109500 ? -1 : 1; -} - -static int sd_setcmp(int regval) -{ - tuner_sanyo_write(SD_OSC, regval); - - sd_set = tuner_measure(MSS_SD, 1000, 32); - - if (abs(sd_set - 38300) <= 31) - return 0; - - return sd_set < 38300 ? -1 : 1; -} - -static void sanyo_sleep(bool sleep) -{ - if (sleep || tuner_awake()) - return; - - if ((tuner_status & (TUNER_PRESENT | TUNER_POWERED)) != - (TUNER_PRESENT | TUNER_POWERED)) - return; - - tuner_status |= TUNER_AWAKE; - - enable_afc(false); - - /* 2. Calibrate the IF frequency at 110 kHz: */ - tuner_sanyo_write_and(RADIO_CTRL2, ~IF_PM_L); - fine_step_tune(if_setcmp, 0x80, 8); - tuner_sanyo_write_or(RADIO_CTRL2, IF_PM_L); - - /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */ - tuner_sanyo_write_or(STEREO_CTRL, SD_PM); - fine_step_tune(sd_setcmp, 0x80, 8); - tuner_sanyo_write_and(STEREO_CTRL, ~SD_PM); - - /* calculate FM tuning coefficients */ - tuner_sanyo_write(FM_CAP, sw_cap_low); - tuner_sanyo_write(FM_OSC, sw_osc_low); - coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); - - tuner_sanyo_write(FM_CAP, sw_cap_high); - coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); - - tuner_sanyo_write(FM_CAP, sw_cap_low); - tuner_sanyo_write(FM_OSC, sw_osc_high); - coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); - - tuner_sanyo_write(FM_CAP, sw_cap_high); - coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64)); - - /* set various audio level settings */ - tuner_sanyo_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0)); - tuner_sanyo_write_or(RADIO_CTRL2, AGCSP); - tuner_sanyo_write_or(RADIO_CTRL3, VOLSH); - tuner_sanyo_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR); - tuner_sanyo_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) | - PW_RAD); -} - -/** Public interfaces **/ -bool radio_power(bool status) -{ - static const unsigned char tuner_defaults[][2] = - { - /* Block 1 writeable registers */ - { MSRC_SEL, AFC_LVL }, - { FM_OSC, 0x80 }, - { SD_OSC, 0x80 }, - { IF_OSC, 0x80 }, - { CNT_CTRL, CNT1_CLR | SWP_CNT_L }, - { IRQ_MSK, 0x00 }, /* IRQ_LVL -> Low to High */ - { FM_CAP, 0x80 }, - /* { IRQ_OUT, 0x00 }, No action on this register (skip) */ - /* Block 2 writeable registers */ - { RADIO_CTRL1, EN_AFC }, - { IF_CENTER, 0x80 }, - { IF_BW, 65*0x80 / 100 }, /* 65% of IF_OSC */ - { RADIO_CTRL2, IF_PM_L }, - { RADIO_CTRL3, AGC_SLVL | SE_FM }, - { STEREO_CTRL, FMCS_SET(4) | AUTOSSR }, - { AUDIO_CTRL1, TONE_LVL_SET(7) | VOL_LVL_SET(7) }, - { AUDIO_CTRL2, BPFREQ_HIGH }, /* deemphasis 50us */ - { PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(3) | PW_RAD }, - }; - - unsigned i; - bool powered = tuner_status & TUNER_POWERED; - - if (status == powered) - return powered; - - if (status) - { - /* init mystery amplification device */ - outl(inl(0x70000084) | 0x1, 0x70000084); - udelay(5); - - /* When power up, host should initialize the 3-wire bus in host read - mode: */ - - /* 1. Set direction of the DATA-line to input-mode. */ - GPIOH_OUTPUT_EN &= ~(1 << FM_DATA_PIN); - GPIOH_ENABLE |= (1 << FM_DATA_PIN); - - /* 2. Drive NR_W low */ - GPIOH_OUTPUT_VAL &= ~(1 << FM_NRW_PIN); - GPIOH_OUTPUT_EN |= (1 << FM_NRW_PIN); - GPIOH_ENABLE |= (1 << FM_NRW_PIN); - - /* 3. Drive CLOCK high */ - GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN); - GPIOH_OUTPUT_EN |= (1 << FM_CLOCK_PIN); - GPIOH_ENABLE |= (1 << FM_CLOCK_PIN); - - tuner_status |= TUNER_POWERED; - - /* if tuner is present, CHIP ID is 0x09 */ - if (tuner_sanyo_read(CHIP_ID) == 0x09) - { - tuner_status |= TUNER_PRESENT; - - /* After power-up, the LV2400x needs to be initialized as - follows: */ - - /* 1. Write default values to the registers: */ - sanyo_regs[BLK_SEL] = 0; /* Force a switch on the first */ - for (i = 0; i < ARRAYLEN(tuner_defaults); i++) - tuner_sanyo_write(tuner_defaults[i][0], tuner_defaults[i][1]); - - /* Complete the startup calibration if the tuner is woken */ - udelay(100000); - } - } - else - { - /* Power off and set all as inputs */ - if (tuner_status & TUNER_PRESENT) - tuner_sanyo_write_and(PW_SCTRL, ~PW_RAD); - - GPIOH_OUTPUT_EN &= ~((1 << FM_DATA_PIN) | (1 << FM_NRW_PIN) | - (1 << FM_CLOCK_PIN)); - GPIOH_ENABLE &= ~((1 << FM_DATA_PIN) | (1 << FM_NRW_PIN) | - (1 << FM_CLOCK_PIN)); - - outl(inl(0x70000084) & ~0x1, 0x70000084); - - tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE); - } - - return powered; -} - -bool radio_powered(void) -{ - return (tuner_status & TUNER_POWERED) != 0; -} - -int sanyo_set(int setting, int value) -{ - int val = 1; - - switch(setting) - { - case RADIO_SLEEP: - sanyo_sleep(value); - break; - - case RADIO_FREQUENCY: - sanyo_set_frequency(value); - break; - - case RADIO_SCAN_FREQUENCY: - /* TODO: really implement this */ - sanyo_set_frequency(value); - val = sanyo_get(RADIO_TUNED); - break; - - case RADIO_MUTE: - if (value) - tuner_sanyo_write_and(RADIO_CTRL3, ~AMUTE_L); - else - tuner_sanyo_write_or(RADIO_CTRL3, AMUTE_L); - break; - - case RADIO_REGION: - switch (value) - { - case REGION_EUROPE: - case REGION_JAPAN: - case REGION_KOREA: - tuner_sanyo_write_and(AUDIO_CTRL2, ~DEEMP); - break; - case REGION_US_CANADA: - tuner_sanyo_write_or(AUDIO_CTRL2, DEEMP); - break; - default: - val = -1; - } - break; - - case RADIO_FORCE_MONO: - if (value) - tuner_sanyo_write_or(STEREO_CTRL, ST_M); - else - tuner_sanyo_write_and(STEREO_CTRL, ~ST_M); - break; - - default: - val = -1; - } - - return val; -} - -int sanyo_get(int setting) -{ - int val = -1; - - switch(setting) - { - case RADIO_ALL: - return tuner_sanyo_read(CTRL_STAT); - - case RADIO_TUNED: - /* TODO: really implement this */ - val = RSS_FS(tuner_sanyo_read(RADIO_STAT)) < 0x1f; - break; - - case RADIO_STEREO: - val = (tuner_sanyo_read(RADIO_STAT) & RSS_MS) != 0; - break; - - case RADIO_PRESENT: - val = (tuner_status & TUNER_PRESENT) != 0; - break; - - /* tuner-specific debug info */ - case RADIO_REG_STAT: - return tuner_sanyo_read(RADIO_STAT); - - case RADIO_MSS_FM: - return tuner_measure(MSS_FM, 1, 16); - - case RADIO_MSS_IF: - return tuner_measure(MSS_IF, 1000, 16); - - case RADIO_MSS_SD: - return tuner_measure(MSS_SD, 1000, 16); - - case RADIO_IF_SET: - return if_set; - - case RADIO_SD_SET: - return sd_set; - } - - return val; -} -#endif /* BOOTLOADER */ diff --git a/uisimulator/common/fmradio.c b/uisimulator/common/fmradio.c index 851c11b..372fd10 100644 --- a/uisimulator/common/fmradio.c +++ b/uisimulator/common/fmradio.c @@ -30,7 +30,7 @@ static bool mono = false; static bool powered = false; #endif -int radio_set(int setting, int value) +int tuner_set(int setting, int value) { switch(setting) { @@ -59,7 +59,7 @@ int radio_set(int setting, int value) return 1; } -int radio_get(int setting) +int tuner_get(int setting) { int val = 0; @@ -86,7 +86,7 @@ int radio_get(int setting) } #ifdef HAVE_TUNER_PWR_CTRL -bool radio_power(bool status) +bool tuner_power(bool status) { bool oldstatus = powered; powered = status; -- cgit v1.1