summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rda/rda_fm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rda/rda_fm')
-rwxr-xr-xdrivers/net/wireless/rda/rda_fm/Makefile1
-rwxr-xr-xdrivers/net/wireless/rda/rda_fm/fm.h517
-rwxr-xr-xdrivers/net/wireless/rda/rda_fm/fm_drv.c2257
3 files changed, 2775 insertions, 0 deletions
diff --git a/drivers/net/wireless/rda/rda_fm/Makefile b/drivers/net/wireless/rda/rda_fm/Makefile
new file mode 100755
index 00000000..fe6851a5
--- /dev/null
+++ b/drivers/net/wireless/rda/rda_fm/Makefile
@@ -0,0 +1 @@
+obj-m := fm_drv.o
diff --git a/drivers/net/wireless/rda/rda_fm/fm.h b/drivers/net/wireless/rda/rda_fm/fm.h
new file mode 100755
index 00000000..c97c85e8
--- /dev/null
+++ b/drivers/net/wireless/rda/rda_fm/fm.h
@@ -0,0 +1,517 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+/* alps/ALPS_SW/TRUNK/MAIN/alps/kernel/arch/arm/mach-mt6516/include/mach/fm.h
+ *
+ * (C) Copyright 2009
+ * MediaTek <www.MediaTek.com>
+ * William Chung <William.Chung@MediaTek.com>
+ *
+ * MT6516 AR10x0 FM Radio Driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __FM_H__
+#define __FM_H__
+
+//#define FMDEBUG
+
+#include <linux/ioctl.h>
+#include <linux/time.h>
+
+//scan sort algorithm
+enum{
+ FM_SCAN_SORT_NON = 0,
+ FM_SCAN_SORT_UP,
+ FM_SCAN_SORT_DOWN,
+ FM_SCAN_SORT_MAX
+};
+
+//*****************************************************************************************
+//***********************************FM config for customer ***********************************
+//*****************************************************************************************
+//RX
+#define FMR_RSSI_TH_LONG 0x0301 //FM radio long antenna RSSI threshold(11.375dBuV)
+#define FMR_RSSI_TH_SHORT 0x02E0 //FM radio short antenna RSSI threshold(-1dBuV)
+#define FMR_CQI_TH 0x00E9 //FM radio Channel quality indicator threshold(0x0000~0x00FF)
+#define FMR_SEEK_SPACE 1 //FM radio seek space,1:100KHZ; 2:200KHZ
+#define FMR_SCAN_CH_SIZE 40 //FM radio scan max channel size
+#define FMR_BAND 1 //FM radio band, 1:87.5MHz~108.0MHz; 2:76.0MHz~90.0MHz; 3:76.0MHz~108.0MHz; 4:special
+#define FMR_BAND_FREQ_L 875 //FM radio special band low freq(Default 87.5MHz)
+#define FMR_BAND_FREQ_H 1080 //FM radio special band high freq(Default 108.0MHz)
+#define FM_SCAN_SORT_SELECT FM_SCAN_SORT_NON
+
+//TX
+
+//*****************************************************************************************
+//***********************************FM config for engineer ***********************************
+//*****************************************************************************************
+//RX
+#define FMR_MR_TH 0x01BD //FM radio MR threshold
+#define ADDR_SCAN_TH 0xE0 //scan thrshold register
+#define ADDR_CQI_TH 0xE1 //scan CQI register
+
+//TX
+#define FMTX_SCAN_HOLE_LOW 923 //92.3MHz~95.4MHz should not show to user
+#define FMTX_SCAN_HOLE_HIGH 954 //92.3MHz~95.4MHz should not show to user
+//*****************************************************************************************
+
+#define FM_NAME "fm"
+#define FM_DEVICE_NAME "/dev/fm"
+
+// errno
+#define FM_SUCCESS 0
+#define FM_FAILED 1
+#define FM_EPARM 2
+#define FM_BADSTATUS 3
+#define FM_TUNE_FAILED 4
+#define FM_SEEK_FAILED 5
+#define FM_BUSY 6
+#define FM_SCAN_FAILED 7
+
+// band
+
+#define FM_BAND_UNKNOWN 0
+#define FM_BAND_UE 1 // US/Europe band 87.5MHz ~ 108MHz (DEFAULT)
+#define FM_BAND_JAPAN 2 // Japan band 76MHz ~ 90MHz
+#define FM_BAND_JAPANW 3 // Japan wideband 76MHZ ~ 108MHz
+#define FM_BAND_SPECIAL 4 // special band between 76MHZ and 108MHz
+#define FM_BAND_DEFAULT FM_BAND_UE
+#define FM_FREQ_MIN FMR_BAND_FREQ_L
+#define FM_FREQ_MAX FMR_BAND_FREQ_H
+#define FM_RAIDO_BAND FM_BAND_UE
+// space
+#define FM_SPACE_UNKNOWN 0
+#define FM_SPACE_100K 1
+#define FM_SPACE_200K 2
+#define FM_SPACE_DEFAULT FMR_SEEK_SPACE
+#define FM_SEEK_SPACE FMR_SEEK_SPACE
+
+//max scan chl num
+#define FM_MAX_CHL_SIZE FMR_SCAN_CH_SIZE
+// auto HiLo
+#define FM_AUTO_HILO_OFF 0
+#define FM_AUTO_HILO_ON 1
+
+// seek direction
+#define FM_SEEK_UP 0
+#define FM_SEEK_DOWN 1
+
+#define FM_CHIP_AR1000 0x1000
+#define FM_CHIP_MT5192 0x91
+#define FM_CHIP_MT5193 0x92
+#define FM_CHIP_MT6616 0x6616
+#define FM_CHIP_MT6626 0x6626
+#define FM_CHIP_MT6628 0x6628
+#define FM_CHIP_MT6620 0x6620
+#define FM_CHIP_UNSUPPORTED 0xffff
+
+// seek threshold
+#define FM_SEEKTH_LEVEL_DEFAULT 4
+
+#define FM_IOC_MAGIC 0xf5 // FIXME: any conflict?
+
+struct fm_tune_parm {
+ uint8_t err;
+ uint8_t band;
+ uint8_t space;
+ uint8_t hilo;
+ uint16_t freq; // IN/OUT parameter
+};
+
+struct fm_seek_parm {
+ uint8_t err;
+ uint8_t band;
+ uint8_t space;
+ uint8_t hilo;
+ uint8_t seekdir;
+ uint8_t seekth;
+ uint16_t freq; // IN/OUT parameter
+};
+
+struct fm_scan_parm {
+ uint8_t err;
+ uint8_t band;
+ uint8_t space;
+ uint8_t hilo;
+ uint16_t freq; // OUT parameter
+ uint16_t ScanTBL[16]; //need no less than the chip
+ uint16_t ScanTBLSize; //IN/OUT parameter
+};
+
+struct fm_ch_rssi{
+ uint16_t freq;
+ uint16_t rssi;
+};
+
+struct fm_rssi_req{
+ uint16_t num;
+ uint16_t read_cnt;
+ struct fm_ch_rssi cr[16*16];
+};
+
+#if 1
+#define NEED_DEF_RDS 1
+#else
+#define NEED_DEF_RDS 0
+#endif
+
+#if NEED_DEF_RDS
+//For RDS feature
+typedef struct
+{
+ uint8_t TP;
+ uint8_t TA;
+ uint8_t Music;
+ uint8_t Stereo;
+ uint8_t Artificial_Head;
+ uint8_t Compressed;
+ uint8_t Dynamic_PTY;
+ uint8_t Text_AB;
+ uint32_t flag_status;
+}RDSFlag_Struct;
+
+typedef struct
+{
+ uint16_t Month;
+ uint16_t Day;
+ uint16_t Year;
+ uint16_t Hour;
+ uint16_t Minute;
+ uint8_t Local_Time_offset_signbit;
+ uint8_t Local_Time_offset_half_hour;
+}CT_Struct;
+
+typedef struct
+{
+ int16_t AF_Num;
+ int16_t AF[2][25]; //100KHz
+ uint8_t Addr_Cnt;
+ uint8_t isMethod_A;
+ uint8_t isAFNum_Get;
+}AF_Info;
+
+typedef struct
+{
+ uint8_t PS[4][8];
+ uint8_t Addr_Cnt;
+}PS_Info;
+
+typedef struct
+{
+ uint8_t TextData[4][64];
+ uint8_t GetLength;
+ uint8_t isRTDisplay;
+ uint8_t TextLength;
+ uint8_t isTypeA;
+ uint8_t BufCnt;
+ uint16_t Addr_Cnt;
+}RT_Info;
+
+struct rds_raw_data
+{
+ int dirty; //indicate if the data changed or not
+ int len; //the data len form chip
+ uint8_t data[146];
+};
+
+struct rds_group_cnt
+{
+ unsigned long total;
+ unsigned long groupA[16]; //RDS groupA counter
+ unsigned long groupB[16]; //RDS groupB counter
+};
+
+enum rds_group_cnt_opcode
+{
+ RDS_GROUP_CNT_READ = 0,
+ RDS_GROUP_CNT_WRITE,
+ RDS_GROUP_CNT_RESET,
+ RDS_GROUP_CNT_MAX
+};
+
+struct rds_group_cnt_req
+{
+ int err;
+ enum rds_group_cnt_opcode op;
+ struct rds_group_cnt gc;
+};
+
+typedef struct
+{
+ CT_Struct CT;
+ RDSFlag_Struct RDSFlag;
+ uint16_t PI;
+ uint8_t Switch_TP;
+ uint8_t PTY;
+ AF_Info AF_Data;
+ AF_Info AFON_Data;
+ uint8_t Radio_Page_Code;
+ uint16_t Program_Item_Number_Code;
+ uint8_t Extend_Country_Code;
+ uint16_t Language_Code;
+ PS_Info PS_Data;
+ uint8_t PS_ON[8];
+ RT_Info RT_Data;
+ uint16_t event_status; //will use RDSFlag_Struct RDSFlag->flag_status to check which event, is that ok?
+ struct rds_group_cnt gc;
+} RDSData_Struct;
+
+
+//Need care the following definition.
+//valid Rds Flag for notify
+typedef enum {
+ RDS_FLAG_IS_TP = 0x0001, // Program is a traffic program
+ RDS_FLAG_IS_TA = 0x0002, // Program currently broadcasts a traffic ann.
+ RDS_FLAG_IS_MUSIC = 0x0004, // Program currently broadcasts music
+ RDS_FLAG_IS_STEREO = 0x0008, // Program is transmitted in stereo
+ RDS_FLAG_IS_ARTIFICIAL_HEAD = 0x0010, // Program is an artificial head recording
+ RDS_FLAG_IS_COMPRESSED = 0x0020, // Program content is compressed
+ RDS_FLAG_IS_DYNAMIC_PTY = 0x0040, // Program type can change
+ RDS_FLAG_TEXT_AB = 0x0080 // If this flag changes state, a new radio text string begins
+} RdsFlag;
+
+typedef enum {
+ RDS_EVENT_FLAGS = 0x0001, // One of the RDS flags has changed state
+ RDS_EVENT_PI_CODE = 0x0002, // The program identification code has changed
+ RDS_EVENT_PTY_CODE = 0x0004, // The program type code has changed
+ RDS_EVENT_PROGRAMNAME = 0x0008, // The program name has changed
+ RDS_EVENT_UTCDATETIME = 0x0010, // A new UTC date/time is available
+ RDS_EVENT_LOCDATETIME = 0x0020, // A new local date/time is available
+ RDS_EVENT_LAST_RADIOTEXT = 0x0040, // A radio text string was completed
+ RDS_EVENT_AF = 0x0080, // Current Channel RF signal strength too weak, need do AF switch
+ RDS_EVENT_AF_LIST = 0x0100, // An alternative frequency list is ready
+ RDS_EVENT_AFON_LIST = 0x0200, // An alternative frequency list is ready
+ RDS_EVENT_TAON = 0x0400, // Other Network traffic announcement start
+ RDS_EVENT_TAON_OFF = 0x0800, // Other Network traffic announcement finished.
+ RDS_EVENT_RDS = 0x2000, // RDS Interrupt had arrived durint timer period
+ RDS_EVENT_NO_RDS = 0x4000, // RDS Interrupt not arrived durint timer period
+ RDS_EVENT_RDS_TIMER = 0x8000 // Timer for RDS Bler Check. ---- BLER block error rate
+} RdsEvent;
+#endif
+
+struct fm_rds_tx_parm {
+ uint8_t err;
+ uint16_t pi;
+ uint16_t ps[12]; // 4 ps
+ uint16_t other_rds[87]; // 0~29 other groups
+ uint8_t other_rds_cnt; // # of other group
+};
+
+typedef struct fm_rds_tx_req{
+ unsigned char pty; // 0~31 integer
+ unsigned char rds_rbds; // 0:RDS, 1:RBDS
+ unsigned char dyn_pty; // 0:static, 1:dynamic
+ unsigned short pi_code; // 2-byte hex
+ unsigned char ps_buf[8]; // hex buf of PS
+ unsigned char ps_len; // length of PS, must be 0 / 8"
+ unsigned char af; // 0~204, 0:not used, 1~204:(87.5+0.1*af)MHz
+ unsigned char ah; // Artificial head, 0:no, 1:yes
+ unsigned char stereo; // 0:mono, 1:stereo
+ unsigned char compress; // Audio compress, 0:no, 1:yes
+ unsigned char tp; // traffic program, 0:no, 1:yes
+ unsigned char ta; // traffic announcement, 0:no, 1:yes
+ unsigned char speech; // 0:music, 1:speech
+}fm_rds_tx_req;
+
+#define TX_SCAN_MAX 10
+#define TX_SCAN_MIN 1
+struct fm_tx_scan_parm {
+ uint8_t err;
+ uint8_t band; //87.6~108MHz
+ uint8_t space;
+ uint8_t hilo;
+ uint16_t freq; // start freq, if less than band min freq, then will use band min freq
+ uint8_t scandir;
+ uint16_t ScanTBL[TX_SCAN_MAX]; //need no less than the chip
+ uint16_t ScanTBLSize; //IN: desired size, OUT: scan result size
+};
+
+struct fm_gps_rtc_info{
+ int err; //error number, 0: success, other: err code
+ int retryCnt; //GPS mnl can decide retry times
+ int ageThd; //GPS 3D fix time diff threshold
+ int driftThd; //GPS RTC drift threshold
+ struct timeval tvThd; //time value diff threshold
+ int age; //GPS 3D fix time diff
+ int drift; //GPS RTC drift
+ union{
+ unsigned long stamp; //time stamp in jiffies
+ struct timeval tv; //time stamp value in RTC
+ };
+ int flag; //rw flag
+};
+
+typedef enum
+{
+ FM_I2S_ON = 0,
+ FM_I2S_OFF
+}fm_i2s_state;
+
+typedef enum
+{
+ FM_I2S_MASTER = 0,
+ FM_I2S_SLAVE
+}fm_i2s_mode;
+
+typedef enum
+{
+ FM_I2S_32K = 0,
+ FM_I2S_44K,
+ FM_I2S_48K
+}fm_i2s_sample;
+
+struct fm_i2s_setting{
+ int onoff;
+ int mode;
+ int sample;
+};
+
+typedef enum{
+ FM_RX = 0,
+ FM_TX = 1
+}FM_PWR_T;
+
+#define FM_IOCTL_POWERUP _IOWR(FM_IOC_MAGIC, 0, struct fm_tune_parm*)
+#define FM_IOCTL_POWERDOWN _IOWR(FM_IOC_MAGIC, 1, int32_t*)
+#define FM_IOCTL_TUNE _IOWR(FM_IOC_MAGIC, 2, struct fm_tune_parm*)
+#define FM_IOCTL_SEEK _IOWR(FM_IOC_MAGIC, 3, struct fm_seek_parm*)
+#define FM_IOCTL_SETVOL _IOWR(FM_IOC_MAGIC, 4, uint32_t*)
+#define FM_IOCTL_GETVOL _IOWR(FM_IOC_MAGIC, 5, uint32_t*)
+#define FM_IOCTL_MUTE _IOWR(FM_IOC_MAGIC, 6, uint32_t*)
+#define FM_IOCTL_GETRSSI _IOWR(FM_IOC_MAGIC, 7, int32_t*)
+#define FM_IOCTL_SCAN _IOWR(FM_IOC_MAGIC, 8, struct fm_scan_parm*)
+#define FM_IOCTL_STOP_SCAN _IO(FM_IOC_MAGIC, 9)
+#define FM_IOCTL_POWERUP_TX _IOWR(FM_IOC_MAGIC, 20, struct fm_tune_parm*)
+#define FM_IOCTL_TUNE_TX _IOWR(FM_IOC_MAGIC, 21, struct fm_tune_parm*)
+#define FM_IOCTL_RDS_TX _IOWR(FM_IOC_MAGIC, 22, struct fm_rds_tx_parm*)
+
+//IOCTL and struct for test
+#define FM_IOCTL_GETCHIPID _IOWR(FM_IOC_MAGIC, 10, uint16_t*)
+#define FM_IOCTL_EM_TEST _IOWR(FM_IOC_MAGIC, 11, struct fm_em_parm*)
+#define FM_IOCTL_RW_REG _IOWR(FM_IOC_MAGIC, 12, struct fm_ctl_parm*)
+#define FM_IOCTL_GETMONOSTERO _IOWR(FM_IOC_MAGIC, 13, uint16_t*)
+#define FM_IOCTL_GETCURPAMD _IOWR(FM_IOC_MAGIC, 14, uint16_t*)
+#define FM_IOCTL_GETGOODBCNT _IOWR(FM_IOC_MAGIC, 15, uint16_t*)
+#define FM_IOCTL_GETBADBNT _IOWR(FM_IOC_MAGIC, 16, uint16_t*)
+#define FM_IOCTL_GETBLERRATIO _IOWR(FM_IOC_MAGIC, 17, uint16_t*)
+
+
+//IOCTL for RDS
+#define FM_IOCTL_RDS_ONOFF _IOWR(FM_IOC_MAGIC, 18, uint16_t*)
+#define FM_IOCTL_RDS_SUPPORT _IOWR(FM_IOC_MAGIC, 19, int32_t*)
+
+#define FM_IOCTL_RDS_SIM_DATA _IOWR(FM_IOC_MAGIC, 23, uint32_t*)
+#define FM_IOCTL_IS_FM_POWERED_UP _IOWR(FM_IOC_MAGIC, 24, uint32_t*)
+
+//IOCTL for FM Tx
+#define FM_IOCTL_TX_SUPPORT _IOWR(FM_IOC_MAGIC, 25, int32_t*)
+#define FM_IOCTL_RDSTX_SUPPORT _IOWR(FM_IOC_MAGIC, 26, int32_t*)
+#define FM_IOCTL_RDSTX_ENABLE _IOWR(FM_IOC_MAGIC, 27, int32_t*)
+#define FM_IOCTL_TX_SCAN _IOWR(FM_IOC_MAGIC, 28, struct fm_tx_scan_parm*)
+
+//IOCTL for FM over BT
+#define FM_IOCTL_OVER_BT_ENABLE _IOWR(FM_IOC_MAGIC, 29, int32_t*)
+
+//IOCTL for FM ANTENNA SWITCH
+#define FM_IOCTL_ANA_SWITCH _IOWR(FM_IOC_MAGIC, 30, int32_t*)
+#define FM_IOCTL_GETCAPARRAY _IOWR(FM_IOC_MAGIC, 31, int32_t*)
+
+//IOCTL for FM compensation by GPS RTC
+#define FM_IOCTL_GPS_RTC_DRIFT _IOWR(FM_IOC_MAGIC, 32, struct fm_gps_rtc_info*)
+
+//IOCTL for FM I2S Setting
+#define FM_IOCTL_I2S_SETTING _IOWR(FM_IOC_MAGIC, 33, struct fm_i2s_setting*)
+
+#define FM_IOCTL_RDS_GROUPCNT _IOWR(FM_IOC_MAGIC, 34, struct rds_group_cnt_req*)
+#define FM_IOCTL_RDS_GET_LOG _IOWR(FM_IOC_MAGIC, 35, struct rds_raw_data*)
+
+#define FM_IOCTL_SCAN_GETRSSI _IOWR(FM_IOC_MAGIC, 36, struct fm_rssi_req*)
+#define FM_IOCTL_SETMONOSTERO _IOWR(FM_IOC_MAGIC, 37, int32_t)
+
+#define FM_IOCTL_RDS_BC_RST _IOWR(FM_IOC_MAGIC, 38, int32_t*)
+
+#define FM_IOCTL_DUMP_REG _IO(FM_IOC_MAGIC, 0xFF)
+
+enum group_idx {
+ mono=0,
+ stereo,
+ RSSI_threshold,
+ HCC_Enable,
+ PAMD_threshold,
+ Softmute_Enable,
+ De_emphasis,
+ HL_Side,
+ Demod_BW,
+ Dynamic_Limiter,
+ Softmute_Rate,
+ AFC_Enable,
+ Softmute_Level,
+ Analog_Volume,
+ GROUP_TOTAL_NUMS
+};
+
+enum item_idx {
+ Sblend_OFF=0,
+ Sblend_ON,
+ ITEM_TOTAL_NUMS
+};
+
+struct fm_ctl_parm {
+ uint8_t err;
+ uint8_t addr;
+ uint16_t val;
+ uint16_t rw_flag;//0:write, 1:read
+};
+
+struct fm_em_parm {
+ uint16_t group_idx;
+ uint16_t item_idx;
+ uint32_t item_value;
+};
+#endif // __FM_H__
diff --git a/drivers/net/wireless/rda/rda_fm/fm_drv.c b/drivers/net/wireless/rda/rda_fm/fm_drv.c
new file mode 100755
index 00000000..cc44d8f3
--- /dev/null
+++ b/drivers/net/wireless/rda/rda_fm/fm_drv.c
@@ -0,0 +1,2257 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h> // udelay()
+#include <linux/device.h> // device_create()
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/version.h> /* constant of kernel version */
+#include <asm/uaccess.h> // get_user()
+#include <linux/semaphore.h>
+
+//#include <linux/fm.h>
+#include "fm.h"
+//#include <mach/mt6573_gpio.h>
+//#include <mach/mtk_rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#define FMDEBUG 1
+#define MTK_MT6515 1
+#define RDA599X_FM_SUPPORT 1
+#define FM_ALERT(f, s...) \
+ do { \
+ printk(KERN_ALERT "RDAFM " f, ## s); \
+ } while(0)
+
+#ifdef FMDEBUG
+#define FM_DEBUG(f, s...) \
+ do { \
+ printk("RDAFM " f, ## s); \
+ } while(0)
+#else
+#define FM_DEBUG(f, s...)
+#endif
+#define RDA599X_SCANTBL_SIZE 16 //16*uinit16_t
+#define RDA599X_FM_SCAN_UP 0x0
+#define RDA599X_FM_SCAN_DOWN 0x01
+
+
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef signed short int16;
+typedef unsigned short uint16;
+typedef signed int int32;
+typedef unsigned int uint32;
+
+extern int rda_fm_power_off();
+extern int rda_fm_power_on();
+
+/******************************************************************************
+ * CONSTANT DEFINITIONS
+ *****************************************************************************/
+#define RDAFM_SLAVE_ADDR (0x11) //RDA FM Chip address
+
+#define RDAFM_MASK_RSSI 0X7F // RSSI
+#define RDAFM_DEV "RDA_FM"
+
+//customer need customize the I2C port
+#define RDAFM_I2C_PORT 0
+
+
+#define ID_RDA5802E 0x5804
+#define ID_RDA5802H 0x5801
+#define ID_RDA5802N 0x5808
+#define ID_RDA5820 0x5805
+#define ID_RDA5820NS 0x5820
+
+
+static struct proc_dir_entry *g_fm_proc = NULL;
+static struct fm *g_fm_struct = NULL;
+static atomic_t scan_complete_flag;
+
+#define FM_PROC_FILE "fm"
+
+/******************************************************************************
+ * STRUCTURE DEFINITIONS
+ *****************************************************************************/
+
+enum RDAFM_CHIP_TYPE {
+ CHIP_TYPE_RDA5802E = 0,
+ CHIP_TYPE_RDA5802H,
+ CHIP_TYPE_RDA5802N,
+ CHIP_TYPE_RDA5820,
+ CHIP_TYPE_RDA5820NS,
+};
+
+
+
+typedef struct
+{
+ uint8_t address;
+ uint16_t value;
+}RDA_FM_REG_T;
+
+typedef struct
+{
+ bool byPowerUp;
+ struct fm_tune_parm parm
+}FM_TUNE_T;
+static FM_TUNE_T fm_tune_data = {false, {}};
+
+typedef enum
+{
+ FM_RECEIVER, //5800,5802,5804
+ FM_TRANSMITTER, //5820
+}RDA_RADIO_WORK_E;
+
+typedef enum
+{
+ OFF,
+ ON,
+}RDA_FM_POWER_STATE_T;
+
+struct fm {
+ uint32_t ref;
+ bool powerup;
+ uint16_t chip_id;
+ uint16_t device_id;
+ dev_t dev_t;
+ uint16_t min_freq; // KHz
+ uint16_t max_freq; // KHz
+ uint8_t band; // TODO
+ struct class *cls;
+ struct device *dev;
+ struct cdev cdev;
+ struct i2c_client *i2c_client;
+};
+
+
+/******************************************************************************
+ * FUNCTION PROTOTYPES
+ *****************************************************************************/
+//extern void fm_low_power_wa(int fmon);
+
+static int RDAFM_clear_hmute(struct i2c_client *client);
+static int RDAFM_enable_hmute(struct i2c_client *client);
+static int RDAFM_clear_tune(struct i2c_client *client);
+static int RDAFM_enable_tune(struct i2c_client *client);
+static int RDAFM_clear_seek(struct i2c_client *client);
+static int RDAFM_enable_seek(struct i2c_client *client);
+static int RDAFM_SetStereo(struct i2c_client *client,uint8_t b);
+static int RDAFM_SetRSSI_Threshold(struct i2c_client *client,uint8_t RssiThreshold);
+static int RDAFM_SetDe_Emphasis(struct i2c_client *client,uint8_t index);
+static bool RDAFM_Scan(struct i2c_client *client,
+ uint16_t min_freq, uint16_t max_freq,
+ uint16_t *pFreq, //get the valid freq after scan
+ uint16_t *pScanTBL,
+ uint16_t *ScanTBLsize,
+ uint16_t scandir,
+ uint16_t space);
+
+
+static int RDAFM_read(struct i2c_client *client, uint8_t addr, uint16_t *val);
+static int RDAFM_write(struct i2c_client *client, uint8_t addr, uint16_t val);
+static void RDAFM_em_test(struct i2c_client *client, uint16_t group_idx, uint16_t item_idx, uint32_t item_value);
+static int fm_setup_cdev(struct fm *fm);
+static int fm_ops_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+static loff_t fm_ops_lseek(struct file *filp, loff_t off, int whence);
+static int fm_ops_open(struct inode *inode, struct file *filp);
+static int fm_ops_release(struct inode *inode, struct file *filp);
+
+static int fm_init(struct i2c_client *client);
+static int fm_destroy(struct fm *fm);
+static int fm_powerup(struct fm *fm, struct fm_tune_parm *parm);
+static int fm_powerdown(struct fm *fm);
+
+static int fm_tune(struct fm *fm, struct fm_tune_parm *parm);
+static int fm_seek(struct fm *fm, struct fm_seek_parm *parm);
+static int fm_scan(struct fm *fm, struct fm_scan_parm *parm);
+static int fm_setvol(struct fm *fm, uint32_t vol);
+static int fm_getvol(struct fm *fm, uint32_t *vol);
+static int fm_getrssi(struct fm *fm, uint32_t *rssi);
+static int fm_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+static int fm_i2c_attach_adapter(struct i2c_adapter *adapter);
+static int fm_i2c_detect(struct i2c_adapter *adapter, int addr, int kind);
+static int fm_i2c_detach_client(struct i2c_client *client);
+#else
+static int fm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id);
+static int fm_i2c_detect(struct i2c_client *client, struct i2c_board_info *info);
+static int fm_i2c_remove(struct i2c_client *client);
+#endif
+
+/******************************************************************************
+ * GLOBAL DATA
+ *****************************************************************************/
+/* Addresses to scan */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+static unsigned short normal_i2c[] = {RDAFM_SLAVE_ADDR, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data RDAFM_addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+#else
+static const struct i2c_device_id fm_i2c_id = {RDAFM_DEV, 0};
+static unsigned short force[] = {RDAFM_I2C_PORT, RDAFM_SLAVE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END};
+static const unsigned short * const forces[] = {force, NULL};
+//static struct i2c_client_address_data addr_data = {.forces = forces};
+static unsigned short addr_data[] = {/*RDAFM_I2C_PORT,*/ RDAFM_SLAVE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END};
+
+#endif
+
+static struct i2c_driver RDAFM_driver = {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = RDAFM_DEV,
+ },
+ .attach_adapter = fm_i2c_attach_adapter,
+ .detach_client = fm_i2c_detach_client,
+#else
+ .class = I2C_CLASS_HWMON,
+ .probe = fm_i2c_probe,
+ .remove = fm_i2c_remove,
+ .detect = fm_i2c_detect,
+ .driver.name = RDAFM_DEV,
+ .driver.owner = THIS_MODULE,
+ .id_table = &fm_i2c_id,
+ //.address_data = &addr_data,
+ .address_list = (const unsigned short*) addr_data,
+#endif
+};
+
+static uint16_t RDAFM_CHIP_ID = 0x5808;
+static RDA_RADIO_WORK_E RDA_RADIO_WorkType = FM_RECEIVER;
+
+
+
+#if 1
+static uint16_t RDA5802N_initialization_reg[]={
+ 0xC005, //02h
+ 0x0000,
+ 0x0400,
+ 0xC6ED, //0x86AD, //05h
+ 0x6000,
+ 0x721A,
+ 0x0000,
+ 0x0000,
+ 0x0000, //0x0ah
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000, //0x10h
+ 0x0019,
+ 0x2A11,
+ 0xB042,
+ 0x2A11,
+ 0xB831, //0x15h
+ 0xC000,
+ 0x2A91,
+ 0x9400,
+ 0x00A8,
+ 0xc400, //0x1ah
+ 0xF7CF, //提高远端噪声抑制
+ 0x2414, //0x2ADC, //0x1ch 提升VIO VDD之间压差引起的不良
+ 0x806F,
+ 0x4608,
+ 0x0086,
+ 0x0661, //0x20H
+ 0x0000,
+ 0x109E,
+ 0x23C8,
+ 0x0406,
+ 0x0E1C, //0x25H
+};
+#else
+static uint16_t RDA5802N_initialization_reg[]={
+ 0xc401, //02h
+ 0x0000,
+ 0x0400,
+ 0x86ad, //05h//
+ 0x0000,
+ 0x42c6,
+ 0x0000,
+ 0x0000,
+ 0x0000, //0x0ah
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000, //0x10h
+ 0x0019,
+ 0x2a11,
+ 0xa053,//0x80,0x53,
+ 0x3e11,//0x22,0x11,
+ 0xfc7d, //0x15h
+ 0xc000,
+ 0x2a91,
+ 0x9400,
+ 0x00a8,
+ 0xc400, //0x1ah
+ 0xe000,
+ 0x2b1d, //0x23,0x14
+ 0x816a,
+ 0x4608,
+ 0x0086,
+ 0x0661, //0x20h
+ 0x0000,
+ 0x109e,
+ 0x2244,
+ 0x0408, //0x24
+ 0x0408, //0x25
+};
+#endif
+
+static RDA_FM_REG_T RDA5820NS_TX_initialization_reg[]={
+ {0x02, 0xE003},
+ {0xFF, 100}, // if address is 0xFF, sleep value ms
+ {0x02, 0xE001},
+ {0x19, 0x88A8},
+ {0x1A, 0x4290},
+ {0x68, 0x0AF0},
+ {0x40, 0x0001},
+ {0x41, 0x41FF},
+ {0xFF, 500},
+ {0x03, 0x1B90},
+};
+
+static RDA_FM_REG_T RDA5820NS_RX_initialization_reg[]={
+ {0x02, 0x0002}, //Soft reset
+ {0xFF, 100}, // wait
+ {0x02, 0xC001}, //Power Up
+ {0x05, 0x888F}, //LNAP 0x884F --LNAN
+ {0x06, 0x6000},
+ {0x13, 0x80E1},
+ {0x14, 0x2A11},
+ {0x1C, 0x22DE},
+ {0x21, 0x0020},
+ {0x03, 0x1B90},
+};
+
+
+
+static struct file_operations fm_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = fm_ops_ioctl,
+ .llseek = fm_ops_lseek,
+ .open = fm_ops_open,
+ .release = fm_ops_release,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+static DECLARE_MUTEX(fm_ops_mutex);
+#else
+DEFINE_SEMAPHORE(fm_ops_mutex);
+#endif
+
+/******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+ *****************************************************************************/
+
+
+
+static int RDAFM_GetChipID(struct i2c_client *client, uint16_t *pChipID)
+{
+ int err;
+ int ret = -1;
+ uint16_t val = 0x0002;
+
+ //Reset RDA FM
+ err = RDAFM_write(client, 0x02, val);
+ if(err < 0){
+#ifdef FMDEBUG
+ FM_DEBUG("RDAFM_GetChipID: reset FM chip failed!\n");
+#endif
+ ret = -1;
+ return ret;
+ }
+ msleep(80);
+
+ val = 0;
+ err = RDAFM_read(client, 0x0C, &val);
+ if (err == 0)
+ {
+ if ((0x5802 == val) || (0x5803 == val))
+ {
+ err = RDAFM_read(client, 0x0E, &val);
+
+ if (err == 0)
+ *pChipID = val;
+ else
+ *pChipID = 0x5802;
+
+#ifdef FMDEBUG
+ FM_DEBUG("RDAFM_GetChipID: Chip ID = %04X\n", val);
+#endif
+
+ ret = 0;
+
+ }
+ else if ((0x5805 == val) || (0x5820 == val))
+ {
+ *pChipID = val;
+ ret = 0;
+ }
+ else
+ {
+#ifdef FMDEBUG
+ FM_DEBUG("RDAFM_GetChipID: get chip ID failed! get value = %04X\n", val);
+#endif
+ ret = -1;
+ }
+
+ }
+ else
+ {
+#ifdef FMDEBUG
+ FM_DEBUG("RDAFM_GetChipID: get chip ID failed!\n");
+#endif
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/*
+ * RDAFM_read
+ */
+static int RDAFM_read(struct i2c_client *client, uint8_t addr, uint16_t *val)
+{
+ int n;
+ char b[2] = {0};
+
+ // first, send addr to RDAFM
+ n = i2c_master_send(client, (char*)&addr, 1);
+ if (n < 0)
+ {
+ FM_ALERT("RDAFM_read send:0x%X err:%d\n", addr, n);
+ return -1;
+ }
+
+ // second, receive two byte from RDAFM
+ n = i2c_master_recv(client, b, 2);
+ if (n < 0)
+ {
+ FM_ALERT("RDAFM_read recv:0x%X err:%d\n", addr, n);
+ return -1;
+ }
+
+ *val = (uint16_t)(b[0] << 8 | b[1]);
+
+ return 0;
+}
+
+/*
+ * RDAFM_write
+ */
+static int RDAFM_write(struct i2c_client *client, uint8_t addr, uint16_t val)
+{
+ int n;
+ char b[3];
+
+ b[0] = addr;
+ b[1] = (char)(val >> 8);
+ b[2] = (char)(val & 0xFF);
+
+ n = i2c_master_send(client, b, 3);
+ if (n < 0)
+ {
+ FM_ALERT("RDAFM_write send:0x%X err:%d\n", addr, n);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int RDAFM_clear_hmute(struct i2c_client *client)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+
+ FM_DEBUG("RDAFM_clear_hmute\n");
+
+ ret = RDAFM_read(client, 0x02, &tRegValue);
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_clear_hmute read register failed!\n");
+ return -1;
+ }
+
+ tRegValue |= (1 << 14);
+
+ ret = RDAFM_write(client, 0x02, tRegValue);
+
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_clear_hmute write register failed!\n");
+ return -1;
+ }
+
+ if(fm_tune_data.byPowerUp){
+ if (fm_tune(g_fm_struct, &(fm_tune_data.parm)) < 0)
+ {
+ fm_tune_data.byPowerUp = false;
+ memset(&fm_tune_data.parm, 0, sizeof(fm_tune_data.parm));
+ return -EPERM;
+ }
+ fm_tune_data.byPowerUp = false;
+ memset(&fm_tune_data.parm, 0, sizeof(fm_tune_data.parm));
+ }
+
+ return 0;
+}
+
+
+
+static int RDAFM_enable_hmute(struct i2c_client *client)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+
+ FM_DEBUG("RDAFM_enable_hmute\n");
+
+ ret = RDAFM_read(client, 0x02, &tRegValue);
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_enable_hmute read register failed!\n");
+ return -1;
+ }
+
+ tRegValue &= (~(1 << 14));
+
+ ret = RDAFM_write(client, 0x02, tRegValue);
+
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_enable_hmute write register failed!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+static int RDAFM_clear_tune(struct i2c_client *client)
+{
+ //Don't need it
+ return 0;
+}
+
+
+
+static int RDAFM_enable_tune(struct i2c_client *client)
+{
+ //Don't need it
+ return 0;
+}
+
+
+
+static int RDAFM_clear_seek(struct i2c_client *client)
+{
+ //Don't need it
+ return 0;
+}
+
+
+
+static int RDAFM_enable_seek(struct i2c_client *client)
+{
+ //Don't need it
+ return 0;
+}
+
+
+//b=true set stereo else set mono
+static int RDAFM_SetStereo(struct i2c_client *client, uint8_t b)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+
+ FM_DEBUG("RDAFM_SetStereo\n");
+
+ ret = RDAFM_read(client, 0x02, &tRegValue);
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_SetStereo read register failed!\n");
+ return -1;
+ }
+ if (b)
+ tRegValue &= (~(1 << 13));//set stereo
+ else
+ tRegValue |= (1 << 13); //set mono
+
+ ret = RDAFM_write(client, 0x02, tRegValue);
+
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_SetStereo write register failed!\n");
+ return -1;
+ }
+
+
+ return 0;
+
+}
+
+
+
+static int RDAFM_SetRSSI_Threshold(struct i2c_client *client, uint8_t RssiThreshold)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+
+ FM_DEBUG("RDAFM_SetRSSI_Threshold\n");
+
+ ret = RDAFM_read(client, 0x05, &tRegValue);
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_SetRSSI_Threshold read register failed!\n");
+ return -1;
+ }
+
+ tRegValue &= 0x80FF;//clear valume
+ tRegValue |= ((RssiThreshold & 0x7f) << 8); //set valume
+
+ ret = RDAFM_write(client, 0x05, tRegValue);
+
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_SetRSSI_Threshold write register failed!\n");
+ return -1;
+ }
+
+
+ return 0;
+
+}
+
+
+
+static int RDAFM_SetDe_Emphasis(struct i2c_client *client, uint8_t index)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+
+ FM_DEBUG("RDAFM_SetRSSI_Threshold\n");
+
+ ret = RDAFM_read(client, 0x04, &tRegValue);
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_SetRSSI_Threshold read register failed!\n");
+ return -1;
+ }
+
+ if (0 == index)
+ {
+ tRegValue &= (~(1 << 11));//De_Emphasis=75us
+ }
+ else if (1 == index)
+ {
+ tRegValue |= (1 << 11);//De_Emphasis=50us
+ }
+
+
+ ret = RDAFM_write(client, 0x04, tRegValue);
+
+ if (ret < 0)
+ {
+ FM_ALERT("RDAFM_SetRSSI_Threshold write register failed!\n");
+ return -1;
+ }
+
+
+ return 0;
+
+
+}
+
+static void RDAFM_em_test(struct i2c_client *client, uint16_t group_idx, uint16_t item_idx, uint32_t item_value)
+{
+ FM_ALERT("RDAFM_em_test %d:%d:%d\n", group_idx, item_idx, item_value);
+ switch (group_idx)
+ {
+ case mono:
+ if(item_value == 1)
+ {
+ RDAFM_SetStereo(client, 0); //force mono
+ }
+ else
+ {
+ RDAFM_SetStereo(client, 1); //stereo
+
+ }
+
+ break;
+ case stereo:
+ if(item_value == 0)
+ {
+ RDAFM_SetStereo(client, 1); //stereo
+ }
+ else
+ {
+ RDAFM_SetStereo(client, 0); //force mono
+ }
+ break;
+ case RSSI_threshold:
+ item_value &= 0x7F;
+ RDAFM_SetRSSI_Threshold(client, item_value);
+ break;
+ case Softmute_Enable:
+ if (item_idx)
+ {
+ RDAFM_enable_hmute(client);
+ }
+ else
+ {
+ RDAFM_clear_hmute(client);
+ }
+ break;
+ case De_emphasis:
+ if(item_idx >= 2) //0us
+ {
+ FM_ALERT("RDAFM not support De_emphasis 0\n");
+ }
+ else
+ {
+ RDAFM_SetDe_Emphasis(client,item_idx);//0=75us,1=50us
+ }
+ break;
+
+ case HL_Side:
+
+ break;
+ default:
+ FM_ALERT("RDAFM not support this setting\n");
+ break;
+ }
+}
+
+static bool RDAFM_Scan(struct i2c_client *client,
+ uint16_t min_freq, uint16_t max_freq,
+ uint16_t *pFreq,
+ uint16_t *pScanTBL,
+ uint16_t *ScanTBLsize,
+ uint16_t scandir,
+ uint16_t space)
+{
+ uint16_t tFreq, tRegValue = 0;
+ uint16_t tmp_scanTBLsize = *ScanTBLsize;
+ int ret = -1;
+ bool isTrueStation = false;
+ uint16_t oldValue = 0;
+ int channel = 0;
+ if((!pScanTBL) || (tmp_scanTBLsize == 0)) {
+ return false;
+ }
+
+ //clear the old value of pScanTBL
+ memset(pScanTBL, 0, sizeof(uint16_t)*RDA599X_SCANTBL_SIZE);
+
+ if(tmp_scanTBLsize > RDA599X_SCANTBL_SIZE)
+ {
+ tmp_scanTBLsize = RDA599X_SCANTBL_SIZE;
+ }
+
+ //scan up
+ if(scandir == RDA599X_FM_SCAN_UP){ // now, only support scan up
+ tFreq = min_freq;
+ }else{ //scan down
+ tFreq = max_freq;//max_freq compare need or not
+ }
+
+ //mute FM
+ RDAFM_enable_hmute(client);
+#if 0
+ //set seekth
+ tRegValue = 0;
+ RDAFM_read(client, 0x05, &tRegValue);
+ tRegValue &= (~(0x7f<<8));
+ tRegValue |= ((0x8 & 0x7f) << 8);
+ RDAFM_write(client, 0x05, tRegValue);
+ msleep(50);
+#endif
+ atomic_set(&scan_complete_flag, 1);
+ do {
+ if(atomic_read(&scan_complete_flag) == 0)
+ break;
+ isTrueStation = false;
+
+ //set channel and enable TUNE
+ tRegValue = 0;
+ RDAFM_read(client, 0x03, &tRegValue);
+ tRegValue &= (~(0x03ff<<6)); //clear bit[15:6]
+ channel = tFreq - min_freq;
+ tRegValue |= ((channel << 6) | (1 << 4)); //set bit[15:6] and bit[4]
+ ret = RDAFM_write(client, 0x03, tRegValue);
+ msleep(40);
+
+ //read 0x0B and check FM_TRUE(bit[8])
+ tRegValue = 0;
+ ret = RDAFM_read(client, 0x0B, &tRegValue);
+ if(!ret){
+ if((tRegValue & 0x0100) == 0x0100){
+ isTrueStation = true;
+ }
+ }
+
+ //if this freq is a true station, read the channel
+ if(isTrueStation){
+ //tRegValue = 0;
+ //RDAFM_read(client, 0x03, &tRegValue);
+ //channel = ((tRegValue>>6) & 0x03ff) - 5;
+ channel = channel - 5;
+ if((channel >= 0) && (channel != 85)){
+ oldValue = *(pScanTBL+(channel/16));
+ oldValue |= (1<<(channel%16));
+ *(pScanTBL+(channel/16)) = oldValue;
+ }
+ }
+
+ //increase freq
+ tFreq += space;
+ }while( tFreq <= max_freq );
+
+#if defined(MTK_MT6515) && defined(MT6626)
+ *(pScanTBL+13) = 0xb2d4;
+ *(pScanTBL+14) = 0xb2d4;
+ *(pScanTBL+15) = 0xb2d4;
+#endif
+
+ *ScanTBLsize = tmp_scanTBLsize;
+ *pFreq = 0;
+
+ //clear FM mute
+ RDAFM_clear_hmute(client);
+
+ return true;
+}
+
+
+static int fm_setup_cdev(struct fm *fm)
+{
+ int err;
+
+ err = alloc_chrdev_region(&fm->dev_t, 0, 1, FM_NAME);
+ if (err) {
+ FM_ALERT("alloc dev_t failed\n");
+ return -1;
+ }
+
+ FM_ALERT("alloc %s:%d:%d\n", FM_NAME,
+ MAJOR(fm->dev_t), MINOR(fm->dev_t));
+
+ cdev_init(&fm->cdev, &fm_ops);
+
+ fm->cdev.owner = THIS_MODULE;
+ fm->cdev.ops = &fm_ops;
+
+ err = cdev_add(&fm->cdev, fm->dev_t, 1);
+ if (err) {
+ FM_ALERT("alloc dev_t failed\n");
+ return -1;
+ }
+
+ fm->cls = class_create(THIS_MODULE, FM_NAME);
+ if (IS_ERR(fm->cls)) {
+ err = PTR_ERR(fm->cls);
+ FM_ALERT("class_create err:%d\n", err);
+ return err;
+ }
+ fm->dev = device_create(fm->cls, NULL, fm->dev_t, NULL, FM_NAME);
+
+ return 0;
+}
+
+
+
+static int fm_ops_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct fm *fm = container_of(filp->f_dentry->d_inode->i_cdev, struct fm, cdev);
+
+ FM_DEBUG("%s cmd(%x)\n", __func__, cmd);
+
+ switch(cmd)
+ {
+ case FM_IOCTL_POWERUP:
+ {
+ struct fm_tune_parm parm;
+ FM_DEBUG("FM_IOCTL_POWERUP\n");
+
+ // FIXME!!
+ // if (!capable(CAP_SYS_ADMIN))
+ // return -EPERM;
+
+ if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm)))
+ return -EFAULT;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ ret = fm_powerup(fm, &parm);
+ up(&fm_ops_mutex);
+ if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm)))
+ return -EFAULT;
+ // fm_low_power_wa(1);
+ break;
+ }
+
+ case FM_IOCTL_POWERDOWN:
+ {
+ FM_DEBUG("FM_IOCTL_POWERDOWN\n");
+ // FIXME!!
+ // if (!capable(CAP_SYS_ADMIN))
+ // return -EPERM;
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ ret = fm_powerdown(fm);
+ up(&fm_ops_mutex);
+// fm_low_power_wa(0);
+ break;
+ }
+
+ // tune (frequency, auto Hi/Lo ON/OFF )
+ case FM_IOCTL_TUNE:
+ {
+ struct fm_tune_parm parm;
+ FM_DEBUG("FM_IOCTL_TUNE\n");
+// FIXME!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm)))
+ return -EFAULT;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ ret = fm_tune(fm, &parm);
+ up(&fm_ops_mutex);
+
+ if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case FM_IOCTL_SEEK:
+ {
+ struct fm_seek_parm parm;
+ FM_DEBUG("FM_IOCTL_SEEK\n");
+
+// FIXME!!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_seek_parm)))
+ return -EFAULT;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ ret = fm_seek(fm, &parm);
+ up(&fm_ops_mutex);
+
+ if (copy_to_user((void*)arg, &parm, sizeof(struct fm_seek_parm)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case FM_IOCTL_SETVOL:
+ {
+ uint32_t vol;
+ FM_DEBUG("FM_IOCTL_SETVOL\n");
+
+// FIXME!!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if(copy_from_user(&vol, (void*)arg, sizeof(uint32_t))) {
+ FM_ALERT("copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ ret = fm_setvol(fm, vol);
+ up(&fm_ops_mutex);
+
+ break;
+ }
+
+ case FM_IOCTL_GETVOL:
+ {
+ uint32_t vol;
+ FM_DEBUG("FM_IOCTL_GETVOL\n");
+
+// FIXME!!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ ret = fm_getvol(fm, &vol);
+ up(&fm_ops_mutex);
+
+ if (copy_to_user((void*)arg, &vol, sizeof(uint32_t)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case FM_IOCTL_MUTE:
+ {
+ uint32_t bmute;
+ FM_DEBUG("FM_IOCTL_MUTE\n");
+
+ // FIXME!!
+ // if (!capable(CAP_SYS_ADMIN))
+ // return -EPERM;
+ if (copy_from_user(&bmute, (void*)arg, sizeof(uint32_t)))
+ {
+ FM_DEBUG("copy_from_user mute failed!\n");
+ return -EFAULT;
+ }
+
+ FM_DEBUG("FM_IOCTL_MUTE:%d\n", bmute);
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+
+ if (bmute){
+ ret = RDAFM_enable_hmute(fm->i2c_client);
+ }else{
+ ret = RDAFM_clear_hmute(fm->i2c_client);
+ }
+
+ up(&fm_ops_mutex);
+
+ break;
+ }
+
+ case FM_IOCTL_GETRSSI:
+ {
+ uint32_t rssi;
+ FM_DEBUG("FM_IOCTL_GETRSSI\n");
+
+// FIXME!!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+
+ ret = fm_getrssi(fm, &rssi);
+ up(&fm_ops_mutex);
+
+ if (copy_to_user((void*)arg, &rssi, sizeof(uint32_t)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case FM_IOCTL_RW_REG:
+ {
+ struct fm_ctl_parm parm_ctl;
+ FM_DEBUG("FM_IOCTL_RW_REG\n");
+
+// FIXME!!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if (copy_from_user(&parm_ctl, (void*)arg, sizeof(struct fm_ctl_parm)))
+ return -EFAULT;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+
+ if(parm_ctl.rw_flag == 0) //write
+ {
+ ret = RDAFM_write(fm->i2c_client, parm_ctl.addr, parm_ctl.val);
+ }
+ else
+ {
+ ret = RDAFM_read(fm->i2c_client, parm_ctl.addr, &parm_ctl.val);
+ }
+
+ up(&fm_ops_mutex);
+ if ((parm_ctl.rw_flag == 0x01) && (!ret)) // Read success.
+ {
+ if (copy_to_user((void*)arg, &parm_ctl, sizeof(struct fm_ctl_parm)))
+ return -EFAULT;
+ }
+ break;
+ }
+
+ case FM_IOCTL_GETCHIPID:
+ {
+ uint16_t chipid;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+
+ RDAFM_GetChipID(fm->i2c_client, &chipid);
+ //chipid = fm->chip_id;
+ chipid = 0x6620;
+ FM_DEBUG("FM_IOCTL_GETCHIPID:%04x\n", chipid);
+ up(&fm_ops_mutex);
+
+ if (copy_to_user((void*)arg, &chipid, sizeof(uint16_t)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case FM_IOCTL_EM_TEST:
+ {
+ struct fm_em_parm parm_em;
+ FM_DEBUG("FM_IOCTL_EM_TEST\n");
+
+// FIXME!!
+// if (!capable(CAP_SYS_ADMIN))
+// return -EPERM;
+
+ if (copy_from_user(&parm_em, (void*)arg, sizeof(struct fm_em_parm)))
+ return -EFAULT;
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+
+ RDAFM_em_test(fm->i2c_client, parm_em.group_idx, parm_em.item_idx, parm_em.item_value);
+
+ up(&fm_ops_mutex);
+
+ break;
+ }
+ case FM_IOCTL_IS_FM_POWERED_UP:
+ {
+ uint32_t powerup;
+ FM_DEBUG("FM_IOCTL_IS_FM_POWERED_UP");
+ if (fm->powerup) {
+ powerup = 1;
+ } else {
+ powerup = 0;
+ }
+ if (copy_to_user((void*)arg, &powerup, sizeof(uint32_t)))
+ return -EFAULT;
+ break;
+ }
+
+#ifdef FMDEBUG
+ case FM_IOCTL_DUMP_REG:
+ {
+ uint16_t chipid = 0;
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ RDAFM_GetChipID(fm->i2c_client, &chipid);
+ up(&fm_ops_mutex);
+
+ break;
+ }
+#endif
+
+ case FM_IOCTL_SCAN:
+ {
+ struct fm_scan_parm parm;
+ FM_DEBUG("FM_IOCTL_SCAN\n");
+ if (false == fm->powerup){
+ return -EFAULT;
+ }
+ if(copy_from_user(&parm, (void*)arg, sizeof(struct fm_scan_parm))){
+ return -EFAULT;
+ }
+ if (down_interruptible(&fm_ops_mutex)){
+ return -EFAULT;
+ }
+ fm_scan(fm, &parm);
+ up(&fm_ops_mutex);
+
+ if(copy_to_user((void*)arg, &parm, sizeof(struct fm_scan_parm))){
+ return -EFAULT;
+ }
+
+ break;
+ }
+
+ case FM_IOCTL_STOP_SCAN:
+ {
+ FM_DEBUG("FM_IOCTL_STOP_SCAN\n");
+ break;
+ }
+
+ default:
+ {
+ FM_DEBUG("default\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+static loff_t fm_ops_lseek(struct file *filp, loff_t off, int whence)
+{
+// struct fm *fm = filp->private_data;
+
+ if(whence == SEEK_END){
+ //fm_hwscan_stop(fm);
+ atomic_set(&scan_complete_flag, 0);
+ }else if(whence == SEEK_SET){
+ //FM_EVENT_SEND(fm->rds_event, FM_RDS_DATA_READY);
+ }
+ return off;
+}
+
+static int fm_ops_open(struct inode *inode, struct file *filp)
+{
+ struct fm *fm = container_of(inode->i_cdev, struct fm, cdev);
+
+ FM_DEBUG("%s\n", __func__);
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+
+ // TODO: only have to set in the first time?
+ // YES!!!!
+
+ fm->ref++;
+
+ up(&fm_ops_mutex);
+
+ filp->private_data = fm;
+
+ // TODO: check open flags
+
+ return 0;
+}
+
+static int fm_ops_release(struct inode *inode, struct file *filp)
+{
+ int err = 0;
+ struct fm *fm = container_of(inode->i_cdev, struct fm, cdev);
+
+ FM_DEBUG("%s\n", __func__);
+
+ if (down_interruptible(&fm_ops_mutex))
+ return -EFAULT;
+ fm->ref--;
+ if(fm->ref < 1) {
+ if(fm->powerup == true) {
+ fm_powerdown(fm);
+ }
+ }
+
+ up(&fm_ops_mutex);
+
+ return err;
+}
+
+//#define WMT_TEST_ORI 1
+
+static int fm_init(struct i2c_client *client)
+{
+ int err;
+ struct fm *fm = NULL;
+ int ret = -1;
+
+
+ FM_DEBUG("%s()\n", __func__);
+ if (!(fm = kzalloc(sizeof(struct fm), GFP_KERNEL)))
+ {
+ FM_ALERT("-ENOMEM\n");
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+
+ fm->ref = 0;
+ fm->powerup = false;
+ atomic_set(&scan_complete_flag, 0);
+
+#ifdef RDA599X_FM_SUPPORT
+
+ ret = rda_fm_power_on();
+ if(ret < 0){
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+ msleep(100);
+#endif
+
+ // First, read 5802NM chip ID
+ FM_DEBUG("%s()First, read 5802NM chip ID\n", __func__);
+ ret = RDAFM_GetChipID(client, &RDAFM_CHIP_ID);
+ if(ret < 0){
+ FM_DEBUG("%s() read RDA chip ID failed\n", __func__);
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+ else{
+ fm->chip_id = RDAFM_CHIP_ID;
+ }
+
+ FM_DEBUG("%s() 5802NM chip ID = 0x%04x\n", __func__, RDAFM_CHIP_ID);
+
+#ifdef RDA599X_FM_SUPPORT
+ ret = rda_fm_power_off();
+ if(ret < 0){
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+#endif
+
+
+#if WMT_TEST_ORI
+ printk("ori code\n");
+ // if failed, means use FM in 5990P_E
+ if(ret < 0){
+ // enable the FM chip in combo
+ FM_DEBUG("%s() enable the FM chip in combo\n", __func__);
+ ret = rda_fm_power_on();
+ if(ret < 0){
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+ msleep(100);
+ ret = RDAFM_GetChipID(client, &RDAFM_CHIP_ID);
+ FM_DEBUG("%s() the FM in combo chip ID = 0x%04x\n", __func__, RDAFM_CHIP_ID);
+ if(ret < 0){
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }else{
+ fm->chip_id = RDAFM_CHIP_ID;
+ }
+
+ // disable the FM chip for power saving
+ ret = rda_fm_power_off();
+ if(ret < 0){
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+ }else{
+ fm->chip_id = RDAFM_CHIP_ID;
+ }
+
+
+#endif
+
+
+ if ((err = fm_setup_cdev(fm)))
+ {
+ goto ERR_EXIT;
+ }
+
+ g_fm_struct = fm;
+ fm->i2c_client = client;
+ i2c_set_clientdata(client, fm);
+
+
+ /***********Add porc file system*************/
+
+ g_fm_proc = create_proc_entry(FM_PROC_FILE, 0444, NULL);
+ if (g_fm_proc == NULL) {
+ FM_ALERT("create_proc_entry failed\n");
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ } else {
+ g_fm_proc->read_proc = fm_proc_read;
+ g_fm_proc->write_proc = NULL;
+ //g_fm_proc->owner = THIS_MODULE;
+ FM_ALERT("create_proc_entry success\n");
+ }
+
+ /********************************************/
+
+ FM_DEBUG("fm_init is ok!\n");
+
+ return 0;
+
+ERR_EXIT:
+ kfree(fm);
+#ifdef RDA599X_FM_SUPPORT
+ rda_fm_power_off();
+#endif
+
+ return err;
+}
+
+static int fm_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int cnt= 0;
+ struct fm *fm = g_fm_struct;
+ FM_ALERT("Enter fm_proc_read.\n");
+ if(off != 0)
+ return 0;
+ if (fm != NULL && fm->powerup) {
+ cnt = sprintf(page, "1\n");
+ } else {
+ cnt = sprintf(page, "0\n");
+ }
+ *eof = 1;
+ FM_ALERT("Leave fm_proc_read. cnt = %d\n", cnt);
+ return cnt;
+}
+
+
+static int fm_destroy(struct fm *fm)
+{
+ int err = 0;
+
+ FM_DEBUG("%s\n", __func__);
+
+ device_destroy(fm->cls, fm->dev_t);
+ class_destroy(fm->cls);
+
+ cdev_del(&fm->cdev);
+ unregister_chrdev_region(fm->dev_t, 1);
+
+ fm_powerdown(fm);
+
+ /***********************************/
+ remove_proc_entry(FM_PROC_FILE, NULL);
+
+ /**********************************/
+
+ // FIXME: any other hardware configuration ?
+
+ // free all memory
+ kfree(fm);
+
+ return err;
+}
+
+/*
+ * fm_powerup
+ */
+static int fm_powerup(struct fm *fm, struct fm_tune_parm *parm)
+{
+ int i;
+ uint16_t tRegValue = 0x0002;
+ int ret = -1;
+
+ struct i2c_client *client = fm->i2c_client;
+
+ if (fm->powerup)
+ {
+ parm->err = FM_BADSTATUS;
+ return -EPERM;
+ }
+
+ // if chip_id is ID_RDA5820NS, enable the FM chip in combo
+#ifdef RDA599X_FM_SUPPORT
+ ret = rda_fm_power_on();
+ if(ret < 0){
+ return -EPERM;
+ }
+ msleep(100);
+#endif
+
+
+ //Reset RDA FM
+ tRegValue = 0x0002;
+ RDAFM_write(client, 0x02, tRegValue);
+ msleep(100);
+
+ if ((ID_RDA5802N == RDAFM_CHIP_ID) || (ID_RDA5802H == RDAFM_CHIP_ID)){
+ //if (ID_RDA5802N == RDAFM_CHIP_ID){
+
+ for (i=0; i<((sizeof(RDA5802N_initialization_reg)) / (sizeof(uint16_t))); i++)
+ {
+ ret = RDAFM_write(client, i+2, RDA5802N_initialization_reg[i]);
+
+ if (ret < 0)
+ {
+ FM_DEBUG("fm_powerup init failed!\n");
+ FM_ALERT("fm_powerup init failed!\n");
+ parm->err = FM_FAILED;
+
+ return -EPERM;
+ }
+ }
+
+ }else if (ID_RDA5820NS == RDAFM_CHIP_ID){
+ if(RDA_RADIO_WorkType == FM_RECEIVER){
+ for (i = 0; i < ((sizeof(RDA5820NS_RX_initialization_reg)) / (sizeof(RDA_FM_REG_T))); i++)
+ {
+ if(RDA5820NS_RX_initialization_reg[i].address == 0xFF){
+ msleep(RDA5820NS_RX_initialization_reg[i].value);
+ }else{
+ ret = RDAFM_write(client, RDA5820NS_RX_initialization_reg[i].address, RDA5820NS_RX_initialization_reg[i].value);
+ if (ret < 0)
+ {
+ FM_DEBUG("fm_powerup init failed!\n");
+ parm->err = FM_FAILED;
+ return -EPERM;
+ }
+ }
+ }
+ }else{
+ for (i = 0; i < ((sizeof(RDA5820NS_TX_initialization_reg)) / (sizeof(RDA_FM_REG_T))); i++)
+ {
+ if(RDA5820NS_TX_initialization_reg[i].address == 0xFF){
+ msleep(RDA5820NS_TX_initialization_reg[i].value);
+ }else{
+ ret = RDAFM_write(client, RDA5820NS_TX_initialization_reg[i].address, RDA5820NS_TX_initialization_reg[i].value);
+ if (ret < 0)
+ {
+ FM_DEBUG("fm_powerup init failed!\n");
+ parm->err = FM_FAILED;
+ return -EPERM;
+ }
+ }
+ }
+ }
+
+ }
+
+
+ FM_DEBUG("pwron ok\n");
+ fm->powerup = true;
+
+ if (fm_tune(fm, parm) < 0)
+ {
+ return -EPERM;
+ }
+ fm_tune_data.byPowerUp = true;
+ memcpy(&fm_tune_data.parm, parm, sizeof(fm_tune_data.parm));
+
+ parm->err = FM_SUCCESS;
+
+ return 0;
+
+}
+
+/*
+ * fm_powerdown
+ */
+static int fm_powerdown(struct fm *fm)
+{
+ uint16_t tRegValue = 0;
+ int ret = -1;
+ struct i2c_client *client = fm->i2c_client;
+
+ RDAFM_read(client, 0x02, &tRegValue);
+ tRegValue &= (~(1 << 0));
+ RDAFM_write(client, 0x02, tRegValue);
+/*
+ if((fm->chip_id == ID_RDA5820NS) || (fm->chip_id == ID_RDA5991_FM){
+ ret = rda_fm_power_off();
+ if(ret < 0){
+ return -EPERM;
+ }
+ }
+*/
+
+#ifdef RDA599X_FM_SUPPORT
+ ret = rda_fm_power_off();
+ if(ret < 0){
+ return -EPERM;
+ }
+#endif
+
+
+ fm->powerup = false;
+ FM_ALERT("pwrdown ok\n");
+
+ return 0;
+}
+
+/*
+ * fm_seek
+ */
+static int fm_seek(struct fm *fm, struct fm_seek_parm *parm)
+{
+ int ret = 0;
+ uint16_t val = 0;
+ uint8_t spaec = 1;
+ uint16_t tFreq = 875;
+ uint16_t tRegValue = 0;
+ uint16_t bottomOfBand = 875;
+ int falseStation = -1;
+
+
+ struct i2c_client *client = fm->i2c_client;
+
+ if (!fm->powerup)
+ {
+ parm->err = FM_BADSTATUS;
+ return -EPERM;
+ }
+
+ if (parm->space == FM_SPACE_100K)
+ {
+ spaec = 1;
+ val &= (~((1<<0) | (1<<1)));
+ }
+ else if (parm->space == FM_SPACE_200K)
+ {
+ spaec = 2;
+ val &= (~(1<<1));
+ val |= (1<<0);
+ }
+ else
+ {
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ if (parm->band == FM_BAND_UE)
+ {
+ val &= (~((1<<2) | (1<<3)));
+ bottomOfBand = 875;
+ fm->min_freq = 875;
+ fm->max_freq = 1080;
+ }
+ else if (parm->band == FM_BAND_JAPAN)
+ {
+ val &= (~(1<<3));
+ val |= (1 << 2);
+ bottomOfBand = 760;
+ fm->min_freq = 760;
+ fm->max_freq = 910;
+ }
+ else if (parm->band == FM_BAND_JAPANW) {
+ val &= (~(1<<2));
+ val |= (1 << 3);
+ bottomOfBand = 760;
+ fm->min_freq = 760;
+ fm->max_freq = 1080;
+ }
+ else
+ {
+ FM_ALERT("band:%d out of range\n", parm->band);
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ if (parm->freq < fm->min_freq || parm->freq > fm->max_freq) {
+ FM_ALERT("freq:%d out of range\n", parm->freq);
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ if (parm->seekth > 0x0B) {
+ FM_ALERT("seekth:%d out of range\n", parm->seekth);
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+#if 0
+ RDAFM_read(client, 0x05, &tRegValue);
+ tRegValue &= (~(0x7f<<8));
+ //tRegValue |= ((parm->seekth & 0x7f) << 8);
+ tRegValue |= ((0x8 & 0x7f) << 8);
+ RDAFM_write(client, 0x05, tRegValue);
+#endif
+
+#ifdef FMDEBUG
+ if (parm->seekdir == FM_SEEK_UP)
+ FM_DEBUG("seek %d up\n", parm->freq);
+ else
+ FM_DEBUG("seek %d down\n", parm->freq);
+#endif
+
+ // (1) set hmute bit
+ RDAFM_enable_hmute(client);
+
+ tFreq = parm->freq;
+
+ do {
+ if (parm->seekdir == FM_SEEK_UP)
+ tFreq += spaec;
+ else
+ tFreq -= spaec;
+
+ if (tFreq > fm->max_freq)
+ tFreq = fm->min_freq;
+ if (tFreq < fm->min_freq)
+ tFreq = fm->max_freq;
+
+ val = (((tFreq - bottomOfBand+5) << 6) | (1 << 4) | (val & 0x0f));
+ RDAFM_write(client, 0x03, val);
+ msleep(40);
+ ret = RDAFM_read(client, 0x0B, &tRegValue);
+ if (ret < 0)
+ {
+ FM_DEBUG("fm_seek: read register failed tunning freq = %4X\n", tFreq);
+ falseStation = -1;
+ }
+ else
+ {
+ if ((tRegValue & 0x0100) == 0x0100)
+ falseStation = 0;
+ else
+ falseStation = -1;
+ }
+
+ if(falseStation == 0)
+ break;
+ }while(tFreq != parm->freq);
+
+
+ //clear hmute
+ RDAFM_clear_hmute(client);
+
+ if (falseStation == 0) // seek successfully
+ {
+ parm->freq = tFreq;
+ FM_ALERT("fm_seek success, freq:%d\n", parm->freq);
+ parm->err = FM_SUCCESS;
+ }
+ else
+ {
+ FM_ALERT("fm_seek failed, invalid freq\n");
+ parm->err = FM_SEEK_FAILED;
+ ret = -1;
+ }
+
+ return ret;
+}
+static int fm_scan(struct fm *fm, struct fm_scan_parm *parm)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+ uint16_t scandir = RDA599X_FM_SCAN_UP; //scandir 搜索方向
+ uint8_t space = 1;
+ struct i2c_client *client = fm->i2c_client;
+
+ if (!fm->powerup){
+ parm->err = FM_BADSTATUS;
+ return -EPERM;
+ }
+
+ RDAFM_read(client, 0x03, &tRegValue);
+
+ if (parm->space == FM_SPACE_100K){
+ space = 1;
+ tRegValue &= (~((1<<0) | (1<<1))); //set 03H's bit[1:0] to 00
+ }else if (parm->space == FM_SPACE_200K) {
+ space = 2;
+ tRegValue &= (~(1<<1)); //clear bit[1]
+ tRegValue |= (1<<0); //set bit[0]
+ }else{
+ //default
+ space = 1;
+ tRegValue &= (~((1<<0) | (1<<1))); //set 03H's bit[1:0] to 00
+ }
+
+ if(parm->band == FM_BAND_UE){
+ tRegValue &= (~((1<<2) | (1<<3)));
+ fm->min_freq = 875;
+ fm->max_freq = 1080;
+ }else if(parm->band == FM_BAND_JAPAN){
+ tRegValue &= (~(1<<3));
+ tRegValue |= (1 << 2);
+ fm->min_freq = 760;
+ fm->max_freq = 900;
+ }else if(parm->band == FM_BAND_JAPANW){
+ tRegValue &= (~(1<<2));
+ tRegValue |= (1 << 3);
+ fm->min_freq = 760;
+ fm->max_freq = 1080;
+ }else{
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ //set space and band
+ RDAFM_write(client, 0x03, tRegValue);
+ msleep(40);
+
+
+ if(RDAFM_Scan(client, fm->min_freq, fm->max_freq, &(parm->freq), parm->ScanTBL, &(parm->ScanTBLSize), scandir, space)){
+ parm->err = FM_SUCCESS;
+ }else{
+ parm->err = FM_SEEK_FAILED;
+ }
+
+ return ret;
+}
+
+
+static int fm_setvol(struct fm *fm, uint32_t vol)
+{
+ int ret = 0;
+ uint16_t tRegValue = 0;
+ struct i2c_client *client = fm->i2c_client;
+
+ if (vol > 15)
+ vol = 15;
+
+ FM_DEBUG("fm_setvol:%d\n", vol);
+
+ ret = RDAFM_read(client, 0x05, &tRegValue);
+ if (ret)
+ return -EPERM;
+ tRegValue &= ~(0x000f);
+ tRegValue |= vol;
+
+ ret = RDAFM_write(client, 0x05, tRegValue);
+ if (ret)
+ return -EPERM;
+
+ return 0;
+}
+
+static int fm_getvol(struct fm *fm, uint32_t *vol)
+{
+ int ret = 0;
+ uint16_t tRegValue;
+ struct i2c_client *client = fm->i2c_client;
+
+ ret = RDAFM_read(client, 0x05, &tRegValue);
+ if (ret)
+ return -EPERM;
+
+
+ *vol = (tRegValue & 0x000F);
+
+ return 0;
+}
+
+static int fm_getrssi(struct fm *fm, uint32_t *rssi)
+{
+ int ret = 0;
+ uint16_t tRegValue;
+ struct i2c_client *client = fm->i2c_client;
+
+ ret = RDAFM_read(client, 0x0B, &tRegValue);
+ if (ret)
+ return -EPERM;
+
+
+ *rssi = (uint32_t)((tRegValue >> 9) & RDAFM_MASK_RSSI);
+
+ FM_DEBUG("rssi value:%d\n", *rssi);
+
+ return 0;
+}
+
+/*
+ * fm_tune
+ */
+static int fm_tune(struct fm *fm, struct fm_tune_parm *parm)
+{
+ int ret;
+ uint16_t val = 0;
+ uint8_t space = 1;
+ uint16_t bottomOfBand = 875;
+
+ struct i2c_client *client = fm->i2c_client;
+
+ FM_DEBUG("%s\n", __func__);
+
+ if (!fm->powerup)
+ {
+ parm->err = FM_BADSTATUS;
+ return -EPERM;
+ }
+
+ if (parm->space == FM_SPACE_100K)
+ {
+ space = 1;
+ val &= (~((1<<0) | (1<<1)));
+ }
+ else if (parm->space == FM_SPACE_200K)
+ {
+ space = 2;
+ val |= (1<<0);
+ val &= (~(1<<1));
+ }
+ else
+ {
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ if (parm->band == FM_BAND_UE)
+ {
+ val &= (~((1<<2) | (1<<3)));
+ bottomOfBand = 875;
+ fm->min_freq = 875;
+ fm->max_freq = 1080;
+ }
+ else if (parm->band == FM_BAND_JAPAN)
+ {
+ val &= (~(1<<3));
+ val |= (1 << 2);
+ bottomOfBand = 760;
+ fm->min_freq = 760;
+ fm->max_freq = 910;
+ }
+ else if (parm->band == FM_BAND_JAPANW) {
+ val &= (~(1<<2));
+ val |= (1 << 3);
+ bottomOfBand = 760;
+ fm->min_freq = 760;
+ fm->max_freq = 1080;
+ }
+ else
+ {
+ FM_ALERT("band:%d out of range\n", parm->band);
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ if (parm->freq < fm->min_freq || parm->freq > fm->max_freq) {
+ FM_ALERT("freq:%d out of range\n", parm->freq);
+ parm->err = FM_EPARM;
+ return -EPERM;
+ }
+
+ FM_DEBUG("fm_tune, freq:%d\n", parm->freq);
+
+ //RDAFM_enable_hmute(client);
+
+ val = (((parm->freq - bottomOfBand + 5) << 6) | (1 << 4) | (val & 0x0f));
+
+ ret = RDAFM_write(client, 0x03, val);
+ if (ret < 0)
+ {
+ FM_ALERT("fm_tune write freq failed\n");
+ parm->err = FM_SEEK_FAILED;
+ return ret;
+ }
+ msleep(40);
+
+#ifdef FM_DEBUG
+ RDAFM_read(client, 0x0A, &val);
+ FM_DEBUG("%s() read freq reg value = 0x%04x\n", __func__, val);
+#endif
+
+ return ret;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+/*
+ * fm_i2c_attach_adapter
+ */
+static int fm_i2c_attach_adapter(struct i2c_adapter *adapter)
+{
+ int err = 0;
+
+ if (adapter->id == RDAFM_I2C_PORT)
+ {
+ return i2c_probe(adapter, &RDAFM_addr_data, fm_i2c_detect);
+ }
+
+ return err;
+}
+
+/*
+ * fm_i2c_detect
+ * This function is called by i2c_detect
+ */
+static int fm_i2c_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+ int err;
+ struct i2c_client *client = NULL;
+
+ /* skip this since MT6516 shall support all the needed functionalities
+ if (!i2c_check_functionality(adapter, xxx))
+ {
+ FM_DEBUG("i2c_check_functionality failed\n");
+ return -ENOTSUPP;
+ }
+ */
+
+ /* initial i2c client */
+ if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+ {
+ FM_ALERT("kzalloc failed\n");
+ err = -ENOMEM;
+ goto ERR_EXIT;
+ }
+
+ client->addr = addr;
+ client->adapter = adapter;
+ client->driver = &RDAFM_driver;
+ client->flags = 0;
+ strncpy(client->name, "RDA FM RADIO", I2C_NAME_SIZE);
+
+ if ((err = fm_init(client)))
+ {
+ FM_ALERT("fm_init ERR:%d\n", err);
+ goto ERR_EXIT;
+ }
+
+ if (err = i2c_attach_client(client))
+ {
+ FM_ALERT("i2c_attach_client ERR:%d\n", err);
+ goto ERR_EXIT;
+ }
+
+ return 0;
+
+ERR_EXIT:
+ kfree(client);
+
+ return err;
+}
+static int fm_i2c_detach_client(struct i2c_client *client)
+{
+ int err = 0;
+ struct fm *fm = i2c_get_clientdata(client);
+
+ FM_DEBUG("fm_i2c_detach_client\n");
+
+ err = i2c_detach_client(client);
+ if (err)
+ {
+ dev_err(&client->dev, "fm_i2c_detach_client failed\n");
+ return err;
+ }
+
+ fm_destroy(fm);
+ kfree(client);
+
+ return err;
+}
+#else
+static int fm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = -1;
+ FM_DEBUG("fm_i2c_probe\n");
+ //client->timing = 50;
+ //client->timing = 200;
+ if ((err = fm_init(client)))
+ {
+ FM_ALERT("fm_init ERR:%d\n", err);
+ goto ERR_EXIT;
+ }
+
+ return 0;
+
+ERR_EXIT:
+ return err;
+}
+/*
+static int fm_for_read_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ client_for_read = client;
+ return 0;
+}
+*/
+
+static int fm_i2c_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ if(adapter->nr == 1)
+ {
+ FM_DEBUG("fm_i2c_detect\n");
+ strcpy(info->type, RDAFM_DEV);
+ return 0;
+ }
+ else
+ {
+ return -ENODEV;
+ }
+}
+/*
+static int fm_for_read_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
+{
+ FM_DEBUG("fm_wifi_i2c_detect\n");
+ printk(KERN_DEBUG "####[%s(): %d]\n", __func__, __LINE__);
+ strcpy(info->type, RDAFM_READ_DEV);
+ return 0;
+}
+*/
+
+static int fm_i2c_remove(struct i2c_client *client)
+{
+ int err = 0;
+ struct fm *fm = i2c_get_clientdata(client);
+
+ FM_DEBUG("fm_i2c_remove\n");
+ if(fm)
+ {
+ fm_destroy(fm);
+ fm = NULL;
+ }
+
+ return err;
+}
+/*
+static int fm_for_read_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+*/
+#endif
+
+static int mt_fm_probe(struct platform_device *pdev)
+{
+ int err = -1;
+ FM_ALERT("mt_fm_probe\n");
+
+ /*
+ printk(KERN_DEBUG "####[%s(): %d]\n", __func__, __LINE__);
+ err = i2c_add_driver(&RDAFM_for_read_driver);
+ if (err)
+ {
+ FM_ALERT("i2c err\n");
+ }
+ */
+
+ // Open I2C driver
+ err = i2c_add_driver(&RDAFM_driver);
+ if (err)
+ {
+ FM_ALERT("i2c err\n");
+ }
+
+ return err;
+}
+
+static int mt_fm_remove(struct platform_device *pdev)
+{
+ FM_ALERT("mt_fm_remove\n");
+ i2c_del_driver(&RDAFM_driver);
+
+ return 0;
+}
+
+
+static struct platform_driver mt_fm_dev_drv =
+{
+ .probe = mt_fm_probe,
+ .remove = mt_fm_remove,
+#if 0//def CONFIG_PM //Not need now
+ .suspend = mt_fm_suspend,
+ .resume = mt_fm_resume,
+#endif
+ .driver = {
+ .name = FM_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+
+
+#if defined(MTK_MT6515)
+static void fm_release(struct device * dev) {}
+
+static struct platform_device mt_fm_device = {
+ .name = FM_NAME,
+ .id = 0,
+ .dev = {
+ .release = fm_release,
+ },
+
+};
+#endif
+
+static void fm_test_tune()
+{
+ struct fm *fm;
+ struct fm_tune_parm parm;
+
+ fm_powerup(fm, &parm);
+}
+
+static struct i2c_client *rda_fm=NULL;
+
+#define RDA_FM_ADDR (0x11)
+#define RDA_FM_I2C_DEVNAME "RDA_FM"
+struct i2c_board_info rda_fm_i2c_board_info = {
+ .type = RDA_FM_I2C_DEVNAME,
+ .flags = 0x00,
+ .addr = RDA_FM_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int rda_i2c_register_device (struct i2c_board_info *ts_i2c_bi,struct i2c_client **l_client)
+{
+ struct i2c_adapter *adapter = NULL;
+
+ adapter = i2c_get_adapter(4);
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ *l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (*l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void rda_i2c_unregister_device(struct i2c_client *l_client)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+
+
+/*
+ * mt_fm_init
+ */
+static int __init mt_fm_init(void)
+{
+ int err = 0;
+ rda_i2c_register_device(&rda_fm_i2c_board_info,&rda_fm);
+
+ FM_ALERT("mt_fm_init\n");
+#if defined(MTK_MT6515)
+ err = platform_device_register(&mt_fm_device);
+ if(err){
+ FM_ALERT("platform_device_register fail\n");
+ return err;
+ }else{
+ FM_ALERT("platform_device_register success\n");
+ }
+#endif
+ err = platform_driver_register(&mt_fm_dev_drv);
+ if (err)
+ {
+ FM_ALERT("platform_driver_register failed\n");
+ }else{
+ FM_ALERT("platform_driver_register success\n");
+ }
+
+ return err;
+}
+
+/*
+ * mt_fm_exit
+ */
+static void __exit mt_fm_exit(void)
+{
+ FM_ALERT("mt_fm_exit\n");
+ platform_driver_unregister(&mt_fm_dev_drv);
+#if defined(MTK_MT6515)
+ platform_device_unregister(&mt_fm_device);
+#endif
+
+ rda_i2c_unregister_device(rda_fm);
+
+}
+
+module_init(mt_fm_init);
+module_exit(mt_fm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek FM Driver");
+MODULE_AUTHOR("William Chung <William.Chung@MediaTek.com>");
+
+