diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/samsung | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/samsung')
-rw-r--r-- | drivers/samsung/Kconfig | 31 | ||||
-rw-r--r-- | drivers/samsung/Makefile | 7 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Makefile | 43 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4705_dev.c | 2064 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4705_main.c | 864 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_common.h | 49 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_dev.c | 2509 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_dev.h | 231 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_i2c_drv.c | 192 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_i2c_drv.h | 8 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_ioctl.h | 121 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_main.c | 844 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_main.h | 8 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/Si4709_regs.h | 799 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/commanddefs.h | 192 | ||||
-rw-r--r-- | drivers/samsung/fm_si4709/propertydefs.h | 578 |
16 files changed, 8540 insertions, 0 deletions
diff --git a/drivers/samsung/Kconfig b/drivers/samsung/Kconfig new file mode 100644 index 0000000..ab0026b --- /dev/null +++ b/drivers/samsung/Kconfig @@ -0,0 +1,31 @@ +# +# +# + +menuconfig SAMSUNG_MODULES + tristate "Samsung Kernel Modules" + default n + help + Say Y to enable Samsung kernel modules. j4fs and param + modules are included. + +menuconfig FM_RADIO + tristate "FM Radio Driver" + depends on SAMSUNG_MODULES + default n + help + Say Y to enable FM Radio Driver + +menuconfig FM_SI4709 + tristate "Si4709 FM radio" + depends on FM_RADIO + default n + help + Say Y to enable Si4709 FM radio support. + + menuconfig FM_SI4705 + tristate "Si4705 FM radio" + depends on FM_RADIO + default n + help + Say Y to enable Si4705 FM radio support. diff --git a/drivers/samsung/Makefile b/drivers/samsung/Makefile new file mode 100644 index 0000000..811c209 --- /dev/null +++ b/drivers/samsung/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the kernel Sensor device drivers. +# + +# Object files in subdirectories + +obj-$(CONFIG_FM_RADIO) += fm_si4709/ diff --git a/drivers/samsung/fm_si4709/Makefile b/drivers/samsung/fm_si4709/Makefile new file mode 100644 index 0000000..80be6a7 --- /dev/null +++ b/drivers/samsung/fm_si4709/Makefile @@ -0,0 +1,43 @@ +############################################################################## +# COPYRIGHT(C) : Samsung Electronics Co.Ltd, 2006-2011 ALL RIGHTS RESERVED +# AUTHOR : Varun Mahajan <m.varun@samsung.com> +############################################################################## +# VERSION&DATE : Version 0.1 +############################################################################## + +ifneq ($(KERNELRELEASE), ) + +MOD_NAME = Si4709_driver +EXTRA_CFLAGS := -I$(PRJROOT)/modules/include + +ifneq ($(CONFIG_FM_SI4705),) +obj-$(CONFIG_FM_SI4705) := $(MOD_NAME).o +$(MOD_NAME)-y := Si4705_dev.o \ + Si4709_i2c_drv.o \ + Si4705_main.o +else +MOD_NAME = Si4709_driver +obj-$(CONFIG_FM_SI4709) := $(MOD_NAME).o +$(MOD_NAME)-y := Si4709_dev.o \ + Si4709_i2c_drv.o \ + Si4709_main.o +endif + +else + +ifndef KDIR + KDIR :=$(PRJROOT)/linux-2.6.29 +endif + +all: + @$(MAKE) --no-print-directory -C $(KDIR) \ + SUBDIRS=$(CURDIR) modules + +clean: + rm -f *.o *.ko *.mod.c *~ .*.cmd + +install: + @$(MAKE) --no-print-directory -C $(KDIR) \ + SUBDIRS=$(CURDIR) modules_install + +endif diff --git a/drivers/samsung/fm_si4709/Si4705_dev.c b/drivers/samsung/fm_si4709/Si4705_dev.c new file mode 100644 index 0000000..0898882 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4705_dev.c @@ -0,0 +1,2064 @@ +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> + +#include <mach/gpio.h> +#include <plat/gpio-cfg.h> + +#include "Si4709_regs.h" +#include "Si4709_main.h" +#include "Si4709_dev.h" +#include "Si4709_common.h" + +#if 1 /* To do */ +#include "commanddefs.h" +#include "propertydefs.h" +#endif + + +enum { + eTRUE, + eFALSE, +} dev_struct_status_t; + +/*dev_state*/ +/*power_state*/ +#define RADIO_ON 1 +#define RADIO_POWERDOWN 0 +/*seek_state*/ +#define RADIO_SEEK_ON 1 +#define RADIO_SEEK_OFF 0 + +#define FREQ_87500_kHz 8750 +#define FREQ_76000_kHz 7600 + +#define RSSI_seek_th_MAX 0x7F +#define RSSI_seek_th_MIN 0x00 + +#define seek_SNR_th_DISB 0x00 +#define seek_SNR_th_MIN 0x01 /*most stops */ +#define seek_SNR_th_MAX 0x0F /*fewest stops */ + +#define seek_FM_ID_th_DISB 0x00 +#define seek_FM_ID_th_MAX 0x01 /*most stops */ +#define seek_FM_ID_th_MIN 0x0F /*fewest stops */ + +#define TUNE_RSSI_THRESHOLD 10 +#define TUNE_SNR_THRESHOLD 4 +#define TUNE_CNT_THRESHOLD 0x00 + +#define _ENABLE_RDS_ + +#ifdef RDS_INTERRUPT_ON_ALWAYS +#define RDS_BUFFER_LENGTH 50 +static u16 *RDS_Block_Data_buffer; +static u8 *RDS_Block_Error_buffer; +static u8 RDS_Buffer_Index_read; /* index number for last read data */ +static u8 RDS_Buffer_Index_write; /* index number for last written data */ + +int Si4709_RDS_flag; +int RDS_Data_Available; +int RDS_Data_Lost; +int RDS_Groups_Available_till_now; +struct workqueue_struct *Si4709_wq; +struct work_struct Si4709_work; +#endif + +u8 cmd[8]; +u8 rsp[13]; +u8 RsqInts; +u8 Status; +u8 SMUTE; +u8 AFCRL; +u8 Valid; +u8 Pilot; +u8 Blend; +u16 Freq; +u8 RSSI; +u8 ASNR; +u8 FreqOff; +u8 STC; +u8 BLTF; +u16 AntCap; +u8 RdsInts; +u8 RdsSync; +u8 GrpLost; +u8 RdsFifoUsed; +u16 BlockA; +u16 BlockB; +u16 BlockC; +u16 BlockD; +u8 BleA; +u8 BleB; +u8 BleC; +u8 BleD; + + + +/*static functions*/ +/**********************************************/ +static void wait(void); + +#ifndef RDS_INTERRUPT_ON_ALWAYS +static void wait_RDS(void); +#endif + +static int powerup(void); +static int powerdown(void); + +static int seek(u32 *, int, int); +static int tune_freq(u32); +static u16 freq_to_channel(u32); +static void fmTuneStatus(u8 cancel, u8 intack); +static void fmRsqStatus(u8 intack); +static void si47xx_set_property(u16 propNumber, u16 propValue); +static int si47xx_command(u8 cmd_size, u8 *cmd, u8 reply_size, u8 *reply); +static void fmRdsStatus(u8 intack, u8 mtfifo); +static void fmTuneFreq(u16 frequency); +static void fmSeekStart(u8 seekUp, u8 wrap); +static u8 si47xx_readStatus(void); +static void si47xx_waitForCTS(void); +static u8 getIntStatus(void); +static int i2c_write(u8 number_bytes, u8 *data_out); +static int i2c_read(u8 number_bytes, u8 *data_in); +/**********************************************/ + +/*Si4709 device structure*/ +static struct Si4709_device_t Si4709_dev = { + .client = NULL, + .valid = eFALSE, + .valid_client_state = eFALSE, +}; + +/*Wait flag*/ +/*WAITING or WAIT_OVER or NO_WAIT*/ +int Si4709_dev_wait_flag = NO_WAIT; +#ifdef RDS_INTERRUPT_ON_ALWAYS +int Si4709_RDS_flag = NO_WAIT; +#endif + +unsigned int Si4709_dev_int; +unsigned int Si4709_dev_irq; + +#if defined(CONFIG_MACH_M0) +unsigned int Si4709_dev_sw; +#endif + +#if defined(CONFIG_MACH_M0_CTC) +static const u16 rx_vol[] = { +0x0, 0x15, 0x18, 0x1B, 0x1E, 0x21, 0x24, 0x27, +0x2A, 0x2D, 0x30, 0x33, 0x36, 0x39, 0x3C, 0x3F}; +#else +static const u16 rx_vol[] = { +0x0, 0x13, 0x16, 0x19, 0x1C, 0x1F, 0x22, 0x25, +0x28, 0x2B, 0x2E, 0x31, 0x34, 0x37, 0x3A, 0x3D}; +#endif + + +int Si4709_dev_init(struct i2c_client *client) +{ + int ret = 0; + + debug("Si4709_dev_init called"); + + mutex_lock(&(Si4709_dev.lock)); + + Si4709_dev.client = client; + +#if defined(CONFIG_MACH_M0) + if (system_rev >= 15) + Si4709_dev_int = GPIO_FM_INT_REV15; + else +#endif + Si4709_dev_int = GPIO_FM_INT; + + Si4709_dev_irq = gpio_to_irq(Si4709_dev_int); + +#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_M0_CTC) + Si4709_dev_sw = GPIO_FM_MIC_SW; +#endif + + disable_irq(Si4709_dev_irq); + + Si4709_dev.state.power_state = RADIO_POWERDOWN; + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + Si4709_dev.valid_client_state = eTRUE; + +#ifdef RDS_INTERRUPT_ON_ALWAYS + /*Creating Circular Buffer */ + /*Single RDS_Block_Data buffer size is 4x16 bits */ + RDS_Block_Data_buffer = kzalloc(RDS_BUFFER_LENGTH * 8, GFP_KERNEL); + if (!RDS_Block_Data_buffer) { + error("Not sufficient memory for creating " + "RDS_Block_Data_buffer"); + ret = -ENOMEM; + goto EXIT; + } + + /*Single RDS_Block_Error buffer size is 4x8 bits */ + RDS_Block_Error_buffer = kzalloc(RDS_BUFFER_LENGTH * 4, GFP_KERNEL); + if (!RDS_Block_Error_buffer) { + error("Not sufficient memory for creating " + "RDS_Block_Error_buffer"); + ret = -ENOMEM; + kfree(RDS_Block_Data_buffer); + goto EXIT; + } + + /*Initialising read and write indices */ + RDS_Buffer_Index_read = 0; + RDS_Buffer_Index_write = 0; + + /*Creating work-queue */ + Si4709_wq = create_singlethread_workqueue("Si4709_wq"); + if (!Si4709_wq) { + error("Not sufficient memory for Si4709_wq, work-queue"); + ret = -ENOMEM; + kfree(RDS_Block_Error_buffer); + kfree(RDS_Block_Data_buffer); + goto EXIT; + } + + /*Initialising work_queue */ + INIT_WORK(&Si4709_work, Si4709_work_func); + + RDS_Data_Available = 0; + RDS_Data_Lost = 0; + RDS_Groups_Available_till_now = 0; +EXIT: +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_init call over"); + + return ret; +} + +int Si4709_dev_exit(void) +{ + int ret = 0; + + debug("Si4709_dev_exit called"); + + mutex_lock(&(Si4709_dev.lock)); + + /* Temporary blocked by abnormal function call(E-CIM 2657654) */ + /* DW Shim. 2010.03.04 */ + /* Si4709_dev.client = NULL; */ + + /* Si4709_dev.valid_client_state = eFALSE; */ + /* Si4709_dev.valid = eFALSE; */ + +#ifdef RDS_INTERRUPT_ON_ALWAYS + if (Si4709_wq) + destroy_workqueue(Si4709_wq); + + kfree(RDS_Block_Error_buffer); + kfree(RDS_Block_Data_buffer); +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_exit call over"); + + return ret; +} + +void Si4709_dev_mutex_init(void) +{ + mutex_init(&(Si4709_dev.lock)); +} + +int Si4709_dev_powerup(void) +{ + int ret = 0; + u32 value = 100; + + debug("Si4709_dev_powerup called"); + + if (!(RADIO_ON == Si4709_dev.state.power_state)) { + ret = powerup(); + if (ret < 0) { + debug("powerup failed"); + } else if (Si4709_dev.valid_client_state == eFALSE) { + debug("Si4709_dev_powerup called " + "when DS(state, client) is invalid"); + ret = -1; + } else { +/* initial settings */ +#ifdef _ENABLE_RDS_ + si47xx_set_property(FM_RDS_CONFIG, 1); + si47xx_set_property(GPO_IEN, GPO_IEN_STCIEN_MASK | + GPO_IEN_STCREP_MASK); + si47xx_set_property(GPO_IEN, GPO_IEN_STCIEN_MASK | + GPO_IEN_RDSIEN_MASK | GPO_IEN_STCREP_MASK); + si47xx_set_property(FM_RDS_INTERRUPT_SOURCE, + FM_RDS_INTERRUPT_SOURCE_RECV_MASK); + si47xx_set_property(FM_RDS_CONFIG, + FM_RDS_CONFIG_RDSEN_MASK | + (3 << FM_RDS_CONFIG_BLETHA_SHFT) | + (3 << FM_RDS_CONFIG_BLETHB_SHFT) | + (3 << FM_RDS_CONFIG_BLETHC_SHFT) | + (3 << FM_RDS_CONFIG_BLETHD_SHFT)); +#endif +/*VNVS:18-NOV'09 : Setting DE-Time Constant as 50us(Europe,Japan,Australia)*/ + si47xx_set_property(FM_DEEMPHASIS, FM_DEEMPH_50US); + /* SYSCONFIG2_BITSET_SEEKTH( */ + /* &Si4709_dev.registers[SYSCONFIG2],2); */ +/*VNVS:18-NOV'09 : modified for detecting more stations of good quality*/ + si47xx_set_property(FM_SEEK_TUNE_RSSI_THRESHOLD, + TUNE_RSSI_THRESHOLD); + si47xx_set_property(FM_SEEK_BAND_BOTTOM, 8750); + si47xx_set_property(FM_SEEK_BAND_TOP, 10800); + Si4709_dev.settings.band = BAND_87500_108000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_87500_kHz; + si47xx_set_property(FM_SEEK_FREQ_SPACING, + CHAN_SPACING_100_kHz); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_100_kHz; + + /* SYSCONFIG3_BITSET_SKSNR( */ + /* &Si4709_dev.registers[SYSCONFIG3],3); */ +/*VNVS:18-NOV'09 : modified for detecting more stations of good quality*/ + si47xx_set_property(FM_SEEK_TUNE_SNR_THRESHOLD, + TUNE_SNR_THRESHOLD); + Si4709_dev.settings.timeout_RDS = + msecs_to_jiffies(value); + Si4709_dev.settings.curr_snr = TUNE_SNR_THRESHOLD; + Si4709_dev.settings.curr_rssi_th = TUNE_RSSI_THRESHOLD; + Si4709_dev_STEREO_SET(); + +/*this will write all the above registers */ + if (ret < 0) + debug("Si4709_dev_powerup i2c_write 1 failed"); + else { + Si4709_dev.valid = eTRUE; +#ifdef RDS_INTERRUPT_ON_ALWAYS +/*Initialising read and write indices */ + RDS_Buffer_Index_read = 0; + RDS_Buffer_Index_write = 0; + + RDS_Data_Available = 0; + RDS_Data_Lost = 0; + RDS_Groups_Available_till_now = 0; +#endif + } + } + } else + debug("Device already Powered-ON"); + enable_irq(Si4709_dev_irq); + si47xx_set_property(0xff00, 0); + + return ret; +} + +int Si4709_dev_powerdown(void) +{ + int ret = 0; + + msleep(500); /* For avoiding turned off pop noise */ + debug("Si4709_dev_powerdown called"); + + mutex_lock(&(Si4709_dev.lock)); + + disable_irq(Si4709_dev_irq); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_powerdown called when DS is invalid"); + ret = -1; + } else { + ret = powerdown(); + if (ret < 0) + debug("powerdown failed"); + } + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_suspend(void) +{ + int ret = 0; + + debug("Si4709_dev_suspend called"); + +#ifndef _ENABLE_RDS_ + disable_irq(Si4709_dev_irq); +#endif + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid_client_state == eFALSE) { + debug("Si4709_dev_suspend called " + "when DS(state, client) is invalid"); + ret = -1; + } + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_enable call over"); + + return ret; +} + +int Si4709_dev_resume(void) +{ + int ret = 0; + + debug("Si4709_dev_resume called"); + +#ifndef _ENABLE_RDS_ + enable_irq(Si4709_dev_irq); +#endif + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid_client_state == eFALSE) { + debug("Si4709_dev_resume called " + "when DS(state, client) is invalid"); + ret = -1; + } + + mutex_unlock(&(Si4709_dev.lock)); + debug("Si4709_dev_disable call over"); + + return ret; +} + +int Si4709_dev_band_set(int band) +{ + int ret = 0; + u16 prev_band = 0; + u32 prev_bottom_of_band = 0; + + debug("Si4709_dev_band_set called"); + + prev_band = Si4709_dev.settings.band; + prev_bottom_of_band = Si4709_dev.settings.bottom_of_band; + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_band_set called when DS is invalid"); + ret = -1; + } else { + switch (band) { + case BAND_87500_108000_kHz: + si47xx_set_property(FM_SEEK_BAND_BOTTOM, 8750); + si47xx_set_property(FM_SEEK_BAND_TOP, 10800); + Si4709_dev.settings.band = BAND_87500_108000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_87500_kHz; + break; + case BAND_76000_108000_kHz: + si47xx_set_property(FM_SEEK_BAND_BOTTOM, 7600); + si47xx_set_property(FM_SEEK_BAND_TOP, 10800); + Si4709_dev.settings.band = BAND_76000_108000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_76000_kHz; + break; + case BAND_76000_90000_kHz: + si47xx_set_property(FM_SEEK_BAND_BOTTOM, 7600); + si47xx_set_property(FM_SEEK_BAND_TOP, 9000); + Si4709_dev.settings.band = BAND_76000_90000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_76000_kHz; + break; + default: + ret = -1; + } + } + + return ret; +} + +int Si4709_dev_ch_spacing_set(int ch_spacing) +{ + int ret = 0; + u16 prev_ch_spacing = 0; + + debug("Si4709_dev_ch_spacing_set called"); + + mutex_lock(&(Si4709_dev.lock)); + prev_ch_spacing = Si4709_dev.settings.channel_spacing; + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_ch_spacing_set called " + "when DS is invalid"); + ret = -1; + } else { + switch (ch_spacing) { + case CHAN_SPACING_200_kHz: + si47xx_set_property(FM_SEEK_FREQ_SPACING, 20); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_200_kHz; + break; + + case CHAN_SPACING_100_kHz: + si47xx_set_property(FM_SEEK_FREQ_SPACING, 10); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_100_kHz; + break; + + case CHAN_SPACING_50_kHz: + si47xx_set_property(FM_SEEK_FREQ_SPACING, 5); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_50_kHz; + break; + + default: + ret = -1; + } + + if (ret == 0) { + if (ret < 0) { + debug("Si4709_dev_ch_spacing_set " + "i2c_write 1 failed"); + Si4709_dev.settings.channel_spacing = + prev_ch_spacing; + } + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_chan_select(u32 frequency) +{ + int ret = 0; + + debug("Si4709_dev_chan_select called"); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_chan_select called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + + ret = tune_freq(frequency); + debug("Si4709_dev_chan_select called1"); + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + return ret; +} + +int Si4709_dev_chan_get(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_chan_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_chan_get called when DS is invalid"); + ret = -1; + } else { + if (ret < 0) + debug("Si4709_dev_chan_get i2c_read failed"); + else { + fmTuneStatus(0, 0); + *frequency = Freq; + } + } + mutex_unlock(&(Si4709_dev.lock)); + return ret; +} + +int Si4709_dev_seek_full(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_seek_full called\n"); + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_seek_full called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + ret = seek(frequency, 1, 0); + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_up(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_seek_up called\n"); + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_seek_up called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + ret = seek(frequency, 1, 1); + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_down(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_seek_down called\n"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_seek_down called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + + ret = seek(frequency, 0, 1); + + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RSSI_seek_th_set(u8 seek_th) +{ + int ret = 0; + + debug("Si4709_dev_RSSI_seek_th_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_RSSI_seek_th_set called " + "when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_SEEK_TUNE_RSSI_THRESHOLD, seek_th); + Si4709_dev.settings.curr_rssi_th = seek_th; + if (ret < 0) + debug("Si4709_dev_RSSI_seek_th_set i2c_write 1 failed"); + } + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_SNR_th_set(u8 seek_SNR) +{ + int ret = 0; + + debug("Si4709_dev_seek_SNR_th_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_seek_SNR_th_set called " + "when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_SEEK_TUNE_SNR_THRESHOLD, seek_SNR); + Si4709_dev.settings.curr_snr = seek_SNR; + + if (ret < 0) + debug("Si4709_dev_seek_SNR_th_set i2c_write 1 failed"); + } + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_FM_ID_th_set(u8 seek_FM_ID_th) +{ + int ret = 0; + + debug("Si4709_dev_seek_FM_ID_th_set called"); + + return ret; +} + +int Si4709_dev_cur_RSSI_get(struct rssi_snr_t *cur_RSSI) +{ + int ret = 0; + + debug("Si4709_dev_cur_RSSI_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_cur_RSSI_get called when DS is invalid"); + ret = -1; + } else { + fmRsqStatus(0); + if (ret < 0) { + debug("Si4709_dev_cur_RSSI_get i2c_read 1 failed"); + } else { + cur_RSSI->curr_rssi = RSSI; + cur_RSSI->curr_rssi_th = + Si4709_dev.settings.curr_rssi_th; + cur_RSSI->curr_snr = Si4709_dev.settings.curr_snr; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* VNVS:START 13-OCT'09 : + Functions which reads device-id,chip-id,power configuration, + system configuration2 registers + */ +int Si4709_dev_device_id(struct device_id *dev_id) +{ + int ret = 0; + + debug("Si4709_dev_device_id called"); +#if 0 /*To Do*/ + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_device_id called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(DEVICE_ID); + if (ret < 0) { + debug("Si4709_dev_device_id i2c_read failed"); + } else { + dev_id->part_number = + DEVICE_ID_PART_NUMBER(Si4709_dev. + registers[DEVICE_ID]); + dev_id->manufact_number = + DEVICE_ID_MANUFACT_NUMBER(Si4709_dev. + registers[DEVICE_ID]); + } + } + + mutex_unlock(&(Si4709_dev.lock)); +#endif + return ret; +} + +int Si4709_dev_chip_id(struct chip_id *chp_id) +{ + int ret = 0; + + debug("Si4709_dev_chip_id called"); +#if 0 /*To Do*/ + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_chip_id called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(CHIP_ID); + if (ret < 0) { + debug("Si4709_dev_chip_id i2c_read failed"); + } else { + chp_id->chip_version = + CHIP_ID_CHIP_VERSION( + Si4709_dev.registers[CHIP_ID]); + chp_id->device = + CHIP_ID_DEVICE(Si4709_dev.registers[CHIP_ID]); + chp_id->firmware_version = + CHIP_ID_FIRMWARE_VERSION(Si4709_dev. + registers[CHIP_ID]); + } + } + + mutex_unlock(&(Si4709_dev.lock)); +#endif + return ret; +} + +int Si4709_dev_sys_config2(struct sys_config2 *sys_conf2) +{ + int ret = 0; + + debug("Si4709_sys_config2 called\n"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_sys_config2 called when DS is invalid"); + ret = -1; + } else { + if (ret < 0) { + debug("Si4709_sys_config2 i2c_read failed"); + } else { + sys_conf2->rssi_th = Si4709_dev.settings.curr_rssi_th; + sys_conf2->fm_band = Si4709_dev.settings.band; + sys_conf2->fm_chan_spac = + Si4709_dev.settings.channel_spacing; + sys_conf2->fm_vol = 0; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config3(struct sys_config3 *sys_conf3) +{ + int ret = 0; + + debug("Si4709_sys_config3 called\n"); + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_sys_config3 called when DS is invalid"); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + if (ret < 0) { + debug("Si4709_sys_config3 i2c_read failed"); + } else { + sys_conf3->smmute = 0; + sys_conf3->smutea = 0; + sys_conf3->volext = 0; + sys_conf3->sksnr = Si4709_dev.settings.curr_snr; + sys_conf3->skcnt = 0; + } + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_status_rssi(struct status_rssi *status) +{ + int ret = 0; + + debug("Si4709_dev_status_rssi called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_status_rssi called when DS is invalid"); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + fmRsqStatus(0); + if (ret < 0) { + debug("Si4709_sys_config3 i2c_read failed"); + } else { + debug("Si4709_dev_status_rssi %d\n", RSSI); + status->rssi = RSSI; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config2_set(struct sys_config2 *sys_conf2) +{ + int ret = 0; + u16 register_bak = 0; + + debug("Si4709_dev_sys_config2_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + register_bak = Si4709_dev.registers[SYSCONFIG2]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_sys_config2_set called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_SEEK_TUNE_RSSI_THRESHOLD, + sys_conf2->rssi_th); + Si4709_dev_band_set(sys_conf2->fm_band); + si47xx_set_property(FM_SEEK_FREQ_SPACING, + sys_conf2->fm_chan_spac); + Si4709_dev.settings.curr_rssi_th = sys_conf2->rssi_th; + Si4709_dev.settings.band = sys_conf2->fm_band; + Si4709_dev.settings.channel_spacing = sys_conf2->fm_chan_spac; + + if (ret < 0) { + debug("Si4709_dev_sys_config2_set i2c_write 1 failed"); + } else + debug(KERN_ERR "Si4709_dev_sys_config2_set() " + ": Write Sucess!!"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config3_set(struct sys_config3 *sys_conf3) +{ + int ret = 0; + u16 register_bak = 0; + + debug("Si4709_dev_sys_config3_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_sys_config3_set called " + "when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_SEEK_TUNE_SNR_THRESHOLD, + sys_conf3->sksnr); + Si4709_dev.settings.curr_snr = sys_conf3->sksnr; + if (ret < 0) { + debug("Si4709_dev_sys_config3_set i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG3] = register_bak; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_power_config(struct power_config *pow_conf) +{ + int ret = 0; + + debug("Si4709_dev_power_config called"); + + return ret; +} + +/* VNVS:END */ + +/* VNVS:START 18-NOV'09 */ +/* Reading AFCRL Bit */ +int Si4709_dev_AFCRL_get(u8 *afc) +{ + int ret = 0; + + debug("Si4709_dev_AFCRL_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_AFCRL_get called when DS is invalid"); + ret = -1; + } else { + fmRsqStatus(0); + if (ret < 0) + debug("Si4709_dev_AFCRL_get i2c_read failed"); + *afc = AFCRL; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* Setting DE + emphasis time constant 50us(Europe,Japan,Australia) or 75us(USA) + */ +int Si4709_dev_DE_set(u8 de_tc) +{ + u16 sysconfig1 = 0; + int ret = 0; + + debug("Si4709_dev_DE_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_DE_set called when DS is invalid"); + ret = -1; + } else { + switch (de_tc) { + case DE_TIME_CONSTANT_50: + si47xx_set_property(FM_DEEMPHASIS, FM_DEEMPH_50US); + break; + + case DE_TIME_CONSTANT_75: + si47xx_set_property(FM_DEEMPHASIS, FM_DEEMPH_75US); + break; + + default: + ret = -1; + } + + if (0 == ret) { + if (ret < 0) + debug("Si4709_dev_DE_set i2c_write failed"); + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/*Resetting the RDS Data Buffer*/ +int Si4709_dev_reset_rds_data() +{ + int ret = 0; + + debug_rds("Si4709_dev_reset_rds_data called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_reset_rds_data called when DS is invalid"); + ret = -1; + } else { + RDS_Buffer_Index_write = 0; + RDS_Buffer_Index_read = 0; + RDS_Data_Lost = 0; + RDS_Data_Available = 0; + memset(RDS_Block_Data_buffer, 0, RDS_BUFFER_LENGTH * 8); + memset(RDS_Block_Error_buffer, 0, RDS_BUFFER_LENGTH * 4); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/*VNVS:END*/ + +int Si4709_dev_VOLEXT_ENB(void) +{ + int ret = 0; + + debug("Si4709_dev_VOLEXT_ENB called"); + + return ret; +} + +int Si4709_dev_VOLEXT_DISB(void) +{ + int ret = 0; + + debug("Si4709_dev_VOLEXT_DISB called"); + + return ret; +} + +int Si4709_dev_volume_set(u8 volume) +{ + int ret = 0; + u16 sysconfig2 = 0; + + debug("Si4709_dev_volume_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_volume_set called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(RX_VOLUME, rx_vol[volume] & RX_VOLUME_MASK); + + if (ret < 0) { + debug("Si4709_dev_volume_set i2c_write failed"); + Si4709_dev.registers[SYSCONFIG2] = sysconfig2; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_volume_get(u8 *volume) +{ + int ret = 0; + + debug("Si4709_dev_volume_get called"); +#if 0 /*To Do*/ + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_volume_get called when DS is invalid"); + ret = -1; + } else + *volume = + SYSCONFIG2_GET_VOLUME(Si4709_dev.registers[SYSCONFIG2]); + + mutex_unlock(&(Si4709_dev.lock)); +#endif + return ret; +} + +/* +VNVS:START 19-AUG'10 : Adding DSMUTE ON/OFF feature. +The Soft Mute feature is available to attenuate the audio +outputs and minimize audible noise in very weak signal conditions. + */ +int Si4709_dev_DSMUTE_ON(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_DSMUTE_ON called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_DSMUTE_ON called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_SOFT_MUTE_RATE, 64); + si47xx_set_property(FM_SOFT_MUTE_MAX_ATTENUATION, 0); + si47xx_set_property(FM_SOFT_MUTE_SNR_THRESHOLD, 4); + if (ret < 0) + error("Si4709_dev_DSMUTE_ON i2c_write failed"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_DSMUTE_OFF(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_DSMUTE_OFF called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_DSMUTE_OFF called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_SOFT_MUTE_RATE, 64); + si47xx_set_property(FM_SOFT_MUTE_MAX_ATTENUATION, 16); + si47xx_set_property(FM_SOFT_MUTE_SNR_THRESHOLD, 4); + if (ret < 0) + error("Si4709_dev_DSMUTE_OFF i2c_write failed"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/*VNVS:END*/ + +int Si4709_dev_MUTE_ON(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_MUTE_ON called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_MUTE_ON called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(RX_HARD_MUTE, + RX_HARD_MUTE_RMUTE_MASK | + RX_HARD_MUTE_LMUTE_MASK); + if (ret < 0) + debug("Si4709_dev_MUTE_ON i2c_write failed"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_MUTE_OFF(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_MUTE_OFF called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_MUTE_OFF called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(RX_HARD_MUTE, 0); + if (ret < 0) + debug("Si4709_dev_MUTE_OFF i2c_write failed"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_MONO_SET(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_MONO_SET called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_MONO_SET called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_BLEND_MONO_THRESHOLD, 127); + si47xx_set_property(FM_BLEND_STEREO_THRESHOLD, 127); + if (ret < 0) + debug("Si4709_dev_MONO_SET i2c_write failed"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_STEREO_SET(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_STEREO_SET called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_STEREO_SET called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_BLEND_MONO_THRESHOLD, 30); + si47xx_set_property(FM_BLEND_STEREO_THRESHOLD, 49); + if (ret < 0) + debug("Si4709_dev_STEREO_SET i2c_write failed"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RDS_ENABLE(void) +{ + int ret = 0; + + debug("Si4709_dev_RDS_ENABLE called"); + + mutex_lock(&(Si4709_dev.lock)); + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_RDS_ENABLE called when DS is invalid"); + ret = -1; + } else { +#ifdef RDS_INTERRUPT_ON_ALWAYS + si47xx_set_property(GPO_IEN, GPO_IEN_STCIEN_MASK | + GPO_IEN_STCREP_MASK | GPO_IEN_RDSIEN_MASK | + GPO_IEN_RDSREP_MASK); +#endif + si47xx_set_property(FM_RDS_INTERRUPT_SOURCE, + FM_RDS_INTERRUPT_SOURCE_RECV_MASK); + si47xx_set_property(FM_RDS_CONFIG, FM_RDS_CONFIG_RDSEN_MASK | + (3 << FM_RDS_CONFIG_BLETHA_SHFT) | + (3 << FM_RDS_CONFIG_BLETHB_SHFT) | + (3 << FM_RDS_CONFIG_BLETHC_SHFT) | + (3 << FM_RDS_CONFIG_BLETHD_SHFT)); + if (ret < 0) + debug("Si4709_dev_RDS_ENABLE i2c_write failed"); +#ifdef RDS_INTERRUPT_ON_ALWAYS + else + Si4709_RDS_flag = RDS_WAITING; +#endif + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RDS_DISABLE(void) +{ + int ret = 0; + + debug("Si4709_dev_RDS_DISABLE called"); + + mutex_lock(&(Si4709_dev.lock)); + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_RDS_DISABLE called when DS is invalid"); + ret = -1; + } else { + si47xx_set_property(FM_RDS_CONFIG, 0); + + if (ret < 0) + debug("Si4709_dev_RDS_DISABLE i2c_write failed"); +#ifdef RDS_INTERRUPT_ON_ALWAYS + else + Si4709_RDS_flag = NO_WAIT; +#endif + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_rstate_get(struct dev_state_t *dev_state) +{ + int ret = 0; + + debug("Si4709_dev_rstate_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_rstate_get called when DS is invalid"); + ret = -1; + } else { + dev_state->power_state = Si4709_dev.state.power_state; + dev_state->seek_state = Si4709_dev.state.seek_state; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* VNVS:START 7-JUNE'10 Function call for work-queue "Si4709_wq" */ +#ifdef RDS_INTERRUPT_ON_ALWAYS +void Si4709_work_func(struct work_struct *work) +{ + int i = 0; +#ifdef RDS_TESTING + u8 group_type; +#endif + debug_rds("%s", __func__); +mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_data_get called when DS is invalid"); + return; + } + + if (RDS_Data_Lost > 1) + debug_rds("No_of_RDS_groups_Lost till now : %d", + RDS_Data_Lost); + fmRdsStatus(1, 0); + /* RDSR bit and RDS Block data, so reading the RDS registers */ + do { + /* Writing into RDS_Block_Data_buffer */ + i = 0; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BlockA; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BlockB; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BlockC; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BlockD; + + /*Writing into RDS_Block_Error_buffer */ + i = 0; + + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BleA; + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BleB; + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BleC; + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + BleD; + fmRdsStatus(1, 0); + } while (RdsFifoUsed != 0); + +#ifdef RDS_TESTING + if (RDS_Block_Error_buffer + [0 + 4 * RDS_Buffer_Index_write] < 3) { + debug_rds("PI Code is %d", + RDS_Block_Data_buffer[0 + 4 + * RDS_Buffer_Index_write]); + } + if (RDS_Block_Error_buffer + [1 + 4 * RDS_Buffer_Index_write] < 2) { + group_type = RDS_Block_Data_buffer[1 + 4 + * RDS_Buffer_Index_write] >> 11; + + if (group_type & 0x01) { + debug_rds("PI Code is %d", + RDS_Block_Data_buffer[2 + 4 + * RDS_Buffer_Index_write]); + } + if (group_type == GROUP_TYPE_2A + || group_type == GROUP_TYPE_2B) { + if (RDS_Block_Error_buffer + [2 + 4 * RDS_Buffer_Index_write] < 3) { + debug_rds("Update RT with RDSC"); + } else { + debug_rds("RDS_Block_Error_buffer" + " of Block C is greater than 3"); + } + } + } +#endif + RDS_Buffer_Index_write++; + + if (RDS_Buffer_Index_write >= RDS_BUFFER_LENGTH) + RDS_Buffer_Index_write = 0; + + debug_rds("RDS_Buffer_Index_write = %d", RDS_Buffer_Index_write); + mutex_unlock(&(Si4709_dev.lock)); +} +#endif +/*VNVS:END*/ + +int Si4709_dev_RDS_data_get(struct radio_data_t *data) +{ + int i, ret = 0; + + debug_rds("Si4709_dev_RDS_data_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_data_get called when DS is invalid"); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } +#ifdef RDS_INTERRUPT_ON_ALWAYS + debug_rds("RDS_Buffer_Index_read = %d", RDS_Buffer_Index_read); + + /*If No New RDS Data is available return error */ + if (RDS_Buffer_Index_read == RDS_Buffer_Index_write) { + debug_rds("No_New_RDS_Data_is_available"); + if (ret < 0) + error("Si4709_dev_RDS_data_get i2c_read 1 failed"); + else { + fmTuneStatus(0, 0); + data->curr_channel = Freq; + data->curr_rssi = RSSI; + debug_rds("curr_channel: %u, curr_rssi:%u", + data->curr_channel, + (u32) data->curr_rssi); + } + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + if (ret < 0) + error("Si4709_dev_RDS_data_get i2c_read 2 failed"); + else { + fmTuneStatus(0, 0); + data->curr_channel = Freq; + data->curr_rssi = RSSI; + debug_rds("curr_channel: %u, curr_rssi:%u", + data->curr_channel, (u32) data->curr_rssi); + + /* Reading from RDS_Block_Data_buffer */ + i = 0; + data->rdsa = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->rdsb = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->rdsc = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->rdsd = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + + /* Reading from RDS_Block_Error_buffer */ + i = 0; + data->blera = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->blerb = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->blerc = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->blerd = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + + /*Flushing the read data */ + memset(&RDS_Block_Data_buffer[0 + 4 * RDS_Buffer_Index_read], + 0, 8); + memset(&RDS_Block_Error_buffer[0 + 4 * RDS_Buffer_Index_read], + 0, 4); + + RDS_Buffer_Index_read++; + + if (RDS_Buffer_Index_read >= RDS_BUFFER_LENGTH) + RDS_Buffer_Index_read = 0; + } + + debug_rds("RDS_Buffer_Index_read = %d", RDS_Buffer_Index_read); +#else +#if 0 /*To Do*/ + SYSCONFIG1_BITSET_RDSIEN_HIGH(&Si4709_dev.registers[SYSCONFIG1]); + + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + debug("Si4709_dev_RDS_data_get i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } else { + ret = i2c_read(SYSCONFIG1); + if (ret < 0) + debug("Si4709_dev_RDS_data_get i2c_read 1 failed"); + + debug("sysconfig1: 0x%x", Si4709_dev.registers[SYSCONFIG1]); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + Si4709_dev_wait_flag = RDS_WAITING; + + wait_RDS(); + + ret = i2c_read(STATUSRSSI); + if (ret < 0) + debug("Si4709_dev_RDS_data_get i2c_read 2 failed"); + + debug("statusrssi: 0x%x", Si4709_dev.registers[STATUSRSSI]); + + SYSCONFIG1_BITSET_RDSIEN_LOW(&Si4709_dev.registers[SYSCONFIG1]); + + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + debug("Si4709_dev_RDS_data_get i2c_write 2 failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } else if (Si4709_dev_wait_flag == WAIT_OVER) { + Si4709_dev_wait_flag = NO_WAIT; + + ret = i2c_read(RDSD); + if (ret < 0) + debug("Si4709_dev_RDS_data_get " + "i2c_read 3 failed"); + else { + data->rdsa = Si4709_dev.registers[RDSA]; + data->rdsb = Si4709_dev.registers[RDSB]; + data->rdsc = Si4709_dev.registers[RDSC]; + data->rdsd = Si4709_dev.registers[RDSD]; + + get_cur_chan_freq(&(data->curr_channel), + Si4709_dev.registers[READCHAN]); + debug("curr_channel: %u", data->curr_channel); + data->curr_rssi = + STATUSRSSI_RSSI_SIGNAL_STRENGTH + (Si4709_dev.registers[STATUSRSSI]); + debug("curr_rssi:%u", (u32)data->curr_rssi); + data->blera = + STATUSRSSI_RDS_BLOCK_A_ERRORS + (Si4709_dev.registers[STATUSRSSI]); + data->blerb = + READCHAN_BLOCK_B_ERRORS(Si4709_dev. + registers[READCHAN]); + data->blerc = + READCHAN_BLOCK_C_ERRORS(Si4709_dev. + registers[READCHAN]); + data->blerd = + READCHAN_BLOCK_D_ERRORS(Si4709_dev. + registers[READCHAN]); + } + } else { + debug("Si4709_dev_RDS_data_get failure " + "no interrupt or timeout"); + Si4709_dev_wait_flag = NO_WAIT; + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + } +#endif +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RDS_timeout_set(u32 time_out) +{ + int ret = 0; + u32 jiffy_count = 0; + + debug("Si4709_dev_RDS_timeout_set called"); + /****convert time_out(in milliseconds) into jiffies*****/ + + jiffy_count = msecs_to_jiffies(time_out); + + debug("jiffy_count%d", jiffy_count); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_RDS_timeout_set called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.settings.timeout_RDS = jiffy_count; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/**************************************************************/ +static int powerup(void) +{ + int ret = 0; + u16 powercfg = Si4709_dev.registers[POWERCFG]; + +#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_M0_CTC) + gpio_set_value(Si4709_dev_sw, GPIO_LEVEL_HIGH); +#endif + + gpio_set_value(GPIO_FM_RST, GPIO_LEVEL_LOW); + usleep_range(5, 10); + s3c_gpio_cfgpin(Si4709_dev_int, S3C_GPIO_OUTPUT); + s3c_gpio_setpull(Si4709_dev_int, S3C_GPIO_PULL_DOWN); + gpio_set_value(Si4709_dev_int, GPIO_LEVEL_LOW); + usleep_range(10, 15); + gpio_set_value(GPIO_FM_RST, GPIO_LEVEL_HIGH); + usleep_range(5, 10); + s3c_gpio_cfgpin(Si4709_dev_int, S3C_GPIO_SFN(0xF)); + s3c_gpio_setpull(Si4709_dev_int, S3C_GPIO_PULL_UP); + usleep_range(10, 15); + + cmd[0] = POWER_UP; + cmd[1] = POWER_UP_IN_GPO2OEN; + cmd[1] |= POWER_UP_IN_FUNC_FMRX; + cmd[2] = POWER_UP_IN_OPMODE_RX_ANALOG; + ret = si47xx_command(3, cmd, 8, rsp); + + if (ret < 0) { + debug("powerup->i2c_write 1 failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } else { + /* Si4709/09 datasheet: Table 7 */ + msleep(110); + Si4709_dev.state.power_state = RADIO_ON; + } + return ret; +} + +static int powerdown(void) +{ + int ret = 0; + + if (!(RADIO_POWERDOWN == Si4709_dev.state.power_state)) { + cmd[0] = POWER_DOWN; + ret = si47xx_command(1, cmd, 1, rsp); + msleep(110); + + if (ret < 0) + debug("powerdown->i2c_write failed"); + else + Si4709_dev.state.power_state = RADIO_POWERDOWN; + + gpio_set_value(GPIO_FM_RST, GPIO_LEVEL_LOW); + } else + debug("Device already Powered-OFF\n"); + +#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_M0_CTC) + gpio_set_value(Si4709_dev_sw, GPIO_LEVEL_LOW); +#endif + + return ret; +} + +static int seek(u32 *frequency, int up, int mode) +{ + int ret = 0; + u8 get_int; + + if (ret < 0) { + debug("seek i2c_write 1 failed"); + } else { + Si4709_dev_wait_flag = SEEK_WAITING; + fmSeekStart(up, mode); /* mode 0 is full scan */ + wait(); + do { + get_int = getIntStatus(); + } while (!(get_int & STCINT)); + + if (Si4709_dev_wait_flag == SEEK_CANCEL) { + fmTuneStatus(1, 0); + if (ret < 0) { + debug("seek i2c_write 2 failed"); + } + if (ret < 0) + debug("seek i2c_read 1 failed"); + else + *frequency = Freq; + + *frequency = 0; + } + + Si4709_dev_wait_flag = NO_WAIT; + + fmTuneStatus(0, 1); + if (BLTF != 1) + *frequency = Freq; + + else { + if (Valid) + *frequency = Freq; + else + *frequency = 0; + } + } + return ret; +} + +static int tune_freq(u32 frequency) +{ + int ret = 0; + + u16 channel = Si4709_dev.registers[CHANNEL]; + mutex_lock(&(Si4709_dev.lock)); + + channel = freq_to_channel(frequency); + if (ret < 0) { + debug("tune_freq i2c_write 1 failed"); + Si4709_dev.registers[CHANNEL] = channel; + } else { + Si4709_dev_wait_flag = TUNE_WAITING; + fmTuneFreq(frequency); + wait(); + Si4709_dev_wait_flag = NO_WAIT; + debug("Si4709_dev_wait_flag = TUNE_WAITING\n"); + + if (!(getIntStatus() & STCINT)) { + printk(KERN_INFO "%s tune is failed!\n", __func__); + fmTuneStatus(1, 1); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + + fmTuneStatus(0, 1); + + debug("Si4709 tune_freq fmTuneStatus %x\n", rsp[0]); + + } + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +static u16 freq_to_channel(u32 frequency) +{ + u16 channel; + + if (frequency < Si4709_dev.settings.bottom_of_band) + frequency = Si4709_dev.settings.bottom_of_band; + + channel = (frequency - Si4709_dev.settings.bottom_of_band) + / Si4709_dev.settings.channel_spacing; + + return channel; +} + +/* Only one thread will be able to call this, since this function call is + protected by a mutex, so no race conditions can arise */ +static void wait(void) +{ + wait_event_interruptible(Si4709_waitq, + (Si4709_dev_wait_flag == WAIT_OVER) || + (Si4709_dev_wait_flag == SEEK_CANCEL)); +} + +#ifndef RDS_INTERRUPT_ON_ALWAYS +static void wait_RDS(void) +{ + wait_event_interruptible_timeout(Si4709_waitq, + (Si4709_dev_wait_flag == WAIT_OVER), + Si4709_dev.settings.timeout_RDS); +} +#endif + +/*----------------------------------------------------------------------------- + Helper function that sends the GET_INT_STATUS command to the part + + Returns: + The status byte from the part. +-----------------------------------------------------------------------------*/ +u8 getIntStatus(void) +{ + cmd[0] = GET_INT_STATUS; + si47xx_command(1, cmd, 1, rsp); + + debug("Si4709 getIntStatus return %x\n", rsp[0]); + return rsp[0]; +} + +/*----------------------------------------------------------------------------- + Helper function that sends the FM_TUNE_FREQ command to the part + + Inputs: + frequency in 10kHz steps +-----------------------------------------------------------------------------*/ +static void fmTuneFreq(u16 frequency) +{ + cmd[0] = FM_TUNE_FREQ; + cmd[1] = 0; + cmd[2] = (u8)(frequency >> 8); + cmd[3] = (u8)(frequency & 0x00FF); + cmd[4] = (u8)0; + si47xx_command(5, cmd, 1, rsp); +} + +/*----------------------------------------------------------------------------- + Helper function that sends the FM_TUNE_STATUS command to the part + + Inputs: + cancel: If non-zero the current seek will be cancelled. + intack: If non-zero the interrupt for STCINT will be cleared. + +Outputs: These are global variables and are set by this method +STC: The seek/tune is complete +BLTF: The seek reached the band limit or original start frequency +AFCRL: The AFC is railed if this is non-zero +Valid: The station is valid if this is non-zero +Freq: The current frequency +RSSI: The RSSI level read at tune. +ASNR: The audio SNR level read at tune. +AntCap: The current level of the tuning capacitor. +-----------------------------------------------------------------------------*/ + +static void fmTuneStatus(u8 cancel, u8 intack) +{ + cmd[0] = FM_TUNE_STATUS; + cmd[1] = 0; + if (cancel) + cmd[1] |= FM_TUNE_STATUS_IN_CANCEL; + if (intack) + cmd[1] |= FM_TUNE_STATUS_IN_INTACK; + + si47xx_command(2, cmd, 8, rsp); + + STC = !!(rsp[0] & STCINT); + BLTF = !!(rsp[1] & FM_TUNE_STATUS_OUT_BTLF); + AFCRL = !!(rsp[1] & FM_TUNE_STATUS_OUT_AFCRL); + Valid = !!(rsp[1] & FM_TUNE_STATUS_OUT_VALID); + Freq = ((u16)rsp[2] << 8) | (u16)rsp[3]; + RSSI = rsp[4]; + ASNR = rsp[5]; + AntCap = rsp[7]; +} + +/*----------------------------------------------------------------------------- + Helper function that sends the FM_RDS_STATUS command to the part + + Inputs: + intack: If non-zero the interrupt for STCINT will be cleared. + mtfifo: If non-zero the fifo will be cleared. + +Outputs: +Status: Contains bits about the status returned from the part. +RdsInts: Contains bits about the interrupts that have fired +related to RDS. +RdsSync: If non-zero the RDS is currently synced. +GrpLost: If non-zero some RDS groups were lost. +RdsFifoUsed: The amount of groups currently remaining +in the RDS fifo. +BlockA: Block A group data from the oldest FIFO entry. +BlockB: Block B group data from the oldest FIFO entry. +BlockC: Block C group data from the oldest FIFO entry. +BlockD: Block D group data from the oldest FIFO entry. +BleA: Block A corrected error information. +BleB: Block B corrected error information. +BleC: Block C corrected error information. +BleD: Block D corrected error information. +-----------------------------------------------------------------------------*/ +void fmRdsStatus(u8 intack, u8 mtfifo) +{ + cmd[0] = FM_RDS_STATUS; + cmd[1] = 0; + if (intack) + cmd[1] |= FM_RDS_STATUS_IN_INTACK; + if (mtfifo) + cmd[1] |= FM_RDS_STATUS_IN_MTFIFO; + + si47xx_command(2, cmd, 13, rsp); + + Status = rsp[0]; + RdsInts = rsp[1]; + RdsSync = !!(rsp[2] & FM_RDS_STATUS_OUT_SYNC); + GrpLost = !!(rsp[2] & FM_RDS_STATUS_OUT_GRPLOST); + RdsFifoUsed = rsp[3]; + BlockA = ((u16)rsp[4] << 8) | (u16)rsp[5]; + BlockB = ((u16)rsp[6] << 8) | (u16)rsp[7]; + BlockC = ((u16)rsp[8] << 8) | (u16)rsp[9]; + BlockD = ((u16)rsp[10] << 8) | (u16)rsp[11]; + BleA = (rsp[12] & FM_RDS_STATUS_OUT_BLEA) >> + FM_RDS_STATUS_OUT_BLEA_SHFT; + BleB = (rsp[12] & FM_RDS_STATUS_OUT_BLEB) >> + FM_RDS_STATUS_OUT_BLEB_SHFT; + BleC = (rsp[12] & FM_RDS_STATUS_OUT_BLEC) >> + FM_RDS_STATUS_OUT_BLEC_SHFT; + BleD = (rsp[12] & FM_RDS_STATUS_OUT_BLED) >> + FM_RDS_STATUS_OUT_BLED_SHFT; +} + + +/*----------------------------------------------------------------------------- + Helper function that sends the FM_RSQ_STATUS command to the part + + Inputs: + intack: If non-zero the interrupt for STCINT will be cleared. + +Outputs: +Status: Contains bits about the status returned from the part. +RsqInts: Contains bits about the interrupts that have fired related to RSQ. +SMUTE: The soft mute function is currently enabled +AFCRL: The AFC is railed if this is non-zero +Valid: The station is valid if this is non-zero +Pilot: A pilot tone is currently present +Blend: Percentage of blend for stereo. (100 = full stereo) +RSSI: The RSSI level read at tune. +ASNR: The audio SNR level read at tune. +FreqOff: The frequency offset in kHz of the current station from the tuned +frequency. +-----------------------------------------------------------------------------*/ +static void fmRsqStatus(u8 intack) +{ + cmd[0] = FM_RSQ_STATUS; + cmd[1] = 0; + if (intack) + cmd[1] |= FM_RSQ_STATUS_IN_INTACK; + + si47xx_command(2, cmd, 8, rsp); + + Status = rsp[0]; + RsqInts = rsp[1]; + SMUTE = !!(rsp[2] & FM_RSQ_STATUS_OUT_SMUTE); + AFCRL = !!(rsp[2] & FM_RSQ_STATUS_OUT_AFCRL); + Valid = !!(rsp[2] & FM_RSQ_STATUS_OUT_VALID); + Pilot = !!(rsp[3] & FM_RSQ_STATUS_OUT_PILOT); + Blend = rsp[3] & FM_RSQ_STATUS_OUT_STBLEND; + RSSI = rsp[4]; + ASNR = rsp[5]; + FreqOff = rsp[7]; +} + + +/*----------------------------------------------------------------------------- + Helper function that sends the FM_SEEK_START command to the part + +Inputs: +seekUp: If non-zero seek will increment otherwise decrement +wrap: If non-zero seek will wrap around band limits when hitting the end +of the band limit. +-----------------------------------------------------------------------------*/ +static void fmSeekStart(u8 seekUp, u8 wrap) +{ + cmd[0] = FM_SEEK_START; + cmd[1] = 0; + if (seekUp) + cmd[1] |= FM_SEEK_START_IN_SEEKUP; + if (wrap) + cmd[1] |= FM_SEEK_START_IN_WRAP; + + si47xx_command(2, cmd, 1, rsp); +} + + +/*----------------------------------------------------------------------------- + Set the passed property number to the passed value. + + Inputs: + propNumber: The number identifying the property to set + propValue: The value of the property. +-----------------------------------------------------------------------------*/ +void si47xx_set_property(u16 propNumber, u16 propValue) +{ + cmd[0] = SET_PROPERTY; + cmd[1] = 0; + cmd[2] = (u8)(propNumber >> 8); + cmd[3] = (u8)(propNumber & 0x00FF); + cmd[4] = (u8)(propValue >> 8); + cmd[5] = (u8)(propValue & 0x00FF); + + si47xx_command(6, cmd, 0, NULL); +} + +/*----------------------------------------------------------------------------- + This command returns the status +-----------------------------------------------------------------------------*/ +u8 si47xx_readStatus(void) +{ + u8 status; + i2c_read(1, &status); + + return status; +} + + +/*----------------------------------------------------------------------------- + Command that will wait for CTS before returning +-----------------------------------------------------------------------------*/ +void si47xx_waitForCTS(void) +{ + u16 i = 1000; + u8 rStatus = 0; + + do { + rStatus = si47xx_readStatus(); + usleep_range(5, 10); + } while (--i && !(rStatus & CTS)); +} + +/*----------------------------------------------------------------------------- + Sends a command to the part and returns the reply bytes +-----------------------------------------------------------------------------*/ +int si47xx_command(u8 cmd_size, u8 *cmd, u8 reply_size, u8 *reply) +{ + int ret = 0; + ret = i2c_write(cmd_size, cmd); + si47xx_waitForCTS(); + + if (reply_size) + i2c_read(reply_size, reply); + + if (ret < 0) + printk(KERN_INFO "%s i2c_write failed %d\n", __func__, ret); + + return ret; +} + +static int i2c_write(u8 number_bytes, u8 *data_out) +{ + int ret = 0; + + ret = i2c_master_send((struct i2c_client *)(Si4709_dev.client), + (const char *)data_out, number_bytes); + if (ret == number_bytes) + ret = 0; + else + ret = -1; + + return ret; +} + +static int i2c_read(u8 number_bytes, u8 *data_in) +{ + int ret = 0; + + ret = i2c_master_recv((struct i2c_client *)(Si4709_dev.client), data_in, + number_bytes); + if (ret < 0) + printk(KERN_INFO "%s i2c_read failed %d\n", __func__, ret); + + return ret; +} diff --git a/drivers/samsung/fm_si4709/Si4705_main.c b/drivers/samsung/fm_si4709/Si4705_main.c new file mode 100644 index 0000000..aaccd5f --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4705_main.c @@ -0,0 +1,864 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/uaccess.h> +#include <linux/irq.h> +#include <asm/irq.h> +#include <linux/io.h> +#include <linux/wait.h> +#include <linux/stat.h> +#include <linux/ioctl.h> +#include <linux/delay.h> + +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> + +#include "Si4709_i2c_drv.h" +#include "Si4709_dev.h" +#include "Si4709_ioctl.h" +#include "Si4709_common.h" + +/*******************************************************/ + +/*static functions*/ + +/*file operatons*/ +static int Si4709_open(struct inode *, struct file *); +static int Si4709_release(struct inode *, struct file *); +static long Si4709_ioctl(struct file *, unsigned int, unsigned long); + +/*ISR*/ +static irqreturn_t Si4709_isr(int irq, void *unused); +/* static void __iomem *gpio_mask_mem; */ +/**********************************************************/ + +static const struct file_operations Si4709_fops = { + .owner = THIS_MODULE, + .open = Si4709_open, + .unlocked_ioctl = Si4709_ioctl, + .release = Si4709_release, +}; + +static struct miscdevice Si4709_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "fmradio", + .fops = &Si4709_fops, +}; + +/*VNVS:START 13-OCT'09---- +dummy struct which is used as a cookie for FM Radio interrupt */ +struct fm_radio { + int i; + int j; +}; + +struct fm_radio fm_radio_1; +/*VNVS:END*/ + +wait_queue_head_t Si4709_waitq; + +unsigned int Si4709_int; +unsigned int Si4709_irq; + +/***************************************************************/ + +static int Si4709_open(struct inode *inode, struct file *filp) +{ + debug("Si4709_open called\n"); + + return nonseekable_open(inode, filp); +} + +static int Si4709_release(struct inode *inode, struct file *filp) +{ + debug("Si4709_release called\n\n"); + + return 0; +} + +static long Si4709_ioctl(struct file *filp, unsigned int ioctl_cmd, + unsigned long arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + + debug("Si4709 ioctl 0x%x", ioctl_cmd); + + if (_IOC_TYPE(ioctl_cmd) != Si4709_IOC_MAGIC) { + debug("Inappropriate ioctl 1 0x%x", ioctl_cmd); + return -ENOTTY; + } + + if (_IOC_NR(ioctl_cmd) > Si4709_IOC_NR_MAX) { + debug("Inappropriate ioctl 2 0x%x", ioctl_cmd); + return -ENOTTY; + } + + + switch (ioctl_cmd) { + case Si4709_IOC_POWERUP: + debug("Si4709_IOC_POWERUP called\n\n"); + + ret = (long)Si4709_dev_powerup(); + if (ret < 0) + debug("Si4709_IOC_POWERUP failed\n"); + break; + + case Si4709_IOC_POWERDOWN: + debug("Si4709_IOC_POWERDOWN called\n"); + + ret = (long)Si4709_dev_powerdown(); + if (ret < 0) + debug("Si4709_IOC_POWERDOWN failed\n"); + break; + + case Si4709_IOC_BAND_SET: + { + int band; + debug("Si4709_IOC_BAND_SET called\n\n"); + + if (copy_from_user((void *)&band, argp, sizeof(int))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_band_set(band); + if (ret < 0) + debug("Si4709_IOC_BAND_SET failed\n"); + } + } + break; + + case Si4709_IOC_CHAN_SPACING_SET: + { + int ch_spacing; + debug("Si4709_IOC_CHAN_SPACING_SET called\n"); + + if (copy_from_user + ((void *)&ch_spacing, argp, sizeof(int))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_ch_spacing_set(ch_spacing); + if (ret < 0) + debug("Si4709_IOC_CHAN_SPACING_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_CHAN_SELECT: + { + u32 frequency; + debug("Si4709_IOC_CHAN_SELECT called\n"); + + if (copy_from_user + ((void *)&frequency, argp, sizeof(u32))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_chan_select(frequency); + if (ret < 0) + debug("Si4709_IOC_CHAN_SELECT " + "failed\n"); + } + } + break; + + case Si4709_IOC_CHAN_GET: + { + u32 frequency = 0; + debug("Si4709_IOC_CHAN_GET called\n"); + + ret = (long)Si4709_dev_chan_get(&frequency); + if (ret < 0) + debug("Si4709_IOC_CHAN_GET failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SEEK_FULL: + { + u32 frequency = 0; + debug("Si4709_IOC_SEEK_FULL called\n"); + + ret = (long)Si4709_dev_seek_full(&frequency); + if (ret < 0) + debug("Si4709_IOC_SEEK_FULL failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SEEK_UP: + { + u32 frequency = 0; + debug("Si4709_IOC_SEEK_UP called\n"); + + ret = (long)Si4709_dev_seek_up(&frequency); + if (ret < 0) + debug("Si4709_IOC_SEEK_UP failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SEEK_DOWN: + { + u32 frequency = 0; + debug("Si4709_IOC_SEEK_DOWN called\n"); + + ret = (long)Si4709_dev_seek_down(&frequency); + if (ret < 0) + debug("Si4709_IOC_SEEK_DOWN failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_RSSI_SEEK_TH_SET: + { + u8 RSSI_seek_th; + debug("Si4709_IOC_RSSI_SEEK_TH_SET called\n"); + + if (copy_from_user + ((void *)&RSSI_seek_th, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_RSSI_seek_th_set(RSSI_seek_th); + if (ret < 0) + debug("Si4709_IOC_RSSI_SEEK_TH_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_SEEK_SNR_SET: + { + u8 seek_SNR_th; + debug("Si4709_IOC_SEEK_SNR_SET called\n"); + + if (copy_from_user + ((void *)&seek_SNR_th, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_seek_SNR_th_set(seek_SNR_th); + if (ret < 0) + debug("Si4709_IOC_SEEK_SNR_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_SEEK_CNT_SET: + { + u8 seek_FM_ID_th; + debug("Si4709_IOC_SEEK_CNT_SET called\n"); + + if (copy_from_user + ((void *)&seek_FM_ID_th, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = + (long)Si4709_dev_seek_FM_ID_th_set(seek_FM_ID_th); + if (ret < 0) + debug("Si4709_IOC_SEEK_CNT_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_CUR_RSSI_GET: + { + struct rssi_snr_t data; + debug("Si4709_IOC_CUR_RSSI_GET called\n"); + + ret = (long)Si4709_dev_cur_RSSI_get(&data); + if (ret < 0) + debug("Si4709_IOC_CUR_RSSI_GET failed\n"); + else if (copy_to_user(argp, (void *)&data, + sizeof(data))) + ret = -EFAULT; + + debug("curr_rssi:%d\ncurr_rssi_th:%d\ncurr_snr:%d\n", + data.curr_rssi, data.curr_rssi_th, data.curr_snr); + } + break; + + case Si4709_IOC_VOLEXT_ENB: + debug("Si4709_IOC_VOLEXT_ENB called\n"); + + ret = (long)Si4709_dev_VOLEXT_ENB(); + if (ret < 0) + debug("Si4709_IOC_VOLEXT_ENB failed\n"); + break; + + case Si4709_IOC_VOLEXT_DISB: + debug("Si4709_IOC_VOLEXT_DISB called\n"); + + ret = (long)Si4709_dev_VOLEXT_DISB(); + if (ret < 0) + debug("Si4709_IOC_VOLEXT_DISB failed\n"); + break; + + case Si4709_IOC_VOLUME_SET: + { + u8 volume; + if (copy_from_user((void *)&volume, argp, sizeof(u8))) + ret = -EFAULT; + else { + debug("Si4709_IOC_VOLUME_SET called " + "vol %d\n", volume); + ret = (long)Si4709_dev_volume_set(volume); + if (ret < 0) + debug("Si4709_IOC_VOLUME_SET failed\n"); + } + } + break; + + case Si4709_IOC_VOLUME_GET: + { + u8 volume; + debug("Si4709_IOC_VOLUME_GET called\n"); + + ret = (long)Si4709_dev_volume_get(&volume); + if (ret < 0) + debug("Si4709_IOC_VOLUME_GET failed\n"); + else if (copy_to_user + (argp, (void *)&volume, sizeof(u8))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_DSMUTE_ON: + debug("Si4709_IOC_DSMUTE_ON called\n\n"); + + ret = (long)Si4709_dev_DSMUTE_ON(); + if (ret < 0) + error("Si4709_IOC_DSMUTE_ON failed\n"); + break; + + case Si4709_IOC_DSMUTE_OFF: + debug("Si4709_IOC_DSMUTE_OFF called\n\n"); + + ret = (long)Si4709_dev_DSMUTE_OFF(); + if (ret < 0) + error("Si4709_IOC_DSMUTE_OFF failed\n"); + break; + + case Si4709_IOC_MUTE_ON: + debug("Si4709_IOC_MUTE_ON called\n"); + + ret = (long)Si4709_dev_MUTE_ON(); + if (ret < 0) + debug("Si4709_IOC_MUTE_ON failed\n"); + break; + + case Si4709_IOC_MUTE_OFF: + debug("Si4709_IOC_MUTE_OFF called\n"); + + ret = (long)Si4709_dev_MUTE_OFF(); + if (ret < 0) + debug("Si4709_IOC_MUTE_OFF failed\n"); + break; + + case Si4709_IOC_MONO_SET: + debug("Si4709_IOC_MONO_SET called\n"); + + ret = (long)Si4709_dev_MONO_SET(); + if (ret < 0) + debug("Si4709_IOC_MONO_SET failed\n"); + break; + + case Si4709_IOC_STEREO_SET: + debug("Si4709_IOC_STEREO_SET called\n"); + + ret = (long)Si4709_dev_STEREO_SET(); + if (ret < 0) + debug("Si4709_IOC_STEREO_SET failed\n"); + break; + + case Si4709_IOC_RSTATE_GET: + { + struct dev_state_t dev_state; + + debug("Si4709_IOC_RSTATE_GET called\n"); + + ret = (long)Si4709_dev_rstate_get(&dev_state); + if (ret < 0) + debug("Si4709_IOC_RSTATE_GET failed\n"); + else if (copy_to_user(argp, (void *)&dev_state, + sizeof(dev_state))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_RDS_DATA_GET: + { + struct radio_data_t data; + debug("Si4709_IOC_RDS_DATA_GET called\n"); + + ret = (long)Si4709_dev_RDS_data_get(&data); + if (ret < 0) + debug(" Si4709_IOC_RDS_DATA_GET failed\n"); + else if (copy_to_user(argp, (void *)&data, + sizeof(data))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_RDS_ENABLE: + debug("Si4709_IOC_RDS_ENABLE called\n"); + + ret = (long)Si4709_dev_RDS_ENABLE(); + if (ret < 0) + debug("Si4709_IOC_RDS_ENABLE failed\n"); + break; + + case Si4709_IOC_RDS_DISABLE: + debug("Si4709_IOC_RDS_DISABLE called\n"); + + ret = (long)Si4709_dev_RDS_DISABLE(); + if (ret < 0) + debug("Si4709_IOC_RDS_DISABLE failed\n"); + break; + + case Si4709_IOC_RDS_TIMEOUT_SET: + { + u32 time_out; + debug("Si4709_IOC_RDS_TIMEOUT_SET called\n"); + + if (copy_from_user + ((void *)&time_out, argp, sizeof(u32))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_RDS_timeout_set(time_out); + if (ret < 0) + debug("Si4709_IOC_RDS_TIMEOUT_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_SEEK_CANCEL: + debug("Si4709_IOC_SEEK_CANCEL called\n"); + + if (Si4709_dev_wait_flag == SEEK_WAITING) { + Si4709_dev_wait_flag = SEEK_CANCEL; + wake_up_interruptible(&Si4709_waitq); + } + break; + +/*VNVS:START 13-OCT'09---- +Switch Case statements for calling functions which reads device-id, +chip-id,power configuration, system configuration2 registers */ + case Si4709_IOC_CHIP_ID_GET: + { + struct chip_id chp_id; + debug("Si4709_IOC_CHIP_ID called\n"); + + ret = (long)Si4709_dev_chip_id(&chp_id); + if (ret < 0) + debug("Si4709_IOC_CHIP_ID failed\n"); + else if (copy_to_user(argp, (void *)&chp_id, + sizeof(chp_id))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_DEVICE_ID_GET: + { + struct device_id dev_id; + debug("Si4709_IOC_DEVICE_ID called\n"); + + ret = (long)Si4709_dev_device_id(&dev_id); + if (ret < 0) + debug("Si4709_IOC_DEVICE_ID failed\n"); + else if (copy_to_user(argp, (void *)&dev_id, + sizeof(dev_id))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SYS_CONFIG2_GET: + { + struct sys_config2 sys_conf2; + debug("Si4709_IOC_SYS_CONFIG2 called\n"); + + ret = (long)Si4709_dev_sys_config2(&sys_conf2); + if (ret < 0) + debug("Si4709_IOC_SYS_CONFIG2 failed\n"); + else if (copy_to_user(argp, (void *)&sys_conf2, + sizeof(sys_conf2))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SYS_CONFIG3_GET: + { + struct sys_config3 sys_conf3; + debug("Si4709_IOC_SYS_CONFIG3 called\n"); + + ret = (long)Si4709_dev_sys_config3(&sys_conf3); + if (ret < 0) + debug("Si4709_IOC_SYS_CONFIG3 failed\n"); + else if (copy_to_user(argp, (void *)&sys_conf3, + sizeof(sys_conf3))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_POWER_CONFIG_GET: + { + struct power_config pow_conf; + debug("Si4709_IOC_POWER_CONFIG called\n"); + + ret = (long)Si4709_dev_power_config(&pow_conf); + if (ret < 0) + debug("Si4709_IOC_POWER_CONFIG failed\n"); + else if (copy_to_user(argp, (void *)&pow_conf, + sizeof(pow_conf))) + ret = -EFAULT; + } + break; +/*VNVS:END*/ + +/*VNVS:START 18-NOV'09*/ + /*Reading AFCRL Bit */ + case Si4709_IOC_AFCRL_GET: + { + u8 afc; + debug("Si4709_IOC_AFCRL_GET called\n"); + + ret = (long)Si4709_dev_AFCRL_get(&afc); + if (ret < 0) + debug("Si4709_IOC_AFCRL_GET failed\n"); + else if (copy_to_user(argp, (void *)&afc, sizeof(u8))) + ret = -EFAULT; + } + break; + + /*Setting DE-emphasis Time Constant. + For DE=0,TC=50us(Europe,Japan,Australia) + and DE=1,TC=75us(USA) */ + case Si4709_IOC_DE_SET: + { + u8 de_tc; + debug("Si4709_IOC_DE_SET called\n"); + + if (copy_from_user((void *)&de_tc, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_DE_set(de_tc); + if (ret < 0) + debug("Si4709_IOC_DE_SET failed\n"); + } + } + break; + + case Si4709_IOC_STATUS_RSSI_GET: + { + struct status_rssi status; + debug("Si4709_IOC_STATUS_RSSI_GET called\n"); + + ret = (long)Si4709_dev_status_rssi(&status); + if (ret < 0) + debug("Si4709_IOC_STATUS_RSSI_GET failed\n"); + else if (copy_to_user(argp, (void *)&status, + sizeof(status))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SYS_CONFIG2_SET: + { + struct sys_config2 sys_conf2; + unsigned long n; + debug("Si4709_IOC_SYS_CONFIG2_SET called\n"); + + n = copy_from_user((void *)&sys_conf2, argp, + sizeof(sys_conf2)); + if (n) { + debug("Si4709_IOC_SYS_CONFIG2_SET() : " + "copy_from_user() has error!! " + "Failed to read [%lu] byes!", n); + ret = -EFAULT; + } else { + ret = (long)Si4709_dev_sys_config2_set(&sys_conf2); + if (ret < 0) + debug("Si4709_IOC_SYS_CONFIG2_SET" + "failed\n"); + } + } + break; + + case Si4709_IOC_SYS_CONFIG3_SET: + { + struct sys_config3 sys_conf3; + unsigned long n; + + debug("Si4709_IOC_SYS_CONFIG3_SET called\n"); + + n = copy_from_user((void *)&sys_conf3, argp, + sizeof(sys_conf3)); + if (n) { + debug("Si4709_IOC_SYS_CONFIG3_SET() : " + "copy_from_user() has error!! " + "Failed to read [%lu] byes!", n); + ret = -EFAULT; + } else { + ret = (long)Si4709_dev_sys_config3_set(&sys_conf3); + if (ret < 0) + debug("Si4709_IOC_SYS_CONFIG3_SET " + "failed\n"); + } + } + break; + + /*Resetting the RDS Data Buffer */ + case Si4709_IOC_RESET_RDS_DATA: + { + debug("Si4709_IOC_RESET_RDS_DATA called\n"); + + ret = (long)Si4709_dev_reset_rds_data(); + if (ret < 0) + error("Si4709_IOC_RESET_RDS_DATA failed\n"); + } + break; +/*VNVS:END*/ + default: + debug(" ioctl default\n"); + ret = -ENOTTY; + break; + } + + return ret; +} + +static irqreturn_t Si4709_isr(int irq, void *unused) +{ + debug("Si4709_isr: FM device called IRQ: %d\n", irq); +#ifdef RDS_INTERRUPT_ON_ALWAYS + if ((Si4709_dev_wait_flag == SEEK_WAITING) || + (Si4709_dev_wait_flag == TUNE_WAITING)) { + debug("Si4709_isr: FM Seek/Tune Interrupt " + "called IRQ %d\n", irq); + Si4709_dev_wait_flag = WAIT_OVER; + wake_up_interruptible(&Si4709_waitq); + } else if (Si4709_RDS_flag == RDS_WAITING) { /* RDS Interrupt */ + debug_rds("Si4709_isr: FM RDS Interrupt " + "called IRQ %d", irq); + + debug_rds("RDS_Groups_Available_till_now b/w " + "Power ON/OFF : %d", + RDS_Groups_Available_till_now); + + if (!work_pending(&Si4709_work)) + queue_work(Si4709_wq, &Si4709_work); + } +#else + if ((Si4709_dev_wait_flag == SEEK_WAITING) || + (Si4709_dev_wait_flag == TUNE_WAITING) || + (Si4709_dev_wait_flag == RDS_WAITING)) { + Si4709_dev_wait_flag = WAIT_OVER; + wake_up_interruptible(&Si4709_waitq); + } +#endif + return IRQ_HANDLED; +} + +/************************************************************/ + +void debug_ioctls(void) +{ + debug("------------------------------------------------\n"); + + debug("Si4709_IOC_POWERUP 0x%x", Si4709_IOC_POWERUP); + + debug("Si4709_IOC_POWERDOWN 0x%x", Si4709_IOC_POWERDOWN); + + debug("Si4709_IOC_BAND_SET 0x%x", Si4709_IOC_BAND_SET); + + debug("Si4709_IOC_CHAN_SPACING_SET 0x%x", Si4709_IOC_CHAN_SPACING_SET); + + debug("Si4709_IOC_CHAN_SELECT 0x%x", Si4709_IOC_CHAN_SELECT); + + debug("Si4709_IOC_CHAN_GET 0x%x", Si4709_IOC_CHAN_GET); + + debug("Si4709_IOC_SEEK_UP 0x%x", Si4709_IOC_SEEK_UP); + + debug("Si4709_IOC_SEEK_DOWN 0x%x", Si4709_IOC_SEEK_DOWN); + + /*VNVS:28OCT'09---- Si4709_IOC_SEEK_AUTO is disabled as of now */ + /* debug("Si4709_IOC_SEEK_AUTO 0x%x", Si4709_IOC_SEEK_AUTO); */ + + debug("Si4709_IOC_RSSI_SEEK_TH_SET 0x%x", Si4709_IOC_RSSI_SEEK_TH_SET); + + debug("Si4709_IOC_SEEK_SNR_SET 0x%x", Si4709_IOC_SEEK_SNR_SET); + + debug("Si4709_IOC_SEEK_CNT_SET 0x%x", Si4709_IOC_SEEK_CNT_SET); + + debug("Si4709_IOC_CUR_RSSI_GET 0x%x", Si4709_IOC_CUR_RSSI_GET); + + debug("Si4709_IOC_VOLEXT_ENB 0x%x", Si4709_IOC_VOLEXT_ENB); + + debug("Si4709_IOC_VOLEXT_DISB 0x%x", Si4709_IOC_VOLEXT_DISB); + + debug("Si4709_IOC_VOLUME_SET 0x%x", Si4709_IOC_VOLUME_SET); + + debug("Si4709_IOC_VOLUME_GET 0x%x", Si4709_IOC_VOLUME_GET); + + debug("Si4709_IOC_MUTE_ON 0x%x", Si4709_IOC_MUTE_ON); + + debug("Si4709_IOC_MUTE_OFF 0x%x", Si4709_IOC_MUTE_OFF); + + debug("Si4709_IOC_MONO_SET 0x%x", Si4709_IOC_MONO_SET); + + debug("Si4709_IOC_STEREO_SET 0x%x", Si4709_IOC_STEREO_SET); + + debug("Si4709_IOC_RSTATE_GET 0x%x", Si4709_IOC_RSTATE_GET); + + debug("Si4709_IOC_RDS_DATA_GET 0x%x", Si4709_IOC_RDS_DATA_GET); + + debug("Si4709_IOC_RDS_ENABLE 0x%x", Si4709_IOC_RDS_ENABLE); + + debug("Si4709_IOC_RDS_DISABLE 0x%x", Si4709_IOC_RDS_DISABLE); + + debug("Si4709_IOC_RDS_TIMEOUT_SET 0x%x", Si4709_IOC_RDS_TIMEOUT_SET); + + debug("Si4709_IOC_DEVICE_ID_GET 0x%x", Si4709_IOC_DEVICE_ID_GET); + + debug("Si4709_IOC_CHIP_ID_GET 0x%x", Si4709_IOC_CHIP_ID_GET); + + debug("Si4709_IOC_SYS_CONFIG2_GET 0x%x", Si4709_IOC_SYS_CONFIG2_GET); + + debug("Si4709_IOC_POWER_CONFIG_GET 0x%x", Si4709_IOC_POWER_CONFIG_GET); + + debug("Si4709_IOC_AFCRL_GET 0x%x", Si4709_IOC_AFCRL_GET); + + debug("Si4709_IOC_DE_SET 0x%x", Si4709_IOC_DE_SET); + + debug("Si4709_IOC_DSMUTE_ON 0x%x", Si4709_IOC_DSMUTE_ON); + + debug("Si4709_IOC_DSMUTE_OFF 0x%x", Si4709_IOC_DSMUTE_OFF); + + debug("Si4709_IOC_RESET_RDS_DATA 0x%x", Si4709_IOC_RESET_RDS_DATA); + + debug("------------------------------------------------\n"); +} + +int __init Si4709_driver_init(void) +{ + int ret = 0; + + debug("Si4709_driver_init called\n"); + + /*Initialize the Si4709 dev mutex */ + Si4709_dev_mutex_init(); + + /*misc device registration */ + ret = misc_register(&Si4709_misc_device); + if (ret < 0) { + error("Si4709_driver_init misc_register failed\n"); + return ret; + } + +#if defined(CONFIG_MACH_M0) + if (system_rev >= 15) + Si4709_int = GPIO_FM_INT_REV15; + else +#endif + Si4709_int = GPIO_FM_INT; + + Si4709_irq = gpio_to_irq(Si4709_int); + + irq_set_irq_type(Si4709_irq, IRQ_TYPE_EDGE_FALLING); + /*KGVS: Configuring the GPIO_FM_INT in mach-jupiter.c */ + ret = request_irq(Si4709_irq, Si4709_isr, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Si4709", NULL); + if (ret) { + error("Si4709_driver_init request_irq " + "failed %d", Si4709_int); + goto MISC_DREG; + } else + debug("Si4709_driver_init request_irq " + "success %d", Si4709_int); + + if (gpio_is_valid(GPIO_FM_RST)) { + if (gpio_request(GPIO_FM_RST, "GPC1")) + debug(KERN_ERR "Failed to request " + "FM_RESET!\n\n"); + gpio_direction_output(GPIO_FM_RST, GPIO_LEVEL_LOW); + } + +#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_M0_CTC) + if (gpio_is_valid(GPIO_FM_MIC_SW)) { + if (gpio_request(GPIO_FM_MIC_SW, "GPL0")) + debug(KERN_ERR "Failed to request " + "FM_MIC_SW!\n\n"); + gpio_direction_output(GPIO_FM_RST, GPIO_LEVEL_LOW); + } +#endif + + /*VNVS: 13-OCT'09---- + Initially Pulling the interrupt pin HIGH + as the FM Radio device gives 5ms low pulse*/ + s3c_gpio_setpull(Si4709_int, S3C_GPIO_PULL_UP); + /****Resetting the device****/ + gpio_set_value(GPIO_FM_RST, GPIO_LEVEL_LOW); + s3c_gpio_cfgpin(Si4709_int, S3C_GPIO_OUTPUT); + s3c_gpio_setpull(Si4709_int, S3C_GPIO_PULL_DOWN); + gpio_set_value(GPIO_FM_RST, GPIO_LEVEL_HIGH); + usleep_range(10, 15); + s3c_gpio_cfgpin(Si4709_int, S3C_GPIO_SFN(0xF)); + s3c_gpio_setpull(Si4709_int, S3C_GPIO_PULL_UP); + + gpio_free(FM_RESET); + + /*Add the i2c driver */ + ret = Si4709_i2c_drv_init(); + if (ret < 0) + goto MISC_IRQ_DREG; + + init_waitqueue_head(&Si4709_waitq); + + debug("Si4709_driver_init successful\n"); + + return ret; + +MISC_IRQ_DREG: + free_irq(Si4709_irq, NULL); +MISC_DREG: + misc_deregister(&Si4709_misc_device); + + return ret; +} + +void __exit Si4709_driver_exit(void) +{ + debug("Si4709_driver_exit called\n"); + +#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_M0_CTC) + gpio_free(GPIO_FM_MIC_SW); +#endif + + /*Delete the i2c driver */ + Si4709_i2c_drv_exit(); + free_irq(Si4709_irq, NULL); + + /*misc device deregistration */ + misc_deregister(&Si4709_misc_device); +} + +module_init(Si4709_driver_init); +module_exit(Si4709_driver_exit); +MODULE_AUTHOR("Varun Mahajan <m.varun@samsung.com>"); +MODULE_DESCRIPTION("Si4709 FM tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/samsung/fm_si4709/Si4709_common.h b/drivers/samsung/fm_si4709/Si4709_common.h new file mode 100644 index 0000000..b35f5b8 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_common.h @@ -0,0 +1,49 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <mach/gpio.h> + +/* #define Si4709_DEBUG */ + +#define error(fmt, arg...) printk(KERN_CRIT fmt "\n", ##arg) + +#ifdef Si4709_DEBUG +#define debug(fmt, arg...) printk(KERN_CRIT "--------" fmt "\n", ##arg) +#else +#define debug(fmt, arg...) +#endif + +#define FM_RESET GPIO_FM_RST +#if defined(CONFIG_MACH_Q1_CMCC_BD) +#define FM_PORT "GPX13" +#else +#define FM_PORT "GPB" +#endif + +#define FM_IRQ_INT gpio_to_irq(GPIO_FM_INT) /* GPB1(EXT_INT3[1]) */ + +/* VNVS:28-OCT'09 : For testing FM tune and seek operation status */ +#define TEST_FM + +/* VNVS:7-JUNE'10 : RDS Interrupt ON Always */ +/* (Enabling interrupt when RDS is enabled) */ +#define RDS_INTERRUPT_ON_ALWAYS + +/* VNVS:18-JUN'10 : For testing RDS */ +/* Enable only for debugging RDS */ +/* #define RDS_TESTING */ +#ifdef RDS_TESTING +#define debug_rds(fmt, arg...) printk(KERN_CRIT "--------" fmt "\n", ##arg) +#define GROUP_TYPE_2A (2 * 2 + 0) +#define GROUP_TYPE_2B (2 * 2 + 1) +#else +#define debug_rds(fmt, arg...) +#endif + +#define YES 1 +#define NO 0 + +#endif diff --git a/drivers/samsung/fm_si4709/Si4709_dev.c b/drivers/samsung/fm_si4709/Si4709_dev.c new file mode 100644 index 0000000..32ef932 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_dev.c @@ -0,0 +1,2509 @@ +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> + +#include <mach/gpio.h> +#include <plat/gpio-cfg.h> + +#include "Si4709_regs.h" +#include "Si4709_main.h" +#include "Si4709_dev.h" +#include "Si4709_common.h" + +enum { + eTRUE, + eFALSE, +} dev_struct_status_t; + +/*dev_state*/ +/*power_state*/ +#define RADIO_ON 1 +#define RADIO_POWERDOWN 0 +/*seek_state*/ +#define RADIO_SEEK_ON 1 +#define RADIO_SEEK_OFF 0 + +#define FREQ_87500_kHz 8750 +#define FREQ_76000_kHz 7600 + +#define RSSI_seek_th_MAX 0x7F +#define RSSI_seek_th_MIN 0x00 + +#define seek_SNR_th_DISB 0x00 +#define seek_SNR_th_MIN 0x01 /*most stops */ +#define seek_SNR_th_MAX 0x0F /*fewest stops */ + +#define seek_FM_ID_th_DISB 0x00 +#define seek_FM_ID_th_MAX 0x01 /*most stops */ +#define seek_FM_ID_th_MIN 0x0F /*fewest stops */ + +#define TUNE_RSSI_THRESHOLD 0x00 +#define TUNE_SNR_THRESHOLD 0x01 +#define TUNE_CNT_THRESHOLD 0x00 + +#ifdef RDS_INTERRUPT_ON_ALWAYS +#define RDS_BUFFER_LENGTH 50 +static u16 *RDS_Block_Data_buffer; +static u8 *RDS_Block_Error_buffer; +static u8 RDS_Buffer_Index_read; /* index number for last read data */ +static u8 RDS_Buffer_Index_write; /* index number for last written data */ + +int Si4709_RDS_flag; +int RDS_Data_Available; +int RDS_Data_Lost; +int RDS_Groups_Available_till_now; +struct workqueue_struct *Si4709_wq; +struct work_struct Si4709_work; +#endif + +/*static functions*/ +/**********************************************/ +static void wait(void); + +#ifndef RDS_INTERRUPT_ON_ALWAYS +static void wait_RDS(void); +#endif + +static int powerup(void); +static int powerdown(void); + +static int seek(u32 *, int); +static int tune_freq(u32); + +static void get_cur_chan_freq(u32 *, u16); + +static u16 freq_to_channel(u32); +static u32 channel_to_freq(u16); + +/* static int insert_preset(u32,u8,u8*); */ + +static int i2c_read(u8); +static int i2c_write(u8); +/**********************************************/ + +/*Si4709 device structure*/ +static struct Si4709_device_t Si4709_dev = { + .client = NULL, + .valid = eFALSE, + .valid_client_state = eFALSE, +}; + +/*Wait flag*/ +/*WAITING or WAIT_OVER or NO_WAIT*/ +int Si4709_dev_wait_flag = NO_WAIT; +#ifdef RDS_INTERRUPT_ON_ALWAYS +int Si4709_RDS_flag = NO_WAIT; +#endif + +unsigned int Si4709_dev_int; +unsigned int Si4709_dev_irq; + +int Si4709_dev_init(struct i2c_client *client) +{ + int ret = 0; + + debug("Si4709_dev_init called"); + + mutex_lock(&(Si4709_dev.lock)); + + Si4709_dev.client = client; + + if (system_rev >= 0x7) { + Si4709_dev_int = GPIO_FM_INT_REV07; + Si4709_dev_irq = gpio_to_irq(GPIO_FM_INT_REV07); + } else { + Si4709_dev_int = GPIO_FM_INT; + Si4709_dev_irq = gpio_to_irq(GPIO_FM_INT); + } + + /***reset the device here****/ + gpio_set_value(FM_RESET, GPIO_LEVEL_LOW); + mdelay(1); + gpio_set_value(FM_RESET, GPIO_LEVEL_HIGH); + mdelay(2); + + s3c_gpio_setpull(Si4709_dev_int, S3C_GPIO_PULL_UP); + /* register sleep GPIO setting in c1_sleep_gpio_table[] (mach-c1.c) */ + /* s3c_gpio_slp_setpull_updown(Si4709_dev_int, S3C_GPIO_PULL_UP); */ + + disable_irq(Si4709_dev_irq); + + Si4709_dev.state.power_state = RADIO_POWERDOWN; + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + + ret = i2c_read(BOOTCONFIG); + if (ret < 0) + error("i2c_read failed"); + else + Si4709_dev.valid_client_state = eTRUE; + +#ifdef RDS_INTERRUPT_ON_ALWAYS + /*Creating Circular Buffer */ + /*Single RDS_Block_Data buffer size is 4x16 bits */ + RDS_Block_Data_buffer = kzalloc(RDS_BUFFER_LENGTH * 8, GFP_KERNEL); + if (!RDS_Block_Data_buffer) { + error("Not sufficient memory for creating " + "RDS_Block_Data_buffer"); + ret = -ENOMEM; + goto EXIT; + } + + /*Single RDS_Block_Error buffer size is 4x8 bits */ + RDS_Block_Error_buffer = kzalloc(RDS_BUFFER_LENGTH * 4, GFP_KERNEL); + if (!RDS_Block_Error_buffer) { + error("Not sufficient memory for creating " + "RDS_Block_Error_buffer"); + ret = -ENOMEM; + kfree(RDS_Block_Data_buffer); + goto EXIT; + } + + /*Initialising read and write indices */ + RDS_Buffer_Index_read = 0; + RDS_Buffer_Index_write = 0; + + /*Creating work-queue */ + Si4709_wq = create_singlethread_workqueue("Si4709_wq"); + if (!Si4709_wq) { + error("Not sufficient memory for Si4709_wq, work-queue"); + ret = -ENOMEM; + kfree(RDS_Block_Error_buffer); + kfree(RDS_Block_Data_buffer); + goto EXIT; + } + + /*Initialising work_queue */ + INIT_WORK(&Si4709_work, Si4709_work_func); + + RDS_Data_Available = 0; + RDS_Data_Lost = 0; + RDS_Groups_Available_till_now = 0; +EXIT: +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_init call over"); + + return ret; +} + +int Si4709_dev_exit(void) +{ + int ret = 0; + + debug("Si4709_dev_exit called"); + + mutex_lock(&(Si4709_dev.lock)); + + /* Temporary blocked by abnormal function call(E-CIM 2657654) */ + /* DW Shim. 2010.03.04 */ + /* Si4709_dev.client = NULL; */ + + /* Si4709_dev.valid_client_state = eFALSE; */ + /* Si4709_dev.valid = eFALSE; */ + +#ifdef RDS_INTERRUPT_ON_ALWAYS + if (Si4709_wq) + destroy_workqueue(Si4709_wq); + + kfree(RDS_Block_Error_buffer); + kfree(RDS_Block_Data_buffer); +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_exit call over"); + + return ret; +} + +void Si4709_dev_mutex_init(void) +{ + mutex_init(&(Si4709_dev.lock)); +} + +int Si4709_dev_powerup(void) +{ + int ret = 0; + u32 value = 100; + + debug("Si4709_dev_powerup called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (!(RADIO_ON == Si4709_dev.state.power_state)) { + ret = powerup(); + if (ret < 0) { + error("powerup failed"); + } else if (Si4709_dev.valid_client_state == eFALSE) { + error("Si4709_dev_powerup called " + "when DS(state, client) is invalid"); + ret = -1; + } else { +/* initial settings */ +#ifdef _ENABLE_RDS_ +#if 0 + POWERCFG_BITSET_RDSM_LOW(&Si4709_dev. + registers[POWERCFG]); +#else + POWERCFG_BITSET_RDSM_HIGH(&Si4709_dev. + registers[POWERCFG]); +#endif +#endif + /* POWERCFG_BITSET_SKMODE_HIGH( */ + /* &Si4709_dev.registers[POWERCFG]); */ +/*VNVS:18-NOV'09 : wrap at the upper and lower band limit and continue seeking*/ + POWERCFG_BITSET_SKMODE_LOW(&Si4709_dev. + registers[POWERCFG]); + SYSCONFIG1_BITSET_STCIEN_HIGH(&Si4709_dev. + registers[SYSCONFIG1]); + SYSCONFIG1_BITSET_RDSIEN_LOW(&Si4709_dev. + registers[SYSCONFIG1]); +#ifdef _ENABLE_RDS_ + SYSCONFIG1_BITSET_RDS_HIGH(&Si4709_dev. + registers[SYSCONFIG1]); +#else + SYSCONFIG1_BITSET_RDS_LOW(&Si4709_dev. + registers[SYSCONFIG1]); +#endif +/*VNVS:18-NOV'09 : Setting DE-Time Constant as 50us(Europe,Japan,Australia)*/ + SYSCONFIG1_BITSET_DE_50(&Si4709_dev. + registers[SYSCONFIG1]); + SYSCONFIG1_BITSET_GPIO_STC_RDS_INT(&Si4709_dev. + registers + [SYSCONFIG1]); + SYSCONFIG1_BITSET_RESERVED(&Si4709_dev. + registers[SYSCONFIG1]); + + /* SYSCONFIG2_BITSET_SEEKTH( */ + /* &Si4709_dev.registers[SYSCONFIG2],2); */ +/*VNVS:18-NOV'09 : modified for detecting more stations of good quality*/ + SYSCONFIG2_BITSET_SEEKTH(&Si4709_dev. + registers[SYSCONFIG2], + TUNE_RSSI_THRESHOLD); + SYSCONFIG2_BITSET_VOLUME(&Si4709_dev. + registers[SYSCONFIG2], 0x0F); + SYSCONFIG2_BITSET_BAND_87p5_108_MHz(&Si4709_dev. + registers + [SYSCONFIG2]); + Si4709_dev.settings.band = BAND_87500_108000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_87500_kHz; + + SYSCONFIG2_BITSET_SPACE_100_KHz(&Si4709_dev. + registers[SYSCONFIG2]); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_100_kHz; + + /* SYSCONFIG3_BITSET_SKSNR( */ + /* &Si4709_dev.registers[SYSCONFIG3],3); */ +/*VNVS:18-NOV'09 : modified for detecting more stations of good quality*/ + SYSCONFIG3_BITSET_SKSNR(&Si4709_dev. + registers[SYSCONFIG3], + TUNE_SNR_THRESHOLD); + SYSCONFIG3_BITSET_SKCNT(&Si4709_dev. + registers[SYSCONFIG3], + TUNE_CNT_THRESHOLD); + + SYSCONFIG3_BITSET_RESERVED(&Si4709_dev. + registers[SYSCONFIG3]); + + Si4709_dev.settings.timeout_RDS = + msecs_to_jiffies(value); + Si4709_dev.settings.curr_snr = TUNE_SNR_THRESHOLD; + Si4709_dev.settings.curr_rssi_th = TUNE_RSSI_THRESHOLD; + +/*this will write all the above registers */ + ret = i2c_write(SYSCONFIG3); + if (ret < 0) + error("Si4709_dev_powerup i2c_write 1 failed"); + else { + Si4709_dev.valid = eTRUE; +#ifdef RDS_INTERRUPT_ON_ALWAYS +/*Initialising read and write indices */ + RDS_Buffer_Index_read = 0; + RDS_Buffer_Index_write = 0; + + RDS_Data_Available = 0; + RDS_Data_Lost = 0; + RDS_Groups_Available_till_now = 0; +#endif + } + } + } else + debug("Device already Powered-ON"); + + enable_irq(Si4709_dev_irq); + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_powerdown(void) +{ + int ret = 0; + + msleep(500); /* For avoiding turned off pop noise */ + debug("Si4709_dev_powerdown called"); + + mutex_lock(&(Si4709_dev.lock)); + + disable_irq(Si4709_dev_irq); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_powerdown called when DS is invalid"); + ret = -1; + } else { + ret = powerdown(); + if (ret < 0) + error("powerdown failed"); + } + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_suspend(void) +{ + int ret = 0; + + debug("Si4709_dev_suspend called"); + +#ifndef _ENABLE_RDS_ + disable_irq(Si4709_dev_irq); +#endif + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid_client_state == eFALSE) { + error("Si4709_dev_suspend called " + "when DS(state, client) is invalid"); + ret = -1; + } +#if 0 + else if (Si4709_dev.state.power_state == RADIO_ON) + ret = powerdown(); +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_enable call over"); + + return ret; +} + +int Si4709_dev_resume(void) +{ + int ret = 0; + + debug("Si4709_dev_resume called"); + +#ifndef _ENABLE_RDS_ +#if 0 + s3c_gpio_cfgpin(Si4709_dev_int, S3C_GPIO_SFN(0xF)); + s3c_gpio_setpull(Si4709_dev_int, S3C_GPIO_PULL_UP); + set_irq_type(Si4709_dev_irq, IRQ_TYPE_EDGE_FALLING); +#endif + + enable_irq(Si4709_dev_irq); +#endif + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid_client_state == eFALSE) { + error("Si4709_dev_resume called " + "when DS(state, client) is invalid"); + ret = -1; + } +#if 0 + else if (Si4709_dev.state.power_state == RADIO_POWERDOWN) { + ret = powerup(); + if (ret < 0) + debug("powerup failed"); + } +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + debug("Si4709_dev_disable call over"); + + return ret; +} + +int Si4709_dev_band_set(int band) +{ + int ret = 0; + u16 sysconfig2 = 0; + u16 prev_band = 0; + u32 prev_bottom_of_band = 0; + + debug("Si4709_dev_band_set called"); + + mutex_lock(&(Si4709_dev.lock)); + sysconfig2 = Si4709_dev.registers[SYSCONFIG2]; + prev_band = Si4709_dev.settings.band; + prev_bottom_of_band = Si4709_dev.settings.bottom_of_band; + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_band_set called when DS is invalid"); + ret = -1; + } else { + switch (band) { + case BAND_87500_108000_kHz: + SYSCONFIG2_BITSET_BAND_87p5_108_MHz(&Si4709_dev. + registers + [SYSCONFIG2]); + Si4709_dev.settings.band = BAND_87500_108000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_87500_kHz; + break; + case BAND_76000_108000_kHz: + SYSCONFIG2_BITSET_BAND_76_108_MHz(&Si4709_dev. + registers + [SYSCONFIG2]); + Si4709_dev.settings.band = BAND_76000_108000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_76000_kHz; + break; + case BAND_76000_90000_kHz: + SYSCONFIG2_BITSET_BAND_76_90_MHz(&Si4709_dev. + registers[SYSCONFIG2]); + Si4709_dev.settings.band = BAND_76000_90000_kHz; + Si4709_dev.settings.bottom_of_band = FREQ_76000_kHz; + break; + default: + ret = -1; + } + + if (ret == 0) { + ret = i2c_write(SYSCONFIG2); + if (ret < 0) { + error("Si4709_dev_band_set i2c_write 1 failed"); + Si4709_dev.settings.band = prev_band; + Si4709_dev.settings.bottom_of_band = + prev_bottom_of_band; + Si4709_dev.registers[SYSCONFIG2] = sysconfig2; + } + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_ch_spacing_set(int ch_spacing) +{ + int ret = 0; + u16 sysconfig2 = 0; + u16 prev_ch_spacing = 0; + + debug("Si4709_dev_ch_spacing_set called"); + + mutex_lock(&(Si4709_dev.lock)); + sysconfig2 = Si4709_dev.registers[SYSCONFIG2]; + prev_ch_spacing = Si4709_dev.settings.channel_spacing; + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_ch_spacing_set called " + "when DS is invalid"); + ret = -1; + } else { + switch (ch_spacing) { + case CHAN_SPACING_200_kHz: + SYSCONFIG2_BITSET_SPACE_200_KHz(&Si4709_dev. + registers[SYSCONFIG2]); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_200_kHz; + break; + + case CHAN_SPACING_100_kHz: + SYSCONFIG2_BITSET_SPACE_100_KHz(&Si4709_dev. + registers[SYSCONFIG2]); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_100_kHz; + break; + + case CHAN_SPACING_50_kHz: + SYSCONFIG2_BITSET_SPACE_50_KHz(&Si4709_dev. + registers[SYSCONFIG2]); + Si4709_dev.settings.channel_spacing = + CHAN_SPACING_50_kHz; + break; + + default: + ret = -1; + } + + if (ret == 0) { + ret = i2c_write(SYSCONFIG2); + if (ret < 0) { + error("Si4709_dev_ch_spacing_set " + "i2c_write 1 failed"); + Si4709_dev.settings.channel_spacing = + prev_ch_spacing; + Si4709_dev.registers[SYSCONFIG2] = sysconfig2; + } + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_chan_select(u32 frequency) +{ + int ret = 0; + + debug("Si4709_dev_chan_select called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_chan_select called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + + ret = tune_freq(frequency); + debug("Si4709_dev_chan_select called1"); + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_chan_get(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_chan_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_chan_get called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(READCHAN); + if (ret < 0) + error("Si4709_dev_chan_get i2c_read failed"); + else + get_cur_chan_freq(frequency, + Si4709_dev.registers[READCHAN]); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_up(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_seek_up called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_seek_up called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + + ret = seek(frequency, 1); + + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_down(u32 *frequency) +{ + int ret = 0; + + debug("Si4709_dev_seek_down called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_seek_down called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.state.seek_state = RADIO_SEEK_ON; + + ret = seek(frequency, 0); + + Si4709_dev.state.seek_state = RADIO_SEEK_OFF; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +#if 0 +int Si4709_dev_seek_auto(u32 *seek_preset_user) +{ + u8 *rssi_seek; + int ret = 0; + int i = 0; + int j = 0; + channel_into_t temp; + + debug("Si4709_dev_seek_auto called"); + + if (Si4709_dev.valid == eFALSE) { + debug("Si4709_dev_seek_auto called when DS is invalid"); + ret = -1; + } else { + rssi_seek = kzalloc(sizeof(u8) * NUM_SEEK_PRESETS, + GFP_KERNEL); + if (rssi_seek == NULL) { + debug("Si4709_ioctl: no memory"); + ret = -ENOMEM; + } else { + ret = tune_freq(FREQ_87500_kHz); + if (ret == 0) { + debug("Si4709_dev_seek_auto tune_freq success"); + get_cur_chan_freq(& + (Si4709_dev.rssi_freq[0]. + frequency), + Si4709_dev. + registers[READCHAN]); + Si4709_dev_cur_RSSI_get(& + (Si4709_dev. + rssi_freq[0]. + rsssi_val)); + } else { + debug("tunning failed, seek auto failed"); + ret = -1; + } +#if 0 + for (i = 0; i < 50; i++) { + ret = + seek(&(Si4709_dev.settings. + seek_preset[i]), + 1); + if (ret == 0) { + get_cur_chan_freq(& + (Si4709_dev. + rssi_freq[i]. + frequency), + Si4709_dev. + registers[READCHAN]); + Si4709_dev_cur_RSSI_get(& + (Si4709_dev. + rssi_freq[i]. + rsssi_val)); + rssi_seek++; + } else + debug("seek failed"); + } +#endif + /***new method ****/ + for (i = 1; i < 30; i++) { + ret = seek(&(Si4709_dev.settings. + seek_preset[i]), 1); + if (ret == 0) { + get_cur_chan_freq(& + (Si4709_dev. + rssi_freq[i]. + frequency), + Si4709_dev. + registers[READCHAN]); + Si4709_dev_cur_RSSI_get(& + (Si4709_dev. + rssi_freq[i]. + rsssi_val)); + } else + debug("seek failed"); + } + + /***Sort the array of structures on the basis of RSSI value****/ + for (i = 0; i < 29; i++) { + for (j = i + 1; j < 30; j++) { + if (Si4709_dev.rssi_freq[j].rsssi_val > + Si4709_dev.rssi_freq[i]. + rsssi_val) { + temp = Si4709_dev.rssi_freq[i]; + Si4709_dev.rssi_freq[i] = + Si4709_dev.rssi_freq[j]; + Si4709_dev.rssi_freq[j] = temp; + } + } + } + + /***Store the frequency in Array*****/ + for (i = 0; i < 19; i++) { + Si4709_dev.settings.seek_preset[i] = + Si4709_dev.rssi_freq[i].frequency; + } + } + } + + memcpy(seek_preset_user, Si4709_dev.settings.seek_preset, + sizeof(int) * NUM_SEEK_PRESETS); + kfree(rssi_seek); + + return ret; +} +#endif + +int Si4709_dev_RSSI_seek_th_set(u8 seek_th) +{ + int ret = 0; + u16 sysconfig2 = 0; + + debug("Si4709_dev_RSSI_seek_th_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig2 = Si4709_dev.registers[SYSCONFIG2]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RSSI_seek_th_set called " + "when DS is invalid"); + ret = -1; + } else { + SYSCONFIG2_BITSET_SEEKTH(&Si4709_dev.registers[SYSCONFIG2], + seek_th); + Si4709_dev.settings.curr_rssi_th = seek_th; + + ret = i2c_write(SYSCONFIG2); + if (ret < 0) { + error("Si4709_dev_RSSI_seek_th_set i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG2] = sysconfig2; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_SNR_th_set(u8 seek_SNR) +{ + int ret = 0; + u16 sysconfig3 = 0; + + debug("Si4709_dev_seek_SNR_th_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig3 = Si4709_dev.registers[SYSCONFIG3]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_seek_SNR_th_set called " + "when DS is invalid"); + ret = -1; + } else { + SYSCONFIG3_BITSET_SKSNR(&Si4709_dev.registers[SYSCONFIG3], + seek_SNR); + SYSCONFIG3_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG3]); + Si4709_dev.settings.curr_snr = seek_SNR; + + ret = i2c_write(SYSCONFIG3); + if (ret < 0) { + error("Si4709_dev_seek_SNR_th_set i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG3] = sysconfig3; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_seek_FM_ID_th_set(u8 seek_FM_ID_th) +{ + int ret = 0; + u16 sysconfig3 = 0; + + debug("Si4709_dev_seek_FM_ID_th_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig3 = Si4709_dev.registers[SYSCONFIG3]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_seek_SNR_th_set called " + "when DS is invalid"); + ret = -1; + } else { + SYSCONFIG3_BITSET_SKCNT(&Si4709_dev.registers[SYSCONFIG3], + seek_FM_ID_th); + SYSCONFIG3_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG3]); + + ret = i2c_write(SYSCONFIG3); + if (ret < 0) { + error("Si4709_dev_seek_FM_ID_th_set i2c_write " + "1 failed"); + sysconfig3 = Si4709_dev.registers[SYSCONFIG3]; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_cur_RSSI_get(struct rssi_snr_t *cur_RSSI) +{ + int ret = 0; + + debug("Si4709_dev_cur_RSSI_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_cur_RSSI_get called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(STATUSRSSI); + if (ret < 0) { + error("Si4709_dev_cur_RSSI_get i2c_read 1 failed"); + } else { + cur_RSSI->curr_rssi = + STATUSRSSI_RSSI_SIGNAL_STRENGTH(Si4709_dev. + registers + [STATUSRSSI]); + cur_RSSI->curr_rssi_th = + Si4709_dev.settings.curr_rssi_th; + cur_RSSI->curr_snr = Si4709_dev.settings.curr_snr; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* VNVS:START 13-OCT'09 : + Functions which reads device-id,chip-id,power configuration, + system configuration2 registers + */ +int Si4709_dev_device_id(struct device_id *dev_id) +{ + int ret = 0; + + debug("Si4709_dev_device_id called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_device_id called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(DEVICE_ID); + if (ret < 0) { + error("Si4709_dev_device_id i2c_read failed"); + } else { + dev_id->part_number = + DEVICE_ID_PART_NUMBER(Si4709_dev. + registers[DEVICE_ID]); + dev_id->manufact_number = + DEVICE_ID_MANUFACT_NUMBER(Si4709_dev. + registers[DEVICE_ID]); + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_chip_id(struct chip_id *chp_id) +{ + int ret = 0; + + debug("Si4709_dev_chip_id called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_chip_id called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(CHIP_ID); + if (ret < 0) { + error("Si4709_dev_chip_id i2c_read failed"); + } else { + chp_id->chip_version = + CHIP_ID_CHIP_VERSION( + Si4709_dev.registers[CHIP_ID]); + chp_id->device = + CHIP_ID_DEVICE(Si4709_dev.registers[CHIP_ID]); + chp_id->firmware_version = + CHIP_ID_FIRMWARE_VERSION(Si4709_dev. + registers[CHIP_ID]); + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config2(struct sys_config2 *sys_conf2) +{ + int ret = 0; + + debug("Si4709_sys_config2 called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_sys_config2 called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(SYSCONFIG2); + if (ret < 0) { + error("Si4709_sys_config2 i2c_read failed"); + } else { + sys_conf2->rssi_th = + SYS_CONFIG2_RSSI_TH(Si4709_dev. + registers[SYSCONFIG2]); + sys_conf2->fm_band = + SYS_CONFIG2_FM_BAND(Si4709_dev. + registers[SYSCONFIG2]); + sys_conf2->fm_chan_spac = + SYS_CONFIG2_FM_CHAN_SPAC(Si4709_dev. + registers[SYSCONFIG2]); + sys_conf2->fm_vol = + SYS_CONFIG2_FM_VOL(Si4709_dev. + registers[SYSCONFIG2]); + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config3(struct sys_config3 *sys_conf3) +{ + int ret = 0; + + debug("Si4709_sys_config3 called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_sys_config3 called when DS is invalid"); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + ret = i2c_read(SYSCONFIG3); + if (ret < 0) { + error("Si4709_sys_config3 i2c_read failed"); + } else { + sys_conf3->smmute = + (Si4709_dev.registers[SYSCONFIG3] & 0xC000) >> 14; + sys_conf3->smutea = + (Si4709_dev.registers[SYSCONFIG3] & 0x3000) >> 12; + sys_conf3->volext = + (Si4709_dev.registers[SYSCONFIG3] & 0x0100) >> 8; + sys_conf3->sksnr = + (Si4709_dev.registers[SYSCONFIG3] & 0x00F0) >> 4; + sys_conf3->skcnt = + (Si4709_dev.registers[SYSCONFIG3] & 0x000F); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_status_rssi(struct status_rssi *status) +{ + int ret = 0; + + debug("Si4709_dev_status_rssi called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_status_rssi called when DS is invalid"); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + ret = i2c_read(STATUSRSSI); + if (ret < 0) { + error("Si4709_sys_config3 i2c_read failed"); + } else { + status->rdsr = + (Si4709_dev.registers[STATUSRSSI] & 0x8000) >> 15; + status->stc = + (Si4709_dev.registers[STATUSRSSI] & 0x4000) >> 14; + status->sfbl = + (Si4709_dev.registers[STATUSRSSI] & 0x2000) >> 13; + status->afcrl = + (Si4709_dev.registers[STATUSRSSI] & 0x1000) >> 12; + status->rdss = + (Si4709_dev.registers[STATUSRSSI] & 0x0800) >> 11; + status->blera = + (Si4709_dev.registers[STATUSRSSI] & 0x0600) >> 9; + status->st = + (Si4709_dev.registers[STATUSRSSI] & 0x0100) >> 8; + status->rssi = + (Si4709_dev.registers[STATUSRSSI] & 0x00FF); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config2_set(struct sys_config2 *sys_conf2) +{ + int ret = 0; + u16 register_bak = 0; + + debug("Si4709_dev_sys_config2_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + register_bak = Si4709_dev.registers[SYSCONFIG2]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_sys_config2_set called when DS is invalid"); + ret = -1; + } else { + printk(KERN_ERR "Si4709_dev_sys_config2_set() " + ": Register Value = [0x%X], rssi-th = [%X]\n", + Si4709_dev.registers[SYSCONFIG2], + sys_conf2->rssi_th); + Si4709_dev.registers[SYSCONFIG2] = + (Si4709_dev.registers[SYSCONFIG2] & 0x00FF) | + ((sys_conf2->rssi_th) << 8); + Si4709_dev.registers[SYSCONFIG2] = + (Si4709_dev.registers[SYSCONFIG2] & 0xFF3F) | + ((sys_conf2->fm_band) << 6); + Si4709_dev.registers[SYSCONFIG2] = + (Si4709_dev.registers[SYSCONFIG2] & 0xFFCF) | + ((sys_conf2->fm_chan_spac) << 4); + Si4709_dev.registers[SYSCONFIG2] = + (Si4709_dev.registers[SYSCONFIG2] & 0xFFF0) | + (sys_conf2->fm_vol); + printk(KERN_ERR "Si4709_dev_sys_config2_set() " + ": After Register Value = [0x%X]\n", + Si4709_dev.registers[SYSCONFIG2]); + + ret = i2c_write(SYSCONFIG2); + if (ret < 0) { + error("Si4709_dev_sys_config2_set i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG2] = register_bak; + } else + printk(KERN_ERR "Si4709_dev_sys_config2_set() " + ": Write Sucess!!"); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_sys_config3_set(struct sys_config3 *sys_conf3) +{ + int ret = 0; + u16 register_bak = 0; + + debug("Si4709_dev_sys_config3_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + register_bak = Si4709_dev.registers[SYSCONFIG3]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_sys_config3_set called " + "when DS is invalid"); + ret = -1; + } else { + printk(KERN_ERR "Si4709_dev_sys_config3_set() : " + "Register Value = [0x%X], sksnrth = [%X]\n", + Si4709_dev.registers[SYSCONFIG3], + sys_conf3->sksnr); + Si4709_dev.registers[SYSCONFIG3] = + (Si4709_dev.registers[SYSCONFIG3] & 0x3FFF) | + ((sys_conf3->smmute) << 14); + Si4709_dev.registers[SYSCONFIG3] = + (Si4709_dev.registers[SYSCONFIG3] & 0xCFFF) | + ((sys_conf3->smutea) << 12); + Si4709_dev.registers[SYSCONFIG3] = + (Si4709_dev.registers[SYSCONFIG3] & 0xFEFF) | + ((sys_conf3->volext) << 8); + Si4709_dev.registers[SYSCONFIG3] = + (Si4709_dev.registers[SYSCONFIG3] & 0xFF0F) | + ((sys_conf3->sksnr) << 4); + Si4709_dev.registers[SYSCONFIG3] = + (Si4709_dev.registers[SYSCONFIG3] & 0xFFF0) | + (sys_conf3->skcnt); + printk(KERN_ERR "Si4709_dev_sys_config3_set() : " + "After Register Value = [0x%X]\n", + Si4709_dev.registers[SYSCONFIG3]); + + ret = i2c_write(SYSCONFIG3); + if (ret < 0) { + error("Si4709_dev_sys_config3_set i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG3] = register_bak; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_power_config(struct power_config *pow_conf) +{ + int ret = 0; + + debug("Si4709_dev_power_config called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_power_config called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(POWERCFG); + if (ret < 0) + error("Si4709_dev_power_config i2c_read failed"); + else { + pow_conf->dsmute = + POWER_CONFIG_SOFTMUTE_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->dmute = + POWER_CONFIG_MUTE_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->mono = + POWER_CONFIG_MONO_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->rds_mode = + POWER_CONFIG_RDS_MODE_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->sk_mode = + POWER_CONFIG_SKMODE_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->seek_up = + POWER_CONFIG_SEEKUP_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->seek = + POWER_CONFIG_SEEK_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->power_disable = + POWER_CONFIG_DISABLE_STATUS(Si4709_dev. + registers[POWERCFG]); + pow_conf->power_enable = + POWER_CONFIG_ENABLE_STATUS(Si4709_dev. + registers[POWERCFG]); + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* VNVS:END */ + +/* VNVS:START 18-NOV'09 */ +/* Reading AFCRL Bit */ +int Si4709_dev_AFCRL_get(u8 *afc) +{ + int ret = 0; + + debug("Si4709_dev_AFCRL_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_AFCRL_get called when DS is invalid"); + ret = -1; + } else { + ret = i2c_read(STATUSRSSI); + if (ret < 0) + error("Si4709_dev_AFCRL_get i2c_read failed"); + *afc = + STATUSRSSI_AFC_RAIL_STATUS(Si4709_dev. + registers[STATUSRSSI]); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* Setting DE + emphasis time constant 50us(Europe,Japan,Australia) or 75us(USA) + */ +int Si4709_dev_DE_set(u8 de_tc) +{ + u16 sysconfig1 = 0; + int ret = 0; + + debug("Si4709_dev_DE_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_DE_set called when DS is invalid"); + ret = -1; + } else { + switch (de_tc) { + case DE_TIME_CONSTANT_50: + SYSCONFIG1_BITSET_DE_50(&Si4709_dev. + registers[SYSCONFIG1]); + SYSCONFIG1_BITSET_RESERVED(&Si4709_dev. + registers[SYSCONFIG1]); + break; + + case DE_TIME_CONSTANT_75: + SYSCONFIG1_BITSET_DE_75(&Si4709_dev. + registers[SYSCONFIG1]); + SYSCONFIG1_BITSET_RESERVED(&Si4709_dev. + registers[SYSCONFIG1]); + break; + + default: + ret = -1; + } + + if (0 == ret) { + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + error("Si4709_dev_DE_set i2c_write failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/*Resetting the RDS Data Buffer*/ +int Si4709_dev_reset_rds_data() +{ + int ret = 0; + + debug_rds("Si4709_dev_reset_rds_data called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_reset_rds_data called when DS is invalid"); + ret = -1; + } else { + RDS_Buffer_Index_write = 0; + RDS_Buffer_Index_read = 0; + RDS_Data_Lost = 0; + RDS_Data_Available = 0; + memset(RDS_Block_Data_buffer, 0, RDS_BUFFER_LENGTH * 8); + memset(RDS_Block_Error_buffer, 0, RDS_BUFFER_LENGTH * 4); + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/*VNVS:END*/ + +int Si4709_dev_VOLEXT_ENB(void) +{ + int ret = 0; + u16 sysconfig3 = 0; + + debug("Si4709_dev_VOLEXT_ENB called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig3 = Si4709_dev.registers[SYSCONFIG3]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_VOLEXT_ENB called when DS is invalid"); + ret = -1; + } else { + SYSCONFIG3_BITSET_VOLEXT_ENB(&Si4709_dev.registers[SYSCONFIG3]); + SYSCONFIG3_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG3]); + + ret = i2c_write(SYSCONFIG3); + if (ret < 0) { + error("Si4709_dev_VOLEXT_ENB i2c_write failed"); + Si4709_dev.registers[SYSCONFIG3] = sysconfig3; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_VOLEXT_DISB(void) +{ + int ret = 0; + u16 sysconfig3 = 0; + + debug("Si4709_dev_VOLEXT_DISB called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig3 = Si4709_dev.registers[SYSCONFIG3]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_VOLEXT_DISB called when DS is invalid"); + ret = -1; + } else { + SYSCONFIG3_BITSET_VOLEXT_DISB(&Si4709_dev. + registers[SYSCONFIG3]); + SYSCONFIG3_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG3]); + + ret = i2c_write(SYSCONFIG3); + if (ret < 0) { + error("Si4709_dev_VOLEXT_DISB i2c_write failed"); + Si4709_dev.registers[SYSCONFIG3] = sysconfig3; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_volume_set(u8 volume) +{ + int ret = 0; + u16 sysconfig2 = 0; + + debug("Si4709_dev_volume_set called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig2 = Si4709_dev.registers[SYSCONFIG2]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_volume_set called when DS is invalid"); + ret = -1; + } else { + SYSCONFIG2_BITSET_VOLUME(&Si4709_dev.registers[SYSCONFIG2], + volume); + + ret = i2c_write(SYSCONFIG2); + if (ret < 0) { + error("Si4709_dev_volume_set i2c_write failed"); + Si4709_dev.registers[SYSCONFIG2] = sysconfig2; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_volume_get(u8 *volume) +{ + int ret = 0; + + debug("Si4709_dev_volume_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_volume_get called when DS is invalid"); + ret = -1; + } else + *volume = + SYSCONFIG2_GET_VOLUME(Si4709_dev.registers[SYSCONFIG2]); + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* +VNVS:START 19-AUG'10 : Adding DSMUTE ON/OFF feature. +The Soft Mute feature is available to attenuate the audio +outputs and minimize audible noise in very weak signal conditions. + */ +int Si4709_dev_DSMUTE_ON(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_DSMUTE_ON called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_DSMUTE_ON called when DS is invalid"); + ret = -1; + } else { + POWERCFG_BITSET_DSMUTE_LOW(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("Si4709_dev_DSMUTE_ON i2c_write failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_DSMUTE_OFF(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_DSMUTE_OFF called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_DSMUTE_OFF called when DS is invalid"); + ret = -1; + } else { + POWERCFG_BITSET_DSMUTE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("Si4709_dev_DSMUTE_OFF i2c_write failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/*VNVS:END*/ + +int Si4709_dev_MUTE_ON(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_MUTE_ON called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_MUTE_ON called when DS is invalid"); + ret = -1; + } else { + POWERCFG_BITSET_DMUTE_LOW(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("Si4709_dev_MUTE_ON i2c_write failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_MUTE_OFF(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_MUTE_OFF called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_MUTE_OFF called when DS is invalid"); + ret = -1; + } else { + POWERCFG_BITSET_DMUTE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("Si4709_dev_MUTE_OFF i2c_write failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_MONO_SET(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_MONO_SET called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_MONO_SET called when DS is invalid"); + ret = -1; + } else { + POWERCFG_BITSET_MONO_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("Si4709_dev_MONO_SET i2c_write failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_STEREO_SET(void) +{ + int ret = 0; + u16 powercfg = 0; + + debug("Si4709_dev_STEREO_SET called"); + + mutex_lock(&(Si4709_dev.lock)); + + powercfg = Si4709_dev.registers[POWERCFG]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_STEREO_SET called when DS is invalid"); + ret = -1; + } else { + POWERCFG_BITSET_MONO_LOW(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("Si4709_dev_STEREO_SET i2c_write failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RDS_ENABLE(void) +{ + u16 sysconfig1 = 0; + int ret = 0; + + debug("Si4709_dev_RDS_ENABLE called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_ENABLE called when DS is invalid"); + ret = -1; + } else { + SYSCONFIG1_BITSET_RDS_HIGH(&Si4709_dev.registers[SYSCONFIG1]); +#ifdef RDS_INTERRUPT_ON_ALWAYS + SYSCONFIG1_BITSET_RDSIEN_HIGH(&Si4709_dev. + registers[SYSCONFIG1]); +#endif + SYSCONFIG1_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG1]); + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + error("Si4709_dev_RDS_ENABLE i2c_write failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } +#ifdef RDS_INTERRUPT_ON_ALWAYS + else + Si4709_RDS_flag = RDS_WAITING; +#endif + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RDS_DISABLE(void) +{ + u16 sysconfig1 = 0; + int ret = 0; + + debug("Si4709_dev_RDS_DISABLE called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_DISABLE called when DS is invalid"); + ret = -1; + } else { + SYSCONFIG1_BITSET_RDS_LOW(&Si4709_dev.registers[SYSCONFIG1]); +#ifdef RDS_INTERRUPT_ON_ALWAYS + SYSCONFIG1_BITSET_RDSIEN_LOW(&Si4709_dev.registers[SYSCONFIG1]); +#endif + SYSCONFIG1_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG1]); + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + error("Si4709_dev_RDS_DISABLE i2c_write failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } +#ifdef RDS_INTERRUPT_ON_ALWAYS + else + Si4709_RDS_flag = NO_WAIT; +#endif + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_rstate_get(struct dev_state_t *dev_state) +{ + int ret = 0; + + debug("Si4709_dev_rstate_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_rstate_get called when DS is invalid"); + ret = -1; + } else { + dev_state->power_state = Si4709_dev.state.power_state; + dev_state->seek_state = Si4709_dev.state.seek_state; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/* VNVS:START 7-JUNE'10 Function call for work-queue "Si4709_wq" */ +#ifdef RDS_INTERRUPT_ON_ALWAYS +void Si4709_work_func(struct work_struct *work) +{ + int i, ret = 0; +#ifdef RDS_TESTING + u8 group_type; +#endif + debug_rds("%s", __func__); +/* mutex_lock(&(Si4709_dev.lock)); */ + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_data_get called when DS is invalid"); + return; + } + + if (RDS_Data_Lost > 1) + debug_rds("No_of_RDS_groups_Lost till now : %d", + RDS_Data_Lost); + +/* RDSR bit and RDS Block data, so reading the RDS registers */ + ret = i2c_read(RDSD); + if (ret < 0) { + error("Si4709_work_func i2c_read failed"); + return; + } + +/*Checking whether RDS Ready bit is set or not, if not set return immediately*/ + if (!(STATUSRSSI_RDS_READY_STATUS(Si4709_dev.registers[STATUSRSSI]))) { + error("RDS Ready Bit Not set"); + return; + } + + debug_rds("RDS Ready bit is set"); + + debug_rds("No_of_RDS_groups_Available : %d", RDS_Data_Available); + + RDS_Data_Available = 0; + + debug_rds("RDS_Buffer_Index_write = %d", + RDS_Buffer_Index_write); + +/* Writing into the Circular Buffer */ + +/* Writing into RDS_Block_Data_buffer */ + i = 0; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + Si4709_dev.registers[RDSA]; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + Si4709_dev.registers[RDSB]; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + Si4709_dev.registers[RDSC]; + RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] = + Si4709_dev.registers[RDSD]; + +/*Writing into RDS_Block_Error_buffer */ + i = 0; + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + STATUSRSSI_RDS_BLOCK_A_ERRORS( + Si4709_dev.registers[STATUSRSSI]); + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + READCHAN_BLOCK_B_ERRORS( + Si4709_dev.registers[READCHAN]); + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + READCHAN_BLOCK_C_ERRORS( + Si4709_dev.registers[READCHAN]); + RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] = + READCHAN_BLOCK_D_ERRORS(Si4709_dev.registers[READCHAN]); + +#ifdef RDS_TESTING + if (RDS_Block_Error_buffer + [0 + 4 * RDS_Buffer_Index_write] < 3) { + debug_rds("PI Code is %d", + RDS_Block_Data_buffer[0 + 4 + * RDS_Buffer_Index_write]); + } + if (RDS_Block_Error_buffer + [1 + 4 * RDS_Buffer_Index_write] < 2) { + group_type = RDS_Block_Data_buffer[1 + 4 + * RDS_Buffer_Index_write] >> 11; + + if (group_type & 0x01) { + debug_rds("PI Code is %d", + RDS_Block_Data_buffer[2 + 4 + * RDS_Buffer_Index_write]); + } + if (group_type == GROUP_TYPE_2A + || group_type == GROUP_TYPE_2B) { + if (RDS_Block_Error_buffer + [2 + 4 * RDS_Buffer_Index_write] < 3) { + debug_rds("Update RT with RDSC"); + } else { + debug_rds("RDS_Block_Error_buffer" + " of Block C is greater than 3"); + } + } + } +#endif + RDS_Buffer_Index_write++; + + if (RDS_Buffer_Index_write >= RDS_BUFFER_LENGTH) + RDS_Buffer_Index_write = 0; + + debug_rds("RDS_Buffer_Index_write = %d", RDS_Buffer_Index_write); + /* mutex_unlock(&(Si4709_dev.lock)); */ +} +#endif +/*VNVS:END*/ + +int Si4709_dev_RDS_data_get(struct radio_data_t *data) +{ + int i, ret = 0; + u16 sysconfig1 = 0; + + debug_rds("Si4709_dev_RDS_data_get called"); + + mutex_lock(&(Si4709_dev.lock)); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_data_get called when DS is invalid"); + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } +#ifdef RDS_INTERRUPT_ON_ALWAYS + debug_rds("RDS_Buffer_Index_read = %d", RDS_Buffer_Index_read); + + /*If No New RDS Data is available return error */ + if (RDS_Buffer_Index_read == RDS_Buffer_Index_write) { + error("No_New_RDS_Data_is_available"); + ret = i2c_read(READCHAN); + if (ret < 0) + error("Si4709_dev_RDS_data_get i2c_read 1 failed"); + else { + get_cur_chan_freq(&(data->curr_channel), + Si4709_dev.registers[READCHAN]); + data->curr_rssi = STATUSRSSI_RSSI_SIGNAL_STRENGTH( + Si4709_dev.registers[STATUSRSSI]); + debug_rds("curr_channel: %u, curr_rssi:%u", + data->curr_channel, + (u32) data->curr_rssi); + } + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + ret = i2c_read(READCHAN); + + if (ret < 0) + error("Si4709_dev_RDS_data_get i2c_read 2 failed"); + else { + get_cur_chan_freq(&(data->curr_channel), + Si4709_dev.registers[READCHAN]); + data->curr_rssi = + STATUSRSSI_RSSI_SIGNAL_STRENGTH( + Si4709_dev.registers[STATUSRSSI]); + debug_rds("curr_channel: %u, curr_rssi:%u", + data->curr_channel, (u32) data->curr_rssi); + + /* Reading from RDS_Block_Data_buffer */ + i = 0; + data->rdsa = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->rdsb = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->rdsc = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->rdsd = RDS_Block_Data_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + + /* Reading from RDS_Block_Error_buffer */ + i = 0; + data->blera = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->blerb = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->blerc = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + data->blerd = RDS_Block_Error_buffer[i++ + 4 + * RDS_Buffer_Index_read]; + + /*Flushing the read data */ + memset(&RDS_Block_Data_buffer[0 + 4 * RDS_Buffer_Index_read], + 0, 8); + memset(&RDS_Block_Error_buffer[0 + 4 * RDS_Buffer_Index_read], + 0, 4); + + RDS_Buffer_Index_read++; + + if (RDS_Buffer_Index_read >= RDS_BUFFER_LENGTH) + RDS_Buffer_Index_read = 0; + } + + debug_rds("RDS_Buffer_Index_read = %d", RDS_Buffer_Index_read); +#else + SYSCONFIG1_BITSET_RDSIEN_HIGH(&Si4709_dev.registers[SYSCONFIG1]); + + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + error("Si4709_dev_RDS_data_get i2c_write 1 failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } else { + ret = i2c_read(SYSCONFIG1); + if (ret < 0) + error("Si4709_dev_RDS_data_get i2c_read 1 failed"); + + debug("sysconfig1: 0x%x", Si4709_dev.registers[SYSCONFIG1]); + + sysconfig1 = Si4709_dev.registers[SYSCONFIG1]; + + Si4709_dev_wait_flag = RDS_WAITING; + + wait_RDS(); + + ret = i2c_read(STATUSRSSI); + if (ret < 0) + error("Si4709_dev_RDS_data_get i2c_read 2 failed"); + + debug("statusrssi: 0x%x", Si4709_dev.registers[STATUSRSSI]); + + SYSCONFIG1_BITSET_RDSIEN_LOW(&Si4709_dev.registers[SYSCONFIG1]); + + ret = i2c_write(SYSCONFIG1); + if (ret < 0) { + error("Si4709_dev_RDS_data_get i2c_write 2 failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + } else if (Si4709_dev_wait_flag == WAIT_OVER) { + Si4709_dev_wait_flag = NO_WAIT; + + ret = i2c_read(RDSD); + if (ret < 0) + error("Si4709_dev_RDS_data_get " + "i2c_read 3 failed"); + else { + data->rdsa = Si4709_dev.registers[RDSA]; + data->rdsb = Si4709_dev.registers[RDSB]; + data->rdsc = Si4709_dev.registers[RDSC]; + data->rdsd = Si4709_dev.registers[RDSD]; + + get_cur_chan_freq(&(data->curr_channel), + Si4709_dev.registers[READCHAN]); + debug("curr_channel: %u", data->curr_channel); + data->curr_rssi = + STATUSRSSI_RSSI_SIGNAL_STRENGTH + (Si4709_dev.registers[STATUSRSSI]); + debug("curr_rssi:%u", (u32)data->curr_rssi); + data->blera = + STATUSRSSI_RDS_BLOCK_A_ERRORS + (Si4709_dev.registers[STATUSRSSI]); + data->blerb = + READCHAN_BLOCK_B_ERRORS(Si4709_dev. + registers[READCHAN]); + data->blerc = + READCHAN_BLOCK_C_ERRORS(Si4709_dev. + registers[READCHAN]); + data->blerd = + READCHAN_BLOCK_D_ERRORS(Si4709_dev. + registers[READCHAN]); + } + } else { + error("Si4709_dev_RDS_data_get failure " + "no interrupt or timeout"); + Si4709_dev_wait_flag = NO_WAIT; + mutex_unlock(&(Si4709_dev.lock)); + return -1; + } + } +#endif + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +int Si4709_dev_RDS_timeout_set(u32 time_out) +{ + int ret = 0; + u32 jiffy_count = 0; + + debug("Si4709_dev_RDS_timeout_set called"); + /****convert time_out(in milliseconds) into jiffies*****/ + + jiffy_count = msecs_to_jiffies(time_out); + + debug("jiffy_count%d", jiffy_count); + + mutex_lock(&(Si4709_dev.lock)); + + if (Si4709_dev.valid == eFALSE) { + error("Si4709_dev_RDS_timeout_set called when DS is invalid"); + ret = -1; + } else { + Si4709_dev.settings.timeout_RDS = jiffy_count; + } + + mutex_unlock(&(Si4709_dev.lock)); + + return ret; +} + +/**************************************************************/ +static int powerup(void) +{ + int ret = 0; + u16 powercfg = Si4709_dev.registers[POWERCFG]; + int reg; + /****Resetting the device****/ + + gpio_set_value(FM_RESET, GPIO_LEVEL_LOW); + gpio_set_value(FM_RESET, GPIO_LEVEL_HIGH); + +#if 0 + /* Add the i2c driver */ + ret = Si4709_i2c_drv_init(); + if (ret < 0) + debug("Si4709_i2c_drv_init failed"); +#endif + + /* Resetting the Si4709_dev.registers[] array */ + for (reg = 0; reg < NUM_OF_REGISTERS; reg++) + Si4709_dev.registers[reg] = 0; + + debug("Resetting the Si4709_dev.registers[] array"); + + POWERCFG_BITSET_DMUTE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_ENABLE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_DISABLE_LOW(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("powerup->i2c_write 1 failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } else { + /* Si4709/09 datasheet: Table 7 */ + mdelay(110); + Si4709_dev.state.power_state = RADIO_ON; + } + + return ret; +} + +static int powerdown(void) +{ + int ret = 0; + u16 test1 = Si4709_dev.registers[TEST1], + sysconfig1 = Si4709_dev.registers[SYSCONFIG1], + powercfg = Si4709_dev.registers[POWERCFG]; + + if (!(RADIO_POWERDOWN == Si4709_dev.state.power_state)) { + /* TEST1_BITSET_AHIZEN_HIGH( &Si4709_dev.registers[TEST1] ); */ + /* TEST1_BITSET_RESERVED( &Si4709_dev.registers[TEST1] ); */ + + SYSCONFIG1_BITSET_GPIO_LOW(&Si4709_dev.registers[SYSCONFIG1]); + SYSCONFIG1_BITSET_RESERVED(&Si4709_dev.registers[SYSCONFIG1]); + /*VNVS: 13-OCT'09 : + During Powerdown of the device RDS should be disabled + according to the Si4708/09 datasheet + */ +#ifdef _ENABLE_RDS_ + SYSCONFIG1_BITSET_RDS_LOW(&Si4709_dev.registers[SYSCONFIG1]); +#endif + POWERCFG_BITSET_DMUTE_LOW(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_ENABLE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_DISABLE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + /*this will write all the above registers */ + ret = i2c_write(TEST1); + if (ret < 0) { + error("powerdown->i2c_write failed"); + Si4709_dev.registers[SYSCONFIG1] = sysconfig1; + Si4709_dev.registers[POWERCFG] = powercfg; + Si4709_dev.registers[TEST1] = test1; + } else { + Si4709_dev.state.power_state = RADIO_POWERDOWN; + } + + /****Resetting the device****/ + gpio_set_value(FM_RESET, GPIO_LEVEL_LOW); + gpio_set_value(FM_RESET, GPIO_LEVEL_HIGH); + gpio_set_value(FM_RESET, GPIO_LEVEL_LOW); + } else + debug("Device already Powered-OFF"); + + return ret; +} + +static int seek(u32 *frequency, int up) +{ + int ret = 0; + u16 powercfg = Si4709_dev.registers[POWERCFG]; + u16 channel = 0; + int valid_station_found = 0; + + if (up) + POWERCFG_BITSET_SEEKUP_HIGH(&Si4709_dev.registers[POWERCFG]); + else + POWERCFG_BITSET_SEEKUP_LOW(&Si4709_dev.registers[POWERCFG]); + + POWERCFG_BITSET_SKMODE_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_SEEK_HIGH(&Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("seek i2c_write 1 failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } else { + Si4709_dev_wait_flag = SEEK_WAITING; + + wait(); + + if (Si4709_dev_wait_flag == SEEK_CANCEL) { + powercfg = Si4709_dev.registers[POWERCFG]; + POWERCFG_BITSET_SEEK_LOW(&Si4709_dev. + registers[POWERCFG]); + POWERCFG_BITSET_RESERVED(&Si4709_dev. + registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("seek i2c_write 2 failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } + + ret = i2c_read(READCHAN); + if (ret < 0) + error("seek i2c_read 1 failed"); + else { + channel = READCHAN_GET_CHAN(Si4709_dev. + registers[READCHAN]); + *frequency = channel_to_freq(channel); + } + *frequency = 0; + } + + Si4709_dev_wait_flag = NO_WAIT; + + ret = i2c_read(STATUSRSSI); + if (ret < 0) + error("seek i2c_read 2 failed"); + else { +/* VNVS:START 13-OCT'09 : Checking the status of Seek/Tune Bit */ +#ifdef TEST_FM + if (STATUSRSSI_SEEK_TUNE_STATUS + (Si4709_dev.registers[STATUSRSSI]) + == COMPLETE) { + debug("Seek/Tune Status is set to 1 by device"); + if (STATUSRSSI_SF_BL_STATUS + (Si4709_dev.registers[STATUSRSSI]) == + SEEK_SUCCESSFUL) { + debug("Seek Fail/Band Limit Status is " + "set to 0 by device ---" + "SeekUp Operation Completed"); + valid_station_found = 1; + } else + debug("Seek Fail/Band Limit Status is " + "set to 1 by device ---" + "SeekUp Operation " + "Not Completed"); + } else + debug("Seek/Tune Status is set to 0 by device " + "---SeekUp Operation " + "Not Completed"); +#endif + /* VNVS:END */ + + powercfg = Si4709_dev.registers[POWERCFG]; + + POWERCFG_BITSET_SEEK_LOW( + &Si4709_dev.registers[POWERCFG]); + POWERCFG_BITSET_RESERVED( + &Si4709_dev.registers[POWERCFG]); + + ret = i2c_write(POWERCFG); + if (ret < 0) { + error("seek i2c_write 2 failed"); + Si4709_dev.registers[POWERCFG] = powercfg; + } else { + do { + ret = i2c_read(STATUSRSSI); + if (ret < 0) { + error("seek i2c_read 3 failed"); + break; + } + } while (STATUSRSSI_SEEK_TUNE_STATUS + (Si4709_dev. + registers[STATUSRSSI]) != + CLEAR); + + if (ret == 0 && valid_station_found == 1) { + ret = i2c_read(READCHAN); + if (ret < 0) + error("seek i2c_read 4 failed"); + else { + channel = + READCHAN_GET_CHAN + (Si4709_dev. + registers[READCHAN]); + *frequency = + channel_to_freq + (channel); + debug("Frequency after seek-up " + "is %d\n", *frequency); + } + } else + debug("Valid station not found\n"); + } + } + } + + return ret; +} + +static int tune_freq(u32 frequency) +{ + int ret = 0; + u16 channel = Si4709_dev.registers[CHANNEL]; +#ifdef TEST_FM + u16 read_channel; +#endif + debug("tune_freq called"); + + Si4709_dev.registers[CHANNEL] = freq_to_channel(frequency); +#ifdef TEST_FM + read_channel = Si4709_dev.registers[CHANNEL]; + debug("Input read_channel =%x", read_channel); +#endif + CHANNEL_BITSET_TUNE_HIGH(&Si4709_dev.registers[CHANNEL]); + CHANNEL_BITSET_RESERVED(&Si4709_dev.registers[CHANNEL]); + + ret = i2c_write(CHANNEL); + if (ret < 0) { + error("tune_freq i2c_write 1 failed"); + Si4709_dev.registers[CHANNEL] = channel; + } else { + Si4709_dev_wait_flag = TUNE_WAITING; + debug("Si4709_dev_wait_flag = TUNE_WAITING"); +#ifdef TEST_FM + ret = i2c_read(READCHAN); + if (ret < 0) + error("tune_freq i2c_read 1 failed"); + else { + read_channel = + READCHAN_GET_CHAN( + Si4709_dev.registers[READCHAN]); + debug("curr_channel before tuning = %x", read_channel); + } +#endif + wait(); + + Si4709_dev_wait_flag = NO_WAIT; + + /* VNVS:START 13-OCT'09 : */ + /* Checking the status of Seek/Tune Bit */ +#ifdef TEST_FM + ret = i2c_read(STATUSRSSI); + if (ret < 0) + error("tune_freq i2c_read 2 failed"); + else if (STATUSRSSI_SEEK_TUNE_STATUS + (Si4709_dev.registers[STATUSRSSI]) == COMPLETE) + debug("Seek/Tune Status is set to 1 by device " + "---Tuning Operation Completed"); + else + debug("Seek/Tune Status is set to 0 by device " + "---Tuning Operation Not Completed"); +#endif + /* VNVS:END */ + + channel = Si4709_dev.registers[CHANNEL]; + + CHANNEL_BITSET_TUNE_LOW(&Si4709_dev.registers[CHANNEL]); + CHANNEL_BITSET_RESERVED(&Si4709_dev.registers[CHANNEL]); + + ret = i2c_write(CHANNEL); + if (ret < 0) { + error("tune_freq i2c_write 2 failed"); + Si4709_dev.registers[CHANNEL] = channel; + } else { + do { + ret = i2c_read(STATUSRSSI); + if (ret < 0) { + error("tune_freq i2c_read 3 failed"); + break; + } + } while (STATUSRSSI_SEEK_TUNE_STATUS + (Si4709_dev.registers[STATUSRSSI]) + != CLEAR); + } + + /* VNVS:START 13-OCT'09 : */ + /* Reading the READCHAN register after tuning operation */ +#ifdef TEST_FM + ret = i2c_read(READCHAN); + if (ret < 0) + error("tune_freq i2c_read 2 failed"); + else { + read_channel = + READCHAN_GET_CHAN( + Si4709_dev.registers[READCHAN]); + debug("curr_channel after tuning= %x", read_channel); + } +#endif + /* VNVS:END */ + } + + return ret; +} + +static void get_cur_chan_freq(u32 *frequency, u16 readchan) +{ + u16 channel = 0; + debug("get_cur_chan_freq called"); + + channel = READCHAN_GET_CHAN(readchan); + debug("read_channel=%x", channel); + + *frequency = channel_to_freq(channel); + + debug("frequency-> %u", *frequency); +} + +static u16 freq_to_channel(u32 frequency) +{ + u16 channel; + + if (frequency < Si4709_dev.settings.bottom_of_band) + frequency = Si4709_dev.settings.bottom_of_band; + + channel = (frequency - Si4709_dev.settings.bottom_of_band) + / Si4709_dev.settings.channel_spacing; + + return channel; +} + +static u32 channel_to_freq(u16 channel) +{ + u32 frequency; + + frequency = Si4709_dev.settings.bottom_of_band + + Si4709_dev.settings.channel_spacing * channel; + + return frequency; +} + +/* Only one thread will be able to call this, since this function call is + protected by a mutex, so no race conditions can arise */ +static void wait(void) +{ + wait_event_interruptible(Si4709_waitq, + (Si4709_dev_wait_flag == WAIT_OVER) || + (Si4709_dev_wait_flag == SEEK_CANCEL)); +} + +#ifndef RDS_INTERRUPT_ON_ALWAYS +static void wait_RDS(void) +{ + wait_event_interruptible_timeout(Si4709_waitq, + (Si4709_dev_wait_flag == WAIT_OVER), + Si4709_dev.settings.timeout_RDS); +} +#endif + +/* i2c read function */ +/* Si4709_dev.client should be set before calling this function. + If Si4709_dev.valid = eTRUE then Si4709_dev.client will b valid + This function should be called from the functions in this file. The + callers should check if Si4709_dev.valid = eTRUE before + calling this function. If it is eFALSE then this function should not + be called */ +static int i2c_read(u8 reg) +{ + u8 idx, reading_reg = STATUSRSSI; + u8 data[NUM_OF_REGISTERS * 2], data_high, data_low; + int msglen = 0, ret = 0; + + for (idx = 0; idx < NUM_OF_REGISTERS * 2; idx++) + data[idx] = 0x00; + + msglen = reg - reading_reg + 1; + + if (msglen > 0) + msglen = msglen * 2; + else + msglen = (msglen + NUM_OF_REGISTERS) * 2; + + ret = i2c_master_recv((struct i2c_client *)(Si4709_dev.client), data, + msglen); + + if (ret == msglen) { + idx = 0; + do { + data_high = data[idx]; + data_low = data[idx + 1]; + + Si4709_dev.registers[reading_reg] = 0x0000; + Si4709_dev.registers[reading_reg] = + (data_high << 8) + data_low; + reading_reg = (reading_reg + 1) & RDSD; + idx = idx + 2; + } while (reading_reg != ((reg + 1) & RDSD)); + + ret = 0; + } else + ret = -1; + + return ret; +} + +/* i2c write function */ +/* Si4709_dev.client should be set before calling this function. + If Si4709_dev.valid = eTRUE then Si4709_dev.client will b valid + This function should be called from the functions in this file. The + callers should check if Si4709_dev.valid = eTRUE before + calling this function. If it is eFALSE then this function should not + be called */ +static int i2c_write(u8 reg) +{ + u8 writing_reg = POWERCFG; + u8 data[NUM_OF_REGISTERS * 2]; + int i, msglen = 0, ret = 0; + + for (i = 0; i < NUM_OF_REGISTERS * 2; i++) + data[i] = 0x00; + + do { + data[msglen++] = (u8) (Si4709_dev.registers[writing_reg] >> 8); + data[msglen++] = + (u8) (Si4709_dev.registers[writing_reg] & 0xFF); + + writing_reg = (writing_reg + 1) & RDSD; + } while (writing_reg != ((reg + 1) & RDSD)); + + ret = i2c_master_send((struct i2c_client *)(Si4709_dev.client), + (const char *)data, msglen); + + if (ret == msglen) + ret = 0; + else + ret = -1; + + return ret; +} + +#if 0 +static int insert_preset(u32 frequency, u8 rssi, u8 *seek_preset_rssi) +{ + u8 i; + u8 min_rssi = 0xff; + u8 min_rssi_preset = 0; + int ret = 0; + + /* first find the minimum rssi and its location + this will always stop at the first location with a zero rssi */ + + debug("si4709 autoseek : insert preset\n"); + + for (i = 0; i < NUM_SEEK_PRESETS; i++) { + if (seek_preset_rssi[i] < min_rssi) { + min_rssi = seek_preset_rssi[i]; + min_rssi_preset = i; + } + } + + if (rssi < min_rssi) + ret = -1; + + /***Delete the preset with the minimum rssi, and clear the last preset + since it would only be a copy of the second to last preset after + the deletion ***/ + for (i = min_rssi_preset; i < NUM_SEEK_PRESETS - 1; i++) { + Si4709_dev.settings.seek_preset[i] = + Si4709_dev.settings.seek_preset[i + 1]; + seek_preset_rssi[i] = seek_preset_rssi[i + 1]; + } + + Si4709_dev.settings.seek_preset[i] = 0; + seek_preset_rssi[i] = 0; + + /*** Fill the first preset with a zero for the frequency. This will + always overwrite the last preset once all presets have been filled. ***/ + for (i = min_rssi_preset; i < NUM_SEEK_PRESETS; i++) { + if (Si4709_dev.settings.seek_preset[i] == 0) { + Si4709_dev.settings.seek_preset[i] = frequency; + seek_preset_rssi[i] = rssi; + break; + } + } + + return ret; +} +#endif diff --git a/drivers/samsung/fm_si4709/Si4709_dev.h b/drivers/samsung/fm_si4709/Si4709_dev.h new file mode 100644 index 0000000..8b8371f --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_dev.h @@ -0,0 +1,231 @@ +#ifndef _Si4709_DEV_H +#define _Si4709_DEV_H + +#include <linux/i2c.h> + +#include "Si4709_regs.h" +#include "Si4709_common.h" + +#define NUM_SEEK_PRESETS 20 + +#define WAIT_OVER 0 +#define SEEK_WAITING 1 +#define NO_WAIT 2 +#define TUNE_WAITING 4 +#define RDS_WAITING 5 +#define SEEK_CANCEL 6 + +/*dev settings*/ +/*band*/ +#define BAND_87500_108000_kHz 1 +#define BAND_76000_108000_kHz 2 +#define BAND_76000_90000_kHz 3 + +/*channel spacing*/ +#define CHAN_SPACING_200_kHz 20 /*US*/ +#define CHAN_SPACING_100_kHz 10 /*Europe,Japan */ +#define CHAN_SPACING_50_kHz 5 +/*DE-emphasis Time Constant*/ +#define DE_TIME_CONSTANT_50 1 /*Europe,Japan,Australia */ +#define DE_TIME_CONSTANT_75 0 /*US*/ +struct dev_state_t { + int power_state; + int seek_state; +}; + +struct rssi_snr_t { + u8 curr_rssi; + u8 curr_rssi_th; + u8 curr_snr; +}; + +struct device_id { + u8 part_number; + u16 manufact_number; +}; + +struct chip_id { + u8 chip_version; + u8 device; + u8 firmware_version; +}; + +struct sys_config2 { + u16 rssi_th; + u8 fm_band; + u8 fm_chan_spac; + u8 fm_vol; +}; + +struct sys_config3 { + u8 smmute; + u8 smutea; + u8 volext; + u8 sksnr; + u8 skcnt; +}; + +struct status_rssi { + u8 rdsr; + u8 stc; + u8 sfbl; + u8 afcrl; + u8 rdss; + u8 blera; + u8 st; + u16 rssi; +}; + +struct power_config { + u16 dsmute:1; + u16 dmute:1; + u16 mono:1; + u16 rds_mode:1; + u16 sk_mode:1; + u16 seek_up:1; + u16 seek:1; + u16 power_disable:1; + u16 power_enable:1; +}; + +struct radio_data_t { + u16 rdsa; + u16 rdsb; + u16 rdsc; + u16 rdsd; + u8 curr_rssi; + u32 curr_channel; + u8 blera; + u8 blerb; + u8 blerc; + u8 blerd; +}; + +struct channel_into_t { + u32 frequency; + u8 rsssi_val; +}; + +struct dev_settings_t { + u16 band; + u32 bottom_of_band; + u16 channel_spacing; + u32 timeout_RDS; /****For storing the jiffy value****/ + u32 seek_preset[NUM_SEEK_PRESETS]; + u8 curr_snr; + u8 curr_rssi_th; +}; + +struct Si4709_device_t { + /*Any function which + - views/modifies the fields of this structure + - does i2c communication + should lock the mutex before doing so. + Recursive locking should not be done. + In this file all the exported functions will take care + of this. The static functions will not deal with the + mutex */ + struct mutex lock; + + struct i2c_client const *client; + + struct dev_state_t state; + + struct dev_settings_t settings; + + struct channel_into_t rssi_freq[50]; + + u16 registers[NUM_OF_REGISTERS]; + + /* This field will be checked by all the functions + exported by this file (except the init function), + to validate the the fields of this structure. + if eTRUE: the fileds are valid + if eFALSE: do not trust the values of the fields + of this structure */ + unsigned short valid; + + /*will be true is the client ans state fields are correct */ + unsigned short valid_client_state; +}; + +extern int Si4709_dev_wait_flag; + +#ifdef RDS_INTERRUPT_ON_ALWAYS +extern int Si4709_RDS_flag; +extern int RDS_Data_Available; +extern int RDS_Data_Lost; +extern int RDS_Groups_Available_till_now; +extern struct workqueue_struct *Si4709_wq; +extern struct work_struct Si4709_work; +#endif + +/* Function prototypes */ + +/*extern functions*/ +/**********************************************/ +/* All the exported functions which view or modify the device + state/data, do i2c com will have to lock the mutex before + doing so +*/ +/**********************************************/ + +extern int Si4709_dev_init(struct i2c_client *); +extern int Si4709_dev_exit(void); + +extern void Si4709_dev_mutex_init(void); + +extern int Si4709_dev_suspend(void); +extern int Si4709_dev_resume(void); + +extern int Si4709_dev_powerup(void); +extern int Si4709_dev_powerdown(void); + +extern int Si4709_dev_band_set(int); +extern int Si4709_dev_ch_spacing_set(int); + +extern int Si4709_dev_chan_select(u32); +extern int Si4709_dev_chan_get(u32 *); + +extern int Si4709_dev_seek_full(u32 *); +extern int Si4709_dev_seek_up(u32 *); +extern int Si4709_dev_seek_down(u32 *); +extern int Si4709_dev_seek_auto(u32 *); + +extern int Si4709_dev_RSSI_seek_th_set(u8); +extern int Si4709_dev_seek_SNR_th_set(u8); +extern int Si4709_dev_seek_FM_ID_th_set(u8); +extern int Si4709_dev_cur_RSSI_get(struct rssi_snr_t *); +extern int Si4709_dev_VOLEXT_ENB(void); +extern int Si4709_dev_VOLEXT_DISB(void); +extern int Si4709_dev_volume_set(u8); +extern int Si4709_dev_volume_get(u8 *); +extern int Si4709_dev_DSMUTE_ON(void); +extern int Si4709_dev_DSMUTE_OFF(void); +extern int Si4709_dev_MUTE_ON(void); +extern int Si4709_dev_MUTE_OFF(void); +extern int Si4709_dev_MONO_SET(void); +extern int Si4709_dev_STEREO_SET(void); +extern int Si4709_dev_rstate_get(struct dev_state_t *); +extern int Si4709_dev_RDS_data_get(struct radio_data_t *); +extern int Si4709_dev_RDS_ENABLE(void); +extern int Si4709_dev_RDS_DISABLE(void); +extern int Si4709_dev_RDS_timeout_set(u32); +extern int Si4709_dev_device_id(struct device_id *); +extern int Si4709_dev_chip_id(struct chip_id *); +extern int Si4709_dev_sys_config2(struct sys_config2 *); +extern int Si4709_dev_sys_config3(struct sys_config3 *); +extern int Si4709_dev_power_config(struct power_config *); +extern int Si4709_dev_AFCRL_get(u8 *); +extern int Si4709_dev_DE_set(u8); +extern int Si4709_dev_status_rssi(struct status_rssi *status); +extern int Si4709_dev_sys_config2_set(struct sys_config2 *sys_conf2); +extern int Si4709_dev_sys_config3_set(struct sys_config3 *sys_conf3); +extern int Si4709_dev_reset_rds_data(void); + +/***********************************************/ + +#ifdef RDS_INTERRUPT_ON_ALWAYS +extern void Si4709_work_func(struct work_struct *); +#endif +#endif diff --git a/drivers/samsung/fm_si4709/Si4709_i2c_drv.c b/drivers/samsung/fm_si4709/Si4709_i2c_drv.c new file mode 100644 index 0000000..4f92197 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_i2c_drv.c @@ -0,0 +1,192 @@ + +#include <linux/kernel.h> +#include <linux/i2c.h> + +#include "Si4709_dev.h" +#include "Si4709_common.h" +#include "Si4709_i2c_drv.h" + +/*static functions*/ +static int Si4709_probe(struct i2c_client *); +static int Si4709_remove(struct i2c_client *); +static int Si4709_suspend(struct i2c_client *, pm_message_t mesg); +static int Si4709_resume(struct i2c_client *); + +static struct i2c_client *Si4709_i2c_client; + +struct si4709_data { + struct i2c_client *client; +}; + +/*I2C Setting*/ + +static struct i2c_driver Si4709_i2c_driver; + +#if 0 +static unsigned short Si4709_normal_i2c[] = { I2C_CLIENT_END }; +static unsigned short Si4709_ignore[] = { I2C_CLIENT_END }; + +static unsigned short Si4709_i2c_probe[] = { + 8, SI4709_I2C_ADDRESS >> 1, I2C_CLIENT_END +}; + +static struct i2c_client_address_data Si4709_addr_data = { + .normal_i2c = Si4709_normal_i2c, + .ignore = Si4709_ignore, + .probe = Si4709_i2c_probe, +}; +#endif + +static const struct i2c_device_id si4709_id[] = { + {"Si4709", 0}, + {} +}; + +static int Si4709_probe(struct i2c_client *client) +{ + int ret = 0; + + debug("Si4709 i2c driver Si4709_probe called"); + + if (strcmp(client->name, "Si4709") != 0) { + ret = -1; + error("Si4709_probe: device not supported"); + } else { + ret = Si4709_dev_init(client); + if (ret < 0) + error("Si4709_dev_init failed"); + } + + return ret; +} + +static int Si4709_remove(struct i2c_client *client) +{ + int ret = 0; + + debug("Si4709 i2c driver Si4709_remove called"); + + if (strcmp(client->name, "Si4709") != 0) { + ret = -1; + error("Si4709_remove: device not supported"); + } else { + ret = Si4709_dev_exit(); + if (ret < 0) + error("Si4709_dev_exit failed"); + } + + return ret; +} + +static int si4709_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct si4709_data *si4709_dev; + + debug("----- %s %d\n", __func__, __LINE__); + + si4709_dev = kzalloc(sizeof(struct si4709_data), GFP_KERNEL); + + if (!si4709_dev) { + err = -ENOMEM; + return err; + } + + Si4709_i2c_client = client; + i2c_set_clientdata(client, si4709_dev); + + if (Si4709_i2c_client == NULL) { + error("Si4709 i2c_client is NULL"); + return -ENODEV; + } + + Si4709_probe(Si4709_i2c_client); + + return 0; +} + +static int __exit si4709_i2c_remove(struct i2c_client *client) +{ + struct si4709_data *si4709_dev = i2c_get_clientdata(client); + + debug("----- %s %d\n", __func__, __LINE__); + + Si4709_remove(Si4709_i2c_client); + kfree(si4709_dev); + kfree(client); + si4709_dev = NULL; + Si4709_i2c_client = NULL; + + return 0; +} + +MODULE_DEVICE_TABLE(i2c, si4709_id); + +static struct i2c_driver Si4709_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "Si4709", + }, + .id_table = si4709_id, + .probe = si4709_i2c_probe, + .remove = __devexit_p(si4709_i2c_remove), + .suspend = Si4709_suspend, + .resume = Si4709_resume, +}; + +static int Si4709_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret = 0; + + debug("Si4709 i2c driver Si4709_suspend called"); + + if (strcmp(client->name, "Si4709") != 0) { + ret = -1; + error("Si4709_suspend: device not supported"); + } else { + ret = Si4709_dev_suspend(); + if (ret < 0) + error("Si4709_dev_disable failed"); + } + + return 0; +} + +static int Si4709_resume(struct i2c_client *client) +{ + int ret = 0; + + /* debug("Si4709 i2c driver Si4709_resume called"); */ + + if (strcmp(client->name, "Si4709") != 0) { + ret = -1; + error("Si4709_resume: device not supported"); + } else { + ret = Si4709_dev_resume(); + if (ret < 0) + error("Si4709_dev_enable failed"); + } + + return 0; +} + +int Si4709_i2c_drv_init(void) +{ + int ret = 0; + + debug("Si4709 i2c driver Si4709_i2c_driver_init called"); + + ret = i2c_add_driver(&Si4709_i2c_driver); + if (ret < 0) + error("Si4709 i2c_add_driver failed"); + + return ret; +} + +void Si4709_i2c_drv_exit(void) +{ + debug("Si4709 i2c driver Si4709_i2c_driver_exit called"); + + i2c_del_driver(&Si4709_i2c_driver); +} diff --git a/drivers/samsung/fm_si4709/Si4709_i2c_drv.h b/drivers/samsung/fm_si4709/Si4709_i2c_drv.h new file mode 100644 index 0000000..e734af4 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_i2c_drv.h @@ -0,0 +1,8 @@ +#ifndef _Si4709_I2C_DRV_H +#define _Si4709_I2C_DRV_H + +/*extern functions*/ +extern int Si4709_i2c_drv_init(void); +extern void Si4709_i2c_drv_exit(void); + +#endif diff --git a/drivers/samsung/fm_si4709/Si4709_ioctl.h b/drivers/samsung/fm_si4709/Si4709_ioctl.h new file mode 100644 index 0000000..e9fba9f --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_ioctl.h @@ -0,0 +1,121 @@ +#ifndef _Si4709_IOCTL_H +#define _Si4709_IOCTL_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +#include "Si4709_dev.h" + +/*****************IOCTLS******************/ +/*magic no*/ +#define Si4709_IOC_MAGIC 0xFA +/*max seq no*/ +#define Si4709_IOC_NR_MAX 40 + +/*commands*/ + +#define Si4709_IOC_POWERUP _IO(Si4709_IOC_MAGIC, 0) + +#define Si4709_IOC_POWERDOWN _IO(Si4709_IOC_MAGIC, 1) + +#define Si4709_IOC_BAND_SET _IOW(Si4709_IOC_MAGIC, 2, int) + +#define Si4709_IOC_CHAN_SPACING_SET _IOW(Si4709_IOC_MAGIC, 3, int) + +#define Si4709_IOC_CHAN_SELECT _IOW(Si4709_IOC_MAGIC, 4, u32) + +#define Si4709_IOC_CHAN_GET _IOR(Si4709_IOC_MAGIC, 5, u32) + +#define Si4709_IOC_SEEK_UP _IOR(Si4709_IOC_MAGIC, 6, u32) + +#define Si4709_IOC_SEEK_DOWN _IOR(Si4709_IOC_MAGIC, 7, u32) + +/*VNVS:28OCT'09---- Si4709_IOC_SEEK_AUTO is disabled as of now*/ +/* #define Si4709_IOC_SEEK_AUTO _IOR(Si4709_IOC_MAGIC, 8, u32) */ + +#define Si4709_IOC_RSSI_SEEK_TH_SET _IOW(Si4709_IOC_MAGIC, 9, u8) + +#define Si4709_IOC_SEEK_SNR_SET _IOW(Si4709_IOC_MAGIC, 10, u8) + +#define Si4709_IOC_SEEK_CNT_SET _IOW(Si4709_IOC_MAGIC, 11, u8) + +#define Si4709_IOC_CUR_RSSI_GET \ +_IOR(Si4709_IOC_MAGIC, 12, struct rssi_snr_t) + +#define Si4709_IOC_VOLEXT_ENB _IO(Si4709_IOC_MAGIC, 13) + +#define Si4709_IOC_VOLEXT_DISB _IO(Si4709_IOC_MAGIC, 14) + +#define Si4709_IOC_VOLUME_SET _IOW(Si4709_IOC_MAGIC, 15, u8) + +#define Si4709_IOC_VOLUME_GET _IOR(Si4709_IOC_MAGIC, 16, u8) + +#define Si4709_IOC_MUTE_ON _IO(Si4709_IOC_MAGIC, 17) + +#define Si4709_IOC_MUTE_OFF _IO(Si4709_IOC_MAGIC, 18) + +#define Si4709_IOC_MONO_SET _IO(Si4709_IOC_MAGIC, 19) + +#define Si4709_IOC_STEREO_SET _IO(Si4709_IOC_MAGIC, 20) + +#define Si4709_IOC_RSTATE_GET \ +_IOR(Si4709_IOC_MAGIC, 21, struct dev_state_t) + +#define Si4709_IOC_RDS_DATA_GET \ +_IOR(Si4709_IOC_MAGIC, 22, struct radio_data_t) + +#define Si4709_IOC_RDS_ENABLE _IO(Si4709_IOC_MAGIC, 23) + +#define Si4709_IOC_RDS_DISABLE _IO(Si4709_IOC_MAGIC, 24) + +#define Si4709_IOC_RDS_TIMEOUT_SET _IOW(Si4709_IOC_MAGIC, 25, u32) + +#define Si4709_IOC_SEEK_CANCEL _IO(Si4709_IOC_MAGIC, 26) + +/*VNVS:START 13-OCT'09 : + Added IOCTLs for reading the device-id,chip-id,power configuration, + system configuration2 registers*/ +#define Si4709_IOC_DEVICE_ID_GET \ +_IOR(Si4709_IOC_MAGIC, 27, struct device_id) + +#define Si4709_IOC_CHIP_ID_GET \ +_IOR(Si4709_IOC_MAGIC, 28, struct chip_id) + +#define Si4709_IOC_SYS_CONFIG2_GET \ +_IOR(Si4709_IOC_MAGIC, 29, struct sys_config2) + +#define Si4709_IOC_POWER_CONFIG_GET \ +_IOR(Si4709_IOC_MAGIC, 30, struct power_config) + +/* For reading AFCRL bit, to check for a valid channel */ +#define Si4709_IOC_AFCRL_GET _IOR(Si4709_IOC_MAGIC, 31, u8) + +/* Setting DE-emphasis Time Constant. +For DE=0,TC=50us(Europe,Japan,Australia) and DE=1,TC=75us(USA) */ +#define Si4709_IOC_DE_SET _IOW(Si4709_IOC_MAGIC, 32, u8) +/*VNVS:END*/ + +#define Si4709_IOC_SYS_CONFIG3_GET \ +_IOR(Si4709_IOC_MAGIC, 33, struct sys_config3) + +#define Si4709_IOC_STATUS_RSSI_GET \ +_IOR(Si4709_IOC_MAGIC, 34, struct status_rssi) + +#define Si4709_IOC_SYS_CONFIG2_SET \ +_IOW(Si4709_IOC_MAGIC, 35, struct sys_config2) + +#define Si4709_IOC_SYS_CONFIG3_SET \ +_IOW(Si4709_IOC_MAGIC, 36, struct sys_config3) + +#define Si4709_IOC_DSMUTE_ON _IO(Si4709_IOC_MAGIC, 37) + +#define Si4709_IOC_DSMUTE_OFF _IO(Si4709_IOC_MAGIC, 38) + +#define Si4709_IOC_RESET_RDS_DATA _IO(Si4709_IOC_MAGIC, 39) + +#define Si4709_IOC_SEEK_FULL _IOR(Si4709_IOC_MAGIC, 40, u32) + + +/*****************************************/ + +#endif diff --git a/drivers/samsung/fm_si4709/Si4709_main.c b/drivers/samsung/fm_si4709/Si4709_main.c new file mode 100644 index 0000000..5531c12 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_main.c @@ -0,0 +1,844 @@ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/uaccess.h> +#include <linux/irq.h> +#include <asm/irq.h> +#include <linux/io.h> +#include <linux/wait.h> +#include <linux/stat.h> +#include <linux/ioctl.h> +#include <linux/delay.h> + +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> + +#include "Si4709_i2c_drv.h" +#include "Si4709_dev.h" +#include "Si4709_ioctl.h" +#include "Si4709_common.h" + +/*******************************************************/ + +/*static functions*/ + +/*file operatons*/ +static int Si4709_open(struct inode *, struct file *); +static int Si4709_release(struct inode *, struct file *); +static long Si4709_ioctl(struct file *, unsigned int, unsigned long); + +/*ISR*/ +static irqreturn_t Si4709_isr(int irq, void *unused); +/* static void __iomem *gpio_mask_mem; */ +/**********************************************************/ + +static const struct file_operations Si4709_fops = { + .owner = THIS_MODULE, + .open = Si4709_open, + .unlocked_ioctl = Si4709_ioctl, + .release = Si4709_release, +}; + +static struct miscdevice Si4709_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "fmradio", + .fops = &Si4709_fops, +}; + +/*VNVS:START 13-OCT'09---- +dummy struct which is used as a cookie for FM Radio interrupt */ +struct fm_radio { + int i; + int j; +}; + +struct fm_radio fm_radio_1; +/*VNVS:END*/ + +wait_queue_head_t Si4709_waitq; + +unsigned int Si4709_int; +unsigned int Si4709_irq; + +/***************************************************************/ + +static int Si4709_open(struct inode *inode, struct file *filp) +{ + debug("Si4709_open called\n"); + + return nonseekable_open(inode, filp); +} + +static int Si4709_release(struct inode *inode, struct file *filp) +{ + debug("Si4709_release called\n\n"); + + return 0; +} + +static long Si4709_ioctl(struct file *filp, unsigned int ioctl_cmd, + unsigned long arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + + debug("Si4709 ioctl 0x%x", ioctl_cmd); + + if (_IOC_TYPE(ioctl_cmd) != Si4709_IOC_MAGIC) { + error("Inappropriate ioctl 1 0x%x", ioctl_cmd); + return -ENOTTY; + } + + if (_IOC_NR(ioctl_cmd) > Si4709_IOC_NR_MAX) { + error("Inappropriate ioctl 2 0x%x", ioctl_cmd); + return -ENOTTY; + } + + + switch (ioctl_cmd) { + case Si4709_IOC_POWERUP: + debug("Si4709_IOC_POWERUP called\n\n"); + + ret = (long)Si4709_dev_powerup(); + if (ret < 0) + error("Si4709_IOC_POWERUP failed\n"); + break; + + case Si4709_IOC_POWERDOWN: + debug("Si4709_IOC_POWERDOWN called\n"); + + ret = (long)Si4709_dev_powerdown(); + if (ret < 0) + error("Si4709_IOC_POWERDOWN failed\n"); + break; + + case Si4709_IOC_BAND_SET: + { + int band; + debug("Si4709_IOC_BAND_SET called\n\n"); + + if (copy_from_user((void *)&band, argp, sizeof(int))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_band_set(band); + if (ret < 0) + error("Si4709_IOC_BAND_SET failed\n"); + } + } + break; + + case Si4709_IOC_CHAN_SPACING_SET: + { + int ch_spacing; + debug("Si4709_IOC_CHAN_SPACING_SET called\n"); + + if (copy_from_user + ((void *)&ch_spacing, argp, sizeof(int))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_ch_spacing_set(ch_spacing); + if (ret < 0) + error("Si4709_IOC_CHAN_SPACING_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_CHAN_SELECT: + { + u32 frequency; + debug("Si4709_IOC_CHAN_SELECT called\n"); + + if (copy_from_user + ((void *)&frequency, argp, sizeof(u32))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_chan_select(frequency); + if (ret < 0) + error("Si4709_IOC_CHAN_SELECT " + "failed\n"); + } + } + break; + + case Si4709_IOC_CHAN_GET: + { + u32 frequency = 0; + debug("Si4709_IOC_CHAN_GET called\n"); + + ret = (long)Si4709_dev_chan_get(&frequency); + if (ret < 0) + error("Si4709_IOC_CHAN_GET failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SEEK_UP: + { + u32 frequency = 0; + debug("Si4709_IOC_SEEK_UP called\n"); + + ret = (long)Si4709_dev_seek_up(&frequency); + if (ret < 0) + error("Si4709_IOC_SEEK_UP failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SEEK_DOWN: + { + u32 frequency = 0; + debug("Si4709_IOC_SEEK_DOWN called\n"); + + ret = (long)Si4709_dev_seek_down(&frequency); + if (ret < 0) + error("Si4709_IOC_SEEK_DOWN failed\n"); + else if (copy_to_user + (argp, (void *)&frequency, sizeof(u32))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_RSSI_SEEK_TH_SET: + { + u8 RSSI_seek_th; + debug("Si4709_IOC_RSSI_SEEK_TH_SET called\n"); + + if (copy_from_user + ((void *)&RSSI_seek_th, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_RSSI_seek_th_set(RSSI_seek_th); + if (ret < 0) + error("Si4709_IOC_RSSI_SEEK_TH_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_SEEK_SNR_SET: + { + u8 seek_SNR_th; + debug("Si4709_IOC_SEEK_SNR_SET called\n"); + + if (copy_from_user + ((void *)&seek_SNR_th, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_seek_SNR_th_set(seek_SNR_th); + if (ret < 0) + error("Si4709_IOC_SEEK_SNR_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_SEEK_CNT_SET: + { + u8 seek_FM_ID_th; + debug("Si4709_IOC_SEEK_CNT_SET called\n"); + + if (copy_from_user + ((void *)&seek_FM_ID_th, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = + (long)Si4709_dev_seek_FM_ID_th_set(seek_FM_ID_th); + if (ret < 0) + error("Si4709_IOC_SEEK_CNT_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_CUR_RSSI_GET: + { + struct rssi_snr_t data; + debug("Si4709_IOC_CUR_RSSI_GET called\n"); + + ret = (long)Si4709_dev_cur_RSSI_get(&data); + if (ret < 0) + error("Si4709_IOC_CUR_RSSI_GET failed\n"); + else if (copy_to_user(argp, (void *)&data, + sizeof(data))) + ret = -EFAULT; + + debug("curr_rssi:%d\ncurr_rssi_th:%d\ncurr_snr:%d\n", + data.curr_rssi, data.curr_rssi_th, data.curr_snr); + } + break; + + case Si4709_IOC_VOLEXT_ENB: + debug("Si4709_IOC_VOLEXT_ENB called\n"); + + ret = (long)Si4709_dev_VOLEXT_ENB(); + if (ret < 0) + error("Si4709_IOC_VOLEXT_ENB failed\n"); + break; + + case Si4709_IOC_VOLEXT_DISB: + debug("Si4709_IOC_VOLEXT_DISB called\n"); + + ret = (long)Si4709_dev_VOLEXT_DISB(); + if (ret < 0) + error("Si4709_IOC_VOLEXT_DISB failed\n"); + break; + + case Si4709_IOC_VOLUME_SET: + { + u8 volume; + if (copy_from_user((void *)&volume, argp, sizeof(u8))) + ret = -EFAULT; + else { + debug("Si4709_IOC_VOLUME_SET called " + "vol %d\n", volume); + ret = (long)Si4709_dev_volume_set(volume); + if (ret < 0) + error("Si4709_IOC_VOLUME_SET failed\n"); + } + } + break; + + case Si4709_IOC_VOLUME_GET: + { + u8 volume; + debug("Si4709_IOC_VOLUME_GET called\n"); + + ret = (long)Si4709_dev_volume_get(&volume); + if (ret < 0) + error("Si4709_IOC_VOLUME_GET failed\n"); + else if (copy_to_user + (argp, (void *)&volume, sizeof(u8))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_DSMUTE_ON: + debug("Si4709_IOC_DSMUTE_ON called\n\n"); + + ret = (long)Si4709_dev_DSMUTE_ON(); + if (ret < 0) + error("Si4709_IOC_DSMUTE_ON failed\n"); + break; + + case Si4709_IOC_DSMUTE_OFF: + debug("Si4709_IOC_DSMUTE_OFF called\n\n"); + + ret = (long)Si4709_dev_DSMUTE_OFF(); + if (ret < 0) + error("Si4709_IOC_DSMUTE_OFF failed\n"); + break; + + case Si4709_IOC_MUTE_ON: + debug("Si4709_IOC_MUTE_ON called\n"); + + ret = (long)Si4709_dev_MUTE_ON(); + if (ret < 0) + error("Si4709_IOC_MUTE_ON failed\n"); + break; + + case Si4709_IOC_MUTE_OFF: + debug("Si4709_IOC_MUTE_OFF called\n"); + + ret = (long)Si4709_dev_MUTE_OFF(); + if (ret < 0) + error("Si4709_IOC_MUTE_OFF failed\n"); + break; + + case Si4709_IOC_MONO_SET: + debug("Si4709_IOC_MONO_SET called\n"); + + ret = (long)Si4709_dev_MONO_SET(); + if (ret < 0) + error("Si4709_IOC_MONO_SET failed\n"); + break; + + case Si4709_IOC_STEREO_SET: + debug("Si4709_IOC_STEREO_SET called\n"); + + ret = (long)Si4709_dev_STEREO_SET(); + if (ret < 0) + error("Si4709_IOC_STEREO_SET failed\n"); + break; + + case Si4709_IOC_RSTATE_GET: + { + struct dev_state_t dev_state; + + debug("Si4709_IOC_RSTATE_GET called\n"); + + ret = (long)Si4709_dev_rstate_get(&dev_state); + if (ret < 0) + error("Si4709_IOC_RSTATE_GET failed\n"); + else if (copy_to_user(argp, (void *)&dev_state, + sizeof(dev_state))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_RDS_DATA_GET: + { + struct radio_data_t data; + debug("Si4709_IOC_RDS_DATA_GET called\n"); + + ret = (long)Si4709_dev_RDS_data_get(&data); + if (ret < 0) + error("Si4709_IOC_RDS_DATA_GET failed\n"); + else if (copy_to_user(argp, (void *)&data, + sizeof(data))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_RDS_ENABLE: + debug("Si4709_IOC_RDS_ENABLE called\n"); + + ret = (long)Si4709_dev_RDS_ENABLE(); + if (ret < 0) + error("Si4709_IOC_RDS_ENABLE failed\n"); + break; + + case Si4709_IOC_RDS_DISABLE: + debug("Si4709_IOC_RDS_DISABLE called\n"); + + ret = (long)Si4709_dev_RDS_DISABLE(); + if (ret < 0) + error("Si4709_IOC_RDS_DISABLE failed\n"); + break; + + case Si4709_IOC_RDS_TIMEOUT_SET: + { + u32 time_out; + debug("Si4709_IOC_RDS_TIMEOUT_SET called\n"); + + if (copy_from_user + ((void *)&time_out, argp, sizeof(u32))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_RDS_timeout_set(time_out); + if (ret < 0) + error("Si4709_IOC_RDS_TIMEOUT_SET " + "failed\n"); + } + } + break; + + case Si4709_IOC_SEEK_CANCEL: + debug("Si4709_IOC_SEEK_CANCEL called\n"); + + if (Si4709_dev_wait_flag == SEEK_WAITING) { + Si4709_dev_wait_flag = SEEK_CANCEL; + wake_up_interruptible(&Si4709_waitq); + } + break; + +/*VNVS:START 13-OCT'09---- +Switch Case statements for calling functions which reads device-id, +chip-id,power configuration, system configuration2 registers */ + case Si4709_IOC_CHIP_ID_GET: + { + struct chip_id chp_id; + debug("Si4709_IOC_CHIP_ID called\n"); + + ret = (long)Si4709_dev_chip_id(&chp_id); + if (ret < 0) + error("Si4709_IOC_CHIP_ID failed\n"); + else if (copy_to_user(argp, (void *)&chp_id, + sizeof(chp_id))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_DEVICE_ID_GET: + { + struct device_id dev_id; + debug("Si4709_IOC_DEVICE_ID called\n"); + + ret = (long)Si4709_dev_device_id(&dev_id); + if (ret < 0) + error("Si4709_IOC_DEVICE_ID failed\n"); + else if (copy_to_user(argp, (void *)&dev_id, + sizeof(dev_id))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SYS_CONFIG2_GET: + { + struct sys_config2 sys_conf2; + debug("Si4709_IOC_SYS_CONFIG2 called\n"); + + ret = (long)Si4709_dev_sys_config2(&sys_conf2); + if (ret < 0) + error("Si4709_IOC_SYS_CONFIG2 failed\n"); + else if (copy_to_user(argp, (void *)&sys_conf2, + sizeof(sys_conf2))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SYS_CONFIG3_GET: + { + struct sys_config3 sys_conf3; + debug("Si4709_IOC_SYS_CONFIG3 called\n"); + + ret = (long)Si4709_dev_sys_config3(&sys_conf3); + if (ret < 0) + error("Si4709_IOC_SYS_CONFIG3 failed\n"); + else if (copy_to_user(argp, (void *)&sys_conf3, + sizeof(sys_conf3))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_POWER_CONFIG_GET: + { + struct power_config pow_conf; + debug("Si4709_IOC_POWER_CONFIG called\n"); + + ret = (long)Si4709_dev_power_config(&pow_conf); + if (ret < 0) + error("Si4709_IOC_POWER_CONFIG failed\n"); + else if (copy_to_user(argp, (void *)&pow_conf, + sizeof(pow_conf))) + ret = -EFAULT; + } + break; +/*VNVS:END*/ + +/*VNVS:START 18-NOV'09*/ + /*Reading AFCRL Bit */ + case Si4709_IOC_AFCRL_GET: + { + u8 afc; + debug("Si4709_IOC_AFCRL_GET called\n"); + + ret = (long)Si4709_dev_AFCRL_get(&afc); + if (ret < 0) + error("Si4709_IOC_AFCRL_GET failed\n"); + else if (copy_to_user(argp, (void *)&afc, sizeof(u8))) + ret = -EFAULT; + } + break; + + /*Setting DE-emphasis Time Constant. + For DE=0,TC=50us(Europe,Japan,Australia) + and DE=1,TC=75us(USA) */ + case Si4709_IOC_DE_SET: + { + u8 de_tc; + debug("Si4709_IOC_DE_SET called\n"); + + if (copy_from_user((void *)&de_tc, argp, sizeof(u8))) + ret = -EFAULT; + else { + ret = (long)Si4709_dev_DE_set(de_tc); + if (ret < 0) + error("Si4709_IOC_DE_SET failed\n"); + } + } + break; + + case Si4709_IOC_STATUS_RSSI_GET: + { + struct status_rssi status; + debug("Si4709_IOC_STATUS_RSSI_GET called\n"); + + ret = (long)Si4709_dev_status_rssi(&status); + if (ret < 0) + error("Si4709_IOC_STATUS_RSSI_GET failed\n"); + else if (copy_to_user(argp, (void *)&status, + sizeof(status))) + ret = -EFAULT; + } + break; + + case Si4709_IOC_SYS_CONFIG2_SET: + { + struct sys_config2 sys_conf2; + unsigned long n; + debug("Si4709_IOC_SYS_CONFIG2_SET called\n"); + + n = copy_from_user((void *)&sys_conf2, argp, + sizeof(sys_conf2)); + if (n) { + debug("Si4709_IOC_SYS_CONFIG2_SET() : " + "copy_from_user() has error!! " + "Failed to read [%lu] byes!", n); + ret = -EFAULT; + } else { + ret = (long)Si4709_dev_sys_config2_set(&sys_conf2); + if (ret < 0) + error("Si4709_IOC_SYS_CONFIG2_SET" + "failed\n"); + } + } + break; + + case Si4709_IOC_SYS_CONFIG3_SET: + { + struct sys_config3 sys_conf3; + unsigned long n; + + debug("Si4709_IOC_SYS_CONFIG3_SET called\n"); + + n = copy_from_user((void *)&sys_conf3, argp, + sizeof(sys_conf3)); + if (n < 0) { + error("Si4709_IOC_SYS_CONFIG3_SET() : " + "copy_from_user() has error!! " + "Failed to read [%lu] byes!", n); + ret = -EFAULT; + } else { + ret = (long)Si4709_dev_sys_config3_set(&sys_conf3); + if (ret < 0) + error("Si4709_IOC_SYS_CONFIG3_SET " + "failed\n"); + } + } + break; + + /*Resetting the RDS Data Buffer */ + case Si4709_IOC_RESET_RDS_DATA: + { + debug("Si4709_IOC_RESET_RDS_DATA called\n"); + + ret = (long)Si4709_dev_reset_rds_data(); + if (ret < 0) + error("Si4709_IOC_RESET_RDS_DATA failed\n"); + } + break; +/*VNVS:END*/ + + default: + error(" ioctl default\n"); + ret = -ENOTTY; + break; + } + + return ret; +} + +static irqreturn_t Si4709_isr(int irq, void *unused) +{ + debug("Si4709_isr: FM device called IRQ: %d", irq); +#ifdef RDS_INTERRUPT_ON_ALWAYS + if ((Si4709_dev_wait_flag == SEEK_WAITING) || + (Si4709_dev_wait_flag == TUNE_WAITING)) { + debug("Si4709_isr: FM Seek/Tune Interrupt " + "called IRQ %d\n", irq); + Si4709_dev_wait_flag = WAIT_OVER; + wake_up_interruptible(&Si4709_waitq); + } else if (Si4709_RDS_flag == RDS_WAITING) { /* RDS Interrupt */ + debug_rds("Si4709_isr: FM RDS Interrupt " + "called IRQ %d", irq); + RDS_Data_Available++; + RDS_Groups_Available_till_now++; + + debug_rds("RDS_Groups_Available_till_now b/w " + "Power ON/OFF : %d", + RDS_Groups_Available_till_now); + + if (RDS_Data_Available > 1) + RDS_Data_Lost++; + + if (!work_pending(&Si4709_work)) + queue_work(Si4709_wq, &Si4709_work); + } +#else + if ((Si4709_dev_wait_flag == SEEK_WAITING) || + (Si4709_dev_wait_flag == TUNE_WAITING) || + (Si4709_dev_wait_flag == RDS_WAITING)) { + Si4709_dev_wait_flag = WAIT_OVER; + wake_up_interruptible(&Si4709_waitq); + } +#endif + return IRQ_HANDLED; +} + +/************************************************************/ + +void debug_ioctls(void) +{ + debug("------------------------------------------------\n"); + + debug("Si4709_IOC_POWERUP 0x%x", Si4709_IOC_POWERUP); + + debug("Si4709_IOC_POWERDOWN 0x%x", Si4709_IOC_POWERDOWN); + + debug("Si4709_IOC_BAND_SET 0x%x", Si4709_IOC_BAND_SET); + + debug("Si4709_IOC_CHAN_SPACING_SET 0x%x", Si4709_IOC_CHAN_SPACING_SET); + + debug("Si4709_IOC_CHAN_SELECT 0x%x", Si4709_IOC_CHAN_SELECT); + + debug("Si4709_IOC_CHAN_GET 0x%x", Si4709_IOC_CHAN_GET); + + debug("Si4709_IOC_SEEK_UP 0x%x", Si4709_IOC_SEEK_UP); + + debug("Si4709_IOC_SEEK_DOWN 0x%x", Si4709_IOC_SEEK_DOWN); + + /*VNVS:28OCT'09---- Si4709_IOC_SEEK_AUTO is disabled as of now */ + /* debug("Si4709_IOC_SEEK_AUTO 0x%x", Si4709_IOC_SEEK_AUTO); */ + + debug("Si4709_IOC_RSSI_SEEK_TH_SET 0x%x", Si4709_IOC_RSSI_SEEK_TH_SET); + + debug("Si4709_IOC_SEEK_SNR_SET 0x%x", Si4709_IOC_SEEK_SNR_SET); + + debug("Si4709_IOC_SEEK_CNT_SET 0x%x", Si4709_IOC_SEEK_CNT_SET); + + debug("Si4709_IOC_CUR_RSSI_GET 0x%x", Si4709_IOC_CUR_RSSI_GET); + + debug("Si4709_IOC_VOLEXT_ENB 0x%x", Si4709_IOC_VOLEXT_ENB); + + debug("Si4709_IOC_VOLEXT_DISB 0x%x", Si4709_IOC_VOLEXT_DISB); + + debug("Si4709_IOC_VOLUME_SET 0x%x", Si4709_IOC_VOLUME_SET); + + debug("Si4709_IOC_VOLUME_GET 0x%x", Si4709_IOC_VOLUME_GET); + + debug("Si4709_IOC_MUTE_ON 0x%x", Si4709_IOC_MUTE_ON); + + debug("Si4709_IOC_MUTE_OFF 0x%x", Si4709_IOC_MUTE_OFF); + + debug("Si4709_IOC_MONO_SET 0x%x", Si4709_IOC_MONO_SET); + + debug("Si4709_IOC_STEREO_SET 0x%x", Si4709_IOC_STEREO_SET); + + debug("Si4709_IOC_RSTATE_GET 0x%x", Si4709_IOC_RSTATE_GET); + + debug("Si4709_IOC_RDS_DATA_GET 0x%x", Si4709_IOC_RDS_DATA_GET); + + debug("Si4709_IOC_RDS_ENABLE 0x%x", Si4709_IOC_RDS_ENABLE); + + debug("Si4709_IOC_RDS_DISABLE 0x%x", Si4709_IOC_RDS_DISABLE); + + debug("Si4709_IOC_RDS_TIMEOUT_SET 0x%x", Si4709_IOC_RDS_TIMEOUT_SET); + + debug("Si4709_IOC_DEVICE_ID_GET 0x%x", Si4709_IOC_DEVICE_ID_GET); + + debug("Si4709_IOC_CHIP_ID_GET 0x%x", Si4709_IOC_CHIP_ID_GET); + + debug("Si4709_IOC_SYS_CONFIG2_GET 0x%x", Si4709_IOC_SYS_CONFIG2_GET); + + debug("Si4709_IOC_POWER_CONFIG_GET 0x%x", Si4709_IOC_POWER_CONFIG_GET); + + debug("Si4709_IOC_AFCRL_GET 0x%x", Si4709_IOC_AFCRL_GET); + + debug("Si4709_IOC_DE_SET 0x%x", Si4709_IOC_DE_SET); + + debug("Si4709_IOC_DSMUTE_ON 0x%x", Si4709_IOC_DSMUTE_ON); + + debug("Si4709_IOC_DSMUTE_OFF 0x%x", Si4709_IOC_DSMUTE_OFF); + + debug("Si4709_IOC_RESET_RDS_DATA 0x%x", Si4709_IOC_RESET_RDS_DATA); + + debug("------------------------------------------------\n"); +} + +int __init Si4709_driver_init(void) +{ + int ret = 0; + + debug("Si4709_driver_init called\n"); + + /*Initialize the Si4709 dev mutex */ + Si4709_dev_mutex_init(); + + /*misc device registration */ + ret = misc_register(&Si4709_misc_device); + if (ret < 0) { + error("Si4709_driver_init misc_register failed\n"); + return ret; + } + + if (system_rev >= 0x7) { + Si4709_int = GPIO_FM_INT_REV07; + Si4709_irq = gpio_to_irq(GPIO_FM_INT_REV07); + } else { + Si4709_int = GPIO_FM_INT; + Si4709_irq = gpio_to_irq(GPIO_FM_INT); + } + + s3c_gpio_cfgpin(Si4709_int, S3C_GPIO_SFN(0xF)); + s3c_gpio_setpull(Si4709_int, S3C_GPIO_PULL_NONE); + + irq_set_irq_type(Si4709_irq, IRQ_TYPE_EDGE_FALLING); + + /*KGVS: Configuring the GPIO_FM_INT in mach-jupiter.c */ + ret = request_irq(Si4709_irq, Si4709_isr, IRQF_DISABLED, + "Si4709", NULL); + if (ret) { + error("Si4709_driver_init request_irq " + "failed %d", Si4709_int); + goto MISC_DREG; + } else + debug("Si4709_driver_init request_irq " + "success %d", Si4709_int); + + if (gpio_is_valid(FM_RESET)) { + if (gpio_request(FM_RESET, FM_PORT)) + debug(KERN_ERR "Failed to request " + "FM_RESET!\n"); + gpio_direction_output(FM_RESET, GPIO_LEVEL_LOW); + } + + /*VNVS: 13-OCT'09---- + Initially Pulling the interrupt pin HIGH + as the FM Radio device gives 5ms low pulse*/ + s3c_gpio_setpull(Si4709_int, S3C_GPIO_PULL_UP); + + /****Resetting the device****/ + gpio_set_value(FM_RESET, GPIO_LEVEL_LOW); + gpio_set_value(FM_RESET, GPIO_LEVEL_HIGH); + /*VNVS: 13-OCT'09---- Freeing the FM_RESET pin */ + + gpio_free(FM_RESET); + + /*Add the i2c driver */ + ret = Si4709_i2c_drv_init(); + if (ret < 0) + goto MISC_IRQ_DREG; + + init_waitqueue_head(&Si4709_waitq); + + debug("Si4709_driver_init successful\n"); + + return ret; + +MISC_IRQ_DREG: + free_irq(Si4709_irq, NULL); +MISC_DREG: + misc_deregister(&Si4709_misc_device); + + return ret; +} + +void __exit Si4709_driver_exit(void) +{ + debug("Si4709_driver_exit called\n"); + + /*Delete the i2c driver */ + Si4709_i2c_drv_exit(); + free_irq(Si4709_irq, NULL); + + /*misc device deregistration */ + misc_deregister(&Si4709_misc_device); +} + +module_init(Si4709_driver_init); +module_exit(Si4709_driver_exit); +MODULE_AUTHOR("Varun Mahajan <m.varun@samsung.com>"); +MODULE_DESCRIPTION("Si4709 FM tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/samsung/fm_si4709/Si4709_main.h b/drivers/samsung/fm_si4709/Si4709_main.h new file mode 100644 index 0000000..61d8872 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_main.h @@ -0,0 +1,8 @@ +#ifndef _Si4709_MAIN_H +#define _Si4709_MAIN_H + +#include <linux/wait.h> + +extern wait_queue_head_t Si4709_waitq; + +#endif diff --git a/drivers/samsung/fm_si4709/Si4709_regs.h b/drivers/samsung/fm_si4709/Si4709_regs.h new file mode 100644 index 0000000..aa77651 --- /dev/null +++ b/drivers/samsung/fm_si4709/Si4709_regs.h @@ -0,0 +1,799 @@ +#ifndef _Si4709_REGS_H +#define _Si4709_REGS_H + +#define NUM_OF_REGISTERS 0x10 + +/*Si4709 registers*/ +#define DEVICE_ID 0x00 +#define CHIP_ID 0x01 +#define POWERCFG 0x02 +#define CHANNEL 0x03 +#define SYSCONFIG1 0x04 +#define SYSCONFIG2 0x05 +#define SYSCONFIG3 0x06 +#define TEST1 0x07 +#define TEST2 0x08 +#define BOOTCONFIG 0x09 +#define STATUSRSSI 0x0A +#define READCHAN 0x0B +#define RDSA 0x0C +#define RDSB 0x0D +#define RDSC 0x0E +#define RDSD 0x0F + +/***********POWERCFG************/ +#define POWERCFG_DSMUTE 0x8000 +#define POWERCFG_DMUTE 0x4000 +#define POWERCFG_MONO 0x2000 +#define POWERCFG_RDSM 0x0800 +#define POWERCFG_SKMODE 0x0400 +#define POWERCFG_SEEKUP 0x0200 +#define POWERCFG_SEEK 0x0100 +#define POWERCFG_DISABLE 0x0040 +#define POWERCFG_ENABLE 0x0001 +/************************************/ + +/***********CHANNEL************/ +#define CHANNEL_TUNE 0x8000 +/************************************/ + +/***********SYSCONFIG1************/ +#define SYSCONFIG1_RDSIEN 0x8000 +#define SYSCONFIG1_STCIEN 0x4000 +#define SYSCONFIG1_RDS 0x1000 +#define SYSCONFIG1_DE 0x0800 +#define SYSCONFIG1_AGCD 0x0400 +#define SYSCONFIG1_BLNDADJ1 0x0080 +#define SYSCONFIG1_BLNDADJ0 0x0040 +#define SYSCONFIG1_GPO1 0x0008 +#define SYSCONFIG1_GPO0 0x0004 +/************************************/ + +/***********SYSCONFIG2************/ +#define SYSCONFIG2_BAND1 0x0080 +#define SYSCONFIG2_BAND0 0x0040 +#define SYSCONFIG2_SPACE1 0x0020 +#define SYSCONFIG2_SPACE0 0x0010 +#define SYSCONFIG2_VOLUME3 0x0008 +#define SYSCONFIG2_VOLUME2 0x0004 +#define SYSCONFIG2_VOLUME1 0x0002 +#define SYSCONFIG2_VOLUME0 0x0001 +/************************************/ + +/***********SYSCONFIG3************/ +#define SYSCONFIG3_SMUTER1 0x8000 +#define SYSCONFIG3_SMUTER0 0x4000 +#define SYSCONFIG3_SMUTEA1 0x2000 +#define SYSCONFIG3_SMUTEA0 0x1000 +#define SYSCONFIG3_VOLEXT 0x0100 +#define SYSCONFIG3_SKSNR3 0x0080 +#define SYSCONFIG3_SKSNR2 0x0040 +#define SYSCONFIG3_SKSNR1 0x0020 +#define SYSCONFIG3_SKSNR0 0x0010 +#define SYSCONFIG3_SKCNT3 0x0008 +#define SYSCONFIG3_SKCNT2 0x0004 +#define SYSCONFIG3_SKCNT1 0x0002 +#define SYSCONFIG3_SKCNT0 0x0001 +/************************************/ + +/***********TEST1************/ +#define TEST1_AHIZEN 0x4000 +/************************************/ + +/***********STATUSRSSI************/ +#define STATUSRSSI_RDSR 0x8000 +#define STATUSRSSI_STC 0x4000 +#define STATUSRSSI_SF_BL 0x2000 +#define STATUSRSSI_AFCRL 0x1000 +#define STATUSRSSI_RDSS 0x0800 +#define STATUSRSSI_BLERA1 0x0400 +#define STATUSRSSI_BLERA0 0x0200 +#define STATUSRSSI_ST 0x0100 +/************************************/ + +/***********READCHAN************/ +#define READCHAN_BLERB1 0x8000 +#define READCHAN_BLERB0 0x4000 +#define READCHAN_BLERC1 0x2000 +#define READCHAN_BLERC0 0x1000 +#define READCHAN_BLERD1 0x0800 +#define READCHAN_BLERD0 0x0400 + +#define READCHAN_CHAN_MASK 0x03FF +/************************************/ + +/*************************************************************/ +static inline void switch_on_bits(u16 *data, u16 bits_on) +{ + *data |= bits_on; +} + +static inline void switch_off_bits(u16 *data, u16 bits_off) +{ + u16 aux = 0xFFFF; + aux ^= bits_off; + *data &= aux; +} + +#define BIT_ON 1 +#define BIT_OFF 0 + +static inline int check_bit(u16 data, u16 bit) +{ + return data & bit ? BIT_ON : BIT_OFF; +} + +/**************************************************************/ + +/********************************************************************/ +static inline void POWERCFG_BITSET_ENABLE_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_ENABLE)); +} + +static inline void POWERCFG_BITSET_DISABLE_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_DISABLE)); +} + +static inline void POWERCFG_BITSET_DISABLE_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_DISABLE)); +} + +static inline void POWERCFG_BITSET_DMUTE_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_DMUTE)); +} + +static inline void POWERCFG_BITSET_DMUTE_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_DMUTE)); +} + +static inline void POWERCFG_BITSET_DSMUTE_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_DSMUTE)); +} + +static inline void POWERCFG_BITSET_DSMUTE_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_DSMUTE)); +} + +static inline void POWERCFG_BITSET_MONO_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_MONO)); +} + +static inline void POWERCFG_BITSET_MONO_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_MONO)); +} + +static inline void POWERCFG_BITSET_RDSM_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_RDSM)); +} + +static inline void POWERCFG_BITSET_RDSM_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_RDSM)); +} + +static inline void POWERCFG_BITSET_SKMODE_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_SKMODE)); +} + +static inline void POWERCFG_BITSET_SKMODE_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_SKMODE)); +} + +static inline void POWERCFG_BITSET_SEEKUP_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_SEEKUP)); +} + +static inline void POWERCFG_BITSET_SEEKUP_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_SEEKUP)); +} + +static inline void POWERCFG_BITSET_SEEK_HIGH(u16 *data) +{ + switch_on_bits(data, (POWERCFG_SEEK)); +} + +static inline void POWERCFG_BITSET_SEEK_LOW(u16 *data) +{ + switch_off_bits(data, (POWERCFG_SEEK)); +} + +static inline void POWERCFG_BITSET_RESERVED(u16 *data) +{ + *data &= 0xEF41; +} + +/********************************************************************/ + +static inline void CHANNEL_BITSET_TUNE_HIGH(u16 *data) +{ + switch_on_bits(data, (CHANNEL_TUNE)); +} + +static inline void CHANNEL_BITSET_TUNE_LOW(u16 *data) +{ + switch_off_bits(data, (CHANNEL_TUNE)); +} + +static inline void CHANNEL_BITSET_RESERVED(u16 *data) +{ + *data &= 0x83FF; +} + +/********************************************************************/ + +static inline void SYSCONFIG1_BITSET_RDSIEN_HIGH(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_RDSIEN)); +} + +static inline void SYSCONFIG1_BITSET_RDSIEN_LOW(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_RDSIEN)); +} + +static inline void SYSCONFIG1_BITSET_STCIEN_HIGH(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_STCIEN)); +} + +static inline void SYSCONFIG1_BITSET_STCIEN_LOW(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_STCIEN)); +} + +static inline void SYSCONFIG1_BITSET_RDS_HIGH(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_RDS)); +} + +static inline void SYSCONFIG1_BITSET_RDS_LOW(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_RDS)); +} + +static inline void SYSCONFIG1_BITSET_DE_50(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_DE)); +} + +static inline void SYSCONFIG1_BITSET_DE_75(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_DE)); +} + +static inline void SYSCONFIG1_BITSET_AGCD_HIGH(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_AGCD)); +} + +static inline void SYSCONFIG1_BITSET_AGCD_LOW(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_AGCD)); +} + +static inline void SYSCONFIG1_BITSET_RSSI_DEF_31_49(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_BLNDADJ1 | SYSCONFIG1_BLNDADJ0)); +} + +static inline void SYSCONFIG1_BITSET_RSSI_37_55(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_BLNDADJ0)); + switch_off_bits(data, (SYSCONFIG1_BLNDADJ1)); +} + +static inline void SYSCONFIG1_BITSET_RSSI_19_37(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_BLNDADJ1)); + switch_off_bits(data, (SYSCONFIG1_BLNDADJ0)); +} + +static inline void SYSCONFIG1_BITSET_RSSI_25_43(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_BLNDADJ1 | SYSCONFIG1_BLNDADJ0)); +} + +static inline void SYSCONFIG1_BITSET_GPIO_HIGH_IMP(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG1_GPO1 | SYSCONFIG1_GPO0)); +} + +static inline void SYSCONFIG1_BITSET_GPIO_STC_RDS_INT(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_GPO0)); + switch_off_bits(data, (SYSCONFIG1_GPO1)); +} + +static inline void SYSCONFIG1_BITSET_GPIO_LOW(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_GPO1)); + switch_off_bits(data, (SYSCONFIG1_GPO0)); +} + +static inline void SYSCONFIG1_BITSET_GPIO_HIGH(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG1_GPO1 | SYSCONFIG1_GPO0)); +} + +static inline void SYSCONFIG1_BITSET_RESERVED(u16 *data) +{ + *data &= 0xDCCC; + *data |= 0x22; +} + +/********************************************************************/ + +/*US/EUROPE (Default)*/ +static inline void SYSCONFIG2_BITSET_BAND_87p5_108_MHz(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG2_BAND1 | SYSCONFIG2_BAND0)); +} + +/*Japan wide band*/ +static inline void SYSCONFIG2_BITSET_BAND_76_108_MHz(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG2_BAND0)); + switch_off_bits(data, (SYSCONFIG2_BAND1)); +} + +/*Japan*/ +static inline void SYSCONFIG2_BITSET_BAND_76_90_MHz(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG2_BAND1)); + switch_off_bits(data, (SYSCONFIG2_BAND0)); +} + +/*US, Australia (Default)*/ +static inline void SYSCONFIG2_BITSET_SPACE_200_KHz(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG2_SPACE1 | SYSCONFIG2_SPACE0)); +} + +/*Europe, Japan*/ +static inline void SYSCONFIG2_BITSET_SPACE_100_KHz(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG2_SPACE0)); + switch_off_bits(data, (SYSCONFIG2_SPACE1)); +} + +static inline void SYSCONFIG2_BITSET_SPACE_50_KHz(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG2_SPACE1)); + switch_off_bits(data, (SYSCONFIG2_SPACE0)); +} + +static inline void SYSCONFIG2_BITSET_VOLUME(u16 *data, u8 volume) +{ + *data &= 0xFFF0; + *data |= (volume & 0x0F); +} + +static inline void SYSCONFIG2_BITSET_SEEKTH(u16 *data, u8 seek_th) +{ + *data &= 0x00FF; + *data |= ((seek_th << 8) & 0xFF00); +} + +static inline u8 SYSCONFIG2_GET_VOLUME(u16 data) +{ + return (u8) (data & 0x000F); +} + +/********************************************************************/ + +static inline void SYSCONFIG3_BITSET_SMUTER_FASTEST(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_SMUTER1 | SYSCONFIG3_SMUTER0)); +} + +static inline void SYSCONFIG3_BITSET_SMUTER_FAST(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_SMUTER1)); + switch_on_bits(data, (SYSCONFIG3_SMUTER0)); +} + +static inline void SYSCONFIG3_BITSET_SMUTER_SLOW(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_SMUTER0)); + switch_on_bits(data, (SYSCONFIG3_SMUTER1)); +} + +static inline void SYSCONFIG3_BITSET_SMUTER_SLOWEST(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG3_SMUTER1 | SYSCONFIG3_SMUTER0)); +} + +static inline void SYSCONFIG3_BITSET_SMUTEA_16_dB(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_SMUTEA1 | SYSCONFIG3_SMUTEA0)); +} + +static inline void SYSCONFIG3_BITSET_SMUTEA_14dB(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_SMUTEA1)); + switch_on_bits(data, (SYSCONFIG3_SMUTEA0)); +} + +static inline void SYSCONFIG3_BITSET_SMUTEA_12dB(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_SMUTEA0)); + switch_on_bits(data, (SYSCONFIG3_SMUTEA1)); +} + +static inline void SYSCONFIG3_BITSET_SMUTEA_10dB(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG3_SMUTEA1 | SYSCONFIG3_SMUTEA0)); +} + +static inline void SYSCONFIG3_BITSET_VOLEXT_DISB(u16 *data) +{ + switch_off_bits(data, (SYSCONFIG3_VOLEXT)); +} + +static inline void SYSCONFIG3_BITSET_VOLEXT_ENB(u16 *data) +{ + switch_on_bits(data, (SYSCONFIG3_VOLEXT)); +} + +static inline void SYSCONFIG3_BITSET_SKSNR(u16 *data, u8 seeksnr) +{ + *data &= 0xFF0F; + *data |= ((seeksnr << 4) & 0xF0); +} + +static inline void SYSCONFIG3_BITSET_SKCNT(u16 *data, u8 seekcnt) +{ + *data &= 0xFFF0; + *data |= ((seekcnt) & 0x0F); +} + +static inline void SYSCONFIG3_BITSET_RESERVED(u16 *data) +{ + *data &= 0xF1FF; +} + +/********************************************************************/ + +static inline void TEST1_BITSET_AHIZEN_HIGH(u16 *data) +{ + switch_on_bits(data, (TEST1_AHIZEN)); +} + +static inline void TEST1_BITSET_AHIZEN_LOW(u16 *data) +{ + switch_off_bits(data, (TEST1_AHIZEN)); +} + +static inline void TEST1_BITSET_RESERVED(u16 *data) +{ + *data &= 0x7FFF; +} + +/********************************************************************/ + +#define NEW_RDS_GROUP_READY 1 +#define NO_RDS_GROUP_READY 0 + +#define COMPLETE 1 +#define CLEAR 0 + +#define SEEK_SUCCESSFUL 1 +#define SEEK_FAILURE_BAND_LMT_RCHD 0 + +#define AFC_RAILED 1 +#define AFC_NOT_RAILED 0 + +#define RDS_DECODER_SYNCHRONIZED 1 +#define RDS_DECODER_NOT_SYNCHRONIZED 0 + +#define STEREO 1 +#define MONO 0 + +#define ERRORS_0 0 +#define ERRORS_1_2 1 +#define ERRORS_3_5 2 +#define ERRORS_NO_CORREC_POSSIBLE_6_p 3 + +static inline int STATUSRSSI_RDS_READY_STATUS(u16 data) +{ + if (check_bit(data, STATUSRSSI_RDSR) == BIT_ON) + return NEW_RDS_GROUP_READY; + else + return NO_RDS_GROUP_READY; +} + +static inline int STATUSRSSI_SEEK_TUNE_STATUS(u16 data) +{ + if (check_bit(data, STATUSRSSI_STC) == BIT_ON) + return COMPLETE; + else + return CLEAR; +} + +static inline int STATUSRSSI_SF_BL_STATUS(u16 data) +{ + if (check_bit(data, STATUSRSSI_SF_BL) == BIT_ON) + return SEEK_FAILURE_BAND_LMT_RCHD; + else + return SEEK_SUCCESSFUL; +} + +static inline int STATUSRSSI_AFC_RAIL_STATUS(u16 data) +{ + if (check_bit(data, STATUSRSSI_AFCRL) == BIT_ON) + return AFC_RAILED; + else + return AFC_NOT_RAILED; +} + +static inline int STATUSRSSI_RDS_SYNC_STATUS(u16 data) +{ + if (check_bit(data, STATUSRSSI_RDSS) == BIT_ON) + return RDS_DECODER_SYNCHRONIZED; + else + return RDS_DECODER_NOT_SYNCHRONIZED; +} + +static inline int STATUSRSSI_RDS_BLOCK_A_ERRORS(u16 data) +{ + int ret = 0; + int bits_status = 0; + + ret = check_bit(data, STATUSRSSI_BLERA1); + + if (ret == BIT_ON) + bits_status = 0x02; + else + bits_status = 0x00; + + ret = check_bit(data, STATUSRSSI_BLERA0); + + if (ret == BIT_ON) + bits_status |= 0x01; + + return bits_status; +} + +static inline int STATUSRSSI_STEREO_STATUS(u16 data) +{ + if (check_bit(data, STATUSRSSI_RDSS) == BIT_ON) + return STEREO; + else + return MONO; +} + +static inline u8 STATUSRSSI_RSSI_SIGNAL_STRENGTH(u16 data) +{ + return (u8) (0x00FF & data); +} + +static inline u8 DEVICE_ID_PART_NUMBER(u16 data) +{ + data = data >> 12; + return (u8) (0x000F & data); +} + +static inline u16 DEVICE_ID_MANUFACT_NUMBER(u16 data) +{ + return (u16) (0x0FFF & data); +} + +static inline u8 CHIP_ID_CHIP_VERSION(u16 data) +{ + data = data >> 10; + return (u8) (0x003F & data); +} + +static inline u8 CHIP_ID_DEVICE(u16 data) +{ + data = data >> 6; + return (u8) (0x000F & data); +} + +static inline u8 CHIP_ID_FIRMWARE_VERSION(u16 data) +{ + return (u8) (0x003F & data); +} + +static inline u8 SYS_CONFIG2_RSSI_TH(u16 data) +{ + data = data >> 8; + return (u8) (0x00FF & data); +} + +static inline u8 SYS_CONFIG2_FM_BAND(u16 data) +{ + data = data >> 6; + return (u8) (0x0003 & data); +} + +static inline u8 SYS_CONFIG2_FM_CHAN_SPAC(u16 data) +{ + data = data >> 4; + return (u8) (0x0003 & data); +} + +static inline u8 SYS_CONFIG2_FM_VOL(u16 data) +{ + return (u8) (0x000F & data); +} + +/*POWER_CONFIG_STATUS*/ + +#define SOFTMUTE_ENABLE 0 +#define SOFTMUTE_DISABLE 1 + +#define MUTE_ENABLE 0 +#define MUTE_DISABLE 1 + +#define STEREO_SELECT 0 +#define MONO_SELECT 1 + +#define RDS_MODE_STANDARD 0 +#define RDS_MODE_VERBOSE 1 + +#define SEEK_MODE_CONT_SEEK 0 +#define SEEK_MODE_STOP_SEEK 1 + +#define SEEK_DOWN 0 +#define SEEK_UP 1 + +#define SEEK_DISABLE 0 +#define SEEK_ABLE 1 + +static inline int POWER_CONFIG_SOFTMUTE_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_DSMUTE) == BIT_ON) + return SOFTMUTE_DISABLE; + else + return SOFTMUTE_ENABLE; +} + +static inline int POWER_CONFIG_MUTE_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_DMUTE) == BIT_ON) + return MUTE_DISABLE; + else + return MUTE_ENABLE; +} + +static inline int POWER_CONFIG_MONO_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_MONO) == BIT_ON) + return MONO_SELECT; + else + return STEREO_SELECT; +} + +static inline int POWER_CONFIG_RDS_MODE_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_RDSM) == BIT_ON) + return RDS_MODE_VERBOSE; + else + return RDS_MODE_STANDARD; +} + +static inline int POWER_CONFIG_SKMODE_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_SKMODE) == BIT_ON) + return SEEK_MODE_STOP_SEEK; + else + return SEEK_MODE_CONT_SEEK; +} + +static inline int POWER_CONFIG_SEEKUP_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_SEEKUP) == BIT_ON) + return SEEK_UP; + else + return SEEK_DOWN; +} + +static inline int POWER_CONFIG_SEEK_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_SEEK) == BIT_ON) + return SEEK_ABLE; + else + return SEEK_DISABLE; +} + +static inline int POWER_CONFIG_DISABLE_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_DISABLE) == BIT_ON) + return 1; + else + return 0; +} + +static inline int POWER_CONFIG_ENABLE_STATUS(u16 data) +{ + if (check_bit(data, POWERCFG_ENABLE) == BIT_ON) + return 1; + else + return 0; +} + +/********************************************************************/ + +static inline int READCHAN_BLOCK_B_ERRORS(u16 data) +{ + int ret = 0; + int bits_status = 0; + + ret = check_bit(data, READCHAN_BLERB1); + + if (ret == BIT_ON) + bits_status = 0x02; + else + bits_status = 0x00; + + ret = check_bit(data, READCHAN_BLERB0); + + if (ret == BIT_ON) + bits_status |= 0x01; + + return bits_status; +} + +static inline int READCHAN_BLOCK_C_ERRORS(u16 data) +{ + int ret = 0; + int bits_status = 0; + + ret = check_bit(data, READCHAN_BLERC1); + + if (ret == BIT_ON) + bits_status = 0x02; + else + bits_status = 0x00; + + ret = check_bit(data, READCHAN_BLERC0); + + if (ret == BIT_ON) + bits_status |= 0x01; + + return bits_status; +} + +static inline int READCHAN_BLOCK_D_ERRORS(u16 data) +{ + int ret = 0; + int bits_status = 0; + + ret = check_bit(data, READCHAN_BLERD1); + + if (ret == BIT_ON) + bits_status = 0x02; + else + bits_status = 0x00; + + ret = check_bit(data, READCHAN_BLERD0); + + if (ret == BIT_ON) + bits_status |= 0x01; + + return bits_status; +} + +static inline u16 READCHAN_GET_CHAN(u16 data) +{ + return data & READCHAN_CHAN_MASK; +} + +/********************************************************************/ + +#endif diff --git a/drivers/samsung/fm_si4709/commanddefs.h b/drivers/samsung/fm_si4709/commanddefs.h new file mode 100644 index 0000000..74ca6d2 --- /dev/null +++ b/drivers/samsung/fm_si4709/commanddefs.h @@ -0,0 +1,192 @@ +/* This file contains the command definitions for the Si47xx Parts. */ +#ifndef _COMMAND_DEFS_H_ +#define _COMMAND_DEFS_H_ + +/*================================================================== +General Commands +==================================================================*/ + +/* STATUS bits - Used by all methods */ +#define STCINT 0x01 +#define ASQINT 0x02 +#define RDSINT 0x04 +#define RSQINT 0x08 +#define ERR 0x40 +#define CTS 0x80 + +/* POWER_UP */ +#define POWER_UP 0x01 +#define POWER_UP_IN_FUNC_FMRX 0x00 +#define POWER_UP_IN_FUNC_AMRX 0x01 +#define POWER_UP_IN_FUNC_FMTX 0x02 +#define POWER_UP_IN_FUNC_WBRX 0x03 +#define POWER_UP_IN_FUNC_QUERY 0x0F +#define POWER_UP_IN_PATCH 0x20 +#define POWER_UP_IN_GPO2OEN 0x40 +#define POWER_UP_IN_CTSIEN 0x80 +#define POWER_UP_IN_OPMODE_RX_ANALOG 0x05 +#define POWER_UP_IN_OPMODE_TX_ANALOG 0x50 + +/* GET_REV */ +#define GET_REV 0x10 + +/* POWER_DOWN */ +#define POWER_DOWN 0x11 + +/* SET_PROPERTY */ +#define SET_PROPERTY 0x12 + +/* GET_PROPERTY */ +#define GET_PROPERTY 0x13 + +/* GET_INT_STATUS */ +#define GET_INT_STATUS 0x14 + +/*================================================================== + FM Receive Commands +==================================================================*/ + +/* FM_TUNE_FREQ */ +#define FM_TUNE_FREQ 0x20 + +/* FM_SEEK_START */ +#define FM_SEEK_START 0x21 +#define FM_SEEK_START_IN_WRAP 0x04 +#define FM_SEEK_START_IN_SEEKUP 0x08 + +/* FM_TUNE_STATUS */ +#define FM_TUNE_STATUS 0x22 +#define FM_TUNE_STATUS_IN_INTACK 0x01 +#define FM_TUNE_STATUS_IN_CANCEL 0x02 +#define FM_TUNE_STATUS_OUT_VALID 0x01 +#define FM_TUNE_STATUS_OUT_AFCRL 0x02 +#define FM_TUNE_STATUS_OUT_BTLF 0x80 + +/* FM_RSQ_STATUS */ +#define FM_RSQ_STATUS 0x23 +#define FM_RSQ_STATUS_IN_INTACK 0x01 +#define FM_RSQ_STATUS_OUT_RSSILINT 0x01 +#define FM_RSQ_STATUS_OUT_RSSIHINT 0x02 +#define FM_RSQ_STATUS_OUT_ASNRLINT 0x04 +#define FM_RSQ_STATUS_OUT_ASNRHINT 0x08 +#define FM_RSQ_STATUS_OUT_BLENDINT 0x80 +#define FM_RSQ_STATUS_OUT_VALID 0x01 +#define FM_RSQ_STATUS_OUT_AFCRL 0x02 +#define FM_RSQ_STATUS_OUT_SMUTE 0x08 +#define FM_RSQ_STATUS_OUT_PILOT 0x80 +#define FM_RSQ_STATUS_OUT_STBLEND 0x7F + +/* FM_RDS_STATUS */ +#define FM_RDS_STATUS 0x24 +#define FM_RDS_STATUS_IN_INTACK 0x01 +#define FM_RDS_STATUS_IN_MTFIFO 0x02 +#define FM_RDS_STATUS_OUT_RECV 0x01 +#define FM_RDS_STATUS_OUT_SYNCLOST 0x02 +#define FM_RDS_STATUS_OUT_SYNCFOUND 0x04 +#define FM_RDS_STATUS_OUT_SYNC 0x01 +#define FM_RDS_STATUS_OUT_GRPLOST 0x04 +#define FM_RDS_STATUS_OUT_BLED 0x03 +#define FM_RDS_STATUS_OUT_BLEC 0x0C +#define FM_RDS_STATUS_OUT_BLEB 0x30 +#define FM_RDS_STATUS_OUT_BLEA 0xC0 +#define FM_RDS_STATUS_OUT_BLED_SHFT 0 +#define FM_RDS_STATUS_OUT_BLEC_SHFT 2 +#define FM_RDS_STATUS_OUT_BLEB_SHFT 4 +#define FM_RDS_STATUS_OUT_BLEA_SHFT 6 + +/*================================================================== + AM Receive Commands +==================================================================*/ + +/* AM_TUNE_FREQ */ +#define AM_TUNE_FREQ 0x40 + +/* AM_SEEK_START */ +#define AM_SEEK_START 0x41 +#define AM_SEEK_START_IN_WRAP 0x04 +#define AM_SEEK_START_IN_SEEKUP 0x08 + +/* AM_TUNE_STATUS */ +#define AM_TUNE_STATUS 0x42 +#define AM_TUNE_STATUS_IN_INTACK 0x01 +#define AM_TUNE_STATUS_IN_CANCEL 0x02 +#define AM_TUNE_STATUS_OUT_VALID 0x01 +#define AM_TUNE_STATUS_OUT_AFCRL 0x02 +#define AM_TUNE_STATUS_OUT_BTLF 0x80 + +/* AM_RSQ_STATUS */ +#define AM_RSQ_STATUS 0x23 +#define AM_RSQ_STATUS_IN_INTACK 0x01 +#define AM_RSQ_STATUS_OUT_RSSILINT 0x01 +#define AM_RSQ_STATUS_OUT_RSSIHINT 0x02 +#define AM_RSQ_STATUS_OUT_ASNRLINT 0x04 +#define AM_RSQ_STATUS_OUT_ASNRHINT 0x08 +#define AM_RSQ_STATUS_OUT_VALID 0x01 +#define AM_RSQ_STATUS_OUT_AFCRL 0x02 +#define AM_RSQ_STATUS_OUT_SMUTE 0x08 + +/*================================================================== + FM Transmit Commands +==================================================================*/ + +/* TX_TUNE_FREQ */ +#define TX_TUNE_FREQ 0x30 + +/* TX_TUNE_POWER */ +#define TX_TUNE_POWER 0x31 + +/* TX_TUNE_MEASURE */ +#define TX_TUNE_MEASURE 0x32 + +/* TX_TUNE_STATUS */ +#define TX_TUNE_STATUS 0x33 +#define TX_TUNE_STATUS_IN_INTACK 0x01 + +/* TX_ASQ_STATUS */ +#define TX_ASQ_STATUS 0x34 +#define TX_ASQ_STATUS_IN_INTACK 0x01 +#define TX_ASQ_STATUS_OUT_IALL 0x01 +#define TX_ASQ_STATUS_OUT_IALH 0x02 +#define TX_ASQ_STATUS_OUT_OVERMOD 0x04 + +/* TX_RDS_BUFF */ +#define TX_RDS_BUFF 0x35 +#define TX_RDS_BUFF_IN_INTACK 0x01 +#define TX_RDS_BUFF_IN_MTBUFF 0x02 +#define TX_RDS_BUFF_IN_LDBUFF 0x04 +#define TX_RDS_BUFF_IN_FIFO 0x80 + +/* TX_RDS_PS */ +#define TX_RDS_PS 0x36 + +/*================================================================== + WB Receive Commands +==================================================================*/ + +/* WB_TUNE_FREQ */ +#define WB_TUNE_FREQ 0x50 + +/* WB_TUNE_STATUS */ +#define WB_TUNE_STATUS 0x52 +#define WB_TUNE_STATUS_IN_INTACK 0x01 +#define WB_TUNE_STATUS_OUT_VALID 0x01 +#define WB_TUNE_STATUS_OUT_AFCRL 0x02 + +/* WB_RSQ_STATUS */ +#define WB_RSQ_STATUS 0x53 +#define WB_RSQ_STATUS_IN_INTACK 0x01 +#define WB_RSQ_STATUS_OUT_RSSILINT 0x01 +#define WB_RSQ_STATUS_OUT_RSSIHINT 0x02 +#define WB_RSQ_STATUS_OUT_ASNRLINT 0x04 +#define WB_RSQ_STATUS_OUT_ASNRHINT 0x08 +#define WB_RSQ_STATUS_OUT_VALID 0x01 +#define WB_RSQ_STATUS_OUT_AFCRL 0x02 + +/* WB_ASQ_STATUS */ +#define WB_ASQ_STATUS 0x55 +#define WB_ASQ_STATUS_IN_INTACK 0x01 +#define WB_ASQ_STATUS_OUT_ALERTONINT 0x01 +#define WB_ASQ_STATUS_OUT_ALERTOFFINT 0x02 +#define WB_ASQ_STATUS_OUT_ALERT 0x01 + +#endif diff --git a/drivers/samsung/fm_si4709/propertydefs.h b/drivers/samsung/fm_si4709/propertydefs.h new file mode 100644 index 0000000..356e9f5 --- /dev/null +++ b/drivers/samsung/fm_si4709/propertydefs.h @@ -0,0 +1,578 @@ +/* This file contains the property definitions for the Si47xx Parts. */ +#ifndef _PROPERTY_DEFS_H_ +#define _PROPERTY_DEFS_H_ + +/*================================================================== + General Properties +==================================================================*/ + +/* GPO_IEN */ +#define GPO_IEN 0x0001 +#define GPO_IEN_STCIEN_MASK 0x0001 +#define GPO_IEN_ASQIEN_MASK 0x0002 +#define GPO_IEN_RDSIEN_MASK 0x0004 +#define GPO_IEN_RSQIEN_MASK 0x0008 +#define GPO_IEN_ERRIEN_MASK 0x0040 +#define GPO_IEN_CTSIEN_MASK 0x0080 +#define GPO_IEN_STCREP_MASK 0x0100 +#define GPO_IEN_ASQREP_MASK 0x0200 +#define GPO_IEN_RDSREP_MASK 0x0400 +#define GPO_IEN_RSQREP_MASK 0x0800 +#define GPO_IEN_STCIEN_SHFT 0 +#define GPO_IEN_ASQIEN_SHFT 1 +#define GPO_IEN_RDSIEN_SHFT 2 +#define GPO_IEN_RSQIEN_SHFT 3 +#define GPO_IEN_ERRIEN_SHFT 6 +#define GPO_IEN_CTSIEN_SHFT 7 +#define GPO_IEN_STCREP_SHFT 8 +#define GPO_IEN_ASQREP_SHFT 9 +#define GPO_IEN_RDSREP_SHFT 10 +#define GPO_IEN_RSQREP_SHFT 11 + +/* DIGITAL_INPUT_FORMAT */ +#define DIGITAL_INPUT_FORMAT 0x0101 +#define DIGITAL_INPUT_FORMAT_ISIZE_MASK 0x0003 +#define DIGITAL_INPUT_FORMAT_IMONO_MASK 0x0004 +#define DIGITAL_INPUT_FORMAT_IMODE_MASK 0x0078 +#define DIGITAL_INPUT_FORMAT_IFALL_MASK 0x0080 +#define DIGITAL_INPUT_FORMAT_ISIZE_SHFT 0 +#define DIGITAL_INPUT_FORMAT_IMONO_SHFT 2 +#define DIGITAL_INPUT_FORMAT_IMODE_SHFT 3 +#define DIGITAL_INPUT_FORMAT_IFALL_SHFT 7 + +/* DIGITAL_INPUT_SAMPLE_RATE */ +#define DIGITAL_INPUT_SAMPLE_RATE 0x0103 + +/* DIGITAL_OUTPUT_FORMAT */ +#define DIGITAL_OUTPUT_FORMAT 0x0102 +#define DIGITAL_OUTPUT_FORMAT_OSIZE_MASK 0x0003 +#define DIGITAL_OUTPUT_FORMAT_OMONO_MASK 0x0004 +#define DIGITAL_OUTPUT_FORMAT_OMODE_MASK 0x0078 +#define DIGITAL_OUTPUT_FORMAT_OFALL_MASK 0x0080 +#define DIGITAL_OUTPUT_FORMAT_OSIZE_SHFT 0 +#define DIGITAL_OUTPUT_FORMAT_OMONO_SHFT 2 +#define DIGITAL_OUTPUT_FORMAT_OMODE_SHFT 3 +#define DIGITAL_OUTPUT_FORMAT_OFALL_SHFT 7 + +/* DIGITAL_OUTPUT_SAMPLE_RATE */ +#define DIGITAL_OUTPUT_SAMPLE_RATE 0x0104 + +/* REFCLK_FREQ */ +#define REFCLK_FREQ 0x0201 + +/* REFCLK_PRESCALE */ +#define REFCLK_PRESCALE 0x0202 +#define REFCLK_PRESCALE_MASK 0x0FFF +#define REFCLK_PRESCALE_SHFT 0 + +/*================================================================== + FM Receive Properties +==================================================================*/ + +/* FM_DEEMPHASIS */ +#define FM_DEEMPHASIS 0x1100 +#define FM_DEEMPHASIS_MASK 0x0003 +#define FM_DEEMPHASIS_SHFT 0 + +/* FM_BLEND_STEREO_THRESHOLD */ +#define FM_BLEND_STEREO_THRESHOLD 0x1105 +#define FM_BLEND_STEREO_THRESHOLD_MASK 0x007F +#define FM_BLEND_STEREO_THRESHOLD_SHFT 0 + +/* FM_BLEND_MONO_THRESHOLD */ +#define FM_BLEND_MONO_THRESHOLD 0x1106 +#define FM_BLEND_MONO_THRESHOLD_MASK 0x007F +#define FM_BLEND_MONO_THRESHOLD_SHFT 0 + + +/* FM_MAX_TUNE_ERROR */ +#define FM_MAX_TUNE_ERROR 0x1108 +#define FM_MAX_TUNE_ERROR_MASK 0x007F +#define FM_MAX_TUNE_ERROR_SHFT 0 + +/* FM_RSQ_INT_SOURCE */ +#define FM_RSQ_INT_SOURCE 0x1200 +#define FM_RSQ_INT_SOURCE_RSSILIEN_MASK 0x0001 +#define FM_RSQ_INT_SOURCE_RSSIHIEN_MASK 0x0002 +#define FM_RSQ_INT_SOURCE_ASNRLIEN_MASK 0x0004 +#define FM_RSQ_INT_SOURCE_ASNRHIEN_MASK 0x0008 +#define FM_RSQ_INT_SOURCE_BLENDIEN_MASK 0x0080 +#define FM_RSQ_INT_SOURCE_RSSILIEN_SHFT 0 +#define FM_RSQ_INT_SOURCE_RSSIHIEN_SHFT 1 +#define FM_RSQ_INT_SOURCE_ASNRLIEN_SHFT 2 +#define FM_RSQ_INT_SOURCE_ASNRHIEN_SHFT 3 +#define FM_RSQ_INT_SOURCE_BLENDIEN_SHFT 7 + +/* FM_RSQ_SNR_HI_THRESHOLD */ +#define FM_RSQ_SNR_HI_THRESHOLD 0x1201 +#define FM_RSQ_SNR_HI_THRESHOLD_MASK 0x007F +#define FM_RSQ_SNR_HI_THRESHOLD_SHFT 0 + +/* FM_RSQ_SNR_LO_THRESHOLD */ +#define FM_RSQ_SNR_LO_THRESHOLD 0x1202 +#define FM_RSQ_SNR_LO_THRESHOLD_MASK 0x007F +#define FM_RSQ_SNR_LO_THRESHOLD_SHFT 0 + +/* FM_RSQ_RSSI_HI_THRESHOLD */ +#define FM_RSQ_RSSI_HI_THRESHOLD 0x1203 +#define FM_RSQ_RSSI_HI_THRESHOLD_MASK 0x007F +#define FM_RSQ_RSSI_HI_THRESHOLD_SHFT 0 + +/* FM_RSQ_RSSI_LO_THRESHOLD */ +#define FM_RSQ_RSSI_LO_THRESHOLD 0x1204 +#define FM_RSQ_RSSI_LO_THRESHOLD_MASK 0x007F +#define FM_RSQ_RSSI_LO_THRESHOLD_SHFT 0 + +/* FM_RSQ_BLEND_THRESHOLD */ +#define FM_RSQ_BLEND_THRESHOLD 0x1207 +#define FM_RSQ_BLEND_THRESHOLD_BLEND_MASK 0x007F +#define FM_RSQ_BLEND_THRESHOLD_PILOT_MASK 0x0080 +#define FM_RSQ_BLEND_THRESHOLD_BLEND_SHFT 0 +#define FM_RSQ_BLEND_THRESHOLD_PILOT_SHFT 7 + +/* FM_SOFT_MUTE_RATE */ +#define FM_SOFT_MUTE_RATE 0x1300 +#define FM_SOFT_MUTE_RATE_MASK 0x00FF +#define FM_SOFT_MUTE_RATE_SHFT 0 + +/* FM_SOFT_MUTE_MAX_ATTENUATION */ +#define FM_SOFT_MUTE_MAX_ATTENUATION 0x1302 +#define FM_SOFT_MUTE_MAX_ATTENUATION_MASK 0x001F +#define FM_SOFT_MUTE_MAX_ATTENUATION_SHFT 0 + +/* FM_SOFT_MUTE_SNR_THRESHOLD */ +#define FM_SOFT_MUTE_SNR_THRESHOLD 0x1303 +#define FM_SOFT_MUTE_SNR_THRESHOLD_MASK 0x000F +#define FM_SOFT_MUTE_SNR_THRESHOLD_SHFT 0 + +/* FM_SEEK_BAND_BOTTOM */ +#define FM_SEEK_BAND_BOTTOM 0x1400 + +/* FM_SEEK_BAND_TOP */ +#define FM_SEEK_BAND_TOP 0x1401 + +/* FM_SEEK_FREQ_SPACING */ +#define FM_SEEK_FREQ_SPACING 0x1402 +#define FM_SEEK_FREQ_SPACING_MASK 0x001F +#define FM_SEEK_FREQ_SPACING_SHFT 0 + +/* FM_SEEK_TUNE_SNR_THRESHOLD */ +#define FM_SEEK_TUNE_SNR_THRESHOLD 0x1403 +#define FM_SEEK_TUNE_SNR_THRESHOLD_MASK 0x007F +#define FM_SEEK_TUNE_SNR_THRESHOLD_SHFT 0 + +/* FM_SEEK_TUNE_RSSI_THRESHOLD */ +#define FM_SEEK_TUNE_RSSI_THRESHOLD 0x1404 +#define FM_SEEK_TUNE_RSSI_THRESHOLD_MASK 0x007F +#define FM_SEEK_TUNE_RSSI_THRESHOLD_SHFT 0 + +/* FM_RDS_INTERRUPT_SOURCE */ +#define FM_RDS_INTERRUPT_SOURCE 0x1500 +#define FM_RDS_INTERRUPT_SOURCE_RECV_MASK 0x0001 +#define FM_RDS_INTERRUPT_SOURCE_SYNCLOST_MASK 0x0002 +#define FM_RDS_INTERRUPT_SOURCE_SYNCFOUND_MASK 0x0004 +#define FM_RDS_INTERRUPT_SOURCE_RECV_SHFT 0 +#define FM_RDS_INTERRUPT_SOURCE_SYNCLOST_SHFT 1 +#define FM_RDS_INTERRUPT_SOURCE_SYNCFOUND_SHFT 2 + +/* FM_RDS_INTERRUPT_FIFO_COUNT */ +#define FM_RDS_INTERRUPT_FIFO_COUNT 0x1501 +#define FM_RDS_INTERRUPT_FIFO_COUNT_MASK 0x00FF +#define FM_RDS_INTERRUPT_FIFO_COUNT_SHFT 0 + +/* FM_RDS_CONFIG */ +#define FM_RDS_CONFIG 0x1502 +#define FM_RDS_CONFIG_RDSEN_MASK 0x0001 +#define FM_RDS_CONFIG_BLETHD_MASK 0x0300 +#define FM_RDS_CONFIG_BLETHC_MASK 0x0C00 +#define FM_RDS_CONFIG_BLETHB_MASK 0x3000 +#define FM_RDS_CONFIG_BLETHA_MASK 0xC000 +#define FM_RDS_CONFIG_RDSEN_SHFT 0 +#define FM_RDS_CONFIG_BLETHD_SHFT 8 +#define FM_RDS_CONFIG_BLETHC_SHFT 10 +#define FM_RDS_CONFIG_BLETHB_SHFT 12 +#define FM_RDS_CONFIG_BLETHA_SHFT 14 + +/*================================================================== + FM Transmit Properties +==================================================================*/ + +/* TX_COMPONENT_ENABLE */ +#define TX_COMPONENT_ENABLE 0x2100 +#define TX_COMPONENT_ENABLE_PILOT_MASK 0x0001 +#define TX_COMPONENT_ENABLE_LMR_MASK 0x0002 +#define TX_COMPONENT_ENABLE_RDS_MASK 0x0004 +#define TX_COMPONENT_ENABLE_PILOT_SHFT 0 +#define TX_COMPONENT_ENABLE_LMR_SHFT 1 +#define TX_COMPONENT_ENABLE_RDS_SHFT 2 + +/* TX_AUDIO_DEVIATION */ +#define TX_AUDIO_DEVIATION 0x2101 + +/* TX_PILOT_DEVIATION */ +#define TX_PILOT_DEVIATION 0x2102 + +/* TX_RDS_DEVIATION */ +#define TX_RDS_DEVIATION 0x2103 + +/* TX_LINE_INPUT_LEVEL */ +#define TX_LINE_INPUT_LEVEL 0x2104 +#define TX_LINE_INPUT_LEVEL_LILEVEL_MASK 0x03FF +#define TX_LINE_INPUT_LEVEL_LIATTEN_MASK 0x3000 +#define TX_LINE_INPUT_LEVEL_LILEVEL_SHFT 0 +#define TX_LINE_INPUT_LEVEL_LIATTEN_SHFT 12 + +/* TX_LINE_INPUT_MUTE */ +#define TX_LINE_INPUT_MUTE 0x2105 +#define TX_LINE_INPUT_MUTE_RIMUTE_MASK 0x0001 +#define TX_LINE_INPUT_MUTE_LIMUTE_MASK 0x0002 +#define TX_LINE_INPUT_MUTE_RIMUTE_SHFT 0 +#define TX_LINE_INPUT_MUTE_LIMUTE_SHFT 1 + +/* TX_PREEMPHASIS */ +#define TX_PREEMPHASIS 0x2106 +#define TX_PREMPHASIS_MASK 0x0003 +#define TX_PREMPHASIS_SHFT 0 + +/* TX_PILOT_FREQUENCY */ +#define TX_PILOT_FREQUENCY 0x2107 + +/* TX_ACOMP_ENABLE */ +#define TX_ACOMP_ENABLE 0x2200 +#define TX_ACOMP_ENABLE_ACEN_MASK 0x0001 +#define TX_ACOMP_ENABLE_LIMITEN_MASK 0x0002 +#define TX_ACOMP_ENABLE_ACEN_SHFT 0 +#define TX_ACOMP_ENABLE_LIMITEN_SHFT 1 + +/* TX_ACOMP_THRESHOLD */ +#define TX_ACOMP_THRESHOLD 0x2201 + +/* TX_ACOMP_ATTACK_TIME */ +#define TX_ACOMP_ATTACK_TIME 0x2202 +#define TX_ACOMP_ATTACK_TIME_MASK 0x000F +#define TX_ACOMP_ATTACK_TIME_SHFT 0 + +/* TX_ACOMP_RELEASE_TIME */ +#define TX_ACOMP_RELEASE_TIME 0x2203 +#define TX_ACOMP_RELEASE_TIME_MASK 0x0007 +#define TX_ACOMP_RELEASE_TIME_SHFT 0 + +/* TX_ACOMP_GAIN */ +#define TX_ACOMP_GAIN 0x2204 +#define TX_ACOMP_GAIN_MASK 0x003F +#define TX_ACOMP_GAIN_SHFT 0 + +/* TX_LIMITER_RELEASE_TIME */ +#define TX_LIMITER_RELEASE_TIME 0x2205 + +/* TX_ASQ_INT_SELECT */ +#define TX_ASQ_INT_SELECT 0x2300 +#define TX_ASQ_INT_SELECT_IALLIEN_MASK 0x0001 +#define TX_ASQ_INT_SELECT_IALHIEN_MASK 0x0002 +#define TX_ASQ_INT_SELECT_OVERMODIEN_MASK 0x0004 +#define TX_ASQ_INT_SELECT_IALLIEN_SHFT 0 +#define TX_ASQ_INT_SELECT_IALHIEN_SHFT 1 +#define TX_ASQ_INT_SELECT_OVERMODIEN_SHFT 2 + +/* TX_ASQ_LEVEL_LOW */ +#define TX_ASQ_LEVEL_LOW 0x2301 +#define TX_ASQ_LEVEL_LOW_MASK 0x00FF +#define TX_ASQ_LEVEL_LOW_SHFT 0 + +/* TX_ASQ_DURATION_LOW */ +#define TX_ASQ_DURATION_LOW 0x2302 + +/* TX_ASQ_LEVEL_HIGH */ +#define TX_ASQ_LEVEL_HIGH 0x2303 +#define TX_ASQ_LEVEL_HIGH_MASK 0x00FF +#define TX_ASQ_LEVEL_HIGH_SHFT 0 + +/* TX_ASQ_DURATION_LOW */ +#define TX_ASQ_DURATION_HIGH 0x2304 + +/* TX_RDS_INT_SOURCE */ +#define TX_RDS_INT_SOURCE 0x2C00 +#define TX_RDS_INT_SOURCE_FIFOMT_MASK 0x0001 +#define TX_RDS_INT_SOURCE_CBUFWRAP_MASK 0x0002 +#define TX_RDS_INT_SOURCE_FIFOXMIT_MASK 0x0004 +#define TX_RDS_INT_SOURCE_CBUFXMIT_MASK 0x0008 +#define TX_RDS_INT_SOURCE_PSXMIT_MASK 0x0010 +#define TX_RDS_INT_SOURCE_FIFOMT_SHFT 0 +#define TX_RDS_INT_SOURCE_CBUFWRAP_SHFT 1 +#define TX_RDS_INT_SOURCE_FIFOXMIT_SHFT 2 +#define TX_RDS_INT_SOURCE_CBUFXMIT_SHFT 3 +#define TX_RDS_INT_SOURCE_PSXMIT_SHFT 4 + +/* TX_RDS_PI */ +#define TX_RDS_PI 0x2C01 + +/* TX_RDS_PS_MIX */ +#define TX_RDS_PS_MIX 0x2C02 +#define TX_RDS_PS_MIX_MASK 0x0007 +#define TX_RDS_PS_MIX_SHFT 0 + +/* TX_RDS_PS_MISC */ +#define TX_RDS_PS_MISC 0x2C03 +#define TX_RDS_PS_MISC_RDSMS_MASK 0x0008 +#define TX_RDS_PS_MISC_RDSTA_MASK 0x0010 +#define TX_RDS_PS_MISC_RDSPTY_MASK 0x03E0 +#define TX_RDS_PS_MISC_RDSTP_MASK 0x0400 +#define TX_RDS_PS_MISC_FORCEB_MASK 0x0800 +#define TX_RDS_PS_MISC_RDSD0_MASK 0x1000 +#define TX_RDS_PS_MISC_RDSD1_MASK 0x2000 +#define TX_RDS_PS_MISC_RDSD2_MASK 0x4000 +#define TX_RDS_PS_MISC_RDSD3_MASK 0x8000 +#define TX_RDS_PS_MISC_RDSMS_SHFT 3 +#define TX_RDS_PS_MISC_RDSTA_SHFT 4 +#define TX_RDS_PS_MISC_RDSPTY_SHFT 5 +#define TX_RDS_PS_MISC_RDSTP_SHFT 10 +#define TX_RDS_PS_MISC_FORCEB_SHFT 11 +#define TX_RDS_PS_MISC_RDSD0_SHFT 12 +#define TX_RDS_PS_MISC_RDSD1_SHFT 13 +#define TX_RDS_PS_MISC_RDSD2_SHFT 14 +#define TX_RDS_PS_MISC_RDSD3_SHFT 15 + +/* TX_RDS_PS_REPEAT_COUNT */ +#define TX_RDS_PS_REPEAT_COUNT 0x2C04 +#define TX_RDS_PS_REPEAT_COUNT_MASK 0x00FF +#define TX_RDS_PS_REPEAT_COUNT_SHFT 0 + +/* TX_RDS_PS_MESSAGE_COUNT */ +#define TX_RDS_PS_MESSAGE_COUNT 0x2C05 +#define TX_RDS_PS_MESSAGE_COUNT_MASK 0x000F +#define TX_RDS_PS_MESSAGE_COUNT_SHFT 0 + +/* TX_RDS_PS_AF */ +#define TX_RDS_PS_AF 0x2C06 + +/* TX_RDS_FIFO_SIZE */ +#define TX_RDS_FIFO_SIZE 0x2C07 +#define TX_RDS_FIFO_SIZE_MASK 0x00FF +#define TX_RDS_FIFO_SIZE_SHFT 0 + + +/*================================================================== + AM Receive Properties +==================================================================*/ + +/* AM_DEEMPHASIS */ +#define AM_DEEMPHASIS 0x3100 +#define AM_DEEMPHASIS_MASK 0x0001 +#define AM_DEEMPHASIS_SHFT 0 + +/* AM_CHANNEL_FILTER */ +#define AM_CHANNEL_FILTER 0x3102 +#define AM_CHANNEL_FILTER_MASK 0x0007 +#define AM_CHANNEL_FILTER_SHFT 0 + +/* AM_RSQ_INT_SOURCE */ +#define AM_RSQ_INT_SOURCE 0x3200 +#define AM_RSQ_INT_SOURCE_RSSILIEN_MASK 0x0001 +#define AM_RSQ_INT_SOURCE_RSSIHIEN_MASK 0x0002 +#define AM_RSQ_INT_SOURCE_ASNRLIEN_MASK 0x0004 +#define AM_RSQ_INT_SOURCE_ASNRHIEN_MASK 0x0008 +#define AM_RSQ_INT_SOURCE_RSSILIEN_SHFT 0 +#define AM_RSQ_INT_SOURCE_RSSIHIEN_SHFT 1 +#define AM_RSQ_INT_SOURCE_ASNRLIEN_SHFT 2 +#define AM_RSQ_INT_SOURCE_ASNRHIEN_SHFT 3 + +/* AM_RSQ_SNR_HI_THRESHOLD */ +#define AM_RSQ_SNR_HI_THRESHOLD 0x3201 +#define AM_RSQ_SNR_HI_THRESHOLD_MASK 0x007F +#define AM_RSQ_SNR_HI_THRESHOLD_SHFT 0 + +/* AM_RSQ_SNR_LO_THRESHOLD */ +#define AM_RSQ_SNR_LO_THRESHOLD 0x3202 +#define AM_RSQ_SNR_LO_THRESHOLD_MASK 0x007F +#define AM_RSQ_SNR_LO_THRESHOLD_SHFT 0 + +/* AM_RSQ_RSSI_HI_THRESHOLD */ +#define AM_RSQ_RSSI_HI_THRESHOLD 0x3203 +#define AM_RSQ_RSSI_HI_THRESHOLD_MASK 0x007F +#define AM_RSQ_RSSI_HI_THRESHOLD_SHFT 0 + +/* AM_RSQ_RSSI_LO_THRESHOLD */ +#define AM_RSQ_RSSI_LO_THRESHOLD 0x3204 +#define AM_RSQ_RSSI_LO_THRESHOLD_MASK 0x007F +#define AM_RSQ_RSSI_LO_THRESHOLD_SHFT 0 + +/* AM_SOFT_MUTE_RATE */ +#define AM_SOFT_MUTE_RATE 0x3300 + +/* AM_SOFT_MUTE_SLOPE */ +#define AM_SOFT_MUTE_SLOPE 0x3301 +#define AM_SOFT_MUTE_SLOPE_MASK 0x000F +#define AM_SOFT_MUTE_SLOPE_SHFT 0 + +/* AM_SOFT_MUTE_MAX_ATTENUATION */ +#define AM_SOFT_MUTE_MAX_ATTENUATION 0x3302 +#define AM_SOFT_MUTE_MAX_ATTENUATION_MASK 0x003F +#define AM_SOFT_MUTE_MAX_ATTENUATION_SHFT 0 + +/* AM_SOFT_MUTE_SNR_THRESHOLD */ +#define AM_SOFT_MUTE_SNR_THRESHOLD 0x3303 +#define AM_SOFT_MUTE_SNR_THRESHOLD_MASK 0x003F +#define AM_SOFT_MUTE_SNR_THRESHOLD_SHFT 0 + +/* AM_SEEK_BAND_BOTTOM */ +#define AM_SEEK_BAND_BOTTOM 0x3400 + +/* AM_SEEK_BAND_TOP */ +#define AM_SEEK_BAND_TOP 0x3401 + +/* AM_SEEK_FREQ_SPACING */ +#define AM_SEEK_FREQ_SPACING 0x3402 +#define AM_SEEK_FREQ_SPACING_MASK 0x000F +#define AM_SEEK_FREQ_SPACING_SHFT 0 + +/* AM_SEEK_TUNE_SNR_THRESHOLD */ +#define AM_SEEK_TUNE_SNR_THRESHOLD 0x3403 +#define AM_SEEK_TUNE_SNR_THRESHOLD_MASK 0x003F +#define AM_SEEK_TUNE_SNR_THRESHOLD_SHFT 0 + +/* AM_SEEK_TUNE_RSSI_THRESHOLD */ +#define AM_SEEK_TUNE_RSSI_THRESHOLD 0x3404 +#define AM_SEEK_TUNE_RSSI_THRESHOLD_MASK 0x003F +#define AM_SEEK_TUNE_RSSI_THRESHOLD_SHFT 0 + +/*================================================================== + General Receive Properties +==================================================================*/ + +/* RX_VOLUME */ +#define RX_VOLUME 0x4000 +#define RX_VOLUME_MASK 0x003F +#define RX_VOLUME_SHFT 0 + +/* RX_HARD_MUTE */ +#define RX_HARD_MUTE 0x4001 +#define RX_HARD_MUTE_RMUTE_MASK 0x0001 +#define RX_HARD_MUTE_LMUTE_MASK 0x0002 +#define RX_HARD_MUTE_RMUTE_SHFT 0 +#define RX_HARD_MUTE_LMUTE_SHFT 1 + +/*================================================================== + Bit Definitions for Properties +==================================================================*/ + +/* DIGITAL_MODE - used for input or output */ +#define DIGITAL_MODE_I2S 0x0 +#define DIGITAL_MODE_LEFT 0x6 +#define DIGITAL_MODE_MSB1ST 0xC +#define DIGITAL_MODE_MSB2ND 0x8 + +/* DIGITAL_SIZE - used for input or output */ +#define DIGITAL_SIZE_16 0x0 +#define DIGITAL_SIZE_20 0x1 +#define DIGITAL_SIZE_24 0x2 +#define DIGITAL_SIZE_8 0x3 + +/* FM_DEEMPH */ +#define FM_DEEMPH_75US 0x2 +#define FM_DEEMPH_50US 0x1 + +/* FM_RDS_BLETH - used for all block error thresholds */ +#define FM_RDS_BLETH_NO_ERRORS 0x0 +#define FM_RDS_BLETH_1OR2_ERRORS 0x1 +#define FM_RDS_BLETH_3TO5_ERRORS 0x2 +#define FM_RDS_BLETH_UNCORRECTABLE 0x3 + +/* TX_LINE_INPUT_LEVEL_LIATTEN */ +#define TX_LINE_INPUT_LEVEL_LIATTEN_396kOhm 0x0000 +#define TX_LINE_INPUT_LEVEL_LIATTEN_100kOhm 0x1000 +#define TX_LINE_INPUT_LEVEL_LIATTEN_74kOhm 0x2000 +#define TX_LINE_INPUT_LEVEL_LIATTEN_60kOhm 0x3000 + +/* TX_DEEMPHASIS */ +#define TX_PREEMPHASIS_75US 0x0 +#define TX_PREEMPHASIS_50US 0x1 +#define TX_PREEMPHASIS_DISABLED 0x2 + +/* TX_ACOMP_ATTACK_TIME */ +#define TX_ACOMP_ATTACK_TIME_0_5MS 0x0 +#define TX_ACOMP_ATTACK_TIME_1_0MS 0x1 +#define TX_ACOMP_ATTACK_TIME_1_5MS 0x2 +#define TX_ACOMP_ATTACK_TIME_2_0MS 0x3 +#define TX_ACOMP_ATTACK_TIME_2_5MS 0x4 +#define TX_ACOMP_ATTACK_TIME_3_0MS 0x5 +#define TX_ACOMP_ATTACK_TIME_3_5MS 0x6 +#define TX_ACOMP_ATTACK_TIME_4_0MS 0x7 +#define TX_ACOMP_ATTACK_TIME_4_5MS 0x8 +#define TX_ACOMP_ATTACK_TIME_5_0MS 0x9 + +/* TX_ACOMP_RELEASE_TIME */ +#define TX_ACOMP_RELEASE_TIME_100MS 0x0 +#define TX_ACOMP_RELEASE_TIME_200MS 0x1 +#define TX_ACOMP_RELEASE_TIME_350MS 0x2 +#define TX_ACOMP_RELEASE_TIME_525MS 0x3 +#define TX_ACOMP_RELEASE_TIME_1000MS 0x4 + +/* TX_RDS_PS_MIX */ +#define TX_RDS_PS_MIX_FIFO_EMPTY 0x0 +#define TX_RDS_PS_MIX_12_5_PCT 0x1 +#define TX_RDS_PS_MIX_25_PCT 0x2 +#define TX_RDS_PS_MIX_50_PCT 0x3 +#define TX_RDS_PS_MIX_75_PCT 0x4 +#define TX_RDS_PS_MIX_87_5_PCT 0x5 +#define TX_RDS_PS_MIX_100_PCT 0x6 + +/* TX_RDS_PS_MISC_PTY */ +#define TX_RDS_PS_MISC_PTY_NONE 0 +#define TX_RDS_PS_MISC_PTY_NEWS 1 +#define TX_RDS_PS_MISC_PTY_INFO 2 +#define TX_RDS_PS_MISC_PTY_SPORTS 3 +#define TX_RDS_PS_MISC_PTY_TALK 4 +#define TX_RDS_PS_MISC_PTY_ROCK 5 +#define TX_RDS_PS_MISC_PTY_CLROCK 6 +#define TX_RDS_PS_MISC_PTY_ADHITS 7 +#define TX_RDS_PS_MISC_PTY_SOFTROCK 8 +#define TX_RDS_PS_MISC_PTY_TOP40 9 +#define TX_RDS_PS_MISC_PTY_COUNTRY 10 +#define TX_RDS_PS_MISC_PTY_OLDIES 11 +#define TX_RDS_PS_MISC_PTY_SOFT 12 +#define TX_RDS_PS_MISC_PTY_NOST 13 +#define TX_RDS_PS_MISC_PTY_JAZZ 14 +#define TX_RDS_PS_MISC_PTY_CLASS 15 +#define TX_RDS_PS_MISC_PTY_RHYBLUES 16 +#define TX_RDS_PS_MISC_PTY_SOFTRB 17 +#define TX_RDS_PS_MISC_PTY_FGNLANG 18 +#define TX_RDS_PS_MISC_PTY_RMUSIC 19 +#define TX_RDS_PS_MISC_PTY_RTALK 20 +#define TX_RDS_PS_MISC_PTY_PERS 21 +#define TX_RDS_PS_MISC_PTY_PUBLIC 22 +#define TX_RDS_PS_MISC_PTY_COLLEGE 23 +#define TX_RDS_PS_MISC_PTY_UN24 24 +#define TX_RDS_PS_MISC_PTY_UN25 25 +#define TX_RDS_PS_MISC_PTY_UN26 26 +#define TX_RDS_PS_MISC_PTY_UN27 27 +#define TX_RDS_PS_MISC_PTY_UN28 28 +#define TX_RDS_PS_MISC_PTY_WTHR 29 +#define TX_RDS_PS_MISC_PTY_EMERTEST 30 +#define TX_RDS_PS_MISC_PTY_ALERT 31 + +/* AM_CHANNEL_FILTER_BW */ +#define AM_CHANNEL_FILTER_BW_6KHZ 0 +#define AM_CHANNEL_FILTER_BW_4KHZ 1 +#define AM_CHANNEL_FILTER_BW_3KHZ 2 +#define AM_CHANNEL_FILTER_BW_2KHZ 3 + + +/*================================================================== + WB Receive Properties +==================================================================*/ + + +/* WB_MAX_TUNE_ERROR */ +#define WB_MAX_TUNE_ERROR 0x5108 + +/* WB_VALID_SNR_THRESHOLD */ +#define WB_VALID_SNR_THRESHOLD 0x5403 +#define WB_VALID_SNR_THRESHOLD_MASK 0x007F +#define WB_VALID_SNR_THRESHOLD_SHFT 0 + +/* WB_VALID_RSSI_THRESHOLD */ +#define WB_VALID_RSSI_THRESHOLD 0x5404 +#define WB_VALID_RSSI_THRESHOLD_MASK 0x007F +#define WB_VALID_RSSI_THRESHOLD_SHFT 0 + +#endif |