aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/samsung/fm_si47xx/Si47xx_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/samsung/fm_si47xx/Si47xx_dev.c')
-rw-r--r--drivers/samsung/fm_si47xx/Si47xx_dev.c1791
1 files changed, 1791 insertions, 0 deletions
diff --git a/drivers/samsung/fm_si47xx/Si47xx_dev.c b/drivers/samsung/fm_si47xx/Si47xx_dev.c
new file mode 100644
index 0000000..bf64915
--- /dev/null
+++ b/drivers/samsung/fm_si47xx/Si47xx_dev.c
@@ -0,0 +1,1791 @@
+/* drivers/samsung/fm_si47xx/Si47xx_dev.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#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 "Si47xx_dev.h"
+#include <linux/i2c/si47xx_common.h>
+
+#include "commanddefs.h"
+#include "propertydefs.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 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 Si47xx_RDS_flag;
+int RDS_Data_Available;
+int RDS_Data_Lost;
+int RDS_Groups_Available_till_now;
+struct workqueue_struct *Si47xx_wq;
+struct work_struct Si47xx_work;
+#endif
+
+/*Si47xx device structure*/
+static struct Si47xx_device_t *Si47xx_dev;
+static struct si47xx_platform_data *pSi47xxdata;
+
+static int si47xx_irq;
+wait_queue_head_t Si47xx_waitq;
+
+/*Wait flag*/
+/*WAITING or WAIT_OVER or NO_WAIT*/
+int Si47xx_dev_wait_flag = NO_WAIT;
+#ifdef RDS_INTERRUPT_ON_ALWAYS
+int Si47xx_RDS_flag = NO_WAIT;
+#endif
+
+static irqreturn_t Si47xx_isr(int irq, void *unused)
+{
+ debug("Si47xx_isr: FM device called IRQ: %d\n", irq);
+#ifdef RDS_INTERRUPT_ON_ALWAYS
+ if ((Si47xx_dev_wait_flag == SEEK_WAITING) ||
+ (Si47xx_dev_wait_flag == TUNE_WAITING)) {
+ debug("Si47xx_isr: FM Seek/Tune Interrupt "
+ "called IRQ %d\n", irq);
+ Si47xx_dev_wait_flag = WAIT_OVER;
+ wake_up_interruptible(&Si47xx_waitq);
+ } else if (Si47xx_RDS_flag == RDS_WAITING) { /* RDS Interrupt */
+ debug_rds("Si47xx_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(&Si47xx_work))
+ queue_work(Si47xx_wq, &Si47xx_work);
+ }
+#else
+ if ((Si47xx_dev_wait_flag == SEEK_WAITING) ||
+ (Si47xx_dev_wait_flag == TUNE_WAITING) ||
+ (Si47xx_dev_wait_flag == RDS_WAITING)) {
+ Si47xx_dev_wait_flag = WAIT_OVER;
+ wake_up_interruptible(&Si47xx_waitq);
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+
+/*-----------------------------------------------------------------------------
+ This command returns the status
+-----------------------------------------------------------------------------*/
+static u8 si47xx_readStatus(void)
+{
+ u8 status;
+ int ret = 0;
+ ret = i2c_master_recv((struct i2c_client *)(Si47xx_dev->client),
+ &status, 1);
+
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "%s si47xx_readStatus failed %d\n",
+ __func__, ret);
+
+ return ret;
+ }
+
+ return status;
+}
+
+
+/*-----------------------------------------------------------------------------
+ Command that will wait for CTS before returning
+-----------------------------------------------------------------------------*/
+static 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
+-----------------------------------------------------------------------------*/
+static int si47xx_command(u8 cmd_size, u8 *cmd, u8 reply_size, u8 *reply)
+{
+ int ret = 0;
+ ret = i2c_master_send((struct i2c_client *)(Si47xx_dev->client),
+ cmd, cmd_size);
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "%s si47xx_command failed %d\n",
+ __func__, ret);
+
+ return ret;
+ }
+ si47xx_waitForCTS();
+
+ if (reply_size) {
+ ret = i2c_master_recv((struct i2c_client *)(Si47xx_dev->client),
+ reply, reply_size);
+
+ if (ret < 0)
+ dev_err(Si47xx_dev->dev,
+ "%s si47xx_command failed %d\n", __func__, ret);
+
+ return ret;
+ }
+
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------
+ Set the passed property number to the passed value.
+
+ Inputs:
+ propNumber: The number identifying the property to set
+ propValue: The value of the property.
+-----------------------------------------------------------------------------*/
+static void si47xx_set_property(u16 propNumber, u16 propValue)
+{
+ u8 cmd[8];
+ int ret = 0;
+
+ 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);
+
+ ret = si47xx_command(6, cmd, 0, NULL);
+
+ if (ret < 0)
+ dev_err(Si47xx_dev->dev,
+ "%s si47xx_set_property failed %d\n", __func__, ret);
+}
+
+static int powerup(void)
+{
+ int ret = 0;
+ u8 cmd[8];
+ u8 rsp[13];
+
+ pSi47xxdata->power(1);
+
+ cmd[0] = POWER_UP;
+ cmd[1] = POWER_UP_IN_GPO2OEN | POWER_UP_IN_FUNC_FMRX;
+ cmd[2] = POWER_UP_IN_OPMODE_RX_ANALOG;
+ ret = si47xx_command(3, cmd, 8, rsp);
+
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "%s failed %d\n", __func__, ret);
+ } else {
+ /* Si4709/09 datasheet: Table 7 */
+ msleep(110);
+ Si47xx_dev->state.power_state = RADIO_ON;
+ }
+ return ret;
+}
+
+static int powerdown(void)
+{
+ int ret = 0;
+ u8 cmd[8];
+ u8 rsp[13];
+
+ if (!(RADIO_POWERDOWN == Si47xx_dev->state.power_state)) {
+ cmd[0] = POWER_DOWN;
+ ret = si47xx_command(1, cmd, 1, rsp);
+
+ if (ret < 0)
+ dev_err(Si47xx_dev->dev, "%s failed %d\n",
+ __func__, ret);
+ else
+ Si47xx_dev->state.power_state = RADIO_POWERDOWN;
+
+ msleep(110);
+ pSi47xxdata->power(0);
+ } else
+ debug("Device already Powered-OFF\n");
+
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------
+ Helper function that sends the GET_INT_STATUS command to the part
+
+ Returns:
+ The status byte from the part.
+-----------------------------------------------------------------------------*/
+static s8 getIntStatus(void)
+{
+ u8 cmd[8];
+ u8 rsp[13];
+ int ret = 0;
+ cmd[0] = GET_INT_STATUS;
+ ret = si47xx_command(1, cmd, 1, rsp);
+
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "%s getIntStatus failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return rsp[0];
+}
+
+/*-----------------------------------------------------------------------------
+ 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 int fmSeekStart(u8 seekUp, u8 wrap)
+{
+ u8 cmd[8];
+ u8 rsp[13];
+ int ret;
+
+ 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;
+
+ ret = si47xx_command(2, cmd, 1, rsp);
+ return ret;
+
+}
+
+static u16 freq_to_channel(u32 frequency)
+{
+ u16 channel;
+
+ if (frequency < Si47xx_dev->settings.bottom_of_band)
+ frequency = Si47xx_dev->settings.bottom_of_band;
+
+ channel = (frequency - Si47xx_dev->settings.bottom_of_band)
+ / Si47xx_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(Si47xx_waitq,
+ (Si47xx_dev_wait_flag == WAIT_OVER) ||
+ (Si47xx_dev_wait_flag == SEEK_CANCEL));
+}
+
+#ifndef RDS_INTERRUPT_ON_ALWAYS
+static void wait_RDS(void)
+{
+ wait_event_interruptible_timeout(Si47xx_waitq,
+ (Si47xx_dev_wait_flag == WAIT_OVER),
+ Si47xx_dev->settings.timeout_RDS);
+}
+#endif
+
+/*-----------------------------------------------------------------------------
+ Helper function that sends the FM_TUNE_FREQ command to the part
+
+ Inputs:
+ frequency in 10kHz steps
+-----------------------------------------------------------------------------*/
+static int fmTuneFreq(u16 frequency)
+{
+ u8 cmd[8];
+ u8 rsp[13];
+ int ret;
+
+ cmd[0] = FM_TUNE_FREQ;
+ cmd[1] = 0;
+ cmd[2] = (u8)(frequency >> 8);
+ cmd[3] = (u8)(frequency & 0x00FF);
+ cmd[4] = (u8)0;
+ ret = si47xx_command(5, cmd, 1, rsp);
+
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------
+ 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 int fmTuneStatus(u8 cancel, u8 intack, struct tune_data_t *tune_data)
+{
+ u8 cmd[8];
+ u8 rsp[13];
+ int ret;
+
+ 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;
+
+ ret = si47xx_command(2, cmd, 8, rsp);
+
+ tune_data->stc = !!(rsp[0] & STCINT);
+ tune_data->bltf = !!(rsp[1] & FM_TUNE_STATUS_OUT_BTLF);
+ tune_data->afcrl = !!(rsp[1] & FM_TUNE_STATUS_OUT_AFCRL);
+ tune_data->valid = !!(rsp[1] & FM_TUNE_STATUS_OUT_VALID);
+ tune_data->freq = ((u16)rsp[2] << 8) | (u16)rsp[3];
+ tune_data->rssi = rsp[4];
+ tune_data->asnr = rsp[5];
+ tune_data->antcap = rsp[7];
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ 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:
+ Si47xx_status.Status: Contains bits about the status returned from the part.
+ Si47xx_status.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, struct rsq_data_t *rsq_data)
+{
+ u8 cmd[8];
+ u8 rsp[13];
+
+ cmd[0] = FM_RSQ_STATUS;
+ cmd[1] = 0;
+
+ if (intack)
+ cmd[1] |= FM_RSQ_STATUS_IN_INTACK;
+
+ si47xx_command(2, cmd, 8, rsp);
+
+ rsq_data->rsqints = rsp[1];
+ rsq_data->smute = !!(rsp[2] & FM_RSQ_STATUS_OUT_SMUTE);
+ rsq_data->afcrl = !!(rsp[2] & FM_RSQ_STATUS_OUT_AFCRL);
+ rsq_data->valid = !!(rsp[2] & FM_RSQ_STATUS_OUT_VALID);
+ rsq_data->pilot = !!(rsp[3] & FM_RSQ_STATUS_OUT_PILOT);
+ rsq_data->blend = rsp[3] & FM_RSQ_STATUS_OUT_STBLEND;
+ rsq_data->rssi = rsp[4];
+ rsq_data->snr = rsp[5];
+ rsq_data->freqoff = 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.
+-----------------------------------------------------------------------------*/
+static void fmRdsStatus(u8 intack, u8 mtfifo, struct radio_data_t *rds_data,
+ u8 *RdsFifoUsed)
+{
+ u8 cmd[8];
+ u8 rsp[13];
+ int ret = 0;
+
+ 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;
+
+ ret = si47xx_command(2, cmd, 13, rsp);
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "%s fmRdsStatusfailed %d\n",
+ __func__, ret);
+ return;
+ }
+
+ *RdsFifoUsed = rsp[3];
+ rds_data->rdsa = ((u16)rsp[4] << 8) | (u16)rsp[5];
+ rds_data->rdsb = ((u16)rsp[6] << 8) | (u16)rsp[7];
+ rds_data->rdsc = ((u16)rsp[8] << 8) | (u16)rsp[9];
+ rds_data->rdsd = ((u16)rsp[10] << 8) | (u16)rsp[11];
+ rds_data->blera = (rsp[12] & FM_RDS_STATUS_OUT_BLEA) >>
+ FM_RDS_STATUS_OUT_BLEA_SHFT;
+ rds_data->blerb = (rsp[12] & FM_RDS_STATUS_OUT_BLEB) >>
+ FM_RDS_STATUS_OUT_BLEB_SHFT;
+ rds_data->blerc = (rsp[12] & FM_RDS_STATUS_OUT_BLEC) >>
+ FM_RDS_STATUS_OUT_BLEC_SHFT;
+ rds_data->blerd = (rsp[12] & FM_RDS_STATUS_OUT_BLED) >>
+ FM_RDS_STATUS_OUT_BLED_SHFT;
+}
+
+static int seek(u32 *frequency, int up, int mode)
+{
+ int ret = 0;
+ struct tune_data_t tune_data;
+
+ Si47xx_dev_wait_flag = SEEK_WAITING;
+ ret = fmSeekStart(up, mode); /* mode 0 is full scan */
+ wait();
+
+ if (Si47xx_dev_wait_flag == SEEK_CANCEL) {
+ ret = fmTuneStatus(1, 1, &tune_data);
+
+ *frequency = 0;
+
+ Si47xx_dev_wait_flag = NO_WAIT;
+
+ return ret;
+ }
+
+ Si47xx_dev_wait_flag = NO_WAIT;
+
+ if (!(getIntStatus() & STCINT)) {
+ dev_err(Si47xx_dev->dev, "%s seek is failed!\n", __func__);
+ fmTuneStatus(1, 1, &tune_data);
+ return -1;
+ }
+
+ ret = fmTuneStatus(0, 1, &tune_data);
+ if (tune_data.bltf != 1)
+ *frequency = tune_data.freq;
+
+ else {
+ if (tune_data.valid)
+ *frequency = tune_data.freq;
+ else
+ *frequency = 0;
+ }
+ return ret;
+}
+
+static int tune_freq(u32 frequency)
+{
+ int ret = 0;
+
+ u16 channel = 0;
+ struct tune_data_t tune_data;
+ mutex_lock(&(Si47xx_dev->lock));
+
+ channel = freq_to_channel(frequency);
+
+ Si47xx_dev_wait_flag = TUNE_WAITING;
+ ret = fmTuneFreq(frequency);
+ wait();
+ Si47xx_dev_wait_flag = NO_WAIT;
+ debug("Si47xx_dev_wait_flag = TUNE_WAITING\n");
+
+ if (!(getIntStatus() & STCINT)) {
+ dev_err(Si47xx_dev->dev, "%s tune is failed!\n", __func__);
+ fmTuneStatus(1, 1, &tune_data);
+ mutex_unlock(&(Si47xx_dev->lock));
+ return -1;
+ }
+
+ ret = fmTuneStatus(0, 1, &tune_data);
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+
+int Si47xx_dev_init(struct Si47xx_device_t *si47xx_dev)
+{
+ int ret = 0;
+ Si47xx_dev = si47xx_dev;
+ Si47xx_dev->client = si47xx_dev->client;
+ pSi47xxdata = si47xx_dev->pdata;
+ si47xx_irq = Si47xx_dev->client->irq;
+
+ debug("Si47xx_dev_init called");
+
+ mutex_lock(&Si47xx_dev->lock);
+
+ Si47xx_dev->state.power_state = RADIO_POWERDOWN;
+ Si47xx_dev->state.seek_state = RADIO_SEEK_OFF;
+ Si47xx_dev->valid_client_state = eTRUE;
+ Si47xx_dev->valid = eFALSE;
+
+#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) {
+ dev_err(Si47xx_dev->dev, "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) {
+ dev_err(Si47xx_dev->dev, "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 */
+ Si47xx_wq = create_singlethread_workqueue("Si47xx_wq");
+ if (!Si47xx_wq) {
+ dev_err(Si47xx_dev->dev, "Not sufficient memory for Si47xx_wq, work-queue");
+ ret = -ENOMEM;
+ kfree(RDS_Block_Error_buffer);
+ kfree(RDS_Block_Data_buffer);
+ goto EXIT;
+ }
+
+ /*Initialising work_queue */
+ INIT_WORK(&Si47xx_work, Si47xx_work_func);
+
+ RDS_Data_Available = 0;
+ RDS_Data_Lost = 0;
+ RDS_Groups_Available_till_now = 0;
+EXIT:
+#endif
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ debug("Si47xx_dev_init call over");
+
+ return ret;
+}
+
+int Si47xx_dev_exit(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_exit called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+#ifdef RDS_INTERRUPT_ON_ALWAYS
+ if (Si47xx_wq)
+ destroy_workqueue(Si47xx_wq);
+
+ kfree(RDS_Block_Error_buffer);
+ kfree(RDS_Block_Data_buffer);
+#endif
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ debug("Si47xx_dev_exit call over");
+
+ return ret;
+}
+
+int Si47xx_dev_powerup(void)
+{
+ int ret = 0;
+ u32 value = 100;
+
+ debug("Si47xx_dev_powerup called");
+
+ if (!(RADIO_ON == Si47xx_dev->state.power_state)) {
+ ret = powerup();
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "%s failed %d\n",
+ __func__, ret);
+ } else if (Si47xx_dev->valid_client_state == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_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( */
+ /* &Si47xx_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);
+ Si47xx_dev->settings.band = BAND_87500_108000_kHz;
+ Si47xx_dev->settings.bottom_of_band = FREQ_87500_kHz;
+ si47xx_set_property(FM_SEEK_FREQ_SPACING,
+ CHAN_SPACING_100_kHz);
+ Si47xx_dev->settings.channel_spacing =
+ CHAN_SPACING_100_kHz;
+
+ /* SYSCONFIG3_BITSET_SKSNR( */
+ /* &Si47xx_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);
+ Si47xx_dev->settings.timeout_RDS =
+ msecs_to_jiffies(value);
+ Si47xx_dev->settings.curr_snr = TUNE_SNR_THRESHOLD;
+ Si47xx_dev->settings.curr_rssi_th = TUNE_RSSI_THRESHOLD;
+ Si47xx_dev->valid = eTRUE;
+
+ Si47xx_dev_STEREO_SET();
+#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");
+
+ ret = request_irq(si47xx_irq, Si47xx_isr,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Si47xx", NULL);
+ si47xx_set_property(0xff00, 0);
+
+ /* tune initial frequency to remove tunestatus func err
+ * sometimes occur tunestatus func err when execute tunestatus function
+ * before to complete tune_freq.
+ * so run tune_freq just after to complete booting sequence*/
+ ret = tune_freq(Si47xx_dev->settings.bottom_of_band);
+
+ return ret;
+}
+
+int Si47xx_dev_powerdown(void)
+{
+ int ret = 0;
+
+ msleep(500); /* For avoiding turned off pop noise */
+ debug("Si47xx_dev_powerdown called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ free_irq(si47xx_irq, NULL);
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_powerdown called when DS is invalid");
+ ret = -1;
+ } else {
+ ret = powerdown();
+ if (ret < 0)
+ dev_err(Si47xx_dev->dev, "%s failed %d\n",
+ __func__, ret);
+ }
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_suspend(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_suspend called");
+
+#ifndef _ENABLE_RDS_
+ disable_irq(si47xx_irq);
+#endif
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid_client_state == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_suspend called "
+ "when DS(state, client) is invalid");
+ ret = -1;
+ }
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ debug("Si47xx_dev_enable call over");
+
+ return ret;
+}
+
+int Si47xx_dev_resume(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_resume called");
+
+#ifndef _ENABLE_RDS_
+ enable_irq(si47xx_irq);
+#endif
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid_client_state == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_resume called "
+ "when DS(state, client) is invalid");
+ ret = -1;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+ debug("Si47xx_dev_disable call over");
+
+ return ret;
+}
+
+int Si47xx_dev_band_set(int band)
+{
+ int ret = 0;
+ u16 prev_band = 0;
+ u32 prev_bottom_of_band = 0;
+
+ debug("Si47xx_dev_band_set called");
+
+ prev_band = Si47xx_dev->settings.band;
+ prev_bottom_of_band = Si47xx_dev->settings.bottom_of_band;
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_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);
+ Si47xx_dev->settings.band = BAND_87500_108000_kHz;
+ Si47xx_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);
+ Si47xx_dev->settings.band = BAND_76000_108000_kHz;
+ Si47xx_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);
+ Si47xx_dev->settings.band = BAND_76000_90000_kHz;
+ Si47xx_dev->settings.bottom_of_band = FREQ_76000_kHz;
+ break;
+ default:
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+int Si47xx_dev_ch_spacing_set(int ch_spacing)
+{
+ int ret = 0;
+ u16 prev_ch_spacing = 0;
+
+ debug("Si47xx_dev_ch_spacing_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+ prev_ch_spacing = Si47xx_dev->settings.channel_spacing;
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_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);
+ Si47xx_dev->settings.channel_spacing =
+ CHAN_SPACING_200_kHz;
+ break;
+
+ case CHAN_SPACING_100_kHz:
+ si47xx_set_property(FM_SEEK_FREQ_SPACING, 10);
+ Si47xx_dev->settings.channel_spacing =
+ CHAN_SPACING_100_kHz;
+ break;
+
+ case CHAN_SPACING_50_kHz:
+ si47xx_set_property(FM_SEEK_FREQ_SPACING, 5);
+ Si47xx_dev->settings.channel_spacing =
+ CHAN_SPACING_50_kHz;
+ break;
+
+ default:
+ ret = -1;
+ }
+
+ if (ret == 0) {
+ if (ret < 0) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_ch_spacing_set "
+ "i2c_write 1 failed");
+ Si47xx_dev->settings.channel_spacing =
+ prev_ch_spacing;
+ }
+ }
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_chan_select(u32 frequency)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_chan_select called");
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_chan_select called when DS is invalid");
+ ret = -1;
+ } else {
+ Si47xx_dev->state.seek_state = RADIO_SEEK_ON;
+
+ ret = tune_freq(frequency);
+ debug("Si47xx_dev_chan_select called1");
+ Si47xx_dev->state.seek_state = RADIO_SEEK_OFF;
+ }
+ return ret;
+}
+
+int Si47xx_dev_chan_get(u32 *frequency)
+{
+ int ret = 0;
+ struct tune_data_t tune_data;
+
+ debug("Si47xx_dev_chan_get called\n");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_chan_get called when DS is invalid");
+ ret = -1;
+ } else {
+ if (ret < 0)
+ debug("Si47xx_dev_chan_get i2c_read failed");
+ else {
+ ret = fmTuneStatus(0, 1, &tune_data);
+ *frequency = tune_data.freq;
+ }
+ }
+ mutex_unlock(&(Si47xx_dev->lock));
+ return ret;
+}
+
+int Si47xx_dev_seek_full(u32 *frequency)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_seek_full called\n");
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_seek_full called when DS is invalid");
+ ret = -1;
+ } else {
+ Si47xx_dev->state.seek_state = RADIO_SEEK_ON;
+ ret = seek(frequency, 1, 0);
+ Si47xx_dev->state.seek_state = RADIO_SEEK_OFF;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_seek_up(u32 *frequency)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_seek_up called\n");
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_seek_up called when DS is invalid");
+ ret = -1;
+ } else {
+ Si47xx_dev->state.seek_state = RADIO_SEEK_ON;
+ ret = seek(frequency, 1, 1);
+ Si47xx_dev->state.seek_state = RADIO_SEEK_OFF;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_seek_down(u32 *frequency)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_seek_down called\n");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_seek_down called when DS is invalid");
+ ret = -1;
+ } else {
+ Si47xx_dev->state.seek_state = RADIO_SEEK_ON;
+
+ ret = seek(frequency, 0, 1);
+
+ Si47xx_dev->state.seek_state = RADIO_SEEK_OFF;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_RSSI_seek_th_set(u8 seek_th)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_RSSI_seek_th_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_RSSI_seek_th_set called "
+ "when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(FM_SEEK_TUNE_RSSI_THRESHOLD, seek_th);
+ Si47xx_dev->settings.curr_rssi_th = seek_th;
+ if (ret < 0)
+ debug("Si47xx_dev_RSSI_seek_th_set i2c_write 1 failed");
+ }
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_seek_SNR_th_set(u8 seek_SNR)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_seek_SNR_th_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_seek_SNR_th_set called "
+ "when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(FM_SEEK_TUNE_SNR_THRESHOLD, seek_SNR);
+ Si47xx_dev->settings.curr_snr = seek_SNR;
+
+ if (ret < 0)
+ debug("Si47xx_dev_seek_SNR_th_set i2c_write 1 failed");
+ }
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/*ToDo Don't use anymore*/
+int Si47xx_dev_seek_FM_ID_th_set(u8 seek_FM_ID_th)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_seek_FM_ID_th_set called");
+
+ return ret;
+}
+
+int Si47xx_dev_cur_RSSI_get(struct rssi_snr_t *cur_RSSI)
+{
+ int ret = 0;
+
+ struct rsq_data_t rsq_data;
+ debug("Si47xx_dev_cur_RSSI_get called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_cur_RSSI_get called when DS is invalid");
+ ret = -1;
+ } else {
+ fmRsqStatus(0, &rsq_data);
+ cur_RSSI->curr_rssi = rsq_data.rssi;
+ cur_RSSI->curr_rssi_th =
+ Si47xx_dev->settings.curr_rssi_th;
+ cur_RSSI->curr_snr = rsq_data.snr;
+
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/*ToDo Don't use anymore*/
+int Si47xx_dev_device_id(struct device_id *dev_id)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_device_id called");
+
+ return ret;
+}
+
+/*ToDo Don't use anymore*/
+int Si47xx_dev_chip_id(struct chip_id *chp_id)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_chip_id called");
+
+ return ret;
+}
+
+int Si47xx_dev_sys_config2(struct sys_config2 *sys_conf2)
+{
+ int ret = 0;
+
+ debug("Si4709_sys_config2 called\n");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si4709_sys_config2 called when DS is invalid");
+ ret = -1;
+ } else {
+ sys_conf2->rssi_th = Si47xx_dev->settings.curr_rssi_th;
+ sys_conf2->fm_band = Si47xx_dev->settings.band;
+ sys_conf2->fm_chan_spac =
+ Si47xx_dev->settings.channel_spacing;
+ sys_conf2->fm_vol = 0;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_sys_config3(struct sys_config3 *sys_conf3)
+{
+ int ret = 0;
+
+ debug("Si4709_sys_config3 called\n");
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si4709_sys_config3 called when DS is invalid");
+ ret = -1;
+ } else {
+ sys_conf3->smmute = 0;
+ sys_conf3->smutea = 0;
+ sys_conf3->volext = 0;
+ sys_conf3->sksnr = Si47xx_dev->settings.curr_snr;
+ sys_conf3->skcnt = 0;
+ }
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_status_rssi(struct status_rssi *status)
+{
+ int ret = 0;
+ struct rsq_data_t rsq_data;
+
+ debug("Si47xx_dev_status_rssi called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_status_rssi called when DS is invalid");
+ mutex_unlock(&(Si47xx_dev->lock));
+ return -1;
+ }
+ fmRsqStatus(0, &rsq_data);
+
+ pr_debug("%s: Si47xx_dev_status_rssi %d\n",
+ __func__, rsq_data.rssi);
+
+ Si47xx_dev->settings.curr_rssi = rsq_data.rssi;
+ status->rssi = rsq_data.rssi;
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_sys_config2_set(struct sys_config2 *sys_conf2)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_sys_config2_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_sys_config2_set called when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(FM_SEEK_TUNE_RSSI_THRESHOLD,
+ sys_conf2->rssi_th);
+ Si47xx_dev_band_set(sys_conf2->fm_band);
+ si47xx_set_property(FM_SEEK_FREQ_SPACING,
+ sys_conf2->fm_chan_spac);
+ Si47xx_dev->settings.curr_rssi_th = sys_conf2->rssi_th;
+ Si47xx_dev->settings.band = sys_conf2->fm_band;
+ Si47xx_dev->settings.channel_spacing = sys_conf2->fm_chan_spac;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_sys_config3_set(struct sys_config3 *sys_conf3)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_sys_config3_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_sys_config3_set called "
+ "when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(FM_SEEK_TUNE_SNR_THRESHOLD,
+ sys_conf3->sksnr);
+ Si47xx_dev->settings.curr_snr = sys_conf3->sksnr;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/* VNVS:END */
+
+/* VNVS:START 18-NOV'09 */
+/* Reading AFCRL Bit */
+int Si47xx_dev_AFCRL_get(u8 *afc)
+{
+ int ret = 0;
+ struct rsq_data_t rsq_data;
+
+ debug("Si47xx_dev_AFCRL_get called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_AFCRL_get called when DS is invalid");
+ ret = -1;
+ } else {
+ fmRsqStatus(0, &rsq_data);
+ *afc = rsq_data.afcrl;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/* Setting DE
+ emphasis time constant 50us(Europe,Japan,Australia) or 75us(USA)
+ */
+int Si47xx_dev_DE_set(u8 de_tc)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_DE_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_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)
+ dev_err(Si47xx_dev->dev, "%s failed %d\n",
+ __func__, ret);
+ }
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/*Resetting the RDS Data Buffer*/
+int Si47xx_dev_reset_rds_data()
+{
+ int ret = 0;
+
+ debug_rds("Si47xx_dev_reset_rds_data called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_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(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_volume_set(u8 volume)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_volume_set called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_volume_set called when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(RX_VOLUME, pSi47xxdata->rx_vol[volume] &
+ RX_VOLUME_MASK);
+ Si47xx_dev->vol_idx = volume;
+
+ if (ret < 0)
+ dev_err(Si47xx_dev->dev, "%s failed %d\n",
+ __func__, ret);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_volume_get(u8 *volume)
+{
+ int ret = 0;
+
+ debug("Si4709_dev_volume_get called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si4709_dev_volume_get called when DS is invalid");
+ ret = -1;
+ } else
+ *volume = Si47xx_dev->vol_idx;
+
+ mutex_unlock(&(Si47xx_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 Si47xx_dev_DSMUTE_ON(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_DSMUTE_ON called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_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);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_DSMUTE_OFF(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_DSMUTE_OFF called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_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);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/*VNVS:END*/
+
+int Si47xx_dev_MUTE_ON(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_MUTE_ON called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_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);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_MUTE_OFF(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_MUTE_OFF called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_MUTE_OFF called when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(RX_HARD_MUTE, 0);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_MONO_SET(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_MONO_SET called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_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);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_STEREO_SET(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_STEREO_SET called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_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);
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_RDS_ENABLE(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_RDS_ENABLE called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_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));
+#ifdef RDS_INTERRUPT_ON_ALWAYS
+ Si47xx_RDS_flag = RDS_WAITING;
+#endif
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_RDS_DISABLE(void)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_RDS_DISABLE called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_RDS_DISABLE called when DS is invalid");
+ ret = -1;
+ } else {
+ si47xx_set_property(FM_RDS_CONFIG, 0);
+#ifdef RDS_INTERRUPT_ON_ALWAYS
+ Si47xx_RDS_flag = NO_WAIT;
+#endif
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_rstate_get(struct dev_state_t *dev_state)
+{
+ int ret = 0;
+
+ debug("Si47xx_dev_rstate_get called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_rstate_get called when DS is invalid");
+ ret = -1;
+ } else {
+ dev_state->power_state = Si47xx_dev->state.power_state;
+ dev_state->seek_state = Si47xx_dev->state.seek_state;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+/* VNVS:START 7-JUNE'10 Function call for work-queue "Si47xx_wq" */
+#ifdef RDS_INTERRUPT_ON_ALWAYS
+void Si47xx_work_func(struct work_struct *work)
+{
+ struct radio_data_t rds_data;
+ int i = 0;
+ u8 RdsFifoUsed;
+#ifdef RDS_TESTING
+ u8 group_type;
+#endif
+ debug_rds("%s", __func__);
+mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_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, &rds_data, &RdsFifoUsed);
+ /* 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] =
+ rds_data.rdsa;
+ RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.rdsb;
+ RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.rdsc;
+ RDS_Block_Data_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.rdsd;
+
+ /*Writing into RDS_Block_Error_buffer */
+ i = 0;
+
+ RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.blera;
+ RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.blerb;
+ RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.blerc;
+ RDS_Block_Error_buffer[i++ + 4 * RDS_Buffer_Index_write] =
+ rds_data.blerd;
+ fmRdsStatus(1, 0, &rds_data, &RdsFifoUsed);
+ } 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(&(Si47xx_dev->lock));
+}
+#endif
+/*VNVS:END*/
+
+int Si47xx_dev_RDS_data_get(struct radio_data_t *data)
+{
+ int i, ret = 0;
+ struct tune_data_t tune_data;
+ struct rsq_data_t rsq_data;
+
+ debug_rds("Si47xx_dev_RDS_data_get called");
+
+ mutex_lock(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ dev_err(Si47xx_dev->dev, "Si47xx_dev_RDS_data_get called when DS is invalid");
+ mutex_unlock(&(Si47xx_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");
+ ret = fmTuneStatus(0, 1, &tune_data);
+ data->curr_channel = tune_data.freq;
+ fmRsqStatus(0, &rsq_data);
+ data->curr_rssi = rsq_data.rssi;
+ debug_rds("curr_channel: %u, curr_rssi:%u",
+ data->curr_channel,
+ (u32) data->curr_rssi);
+ mutex_unlock(&(Si47xx_dev->lock));
+ return -1;
+ }
+
+ ret = fmTuneStatus(0, 1, &tune_data);
+ data->curr_channel = tune_data.freq;
+ fmRsqStatus(0, &rsq_data);
+ data->curr_rssi = rsq_data.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);
+#endif
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}
+
+int Si47xx_dev_RDS_timeout_set(u32 time_out)
+{
+ int ret = 0;
+ u32 jiffy_count = 0;
+
+ debug("Si47xx_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(&(Si47xx_dev->lock));
+
+ if (Si47xx_dev->valid == eFALSE) {
+ debug("Si47xx_dev_RDS_timeout_set called when DS is invalid");
+ ret = -1;
+ } else {
+ Si47xx_dev->settings.timeout_RDS = jiffy_count;
+ }
+
+ mutex_unlock(&(Si47xx_dev->lock));
+
+ return ret;
+}