mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 10:37:38 -04:00
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
This commit is contained in:
parent
af4cd0a84c
commit
21a4a87ca2
12 changed files with 1039 additions and 39 deletions
|
@ -1990,16 +1990,48 @@ static bool dbg_fm_radio(void)
|
|||
|
||||
lcd_setmargins(0, 0);
|
||||
|
||||
fm_detected = radio_hardware_present();
|
||||
|
||||
while(1)
|
||||
{
|
||||
int row = 0;
|
||||
|
||||
lcd_clear_display();
|
||||
fm_detected = radio_hardware_present();
|
||||
|
||||
snprintf(buf, sizeof buf, "HW detected: %s", fm_detected?"yes":"no");
|
||||
lcd_puts(0, row++, buf);
|
||||
#if (CONFIG_TUNER & LV24020LP)
|
||||
if (fm_detected)
|
||||
{
|
||||
snprintf(buf, sizeof buf, "CTRL_STAT: %02X",
|
||||
sanyo_get(RADIO_ALL) );
|
||||
lcd_puts(0, row++, buf);
|
||||
|
||||
snprintf(buf, sizeof buf, "RADIO_STAT: %02X",
|
||||
sanyo_get(RADIO_REG_STAT));
|
||||
lcd_puts(0, row++, buf);
|
||||
|
||||
snprintf(buf, sizeof buf, "MSS_FM: %d kHz",
|
||||
(sanyo_get(RADIO_MSS_FM) ) );
|
||||
lcd_puts(0, row++, buf);
|
||||
|
||||
snprintf(buf, sizeof buf, "MSS_IF: %d Hz",
|
||||
(sanyo_get(RADIO_MSS_IF) ) );
|
||||
lcd_puts(0, row++, buf);
|
||||
|
||||
snprintf(buf, sizeof buf, "MSS_SD: %d Hz",
|
||||
(sanyo_get(RADIO_MSS_SD) ) );
|
||||
lcd_puts(0, row++, buf);
|
||||
|
||||
snprintf(buf, sizeof buf, "if_set: %d Hz",
|
||||
(sanyo_get(RADIO_IF_SET) ) );
|
||||
lcd_puts(0, row++, buf);
|
||||
|
||||
snprintf(buf, sizeof buf, "sd_set: %d Hz",
|
||||
(sanyo_get(RADIO_SD_SET) ) );
|
||||
lcd_puts(0, row++, buf);
|
||||
}
|
||||
#endif
|
||||
#if (CONFIG_TUNER & S1A0903X01)
|
||||
snprintf(buf, sizeof buf, "Samsung regs: %08X",
|
||||
samsung_get(RADIO_ALL));
|
||||
|
|
|
@ -219,6 +219,17 @@ static const struct button_mapping button_context_recscreen[] = {
|
|||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_recscreen */
|
||||
|
||||
/** FM Radio Screen **/
|
||||
static const struct button_mapping button_context_radio[] = {
|
||||
{ ACTION_FM_MENU, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_FM_PRESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_FM_STOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP },
|
||||
{ ACTION_FM_MODE, BUTTON_REC, BUTTON_NONE },
|
||||
{ ACTION_FM_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_FM_PLAY, BUTTON_UP|BUTTON_REL, BUTTON_UP },
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
|
||||
}; /* button_context_radio */
|
||||
|
||||
static const struct button_mapping button_context_keyboard[] = {
|
||||
{ ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
@ -284,6 +295,8 @@ const struct button_mapping* get_context_mapping(int context)
|
|||
|
||||
case CONTEXT_YESNOSCREEN:
|
||||
return button_context_yesno;
|
||||
case CONTEXT_FM:
|
||||
return button_context_radio;
|
||||
case CONTEXT_BOOKMARKSCREEN:
|
||||
return button_context_bmark;
|
||||
case CONTEXT_QUICKSCREEN:
|
||||
|
|
|
@ -90,6 +90,13 @@
|
|||
#elif CONFIG_KEYPAD == ONDIO_PAD
|
||||
#define FM_RECORD_DBLPRE
|
||||
#define FM_RECORD
|
||||
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
|
||||
#define FM_MENU
|
||||
#define FM_PRESET
|
||||
#define FM_STOP
|
||||
#define FM_MODE
|
||||
#define FM_EXIT
|
||||
#define FM_PLAY
|
||||
#endif
|
||||
|
||||
#define RADIO_SCAN_MODE 0
|
||||
|
@ -97,9 +104,13 @@
|
|||
|
||||
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),
|
||||
};
|
||||
|
||||
|
@ -158,13 +169,18 @@ bool in_radio_screen(void)
|
|||
return in_screen;
|
||||
}
|
||||
|
||||
/* TODO: Move some more of the control functionality to an HAL and clean up the
|
||||
mess */
|
||||
|
||||
/* secret flag for starting paused - prevents unmute */
|
||||
#define FMRADIO_START_PAUSED 0x8000
|
||||
void radio_start(void)
|
||||
{
|
||||
const struct fm_region_setting *fmr;
|
||||
bool start_paused;
|
||||
#if CONFIG_TUNER != LV24020LP
|
||||
int mute_timeout;
|
||||
#endif
|
||||
|
||||
if(radio_status == FMRADIO_PLAYING)
|
||||
return;
|
||||
|
@ -182,8 +198,14 @@ void radio_start(void)
|
|||
* 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
|
||||
|
||||
if(radio_status == FMRADIO_OFF)
|
||||
{
|
||||
#if (CONFIG_TUNER & S1A0903X01)
|
||||
|
@ -209,6 +231,7 @@ void radio_start(void)
|
|||
break;
|
||||
yield();
|
||||
}
|
||||
#endif /* CONFIG_TUNER != LV24020LP */
|
||||
|
||||
/* keep radio from sounding initially */
|
||||
if(!start_paused)
|
||||
|
@ -1311,6 +1334,9 @@ void toggle_mono_mode(bool 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);
|
||||
|
|
|
@ -297,6 +297,7 @@ Akio Idehara
|
|||
Dagni McPhee
|
||||
Alex Gerchanovsky
|
||||
Gerhard Dirschl
|
||||
Ivan Zupan
|
||||
The libmad team
|
||||
The wavpack team
|
||||
The ffmpeg team
|
||||
|
|
|
@ -156,6 +156,9 @@ tuner_samsung.c
|
|||
drivers/fmradio_i2c.c
|
||||
tuner_philips.c
|
||||
#endif /* (CONFIG_TUNER & TEA5767) */
|
||||
#if (CONFIG_TUNER & LV24020LP)
|
||||
tuner_sanyo.c
|
||||
#endif /* (CONFIG_TUNER & LV24020LP) */
|
||||
#endif /*SIMULATOR */
|
||||
#endif /* CONFIG_TUNER */
|
||||
|
||||
|
|
|
@ -402,7 +402,6 @@ void audiohw_set_monitor(int enable)
|
|||
|
||||
if (enable) {
|
||||
source = SOURCE_LINE_IN1_ANALOG;
|
||||
audiohw_set_master_vol(as3514.vol_l, as3514.vol_r);
|
||||
|
||||
/* LI1R_Mute_off */
|
||||
line_in1_r |= (1 << 5);
|
||||
|
@ -415,4 +414,7 @@ void audiohw_set_monitor(int enable)
|
|||
as3514_write(AUDIOSET1, audioset1);
|
||||
as3514_write(LINE_IN1_R, line_in1_r);
|
||||
as3514_write(LINE_IN1_L, line_in1_l);
|
||||
|
||||
/* Sync mixer volume */
|
||||
audiohw_set_master_vol(as3514.vol_l, as3514.vol_r);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/* Define bitmask of input sources - recordable bitmask can be defined
|
||||
explicitly if different */
|
||||
#define INPUT_SRC_CAPS (SRC_CAP_MIC)
|
||||
#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO)
|
||||
|
||||
/* define this if you have a bitmap LCD display */
|
||||
#define HAVE_LCD_BITMAP
|
||||
|
@ -88,8 +88,8 @@
|
|||
#define AB_REPEAT_ENABLE 1
|
||||
|
||||
/* FM Tuner */
|
||||
/*#define CONFIG_TUNER TEA5767
|
||||
#define CONFIG_TUNER_XTAL 32768 *//* TODO: what is this? */
|
||||
#define CONFIG_TUNER LV24020LP
|
||||
#define HAVE_TUNER_PWR_CTRL
|
||||
|
||||
/* Define this for LCD backlight available */
|
||||
#define HAVE_BACKLIGHT
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
/* CONFIG_TUNER (note these are combineable bit-flags) */
|
||||
#define S1A0903X01 0x01 /* Samsung */
|
||||
#define TEA5767 0x02 /* Philips */
|
||||
#define LV24020LP 0x04 /* Sanyo */
|
||||
|
||||
/* CONFIG_CODEC */
|
||||
#define MAS3587F 3587
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef __TUNER_SAMSUNG_H__
|
||||
#define __TUNER_SAMSUNG_H__
|
||||
#ifndef __TUNER_H__
|
||||
#define __TUNER_H__
|
||||
|
||||
#include "hwcompat.h"
|
||||
|
||||
|
@ -35,11 +35,25 @@
|
|||
#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
|
||||
|
||||
#define REGION_EUROPE 0
|
||||
#define REGION_US_CANADA 1
|
||||
#define REGION_JAPAN 2
|
||||
#define REGION_KOREA 3
|
||||
|
||||
#if CONFIG_TUNER
|
||||
|
||||
#ifdef SIMULATOR
|
||||
|
@ -49,6 +63,9 @@ int radio_get(int setting);
|
|||
#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
|
||||
|
@ -57,14 +74,19 @@ int radio_get(int setting);
|
|||
#define radio_get _radio_get
|
||||
int (*_radio_set)(int setting, int value);
|
||||
int (*_radio_get)(int setting);
|
||||
#endif
|
||||
#endif
|
||||
#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 */
|
||||
|
||||
#if (CONFIG_TUNER & LV24020LP)
|
||||
int sanyo_set(int setting, int value);
|
||||
int sanyo_get(int setting);
|
||||
#endif /* CONFIG_TUNER & LV24020LP */
|
||||
|
||||
#if (CONFIG_TUNER & TEA5767)
|
||||
struct philips_dbg_info
|
||||
{
|
||||
|
@ -98,4 +120,4 @@ static inline void tuner_init(void)
|
|||
|
||||
#endif /* #if CONFIG_TUNER */
|
||||
|
||||
#endif
|
||||
#endif /* __TUNER_H__ */
|
||||
|
|
|
@ -377,7 +377,7 @@ void fiq_record(void)
|
|||
if (audio_channels == 2) {
|
||||
/* RX is stereo */
|
||||
while (p_size > 0) {
|
||||
if (FIFO_FREE_COUNT < 2) {
|
||||
if (FIFO_FREE_COUNT < 8) {
|
||||
/* enable interrupt */
|
||||
IISCONFIG |= (1 << 0);
|
||||
goto fiq_record_exit;
|
||||
|
@ -401,7 +401,7 @@ void fiq_record(void)
|
|||
else {
|
||||
/* RX is left channel mono */
|
||||
while (p_size > 0) {
|
||||
if (FIFO_FREE_COUNT < 2) {
|
||||
if (FIFO_FREE_COUNT < 8) {
|
||||
/* enable interrupt */
|
||||
IISCONFIG |= (1 << 0);
|
||||
goto fiq_record_exit;
|
||||
|
|
|
@ -42,11 +42,8 @@ void audio_set_output_source(int source)
|
|||
void audio_set_source(int source, unsigned flags)
|
||||
{
|
||||
static int last_source = AUDIO_SRC_PLAYBACK;
|
||||
#if 0
|
||||
static bool last_recording = false;
|
||||
bool recording = flags & SRCF_RECORDING;
|
||||
#endif
|
||||
(void)flags;
|
||||
|
||||
switch (source)
|
||||
{
|
||||
|
@ -70,13 +67,9 @@ void audio_set_source(int source, unsigned flags)
|
|||
}
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case AUDIO_SRC_FMRADIO: /* recording and playback */
|
||||
audio_channels = 2;
|
||||
|
||||
if (!recording)
|
||||
audiohw_set_recvol(23, 23, AUDIO_GAIN_LINEIN);
|
||||
|
||||
if (source == last_source && recording == last_recording)
|
||||
break;
|
||||
|
||||
|
@ -92,9 +85,7 @@ void audio_set_source(int source, unsigned flags)
|
|||
audiohw_disable_recording();
|
||||
audiohw_set_monitor(true); /* line 1 analog audio path */
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
} /* end switch */
|
||||
|
||||
last_source = source;
|
||||
|
|
909
firmware/tuner_sanyo.c
Normal file
909
firmware/tuner_sanyo.c
Normal file
|
@ -0,0 +1,909 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* 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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
outl(inl(0x70000080) | 0x4, 0x70000080);
|
||||
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 */
|
Loading…
Add table
Add a link
Reference in a new issue