[9610] media: radio: add FM radio code for FM enable
authorYunsu Kim <iamyunsu.kim@samsung.com>
Mon, 11 Jun 2018 04:42:25 +0000 (13:42 +0900)
committerYoungsoo Kim <youngss.kim@samsung.com>
Fri, 15 Jun 2018 23:32:04 +0000 (08:32 +0900)
Change-Id: I304fa251a7a07bdf7436dc6acc6b745d407223a8
Signed-off-by: Yunsu Kim <iamyunsu.kim@samsung.com>
13 files changed:
drivers/media/radio/Kconfig
drivers/media/radio/Makefile
drivers/media/radio/s610/Kconfig [new file with mode: 0644]
drivers/media/radio/s610/Makefile [new file with mode: 0644]
drivers/media/radio/s610/fm_ctrl.c [new file with mode: 0644]
drivers/media/radio/s610/fm_ctrl.h [new file with mode: 0644]
drivers/media/radio/s610/fm_low.c [new file with mode: 0644]
drivers/media/radio/s610/fm_low_ref.h [new file with mode: 0644]
drivers/media/radio/s610/fm_low_struc.h [new file with mode: 0644]
drivers/media/radio/s610/fm_rds.c [new file with mode: 0644]
drivers/media/radio/s610/fm_rds.h [new file with mode: 0644]
drivers/media/radio/s610/radio-s610.c [new file with mode: 0644]
drivers/media/radio/s610/radio-s610.h [new file with mode: 0644]

index 192f36f2f4aa3dd5e2f7eae6fd4ab6e106d51d26..0552b3eaa72c60c6c36cf8ab0c0d2c38f400e07b 100644 (file)
@@ -12,6 +12,12 @@ menuconfig RADIO_ADAPTERS
 
 if RADIO_ADAPTERS && VIDEO_V4L2
 
+config RADIO_S5E9610
+       tristate "SAMSUNG S5E9610 FM Radio"
+       depends on VIDEO_V4L2
+
+source "drivers/media/radio/s610/Kconfig"
+
 config RADIO_TEA575X
        tristate
 
index 37e6e8255b57fe943b12323b34156a7232683fb5..6b60c2ce51b802efb7cf6bc5d7c30eadc4ee0624 100644 (file)
@@ -34,6 +34,7 @@ obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
 obj-$(CONFIG_RADIO_WL128X) += wl128x/
 obj-$(CONFIG_RADIO_TEA575X) += tea575x.o
 obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o
+obj-$(CONFIG_RADIO_S5E9610) += s610/
 
 shark2-objs := radio-shark2.o radio-tea5777.o
 
diff --git a/drivers/media/radio/s610/Kconfig b/drivers/media/radio/s610/Kconfig
new file mode 100644 (file)
index 0000000..fb3d405
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# SAMSUNG's S610 FM driver based on SPEEDY driver.
+#
+menu "Samsung S610 FM driver (SPEEDY based)"
+config RADIO_S610
+       tristate "Samsung S610 FM Radio"
+       depends on VIDEO_V4L2 && RADIO_S5E9610
+       help
+       Choose Y here if you have this FM radio chip.
+
+endmenu
diff --git a/drivers/media/radio/s610/Makefile b/drivers/media/radio/s610/Makefile
new file mode 100644 (file)
index 0000000..36af407
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for SAMSUNG Radio driver based s610
+# FM radio.
+#
+obj-$(CONFIG_RADIO_S610)       += fm_s5e9610.o
+fm_s5e9610-objs                := radio-s610.o \
+                               fm_low.o \
+                               fm_ctrl.o \
+                               fm_rds.o
diff --git a/drivers/media/radio/s610/fm_ctrl.c b/drivers/media/radio/s610/fm_ctrl.c
new file mode 100644 (file)
index 0000000..a451fd0
--- /dev/null
@@ -0,0 +1,385 @@
+/****************************************************************************
+ Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+
+ ******************************************************************************/
+
+#include "fm_low_struc.h"
+#include "radio-s610.h"
+#include "fm_ctrl.h"
+
+extern struct s610_radio *gradio;
+
+void fm_pwron(void)
+{
+       fmspeedy_set_reg_field(0xFFF226, 0, 0x0001, 1); /* FM reset assert */
+       fmspeedy_set_reg(0xFFF212, 0); /*  last power on  */
+       fmspeedy_set_reg(0xFFF211, 0); /*  first power on  */
+       fmspeedy_set_reg_field(0xFFF227, 0, 0x0001, 1); /* FM reset deassert */
+       fmspeedy_set_reg(0xFFF210, 0); /*  FM isolaton disable  */
+}
+
+void fm_pwroff(void)
+{
+       fmspeedy_set_reg_field(0xFFF226, 0, 0x0001, 1); /* FM reset assert */
+       fmspeedy_set_reg(0xFFF210, 1); /*  FM isolaton enable  */
+       fmspeedy_set_reg(0xFFF211, 1); /*  first power off  */
+       fmspeedy_set_reg(0xFFF212, 1); /*  last power off  */
+}
+
+void fmspeedy_wakeup(void)
+{
+       write32(gradio->fmspeedy_base + FMSPDY_CTL, SPDY_WAKEUP);
+       udelay(5);
+}
+
+void fm_en_speedy_m_int(void)
+{
+       SetBits(gradio->fmspeedy_base + FMSPDY_INTR_MASK,
+               FM_SLV_INT_MASK_BIT, 1, 0);
+}
+
+void fm_dis_speedy_m_int(void)
+{
+       SetBits(gradio->fmspeedy_base + FMSPDY_INTR_MASK,
+               FM_SLV_INT_MASK_BIT, 1, 1);
+}
+
+void fm_speedy_m_int_stat_clear(void)
+{
+       write32(gradio->fmspeedy_base + FMSPDY_STAT, 0x1F);
+}
+
+void fm_speedy_m_int_stat_clear_all(void)
+{
+       write32(gradio->fmspeedy_base + FMSPDY_STAT, 0x7F);
+}
+
+void fm_speedy_m_int_enable(void)
+{
+       fm_en_speedy_m_int();
+       fm_speedy_m_int_stat_clear_all();
+}
+
+void fm_speedy_m_int_disable(void)
+{
+       fm_dis_speedy_m_int();
+       fm_speedy_m_int_stat_clear_all();
+}
+
+u32 fmspeedy_get_reg_core(u32 addr)
+{
+       u16 jj = 0;
+       u32 status1;
+       u32 ret = 0;
+
+       fm_dis_speedy_m_int();
+
+       fm_speedy_m_int_stat_clear();
+       write32(gradio->fmspeedy_base + FMSPDY_CMD,
+                       FMSPDY_READ | FMSPDY_RANDOM
+                       | ((addr & 0x1FF) << 7));
+
+       for (jj = 0; jj < 100; jj++) {
+               udelay(2);
+               status1 = read32(gradio->fmspeedy_base + FMSPDY_STAT);
+               if ((status1 & STAT_DONE) == 1)
+                       break;
+       }
+
+       if (jj >= 99) {
+               dev_err(gradio->dev, "%s(), Fail addr:0x%xh\n",
+                       __func__, addr);
+               ret = -1;
+               goto get_fail;
+       }
+
+       ret = read32(gradio->fmspeedy_base + FMSPDY_DATA);
+
+get_fail:
+       fm_en_speedy_m_int();
+
+       return ret;
+
+}
+
+u32 fmspeedy_get_reg(u32 addr)
+{
+       u32 data;
+       
+       API_ENTRY(gradio);
+
+       spin_lock_irq(&gradio->slock);
+
+       atomic_set(&gradio->is_doing, 1);
+       data = fmspeedy_get_reg_core(addr);
+       if (data == -1)
+               gradio->speedy_error++;
+       atomic_set(&gradio->is_doing, 0);
+
+       spin_unlock_irq(&gradio->slock);
+       
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x]", __func__, addr, data);
+       API_EXIT(gradio);
+       return data;
+}
+
+u32 fmspeedy_get_reg_work(u32 addr)
+{
+       u32 data;
+       
+       API_ENTRY(gradio);
+
+       data = fmspeedy_get_reg_core(addr);
+       if (data == -1)
+               gradio->speedy_error++;
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x]", __func__, addr, data);
+       API_EXIT(gradio);
+       return data;
+}
+
+int fmspeedy_set_reg_core(u32 addr, u32 data)
+{
+       u16 jj;
+       u32 status1;
+       int ret = 0;
+
+       fm_dis_speedy_m_int();
+
+       fm_speedy_m_int_stat_clear();
+       write32(gradio->fmspeedy_base + FMSPDY_DATA, data);
+       write32(gradio->fmspeedy_base + FMSPDY_CMD,
+               FMSPDY_WRITE | FMSPDY_RANDOM
+               | ((addr & 0x1FF) << 7));
+
+       for (jj = 0; jj < 100; jj++) {
+               udelay(2);
+               status1 = read32(gradio->fmspeedy_base + FMSPDY_STAT);
+               if ((status1 & STAT_DONE) == 1)
+                       break;
+       }
+
+       if (jj >= 99) {
+               dev_err(gradio->dev, "%s(), Fail addr:0x%xh, data:0x%xh\n",
+                       __func__, addr, data);
+               ret = -1;
+       }
+
+       fm_en_speedy_m_int();
+
+       return ret;
+}
+
+int fmspeedy_set_reg(u32 addr, u32 data)
+{
+       int ret = 0;
+       
+       API_ENTRY(gradio);
+       
+       spin_lock_irq(&gradio->slock);
+
+       atomic_set(&gradio->is_doing, 1);
+       ret = fmspeedy_set_reg_core(addr, data);
+       if (ret == -1)
+               gradio->speedy_error++;
+       atomic_set(&gradio->is_doing, 0);
+
+       spin_unlock_irq(&gradio->slock);
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x], ret[0x%x]", __func__, addr, data, ret);
+       API_EXIT(gradio);
+       return ret;
+}
+
+int fmspeedy_set_reg_work(u32 addr, u32 data)
+{
+       int ret = 0;
+       
+       API_ENTRY(gradio);
+       ret = fmspeedy_set_reg_core(addr, data);
+       if (ret == -1)
+               gradio->speedy_error++;
+       
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x], ret[0x%x]", __func__, addr, data, ret);
+       API_EXIT(gradio);
+       return ret;
+}
+
+u32 fmspeedy_get_reg_field_core(u32 addr, u32 shift, u32 mask)
+{
+       u16 jj;
+       u32 status1;
+       u32 ret = 0;
+
+       fm_dis_speedy_m_int();
+
+       fm_speedy_m_int_stat_clear();
+       write32(gradio->fmspeedy_base + FMSPDY_CMD,
+                       FMSPDY_READ | FMSPDY_RANDOM
+                       | ((addr & 0x1FF) << 7));
+
+       for (jj = 0; jj < 100; jj++) {
+               udelay(2);
+               status1 = read32(gradio->fmspeedy_base + FMSPDY_STAT);
+               if ((status1 & STAT_DONE) == 1)
+                       break;
+       }
+
+       if (jj >= 99) {
+               dev_err(gradio->dev, "%s(), Fail addr:0x%xh\n",
+                       __func__, addr);
+               ret = -1;
+               goto read_fail_f;
+       }
+       ret = (read32(gradio->fmspeedy_base + FMSPDY_DATA) & (mask)) >> shift;
+
+read_fail_f:
+       fm_en_speedy_m_int();
+
+       return ret;
+}
+
+u32 fmspeedy_get_reg_field(u32 addr, u32 shift, u32 mask)
+{
+       u32 data;
+
+       API_ENTRY(gradio);
+       
+       spin_lock_irq(&gradio->slock);
+
+       atomic_set(&gradio->is_doing, 1);
+       data = fmspeedy_get_reg_field_core(addr, shift, mask);
+       if (data == -1)
+               gradio->speedy_error++;
+       atomic_set(&gradio->is_doing, 0);
+
+       spin_unlock_irq(&gradio->slock);
+       
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x]", __func__, addr, data);
+       API_EXIT(gradio);
+       return data;
+}
+
+u32 fmspeedy_get_reg_field_work(u32 addr, u32 shift, u32 mask)
+{
+       u32 data;
+       
+       API_ENTRY(gradio);
+       
+       data = fmspeedy_get_reg_field_core(addr, shift, mask);
+       if (data == -1)
+               gradio->speedy_error++;
+       
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x]", __func__, addr, data);
+       API_EXIT(gradio);
+
+       return data;
+}
+
+int fmspeedy_set_reg_field_core(u32 addr, u32 shift, u32 mask, u32 data)
+{
+       u32 value, value1;
+       u16 jj;
+       u32 status1;
+       int ret = 0;
+
+       fm_dis_speedy_m_int();
+
+       fm_speedy_m_int_stat_clear();
+       write32(gradio->fmspeedy_base + FMSPDY_CMD,
+                       FMSPDY_READ | FMSPDY_RANDOM
+                       | ((addr & 0x1FF) << 7));
+
+       for (jj = 0; jj < 100; jj++) {
+               udelay(2);
+               status1 = read32(gradio->fmspeedy_base + FMSPDY_STAT);
+               if ((status1 & STAT_DONE) == 1)
+                       break;
+       }
+
+       if (jj >= 99) {
+               dev_err(gradio->dev, "%s(), Fail addr:0x%xh, data:0x%xh, cnt:%d\n",
+                       __func__, addr, data, jj);
+               ret = -1;
+               goto set_fail_f;
+       }
+
+       value1 = read32(gradio->fmspeedy_base + FMSPDY_DATA);
+       value = (value1 & ~(mask)) | ((data) << (shift));
+
+       write32(gradio->fmspeedy_base + FMSPDY_DATA, value);
+       write32(gradio->fmspeedy_base + FMSPDY_STAT, 0x1F);
+       write32(gradio->fmspeedy_base + FMSPDY_CMD,
+               FMSPDY_WRITE | FMSPDY_RANDOM
+               | ((addr & 0x1FF) << 7));
+
+       for (jj = 0; jj < 100; jj++) {
+               udelay(2);
+               status1 = read32(gradio->fmspeedy_base + FMSPDY_STAT);
+               if ((status1 & STAT_DONE) == 1)
+                       break;
+       }
+
+       if (jj >= 99) {
+               dev_err(gradio->dev, "%s(), Fail addr:0x%xh, data:0x%xh, cnt:%d\n",
+                       __func__, addr, data, jj);
+               ret = -1;
+       }
+
+set_fail_f:
+       fm_en_speedy_m_int();
+
+       return ret;
+}
+
+int fmspeedy_set_reg_field(u32 addr, u32 shift, u32 mask, u32 data)
+{
+       int ret = 0;
+       
+       API_ENTRY(gradio);
+       spin_lock_irq(&gradio->slock);
+
+       atomic_set(&gradio->is_doing, 1);
+       ret = fmspeedy_set_reg_field_core(addr, shift, mask, data);
+       if (ret == -1)
+               gradio->speedy_error++;
+       atomic_set(&gradio->is_doing, 0);
+
+       spin_unlock_irq(&gradio->slock);
+       
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x], ret[0x%x]", __func__, addr, data, ret);
+       API_EXIT(gradio);
+       return ret;
+}
+
+int fmspeedy_set_reg_field_work(u32 addr, u32 shift, u32 mask, u32 data)
+{
+       int ret = 0;
+       
+       API_ENTRY(gradio);
+       ret = fmspeedy_set_reg_field_core(addr, shift, mask, data);
+       if (ret == -1)
+               gradio->speedy_error++;
+       
+       APIEBUG(gradio, "%s():addr[0x%x], data[0x%x], ret[0x%x]", __func__, addr, data, ret);
+       API_EXIT(gradio);
+
+       return ret;
+}
+
+/****************************************************************************
+ NAME
+ fm_audio_control   -  Audio out enable/disable
+
+ FUNCTION
+ Setting registers for Audio
+ ****************************************************************************/
+void fm_audio_control(struct s610_radio *radio,
+               bool audio_out, bool lr_switch,
+               u32 req_time, u32 audio_addr)
+{
+       write32(radio->fmspeedy_base + AUDIO_CTRL,
+               ((audio_out << 21) | (lr_switch << 20)
+               | ((req_time & 0x07FF) << 9)
+               | (audio_addr & 0x01FF)));
+       udelay(15);
+}
+
diff --git a/drivers/media/radio/s610/fm_ctrl.h b/drivers/media/radio/s610/fm_ctrl.h
new file mode 100644 (file)
index 0000000..5be98c2
--- /dev/null
@@ -0,0 +1,19 @@
+void fm_pwron(void);
+void fm_pwroff(void);
+void fmspeedy_wakeup(void);
+void fm_speedy_m_int_enable(void);
+void fm_speedy_m_int_disable(void);
+void fm_en_speedy_m_int(void);
+void fm_dis_speedy_m_int(void);
+void fm_speedy_m_int_stat_clear(void);
+u32 fmspeedy_get_reg(u32 addr);
+u32 fmspeedy_get_reg_work(u32 addr);
+int fmspeedy_set_reg(u32 addr, u32 data);
+int fmspeedy_set_reg_work(u32 addr, u32 data);
+u32 fmspeedy_get_reg_field(u32 addr, u32 shift, u32 mask);
+u32 fmspeedy_get_reg_field_work(u32 addr, u32 shift, u32 mask);
+int fmspeedy_set_reg_field(u32 addr, u32 shift, u32 mask, u32 data);
+int fmspeedy_set_reg_field_work(u32 addr, u32 shift, u32 mask, u32 data);
+void fm_audio_control(struct s610_radio *radio, bool audio_out, bool lr_switch,
+               u32 req_time, u32 audio_addr);
+
diff --git a/drivers/media/radio/s610/fm_low.c b/drivers/media/radio/s610/fm_low.c
new file mode 100644 (file)
index 0000000..7e5a7bb
--- /dev/null
@@ -0,0 +1,2499 @@
+/****************************************************************************
+ FILE
+ */
+#include "fm_low_struc.h"
+#include "radio-s610.h"
+#include "fm_low_ref.h"
+
+extern struct s610_radio *gradio;
+
+/* Numeric identifier embedded in the code. */
+const u32 build_identifier_integer = 0x3ac6bb6b;
+void (*handler_if_count)(struct s610_radio *radio) = NULL;
+void (*handler_audio_pause)(struct s610_radio *radio) = NULL;
+extern u32 *vol_level_init;
+extern u32 *fm_spur_trf_init;
+extern u32 *fm_dual_clk_init;
+
+/****************************************************************************
+
+ Functions for initialization
+
+ ****************************************************************************/
+
+int fm_boot(struct s610_radio *radio)
+{
+       bool aux_ret;
+
+       fm_audio_control(radio, 0, 0, 0, 0);
+
+       aux_ret = fm_aux_pll_initialize();
+       if (!aux_ret)
+               return -1;
+
+       /* power on for FM digital block */
+       fm_pwron();
+
+       fm_lo_initialize(radio);
+
+       fm_initialize(radio);
+
+       return 0;
+}
+
+void fm_power_off(void)
+{
+       fm_iclkaux_set(0); /* restore CLKMUX */
+
+       fm_lo_off();
+
+       /* power off for FM digital block */
+       fm_pwroff();
+
+       fm_aux_pll_off();
+}
+
+void fm_iclkaux_set(u32 data)
+{
+       fmspeedy_set_reg_field(0xFFF220, 0, (0x0001<<0), data); /* iCLKAux */
+       dev_info(gradio->dev, "%s: iClk Aux: 0x%xh get val: 0x%xh", __func__,
+               data,
+               fmspeedy_get_reg(0xFFF220));
+}
+
+void fm_initialize(struct s610_radio *radio)
+{
+       API_ENTRY(radio);
+
+       /* Initialize the analogue block */
+       fm_rx_init();
+
+       fm_iclkaux_set(radio->iclkaux);
+
+       /* Set the demod reg. */
+       if (radio->rfchip_ver == S620_REV_0) {
+               radio->low->fm_config.demod_conf_ini |= 0x2C000;
+               dev_info(radio->dev, "%s():demod_conf_ini[%08X]",
+                       __func__,
+                       radio->low->fm_config.demod_conf_ini);
+       }
+       fmspeedy_set_reg(0xFFF2A9, radio->low->fm_config.demod_conf_ini);
+       if (radio->vol_3db_att)
+               fmspeedy_set_reg_field(0xFFF2A9, 10, (0x01 << 10), 1);
+       else
+               fmspeedy_set_reg_field(0xFFF2A9, 10, (0x01 << 10), 0);
+       fmspeedy_set_reg(0xFFF2B9, radio->low->fm_config.narrow_thres_ini);
+       fmspeedy_set_reg(0xFFF2C6, radio->low->fm_config.snr_adj_ini);
+       fmspeedy_set_reg(0xFFF2CE, radio->low->fm_config.stereo_thres_ini);
+
+       fmspeedy_set_reg(0xFFF2C8, radio->low->fm_config.snr_smooth_conf_ini);
+       fmspeedy_set_reg(0xFFF2C9,
+               radio->low->fm_config.soft_muffle_conf_ini.muffle_coeffs);
+
+       fmspeedy_set_reg_field(0xFFF2AA, 3, (0x0007 << 3),
+               radio->low->fm_config.soft_mute_atten_max_ini);
+       fmspeedy_set_reg_field(0xFFF2AA, 0, 0x0007,
+               radio->low->fm_config.soft_muffle_conf_ini.lpf_bw);
+       fmspeedy_set_reg_field(0xFFF2AA, 6, (0x0001 << 6),
+               radio->low->fm_config.soft_muffle_conf_ini.lpf_en);
+       fmspeedy_set_reg_field(0xFFF2AA, 7, (0x0001 << 7),
+               radio->low->fm_config.soft_muffle_conf_ini.lpf_auto);
+       fmspeedy_set_reg_field(0xFFF2AA, 8, (0x0001 << 8), 1);
+
+       if (!radio->without_elna)
+               radio->rssi_adjust = RSSI_ADJUST_WITHOUT_ELNA_VALUE;
+
+       fmspeedy_set_reg(0xFFF2C2, radio->low->fm_config.rssi_adj_ini+radio->rssi_adjust);
+       APIEBUG(radio, "%s(): 0xFFF2C2:0x%x %d %d", __func__,
+               fmspeedy_get_reg(0xFFF2C2), radio->low->fm_config.rssi_adj_ini, radio->rssi_adjust);
+
+       fmspeedy_set_reg(0xFFF299, 0xFF64);
+
+#ifdef USE_IQ_IMBAL_SMOOTH
+       fmspeedy_set_reg(0xFFF2B6, 0x081C);
+#endif /*USE_IQ_IMBAL_SMOOTH*/
+
+#ifdef USE_SPUR_CANCEL
+       if (radio->tc_on)
+               fmspeedy_set_reg(0xFFF2D3, 0x18);
+#endif
+
+       /* Enable the volume control */
+       fmspeedy_set_reg_field(0xFFF251, 11, (0x0001 << 11), 1);
+
+       fm_set_band(radio, 0); /*FM band(87.5 ~ 108 MHz)*/
+       fm_set_freq_step(radio, 1); /*freq_step(100 KHz)*/
+
+       fm_set_blend_mute(radio);
+       fm_set_mute(TRUE);
+#ifdef USE_RINGBUFF_API
+       /* Create the RDS buffer. */
+       if (radio->rds_parser_enable)
+               radio->low->rds_buffer_mem = kzalloc(FM_RDS_MEM_SIZE_PARSER, GFP_KERNEL);
+       else
+               radio->low->rds_buffer_mem = kzalloc(FM_RDS_MEM_SIZE, GFP_KERNEL);
+
+       /* ringbuf init */
+       if (radio->rds_parser_enable)
+               radio->rds_rb.size = FM_RDS_MEM_SIZE_PARSER;
+       else
+               radio->rds_rb.size = FM_RDS_MEM_SIZE;
+
+       radio->rds_rb.buf = radio->low->rds_buffer_mem;
+       radio->rds_rb.head = radio->rds_rb.tail= radio->rds_rb.buf;
+#else  /* USE_RINGBUFF_API */
+       /* Create the RDS buffer. */
+       radio->low->rds_buffer = (rds_buf_conf *) kzalloc(sizeof(rds_buf_conf),
+                       GFP_KERNEL);
+       radio->low->rds_buffer_mem = kzalloc(FM_RDS_MEM_SIZE, GFP_KERNEL);
+
+       radio->low->rds_buffer->base = radio->low->rds_buffer_mem;
+       radio->low->rds_buffer->index = radio->low->rds_buffer->outdex = 0;
+       radio->low->rds_buffer->size = FM_RDS_MEM_SIZE;
+#endif /* USE_RINGBUFF_API */
+
+       fm_rds_flush_buffers(radio, FALSE);
+
+       API_EXIT(radio);
+}
+
+/****************************************************************************
+
+ Functions for conversion
+
+ ****************************************************************************/
+
+u16 if_count_device_to_host(struct s610_radio *radio, u16 val)
+{
+       bool negative = !!(val & 0x8000);
+       u32 resp;
+
+       if (negative)
+               val = -val;
+
+       resp = ((u32) val) / 128;
+
+       if (resp > 0x7FFF)
+               resp = 0x7FFF;
+
+       return negative ? (u16) -resp : (u16) resp;
+}
+
+#define AGGR_RSSI_OFFSET (-114)
+
+static u16 aggr_rssi_host_to_device(u8 val)
+{
+       s8 val_t = (val > 127) ? ((s16)(val & 0x00FF) - 256) : (s16) val;
+       u16 resp;
+
+       if (val_t >= AGGR_RSSI_OFFSET)
+               resp = ((u16) val_t - AGGR_RSSI_OFFSET) * 4;
+       else
+               resp = 0;
+
+       return resp;
+}
+
+u8 aggr_rssi_device_to_host(u16 val)
+{
+       s8 resp;
+
+       resp = (val / 4) + AGGR_RSSI_OFFSET;
+       return ((u8) resp) & 0x00FF;
+}
+
+u16 rssi_device_to_host(u16 digi_rssi, u16 agc_gain, u16 rssi_adj)
+{
+       u16 aggr_rssi;
+       u16 digi_rssi_t = (digi_rssi & 0x1FF);
+       u16 digi_gain = (agc_gain & 0xF000) >> 12;
+       u16 ana_gain = (agc_gain & 0x0F80) >> 7;
+
+       aggr_rssi = digi_rssi_t - (12 * digi_gain) - (8 * ana_gain) - rssi_adj
+                       + 160 + 84;
+
+       return aggr_rssi_device_to_host(aggr_rssi);
+}
+
+/****************************************************************************
+
+ Functions for the interaction with a device
+
+ ****************************************************************************/
+void fm_set_audio_gain(struct s610_radio *radio, u16 gain)
+{
+       if (gain >= radio->vol_num)
+               gain = radio->vol_num - 1;
+
+       if (gain < 0)
+               gain = 0;
+
+       fmspeedy_set_reg_field(0xFFF251, 0, (0x07FF << 0),
+               radio->vol_level_mod[gain]);
+}
+
+static bool is_freq_in_spur(int freq, u32 *freq_array, int max_freq) {
+       int i;
+
+       for (i=0; i < max_freq; i++) {
+               if (freq_array[i] == freq)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+#define AGC_CONFIG_WBRSSI_DISABLE      0x9D1
+#define AGC_CONFIG_WBRSSI_ENABLE       0x95F
+
+void enable_agc_config_wbrssi(struct s610_radio *radio, bool onoff)
+{
+       if (onoff) {
+               /* AGC config WBRSSI_LO/HI enable) */
+               fmspeedy_set_reg(0xFFF280, AGC_CONFIG_WBRSSI_ENABLE);
+               fmspeedy_set_reg(0xFFF29A, 0x0804);
+               radio->agc_enable = AGC_CONFIG_WBRSSI_ENABLE;
+       } else {
+               /* AGC config WBRSSI_LO/HI disable */
+               fmspeedy_set_reg(0xFFF280, AGC_CONFIG_WBRSSI_DISABLE);
+               fmspeedy_set_reg(0xFFF29A, 0x0906);
+               radio->agc_enable = AGC_CONFIG_WBRSSI_DISABLE;
+       }
+
+       dev_info(radio->dev, "%s(%d):280:%08X, 29A:%08X ", __func__,
+               onoff,
+               fmspeedy_get_reg(0xFFF280), fmspeedy_get_reg(0xFFF29A));
+}
+
+void reset_agc_gain(void)
+{
+       /* FM AGC gain reset */
+       fmspeedy_set_reg(0xFFF286, 0x33C);
+       mdelay(5);
+       fmspeedy_set_reg(0xFFF286, 0x13C);
+}
+
+void fm_set_freq(struct s610_radio *radio, u32 freq, bool mix_hi)
+{
+       int ii;
+       u32 fifo_tmp;
+
+       API_ENTRY(radio);
+       APIEBUG(radio, "set freq: %d", radio->low->fm_state.freq);
+
+       radio->low->fm_tune_info.rx_setup.fm_freq_khz = freq;
+       radio->low->fm_tune_info.rx_setup.fm_freq_hz = freq * 1000;
+
+       if (mix_hi) {
+               radio->low->fm_tune_info.lo_setup.rx_lo_req_freq =
+                               radio->low->fm_tune_info.rx_setup.fm_freq_hz + 224609;
+               radio->low->fm_tune_info.rx_setup.demod_if = 0xF8D;
+       } else {
+               radio->low->fm_tune_info.lo_setup.rx_lo_req_freq =
+                               radio->low->fm_tune_info.rx_setup.fm_freq_hz - 224609;
+               radio->low->fm_tune_info.rx_setup.demod_if = 0x73;
+       }
+
+       if (radio->dual_clk_on && (radio->rfchip_ver == S620_REV_0)) {
+               if (is_freq_in_spur(radio->low->fm_state.freq, fm_dual_clk_init, radio->dual_clk_on)) {
+                       if (mix_hi)
+                               radio->low->fm_tune_info.rx_setup.demod_if = 0xF85;
+                       else
+                               radio->low->fm_tune_info.rx_setup.demod_if = 0x7B;
+               }
+       }
+
+       fm_lo_prepare_setup(radio);
+
+       if (freq <= 70000)
+               radio->low->fm_tune_info.rx_setup.lna_cdac = 0x28;
+       else if (freq < 80000)
+               radio->low->fm_tune_info.rx_setup.lna_cdac = 0x16;
+       else if (freq < 90000)
+               radio->low->fm_tune_info.rx_setup.lna_cdac = 0x12;
+       else if (freq < 100000)
+               radio->low->fm_tune_info.rx_setup.lna_cdac = 0x0A;
+       else
+               radio->low->fm_tune_info.rx_setup.lna_cdac = 0x05;
+
+       fmspeedy_set_reg_field(0xFFF2A9, 7, (0x0001<<7), 1);
+       fmspeedy_set_reg_field(0xFFF2A9, 6, (0x0001<<6), 0);
+       fmspeedy_set_reg_field(0xFFF2A9, 4, (0x0001<<4), 0);
+
+       if (!radio->without_elna)
+               radio->rssi_adjust = RSSI_ADJUST_WITHOUT_ELNA_VALUE;
+
+       fmspeedy_set_reg(0xFFF2C2, radio->low->fm_config.rssi_adj_ini+radio->rssi_adjust);
+       APIEBUG(radio, "%s(): 0xFFF2C2: 0x%x %d %d", __func__,
+                       fmspeedy_get_reg(0xFFF2C2), radio->low->fm_config.rssi_adj_ini, radio->rssi_adjust);
+#ifdef IDLE_POLLING_ENABLE
+       fm_idle_periodic_cancel((unsigned long) radio);
+#endif /*IDLE_POLLING_ENABLE*/
+
+       if (radio->trf_on && (radio->rfchip_ver == S620_REV_0)) {
+               if (is_freq_in_spur(radio->low->fm_state.freq, fm_spur_trf_init, radio->trf_on)) {
+                       fmspeedy_set_reg_field(0xFFF2A9, 7, (0x0001<<7), 0);
+                       fmspeedy_set_reg_field(0xFFF2A9, 6, (0x0001<<6), 1);
+                       fmspeedy_set_reg_field(0xFFF2A9, 4, (0x0001<<4), 1);
+                       if (radio->seek_status == FM_TUNER_PRESET_MODE)
+                               udelay(100);
+#ifdef IDLE_POLLING_ENABLE
+                       if (radio->low->fm_state.freq == 104000)
+                               fm_idle_periodic_update((unsigned long) radio);
+#endif /*IDLE_POLLING_ENABLE*/
+                       dev_info(radio->dev, "TRF ON [%06d][%08X]",
+                                       radio->low->fm_state.freq, fmspeedy_get_reg(0xFFF2A9));
+               }
+       }
+
+#ifdef USE_SPUR_CANCEL
+       if (radio->tc_on)
+               fm_rx_check_spur(radio);
+#endif
+
+#ifdef USE_IQ_IMBAL_SMOOTH
+       /* Clear the smooth config lock for IQ imbalance */
+       fmspeedy_set_reg_field(0xFFF2B6, 10, (0x0001 << 10), 0);
+#endif /*USE_IQ_IMBAL_SMOOTH*/
+
+       /* Set up CDAC */
+       fmspeedy_set_reg_field(0xFFF264, 22, (0x003F << 22),
+                       radio->low->fm_tune_info.rx_setup.lna_cdac);
+
+       if (freq == 104000) {
+               fmspeedy_set_reg(0xFFF244, 0x0A20D0);
+               fmspeedy_set_reg_field(0xFFF265, 17, (0x0001<<17), 1); /* AUX_SEL_RX_ADC_CLK_30M  */
+               fmspeedy_set_reg_field(0xFFF241, 9, (0x0001<<9), 0); /* LO_CLKREF_ADC_SEL_CLKREF */
+               fmspeedy_set_reg_field(0xFFF258, 27, (0x0001<<27), 0); /* XTAL_EN_FM_BUF off*/
+       } else {
+               fmspeedy_set_reg(0xFFF244, 0x24A0D0);
+               fmspeedy_set_reg_field(0xFFF265, 17, (0x0001<<17), 0); /* AUX_SEL_RX_ADC_CLK_30M  */
+               fmspeedy_set_reg_field(0xFFF241, 9, (0x0001<<9), 1); /* LO_CLKREF_ADC_SEL_CLKREF */
+               fmspeedy_set_reg_field(0xFFF258, 27, (0x0001<<27), 1); /* XTAL_EN_FM_BUF on */
+       }
+
+       /* Set up LO */
+       fm_lo_set(radio->low->fm_tune_info.lo_setup);
+
+       if (!radio->dual_clk_on) {
+               if ((freq == 99900) || (freq == 100000) || (freq == 100100)) {
+                       fmspeedy_set_reg_field(0xFFF255, 21, (0x0001<<21), 1); /* FMCLK_32M */
+                       fmspeedy_set_reg_field(0xFFF2A8, 5, (0x0001 << 5), 1);
+               } else {
+                       fmspeedy_set_reg_field(0xFFF255, 21, (0x0001<<21), 0); /* FMCLK_40M */
+                       fmspeedy_set_reg_field(0xFFF2A8, 5, (0x0001 << 5), 0);
+               }
+       }
+
+#if 0
+       /* Initialise I/Q imbalance */
+       fm_setup_iq_imbalance();
+#endif
+
+       fmspeedy_set_reg_field(0xFFF255, 21, (0x0001<<21), 0); /* FMCLK_40_32M, default = 1 */
+       /* Set up Demod IF */
+       fmspeedy_set_reg_field(0xFFF2A8, 7, (0x0001<<7), 0);
+       fmspeedy_set_reg_field(0xFFF2A8, 8, (0x0001<<8), 0);
+       if (radio->seek_status == FM_TUNER_PRESET_MODE)
+               udelay(100);
+       fmspeedy_set_reg_field(0xFFF222, 0, (0x000F), 0x02);
+       fmspeedy_set_reg_field(0xFFF222, 4, (0x000F<<4), 0x09);
+       if (radio->seek_status == FM_TUNER_PRESET_MODE)
+               udelay(100);
+
+       if (radio->dual_clk_on && (radio->rfchip_ver == S620_REV_0)) {
+               if (is_freq_in_spur(radio->low->fm_state.freq, fm_dual_clk_init, radio->dual_clk_on)) {
+                       fmspeedy_set_reg_field(0xFFF255, 21, (0x0001<<21), 1); /* FMCLK_40_32M, default = 1 */
+                       /* Set up Demod IF */
+                       fmspeedy_set_reg_field(0xFFF2A8, 7, (0x0001<<7), 1);
+                       fmspeedy_set_reg_field(0xFFF2A8, 8, (0x0001<<8), 1);
+                       if (radio->seek_status == FM_TUNER_PRESET_MODE)
+                               udelay(100);
+                       fmspeedy_set_reg_field(0xFFF222, 0, (0x000F), 0x03);
+                       fmspeedy_set_reg_field(0xFFF222, 4, (0x000F<<4), 0x07);
+                       if (radio->seek_status == FM_TUNER_PRESET_MODE)
+                               udelay(100);
+                       dev_info(radio->dev, "7.5MHz Dual Clock ON [%06d]", radio->low->fm_state.freq);
+               }
+       }
+
+       fmspeedy_set_reg(0xFFF2AF, radio->low->fm_tune_info.rx_setup.demod_if);
+       fmspeedy_set_reg(0xFFF2AE, 0x924); /* wide w te */
+
+       if (radio->rssi_est_on) {
+               fm_update_rssi(radio);
+               if (radio->low->fm_state.rssi >= 176)
+                       fmspeedy_set_reg_field(0xFFF2A9, 4, (0x0001 << 4), 1);
+       } else
+               fmspeedy_set_reg_field(0xFFF2A9, 4, (0x0001 << 4), 0);
+
+       if (radio->sw_mute_weak) {
+               radio->low->fm_config.mute_coeffs_soft = 0x1B16;
+               fmspeedy_set_reg(0xFFF2CA, radio->low->fm_config.mute_coeffs_soft);
+       }
+
+       /* change blending ref to RSSI */
+       if (radio->low->fm_state.freq != 104000) {
+               if(radio->rssi_ref_enable)
+                       fmspeedy_set_reg_field(0xFFF2A9, 4, (0x0001 << 4), 1);
+               else
+                       fmspeedy_set_reg_field(0xFFF2A9, 4, (0x0001 << 4), 0);
+       }
+
+       /* FM ADC reset */
+       fmspeedy_set_reg_field(0xFFF304, 5, (0x0001<<5), 0);
+       fmspeedy_set_reg_field(0xFFF304, 5, (0x0001<<5), 1);
+
+       FDEBUG(radio, "%s():seek_status:%d %d", __func__,
+               radio->seek_status, radio->low->fm_state.tuner_mode);
+
+       /* FM AGC config control */
+       if (radio->seek_status == FM_TUNER_PRESET_MODE) {
+               enable_agc_config_wbrssi(radio, TRUE);
+               reset_agc_gain();
+       } else {
+               if (radio->agc_enable != AGC_CONFIG_WBRSSI_DISABLE)
+                       enable_agc_config_wbrssi(radio, FALSE);
+       }
+
+       if (radio->rds_parser_enable) {
+               /* RDS parser reset */
+               fm_rds_parser_reset(&(radio->pi));
+
+               /* FIFO clear */
+               for (ii = 0; ii < 32; ii++)
+                       fifo_tmp = fmspeedy_get_reg_work(0xFFF3C0);
+
+#ifdef USE_RINGBUFF_API
+               radio->rds_rb.head = radio->rds_rb.tail= radio->rds_rb.buf;
+#else  /* USE_RINGBUFF_API */
+               radio->low->rds_buffer->index = radio->low->rds_buffer->outdex = 0;
+#endif /* USE_RINGBUFF_API */
+       }
+
+       API_EXIT(radio);
+}
+
+void fm_set_mute(bool mute)
+{
+       if (mute)
+               fmspeedy_set_reg_field(0xFFF2A9, 0, 0x0001, 1); /* mute*/
+       else
+               fmspeedy_set_reg_field(0xFFF2A9, 0, 0x0001, 0); /*unmute*/
+}
+
+void fm_set_blend_mute(struct s610_radio *radio)
+{
+       u16 mute_coeffs, blend_coeffs;
+
+#ifdef MONO_SWITCH_INTERF
+       if ((radio->low->fm_state.force_mono)
+                       || (radio->low->fm_state.force_mono_interf)) {
+#else
+       if (radio->low->fm_state.force_mono) {
+#endif
+               blend_coeffs = radio->low->fm_config.blend_coeffs_dis;
+       } else if (radio->low->fm_state.use_switched_blend) {
+               /* Switched blend mode */
+               blend_coeffs = radio->low->fm_config.blend_coeffs_switch;
+       } else {
+               /* Soft blend mode */
+               blend_coeffs = radio->low->fm_config.blend_coeffs_soft;
+       }
+
+       if (radio->low->fm_state.use_soft_mute) {
+               /* Soft mute */
+               mute_coeffs = radio->low->fm_config.mute_coeffs_soft;
+       } else {
+               mute_coeffs = radio->low->fm_config.mute_coeffs_dis;
+       }
+
+       fmspeedy_set_reg(0xFFF2CC, blend_coeffs);
+       fmspeedy_set_reg(0xFFF2CA, mute_coeffs);
+}
+
+static void fm_rds_flush_buffers(struct s610_radio *radio, bool clear_buffer)
+{
+       bool clear_sync = FALSE;
+
+#ifdef USE_RINGBUFF_API
+               /* Clear the buffer pointers. */
+               radio->rds_rb.head = radio->rds_rb.tail= radio->rds_rb.buf;
+#else  /* USE_RINGBUFF_API */
+       if (radio->low->rds_buffer != 0)
+               /* Clear the buffer pointers. */
+               radio->low->rds_buffer->index =
+                       radio->low->rds_buffer->outdex = 0;
+#endif /* USE_RINGBUFF_API */
+
+       if (clear_buffer) {
+               /* Diable RDS block */
+               fmspeedy_set_reg_field(0xFFF304, 1, (0x0001 << 1), 0);
+#ifndef RDS_POLLING_ENABLE
+               /* Disable RDS int. */
+               fm_set_interrupt_source((0x0001 << 4), FALSE);
+#endif /* RDS_POLLING_ENABLE */
+               /* Initialize the RDS state */
+               radio->low->fm_rds_state.current_state = RDS_STATE_INIT;
+               /* Clear the Sync flag after updating the status */
+               clear_sync = TRUE;
+                /* Enable RDS block */
+               fmspeedy_set_reg_field(0xFFF304, 1, (0x0001 << 1), 1);
+#ifndef RDS_POLLING_ENABLE
+                /* Enable RDS int. */
+               fm_set_interrupt_source((0x0001 << 4), TRUE);
+#endif /* RDS_POLLING_ENABLE */
+       }
+
+       fm_clear_flag_bits(radio, FLAG_BUF_FUL);
+       radio->low->fm_state.status &= ~STATUS_MASK_RDS_AVA;
+
+       if (clear_sync)
+               fm_update_rds_sync_status(radio, FALSE);
+}
+
+bool fm_radio_on(struct s610_radio *radio)
+{
+       u32 fm_en;
+
+       API_ENTRY(radio);
+
+       /* Start up analogue block */
+       fm_rx_ana_start();
+
+       /* Enable FM, DEMOD and ADC. */
+       fm_en = fmspeedy_get_reg(0xFFF304);
+       fmspeedy_set_reg(0xFFF304, (fm_en & 0x1DA));
+       fmspeedy_set_reg(0xFFF304, (fm_en | 0x25));
+
+       /* Clear int source */
+       fmspeedy_set_reg(0xFFF302, 0xFFFF);
+
+       radio->low->fm_state.last_status_blend_stereo = FALSE;
+       radio->low->fm_state.last_status_rds_sync = FALSE;
+
+       API_EXIT(radio);
+       /* Indicate success */
+       return TRUE;
+}
+
+void fm_radio_off(struct s610_radio *radio)
+{
+       /* Disable all interrupt. */
+       fm_set_interrupt_source(0xFFFF, FALSE);
+
+       /* disable AudioOutEn */
+       fm_audio_control(radio, 0, 0, 0, 0);
+
+       /* Turn off FM digital block */
+       fmspeedy_set_reg(0xFFF304, 0);
+
+       /* Turn off analogue block */
+       fm_rx_ana_stop();
+}
+
+void fm_rds_on(struct s610_radio *radio)
+{
+       memset(&radio->low->fm_rds_state, 0, sizeof(radio->low->fm_rds_state));
+
+       /* Set the interrupt rate for RDS */
+       fmspeedy_set_reg(0xFFF2BF, radio->low->fm_config.rds_int_byte_count);
+}
+
+void fm_rds_off(struct s610_radio *radio)
+{
+       radio->low->fm_state.status &= ~STATUS_MASK_RDS_AVA;
+}
+
+void fm_rds_enable(struct s610_radio *radio)
+{
+#ifndef        USE_RDS_HW_DECODER
+       u32 val = fmspeedy_get_reg(0xFFF2D7);
+       u32 mask = ~0x2800;
+
+       val &= mask;
+       fmspeedy_set_reg(0xFFF2D7, val | 0x800);
+#endif /*USE_RDS_HW_DECODER*/
+       fm_rds_flush_buffers(radio, TRUE);
+}
+
+void fm_rds_disable(struct s610_radio *radio)
+{
+        /* Diable RDS block */
+       fmspeedy_set_reg_field(0xFFF304, 1, (0x0001 << 1), 0);
+        /* Disable RDS int. */
+       fm_set_interrupt_source((0x0001 << 4), FALSE);
+        /* Clear RDS sync. */
+       fm_update_rds_sync_status(radio, FALSE);
+}
+
+/****************************************************************************
+
+ Functions for the information management
+
+ ****************************************************************************/
+
+u16 fm_get_flags(struct s610_radio *radio)
+{
+       u16 resp = radio->low->fm_state.flags;
+
+       fm_set_flags(radio, 0);
+
+       return resp;
+}
+
+void fm_set_flags(struct s610_radio *radio, u16 flags)
+{
+       radio->low->fm_state.flags = flags;
+}
+
+void fm_update_if_count(struct s610_radio *radio)
+{
+       radio->low->fm_state.last_ifc = if_count_device_to_host(radio,
+                       fmspeedy_get_reg(0xFFF2B0));
+}
+
+void fm_update_if_count_int(struct s610_radio *radio)
+{
+       radio->low->fm_state.last_ifc = if_count_device_to_host(radio,
+                       fmspeedy_get_reg(0xFFF2B0));
+}
+
+void fm_update_rssi(struct s610_radio *radio)
+{
+       radio->low->fm_state.rssi =
+               rssi_device_to_host(fmspeedy_get_reg(0xFFF2AD),
+               fmspeedy_get_reg(0xFFF285), fmspeedy_get_reg(0xFFF2C2));
+}
+
+void fm_update_rssi_work(struct s610_radio *radio)
+{
+       radio->low->fm_state.rssi =
+               rssi_device_to_host(fmspeedy_get_reg_work(0xFFF2AD),
+               fmspeedy_get_reg_work(0xFFF285), fmspeedy_get_reg_work(0xFFF2C2));
+}
+
+void fm_update_snr(struct s610_radio *radio)
+{
+       radio->low->fm_state.snr = fmspeedy_get_reg(0xFFF2C5);
+}
+
+void fm_update_sig_info(struct s610_radio *radio)
+{
+       fm_update_rssi(radio);
+       fm_update_snr(radio);
+}
+
+void fm_update_rds_sync_status(struct s610_radio *radio, bool synced)
+{
+       if (radio->low->fm_state.last_status_rds_sync != synced) {
+               if (synced != TRUE)
+                       fm_set_flag_bits(radio, FLAG_SYN_LOS);
+
+               radio->low->fm_state.last_status_rds_sync = synced;
+       }
+}
+
+#ifndef        USE_RDS_HW_DECODER
+bool fm_get_rds_sync_status(struct s610_radio *radio)
+{
+       return radio->low->fm_state.last_status_rds_sync;
+}
+#endif /*USE_RDS_HW_DECODER*/
+
+u16 fm_update_rx_status(struct s610_radio *radio, u16 d_status)
+{
+       u16 flags = 0;
+       u8 status = radio->low->fm_state.status & ~STATUS_MASK_STEREO;
+       bool blend_stereo = !!(d_status & FM_DEMOD_BLEND_STEREO_MASK);
+
+       if (blend_stereo
+               != radio->low->fm_state.last_status_blend_stereo) {
+               radio->low->fm_state.last_status_blend_stereo = blend_stereo;
+               flags |= FLAG_CH_STAT;
+       }
+       if (blend_stereo)
+               status |= STATUS_MASK_STEREO;
+
+       radio->low->fm_state.status = status;
+
+       return flags;
+}
+
+void fm_update_tuner_mode(struct s610_radio *radio)
+{
+       u8 tuner_mode = radio->low->fm_state.tuner_mode
+                       & ~TUNER_MODE_MASK_TUN_MOD;
+       u32 tuner_state = (u32) radio->low->fm_tuner_state.tuner_state;
+
+       switch (tuner_state) {
+       case TUNER_OFF:
+               tuner_mode |= TUNER_MODE_NONE;
+               break;
+       case TUNER_NOTTUNED:
+               tuner_mode |= TUNER_MODE_NONE;
+               break;
+       case TUNER_IDLE:
+               tuner_mode |= TUNER_MODE_NONE;
+               break;
+       case TUNER_PRESET:
+               tuner_mode |= TUNER_MODE_PRESET;
+               break;
+       case TUNER_SEARCH:
+               tuner_mode |= TUNER_MODE_SEARCH;
+               break;
+       default:
+               break;
+       }
+
+       radio->low->fm_state.tuner_mode = tuner_mode;
+}
+
+bool fm_check_rssi_level(u16 limit)
+{
+       u16 d_rssi, gain, adjust;
+       s16 rssi, thres;
+
+       d_rssi = fmspeedy_get_reg(0xFFF2AD);
+       gain = fmspeedy_get_reg(0xFFF285);
+       adjust = fmspeedy_get_reg(0xFFF2C2);
+
+       rssi = rssi_device_to_host(d_rssi, gain, adjust);
+       thres = aggr_rssi_device_to_host(limit);
+
+       rssi = (rssi & 0x80) ? rssi - 256 : rssi;
+       thres = (thres & 0x80) ? thres - 256 : thres;
+
+       return (rssi < thres);
+}
+
+/*******************************************************************/
+int low_get_search_lvl(struct s610_radio *radio, u16 *value)
+{
+       *value =
+               aggr_rssi_device_to_host(
+                       radio->low->fm_state.rssi_limit_search);
+
+       return 0;
+}
+/*******************************************************************/
+/* set function */
+int low_set_if_limit(struct s610_radio *radio, u16 value)
+{
+       fmspeedy_set_reg(0xFFF2B3, (u8) value);
+
+       return 0;
+}
+
+int low_set_search_lvl(struct s610_radio *radio, u16 value)
+{
+       radio->low->fm_state.rssi_limit_search =
+                       aggr_rssi_host_to_device(value);
+       fm_set_rssi_thresh(radio, radio->low->fm_tuner_state.tuner_state);
+
+       return 0;
+}
+
+int low_set_freq(struct s610_radio *radio, u32 value)
+{
+       u32 freq = value;
+
+       (void) fm_band_trim(radio, &freq);
+       radio->low->fm_state.freq = freq;
+
+       return 0;
+}
+
+int low_set_tuner_mode(struct s610_radio *radio, u16 value)
+{
+       API_ENTRY(radio);
+
+       radio->low->fm_state.tuner_mode = value;
+       radio->low->fm_tuner_state.curr_search_down =
+                       radio->low->fm_state.search_down;
+
+       fm_set_tuner_mode(radio);
+
+       radio->seek_status = value;
+       FDEBUG(radio, "%s(), seek_status:%d %d", __func__, radio->seek_status, value);
+
+       /* FM AGC config control */
+       if (radio->seek_status == FM_TUNER_PRESET_MODE) {
+               enable_agc_config_wbrssi(radio, TRUE);
+               reset_agc_gain();
+       }
+
+       if (value == FM_TUNER_STOP_SEARCH_MODE) {
+               /* Seek_cacel complete */
+               complete(&radio->flags_seek_fr_comp);
+               dev_info(radio->dev, ">>> send seek cancel complete");
+       }
+
+       API_EXIT(radio);
+
+       return 0;
+}
+
+void fm_tuner_set_force_mute(struct s610_radio *radio, bool mute)
+{
+       radio->low->fm_state.mute_forced = mute;
+       radio->low->fm_state.mute_audio = mute;
+       fm_tuner_control_mute(radio);
+}
+
+int low_set_mute_state(struct s610_radio *radio, u16 value)
+{
+       /* Default set only fm stat initialie */
+       /*radio->low->fm_state.use_soft_mute = !!(value & MUTE_STATE_MASK_SOFT);*/
+
+       fm_tuner_set_force_mute(radio, !!(value & MUTE_STATE_MASK_HARD));
+       fm_set_blend_mute(radio);
+
+       return 0;
+}
+
+int low_set_most_mode(struct s610_radio *radio, u16 value)
+{
+       radio->low->fm_state.force_mono = !(value & MODE_MASK_MONO_STEREO);
+       fm_set_blend_mute(radio);
+
+       return 0;
+}
+
+int low_set_most_blend(struct s610_radio *radio, u16 value)
+{
+/*     radio->low->fm_state.use_switched_blend = !!(value & MODE_MASK_BLEND);*/
+       fm_set_blend_mute(radio);
+
+       return 0;
+}
+
+int low_set_pause_lvl(struct s610_radio *radio, u16 value)
+{
+       fmspeedy_set_reg(0xFFF2A4, (u8)(value & 0x00FF));
+
+       return 0;
+}
+
+int low_set_pause_dur(struct s610_radio *radio, u16 value)
+{
+       fmspeedy_set_reg(0xFFF2A2, (u8)(value & 0x3F));
+
+       return 0;
+}
+
+int low_set_demph_mode(struct s610_radio *radio, u16 value)
+{
+       if (value & MODE_MASK_DEEMPH)
+               fmspeedy_set_reg_field(0xFFF2A9, 1, (0x0001 << 1), 1);
+       else
+               fmspeedy_set_reg_field(0xFFF2A9, 1, (0x0001 << 1), 0);
+
+       return 0;
+}
+
+int low_set_rds_cntr(struct s610_radio *radio, u16 value)
+{
+       if (value & RDS_CTRL_MASK_FLUSH)
+               fm_rds_flush_buffers(radio, !!(value & RDS_CTRL_MASK_RESYNC));
+
+       return 0;
+}
+
+int low_set_power(struct s610_radio *radio, u16 value)
+{
+       fm_tuner_set_power_state(radio,
+                       value & PWR_MASK_FM, value & PWR_MASK_RDS);
+
+       return 0;
+}
+
+
+/****************************************************************************
+
+ Functions for interrupt
+
+ ****************************************************************************/
+#ifndef        RDS_POLLING_ENABLE
+void fm_set_handler_if_count(void (*fn)(struct s610_radio *radio))
+{
+       handler_if_count = fn;
+       fm_set_interrupt_source(1, fn ? TRUE : FALSE);
+}
+
+void fm_set_handler_audio_pause(void (*fn)(struct s610_radio *radio))
+{
+       handler_audio_pause = fn;
+       fm_set_interrupt_source((1 << 3), fn ? TRUE : FALSE);
+}
+#endif /*RDS_POLLING_ENABLE*/
+
+void fm_set_interrupt_source(u16 sources, bool enable)
+{
+       u32 mask;
+
+       if (enable) {
+               /*fmspeedy_set_reg(0xFFF302, sources);*//* clear int. */
+                /* Get int. mask */
+               mask = fmspeedy_get_reg(0xFFF303);
+                /* Set Int. mask */
+               fmspeedy_set_reg(0xFFF303, (mask | sources));
+       } else {
+                /* Get int. mask */
+               mask = fmspeedy_get_reg(0xFFF303);
+                /* Set Int. mask */
+               fmspeedy_set_reg(0xFFF303, (mask & ~sources));
+       }
+}
+
+#ifdef ENABLE_RDS_WORK_QUEUE
+void s610_rds_work(struct work_struct *work)
+{
+       struct s610_radio *radio;
+
+       radio = container_of(work, struct s610_radio, work);
+
+       /*      FDEBUG(radio, ">R");*/
+       fm_process_rds_data(radio);
+}
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+
+#ifndef        RDS_POLLING_ENABLE
+#ifdef ENABLE_IF_WORK_QUEUE
+void s610_if_work(struct work_struct *work)
+{
+       struct s610_radio *radio;
+
+       radio = container_of(work, struct s610_radio, if_work);
+
+       /*      FUNC_ENTRY(radio);*/
+       FDEBUG(radio, ">IF");
+
+       if (handler_if_count)
+               (*handler_if_count)(radio);
+
+       /*      FUNC_EXIT(radio);*/
+}
+#endif /*ENABLE_IF_WORK_QUEUE*/
+#endif /* RDS_POLLING_ENABLE */
+
+void s610_sig2_work(struct work_struct *work)
+{
+       struct s610_radio *radio;
+
+       radio = container_of(work, struct s610_radio, dwork_sig2.work);
+
+       FDEBUG(radio, ">S;%d, %d", radio->low->fm_config.search_conf.normal_ifca_m,
+               radio->low->fm_config.search_conf.normal_ifca_h);
+       fm_search_check_signal2((unsigned long) radio);
+}
+
+void s610_tune_work(struct work_struct *work)
+{
+       struct s610_radio *radio;
+
+       radio = container_of(work, struct s610_radio, dwork_tune.work);
+
+       FDEBUG(radio, ">T");
+       fm_search_tuned((unsigned long) radio);
+}
+
+#ifdef RDS_POLLING_ENABLE
+#define RDS_MAX_FIFO   32
+void s610_rds_poll_work(struct work_struct *work)
+{
+       struct s610_radio *radio;
+       u32 fifo_status, rds_count;
+
+       radio = container_of(work, struct s610_radio, dwork_rds_poll.work);
+
+       if (radio->rds_flag == FM_RDS_ENABLE) {
+               spin_lock_irq(&radio->slock);
+
+               fm_update_rssi_work(radio);
+               if (radio->low->fm_state.rssi < RDS_VALID_THRESHOLD) {
+                       RDSEBUG(radio, "RDS Current RSSI invalid!![%02d][%02d]",
+                               radio->low->fm_state.rssi, RDS_VALID_THRESHOLD);
+                       radio->invalid_rssi = TRUE;
+                       goto fm_periodic_update;
+               }
+
+               fifo_status = fmspeedy_get_reg_work(0xFFF398);
+               if (fifo_status & 0x02) {
+                       dev_info(radio->dev, ">>>>> RDS FIFO FULL restart RDS!!");
+               }
+
+               rds_count = (fifo_status >> 8) & 0x3F;
+               if (rds_count >= RDS_MAX_FIFO/4) {
+                       RDSEBUG(radio,"%s(): rds_count:%d fifo_status[%08X]",
+                               __func__, rds_count, fifo_status);
+               fm_process_rds_data(radio);
+               }
+
+fm_periodic_update:
+               fm_rds_periodic_update((unsigned long) radio);
+
+               spin_unlock_irq(&radio->slock);
+       }
+}
+#endif /*RDS_POLLING_ENABLE*/
+
+#ifdef IDLE_POLLING_ENABLE
+void s610_idle_poll_work(struct work_struct *work)
+{
+       struct s610_radio *radio;
+       int rssi;
+
+       radio = container_of(work, struct s610_radio, dwork_idle_poll.work);
+
+       if (!wake_lock_active(&radio->wakelock))
+               wake_lock(&radio->wakelock);
+
+       spin_lock_irq(&radio->slock);
+
+       fm_update_rssi_work(radio);
+       rssi = radio->low->fm_state.rssi;
+       rssi = (rssi & 0x80) ? rssi - 256 : rssi;
+       APIEBUG(radio, "Current RSSI is [%02d][%02d] TRF ON/OFF RSSI[%2d:%2d] SNR ON/FF RSSI[%2d:%2d]",
+                       rssi, radio->low->fm_state.rssi,
+                       TRF_ON_RSSI_VALUE, TRF_OFF_RSSI_VALUE,
+                       SNR_ON_RSSI_VALUE, SNR_OFF_RSSI_VALUE);
+
+       if (radio->low->fm_state.freq == 104000) {
+               /* TRF ON ? */
+               if ((!fmspeedy_get_reg_field_work(0xFFF2A9, 7, (0x0001<<7))) &&
+                               (fmspeedy_get_reg_field_work(0xFFF2A9, 6, (0x0001<<6)))) {
+                       if ((radio->low->fm_state.rssi >= TRF_OFF_RSSI_VALUE)) {
+                               if (!fmspeedy_get_reg_field_work(0xFFF2A9, 7, (0x0001<<7)))
+                                       fmspeedy_set_reg_field_work(0xFFF2A9, 7, (0x0001<<7), 1);
+
+                               if (fmspeedy_get_reg_field_work(0xFFF2A9, 6, (0x0001<<6)))
+                                       fmspeedy_set_reg_field_work(0xFFF2A9, 6, (0x0001<<6), 0);
+                       }
+               } else {        /* TRF OFF? */
+                       if ((fmspeedy_get_reg_field_work(0xFFF2A9, 7, (0x0001<<7))) &&
+                                       (!fmspeedy_get_reg_field_work(0xFFF2A9, 6, (0x0001<<6)))) {
+                               if ((radio->low->fm_state.rssi < TRF_ON_RSSI_VALUE)) {
+                                       if (fmspeedy_get_reg_field_work(0xFFF2A9, 7, (0x0001<<7)))
+                                               fmspeedy_set_reg_field_work(0xFFF2A9, 7, (0x0001<<7), 0);
+                                       if (!fmspeedy_get_reg_field_work(0xFFF2A9, 6, (0x0001<<6)))
+                                               fmspeedy_set_reg_field_work(0xFFF2A9, 6, (0x0001<<6), 1);
+                               }
+                       }
+               }
+
+               /* SNR deviation bit 4 : 0 ? */
+               if (!fmspeedy_get_reg_field_work(0xFFF2A9, 4, (0x0001 << 4))) {
+                       if (radio->low->fm_state.rssi >= SNR_OFF_RSSI_VALUE) {
+                               fmspeedy_set_reg_field_work(0xFFF2A9, 4, (0x0001 << 4), 1);
+                       }
+               } else {
+                       /* SNR deviation bit 4 : 1 ? */
+                       if (radio->low->fm_state.rssi < SNR_ON_RSSI_VALUE) {
+                               fmspeedy_set_reg_field_work(0xFFF2A9, 4, (0x0001 << 4), 0);
+                       }
+               }
+       }
+
+       APIEBUG(radio, ">>> 0xFFF2A9 bit[7:6:4] and 0xFFF2C2 is [%02d:%02d:%02d][%04x]",
+                       fmspeedy_get_reg_field_work(0xFFF2A9, 7, (0x0001<<7)),
+                       fmspeedy_get_reg_field_work(0xFFF2A9, 6, (0x0001<<6)),
+                       fmspeedy_get_reg_field_work(0xFFF2A9, 4, (0x0001<<4)),
+                       fmspeedy_get_reg_work(0xFFF2C2));
+
+       fm_idle_periodic_update((unsigned long) radio);
+
+       spin_unlock_irq(&radio->slock);
+
+       if (wake_lock_active(&radio->rdswakelock))
+               wake_unlock(&radio->rdswakelock);
+
+}
+#endif /*IDLE_POLLING_ENABLE*/
+
+#ifndef        RDS_POLLING_ENABLE
+void fm_isr(struct s610_radio *radio)
+{
+       u16 cause;
+
+       cause = fmspeedy_get_reg(0xFFF301); /* save */
+
+       fmspeedy_set_reg(0xFFF302, cause); /* clear */
+       udelay(10);
+
+       cause &= fmspeedy_get_reg(0xFFF303); /* mask */
+
+       if (cause & INT_IFC_READY_MASK) {
+               fmspeedy_set_reg(0xFFF303,
+                       fmspeedy_get_reg(0xFFF303) & 0xFFFE);
+#ifdef ENABLE_IF_WORK_QUEUE
+               schedule_work(&radio->if_work);
+#else
+               if (handler_if_count)
+                       (*handler_if_count)(radio);
+
+#endif /*ENABLE_IF_WORK_QUEUE*/
+
+       }
+
+       if (cause & INT_RDS_BYTES_MASK) {
+#ifdef ENABLE_RDS_WORK_QUEUE
+               schedule_work(&radio->work);
+#else
+               fm_process_rds_data(radio);
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+       }
+
+       if (cause & INT_AUDIO_PAU_MASK) {
+               fmspeedy_set_reg(0xFFF303,
+                       fmspeedy_get_reg(0xFFF303) & 0xFFF7);
+
+               if (handler_audio_pause)
+                       (*handler_audio_pause)(radio);
+       }
+}
+#endif /* RDS_POLLING_ENABLE */
+
+/****************************************************************************
+
+ Functions for RX
+
+ ****************************************************************************/
+
+void fm_rx_ana_start(void)
+{
+       u32 adc_config1 = 0;
+
+       /* ADC setting */
+       adc_config1 = 0x01EF7A53;
+
+       /* RF setting */
+       fmspeedy_set_reg(0xFFF263, 0xFC1CDFFF);
+       /* fmspeedy_set_reg(0xFFF265, 0x80988002); */
+       fmspeedy_set_reg(0xFFF265, 0x81788002);
+       fmspeedy_set_reg(0xFFF264, 0x040003FD);
+
+       /* ADC input disconnect */
+       fmspeedy_set_reg(0xFFF261, 0);
+       /* ADC enable */
+       fmspeedy_set_reg(0xFFF260, adc_config1);
+       /* ADC reset */
+       fmspeedy_set_reg(0xFFF260, adc_config1 | (1 << 31) | (1 << 30));
+       fmspeedy_set_reg(0xFFF260, adc_config1);
+       /* Overload block reset */
+       fmspeedy_set_reg(0xFFF260, adc_config1 | (1 << 25));
+       fmspeedy_set_reg(0xFFF260, adc_config1);
+       /* ADC input connect */
+       fmspeedy_set_reg(0xFFF261, 2);
+}
+
+void fm_rx_ana_stop(void)
+{
+       fmspeedy_set_reg(0xFFF260, 0);
+       fmspeedy_set_reg(0xFFF261, 0);
+       fmspeedy_set_reg(0xFFF263, 0);
+       fmspeedy_set_reg(0xFFF264, 0);
+       fmspeedy_set_reg(0xFFF265, 0);
+}
+
+void fm_setup_iq_imbalance(void)
+{
+       fmspeedy_set_reg_field(0xFFF2B4, 0, 0x03FF, 511);
+       fmspeedy_set_reg_field(0xFFF2B5, 0, 0x03FF, 511);
+}
+
+void fm_rx_init(void)
+{
+       /* Turn off analogue. */
+       fm_rx_ana_stop();
+}
+
+#ifdef USE_SPUR_CANCEL
+void fm_rx_en_spur_removal(struct s610_radio *radio)
+{
+       fmspeedy_set_reg_field(0xFFF2A9, 13, (0x0001 << 13), 0);
+       fmspeedy_set_reg_field(0xFFF2A9, 12, (0x0001 << 12), 1);
+       fmspeedy_set_reg(0xFFF2D2, radio->low->fm_tune_info.rx_setup.spur_freq);
+}
+
+void fm_rx_dis_spur_removal(void)
+{
+       fmspeedy_set_reg_field(0xFFF2A9, 13, (0x0001 << 13), 1);
+       fmspeedy_set_reg_field(0xFFF2A9, 12, (0x0001 << 12), 0);
+}
+
+void fm_rx_check_spur(struct s610_radio *radio)
+{
+       u32 freq_gap_khz;
+       u16 i;
+
+       fm_rx_dis_spur_removal();
+
+       for (i = 0; i < radio->tc_on; i++) {
+               if (radio->low->fm_tune_info.rx_setup.fm_freq_khz
+                       >= radio->low->fm_spur[i]) {
+                       if ((radio->low->fm_tune_info.rx_setup.fm_freq_khz
+                               - radio->low->fm_spur[i]) < 160) {
+                               freq_gap_khz =
+                                       radio->low->fm_tune_info.rx_setup.fm_freq_khz
+                                       - radio->low->fm_spur[i];
+
+                               radio->low->fm_tune_info.rx_setup.spur_freq =
+                                               (s16)((freq_gap_khz * 2048) / 10);
+                               fm_rx_en_spur_removal(radio);
+                               break;
+                       }
+               } else {
+                       if ((radio->low->fm_spur[i]
+                               - radio->low->fm_tune_info.rx_setup.fm_freq_khz) < 160) {
+                               freq_gap_khz =
+                                       radio->low->fm_spur[i]
+                                       - radio->low->fm_tune_info.rx_setup.fm_freq_khz;
+
+                               radio->low->fm_tune_info.rx_setup.spur_freq =
+                                               (s16)(((freq_gap_khz * 2048) / 10) * (-1));
+                               fm_rx_en_spur_removal(radio);
+                               break;
+                       }
+               }
+
+       }
+}
+
+void fm_rx_check_spur_mono(struct s610_radio *radio)
+{
+       if ((radio->low->fm_tune_info.rx_setup.spur_ctrl &
+                       DIS_SPUR_REMOVAL_MONO) &&
+               (!fmspeedy_get_reg_field(0xFFF2CB, 1, 0x0001 << 1))) {
+               fm_rx_dis_spur_removal();
+
+               /* Disable spur removal */
+               radio->low->fm_tune_info.rx_setup.spur_ctrl = 0;
+       }
+}
+#endif
+
+/****************************************************************************
+
+ Functions for LO
+
+ ****************************************************************************/
+
+void fm_lo_off(void)
+{
+       fmspeedy_set_reg(0xFFF240, 0);
+}
+
+void fm_lo_prepare_setup(struct s610_radio *radio)
+{
+       u32 freq_hz;
+       u32 fref;
+       u32 flimit;
+       s64 flodiv_prev, flodiv_cur;
+       u16 ii;
+       u32 n_lodiv;
+
+       u64 fdco_t, ndiv_t, fcw_total = 0;
+       u64 fdco_r;
+
+       freq_hz = radio->low->fm_tune_info.lo_setup.rx_lo_req_freq;
+       if (radio->low->fm_tune_info.rx_setup.fm_freq_khz == 104000)
+               fref = 20000000;
+       else
+               fref = 26000000;
+
+       flimit = 3000000000;
+
+       /* Calculate the division value of LO divider
+        Look for the index of the smallest abs value */
+       for (ii = 1; ii < 11; ii++) {
+               flodiv_cur = ABS((int)(flimit - ((ii + 13) * freq_hz * 2)));
+               if (ii == 1)
+                       flodiv_prev = flodiv_cur;
+
+               n_lodiv = ii + 13;
+               if (flodiv_cur > flodiv_prev) {
+                       n_lodiv -= 1;
+                       break;
+               }
+               flodiv_prev = flodiv_cur;
+       }
+
+       fdco_t = freq_hz * n_lodiv * 2;
+       ndiv_t = fdco_t / (u64) fref;
+       fdco_r = fdco_t % (u64) fref;
+
+       ndiv_t *= (1 << 22);
+       fdco_r = ((fdco_r * (1 << 22)) + (fref >> 1)) / (u64) fref;
+
+       fcw_total = ndiv_t + fdco_r;
+
+       radio->low->fm_tune_info.lo_setup.n_mmdiv = (u32)(
+                       (fcw_total >> 22) & 0x1FF);
+       radio->low->fm_tune_info.lo_setup.frac_b1 = (u32)(
+                       ((fcw_total % (1 << 22)) >> 10) & 0xFFF);
+       radio->low->fm_tune_info.lo_setup.frac_b0 = (u32)(
+                       (fcw_total % (1 << 10)) & 0x3FF);
+       radio->low->fm_tune_info.lo_setup.n_lodiv = n_lodiv;
+
+}
+
+void fm_lo_set(const struct_fm_lo_setup lo_set)
+{
+       fmspeedy_set_reg(0xFFF242,
+                       (1 << 21) | (lo_set.n_mmdiv << 12) | lo_set.frac_b1);
+       fmspeedy_set_reg(0xFFF243,
+                       (1 << 21)
+                       | (lo_set.frac_b0 << 11)
+                       | (lo_set.n_lodiv << 6)
+                       | 8);
+       udelay(100);
+}
+
+void fm_lo_initialize(struct s610_radio *radio)
+{
+       API_ENTRY(gradio);
+
+       fm_sx_reset();
+
+       /* Set up the default PLL frequency */
+       radio->low->fm_tune_info.lo_setup.rx_lo_req_freq = 76000000;
+
+       /* Turn on the logic controller and dividers. */
+       fm_sx_start();
+
+       API_EXIT(gradio);
+}
+
+void fm_sx_reset(void)
+{
+       API_ENTRY(gradio);
+
+       /* Reset the FM_SX registers */
+       if (gradio->rfchip_ver == S620_REV_0) {
+               fmspeedy_set_reg(0xFFF240, 0x00302A);
+               fmspeedy_set_reg(0xFFF241, 0x004600);
+               fmspeedy_set_reg(0xFFF242, 0x27365E);
+               fmspeedy_set_reg(0xFFF243, 0x10BDC8);
+               fmspeedy_set_reg(0xFFF244, 0x0A20D0);
+               fmspeedy_set_reg(0xFFF245, 0x018132);
+               fmspeedy_set_reg(0xFFF246, 0x065A78);
+               fmspeedy_set_reg(0xFFF247, 0x243100);
+               fmspeedy_set_reg(0xFFF248, 0x0C0518);
+               fmspeedy_set_reg(0xFFF249, 0);
+               fmspeedy_set_reg(0xFFF24B, 0);
+               fmspeedy_set_reg(0xFFF24C, 0x01F8F4);
+               fmspeedy_set_reg(0xFFF24D, 0);
+               fmspeedy_set_reg(0xFFF24E, 0);
+               fmspeedy_set_reg(0xFFF24F, 0x26081D);
+               fmspeedy_set_reg(0xFFF250, 0);
+               fmspeedy_set_reg(0xFFF251, 0x2C0000);
+               fmspeedy_set_reg(0xFFF252, 0x040000);
+               fmspeedy_set_reg(0xFFF253, 0x00883C);
+       } else {
+               fmspeedy_set_reg(0xFFF240, 0x009020);
+               fmspeedy_set_reg(0xFFF241, 0x004600);
+               fmspeedy_set_reg(0xFFF242, 0x27365E);
+               fmspeedy_set_reg(0xFFF243, 0x10BDC8);
+               fmspeedy_set_reg(0xFFF244, 0x24A0D0);
+               fmspeedy_set_reg(0xFFF245, 0x018132);
+               fmspeedy_set_reg(0xFFF246, 0x065A78);
+               fmspeedy_set_reg(0xFFF247, 0x243100);
+               fmspeedy_set_reg(0xFFF248, 0x0C0518);
+               fmspeedy_set_reg(0xFFF249, 0);
+               fmspeedy_set_reg(0xFFF24B, 0);
+               fmspeedy_set_reg(0xFFF24C, 0x01F8F4);
+               fmspeedy_set_reg(0xFFF24D, 0);
+               fmspeedy_set_reg(0xFFF24E, 0);
+               fmspeedy_set_reg(0xFFF24F, 0x26081D);
+               fmspeedy_set_reg(0xFFF250, 0);
+               fmspeedy_set_reg(0xFFF251, 0x2C0000);
+               fmspeedy_set_reg(0xFFF252, 0x040000);
+               fmspeedy_set_reg(0xFFF253, 0x00883C);
+       }
+
+       API_EXIT(gradio);
+}
+
+void fm_sx_start(void)
+{
+       API_ENTRY(gradio);
+
+       if (gradio->rfchip_ver == S620_REV_0) {
+               fmspeedy_set_reg(0xFFF253, 0x0F883C);
+               udelay(50);
+
+               fmspeedy_set_reg(0xFFF240, 0x3C302A);
+               udelay(20);
+
+               fmspeedy_set_reg(0xFFF253, 0x0B883C);
+               fmspeedy_set_reg(0xFFF240, 0x14302A);
+       } else {
+               fmspeedy_set_reg(0xFFF253, 0x0F883C);
+               udelay(50);
+
+               fmspeedy_set_reg(0xFFF240, 0x3C9020);
+               udelay(20);
+
+               fmspeedy_set_reg(0xFFF253, 0x0B883C);
+               fmspeedy_set_reg(0xFFF240, 0x149020);
+       }
+
+       API_ENTRY(gradio);
+}
+
+bool fm_aux_pll_initialize(void)
+{
+       u32 pll_locked = 0;
+       u16 i;
+
+       API_ENTRY(gradio);
+
+       if (gradio->rfchip_ver == S620_REV_0) {
+               fmspeedy_set_reg_field(0xFFF221, 0, 0x0001, 0); /* PLL_CLK_EN, default = 1 */
+               udelay(20);
+               dev_info(gradio->dev, "%s():PLL_CLK_EN[%02X]",
+                       __func__,
+                       fmspeedy_get_reg_field(0xFFF221, 0, 0x0001));
+       }
+
+       fmspeedy_set_reg_field(0xFFF255, 10, (0x0001<<10), 1); /* FMCLK_from240M, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 21, (0x0001<<21), 0); /* FMCLK_40_32M, default = 1 */
+       fmspeedy_set_reg_field(0xFFF255, 22, (0x0001<<22), 0); /* FMCLK_from260M, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 30, (0x0001<<30), 1); /* PLL_LOCK_EN, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 28, (0x0001<<28), 0); /* PLL_FEED_EN, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 26, (0x0001<<26), 1); /* PLL_EN_CLK_240M, default = 1 */
+       fmspeedy_set_reg_field(0xFFF255, 25, (0x0001<<25), 1); /* PLL_EN_CLK_120M, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 27, (0x0001<<27), 1); /* PLL_EN_CLK_80M, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 24, (0x0001<<24), 1); /* PLL_EN, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 23, (0x0001<<23), 1); /* OUT_EN, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 16, (0x0001<<16), 1); /* IREF_EN, default = 1 */
+       fmspeedy_set_reg_field(0xFFF255, 9, (0x0001<<9), 1); /* BUFFER_AD_EN, default = 1 */
+       fmspeedy_set_reg_field(0xFFF255, 29, (0x0001<<29), 0); /* PLL_FSEL, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 17, (0x000F<<17), 8); /* IREFTRIM, default = 8 */
+       fmspeedy_set_reg_field(0xFFF255, 14, (0x0003<<14), 0); /* IO_SPARE, default = 0 */
+       fmspeedy_set_reg_field(0xFFF255, 11, (0x0007<<11), 0); /* IO_OUT_SEL, default = 0 */
+
+#if 0
+       fmspeedy_set_reg_field(0xFFF256, 21, (0x003F<<21), 16); /* PLL1_LPFRBUS, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 19, (0x0003<<19), 3); /* PLL1_LOCK_OUT, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 17, (0x0003<<17), 3); /* PLL1_LOCK_IN, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 15, (0x0003<<15), 3); /* PLL1_LOCK_DLY, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 7, (0x00FF<<7), 10); /* PLL1_FB_DIV, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 4, (0x0007<<4), 7); /* PLL1_CPCBUS, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 3, (0x0001<<3), 0); /* PLL1_BYPASS, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 0, 0x0001, 0); /* PLL_SEL_PLL, default = 0 */
+       fmspeedy_set_reg_field(0xFFF256, 1, (0x0003<<1), 0); /* PLL_SPARE, default = 0 */
+#else
+       fmspeedy_set_reg(0xFFF256, 0x21F8570);
+#endif
+
+#if 0
+       fmspeedy_set_reg_field(0xFFF257, 0, 0x003F, 1); /* PLL1_PRE_DIV, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 7, (0x0001<<7), 0); /* PLL1_SEL_CONTROL, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 6, (0x0001<<6), 0); /* PLL1_SEL_BW_TYP, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 9, (0x0003<<9), 1); /* PLL1_VCO_TUNE, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 8, (0x0001<<8), 1); /* PLL1_SEL_HP, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 15, (0x00FF<<15), 12); /* PLL2_FB_DIV, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 27, (0x0003<<27), 3); /* PLL2_LOCK_OUT, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 25, (0x0003<<25), 3); /* _PLL2_LOCK_IN, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 23, (0x0003<<23), 3); /* PLL2_LOCK_DLY, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 12, (0x0007<<12), 4); /* PLL2_CPCBUS, default = 0 */
+       fmspeedy_set_reg_field(0xFFF257, 11, (0x0001<<11), 0); /* PLL2_BYPASS, default = 0 */
+#else
+       fmspeedy_set_reg(0xFFF257, 0x1F864301);
+#endif
+
+#if 0
+       fmspeedy_set_reg_field(0xFFF258, 6, (0x003F<<6), 13); /* PLL2_PRE_DIV, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 0, 0x003F, 16); /* PLL2_LPFRBUS, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 15, (0x0003<<15), 1); /* PLL2_VCO_TUNE, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 14, (0x0001<<14), 1); /* PLL2_SEL_HP, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 13, (0x0001<<13), 0); /* PLL2_SEL_CONTROL, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 12, (0x0001<<12), 0); /* PLL2_SEL_BW_TYP, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 19, (0x001F<<19), 0); /* XTAL_AMPLVL, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 27, (0x0001<<27), 1); /* XTAL_EN_FM_BUF, default = 0 */
+       fmspeedy_set_reg_field(0xFFF258, 26, (0x0001<<26), 1); /* XTAL_EN_CORE_BUF, default = 1 */
+       fmspeedy_set_reg_field(0xFFF258, 24, (0x0001<<24), 1); /* XTAL_EN, default = 1 */
+       fmspeedy_set_reg_field(0xFFF258, 17, (0x0001<<17), 1); /* PTAT_EN, default = 1 */
+#else
+       fmspeedy_set_reg(0xFFF258, 0xD06C350);
+#endif
+
+#if 0
+       fmspeedy_set_reg_field(0xFFF259, 0, 0x0001, 1); /* XTAL_IREF_EN, default = 1 */
+       fmspeedy_set_reg_field(0xFFF259, 5, (0x0001<<5), 1); /* XTAL_SEL_XTAL, default = 1 */
+       fmspeedy_set_reg_field(0xFFF259, 4, (0x0001<<4), 0); /* XTAL_SEL_ULP, default = 0 */
+       fmspeedy_set_reg_field(0xFFF259, 3, (0x0001<<3), 0); /* XTAL_SEL_MON, default = 0 */
+       fmspeedy_set_reg_field(0xFFF259, 2, (0x0001<<2), 0); /* XTAL_SEL_LP, default = 0 */
+       fmspeedy_set_reg_field(0xFFF259, 1, (0x0001<<1), 0); /* XTAL_SEL_ALC, default = 0 */
+#else
+       fmspeedy_set_reg(0xFFF259, 0x21);
+#endif
+
+       for (i = 0; i < 10; i++) {
+               udelay(300);
+               pll_locked = fmspeedy_get_reg_field(0xFFF25B, 4, (0x0001 << 4));
+               if (pll_locked) {
+                       APIEBUG(gradio, "API> Aux pll lock!!");
+                       return TRUE;
+               }
+       }
+       dev_err(gradio->dev, "Fail aux pll lock. Check RF power!!");
+
+       API_EXIT(gradio);
+
+       return FALSE;
+}
+
+void fm_ds_set(u32 data)
+{
+       fmspeedy_set_reg_field(0xFFF390, 7, (0x0003 << 7), data);
+       mdelay(10);
+       dev_info(gradio->v4l2dev.dev,
+               "%s: DS set: 0x%xh, reg val: 0x%xh\n", __func__,
+               data, fmspeedy_get_reg(0xFFF390));
+}
+
+void fm_get_version_number(void)
+{
+       dev_info(gradio->dev,
+               ">>> FM version: DEMOD[%04X]ANA[%04X]PMU[%04X]SCG[%04X]SPY[%04X]TOP[%04X]DS[%d]BUILD[%08X]",
+               fmspeedy_get_reg(0xFFF399),
+               fmspeedy_get_reg(0xFFF39A),
+               fmspeedy_get_reg(0xFFF39B),
+               fmspeedy_get_reg(0xFFF39C),
+               fmspeedy_get_reg(0xFFF39D),
+               fmspeedy_get_reg(0xFFF39E),
+               fmspeedy_get_reg_field(0xFFF390, 7, (0x0003 << 7)),
+               build_identifier_integer);
+}
+
+void fm_aux_pll_off(void)
+{
+       fmspeedy_set_reg(0xFFF255, 0x04300308);
+       fmspeedy_set_reg(0xFFF256, 0);
+       fmspeedy_set_reg(0xFFF257, 0);
+       fmspeedy_set_reg(0xFFF258, 0x05040000);
+       fmspeedy_set_reg(0xFFF259, 0x21);
+
+       if (gradio->rfchip_ver == S620_REV_0) {
+               fmspeedy_set_reg_field(0xFFF221, 0, 0x0001, 1); /* PLL_CLK_EN, default = 1 */
+               dev_info(gradio->dev, "%s():PLL_CLK_EN[%02X]",
+                       __func__,
+                       fmspeedy_get_reg_field(0xFFF221, 0, 0x0001));
+       }
+}
+
+/****************************************************************************
+
+ Functions for tunning
+
+ ****************************************************************************/
+
+void fm_set_band(struct s610_radio *radio, u8 index)
+{
+       u16 num_of_bands = 0;
+
+       num_of_bands = sizeof(radio->low->fm_bands) / sizeof(fm_band_s);
+
+       if (index >= num_of_bands)
+               index = num_of_bands - 1;
+
+       radio->low->fm_state.band = index;
+
+       radio->low->fm_tuner_state.band_limit_lo =
+                       radio->low->fm_bands[radio->low->fm_state.band].lo;
+       radio->low->fm_tuner_state.band_limit_hi =
+                       radio->low->fm_bands[radio->low->fm_state.band].hi;
+
+       radio->low->fm_state.freq = radio->low->fm_tuner_state.band_limit_lo;
+}
+
+void fm_set_freq_step(struct s610_radio *radio, u8 index)
+{
+       radio->low->fm_tuner_state.freq_step = radio->low->fm_freq_steps[index];
+}
+
+bool fm_band_trim(struct s610_radio *radio, u32 *freq)
+{
+       bool bl = FALSE;
+
+       if (*freq <= radio->low->fm_tuner_state.band_limit_lo) {
+               *freq = radio->low->fm_tuner_state.band_limit_lo;
+               bl = TRUE;
+       }
+       if (*freq >= radio->low->fm_tuner_state.band_limit_hi) {
+               *freq = radio->low->fm_tuner_state.band_limit_hi;
+               bl = TRUE;
+       }
+
+       return bl;
+}
+
+static bool fm_tuner_push_freq(struct s610_radio *radio, bool down)
+{
+       u32 new_freq = radio->low->fm_state.freq;
+       bool in_bl;
+
+       if (down)
+               new_freq -= radio->low->fm_tuner_state.freq_step;
+       else
+               new_freq += radio->low->fm_tuner_state.freq_step;
+
+       in_bl = fm_band_trim(radio, &new_freq);
+       radio->low->fm_state.freq = new_freq;
+
+       return in_bl;
+}
+
+static void fm_tuner_enable_rds(struct s610_radio *radio, bool enable)
+{
+       if (radio->low->fm_state.rds_pwr_on) {
+               if (enable && !radio->low->fm_state.rds_rx_enabled)
+                       fm_rds_enable(radio);
+               else if (!enable && radio->low->fm_state.rds_rx_enabled)
+                       fm_rds_disable(radio);
+       }
+       radio->low->fm_state.rds_rx_enabled = enable;
+}
+
+void fm_set_rssi_thresh(struct s610_radio *radio, fm_tuner_state state)
+{
+       switch (state) {
+       case TUNER_SEARCH:
+               fmspeedy_set_reg(0xFFF2C4,
+                       radio->low->fm_state.rssi_limit_search);
+               break;
+       case TUNER_IDLE:
+       case TUNER_PRESET:
+       default:
+               fmspeedy_set_reg(0xFFF2C4,
+                       radio->low->fm_state.rssi_limit_normal);
+               break;
+       }
+}
+
+static void fm_tuner_control_mute(struct s610_radio *radio)
+{
+       bool mute = radio->low->fm_state.mute_forced
+                       || radio->low->fm_state.mute_audio
+                       || (!radio->low->fm_tuner_state.tune_done);
+       fm_set_mute(mute);
+}
+
+void fm_tuner_set_mute_audio(struct s610_radio *radio, bool mute)
+{
+       radio->low->fm_state.mute_audio = mute;
+       fm_tuner_control_mute(radio);
+}
+
+#ifdef MONO_SWITCH_INTERF
+void fm_reset_force_mono_interf(struct s610_radio *radio)
+{
+       radio->low->fm_state.force_mono_interf = FALSE;
+       radio->low->fm_state.mono_interf_reset_time = get_time();
+       radio->low->fm_state.interf_checked = FALSE;
+       fm_set_blend_mute(radio);
+}
+
+void fm_check_interferer(struct s610_radio *radio)
+{
+       s16 rssi;
+       bool old_state = radio->low->fm_state.force_mono_interf;
+       TIME passed_time =
+                       get_time()
+                       - radio->low->fm_state.mono_interf_reset_time;
+
+       if (!radio->low->fm_state.interf_checked) {
+               if (passed_time < (1 * SECOND)) {
+                       return;
+               } else {
+                       radio->low->fm_state.interf_checked = TRUE;
+                       radio->low->fm_state.mono_interf_reset_time =
+                                       get_time();
+                       radio->low->fm_state.mono_switched_interf =
+                       FALSE;
+               }
+       }
+
+       rssi = (radio->low->fm_state.rssi & 0x80) ?
+                       radio->low->fm_state.rssi - 256 :
+                       radio->low->fm_state.rssi;
+       if ((rssi > radio->low->fm_config.interf_rssi.hi)
+                       && (radio->low->fm_state.snr
+                       < radio->low->fm_config.interf_snr.lo)) {
+               radio->low->fm_state.force_mono_interf = TRUE;
+               radio->low->fm_state.mono_switched_interf = TRUE;
+       } else if ((rssi < radio->low->fm_config.interf_rssi.lo)
+                       || (radio->low->fm_state.snr >
+                       radio->low->fm_config.interf_snr.hi)) {
+               radio->low->fm_state.force_mono_interf = FALSE;
+       }
+
+       if (old_state != radio->low->fm_state.force_mono_interf)
+               fm_set_blend_mute(radio);
+}
+#endif /* MONO_SWITCH_INTERF */
+
+void fm_start_if_counter(void)
+{
+       fmspeedy_set_reg_field(0xFFF2A9, 5, (0x0001 << 5), 0);
+       udelay(4);
+       fmspeedy_set_reg_field(0xFFF2A9, 5, (0x0001 << 5), 1);
+}
+
+static void fm_preset_tuned(struct s610_radio *radio)
+{
+       u16 count;
+       int ii;
+       u16 flag;
+
+       API_ENTRY(radio);
+
+       count = (fmspeedy_get_reg(0xFFF2B2) * 5) / 10;
+
+       fm_tuner_enable_rds(radio, TRUE);
+       fm_start_if_counter();
+
+       fmspeedy_set_reg_field(0xFFF302, 0, 1, 1); /* Clear Int. */
+
+       for (ii = 0; ii < count; ii++)
+               udelay(1000); /* ms */
+
+       flag = fm_update_rx_status(radio, fmspeedy_get_reg(0xFFF2CB));
+
+       fm_update_if_count(radio);
+       fm_update_sig_info(radio);
+
+       fm_tuner_exit_state(radio);
+       fm_tuner_change_state(radio, TUNER_IDLE);
+       if (radio->low->fm_tuner_state.hit_band_limit)
+               flag |= FLAG_BD_LMT;
+       fm_set_flag_bits(radio, flag | FLAG_TUNED);
+       /*Set freq completed */
+       complete(&radio->flags_set_fr_comp);
+       APIEBUG(radio, ">>>>> preset tune complete!! 0x%x",
+                       radio->low->fm_state.freq);
+
+       API_EXIT(radio);
+}
+
+static void fm_search_done(struct s610_radio *radio, u16 flags)
+{
+       API_ENTRY(radio);
+       fm_tuner_set_mute_audio(radio, FALSE); /* unmute */
+
+       fm_tuner_exit_state(radio);
+       fm_tuner_change_state(radio, TUNER_IDLE);
+       APIEBUG(radio, "API 01> Seek done doing complete!! 0x%x, flag 0x%x",
+                       radio->low->fm_state.freq, flags);
+
+       fm_set_flag_bits(radio, flags | FLAG_TUNED);
+       radio->irq_flag = flags;
+
+       /* Seek_done complete */
+       complete(&radio->flags_seek_fr_comp);
+
+       fm_tuner_enable_rds(radio, TRUE);
+       API_EXIT(radio);
+}
+
+static void fm_search_check_signal2(unsigned long data)
+{
+#ifdef USE_NEW_SCAN
+       static u16 min_weak_ifc_abs;
+       static u16 min_normal_ifc_abs;
+#else
+       static u16 min_ifc_abs;
+#endif
+       static u16 retry_count;
+
+#ifdef USE_NEW_SCAN
+       int rssi;
+       u16 d_rssi, gain, adjust;
+#endif
+       bool check_ok = TRUE;
+       bool done = FALSE;
+       u16 ifc_abs;
+       struct s610_radio *radio = (void *) data;
+
+       API_ENTRY(radio);
+       if (radio->sig2_fniarg) {
+               retry_count = 0;
+#ifdef USE_NEW_SCAN
+               min_weak_ifc_abs = 0xffff;
+               min_normal_ifc_abs = 0xffff;
+#else
+               min_ifc_abs = 0xffff;
+#endif
+       }
+
+#ifdef USE_NEW_SCAN
+               d_rssi = fmspeedy_get_reg(0xFFF2AD);
+               gain = fmspeedy_get_reg(0xFFF285);
+               adjust = fmspeedy_get_reg(0xFFF2C2);
+
+               rssi = rssi_device_to_host(d_rssi, gain, adjust);
+               rssi = (rssi & 0x80) ? rssi - 256 : rssi;
+
+       APIEBUG(radio, "SIG2> rssi %d", rssi);
+       APIEBUG(radio, "SIG2> weak ifca_m %d", radio->low->fm_config.search_conf.weak_ifca_m);
+       APIEBUG(radio, "SIG2> normal ifca_m %d", radio->low->fm_config.search_conf.normal_ifca_m);
+       APIEBUG(radio, "SIG2> RSSI limit %d %d",
+                       aggr_rssi_device_to_host(radio->low->fm_state.rssi_limit_search),
+                       radio->low->fm_state.rssi_limit_search);
+       APIEBUG(radio, "SIG2> with eLNA %d RSSI ADJUST %x %x",
+               radio->without_elna, RSSI_ADJUST_WITHOUT_ELNA_VALUE, radio->low->fm_config.rssi_adj_ini);
+       APIEBUG(radio, "SIG2> seek weak rssi %d", radio->seek_weak_rssi);
+       APIEBUG(radio, "SIG2> Current Freq %d", radio->low->fm_state.freq);
+
+       if (rssi < radio->seek_weak_rssi)
+               radio->low->fm_config.search_conf.weak_sig = TRUE;
+       else
+               radio->low->fm_config.search_conf.weak_sig = FALSE;
+
+#endif
+
+#ifndef USE_NEW_SCAN
+       if (fmspeedy_get_reg_field(0xFFF2CB, 7, (0x0001 << 7)) != 0) {
+               done = TRUE;
+               check_ok = FALSE;
+       }
+#endif
+
+       if (!done) {
+               ifc_abs = fmspeedy_get_reg(0xFFF2B1);
+
+#ifdef USE_NEW_SCAN
+               if (radio->low->fm_config.search_conf.weak_sig) {
+                       if (ifc_abs < radio->low->fm_config.search_conf.weak_ifca_l) {
+                               APIEBUG(radio, "SIG> weak good %d", ifc_abs);
+                               done = TRUE;
+                               check_ok = TRUE;
+                       }
+
+                       else if (ifc_abs > radio->low->fm_config.search_conf.weak_ifca_h) {
+                               APIEBUG(radio, "SIG> weak bad %d", ifc_abs);
+                               done = TRUE;
+                               check_ok = FALSE;
+                       }
+
+                       else if (ifc_abs < min_weak_ifc_abs) {
+                               APIEBUG(radio, "SIG> weak mid %d %d", ifc_abs, min_weak_ifc_abs);
+                               min_weak_ifc_abs = ifc_abs;
+                       }
+               } else {
+                       if (ifc_abs < radio->low->fm_config.search_conf.normal_ifca_l) {
+                               APIEBUG(radio, "SIG> normal good %d", ifc_abs);
+                               done = TRUE;
+                               check_ok = TRUE;
+                       }
+
+                       else if (ifc_abs > radio->low->fm_config.search_conf.normal_ifca_h) {
+                               APIEBUG(radio, "SIG> normal bad %d", ifc_abs);
+                               done = TRUE;
+                               check_ok = FALSE;
+                       }
+
+                       else if (ifc_abs < min_normal_ifc_abs) {
+                               APIEBUG(radio, "SIG> normal mid %d %d", ifc_abs, min_weak_ifc_abs);
+                               min_normal_ifc_abs = ifc_abs;
+                       }
+               }
+#else
+               if (ifc_abs < radio->low->fm_config.search_conf.ifca_l) {
+                       done = TRUE;
+                       check_ok = TRUE;
+               } else if (ifc_abs > radio->low->fm_config.search_conf.ifca_h) {
+                       done = TRUE;
+                       check_ok = FALSE;
+               } else if (ifc_abs < min_ifc_abs) {
+                       min_ifc_abs = ifc_abs;
+               }
+#endif
+       }
+
+#ifdef USE_NEW_SCAN
+       if ((!done) && (++retry_count >= 5)) {
+               done = TRUE;
+
+               if (((min_weak_ifc_abs < 0xffff)
+                       && (min_weak_ifc_abs > radio->low->fm_config.search_conf.weak_ifca_m))
+                       || ((min_normal_ifc_abs < 0xffff)
+                       && (min_normal_ifc_abs > radio->low->fm_config.search_conf.normal_ifca_m))) {
+                       check_ok = FALSE;
+                       APIEBUG(radio, "SIG> check mid fail");
+               }
+       }
+#else
+       if ((!done) && (++retry_count >= 10)) {
+
+               done = TRUE;
+               if (min_ifc_abs >
+                       radio->low->fm_config.search_conf.ifca_m)
+                       check_ok = FALSE;
+       }
+#endif
+
+       if (done) {
+               if (check_ok) {
+                       u16 flag =
+                               fm_update_rx_status(radio,
+                                       fmspeedy_get_reg(0xFFF2CB));
+
+                       fm_update_if_count(radio);
+                       fm_update_sig_info(radio);
+                       fm_search_done(radio, flag);
+               } else {
+                       fm_search_check_signal1(radio, TRUE);
+               }
+
+       } else {
+               radio->sig2_fniarg = FALSE;
+               radio->dwork_sig2_counter++;
+               schedule_delayed_work(&radio->dwork_sig2,
+                       msecs_to_jiffies(SEARCH_DELAY_MS));
+       }
+
+       API_EXIT(radio);
+}
+
+static void fm_search_check_signal1(struct s610_radio *radio, bool rssi_oor)
+{
+       u16 flag;
+       u16 d_status;
+
+       /*      API_ENTRY(radio);*/
+#ifndef        RDS_POLLING_ENABLE
+       fm_set_handler_if_count(NULL);
+#endif /* RDS_POLLING_ENABLE */
+
+       d_status = fmspeedy_get_reg(0xFFF2CB);
+
+       if (rssi_oor || !!(d_status & (0x0001 << 7))) {
+               if (radio->low->fm_tuner_state.hit_band_limit) {
+                       APIEBUG(radio, "SIG1> IF_OOR 0x%x,0x%x,0x%x",
+                                       radio->wrap_around, radio->seek_freq,
+                                       radio->low->fm_state.freq);
+                       if (radio->wrap_around) {
+                               if (radio->seek_freq == radio->low->fm_state.freq) {
+                                       flag = fm_update_rx_status(radio, d_status);
+                                       fm_update_if_count(radio);
+                                       fm_update_sig_info(radio);
+
+                                       fm_search_done(radio, flag);
+                               } else {
+                                       radio->low->fm_tuner_state.hit_band_limit =
+                                               fm_tuner_push_freq(
+                                               radio,
+                                               radio->low->fm_tuner_state.curr_search_down);
+                                       /* disable audio out */
+                                       fm_audio_control(radio, 0, 1, 0x100, 0x1A0);
+
+                                       fm_set_freq(radio, radio->low->fm_state.freq, 1);
+
+                                       /* enable audio out */
+                                       fm_audio_control(radio, 1, 1, 0x100, 0x1A0);
+
+                                       radio->tune_fniarg = 0;
+                                       radio->dwork_tune_counter++;
+                                       schedule_delayed_work(&radio->dwork_tune,
+                                               msecs_to_jiffies(TUNE_TIME_FAST_MS));
+                               }
+                       } else {
+                               flag = fm_update_rx_status(radio, d_status);
+                               fm_update_if_count(radio);
+                               fm_update_sig_info(radio);
+
+                               if (radio->seek_freq == radio->low->fm_state.freq)
+                                       fm_search_done(radio, flag);
+                               else
+                                       fm_search_done(radio, flag | FLAG_BD_LMT);
+
+                               APIEBUG(radio, "SIG1> IF_Not OOR 0x%x,0x%x,0x%x,0x%x",
+                                               radio->wrap_around, radio->seek_freq,
+                                               radio->low->fm_state.freq, flag);
+                       }
+               } else {
+                       if (radio->wrap_around &&
+                               (radio->seek_freq == radio->low->fm_state.freq)) {
+                                       flag = fm_update_rx_status(radio, d_status);
+                                       fm_update_if_count(radio);
+                                       fm_update_sig_info(radio);
+                                       fm_search_done(radio, flag);
+                       } else {
+                               radio->low->fm_tuner_state.hit_band_limit =
+                                       fm_tuner_push_freq(
+                                       radio,
+                                       radio->low->fm_tuner_state.curr_search_down);
+                               /* disable audio out */
+                               fm_audio_control(radio, 0, 1, 0x100, 0x1A0);
+
+                               fm_set_freq(radio, radio->low->fm_state.freq, 1);
+
+                               /* enable audio out */
+                               fm_audio_control(radio, 1, 1, 0x100, 0x1A0);
+                               radio->tune_fniarg = 0;
+                               radio->dwork_tune_counter++;
+                               schedule_delayed_work(&radio->dwork_tune,
+                                       msecs_to_jiffies(TUNE_TIME_FAST_MS));
+                       }
+               }
+       } else {
+                       radio->sig2_fniarg = 1;
+                       radio->dwork_sig2_counter++;
+                       schedule_delayed_work(&radio->dwork_sig2,
+                               msecs_to_jiffies(SEARCH_DELAY_MS));
+       }
+       /*      API_EXIT(radio);*/
+}
+
+static void fm_search_tuned(unsigned long data)
+{
+       u16 count, ii;
+       struct s610_radio *radio = (void *) data;
+
+       API_ENTRY(radio);
+
+       count = (fmspeedy_get_reg(0xFFF2B2) * 5) / 10;
+
+       if (fm_check_rssi_level(radio->low->fm_state.rssi_limit_search)) {
+               fm_search_check_signal1(radio, TRUE);
+       } else {
+               fm_start_if_counter();
+
+               fmspeedy_set_reg_field(0xFFF302, 0, 1, 1); /* Clear Int. */
+
+               for (ii = 0; ii < count; ii++)
+                       udelay(1000); /* ms */
+
+               fm_search_check_signal1(radio, FALSE);
+       }
+
+       API_EXIT(radio);
+}
+
+#ifdef USE_FILTER_SELECT_BY_FREQ
+static const u32 filter_freq_very[MAX_FILTER_FREQ_NUM] = {
+       87900, 88100, 95900, 96100, 103900, 104100
+};
+
+static bool is_freq_in_array(int freq) {
+       int i;
+
+       for (i = 0; i < MAX_FILTER_FREQ_NUM; i++) {
+               if (filter_freq_very[i] == freq)
+                       return TRUE;
+       }
+       return FALSE;
+}
+#endif /* USE_FILTER_SELECT_BY_FREQ */
+
+#ifdef RDS_POLLING_ENABLE
+void fm_rds_periodic_cancel(unsigned long data)
+{
+       struct s610_radio *radio = (struct s610_radio *) data;
+
+       cancel_delayed_work(&radio->dwork_rds_poll);
+}
+
+void fm_rds_periodic_update(unsigned long data)
+{
+       struct s610_radio *radio = (struct s610_radio *) data;
+
+       radio->dwork_rds_counter++;
+       schedule_delayed_work(&radio->dwork_rds_poll,
+               msecs_to_jiffies(RDS_POLL_DELAY_MS));
+}
+#endif /*RDS_POLLING_ENABLE*/
+
+#ifdef IDLE_POLLING_ENABLE
+void fm_idle_periodic_cancel(unsigned long data)
+{
+       struct s610_radio *radio = (struct s610_radio *) data;
+
+       cancel_delayed_work(&radio->dwork_idle_poll);
+}
+
+void fm_idle_periodic_update(unsigned long data)
+{
+       struct s610_radio *radio = (struct s610_radio *) data;
+
+       radio->dwork_idle_counter++;
+       schedule_delayed_work(&radio->dwork_idle_poll,
+               msecs_to_jiffies(IDLE_TIME_MS));
+}
+#endif /*IDLE_POLLING_ENABLE*/
+
+static void fm_start_tune(struct s610_radio *radio, fm_tuner_state new_state)
+{
+       bool next = !!(radio->low->fm_state.tuner_mode & TUNER_MODE_MASK_NEXT);
+
+       API_ENTRY(radio);
+
+       switch (new_state) {
+       case TUNER_NOTTUNED:
+               break;
+       case TUNER_IDLE:
+               radio->low->fm_tuner_state.tune_done = TRUE;
+
+#ifdef USE_IQ_IMBAL_SMOOTH
+               hal_set_fm_image_trim_smooth_config_lock(1);
+#endif /*USE_IQ_IMBAL_SMOOTH*/
+
+#ifdef USE_FILTER_SELECT_BY_FREQ
+               if (is_freq_in_array(radio->low->fm_state.freq))
+                       /* Set the filter to use very narrow band */
+                       fmspeedy_set_reg(0xFFF2AE, 0x0DB6);
+               else
+                       /* Set the filter to use narrow band */
+                       fmspeedy_set_reg(0xFFF2AE, 0x0B6D);
+#else
+               fmspeedy_set_reg(0xFFF2AE, 0x0B6D);
+#endif /* USE_FILTER_SELECT_BY_FREQ */
+
+               if (!radio->low->fm_state.tuner_mode)
+                       radio->low->fm_state.mute_audio = 0;
+               fm_tuner_control_mute(radio);
+
+#ifdef MONO_SWITCH_INTERF
+               fm_reset_force_mono_interf(radio);
+#endif
+               break;
+       case TUNER_PRESET:
+               fm_tuner_enable_rds(radio, FALSE);
+               radio->low->fm_tuner_state.hit_band_limit = FALSE;
+               if (next)
+                       radio->low->fm_tuner_state.hit_band_limit =
+                               fm_tuner_push_freq(
+                               radio,
+                               radio->low->fm_tuner_state.curr_search_down);
+
+               /* disable audio out */
+               fm_audio_control(radio, 0, 1, 0x100, 0x1A0);
+
+               fm_set_freq(radio, radio->low->fm_state.freq, 1);
+
+               /* enable audio out */
+               fm_audio_control(radio, 1, 1, 0x100, 0x1A0);
+
+               mdelay(TUNE_TIME_SLOW_MS);
+               fm_preset_tuned(radio);
+
+               break;
+       case TUNER_SEARCH:
+               fm_tuner_enable_rds(radio, FALSE);
+               fm_tuner_set_mute_audio(radio, TRUE);
+               fm_set_rssi_thresh(radio, new_state);
+               radio->low->fm_tuner_state.hit_band_limit = FALSE;
+
+               if (next) {
+                       radio->low->fm_tuner_state.hit_band_limit =
+                               fm_tuner_push_freq(
+                               radio,
+                               radio->low->fm_tuner_state.curr_search_down);
+               }
+
+               /* disable audio out */
+               fm_audio_control(radio, 0, 1, 0x100, 0x1A0);
+
+               fm_set_freq(radio, radio->low->fm_state.freq, 1);
+
+               /* enable audio out */
+               fm_audio_control(radio, 1, 1, 0x100, 0x1A0);
+
+               radio->tune_fniarg = 0;
+               radio->dwork_tune_counter++;
+               schedule_delayed_work(&radio->dwork_tune,
+                       msecs_to_jiffies(TUNE_TIME_FAST_MS));
+               break;
+       default:
+               break;
+       }
+
+       API_EXIT(radio);
+
+}
+
+static void fm_tuner_change_state(struct s610_radio *radio,
+               fm_tuner_state new_state)
+{
+       radio->low->fm_tuner_state.tuner_state = new_state;
+       fm_update_tuner_mode(radio);
+
+       switch (new_state) {
+       case TUNER_OFF:
+               break;
+       case TUNER_NOTTUNED:
+               radio->low->fm_tuner_state.tune_done = FALSE;
+               fm_tuner_enable_rds(radio, FALSE);
+               break;
+       case TUNER_IDLE:
+       case TUNER_PRESET:
+       case TUNER_SEARCH:
+               fm_start_tune(radio, new_state);
+               break;
+       }
+}
+
+static void fm_cancel_delayed_work(struct s610_radio *radio)
+{
+       cancel_delayed_work(&radio->dwork_sig2);
+       cancel_delayed_work(&radio->dwork_tune);
+}
+
+static void fm_tuner_exit_state(struct s610_radio *radio)
+{
+       fm_cancel_delayed_work(radio);
+#ifndef        RDS_POLLING_ENABLE
+       fm_set_handler_if_count(NULL);
+       fm_set_handler_audio_pause(NULL);
+#endif /*RDS_POLLING_ENABLE*/
+       fm_set_rssi_thresh(radio, TUNER_IDLE);
+}
+
+void fm_set_tuner_mode(struct s610_radio *radio)
+{
+       u8 tm;
+       fm_tuner_state new_state;
+
+       API_ENTRY(radio);
+
+       if (!radio->low->fm_state.fm_pwr_on) {
+               fm_tuner_exit_state(radio);
+               fm_tuner_change_state(radio, TUNER_OFF);
+       } else {
+               tm = radio->low->fm_state.tuner_mode & TUNER_MODE_MASK_TUN_MOD;
+               new_state =
+                       radio->low->fm_tuner_state.tune_done ?
+                               TUNER_IDLE : TUNER_NOTTUNED;
+
+               switch (tm) {
+               case TUNER_MODE_PRESET:
+                       new_state = TUNER_PRESET;
+                       break;
+               case TUNER_MODE_SEARCH:
+                       new_state = TUNER_SEARCH;
+                       break;
+               case TUNER_MODE_NONE:
+               default:
+                       break;
+               }
+
+               fm_tuner_exit_state(radio);
+               fm_tuner_change_state(radio, new_state);
+       }
+
+       API_EXIT(radio);
+}
+
+static bool fm_tuner_on(struct s610_radio *radio)
+{
+       API_ENTRY(radio);
+
+       if (!fm_radio_on(radio)) {
+               radio->low->fm_state.fm_pwr_on =
+               radio->low->fm_state.rds_pwr_on =
+               FALSE;
+               fm_tuner_exit_state(radio);
+               fm_tuner_change_state(radio, TUNER_OFF);
+
+               return FALSE;
+       }
+
+       API_EXIT(radio);
+
+       return TRUE;
+}
+
+static void fm_tuner_off(struct s610_radio *radio)
+{
+       fm_radio_off(radio);
+}
+
+void fm_tuner_rds_on(struct s610_radio *radio)
+{
+       fm_rds_on(radio);
+
+       if (radio->low->fm_state.rds_rx_enabled)
+               fm_rds_enable(radio);
+}
+
+void fm_tuner_rds_off(struct s610_radio *radio)
+{
+       if (radio->low->fm_state.rds_rx_enabled)
+               fm_rds_disable(radio);
+
+       fm_rds_off(radio);
+}
+
+bool fm_tuner_set_power_state(struct s610_radio *radio, bool fm_on, bool rds_on)
+{
+
+       API_ENTRY(radio);
+
+       if (fm_on && !radio->low->fm_state.fm_pwr_on) {
+               fm_tuner_exit_state(radio);
+               fm_tuner_change_state(radio, TUNER_NOTTUNED);
+               fm_tuner_control_mute(radio);
+       } else if (!fm_on && radio->low->fm_state.fm_pwr_on) {
+               fm_tuner_exit_state(radio);
+               fm_tuner_change_state(radio, TUNER_OFF);
+       }
+
+       if (fm_on && !radio->low->fm_state.fm_pwr_on) {
+               if (!fm_tuner_on(radio))
+                       return FALSE;
+
+               radio->low->fm_state.fm_pwr_on = TRUE;
+       }
+
+       if (rds_on && !radio->low->fm_state.rds_pwr_on) {
+               if (radio->low->fm_state.fm_pwr_on) {
+                       fm_tuner_rds_on(radio);
+                       radio->low->fm_state.rds_pwr_on = TRUE;
+               }
+       } else if ((!rds_on || !fm_on) && radio->low->fm_state.rds_pwr_on) {
+               fm_tuner_rds_off(radio);
+               radio->low->fm_state.rds_pwr_on = FALSE;
+       }
+
+       if (!fm_on && radio->low->fm_state.fm_pwr_on) {
+               fm_tuner_off(radio);
+               radio->low->fm_state.fm_pwr_on = FALSE;
+               fm_tuner_enable_rds(radio, FALSE);
+       }
+
+       API_EXIT(radio);
+
+       return TRUE;
+}
+
+
+fm_conf_ini_values low_fm_conf_init = {
+               .demod_conf_ini = 0x228C,
+               .rssi_adj_ini = 0x006E,
+               .soft_muffle_conf_ini = { 0x2516, 1, 1, 7 },
+               .soft_mute_atten_max_ini = 0x0007,
+               .stereo_thres_ini = 0x00C8,
+               .narrow_thres_ini = 0x0074,
+               .snr_adj_ini = 0x001C,
+               .snr_smooth_conf_ini = 0x082F,
+               .mute_coeffs_soft = 0x2516,
+               .mute_coeffs_dis = 0x0000,
+               .blend_coeffs_soft = 0x095A,
+               .blend_coeffs_switch =  0x7D8C,
+               .blend_coeffs_dis = 0x00FF,
+               .rds_int_byte_count = RDS_MEM_MAX_THRESH,
+#ifdef USE_NEW_SCAN
+               /*.search_conf = { 4000, 5000, 7000, 3000, 3800, 5600, FALSE},*/
+               .search_conf = { 4500, 6000, 7000, 4000, 4800, 5600, FALSE},
+#else
+               .search_conf = { 4100, 4700, 5500 },
+#endif
+#ifdef MONO_SWITCH_INTERF
+               .interf_rssi = { -85, -75 },
+               .interf_snr = { 20, 43 },
+#endif
+               .rds_error_limit = 3
+};
+
+fm_state_s low_fm_state_init = {
+               .rds_rx_enabled = FALSE,
+               .fm_pwr_on = FALSE,
+               .rds_pwr_on = FALSE,
+               .force_mono = FALSE,
+               .use_switched_blend = FALSE,
+               .use_soft_mute = TRUE,
+               .mute_forced = FALSE,
+               .mute_audio = FALSE,
+               .search_down = FALSE,
+               .use_rbds = FALSE,
+               .save_eblks = FALSE,
+               .last_status_blend_stereo = FALSE,
+               .last_status_rds_sync = FALSE,
+#ifdef MONO_SWITCH_INTERF
+               .force_mono_interf = FALSE,
+               .interf_checked = FALSE,
+               .mono_switched_interf = FALSE,
+               .mono_interf_reset_time = 0,
+#endif
+               .tuner_mode = 0,
+               .status = 0,
+               .rds_mem_thresh = 0,
+               .rssi = 0,
+               .band = 0,
+               .last_ifc = 0,
+               .snr = 0,
+               .rssi_limit_normal = 0,
+               .rssi_limit_search = 0,
+               .freq = 0,
+               .flags = 0,
+               .rds_unsync_uncorr_weight = 10,
+               .rds_unsync_blk_cnt = 20,
+               .rds_unsync_bit_cnt = 48
+};
+
+fm_tuner_state_s low_fm_tuner_state_init = {
+               .tuner_state = TUNER_OFF,
+               .curr_search_down = FALSE,
+               .hit_band_limit = FALSE,
+               .tune_done = FALSE,
+               .freq_step = 100,
+               .band_limit_lo = 87500,
+               .band_limit_hi = 108000
+};
+
+fm_band_s fm_bands_init[] = { { 87500, 108000 }, { 76000, 90000 } };
+u16 fm_freq_steps_init[] = { 50, 100, 200 };
+#ifdef USE_SPUR_CANCEL
+extern u32 *fm_spur_init;
+#endif
+#ifdef USE_S612_DUAL_CLOCKING
+u32 filter_freq_spur_case_1[] = {
+               87900, 88000, 88100, 95900, 96000, 96100, 99900, 100100 };
+#endif /* USE_S612_DUAL_CLOCKING */
+
+#ifdef USE_SPUR_CANCEL_TRF
+u32 filter_freq_spur_select[] = {
+               98300, 100000, 104000 };
+#endif /* USE_SPUR_CANCEL_TRF */
+
+int init_low_struc(struct s610_radio *radio)
+{
+       memcpy(&radio->low->fm_config, &low_fm_conf_init,
+                       sizeof(fm_conf_ini_values));
+       memcpy(&radio->low->fm_state, &low_fm_state_init, sizeof(fm_state_s));
+       memcpy(&radio->low->fm_tuner_state, &low_fm_tuner_state_init,
+                       sizeof(fm_tuner_state_s));
+       memcpy(&radio->low->fm_bands, &fm_bands_init, sizeof(fm_band_s) * 2);
+       memcpy(&radio->low->fm_freq_steps,
+                       &fm_freq_steps_init, sizeof(u16) * 3);
+       if (radio->sw_mute_weak) {
+               radio->low->fm_config.soft_muffle_conf_ini.muffle_coeffs = 0x1B16;
+               radio->low->fm_config.mute_coeffs_soft = 0x1B16;
+       }
+#ifdef USE_SPUR_CANCEL
+       if (radio->tc_on)
+               memcpy(radio->low->fm_spur, fm_spur_init,
+                               sizeof(u32) * radio->tc_on);
+#endif
+#ifdef USE_S612_DUAL_CLOCKING
+       if ((!radio->dual_clk_on) && (radio->rfchip_ver == S620_REV_0)) {
+               memcpy(radio->low->fm_dual_clk, filter_freq_spur_case_1,
+                               sizeof(filter_freq_spur_case_1));
+               fm_dual_clk_init = radio->low->fm_dual_clk;
+               radio->dual_clk_on = sizeof(filter_freq_spur_case_1)/sizeof(u32);
+       }
+#endif /* USE_S612_DUAL_CLOCKING */
+#ifdef USE_SPUR_CANCEL_TRF
+       if ((!radio->trf_on) && (radio->rfchip_ver == S620_REV_0)) {
+               memcpy(radio->low->fm_spur_trf, filter_freq_spur_select,
+                               sizeof(filter_freq_spur_select));
+               fm_spur_trf_init = radio->low->fm_spur_trf;
+               radio->trf_on = sizeof(filter_freq_spur_select)/sizeof(u32);
+       }
+#endif /* USE_SPUR_CANCEL_TRF */
+
+       return 0;
+}
diff --git a/drivers/media/radio/s610/fm_low_ref.h b/drivers/media/radio/s610/fm_low_ref.h
new file mode 100644 (file)
index 0000000..c123824
--- /dev/null
@@ -0,0 +1,163 @@
+#ifndef FM_LOW_REF_H
+#define FM_LOW_REF_H
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+
+/******************************************************************************
+ *     definition
+ ******************************************************************************/
+#define ABS(a) (((a) < 0) ? -(a) : (a))
+
+int fm_boot(struct s610_radio *radio);
+void fm_power_off(void);
+u16 if_count_device_to_host(struct s610_radio *radio, u16 val);
+static u16 aggr_rssi_host_to_device(u8 val);
+u8 aggr_rssi_device_to_host(u16 val);
+u16 rssi_device_to_host(u16 digi_rssi, u16 agc_gain, u16 rssi_adj);
+#ifdef USE_SPUR_CANCEL
+void fm_rx_en_spur_removal(struct s610_radio *radio);
+void fm_rx_dis_spur_removal(void);
+void fm_rx_check_spur(struct s610_radio *radio);
+void fm_rx_check_spur_mono(struct s610_radio *radio);
+#endif
+void fm_set_freq(struct s610_radio *radio, u32 freq, bool mix_hi);
+void fm_set_mute(bool mute);
+void fm_set_blend_mute(struct s610_radio *radio);
+static void fm_rds_flush_buffers(struct s610_radio *radio,
+               bool clear_buffer);
+void fm_rds_enable(struct s610_radio *radio);
+void fm_rds_disable(struct s610_radio *radio);
+bool fm_radio_on(struct s610_radio *radio);
+void fm_radio_off(struct s610_radio *radio);
+void fm_rds_on(struct s610_radio *radio);
+void fm_rds_off(struct s610_radio *radio);
+void fm_initialize(struct s610_radio *radio);
+u16 fm_get_flags(struct s610_radio *radio);
+void fm_set_flags(struct s610_radio *radio, u16 flags);
+void fm_set_handler_if_count(void (*fn)(struct s610_radio *radio));
+void fm_set_handler_audio_pause(void (*fn)(struct s610_radio *radio));
+void fm_update_if_count(struct s610_radio *radio);
+void fm_update_if_count_int(struct s610_radio *radio);
+void fm_update_rssi(struct s610_radio *radio);
+void fm_update_rssi_work(struct s610_radio *radio);
+void fm_update_snr(struct s610_radio *radio);
+void fm_update_sig_info(struct s610_radio *radio);
+void fm_update_rds_sync_status(struct s610_radio *radio,
+       bool synced);
+u16 fm_update_rx_status(struct s610_radio *radio, u16 d_status);
+void fm_update_tuner_mode(struct s610_radio *radio);
+bool fm_check_rssi_level(u16 limit);
+int low_get_search_lvl(struct s610_radio *radio, u16 *value);
+int low_set_if_limit(struct s610_radio *radio, u16 value);
+int low_set_search_lvl(struct s610_radio *radio, u16 value);
+int low_set_freq(struct s610_radio *radio, u32 value);
+int low_set_tuner_mode(struct s610_radio *radio, u16 value);
+int low_set_mute_state(struct s610_radio *radio, u16 value);
+int low_set_most_mode(struct s610_radio *radio, u16 value);
+int low_set_most_blend(struct s610_radio *radio, u16 value);
+int low_set_pause_lvl(struct s610_radio *radio, u16 value);
+int low_set_pause_dur(struct s610_radio *radio, u16 value);
+int low_set_demph_mode(struct s610_radio *radio, u16 value);
+int low_set_rds_cntr(struct s610_radio *radio, u16 value);
+int low_set_power(struct s610_radio *radio, u16 value);
+
+void fm_set_interrupt_source(u16 sources, bool enable);
+void fm_isr(struct s610_radio *radio);
+void fm_rx_ana_start(void);
+void fm_rx_ana_stop(void);
+void fm_setup_iq_imbalance(void);
+void fm_rx_init(void);
+
+void fm_lo_off(void);
+void fm_lo_prepare_setup(struct s610_radio *radio);
+void fm_lo_set(const struct_fm_lo_setup lo_set);
+void fm_lo_initialize(struct s610_radio *radio);
+void fm_sx_reset(void);
+void fm_sx_start(void);
+bool fm_aux_pll_initialize(void);
+void fm_aux_pll_off(void);
+void fm_set_band(struct s610_radio *radio, u8 index);
+void fm_set_freq_step(struct s610_radio *radio, u8 index);
+bool fm_band_trim(struct s610_radio *radio, u32 *freq);
+void fm_get_if_filter_config(struct s610_radio *radio);
+static bool fm_tuner_push_freq(struct s610_radio *radio,
+       bool down);
+static void fm_tuner_enable_rds(struct s610_radio *radio,
+       bool enable);
+void fm_set_rssi_thresh(struct s610_radio *radio,
+       fm_tuner_state state);
+static void fm_tuner_control_mute(struct s610_radio *radio);
+void fm_tuner_set_force_mute(struct s610_radio *radio, bool mute);
+void fm_tuner_set_mute_audio(struct s610_radio *radio, bool mute);
+#ifdef MONO_SWITCH_INTERF
+void fm_check_interferer(struct s610_radio *radio);
+void fm_reset_force_mono_interf(struct s610_radio *radio);
+#endif
+void fm_timer_reset(fm_timer_t *timer, int usec,
+               fm_callback_t *func, void *arg);
+void fm_start_if_counter(void);
+static void fm_preset_tuned(struct s610_radio *radio);
+static void fm_search_done(struct s610_radio *radio, u16 flags);
+static void fm_search_check_signal2(unsigned long data);
+static void fm_search_check_signal1(struct s610_radio *radio, bool rssi_oor);
+static void fm_search_tuned(unsigned long data);
+static void fm_start_tune(struct s610_radio *radio,
+               fm_tuner_state new_state);
+static void fm_tuner_change_state(struct s610_radio *radio,
+               fm_tuner_state new_state);
+void cancel_tuner_timer(struct s610_radio *radio);
+static void fm_tuner_exit_state(struct s610_radio *radio);
+void fm_set_tuner_mode(struct s610_radio *radio);
+static bool fm_tuner_on(struct s610_radio *radio);
+static void fm_tuner_off(struct s610_radio *radio);
+void fm_tuner_rds_on(struct s610_radio *radio);
+void fm_tuner_rds_off(struct s610_radio *radio);
+bool fm_tuner_set_power_state(struct s610_radio *radio,
+               bool fm_on, bool rds_on);
+int init_low_struc(struct s610_radio *radio);
+void fm_iclkaux_set(u32 data);
+
+#ifdef ENABLE_RDS_WORK_QUEUE
+void s610_rds_work(struct work_struct *work);
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+#ifdef ENABLE_IF_WORK_QUEUE
+void s610_if_work(struct work_struct *work);
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+
+void s610_sig2_work(struct work_struct *work);
+void s610_tune_work(struct work_struct *work);
+
+#ifdef RDS_POLLING_ENABLE
+void fm_rds_periodic_update(unsigned long data);
+void fm_rds_periodic_cancel(unsigned long data);
+#endif /*RDS_POLLING_ENABLE*/
+#ifdef IDLE_POLLING_ENABLE
+void fm_idle_periodic_update(unsigned long data);
+void fm_idle_periodic_cancel(unsigned long data);
+#endif /*IDLE_POLLING_ENABLE*/
+void fm_set_audio_gain(struct s610_radio *radio, u16 gain);
+void fm_ds_set(u32 data);
+void fm_get_version_number(void);
+extern void fmspeedy_wakeup(void);
+extern void fm_pwron(void);
+extern void fm_pwroff(void);
+extern int fmspeedy_set_reg_field(
+       u32 addr, u32 shift, u32 mask, u32 data);
+extern int fmspeedy_set_reg_field_work(
+       u32 addr, u32 shift, u32 mask, u32 data);
+extern int fmspeedy_set_reg(u32 addr, u32 data);
+extern int fmspeedy_set_reg_work(u32 addr, u32 data);
+extern u32 fmspeedy_get_reg(u32 addr);
+extern u32 fmspeedy_get_reg_work(u32 addr);
+extern u32 fmspeedy_get_reg_field(u32 addr, u32 shift, u32 mask);
+extern u32 fmspeedy_get_reg_field_work(u32 addr, u32 shift, u32 mask);
+extern void fm_audio_control(struct s610_radio *radio,
+       bool audio_out, bool lr_switch,
+       u32 req_time, u32 audio_addr);
+extern int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
+               u16 *blocks);
+extern void fm_process_rds_data(struct s610_radio *radio);
+#endif /*FM_LOW_REF_H*/
diff --git a/drivers/media/radio/s610/fm_low_struc.h b/drivers/media/radio/s610/fm_low_struc.h
new file mode 100644 (file)
index 0000000..5106af5
--- /dev/null
@@ -0,0 +1,487 @@
+#ifndef FM_LOW_STRUC_H
+#define FM_LOW_STRUC_H
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#define S620_REV_0     (0x100000)
+
+#define USE_SPUR_CANCEL
+#undef USE_SPUR_CANCEL
+
+/* S612 Rev1 SPUR TRF Default define */
+#define USE_SPUR_CANCEL_TRF
+/*#undef USE_SPUR_CANCEL_TRF*/
+
+/* S612 Rev1 Dual clocking Default define */
+#define USE_S612_DUAL_CLOCKING
+/*#undef USE_S612_DUAL_CLOCKING*/
+
+#define USE_FILTER_SELECT_BY_FREQ
+#define MAX_FILTER_FREQ_NUM    6
+
+#define USE_NEW_SCAN
+#define        USE_RDS_HW_DECODER
+#undef USE_RDS_HW_DECODER
+
+#define USE_RINGBUFF_API
+/*#undef USE_RINGBUFF_API*/
+
+#define FM_LOW_DRV_DELAY_MS  1
+#define AGGR_RSSI_OFFSET (-114)
+#define RDS_VALID_THRESHOLD (140) /* -104dB */
+
+#undef  TRUE
+#define TRUE   (1)
+
+#undef  FALSE
+#define FALSE  (0)
+
+typedef u32 TIME;
+
+/* TIME constants. */
+
+#define        SECOND  HZ
+#define        IDLE_TIME_MS    (300)
+#define        RDS_POLL_DELAY_MS       (150)
+#define        TUNE_TIME_FAST_MS       (30)
+#define        TUNE_TIME_SLOW_MS       (60)
+#ifdef USE_NEW_SCAN
+#define        SEARCH_DELAY_MS (20)
+#else
+#define        SEARCH_DELAY_MS (15)
+#endif
+
+#define RSSI_REF_ENABLE 0x01
+#define FM_RDS_MEM_SIZE_PARSER 1000
+#define FM_RDS_MEM_SIZE        480
+#define RDS_PARSER_ENABLE 0x04
+#define FM_RADIO_RDS_PARSER_VER_CHECK 0x400
+
+#ifdef USE_SPUR_CANCEL
+#define EN_SPUR_REMOVAL        (0x0001)
+#define DIS_SPUR_REMOVAL_MONO      (0x0002)
+#endif
+
+#define RSSI_ADJUST_WITHOUT_ELNA_VALUE (68)
+#define SNR_OFF_RSSI_VALUE     (174)
+#define SNR_ON_RSSI_VALUE      (171)
+#define TRF_OFF_RSSI_VALUE     (202)
+#define TRF_ON_RSSI_VALUE      (199)
+#define SCAN_WEAK_SIG_THRESHOLD (-103)
+
+/******************************************************************************
+ *     DEFINITIONS AND MACROS
+ ******************************************************************************/
+/* == FM SPEEDY registers == */
+#define FM_SPEEDY_MA_BASE      (0x14840000)
+#define FMSPDY_CTL     (0x00000000)
+#define FMSPDY_STAT    (0x00000004)
+#define FMSPDY_DISR    (0x00000008)
+#define FMSPDY_INTR_MASK       (0x0000000C)
+#define FMSPDY_DATA    (0x00000010)
+#define FMSPDY_CMD     (0x00000014)
+#define FMSPDY_ERR_CNT (0x00000018)
+#define FMSPDY_MISC_STAT       (0x0000001C)
+#define FMSPDY_PRAMS   (0x00000020)
+#define FM_SLV_INT     (0x00000040)
+#define AUDIO_CTRL     (0x00000024)
+#define AUDIO_FIFO     (0x00000028)
+#define FM_SPEEDY_MA_SIZE      1024
+
+/* FMSPDY INT Mask bits */
+#define FM_SLV_INT_MASK_BIT    (5)
+
+/* FMSPDY Control Register Flags */
+#define SPDY_WAKEUP        (1<<23)
+
+/* FMSPDY Status Register Flags*/
+#define RX_FMT_ERR         (1<<4)
+#define RX_NO_STOP         (1<<3)
+#define RX_GLITCHED        (1<<2)
+#define RX_TIMEOUT         (1<<1)
+#define RX_ALL_ERR         (RX_FMT_ERR|RX_NO_STOP|RX_GLITCHED|RX_TIMEOUT)
+#define STAT_DONE          (1<<0)
+
+/* FMSPDY Command Register Flags*/
+#define FMSPDY_READ            (0<<17)
+#define FMSPDY_WRITE           (1<<17)
+#define FMSPDY_RANDOM          (1<<16)
+
+#define write32(addr, data) __raw_writel(data, addr)
+#define read32(addr) __raw_readl((volatile void __iomem *)addr)
+#define SetBits(uAddr, uBaseBit, uMaskValue, uSetValue) \
+               write32(uAddr, (read32(uAddr) & ~((uMaskValue)<<(uBaseBit))) | \
+                               (((uMaskValue)&(uSetValue))<<(uBaseBit)))
+#define GetBits(uAddr, uBaseBit, uMaskValue) \
+               ((read32(uAddr)>>(uBaseBit))&(uMaskValue))
+
+
+enum flags_enum {
+       FLAG_TUNED              = (1 << 0),
+       FLAG_BD_LMT             = (1 << 1),
+       FLAG_SYN_LOS    = (1 << 2),
+       FLAG_BUF_FUL    = (1 << 3),
+       FLAG_AUD_PAU    = (1 << 4),
+       FLAG_CH_STAT    = (1 << 5)
+};
+
+enum fm_status_mask_enum {
+       STATUS_MASK_RDS_AVA     = (1 << 0),
+       STATUS_MASK_STEREO      = (1 << 1)
+};
+
+enum fm_search_dir_mask_enum {
+       SEARCH_DIR_MASK = (1 << 0)
+};
+
+enum fm_tuner_mode_mask_enum {
+       TUNER_MODE_MASK_TUN_MOD = (7 << 0),
+       TUNER_MODE_MASK_NEXT    = (1 << 3)
+};
+
+enum fm_tuner_mode_enum {
+       TUNER_MODE_NONE                 = 0,
+       TUNER_MODE_PRESET               = 1,
+       TUNER_MODE_SEARCH               = 2
+};
+
+enum fm_mute_state_mask_enum {
+       MUTE_STATE_MASK_SOFT = (1 << 0),
+       MUTE_STATE_MASK_HARD = (1 << 1)
+};
+
+enum fm_output_mode_mask_enum {
+       MODE_MASK_MONO_STEREO = (1 << 0)
+};
+
+enum fm_blend_mode_mask_enum {
+       MODE_MASK_BLEND = (1 << 0)
+};
+
+enum fm_rds_ctrl_mask_enum {
+       RDS_CTRL_MASK_FLUSH             = (1 << 0),
+       RDS_CTRL_MASK_RESYNC    = (1 << 1)
+};
+
+enum fm_rds_system_mask_enum {
+       RDS_SYSTEM_MASK_RDS             = (1 << 0),
+       RDS_SYSTEM_MASK_EBLK    = (1 << 1)
+};
+
+enum fm_power_mask_enum {
+       PWR_MASK_FM             = (1 << 0),
+       PWR_MASK_RDS    = (1 << 1)
+};
+
+enum fm_deemph_mode_mask_enum {
+       MODE_MASK_DEEMPH = (1 << 0)
+};
+
+typedef enum {
+       HOST_RDS_ERRS_NONE              = 0,
+       HOST_RDS_ERRS_2CORR             = 1,
+       HOST_RDS_ERRS_5CORR             = 2,
+       HOST_RDS_ERRS_UNCORR    = 3
+} fm_host_rds_errors_enum;
+
+#define RDS_MEM_MAX_THRESH     (48)
+#define RDS_MEM_MAX_THRESH_PARSER      (100)
+
+enum fm_host_rds_data_enum {
+       HOST_RDS_DATA_BLKTYPE_POSI      = 0,
+       HOST_RDS_DATA_ERRORS_POSI       = 3,
+       HOST_RDS_DATA_AVAIL_MASK        = (1 << 5)
+};
+
+#define HOST_RDS_BLOCK_SIZE                    3
+#define HOST_RDS_BLOCK_FMT_LSB         0
+#define HOST_RDS_BLOCK_FMT_MSB         1
+#define HOST_RDS_BLOCK_FMT_STATUS      2
+
+enum fm_demod_stat_mask_enum {
+       FM_DEMOD_BLEND_STEREO_MASK = (0x0001 << 4),
+       FM_DEMOD_IF_OOR_MASK = (0x0001 << 7)
+};
+
+enum fm_int_src_mask_enum {
+       INT_IFC_READY_MASK = (0x0001 << 0),
+       INT_AUDIO_PAU_MASK = (0x0001 << 3),
+       INT_RDS_BYTES_MASK = (0x0001 << 4),
+};
+
+enum fm_audio_gain_enum {
+       AUDIO_ATTENUATION_Max           = 0,
+       AUDIO_ATTENUATION_42dB          = 1,
+       AUDIO_ATTENUATION_39dB          = 2,
+       AUDIO_ATTENUATION_36dB          = 3,
+       AUDIO_ATTENUATION_33dB          = 4,
+       AUDIO_ATTENUATION_30dB          = 5,
+       AUDIO_ATTENUATION_27dB          = 6,
+       AUDIO_ATTENUATION_24dB          = 7,
+       AUDIO_ATTENUATION_21dB          = 8,
+       AUDIO_ATTENUATION_18dB          = 9,
+       AUDIO_ATTENUATION_15dB          = 10,
+       AUDIO_ATTENUATION_12dB          = 11,
+       AUDIO_ATTENUATION_9dB           = 12,
+       AUDIO_ATTENUATION_6dB           = 13,
+       AUDIO_ATTENUATION_3dB           = 14,
+       AUDIO_ATTENUATION_0dB           = 15
+};
+
+/***************************************************************************/
+
+typedef struct {
+       u16 muffle_coeffs;
+       u16 lpf_en;
+       u16 lpf_auto;
+       u16 lpf_bw;
+} soft_muffle_conf;
+
+typedef struct {
+#ifdef USE_NEW_SCAN
+       u16 weak_ifca_l;
+       u16 weak_ifca_m;
+       u16 weak_ifca_h;
+       u16 normal_ifca_l;
+       u16 normal_ifca_m;
+       u16 normal_ifca_h;
+       bool weak_sig;
+#else
+       u16 ifca_l;
+       u16 ifca_m;
+       u16 ifca_h;
+#endif
+} search_config;
+
+#ifdef MONO_SWITCH_INTERF
+typedef struct {
+       s16 lo;
+       s16 hi;
+} interf_rssi_thres;
+
+typedef struct {
+       u16 lo;
+       u16 hi;
+} interf_snr_thres;
+#endif /* MONO_SWITCH_INTERF */
+
+typedef struct {
+       u32 demod_conf_ini;
+       u16 rssi_adj_ini;
+       soft_muffle_conf soft_muffle_conf_ini;
+       u16 soft_mute_atten_max_ini;
+       u16 stereo_thres_ini;
+       u16 narrow_thres_ini;
+       u16 snr_adj_ini;
+       u16 snr_smooth_conf_ini;
+       u16 mute_coeffs_soft;
+       u16 mute_coeffs_dis;
+       u16 blend_coeffs_soft;
+       u16 blend_coeffs_switch;
+       u16 blend_coeffs_dis;
+       u16 rds_int_byte_count;
+       search_config search_conf;
+#ifdef MONO_SWITCH_INTERF
+       interf_rssi_thres interf_rssi;
+       interf_snr_thres interf_snr;
+#endif
+       u16 rds_error_limit;
+} fm_conf_ini_values;
+
+/***************************************************************************/
+
+typedef struct {
+       bool rds_rx_enabled;
+       bool fm_pwr_on;
+       bool rds_pwr_on;
+       bool force_mono;
+       bool use_switched_blend;
+       bool use_soft_mute;
+       bool mute_forced;
+       bool mute_audio;
+       bool search_down;
+       bool use_rbds;
+       bool save_eblks;
+       bool last_status_blend_stereo;
+       bool last_status_rds_sync;
+#ifdef MONO_SWITCH_INTERF
+       bool force_mono_interf;
+       bool interf_checked;
+       bool mono_switched_interf;
+       TIME mono_interf_reset_time;
+#endif /* MONO_SWITCH_INTERF */
+       u8 tuner_mode;
+       u8 status;
+       u8 rds_mem_thresh;
+       u8 rssi;
+       u8 band;
+       u16 last_ifc;
+       u16 snr;
+       u16 rssi_limit_normal;
+       u16 rssi_limit_search;
+       u32 freq;
+       u16 flags;
+       u8 rds_unsync_uncorr_weight;
+       u8 rds_unsync_blk_cnt;
+       u16 rds_unsync_bit_cnt;
+} fm_state_s;
+
+typedef enum {
+       TUNER_OFF,
+       TUNER_NOTTUNED,
+       TUNER_IDLE,
+       TUNER_PRESET,
+       TUNER_SEARCH
+} fm_tuner_state;
+
+/***************************************************************************/
+
+typedef struct {
+       fm_tuner_state tuner_state;
+       bool curr_search_down;
+       bool hit_band_limit;
+       bool tune_done;
+       u16 freq_step;
+       u32 band_limit_lo;
+       u32 band_limit_hi;
+} fm_tuner_state_s;
+
+typedef enum {
+       RDS_RM_ALIGN_0 = 0,
+       RDS_RM_ALIGN_1 = 1,
+       RDS_RM_ALIGN_2 = 2,
+       RDS_RM_ALIGN_3 = 3,
+       RDS_RM_ALIGN_NONE = 4
+} fm_rds_rm_align_enum;
+
+#ifdef USE_RDS_HW_DECODER
+typedef enum {
+       RDS_BLKTYPE_A   = 0,
+       RDS_BLKTYPE_B   = 1,
+       RDS_BLKTYPE_C   = 2,
+       RDS_BLKTYPE_C2  = 3,
+       RDS_BLKTYPE_D   = 4,
+       RDS_BLKTYPE_E   = 5,
+       RDS_NUM_BLOCK_TYPES = 6
+} fm_rds_block_type_enum;
+
+typedef enum {
+       RDS_STATE_INIT,
+       RDS_STATE_HAVE_DATA,
+       RDS_STATE_PRE_SYNC,
+       RDS_STATE_FULL_SYNC
+} fm_rds_state_enum;
+#else
+typedef enum {
+       RDS_BLKTYPE_A   = 0,
+       RDS_BLKTYPE_B   = 1,
+       RDS_BLKTYPE_C   = 2,
+       RDS_BLKTYPE_D   = 3,
+       RDS_BLKTYPE_E   = 4,
+       RDS_NUM_BLOCK_TYPES = 5
+} fm_rds_block_type_enum;
+
+typedef enum {
+       RDS_STATE_FOUND_BL_A,
+       RDS_STATE_FOUND_BL_B,
+       RDS_STATE_FOUND_BL_C,
+       RDS_STATE_FOUND_BL_D,
+       RDS_STATE_HAVE_DATA,
+       RDS_STATE_PRE_SYNC,
+       RDS_STATE_FULL_SYNC,
+       RDS_STATE_INIT,
+} fm_rds_state_enum;
+#endif /*USE_RDS_HW_DECODER*/
+
+typedef struct {
+       unsigned current_state :3;
+       u16 error_bits;
+       u8 error_blks;
+} fm_rds_state_s;
+
+typedef struct rds_buf_conf {
+       u8 *base;
+       u16 index;
+       u16 outdex;
+       u16 size;
+} rds_buf_conf;
+
+
+/****************************************************************************/
+
+typedef struct struct_fm_rx_setup {
+       u32 fm_freq_hz;
+       u32 fm_freq_khz;
+       u16 demod_if;
+       s16 lna_cdac;
+       u16 spur_ctrl;
+       s16 spur_freq;
+} struct_fm_rx_setup;
+
+typedef struct struct_fm_lo_setup {
+       u32 rx_lo_req_freq;
+       u32 n_mmdiv;
+       u32 frac_b1;
+       u32 frac_b0;
+       u32 n_lodiv;
+} struct_fm_lo_setup;
+
+ typedef struct struct_fm_rx_tune_info {
+       struct_fm_rx_setup rx_setup;
+       struct_fm_lo_setup lo_setup;
+} struct_fm_rx_tune_info;
+
+/**********************************************/
+/* FM low struct
+ **********************************************/
+typedef struct {
+       u32 lo;
+       u32 hi;
+} fm_band_s;
+
+struct s610_low {
+       /* fm low level struct - start */
+       fm_conf_ini_values fm_config;
+       fm_state_s fm_state;
+       fm_tuner_state_s fm_tuner_state;
+       fm_rds_state_s fm_rds_state;
+
+       u8 *rds_buffer_mem;
+       rds_buf_conf rds_buffer_config;
+       rds_buf_conf *rds_buffer;
+
+       struct_fm_rx_tune_info fm_tune_info;
+
+       fm_band_s fm_bands[2];
+
+       u16 fm_freq_steps[3];
+
+#ifdef USE_SPUR_CANCEL
+       u32 fm_spur[256];
+#endif
+#ifdef USE_SPUR_CANCEL_TRF
+       u32 fm_spur_trf[256];
+#endif
+#ifdef USE_S612_DUAL_CLOCKING
+       u32 fm_dual_clk[256];
+#endif
+       /* fm low level struct  -  end */
+};
+
+typedef void fm_callback_t(unsigned long);
+typedef struct timer_list fm_timer_t;
+typedef void fm_linux_callback_t(u_long);
+
+#define fm_set_flag_bits(radio, value) \
+               fm_set_flags(radio, radio->low->fm_state.flags | (value))
+#define fm_clear_flag_bits(radio, value) \
+               fm_set_flags(radio, radio->low->fm_state.flags & ~(value))
+#define fm_get_band(radio) (radio->low->fm_state.band)
+#define fm_get_freq_step(radio) (radio->low->fm_tuner_state.freq_step)
+
+
+#endif /*FM_LOW_STRUC_H*/
diff --git a/drivers/media/radio/s610/fm_rds.c b/drivers/media/radio/s610/fm_rds.c
new file mode 100644 (file)
index 0000000..539dea5
--- /dev/null
@@ -0,0 +1,1553 @@
+
+#include "fm_low_struc.h"
+#include "radio-s610.h"
+#include "fm_rds.h"
+extern struct s610_radio *gradio;
+
+#ifdef USE_RINGBUFF_API
+void ringbuf_reset(struct ringbuf_t *rb)
+{
+       rb->head = rb->tail = rb->buf;
+}
+
+int ringbuf_buffer_size(const struct ringbuf_t *rb)
+{
+       return rb->size;
+}
+
+int ringbuf_capacity(const struct ringbuf_t *rb)
+{
+       return ringbuf_buffer_size(rb) - 1;
+}
+
+static const u8 *ringbuf_end(const struct ringbuf_t *rb)
+{
+       return rb->buf + ringbuf_buffer_size(rb);
+}
+
+int ringbuf_bytes_free(const struct ringbuf_t *rb)
+{
+       if (rb->head >= rb->tail)
+               return ringbuf_capacity(rb) - (rb->head - rb->tail);
+       else
+               return rb->tail - rb->head - 1;
+}
+
+int ringbuf_bytes_used(const struct ringbuf_t *rb)
+{
+       return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
+}
+
+int ringbuf_is_full(const struct ringbuf_t *rb)
+{
+       return ringbuf_bytes_free(rb) == 0;
+}
+
+int ringbuf_is_empty(const struct ringbuf_t *rb)
+{
+       return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
+}
+
+const void *ringbuf_tail(const struct ringbuf_t *rb)
+{
+       return rb->tail;
+}
+
+const void *ringbuf_head(const struct ringbuf_t *rb)
+{
+       return rb->head;
+}
+
+static u8 *ringbuf_nextp(struct ringbuf_t *rb, const u8 *p)
+{
+       /*
+        * The assert guarantees the expression (++p - rb->buf) is
+        * non-negative; therefore, the modulus operation is safe and
+        * portable.
+        */
+       return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));
+}
+
+void *ringbuf_memcpy_into(struct ringbuf_t *dst, const void *src, int count)
+{
+       const u8 *u8src = src;
+       const u8 *bufend = ringbuf_end(dst);
+       int overflow = count > ringbuf_bytes_free(dst);
+       int nread = 0;
+       int n = 0;
+
+       while (nread != count) {
+               n = MIN(bufend - dst->head, count - nread);
+               memcpy(dst->head, u8src + nread, n);
+               dst->head += n;
+               nread += n;
+
+               /* wrap? */
+               if (dst->head == bufend)
+                       dst->head = dst->buf;
+       }
+
+       if (overflow)
+               dst->tail = ringbuf_nextp(dst, dst->head);
+
+       return dst->head;
+}
+
+void *ringbuf_memcpy_from(void *dst, struct ringbuf_t *src, int count)
+{
+       int n = 0;
+       int bytes_used = ringbuf_bytes_used(src);
+       u8 *u8dst = dst;
+       const u8 *bufend = ringbuf_end(src);
+       int nwritten = 0;
+
+       if (count > bytes_used)
+               return 0;
+
+       while (nwritten != count) {
+               n = MIN(bufend - src->tail, count - nwritten);
+               memcpy(u8dst + nwritten, src->tail, n);
+               src->tail += n;
+               nwritten += n;
+
+               /* wrap ? */
+               if (src->tail == bufend)
+                       src->tail = src->buf;
+       }
+
+       return src->tail;
+}
+
+void * ringbuf_memcpy_remove(struct ringbuf_t *dst, int count)
+{
+       int n = 0;
+       int bytes_used = ringbuf_bytes_used(dst);
+       const u8 *bufend = ringbuf_end(dst);
+       int nwritten = 0;
+       unsigned char *cls_start;
+
+       if (count > bytes_used)
+               return 0;
+
+       while (nwritten != count) {
+               n = MIN(bufend - dst->head, count - nwritten);
+               cls_start = dst->head - n;
+               memset(cls_start, 0, n);
+               dst->head -= n;
+               nwritten += n;
+
+               /* wrap ? */
+               if (dst->head == bufend)
+                       dst->head = dst->buf;
+       }
+
+       return dst->head;
+}
+#endif /* USE_RINGBUFF_API */
+
+#ifdef USE_RINGBUFF_API
+void fm_rds_write_data(struct s610_radio *radio,
+               u16 rds_data, fm_rds_block_type_enum blk_type,
+               fm_host_rds_errors_enum errors)
+{
+       u8 buf_ptr[HOST_RDS_BLOCK_SIZE];
+       u16 usage;
+
+       if (ringbuf_is_full(&radio->rds_rb)) {
+               dev_info(radio->dev, "%s():>>>RB full! H[%ld]T[%ld]",
+                       __func__,
+                       (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
+                       (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
+
+               goto skip_into_buf;
+       }
+
+       buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff);
+       buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8)(rds_data >> 8);
+       buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] =
+                       (blk_type << HOST_RDS_DATA_BLKTYPE_POSI)
+                       | (errors << HOST_RDS_DATA_ERRORS_POSI)
+                       | HOST_RDS_DATA_AVAIL_MASK;
+
+       if (!ringbuf_memcpy_into(&radio->rds_rb, buf_ptr, HOST_RDS_BLOCK_SIZE)) {
+               usage = ringbuf_bytes_used(&radio->rds_rb);
+               dev_info(radio->dev,
+                       "%s():>>>RB memcpy into fail! usage:%04d",
+                       __func__, ringbuf_bytes_used(&radio->rds_rb));
+               if (!usage)
+                       return;
+       }
+
+skip_into_buf:
+       usage = ringbuf_bytes_used(&radio->rds_rb);
+
+       if (usage >= HOST_RDS_BLOCK_SIZE)
+               radio->low->fm_state.status |= STATUS_MASK_RDS_AVA;
+
+       if (radio->low->fm_state.rds_mem_thresh != 0) {
+               if (usage >= (radio->low->fm_state.rds_mem_thresh+(HOST_RDS_BLOCK_SIZE*4))) {
+                       if (atomic_read(&radio->is_rds_new))
+                               return;
+
+                       fm_set_flag_bits(radio, FLAG_BUF_FUL);
+                       atomic_set(&radio->is_rds_new, 1);
+                       wake_up_interruptible(&radio->core->rds_read_queue);
+                       radio->rds_n_count++;
+
+                       if (!(radio->rds_n_count%200)) {
+                               fm_update_rssi_work(radio);
+                               dev_info(radio->dev,
+                                       ">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]",
+                                       radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt,
+                                       usage, radio->rds_sync_loss_cnt);
+                       }
+               }
+       }
+}
+#else  /* USE_RINGBUFF_API */
+void fm_rds_write_data(struct s610_radio *radio, u16 rds_data,
+       fm_rds_block_type_enum blk_type, fm_host_rds_errors_enum errors)
+{
+       u8 *buf_ptr;
+       u16 usage;
+       u16 capa;
+
+       capa = radio->low->rds_buffer->size;
+       if (radio->low->rds_buffer->outdex
+                       > radio->low->rds_buffer->index)
+               usage = radio->low->rds_buffer->size
+                       - radio->low->rds_buffer->outdex
+                       + radio->low->rds_buffer->index;
+       else
+               usage = radio->low->rds_buffer->index
+                       - radio->low->rds_buffer->outdex;
+
+       if ((capa - usage) >= (HOST_RDS_BLOCK_SIZE * 4)) {
+               buf_ptr = radio->low->rds_buffer->base
+                               + radio->low->rds_buffer->index;
+
+               buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff);
+               buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8) (rds_data >> 8);
+               buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] = (blk_type
+                               << HOST_RDS_DATA_BLKTYPE_POSI)
+                               | (errors << HOST_RDS_DATA_ERRORS_POSI)
+                               | HOST_RDS_DATA_AVAIL_MASK;
+
+               /* Advances the buffer's index */
+               radio->low->rds_buffer->index
+                       += HOST_RDS_BLOCK_SIZE;
+
+               /* Check if the buffer's index wraps */
+               if (radio->low->rds_buffer->index >=
+                               radio->low->rds_buffer->size) {
+                       radio->low->rds_buffer->index -=
+                               radio->low->rds_buffer->size;
+               }
+
+               if (usage >= HOST_RDS_BLOCK_SIZE)
+                       radio->low->fm_state.status     |= STATUS_MASK_RDS_AVA;
+
+       }
+
+       if (radio->low->fm_state.rds_mem_thresh != 0) {
+               if (usage >= (radio->low->fm_state.rds_mem_thresh+(HOST_RDS_BLOCK_SIZE*4)
+                                       /** HOST_RDS_BLOCK_SIZE*/)) {
+                       if (atomic_read(&radio->is_rds_new))
+                               return;
+
+                       fm_set_flag_bits(radio, FLAG_BUF_FUL);
+                       atomic_set(&radio->is_rds_new, 1);
+                       wake_up_interruptible(&radio->core->rds_read_queue);
+                       radio->rds_n_count++;
+
+                       if (!(radio->rds_n_count%200)) {
+                               fm_update_rssi_work(radio);
+                               dev_info(radio->dev,
+                                       ">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]",
+                                       radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt,
+                                       usage, radio->rds_sync_loss_cnt);
+                       }
+               }
+       }
+}
+#endif /* USE_RINGBUFF_API */
+
+#ifdef USE_RDS_BLOCK_SEQ_CORRECT
+#ifdef USE_RINGBUFF_API
+void fm_rds_write_data_remove(struct s610_radio *radio,
+       fm_rds_rm_align_enum removeblock)
+{
+       unsigned long pre_head, cur_head;
+       pre_head = (unsigned long) radio->rds_rb.head;
+       ringbuf_memcpy_remove(&radio->rds_rb,
+               (int)removeblock*HOST_RDS_BLOCK_SIZE);
+       cur_head = (unsigned long) radio->rds_rb.head;
+       dev_info(radio->dev, ">>> pre-head :%08lX, cur-head :%08lX\n",
+               pre_head, cur_head);
+}
+#else
+void fm_rds_write_data_remove(struct s610_radio *radio,
+       fm_rds_rm_align_enum removeblock)
+{
+       int i;
+       u8 *buf_ptr;
+
+       for (i = 0; i < removeblock*HOST_RDS_BLOCK_SIZE; i++) {
+               buf_ptr = radio->low->rds_buffer->base
+                       + radio->low->rds_buffer->index;
+               buf_ptr[0] = 0;
+               if (radio->low->rds_buffer->index == 0)
+                       radio->low->rds_buffer->index = radio->low->rds_buffer->size;
+               radio->low->rds_buffer->index--;
+       }
+       RDSEBUG(radio, "%s():<<<WR-RM:%d index[%04d]",
+                __func__, removeblock,
+               radio->low->rds_buffer->index);
+
+}
+#endif /*USE_RINGBUFF_API*/
+#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
+
+#ifdef USE_RINGBUFF_API
+int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
+               u16 *blocks)
+{
+       u16 rsize = size;
+
+       if (ringbuf_is_empty(&radio->rds_rb)) {
+               dev_info(radio->dev,
+                       "%s():>>>RB empty!! H[%04ld]T[%04ld]",
+                       __func__,
+                       (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
+                       (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
+
+               return 0;
+       }
+       radio->rb_used = ringbuf_bytes_used(&radio->rds_rb);
+       if (!ringbuf_memcpy_from(buffer, &radio->rds_rb, rsize)) {
+               dev_info(radio->dev,
+                       "%s():>>>RB memcpy from fail! H[%04ld]T[%04ld]",
+                       __func__,
+                       (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
+                       (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
+
+               /* ringbuff reset */
+               ringbuf_reset(&radio->rds_rb);
+               if (blocks)
+                       blocks = 0;
+               return 0;
+       }
+
+       if (blocks)
+               *blocks = rsize / HOST_RDS_BLOCK_SIZE;
+
+       /* Update RDS flags */
+       if ((rsize / HOST_RDS_BLOCK_SIZE) < radio->low->fm_state.rds_mem_thresh)
+               fm_clear_flag_bits(radio, FLAG_BUF_FUL);
+
+       RDSEBUG(radio,
+               "%s():>>>RB1 H[%04ld]T[%04ld]",
+               __func__,
+               (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
+               (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
+
+       return rsize;
+}
+#else  /* USE_RINGBUFF_API */
+u16 fm_rds_get_avail_bytes(struct s610_radio *radio)
+{
+       u16 avail_bytes;
+
+       if (radio->low->rds_buffer->outdex >
+                       radio->low->rds_buffer->index)
+               avail_bytes = (radio->low->rds_buffer->size
+                               - radio->low->rds_buffer->outdex
+                               + radio->low->rds_buffer->index);
+       else
+               avail_bytes = (radio->low->rds_buffer->index
+                               - radio->low->rds_buffer->outdex);
+
+       return avail_bytes;
+}
+
+
+int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
+               u16 *blocks)
+{
+       u16 avail_bytes;
+       s16 avail_blocks;
+       s16 orig_avail;
+       u8 *buf_ptr;
+
+       if (radio->low->rds_buffer == NULL) {
+               size = 0;
+               if (blocks)
+                       *blocks = 0;
+               return FALSE;
+       }
+
+       orig_avail = avail_bytes = fm_rds_get_avail_bytes(radio);
+
+       if (avail_bytes > size)
+               avail_bytes = size;
+
+       avail_blocks = avail_bytes / HOST_RDS_BLOCK_SIZE;
+       avail_bytes = avail_blocks * HOST_RDS_BLOCK_SIZE;
+
+       if (avail_bytes == 0) {
+               size = 0;
+               if (blocks)
+                       *blocks = 0;
+               return FALSE;
+       }
+
+       buf_ptr = radio->low->rds_buffer->base
+               + radio->low->rds_buffer->outdex;
+       (void) memcpy(buffer, buf_ptr, avail_bytes);
+
+       /* advances the buffer's outdex */
+       radio->low->rds_buffer->outdex += avail_bytes;
+
+       /* Check if the buffer's outdex wraps */
+       if (radio->low->rds_buffer->outdex >= radio->low->rds_buffer->size)
+               radio->low->rds_buffer->outdex -= radio->low->rds_buffer->size;
+
+       if (orig_avail == avail_bytes) {
+               buffer[(avail_blocks - 1) * HOST_RDS_BLOCK_SIZE
+                          + HOST_RDS_BLOCK_FMT_STATUS] &=
+                                          ~HOST_RDS_DATA_AVAIL_MASK;
+               radio->low->fm_state.status &= ~STATUS_MASK_RDS_AVA;
+       }
+
+       size = avail_bytes; /* number of bytes read */
+
+       if (blocks)
+               *blocks = avail_bytes / HOST_RDS_BLOCK_SIZE;
+
+       /* Update RDS flags */
+       if ((avail_bytes / HOST_RDS_BLOCK_SIZE)
+                       < radio->low->fm_state.rds_mem_thresh)
+               fm_clear_flag_bits(radio, FLAG_BUF_FUL);
+
+       return size;
+}
+#endif /* USE_RINGBUFF_API */
+
+#ifdef USE_RDS_HW_DECODER
+void fm_rds_change_state(struct s610_radio *radio,
+               fm_rds_state_enum new_state)
+{
+       fm_rds_state_enum old_state =
+               (fm_rds_state_enum) radio->low->fm_rds_state.current_state;
+
+       radio->low->fm_rds_state.current_state = new_state;
+
+       if ((old_state == RDS_STATE_FULL_SYNC)
+                       && (new_state == RDS_STATE_HAVE_DATA)) {
+               fm_update_rds_sync_status(radio, FALSE); /* unsynced */
+       } else if ((old_state != RDS_STATE_FULL_SYNC)
+                       && (new_state == RDS_STATE_FULL_SYNC)) {
+               fm_update_rds_sync_status(radio, TRUE); /* synced */
+       }
+}
+#else  /*USE_RDS_HW_DECODER*/
+void fm_rds_change_state(struct s610_radio *radio,
+               fm_rds_state_enum new_state)
+{
+       radio->low->fm_rds_state.current_state = new_state;
+}
+
+fm_rds_state_enum fm_rds_get_state(struct s610_radio *radio)
+{
+       return radio->low->fm_rds_state.current_state;
+}
+#endif /*USE_RDS_HW_DECODER*/
+
+void fm_rds_update_error_status(struct s610_radio *radio, u16 errors)
+{
+       if (errors == 0) {
+               radio->low->fm_rds_state.error_bits = 0;
+               radio->low->fm_rds_state.error_blks = 0;
+       } else {
+               radio->low->fm_rds_state.error_bits += errors;
+               radio->low->fm_rds_state.error_blks++;
+       }
+
+       if (radio->low->fm_rds_state.error_blks
+                       >= radio->low->fm_state.rds_unsync_blk_cnt) {
+               if (radio->low->fm_rds_state.error_bits
+                       >= radio->low->fm_state.rds_unsync_bit_cnt) {
+                       /* sync-loss */
+                       fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
+                       RDSEBUG(radio, "%s() >>>>> RDS sync-loss[%08d]!!!!!",
+                               __func__, radio->rds_sync_loss_cnt);
+
+#ifdef USE_RDS_HW_DECODER
+                       fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
+#else
+                       if (!radio->rds_sync_loss_cnt) {
+#ifdef USE_RINGBUFF_API
+                               ringbuf_reset(&radio->rds_rb);
+#else
+                               radio->low->rds_buffer->index = radio->low->rds_buffer->outdex = 0;
+#endif
+                       } else {
+                               /*remove data*/
+                               fm_rds_write_data_remove(radio, fm_rds_get_state(radio));
+                       }
+                       fm_rds_change_state(radio, RDS_STATE_INIT);
+#endif /*USE_RDS_HW_DECODER*/
+               }
+               radio->low->fm_rds_state.error_bits = 0;
+               radio->low->fm_rds_state.error_blks = 0;
+               radio->rds_sync_loss_cnt++;
+       }
+}
+
+static fm_host_rds_errors_enum fm_rds_process_block(
+               struct s610_radio *radio,
+               u16 data, fm_rds_block_type_enum block_type,
+               u16 err_count)
+{
+       fm_host_rds_errors_enum error_type;
+       struct fm_rds_parser_info *pi;
+
+       if (radio->rds_parser_enable)
+               pi = &(radio->pi);
+
+       if (err_count == 0) {
+               error_type = HOST_RDS_ERRS_NONE;
+       } else if ((err_count <= 2)
+                       && (err_count
+                       <= radio->low->fm_config.rds_error_limit)) {
+               error_type = HOST_RDS_ERRS_2CORR;
+       } else if ((err_count <= 5)
+                       && (err_count
+                       <= radio->low->fm_config.rds_error_limit)) {
+               error_type = HOST_RDS_ERRS_5CORR;
+       } else {
+               error_type = HOST_RDS_ERRS_UNCORR;
+       }
+
+       /* Write the data into the buffer */
+       if ((block_type != RDS_BLKTYPE_E)
+               || (radio->low->fm_state.save_eblks)) {
+               if (radio->rds_parser_enable) {
+                       fm_rds_parser(pi, data, block_type, error_type);
+                       fm_rds_write_data_pi(radio, pi);
+               } else {
+               fm_rds_write_data(radio, data, block_type, error_type);
+       }
+       }
+
+       return error_type;
+}
+
+#ifdef USE_RDS_BLOCK_SEQ_CORRECT
+fm_rds_rm_align_enum fm_check_block_seq(fm_rds_block_type_enum pre_block_type,
+       fm_rds_block_type_enum curr_block_type)
+{
+       fm_rds_rm_align_enum ret = RDS_RM_ALIGN_NONE;
+
+       if ((pre_block_type == RDS_BLKTYPE_A) && (curr_block_type != RDS_BLKTYPE_B)) {
+               ret = RDS_RM_ALIGN_1;
+       }
+       else if ((pre_block_type == RDS_BLKTYPE_B) && (curr_block_type != RDS_BLKTYPE_C)) {
+               ret = RDS_RM_ALIGN_2;
+       }
+       else if ((pre_block_type == RDS_BLKTYPE_C) && (curr_block_type != RDS_BLKTYPE_D)) {
+               ret = RDS_RM_ALIGN_3;
+       }
+       else if ((pre_block_type == RDS_BLKTYPE_D) && (curr_block_type != RDS_BLKTYPE_A)) {
+               ret = RDS_RM_ALIGN_0;
+       }
+       return ret;
+}
+#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
+
+#ifdef USE_RDS_HW_DECODER
+void fm_process_rds_data(struct s610_radio *radio)
+{
+       u32 fifo_data;
+       u16 i;
+       u16 avail_blocks;
+       u16 data;
+       u8 status;
+       u16 err_count;
+       fm_rds_block_type_enum block_type;
+#ifdef USE_RDS_BLOCK_SEQ_CORRECT
+       fm_rds_rm_align_enum rm_blk = RDS_RM_ALIGN_NONE;
+#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
+
+       API_ENTRY(radio);
+
+       if (!radio->low->fm_state.rds_rx_enabled)
+               return;
+
+       avail_blocks = RDS_MEM_MAX_THRESH/4;
+
+       for (i = 0; i < avail_blocks; i++) {
+               /* Fetch the RDS word data. */
+               atomic_set(&radio->is_rds_doing, 1);
+               fifo_data = fmspeedy_get_reg_work(0xFFF3C0);
+               radio->rds_fifo_rd_cnt++;
+               data = (u16)((fifo_data >> 16) & 0xFFFF);
+               status = (u8)((fifo_data >> 8) & 0xFF);
+               block_type =
+                       (fm_rds_block_type_enum) ((status & RDS_BLK_TYPE_MASK)
+                               >> RDS_BLK_TYPE_SHIFT);
+               err_count = (status & RDS_ERR_CNT_MASK);
+               atomic_set(&radio->is_rds_doing, 0);
+
+               switch (radio->low->fm_rds_state.current_state) {
+               case RDS_STATE_INIT:
+                       APIEBUG(radio, "RDS_STATE_INIT");
+                       fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
+               case RDS_STATE_HAVE_DATA:
+                       APIEBUG(radio, "RDS_STATE_HAVE_DATA");
+                       if ((block_type == RDS_BLKTYPE_A)
+                                       && (err_count == 0)) {
+#ifdef USE_RDS_BLOCK_SEQ_CORRECT
+                               radio->block_seq = RDS_BLKTYPE_A;
+#endif /*USE_RDS_BLOCK_SEQ_CORRECT */
+                               /* Move to full sync */
+                               fm_rds_change_state(radio,
+                                               RDS_STATE_FULL_SYNC);
+                               fm_rds_process_block(radio,
+                                               data, block_type, err_count);
+                       }
+                       break;
+               case RDS_STATE_PRE_SYNC:
+                       break;
+               case RDS_STATE_FULL_SYNC:
+                       APIEBUG(radio, "RDS_STATE_FULL_SYNC");
+#ifdef USE_RDS_BLOCK_SEQ_CORRECT
+                       rm_blk = fm_check_block_seq(radio->block_seq, block_type);
+                       if (rm_blk < RDS_RM_ALIGN_NONE) {
+                               RDSEBUG(radio,
+                                       "pre block[%02d],curr block[%02d],err count[%08d]",
+                                       radio->block_seq, block_type, radio->rds_fifo_err_cnt);
+                               if (rm_blk != RDS_RM_ALIGN_0) {
+                                       fm_rds_write_data_remove(radio, rm_blk);
+                                       radio->block_seq = RDS_BLKTYPE_D;
+                               }
+                               fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
+                               radio->rds_fifo_err_cnt++;
+                               break;
+                       }
+                       radio->block_seq = block_type;
+#endif /*      USE_RDS_BLOCK_SEQ_CORRECT */
+                       if (fm_rds_process_block(radio,
+                               data, block_type, err_count)
+                               == HOST_RDS_ERRS_UNCORR) {
+                               fm_rds_update_error_status(radio,
+                               radio->low->fm_state.rds_unsync_uncorr_weight);
+                       } else {
+                               fm_rds_update_error_status(radio, err_count);
+                       }
+                       break;
+               }
+       }
+
+       API_EXIT(radio);
+}
+#endif /* USE_RDS_HW_DECODER */
+void find_pi_data(struct fm_rds_parser_info *pi, u16 info)
+{
+
+       pi->pi_buf[pi->pi_idx % 2] = info;
+
+       if (!(++pi->pi_idx % 2)) {
+               if (pi->pi_buf[0] == pi->pi_buf[1]) {
+                       pi->rds_event |= RDS_EVENT_PI_MASK;
+                       RDSEBUG(gradio, "[RDS] PI : 0x%x\n", pi->pi_buf[0]);
+               }
+       }
+}
+
+void find_ecc_data(struct fm_rds_parser_info *pi, u16 info)
+{
+
+       pi->ecc_buf[pi->ecc_idx % 2] = info & 0xFF;
+
+       if (!(++pi->ecc_idx % 2)) {
+               if (pi->ecc_buf[0] == pi->ecc_buf[1]) {
+                       pi->rds_event |= RDS_EVENT_ECC_MASK;
+                       RDSEBUG(gradio, "[RDS] ECC : %d\n", pi->ecc_buf[0]);
+               }
+       }
+}
+
+void store_ps_data(struct fm_rds_parser_info *pi,
+       u16 info, u8 err_cnt)
+{
+       char a, b;
+       u32 i = pi->ps_idx % 3;
+       u8 seg = pi->ps_segment;
+
+       if (pi->drop_blk)
+               return;
+
+       a = (info >> 8) & 0xff;
+       b = info & 0xff;
+
+       pi->ps_buf[i][seg * 2] = a;
+       pi->ps_buf[i][seg * 2 + 1] = b;
+       pi->ps_err[i][seg] = err_cnt;
+       /*RDSEBUG(gradio,
+               "[RDS] PS: [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
+               a, b, (int)a, (int)b, i, seg);*/
+}
+
+void validate_ps_data(struct fm_rds_parser_info *pi)
+{
+       u32 i;
+       bool match = true;
+
+       for (i = 0; i < pi->ps_len / 2; i++) {
+               if (pi->ps_err[pi->ps_idx % 3][i] > 0)
+                       break;
+       }
+
+       if (i == pi->ps_len / 2) {
+               memcpy(pi->ps_candidate,
+                       pi->ps_buf[pi->ps_idx % 3],
+                       pi->ps_len);
+               memset(pi->ps_err[pi->ps_idx % 3],
+                       0xFF, MAX_PS / 2);
+               pi->ps_candidate[pi->ps_len] = 0;
+               pi->rds_event |= RDS_EVENT_PS_MASK;
+               pi->ps_idx++;
+               RDSEBUG(gradio,
+                       "[RDS] ### PS candidate[i]: %s\n",
+                       pi->ps_candidate);
+       } else {
+               if (++pi->ps_idx >= 3) {
+                       for (i = 0; i < pi->ps_len; i++) {
+                               if (pi->ps_buf[0][i] == pi->ps_buf[1][i])
+                                       pi->ps_candidate[i] = pi->ps_buf[0][i];
+                               else if (pi->ps_buf[0][i] == pi->ps_buf[2][i])
+                                       pi->ps_candidate[i] = pi->ps_buf[0][i];
+                               else if (pi->ps_buf[1][i] == pi->ps_buf[2][i])
+                                       pi->ps_candidate[i] = pi->ps_buf[1][i];
+                               else
+                                       match = false;
+                       }
+
+                       if (match) {
+                               pi->ps_candidate[pi->ps_len] = 0;
+                               pi->rds_event |= RDS_EVENT_PS_MASK;
+                               RDSEBUG(gradio,
+                                       "[RDS] ### PS candidate[m]: %s\n",
+                                       pi->ps_candidate);
+                       }
+               }
+       }
+
+       i = pi->ps_idx - 1;
+       pi->ps_buf[i % 3][pi->ps_len] = 0;
+
+       for (i = 0; i < 3; i++)
+               RDSEBUG(gradio,
+               "[RDS] ### PS received[%d]: %s\n",
+                       i, pi->ps_buf[i]);
+}
+
+void store_rt_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt)
+{
+       char a, b;
+       u32 i = pi->rt_idx % 3;
+       u8 seg = pi->rt_segment;
+
+       if (pi->drop_blk)
+               return;
+
+       a = (info >> 8) & 0xff;
+       b = info & 0xff;
+
+       switch (blk_type) {
+       case RDS_BLKTYPE_C:
+               pi->rt_buf[i][seg * 4] = a;
+               pi->rt_buf[i][seg * 4 + 1] = b;
+               pi->rt_err[i][seg * 2] = err_cnt;
+               /*RDSEBUG(gradio,
+                       "[RDS] RT_A(C): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
+                       a, b, (int)a, (int)b, i, seg);*/
+               break;
+
+       case RDS_BLKTYPE_D:
+               if (pi->grp == RDS_GRPTYPE_2A) {
+                       pi->rt_buf[i][seg * 4 + 2] = a;
+                       pi->rt_buf[i][seg * 4 + 3] = b;
+                       pi->rt_err[i][seg * 2 + 1] = err_cnt;
+                       /*RDSEBUG(gradio,
+                               "[RDS] RT_A(D): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
+                               a, b, (int)a, (int)b, i, seg);*/
+               } else if (pi->grp == RDS_GRPTYPE_2B) {
+                       pi->rt_buf[i][seg * 2] = a;
+                       pi->rt_buf[i][seg * 2 + 1] = b;
+                       pi->rt_err[i][seg] = err_cnt;
+                       /*RDSEBUG(gradio,
+                               "[RDS] RT_B(C): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
+                               a, b, (int)a, (int)b, i, seg);*/
+               }
+       default:
+               break;
+       }
+}
+
+void validate_rt_data(struct fm_rds_parser_info *pi)
+{
+       u32 i;
+       bool match = true;
+
+       for (i = 0; i < pi->rt_len / 2; i++) {
+               if (pi->rt_err[pi->rt_idx % 3][i] > 0)
+                       break;
+       }
+
+       if (i == pi->rt_len / 2) {
+               memcpy(pi->rt_candidate, pi->rt_buf[pi->rt_idx % 3], pi->rt_len);
+               memset(pi->rt_err[pi->rt_idx % 3], 0xFF, MAX_RT / 2);
+               pi->rt_candidate[pi->rt_len] = 0;
+
+               if (strlen(pi->rt_candidate) == pi->rt_len) {
+                       pi->rds_event |= RDS_EVENT_RT_MASK;
+                       pi->rt_validated = 1;
+                       pi->rt_idx++;
+                       RDSEBUG(gradio,
+                               "[RDS] ### RT candidate[i]: %s,  %d, %d\n",
+                               pi->rt_candidate, (int)strlen(pi->rt_candidate), pi->rt_len);
+               }
+       } else {
+               if (++pi->rt_idx >= 3) {
+                       for (i = 0; i < pi->rt_len; i++) {
+                               if (pi->rt_buf[0][i] == pi->rt_buf[1][i])
+                                       pi->rt_candidate[i] = pi->rt_buf[0][i];
+                               else if (pi->rt_buf[0][i] == pi->rt_buf[2][i])
+                                       pi->rt_candidate[i] = pi->rt_buf[0][i];
+                               else if (pi->rt_buf[1][i] == pi->rt_buf[2][i])
+                                       pi->rt_candidate[i] = pi->rt_buf[1][i];
+                               else
+                                       match = false;
+                       }
+
+                       if (match) {
+                               pi->rt_candidate[pi->rt_len] = 0;
+
+                               if (strlen(pi->rt_candidate) == pi->rt_len) {
+                                       pi->rds_event |= RDS_EVENT_RT_MASK;
+                                       pi->rt_validated = 1;
+                                       RDSEBUG(gradio,
+                                               "[RDS] ### RT candidate[m]: %s, %d, %d\n",
+                                               pi->rt_candidate, (int)strlen(pi->rt_candidate), pi->rt_len);
+                               }
+                       }
+               }
+       }
+
+       i = pi->rt_idx - 1;
+       pi->rt_buf[i % 3][pi->rt_len] = 0;
+       for (i = 0; i < 3; i++)
+               RDSEBUG(gradio,
+               "[RDS] ### RT received[%d]: %s\n",
+               i, pi->rt_buf[i]);
+}
+
+void reset_rtp_data(struct fm_rds_parser_info *pi)
+{
+       memset(&(pi->rtp_data), 0x0, sizeof(struct rtp_info));
+       memset(pi->rtp_raw_data, 0x0, sizeof(u16) * 3);
+}
+
+void validate_rtp_data(struct fm_rds_parser_info *pi)
+{
+       u8 i;
+       static u16 rtp_validated;
+       struct rtp_tag_info tag[2];
+
+       if (!pi->rtp_data.running) {
+        RDSEBUG(gradio, "[RDS] RTP running is stopped\n");
+               return;
+    }
+
+       tag[0].content_type = ((pi->rtp_raw_data[RDS_BLKTYPE_B - 1] & 0x0007) << 3) |
+                                                       ((pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0xE000) >> 13);
+       tag[0].start_pos = (pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x1F80) >> 7;
+       tag[0].len = (pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x007E) >> 1;
+
+       tag[1].content_type = ((pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x0001) << 5) |
+                                                               ((pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0xF800) >> 11);
+       tag[1].start_pos = (pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0x07E0) >> 5;
+       tag[1].len = pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0x001F;
+
+       RDSEBUG(gradio, "[RDS] RTP tag[0]:[%d,%d,%d]\n", tag[0].content_type, tag[0].start_pos, tag[0].len);
+       RDSEBUG(gradio, "[RDS] RTP tag[1]:[%d,%d,%d]\n", tag[1].content_type, tag[1].start_pos, tag[1].len);
+
+       /* Check overlap */
+       if (((tag[1].content_type != 0) && (tag[0].start_pos < tag[1].start_pos) && ((tag[0].start_pos + tag[0].len) >= tag[1].start_pos)) ||
+               ((tag[0].content_type != 0) && (tag[1].start_pos < tag[0].start_pos) && ((tag[1].start_pos + tag[1].len) >= tag[0].start_pos))) {
+               RDSEBUG(gradio, "[RDS] RTP tag[0] & tag[1] are overlapped.\n");
+               return;
+       }
+
+       for (i = 0; i < MAX_RTP_TAG; i++) {
+               if (tag[i].content_type == pi->rtp_data.tag[i].content_type &&
+                       tag[i].start_pos == pi->rtp_data.tag[i].start_pos &&
+                       tag[i].len == pi->rtp_data.tag[i].len) {
+                       rtp_validated++;
+            RDSEBUG(gradio, "[RDS] RTP tag validation check count:0x%x\n", (int)rtp_validated);
+               } else {
+                       pi->rtp_data.tag[i].content_type = tag[i].content_type;
+                       pi->rtp_data.tag[i].start_pos = tag[i].start_pos;
+                       pi->rtp_data.tag[i].len = tag[i].len;
+                       rtp_validated = 0;
+               }
+       }
+
+       /* RT is ready to be displayed along with RTP data */
+       if (pi->rt_validated && rtp_validated > 2)
+               pi->rds_event |= RDS_EVENT_RTP_MASK;
+}
+
+void store_rtp_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type)
+{
+    u8 toggle;
+
+       if (pi->drop_blk) {
+               return;
+    }
+
+       if (pi->grp != pi->rtp_code_group) {
+               RDSEBUG(gradio, "[RDS] Received unexpected code group(0x%x), expected=0x%x\n",
+                       (int)pi->grp, (int)pi->rtp_code_group);
+               return;
+       }
+
+       switch (blk_type) {
+       case RDS_BLKTYPE_B:
+        toggle = (info & 0x010) >> 4;
+        if (toggle != pi->rtp_data.toggle) {
+            reset_rtp_data(pi);
+            pi->rtp_data.toggle = toggle;
+        }
+               pi->rtp_data.running = (info & 0x0008) >> 3;
+               pi->rtp_raw_data[blk_type-1] = info;
+        RDSEBUG(gradio, "[RDS] Received RTP B block/0x%x Group\n", (int)pi->grp);
+               break;
+       case RDS_BLKTYPE_C:
+               pi->rtp_raw_data[blk_type-1] = info;
+        RDSEBUG(gradio, "[RDS] Received RTP C block/0x%x Group\n", (int)pi->grp);
+               break;
+       case RDS_BLKTYPE_D:
+               pi->rtp_raw_data[blk_type-1] = info;
+        RDSEBUG(gradio, "[RDS] Received RTP D block/0x%x Group\n", (int)pi->grp);
+               validate_rtp_data(pi);
+               break;
+       default:
+               break;
+       }
+}
+
+void find_rtp_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type)
+{
+       if (pi->drop_blk)
+               return;
+
+       switch (blk_type) {
+       case RDS_BLKTYPE_B:
+               pi->rtp_code_group = info & 0x1f;
+        RDSEBUG(gradio, "[RDS] RTP code group:0x%x\n", (int)pi->rtp_code_group);
+               break;
+       case RDS_BLKTYPE_C:
+               /* Not support SCB/RTP template */
+        RDSEBUG(gradio, "[RDS] RTP not support SCB/RTP template\n");
+               break;
+       case RDS_BLKTYPE_D:
+               if (info != RDS_RTP_AID) {
+                       RDSEBUG(gradio, "[RDS] Invalid RTP aid=0x%x\n", (int)info);
+                       pi->rtp_code_group = 0;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+void find_group_data(struct fm_rds_parser_info *pi, u16 info)
+{
+       u8 segment, rt_change;
+
+       pi->grp = (info >> 11) & 0x1f;
+
+       switch (pi->grp) {
+       case RDS_GRPTYPE_0A:
+       case RDS_GRPTYPE_0B:
+               segment = info & 0x3;
+
+               if (!segment && pi->ps_segment != 0xFF)
+                       validate_ps_data(pi);
+
+               pi->ps_segment = segment;
+
+               if (pi->ps_len < (segment + 1) * 2)
+                       pi->ps_len = (segment + 1) * 2;
+
+               /*RDSEBUG(gradio,
+                       "[RDS] PS: seg=%d, len=%d\n",
+                       segment, pi->ps_len);*/
+               break;
+
+       case RDS_GRPTYPE_2A:
+       case RDS_GRPTYPE_2B:
+               segment = info & 0xF;
+               rt_change = (info & 0x10) >> 4;
+
+               RDSEBUG(gradio,
+                       "[RDS] segment=%d, pi->rt_segment=%d, pi->rt_change=%d, rt_change=%d\n",
+                               segment, pi->rt_segment, pi->rt_change, rt_change);
+
+               if ((!segment && pi->rt_segment != 0xFF)
+                               || (pi->rt_change != 0xFF && pi->rt_change != rt_change)) {
+                       validate_rt_data(pi);
+
+                       if (pi->rt_change != 0xFF && pi->rt_change != rt_change) {
+                               pi->rt_len = 0;
+                               pi->rt_idx = 0;
+                               memset(pi->rt_buf, 0, 3 * (MAX_RT + 1));
+                               memset(pi->rt_err, 0xFF, 3 * MAX_RT / 2);
+                               pi->rt_validated = 0;
+                       }
+               }
+
+               pi->rt_segment = segment;
+               pi->rt_change = rt_change;
+
+               if (pi->grp == RDS_GRPTYPE_2A) {
+                       if (pi->rt_len < (segment + 1) * 4)
+                               pi->rt_len = (segment + 1) * 4;
+               } else {
+                       if (pi->rt_len < (segment + 1) * 2)
+                               pi->rt_len = (segment + 1) * 2;
+               }
+
+               RDSEBUG(gradio,
+                       "[RDS] RT: seg=%d, tc=%d, len=%d\n",
+                       segment, rt_change, pi->rt_len);
+               break;
+
+       case RDS_GRPTYPE_3A:
+               find_rtp_data(pi, info, RDS_BLKTYPE_B);
+               break;
+
+       case RDS_GRPTYPE_5A:
+       case RDS_GRPTYPE_6A:
+       case RDS_GRPTYPE_7A:
+       case RDS_GRPTYPE_8A:
+       case RDS_GRPTYPE_9A:
+       case RDS_GRPTYPE_11A:
+       case RDS_GRPTYPE_12A:
+       case RDS_GRPTYPE_13A:
+               store_rtp_data(pi, info, RDS_BLKTYPE_B);
+               break;
+
+       default:
+               break;
+       }
+}
+
+void find_af_data(struct fm_rds_parser_info *pi, u16 info)
+{
+
+       pi->af_buf[pi->af_idx % 2] = info;
+
+       if (!(++pi->af_idx % 2)) {
+               if (pi->af_buf[0] == pi->af_buf[1])
+                       pi->rds_event |= RDS_EVENT_AF_MASK;
+       }
+}
+
+void fm_rds_parser_reset(struct fm_rds_parser_info *pi)
+{
+    /* reset rds parser data structure */
+    memset(pi, 0x0, sizeof(struct fm_rds_parser_info));
+
+    pi->ps_segment = 0xFF;
+    pi->rt_segment = 0xFF;
+    pi->rt_change = 0xFF;
+    pi->rtp_data.toggle = 0xFF;
+    memset(pi->rt_err, 0xFF, 3 * MAX_RT / 2);
+    memset(pi->ps_err, 0xFF, 3 * MAX_PS / 2);
+}
+
+void fm_rds_parser(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt)
+{
+
+       switch (blk_type) {
+       case RDS_BLKTYPE_A:
+               find_pi_data(pi, info);
+               break;
+
+       case RDS_BLKTYPE_B:
+               if (err_cnt > 2) {
+                       pi->grp = RDS_GRPTYPE_NONE;
+                       pi->drop_blk = true;
+                       break;
+               } else {
+                       pi->drop_blk = false;
+               }
+
+               find_group_data(pi, info);
+               break;
+
+       case RDS_BLKTYPE_C:
+               if (err_cnt > 5)
+                       return;
+
+               switch (pi->grp) {
+               case RDS_GRPTYPE_0A:
+                       find_af_data(pi, info);
+                       break;
+
+               case RDS_GRPTYPE_0B:
+                       find_pi_data(pi, info);
+                       break;
+
+               case RDS_GRPTYPE_1A:
+                       find_ecc_data(pi, info);
+                       break;
+
+               case RDS_GRPTYPE_2A:
+                       store_rt_data(pi, info, blk_type, err_cnt);
+                       break;
+
+               case RDS_GRPTYPE_2B:
+                       find_pi_data(pi, info);
+                       break;
+
+               case RDS_GRPTYPE_3A:
+                       find_rtp_data(pi, info, blk_type);
+                       break;
+
+               case RDS_GRPTYPE_5A:
+               case RDS_GRPTYPE_6A:
+               case RDS_GRPTYPE_7A:
+               case RDS_GRPTYPE_8A:
+               case RDS_GRPTYPE_9A:
+               case RDS_GRPTYPE_11A:
+               case RDS_GRPTYPE_12A:
+               case RDS_GRPTYPE_13A:
+                       store_rtp_data(pi, info, blk_type);
+                       break;
+
+               default:
+                       break;
+               }
+               break;
+
+       case RDS_BLKTYPE_D:
+               if (err_cnt > 5)
+                       return;
+
+                       switch (pi->grp) {
+                       case RDS_GRPTYPE_0A:
+                       case RDS_GRPTYPE_0B:
+                               store_ps_data(pi, info, err_cnt);
+                               break;
+
+                       case RDS_GRPTYPE_2A:
+                       case RDS_GRPTYPE_2B:
+                               store_rt_data(pi, info, blk_type, err_cnt);
+                               break;
+
+                       case RDS_GRPTYPE_3A:
+                               find_rtp_data(pi, info, blk_type);
+                               break;
+
+                       case RDS_GRPTYPE_5A:
+                       case RDS_GRPTYPE_6A:
+                       case RDS_GRPTYPE_7A:
+                       case RDS_GRPTYPE_8A:
+                       case RDS_GRPTYPE_9A:
+                       case RDS_GRPTYPE_11A:
+                       case RDS_GRPTYPE_12A:
+                       case RDS_GRPTYPE_13A:
+                               store_rtp_data(pi, info, blk_type);
+                               break;
+
+                       default:
+                               break;
+                       }
+                       break;
+       }
+}
+
+void fm_rds_write_data_pi(struct s610_radio *radio,
+       struct fm_rds_parser_info *pi)
+{
+       u8 buf_ptr[RDS_MEM_MAX_THRESH_PARSER];
+       u8 i, str_len;
+       int total_size = 0;
+       u16 usage;
+
+#ifdef USE_RINGBUFF_API
+       if (ringbuf_bytes_free(&radio->rds_rb) < RDS_MEM_MAX_THRESH_PARSER)     {
+               dev_info(radio->dev, ">>> ringbuff not free, wait..");
+               /*ringbuf_reset(&radio->rds_rb);*/
+               return;
+       }
+#else
+       dev_info(radio->dev, ">>> ringbuff API not used, fail..");
+       return;
+#endif /* USE_RINGBUFF_API */
+
+       memset(buf_ptr, 0, RDS_MEM_MAX_THRESH_PARSER);
+
+       while (pi->rds_event) {
+               RDSEBUG(radio,
+                       "%s() rds_event[%04X]\n", __func__, pi->rds_event);
+
+               if (pi->rds_event & RDS_EVENT_PI_MASK) {
+                       pi->rds_event &= ~RDS_EVENT_PI_MASK;
+                       buf_ptr[total_size] = RDS_EVENT_PI;
+                       buf_ptr[total_size+1] = 2;
+                       buf_ptr[total_size+2] = (u8)(pi->pi_buf[0] & 0xFF);
+                       buf_ptr[total_size+3] = (u8)(pi->pi_buf[0] >> 8);
+                       RDSEBUG(radio,
+                               "[RDS] Enqueue PI data[%02X:%02X:%02X:%02X]. total=%d\n",
+                               buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],buf_ptr[total_size+3],
+                               total_size+4);
+
+                       total_size += 4;
+
+               } else if (pi->rds_event & RDS_EVENT_AF_MASK) {
+                       pi->rds_event &= ~RDS_EVENT_AF_MASK;
+                       buf_ptr[total_size] = RDS_EVENT_AF;
+                       buf_ptr[total_size+1] = 2;
+                       buf_ptr[total_size+2] = (u8)(pi->af_buf[0] & 0xFF);
+                       buf_ptr[total_size+3] = (u8)(pi->af_buf[0] >> 8);
+                       RDSEBUG(radio,
+                               "[RDS] Enqueue AF data[%02X:%02X:%02X:%02X]. total=%d\n",
+                               buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],buf_ptr[total_size+3],
+                               total_size+4);
+
+                       total_size += 4;
+
+               } else if (pi->rds_event & RDS_EVENT_PS_MASK) {
+                       pi->rds_event &= ~RDS_EVENT_PS_MASK;
+                       str_len = strlen(pi->ps_candidate);
+                       buf_ptr[total_size] = RDS_EVENT_PS;
+                       buf_ptr[total_size+1] = str_len;
+
+                       for (i = 0; i < str_len; i++) {
+                               buf_ptr[total_size + i + 2] = pi->ps_candidate[i];
+                       }
+
+                       total_size += str_len + 2;
+                       RDSEBUG(radio,
+                               "[RDS] Enqueue PS data. total=%d,%d,%d\n", total_size, pi->ps_len + 2, str_len);
+               } else if (pi->rds_event & RDS_EVENT_RT_MASK) {
+                       pi->rds_event &= ~RDS_EVENT_RT_MASK;
+                       str_len = strlen(pi->rt_candidate);
+                       buf_ptr[total_size] = RDS_EVENT_RT;
+                       buf_ptr[total_size+1] = str_len;
+
+                       for (i = 0; i < str_len; i++) {
+                               buf_ptr[total_size + i + 2] = pi->rt_candidate[i];
+                       }
+
+                       total_size += str_len + 2;
+                       RDSEBUG(radio,
+                               "[RDS] Enqueue RT data. total=%d,%d,%d\n", total_size, pi->rt_len + 2, str_len + 2);
+               } else if (pi->rds_event & RDS_EVENT_ECC_MASK) {
+                       pi->rds_event &= ~RDS_EVENT_ECC_MASK;
+
+                       buf_ptr[total_size] = RDS_EVENT_ECC;
+                       buf_ptr[total_size+1] = 1;
+                       buf_ptr[total_size+2] = (u8)pi->ecc_buf[0];
+                       RDSEBUG(radio,
+                               "[RDS] Enqueue ECC data[%02d:%02d:%02d]. total=%d\n",
+                               buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],
+                               total_size+3);
+
+                       total_size += 3;
+               } else if (pi->rds_event & RDS_EVENT_RTP_MASK) {
+                       pi->rds_event &= ~RDS_EVENT_RTP_MASK;
+
+                       buf_ptr[total_size] = RDS_EVENT_RTP;
+                       buf_ptr[total_size+1] = sizeof(struct rtp_info);
+                       memcpy(buf_ptr+total_size+2, &(pi->rtp_data), sizeof(struct rtp_info));
+                       RDSEBUG(radio, "[RDS] Enqueue RTPlus data\n");
+                       RDSEBUG(radio, "[RDS] Toggle:%d, Running:%d, Validated;%d\n", pi->rtp_data.toggle, pi->rtp_data.running, pi->rtp_data.validated);
+                       RDSEBUG(radio, "[%d,%d,%d]\n", pi->rtp_data.tag[0].content_type, pi->rtp_data.tag[0].start_pos, pi->rtp_data.tag[0].len);
+                       RDSEBUG(radio, "[%d,%d,%d]\n", pi->rtp_data.tag[1].content_type, pi->rtp_data.tag[1].start_pos, pi->rtp_data.tag[1].len);
+
+                       total_size += sizeof(struct rtp_info) + 2;
+               }
+
+               if (!pi->rds_event && total_size) {
+#ifdef USE_RINGBUFF_API
+                       if (!ringbuf_memcpy_into(&radio->rds_rb, buf_ptr, total_size)) {
+                               usage = ringbuf_bytes_used(&radio->rds_rb);
+                               dev_info(radio->dev,
+                                       "%s():>>>RB memcpy into fail! usage:%04d",
+                                       __func__, ringbuf_bytes_used(&radio->rds_rb));
+                               if (!usage)
+                                       return;
+                       }
+
+                       usage = ringbuf_bytes_used(&radio->rds_rb);
+#else
+                       dev_info(radio->dev, ">>> ringbuff API not used, fail..");
+#endif /* USE_RINGBUFF_API */
+
+                       if (atomic_read(&radio->is_rds_new))
+                               return;
+
+                       fm_set_flag_bits(radio, FLAG_BUF_FUL);
+                       atomic_set(&radio->is_rds_new, 1);
+                       wake_up_interruptible(&radio->core->rds_read_queue);
+                       radio->rds_n_count++;
+
+                       if (!(radio->rds_n_count%200)) {
+                               fm_update_rssi_work(radio);
+                               dev_info(radio->dev,
+                                       ">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]",
+                                       radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt,
+                                       usage, radio->rds_sync_loss_cnt);
+                       }
+               }
+       }
+}
+
+#ifndef USE_RDS_HW_DECODER
+struct fm_decoded_data {
+       u16 info;
+       fm_rds_block_type_enum blk_type;
+       u8 err_cnt;
+};
+
+void put_bit_to_byte(u8 *fifo, u32 data)
+{
+       s8 i, j;
+       u32 mask = 0x80000000;
+
+       for (i = BUF_LEN - 1, j = 0; i >= 0; i--, j++) {
+               *(fifo + j) = (mask & data) ? 1 : 0;
+               mask = mask >> 1;
+       }
+}
+
+u32 get_block_data(u8 *fifo, u32 sp)
+{
+       u8 i, j;
+       u32 data = 0;
+
+       data |= *(fifo + (sp++ % 160));
+
+       for (i = BLK_LEN-1, j = 0; i > 0; i--, j++) {
+               data <<= 1;
+               data |= *(fifo + (sp++ % 160));
+       }
+
+       return data;
+}
+
+u8 find_code_word(u32 *data, fm_rds_block_type_enum b_type, bool seq_lock)
+{
+       u16 first13;
+       u16 second13;
+       u16 syndrome;
+
+       first13 = *data >> 13;
+       second13 = (*data & 0x1FFF) ^ OFFSET_WORD[b_type];
+
+       syndrome = CRC_LUT[(CRC_LUT[first13] << 3) ^ second13];
+
+       if (syndrome) {
+               u32 corrected;
+               u8 i, j;
+               u8 maxerr = (b_type == RDS_BLKTYPE_A) ? 2 : sizeof(burstErrorPattern) / sizeof(u8);
+
+               for (i = 0; i < maxerr; i++) {
+                       for (j = 0; j <= BLK_LEN - burstErrorLen[i]; j++) {
+                               corrected = *data ^ (burstErrorPattern[i] << j);
+
+                               first13 = corrected >> 13;
+                               second13 = (corrected & 0x1FFF) ^ OFFSET_WORD[b_type];
+
+                               syndrome = CRC_LUT[(CRC_LUT[first13] << 3) ^ second13];
+
+                               if (!syndrome) {
+                                       *data = corrected;
+                                       return burstErrorLen[i];
+                               }
+                       }
+               }
+       } else {
+               return 0;
+       }
+       return 6;
+}
+
+void fm_process_rds_data(struct s610_radio *radio)
+{
+       u16 i, j, k, l;
+       u16 avail_blocks;
+       u32 fifo_data;
+       u32 data;
+       fm_host_rds_errors_enum err_cnt;
+       u8 min_pos = 0;
+       u8 min_blk = 0;
+       u8 min_blk_tmp = 0;
+       u8 min_pos_sum, min_blk_sum;
+
+       static u32 idx;
+       static u8 fifo[160];
+       static u32 start_pos;
+       static u32 end_pos;
+       static bool fetch_data;
+       static bool seq_lock;
+       static bool remains;
+       static struct fm_decoded_data decoded[BLK_LEN][RDS_NUM_BLOCK_TYPES - 1][RDS_NUM_BLOCK_TYPES - 1];
+       u32 fifo_status;
+
+       fifo_status = fmspeedy_get_reg_work(0xFFF398);
+       avail_blocks = (((fifo_status >> 8) & 0x3F));
+       RDSEBUG(radio,"%s(): avail_blocks:%d fifo_status[%08X]",__func__, avail_blocks, fifo_status);
+
+       while (avail_blocks) {
+               /* Fetch the RDS raw data. */
+               if (fetch_data) {
+                       fifo_data = fmspeedy_get_reg_work(0xFFF3C0);
+                       put_bit_to_byte(fifo + 32 * ((idx++) % 5), fifo_data);
+                       avail_blocks--;
+               }
+
+               switch (fm_rds_get_state(radio)) {
+               case RDS_STATE_INIT:
+                       fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
+                       fm_update_rds_sync_status(radio, false);
+                       radio->low->fm_rds_state.error_bits = 0;
+                       radio->low->fm_rds_state.error_blks = 0;
+
+                       idx = 0;
+                       start_pos = 0;
+                       end_pos = BLK_LEN - 1;
+                       fetch_data = false;
+                       seq_lock = false;
+                       memset(fifo, 0, sizeof(fifo) / sizeof(u8));
+
+               case RDS_STATE_HAVE_DATA:
+                       if (idx < 5) {
+                               fifo_data = fmspeedy_get_reg_work(0xFFF3C0);
+                               put_bit_to_byte(fifo + 32 * ((idx++) % 5), fifo_data);
+                               avail_blocks--;
+
+                               if (idx == 5) {
+                                       for (i = 0; i < BLK_LEN; i++) {
+                                               for (j = 0; j < RDS_NUM_BLOCK_TYPES - 1; j++) {
+                                                       start_pos = i;
+
+                                                       for (k = 0, l = j; k < RDS_NUM_BLOCK_TYPES - 1; k++) {
+                                                               data = get_block_data(fifo, start_pos);
+                                                               err_cnt = find_code_word(&data, l, seq_lock);
+
+                                                               decoded[i][j][k].info = data >> 10;
+                                                               decoded[i][j][k].blk_type = l;
+                                                               decoded[i][j][k].err_cnt = err_cnt;
+                                                               start_pos += BLK_LEN;
+                                                               l = (l + 1) % (RDS_NUM_BLOCK_TYPES - 1);
+                                                       }
+                                               }
+                                       }
+
+                                       for (i = 0, min_pos_sum = 0xFF; i < BLK_LEN; i++) {
+                                               for (j = 0, min_blk_sum = 0xFF; j < RDS_NUM_BLOCK_TYPES - 1; j++) {
+                                                       for (k = 0, err_cnt = 0; k < RDS_NUM_BLOCK_TYPES - 1; k++) {
+                                                               err_cnt += decoded[i][j][k].err_cnt;
+                                                       }
+
+                                                       if (min_blk_sum > err_cnt) {
+                                                               min_blk_sum = err_cnt;
+                                                               min_blk_tmp = j;
+                                                       }
+                                               }
+
+                                               if (min_pos_sum > min_blk_sum) {
+                                                       min_pos_sum = min_blk_sum;
+                                                       min_pos = i;
+                                                       min_blk = min_blk_tmp;
+                                               }
+
+                                       }
+
+                                       fm_rds_change_state(radio, RDS_STATE_PRE_SYNC);
+                               } else
+                                       break;
+                       }
+
+               case RDS_STATE_PRE_SYNC:
+                       for (i = 0; i < RDS_NUM_BLOCK_TYPES - 1; i++) {
+                               if (decoded[min_pos][min_blk][i].blk_type == RDS_BLKTYPE_A) {
+                                       fm_update_rds_sync_status(radio, TRUE);
+                               }
+
+                               if (fm_get_rds_sync_status(radio)) {
+
+                                       if (fm_rds_process_block(radio, decoded[min_pos][min_blk][i].info,
+                                                       decoded[min_pos][min_blk][i].blk_type, decoded[min_pos][min_blk][i].err_cnt)
+                                                       == HOST_RDS_ERRS_UNCORR) {
+                                               fm_rds_update_error_status(radio,
+                                                               radio->low->fm_state.rds_unsync_uncorr_weight);
+                                       } else {
+                                               fm_rds_update_error_status(radio, decoded[min_pos][min_blk][i].err_cnt);
+                                       }
+                               }
+                       }
+
+                       start_pos = min_pos + BLK_LEN * 3;
+                       end_pos = start_pos + BLK_LEN - 1;
+
+                       fm_rds_change_state(radio, (min_blk + 3) % (RDS_NUM_BLOCK_TYPES - 1));
+                       seq_lock = false;
+                       remains = true;
+
+               case RDS_STATE_FOUND_BL_A:
+               case RDS_STATE_FOUND_BL_B:
+               case RDS_STATE_FOUND_BL_C:
+               case RDS_STATE_FOUND_BL_D:
+                       if ((end_pos / BUF_LEN != (end_pos + BLK_LEN) / BUF_LEN) && !fetch_data && !remains) {
+                               fetch_data = true;
+                       } else {
+                               if (end_pos + BLK_LEN >= 160 && remains) {
+                                       remains = false;
+                                       fetch_data = true;
+                                       break;
+                               }
+
+                               start_pos += BLK_LEN;
+                               end_pos += BLK_LEN;
+
+                               data = get_block_data(fifo, start_pos);
+                               fetch_data = false;
+
+                               i = (fm_rds_get_state(radio) + 1) % (RDS_NUM_BLOCK_TYPES - 1);
+                               fm_rds_change_state(radio, i);
+                               err_cnt = find_code_word(&data, i, seq_lock);
+
+                               RDSEBUG(radio,"%s(): data:0x%x, errcnt:%d, gcount:%d", __func__, data, err_cnt, radio->rds_gcnt++);
+                               if (fm_rds_process_block(radio, data >> 10, i, err_cnt) == HOST_RDS_ERRS_UNCORR) {
+                                       fm_rds_update_error_status(radio, radio->low->fm_state.rds_unsync_uncorr_weight);
+                               } else {
+                                       fm_rds_update_error_status(radio, err_cnt);
+                               }
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+#endif /*USE_RDS_HW_DECODER*/
diff --git a/drivers/media/radio/s610/fm_rds.h b/drivers/media/radio/s610/fm_rds.h
new file mode 100644 (file)
index 0000000..c5fa66c
--- /dev/null
@@ -0,0 +1,2148 @@
+#ifndef FM_RDS_H
+#define FM_RDS_H
+
+void fm_rds_write_data(struct s610_radio *radio, u16 rds_data,
+       fm_rds_block_type_enum blk_type, fm_host_rds_errors_enum errors);
+void fm_rds_write_data_remove(struct s610_radio *radio,
+       fm_rds_rm_align_enum removeblock);
+u16 fm_rds_get_avail_bytes(struct s610_radio *radio);
+int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
+               u16 *blocks);
+void fm_rds_change_state(struct s610_radio *radio,
+               fm_rds_state_enum new_state);
+void fm_rds_update_error_status(struct s610_radio *radio, u16 errors);
+static fm_host_rds_errors_enum fm_rds_process_block(struct s610_radio *radio,
+               u16 data, fm_rds_block_type_enum block_type, u16 err_count);
+void fm_process_rds_data(struct s610_radio *radio);
+int ringbuf_bytes_used(const struct ringbuf_t *rb);
+void fm_rds_parser(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt);
+void fm_rds_write_data_pi(struct s610_radio *radio,
+       struct fm_rds_parser_info *pi);
+void fm_rds_parser_reset(struct fm_rds_parser_info *pi);
+
+extern void fm_update_rds_sync_status(struct s610_radio *radio, bool synced);
+#ifndef        USE_RDS_HW_DECODER
+extern bool fm_get_rds_sync_status(struct s610_radio *radio);
+#endif /*USE_RDS_HW_DECODER*/
+
+extern int fm_set_flags(struct s610_radio *radio, u16 flags);
+extern void fm_timer_reset(fm_timer_t *timer, int usec, fm_callback_t *func,
+               void *arg);
+extern u32 fmspeedy_get_reg(u32 addr);
+extern u32 fmspeedy_get_reg_work(u32 addr);
+extern void fm_update_rssi_work(struct s610_radio *radio);
+
+#define RDS_BLK_TYPE_MASK      0xE0
+#define RDS_BLK_TYPE_SHIFT     5
+#define RDS_ERR_CNT_MASK       0x1F
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+
+#define USE_RDS_BLOCK_SEQ_CORRECT
+/*#undef  USE_RDS_BLOCK_SEQ_CORRECT*/
+
+#define BUF_LEN 32
+#define BLK_LEN 26
+
+#define RDS_EVENT_NONE_MASK  0x0
+#define RDS_EVENT_PI_MASK    0x1
+#define RDS_EVENT_PS_MASK    0x2
+#define RDS_EVENT_AF_MASK    0x4
+#define RDS_EVENT_RT_MASK    0x8
+#define RDS_EVENT_ECC_MASK   0x10
+#define RDS_EVENT_RTP_MASK   0x20
+
+#define RDS_RTP_AID    0x4BD7
+
+typedef enum {
+       RDS_EVENT_NONE = 0,
+       RDS_EVENT_PI,
+       RDS_EVENT_PS,
+       RDS_EVENT_AF,
+       RDS_EVENT_RT,
+       RDS_EVENT_ECC,
+       RDS_EVENT_RTP,
+} fm_rds_event_type_enum;
+
+enum {
+       RDS_GRPTYPE_0A = 0x0,
+       RDS_GRPTYPE_0B = 0x1,
+       RDS_GRPTYPE_1A = 0x2,
+       RDS_GRPTYPE_2A = 0x4,
+       RDS_GRPTYPE_2B = 0x5,
+       RDS_GRPTYPE_3A = 0x6,
+       RDS_GRPTYPE_5A = 0xA,
+       RDS_GRPTYPE_6A = 0xC,
+       RDS_GRPTYPE_7A = 0xE,
+       RDS_GRPTYPE_8A = 0x10,
+       RDS_GRPTYPE_9A = 0x12,
+       RDS_GRPTYPE_11A = 0x16,
+       RDS_GRPTYPE_12A = 0x18,
+       RDS_GRPTYPE_13A = 0x1A,
+       RDS_GRPTYPE_NONE = 0xFF,
+};
+
+u16 OFFSET_WORD[] = {
+               0xFC,
+               0x198,
+               0x168,
+               //0x350, /* Need to override C with C' if current version is B */
+               0x1B4,
+};
+
+u8 burstErrorPattern[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 };
+u8 burstErrorLen[] = { 1, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5 };
+
+#ifndef        USE_RDS_HW_DECODER
+u16 CRC_LUT[] = {
+0x0000, 0x01b9, 0x0372, 0x02cb,
+0x035d, 0x02e4, 0x002f, 0x0196,
+0x0303, 0x02ba, 0x0071, 0x01c8,
+0x005e, 0x01e7, 0x032c, 0x0295,
+0x03bf, 0x0206, 0x00cd, 0x0174,
+0x00e2, 0x015b, 0x0390, 0x0229,
+0x00bc, 0x0105, 0x03ce, 0x0277,
+0x03e1, 0x0258, 0x0093, 0x012a,
+0x02c7, 0x037e, 0x01b5, 0x000c,
+0x019a, 0x0023, 0x02e8, 0x0351,
+0x01c4, 0x007d, 0x02b6, 0x030f,
+0x0299, 0x0320, 0x01eb, 0x0052,
+0x0178, 0x00c1, 0x020a, 0x03b3,
+0x0225, 0x039c, 0x0157, 0x00ee,
+0x027b, 0x03c2, 0x0109, 0x00b0,
+0x0126, 0x009f, 0x0254, 0x03ed,
+0x0037, 0x018e, 0x0345, 0x02fc,
+0x036a, 0x02d3, 0x0018, 0x01a1,
+0x0334, 0x028d, 0x0046, 0x01ff,
+0x0069, 0x01d0, 0x031b, 0x02a2,
+0x0388, 0x0231, 0x00fa, 0x0143,
+0x00d5, 0x016c, 0x03a7, 0x021e,
+0x008b, 0x0132, 0x03f9, 0x0240,
+0x03d6, 0x026f, 0x00a4, 0x011d,
+0x02f0, 0x0349, 0x0182, 0x003b,
+0x01ad, 0x0014, 0x02df, 0x0366,
+0x01f3, 0x004a, 0x0281, 0x0338,
+0x02ae, 0x0317, 0x01dc, 0x0065,
+0x014f, 0x00f6, 0x023d, 0x0384,
+0x0212, 0x03ab, 0x0160, 0x00d9,
+0x024c, 0x03f5, 0x013e, 0x0087,
+0x0111, 0x00a8, 0x0263, 0x03da,
+0x006e, 0x01d7, 0x031c, 0x02a5,
+0x0333, 0x028a, 0x0041, 0x01f8,
+0x036d, 0x02d4, 0x001f, 0x01a6,
+0x0030, 0x0189, 0x0342, 0x02fb,
+0x03d1, 0x0268, 0x00a3, 0x011a,
+0x008c, 0x0135, 0x03fe, 0x0247,
+0x00d2, 0x016b, 0x03a0, 0x0219,
+0x038f, 0x0236, 0x00fd, 0x0144,
+0x02a9, 0x0310, 0x01db, 0x0062,
+0x01f4, 0x004d, 0x0286, 0x033f,
+0x01aa, 0x0013, 0x02d8, 0x0361,
+0x02f7, 0x034e, 0x0185, 0x003c,
+0x0116, 0x00af, 0x0264, 0x03dd,
+0x024b, 0x03f2, 0x0139, 0x0080,
+0x0215, 0x03ac, 0x0167, 0x00de,
+0x0148, 0x00f1, 0x023a, 0x0383,
+0x0059, 0x01e0, 0x032b, 0x0292,
+0x0304, 0x02bd, 0x0076, 0x01cf,
+0x035a, 0x02e3, 0x0028, 0x0191,
+0x0007, 0x01be, 0x0375, 0x02cc,
+0x03e6, 0x025f, 0x0094, 0x012d,
+0x00bb, 0x0102, 0x03c9, 0x0270,
+0x00e5, 0x015c, 0x0397, 0x022e,
+0x03b8, 0x0201, 0x00ca, 0x0173,
+0x029e, 0x0327, 0x01ec, 0x0055,
+0x01c3, 0x007a, 0x02b1, 0x0308,
+0x019d, 0x0024, 0x02ef, 0x0356,
+0x02c0, 0x0379, 0x01b2, 0x000b,
+0x0121, 0x0098, 0x0253, 0x03ea,
+0x027c, 0x03c5, 0x010e, 0x00b7,
+0x0222, 0x039b, 0x0150, 0x00e9,
+0x017f, 0x00c6, 0x020d, 0x03b4,
+0x00dc, 0x0165, 0x03ae, 0x0217,
+0x0381, 0x0238, 0x00f3, 0x014a,
+0x03df, 0x0266, 0x00ad, 0x0114,
+0x0082, 0x013b, 0x03f0, 0x0249,
+0x0363, 0x02da, 0x0011, 0x01a8,
+0x003e, 0x0187, 0x034c, 0x02f5,
+0x0060, 0x01d9, 0x0312, 0x02ab,
+0x033d, 0x0284, 0x004f, 0x01f6,
+0x021b, 0x03a2, 0x0169, 0x00d0,
+0x0146, 0x00ff, 0x0234, 0x038d,
+0x0118, 0x00a1, 0x026a, 0x03d3,
+0x0245, 0x03fc, 0x0137, 0x008e,
+0x01a4, 0x001d, 0x02d6, 0x036f,
+0x02f9, 0x0340, 0x018b, 0x0032,
+0x02a7, 0x031e, 0x01d5, 0x006c,
+0x01fa, 0x0043, 0x0288, 0x0331,
+0x00eb, 0x0152, 0x0399, 0x0220,
+0x03b6, 0x020f, 0x00c4, 0x017d,
+0x03e8, 0x0251, 0x009a, 0x0123,
+0x00b5, 0x010c, 0x03c7, 0x027e,
+0x0354, 0x02ed, 0x0026, 0x019f,
+0x0009, 0x01b0, 0x037b, 0x02c2,
+0x0057, 0x01ee, 0x0325, 0x029c,
+0x030a, 0x02b3, 0x0078, 0x01c1,
+0x022c, 0x0395, 0x015e, 0x00e7,
+0x0171, 0x00c8, 0x0203, 0x03ba,
+0x012f, 0x0096, 0x025d, 0x03e4,
+0x0272, 0x03cb, 0x0100, 0x00b9,
+0x0193, 0x002a, 0x02e1, 0x0358,
+0x02ce, 0x0377, 0x01bc, 0x0005,
+0x0290, 0x0329, 0x01e2, 0x005b,
+0x01cd, 0x0074, 0x02bf, 0x0306,
+0x00b2, 0x010b, 0x03c0, 0x0279,
+0x03ef, 0x0256, 0x009d, 0x0124,
+0x03b1, 0x0208, 0x00c3, 0x017a,
+0x00ec, 0x0155, 0x039e, 0x0227,
+0x030d, 0x02b4, 0x007f, 0x01c6,
+0x0050, 0x01e9, 0x0322, 0x029b,
+0x000e, 0x01b7, 0x037c, 0x02c5,
+0x0353, 0x02ea, 0x0021, 0x0198,
+0x0275, 0x03cc, 0x0107, 0x00be,
+0x0128, 0x0091, 0x025a, 0x03e3,
+0x0176, 0x00cf, 0x0204, 0x03bd,
+0x022b, 0x0392, 0x0159, 0x00e0,
+0x01ca, 0x0073, 0x02b8, 0x0301,
+0x0297, 0x032e, 0x01e5, 0x005c,
+0x02c9, 0x0370, 0x01bb, 0x0002,
+0x0194, 0x002d, 0x02e6, 0x035f,
+0x0085, 0x013c, 0x03f7, 0x024e,
+0x03d8, 0x0261, 0x00aa, 0x0113,
+0x0386, 0x023f, 0x00f4, 0x014d,
+0x00db, 0x0162, 0x03a9, 0x0210,
+0x033a, 0x0283, 0x0048, 0x01f1,
+0x0067, 0x01de, 0x0315, 0x02ac,
+0x0039, 0x0180, 0x034b, 0x02f2,
+0x0364, 0x02dd, 0x0016, 0x01af,
+0x0242, 0x03fb, 0x0130, 0x0089,
+0x011f, 0x00a6, 0x026d, 0x03d4,
+0x0141, 0x00f8, 0x0233, 0x038a,
+0x021c, 0x03a5, 0x016e, 0x00d7,
+0x01fd, 0x0044, 0x028f, 0x0336,
+0x02a0, 0x0319, 0x01d2, 0x006b,
+0x02fe, 0x0347, 0x018c, 0x0035,
+0x01a3, 0x001a, 0x02d1, 0x0368,
+0x01b8, 0x0001, 0x02ca, 0x0373,
+0x02e5, 0x035c, 0x0197, 0x002e,
+0x02bb, 0x0302, 0x01c9, 0x0070,
+0x01e6, 0x005f, 0x0294, 0x032d,
+0x0207, 0x03be, 0x0175, 0x00cc,
+0x015a, 0x00e3, 0x0228, 0x0391,
+0x0104, 0x00bd, 0x0276, 0x03cf,
+0x0259, 0x03e0, 0x012b, 0x0092,
+0x037f, 0x02c6, 0x000d, 0x01b4,
+0x0022, 0x019b, 0x0350, 0x02e9,
+0x007c, 0x01c5, 0x030e, 0x02b7,
+0x0321, 0x0298, 0x0053, 0x01ea,
+0x00c0, 0x0179, 0x03b2, 0x020b,
+0x039d, 0x0224, 0x00ef, 0x0156,
+0x03c3, 0x027a, 0x00b1, 0x0108,
+0x009e, 0x0127, 0x03ec, 0x0255,
+0x018f, 0x0036, 0x02fd, 0x0344,
+0x02d2, 0x036b, 0x01a0, 0x0019,
+0x028c, 0x0335, 0x01fe, 0x0047,
+0x01d1, 0x0068, 0x02a3, 0x031a,
+0x0230, 0x0389, 0x0142, 0x00fb,
+0x016d, 0x00d4, 0x021f, 0x03a6,
+0x0133, 0x008a, 0x0241, 0x03f8,
+0x026e, 0x03d7, 0x011c, 0x00a5,
+0x0348, 0x02f1, 0x003a, 0x0183,
+0x0015, 0x01ac, 0x0367, 0x02de,
+0x004b, 0x01f2, 0x0339, 0x0280,
+0x0316, 0x02af, 0x0064, 0x01dd,
+0x00f7, 0x014e, 0x0385, 0x023c,
+0x03aa, 0x0213, 0x00d8, 0x0161,
+0x03f4, 0x024d, 0x0086, 0x013f,
+0x00a9, 0x0110, 0x03db, 0x0262,
+0x01d6, 0x006f, 0x02a4, 0x031d,
+0x028b, 0x0332, 0x01f9, 0x0040,
+0x02d5, 0x036c, 0x01a7, 0x001e,
+0x0188, 0x0031, 0x02fa, 0x0343,
+0x0269, 0x03d0, 0x011b, 0x00a2,
+0x0134, 0x008d, 0x0246, 0x03ff,
+0x016a, 0x00d3, 0x0218, 0x03a1,
+0x0237, 0x038e, 0x0145, 0x00fc,
+0x0311, 0x02a8, 0x0063, 0x01da,
+0x004c, 0x01f5, 0x033e, 0x0287,
+0x0012, 0x01ab, 0x0360, 0x02d9,
+0x034f, 0x02f6, 0x003d, 0x0184,
+0x00ae, 0x0117, 0x03dc, 0x0265,
+0x03f3, 0x024a, 0x0081, 0x0138,
+0x03ad, 0x0214, 0x00df, 0x0166,
+0x00f0, 0x0149, 0x0382, 0x023b,
+0x01e1, 0x0058, 0x0293, 0x032a,
+0x02bc, 0x0305, 0x01ce, 0x0077,
+0x02e2, 0x035b, 0x0190, 0x0029,
+0x01bf, 0x0006, 0x02cd, 0x0374,
+0x025e, 0x03e7, 0x012c, 0x0095,
+0x0103, 0x00ba, 0x0271, 0x03c8,
+0x015d, 0x00e4, 0x022f, 0x0396,
+0x0200, 0x03b9, 0x0172, 0x00cb,
+0x0326, 0x029f, 0x0054, 0x01ed,
+0x007b, 0x01c2, 0x0309, 0x02b0,
+0x0025, 0x019c, 0x0357, 0x02ee,
+0x0378, 0x02c1, 0x000a, 0x01b3,
+0x0099, 0x0120, 0x03eb, 0x0252,
+0x03c4, 0x027d, 0x00b6, 0x010f,
+0x039a, 0x0223, 0x00e8, 0x0151,
+0x00c7, 0x017e, 0x03b5, 0x020c,
+0x0164, 0x00dd, 0x0216, 0x03af,
+0x0239, 0x0380, 0x014b, 0x00f2,
+0x0267, 0x03de, 0x0115, 0x00ac,
+0x013a, 0x0083, 0x0248, 0x03f1,
+0x02db, 0x0362, 0x01a9, 0x0010,
+0x0186, 0x003f, 0x02f4, 0x034d,
+0x01d8, 0x0061, 0x02aa, 0x0313,
+0x0285, 0x033c, 0x01f7, 0x004e,
+0x03a3, 0x021a, 0x00d1, 0x0168,
+0x00fe, 0x0147, 0x038c, 0x0235,
+0x00a0, 0x0119, 0x03d2, 0x026b,
+0x03fd, 0x0244, 0x008f, 0x0136,
+0x001c, 0x01a5, 0x036e, 0x02d7,
+0x0341, 0x02f8, 0x0033, 0x018a,
+0x031f, 0x02a6, 0x006d, 0x01d4,
+0x0042, 0x01fb, 0x0330, 0x0289,
+0x0153, 0x00ea, 0x0221, 0x0398,
+0x020e, 0x03b7, 0x017c, 0x00c5,
+0x0250, 0x03e9, 0x0122, 0x009b,
+0x010d, 0x00b4, 0x027f, 0x03c6,
+0x02ec, 0x0355, 0x019e, 0x0027,
+0x01b1, 0x0008, 0x02c3, 0x037a,
+0x01ef, 0x0056, 0x029d, 0x0324,
+0x02b2, 0x030b, 0x01c0, 0x0079,
+0x0394, 0x022d, 0x00e6, 0x015f,
+0x00c9, 0x0170, 0x03bb, 0x0202,
+0x0097, 0x012e, 0x03e5, 0x025c,
+0x03ca, 0x0273, 0x00b8, 0x0101,
+0x002b, 0x0192, 0x0359, 0x02e0,
+0x0376, 0x02cf, 0x0004, 0x01bd,
+0x0328, 0x0291, 0x005a, 0x01e3,
+0x0075, 0x01cc, 0x0307, 0x02be,
+0x010a, 0x00b3, 0x0278, 0x03c1,
+0x0257, 0x03ee, 0x0125, 0x009c,
+0x0209, 0x03b0, 0x017b, 0x00c2,
+0x0154, 0x00ed, 0x0226, 0x039f,
+0x02b5, 0x030c, 0x01c7, 0x007e,
+0x01e8, 0x0051, 0x029a, 0x0323,
+0x01b6, 0x000f, 0x02c4, 0x037d,
+0x02eb, 0x0352, 0x0199, 0x0020,
+0x03cd, 0x0274, 0x00bf, 0x0106,
+0x0090, 0x0129, 0x03e2, 0x025b,
+0x00ce, 0x0177, 0x03bc, 0x0205,
+0x0393, 0x022a, 0x00e1, 0x0158,
+0x0072, 0x01cb, 0x0300, 0x02b9,
+0x032f, 0x0296, 0x005d, 0x01e4,
+0x0371, 0x02c8, 0x0003, 0x01ba,
+0x002c, 0x0195, 0x035e, 0x02e7,
+0x013d, 0x0084, 0x024f, 0x03f6,
+0x0260, 0x03d9, 0x0112, 0x00ab,
+0x023e, 0x0387, 0x014c, 0x00f5,
+0x0163, 0x00da, 0x0211, 0x03a8,
+0x0282, 0x033b, 0x01f0, 0x0049,
+0x01df, 0x0066, 0x02ad, 0x0314,
+0x0181, 0x0038, 0x02f3, 0x034a,
+0x02dc, 0x0365, 0x01ae, 0x0017,
+0x03fa, 0x0243, 0x0088, 0x0131,
+0x00a7, 0x011e, 0x03d5, 0x026c,
+0x00f9, 0x0140, 0x038b, 0x0232,
+0x03a4, 0x021d, 0x00d6, 0x016f,
+0x0045, 0x01fc, 0x0337, 0x028e,
+0x0318, 0x02a1, 0x006a, 0x01d3,
+0x0346, 0x02ff, 0x0034, 0x018d,
+0x001b, 0x01a2, 0x0369, 0x02d0,
+0x0370, 0x02c9, 0x0002, 0x01bb,
+0x002d, 0x0194, 0x035f, 0x02e6,
+0x0073, 0x01ca, 0x0301, 0x02b8,
+0x032e, 0x0297, 0x005c, 0x01e5,
+0x00cf, 0x0176, 0x03bd, 0x0204,
+0x0392, 0x022b, 0x00e0, 0x0159,
+0x03cc, 0x0275, 0x00be, 0x0107,
+0x0091, 0x0128, 0x03e3, 0x025a,
+0x01b7, 0x000e, 0x02c5, 0x037c,
+0x02ea, 0x0353, 0x0198, 0x0021,
+0x02b4, 0x030d, 0x01c6, 0x007f,
+0x01e9, 0x0050, 0x029b, 0x0322,
+0x0208, 0x03b1, 0x017a, 0x00c3,
+0x0155, 0x00ec, 0x0227, 0x039e,
+0x010b, 0x00b2, 0x0279, 0x03c0,
+0x0256, 0x03ef, 0x0124, 0x009d,
+0x0347, 0x02fe, 0x0035, 0x018c,
+0x001a, 0x01a3, 0x0368, 0x02d1,
+0x0044, 0x01fd, 0x0336, 0x028f,
+0x0319, 0x02a0, 0x006b, 0x01d2,
+0x00f8, 0x0141, 0x038a, 0x0233,
+0x03a5, 0x021c, 0x00d7, 0x016e,
+0x03fb, 0x0242, 0x0089, 0x0130,
+0x00a6, 0x011f, 0x03d4, 0x026d,
+0x0180, 0x0039, 0x02f2, 0x034b,
+0x02dd, 0x0364, 0x01af, 0x0016,
+0x0283, 0x033a, 0x01f1, 0x0048,
+0x01de, 0x0067, 0x02ac, 0x0315,
+0x023f, 0x0386, 0x014d, 0x00f4,
+0x0162, 0x00db, 0x0210, 0x03a9,
+0x013c, 0x0085, 0x024e, 0x03f7,
+0x0261, 0x03d8, 0x0113, 0x00aa,
+0x031e, 0x02a7, 0x006c, 0x01d5,
+0x0043, 0x01fa, 0x0331, 0x0288,
+0x001d, 0x01a4, 0x036f, 0x02d6,
+0x0340, 0x02f9, 0x0032, 0x018b,
+0x00a1, 0x0118, 0x03d3, 0x026a,
+0x03fc, 0x0245, 0x008e, 0x0137,
+0x03a2, 0x021b, 0x00d0, 0x0169,
+0x00ff, 0x0146, 0x038d, 0x0234,
+0x01d9, 0x0060, 0x02ab, 0x0312,
+0x0284, 0x033d, 0x01f6, 0x004f,
+0x02da, 0x0363, 0x01a8, 0x0011,
+0x0187, 0x003e, 0x02f5, 0x034c,
+0x0266, 0x03df, 0x0114, 0x00ad,
+0x013b, 0x0082, 0x0249, 0x03f0,
+0x0165, 0x00dc, 0x0217, 0x03ae,
+0x0238, 0x0381, 0x014a, 0x00f3,
+0x0329, 0x0290, 0x005b, 0x01e2,
+0x0074, 0x01cd, 0x0306, 0x02bf,
+0x002a, 0x0193, 0x0358, 0x02e1,
+0x0377, 0x02ce, 0x0005, 0x01bc,
+0x0096, 0x012f, 0x03e4, 0x025d,
+0x03cb, 0x0272, 0x00b9, 0x0100,
+0x0395, 0x022c, 0x00e7, 0x015e,
+0x00c8, 0x0171, 0x03ba, 0x0203,
+0x01ee, 0x0057, 0x029c, 0x0325,
+0x02b3, 0x030a, 0x01c1, 0x0078,
+0x02ed, 0x0354, 0x019f, 0x0026,
+0x01b0, 0x0009, 0x02c2, 0x037b,
+0x0251, 0x03e8, 0x0123, 0x009a,
+0x010c, 0x00b5, 0x027e, 0x03c7,
+0x0152, 0x00eb, 0x0220, 0x0399,
+0x020f, 0x03b6, 0x017d, 0x00c4,
+0x03ac, 0x0215, 0x00de, 0x0167,
+0x00f1, 0x0148, 0x0383, 0x023a,
+0x00af, 0x0116, 0x03dd, 0x0264,
+0x03f2, 0x024b, 0x0080, 0x0139,
+0x0013, 0x01aa, 0x0361, 0x02d8,
+0x034e, 0x02f7, 0x003c, 0x0185,
+0x0310, 0x02a9, 0x0062, 0x01db,
+0x004d, 0x01f4, 0x033f, 0x0286,
+0x016b, 0x00d2, 0x0219, 0x03a0,
+0x0236, 0x038f, 0x0144, 0x00fd,
+0x0268, 0x03d1, 0x011a, 0x00a3,
+0x0135, 0x008c, 0x0247, 0x03fe,
+0x02d4, 0x036d, 0x01a6, 0x001f,
+0x0189, 0x0030, 0x02fb, 0x0342,
+0x01d7, 0x006e, 0x02a5, 0x031c,
+0x028a, 0x0333, 0x01f8, 0x0041,
+0x039b, 0x0222, 0x00e9, 0x0150,
+0x00c6, 0x017f, 0x03b4, 0x020d,
+0x0098, 0x0121, 0x03ea, 0x0253,
+0x03c5, 0x027c, 0x00b7, 0x010e,
+0x0024, 0x019d, 0x0356, 0x02ef,
+0x0379, 0x02c0, 0x000b, 0x01b2,
+0x0327, 0x029e, 0x0055, 0x01ec,
+0x007a, 0x01c3, 0x0308, 0x02b1,
+0x015c, 0x00e5, 0x022e, 0x0397,
+0x0201, 0x03b8, 0x0173, 0x00ca,
+0x025f, 0x03e6, 0x012d, 0x0094,
+0x0102, 0x00bb, 0x0270, 0x03c9,
+0x02e3, 0x035a, 0x0191, 0x0028,
+0x01be, 0x0007, 0x02cc, 0x0375,
+0x01e0, 0x0059, 0x0292, 0x032b,
+0x02bd, 0x0304, 0x01cf, 0x0076,
+0x03c2, 0x027b, 0x00b0, 0x0109,
+0x009f, 0x0126, 0x03ed, 0x0254,
+0x00c1, 0x0178, 0x03b3, 0x020a,
+0x039c, 0x0225, 0x00ee, 0x0157,
+0x007d, 0x01c4, 0x030f, 0x02b6,
+0x0320, 0x0299, 0x0052, 0x01eb,
+0x037e, 0x02c7, 0x000c, 0x01b5,
+0x0023, 0x019a, 0x0351, 0x02e8,
+0x0105, 0x00bc, 0x0277, 0x03ce,
+0x0258, 0x03e1, 0x012a, 0x0093,
+0x0206, 0x03bf, 0x0174, 0x00cd,
+0x015b, 0x00e2, 0x0229, 0x0390,
+0x02ba, 0x0303, 0x01c8, 0x0071,
+0x01e7, 0x005e, 0x0295, 0x032c,
+0x01b9, 0x0000, 0x02cb, 0x0372,
+0x02e4, 0x035d, 0x0196, 0x002f,
+0x03f5, 0x024c, 0x0087, 0x013e,
+0x00a8, 0x0111, 0x03da, 0x0263,
+0x00f6, 0x014f, 0x0384, 0x023d,
+0x03ab, 0x0212, 0x00d9, 0x0160,
+0x004a, 0x01f3, 0x0338, 0x0281,
+0x0317, 0x02ae, 0x0065, 0x01dc,
+0x0349, 0x02f0, 0x003b, 0x0182,
+0x0014, 0x01ad, 0x0366, 0x02df,
+0x0132, 0x008b, 0x0240, 0x03f9,
+0x026f, 0x03d6, 0x011d, 0x00a4,
+0x0231, 0x0388, 0x0143, 0x00fa,
+0x016c, 0x00d5, 0x021e, 0x03a7,
+0x028d, 0x0334, 0x01ff, 0x0046,
+0x01d0, 0x0069, 0x02a2, 0x031b,
+0x018e, 0x0037, 0x02fc, 0x0345,
+0x02d3, 0x036a, 0x01a1, 0x0018,
+0x02c8, 0x0371, 0x01ba, 0x0003,
+0x0195, 0x002c, 0x02e7, 0x035e,
+0x01cb, 0x0072, 0x02b9, 0x0300,
+0x0296, 0x032f, 0x01e4, 0x005d,
+0x0177, 0x00ce, 0x0205, 0x03bc,
+0x022a, 0x0393, 0x0158, 0x00e1,
+0x0274, 0x03cd, 0x0106, 0x00bf,
+0x0129, 0x0090, 0x025b, 0x03e2,
+0x000f, 0x01b6, 0x037d, 0x02c4,
+0x0352, 0x02eb, 0x0020, 0x0199,
+0x030c, 0x02b5, 0x007e, 0x01c7,
+0x0051, 0x01e8, 0x0323, 0x029a,
+0x03b0, 0x0209, 0x00c2, 0x017b,
+0x00ed, 0x0154, 0x039f, 0x0226,
+0x00b3, 0x010a, 0x03c1, 0x0278,
+0x03ee, 0x0257, 0x009c, 0x0125,
+0x02ff, 0x0346, 0x018d, 0x0034,
+0x01a2, 0x001b, 0x02d0, 0x0369,
+0x01fc, 0x0045, 0x028e, 0x0337,
+0x02a1, 0x0318, 0x01d3, 0x006a,
+0x0140, 0x00f9, 0x0232, 0x038b,
+0x021d, 0x03a4, 0x016f, 0x00d6,
+0x0243, 0x03fa, 0x0131, 0x0088,
+0x011e, 0x00a7, 0x026c, 0x03d5,
+0x0038, 0x0181, 0x034a, 0x02f3,
+0x0365, 0x02dc, 0x0017, 0x01ae,
+0x033b, 0x0282, 0x0049, 0x01f0,
+0x0066, 0x01df, 0x0314, 0x02ad,
+0x0387, 0x023e, 0x00f5, 0x014c,
+0x00da, 0x0163, 0x03a8, 0x0211,
+0x0084, 0x013d, 0x03f6, 0x024f,
+0x03d9, 0x0260, 0x00ab, 0x0112,
+0x02a6, 0x031f, 0x01d4, 0x006d,
+0x01fb, 0x0042, 0x0289, 0x0330,
+0x01a5, 0x001c, 0x02d7, 0x036e,
+0x02f8, 0x0341, 0x018a, 0x0033,
+0x0119, 0x00a0, 0x026b, 0x03d2,
+0x0244, 0x03fd, 0x0136, 0x008f,
+0x021a, 0x03a3, 0x0168, 0x00d1,
+0x0147, 0x00fe, 0x0235, 0x038c,
+0x0061, 0x01d8, 0x0313, 0x02aa,
+0x033c, 0x0285, 0x004e, 0x01f7,
+0x0362, 0x02db, 0x0010, 0x01a9,
+0x003f, 0x0186, 0x034d, 0x02f4,
+0x03de, 0x0267, 0x00ac, 0x0115,
+0x0083, 0x013a, 0x03f1, 0x0248,
+0x00dd, 0x0164, 0x03af, 0x0216,
+0x0380, 0x0239, 0x00f2, 0x014b,
+0x0291, 0x0328, 0x01e3, 0x005a,
+0x01cc, 0x0075, 0x02be, 0x0307,
+0x0192, 0x002b, 0x02e0, 0x0359,
+0x02cf, 0x0376, 0x01bd, 0x0004,
+0x012e, 0x0097, 0x025c, 0x03e5,
+0x0273, 0x03ca, 0x0101, 0x00b8,
+0x022d, 0x0394, 0x015f, 0x00e6,
+0x0170, 0x00c9, 0x0202, 0x03bb,
+0x0056, 0x01ef, 0x0324, 0x029d,
+0x030b, 0x02b2, 0x0079, 0x01c0,
+0x0355, 0x02ec, 0x0027, 0x019e,
+0x0008, 0x01b1, 0x037a, 0x02c3,
+0x03e9, 0x0250, 0x009b, 0x0122,
+0x00b4, 0x010d, 0x03c6, 0x027f,
+0x00ea, 0x0153, 0x0398, 0x0221,
+0x03b7, 0x020e, 0x00c5, 0x017c,
+0x0214, 0x03ad, 0x0166, 0x00df,
+0x0149, 0x00f0, 0x023b, 0x0382,
+0x0117, 0x00ae, 0x0265, 0x03dc,
+0x024a, 0x03f3, 0x0138, 0x0081,
+0x01ab, 0x0012, 0x02d9, 0x0360,
+0x02f6, 0x034f, 0x0184, 0x003d,
+0x02a8, 0x0311, 0x01da, 0x0063,
+0x01f5, 0x004c, 0x0287, 0x033e,
+0x00d3, 0x016a, 0x03a1, 0x0218,
+0x038e, 0x0237, 0x00fc, 0x0145,
+0x03d0, 0x0269, 0x00a2, 0x011b,
+0x008d, 0x0134, 0x03ff, 0x0246,
+0x036c, 0x02d5, 0x001e, 0x01a7,
+0x0031, 0x0188, 0x0343, 0x02fa,
+0x006f, 0x01d6, 0x031d, 0x02a4,
+0x0332, 0x028b, 0x0040, 0x01f9,
+0x0223, 0x039a, 0x0151, 0x00e8,
+0x017e, 0x00c7, 0x020c, 0x03b5,
+0x0120, 0x0099, 0x0252, 0x03eb,
+0x027d, 0x03c4, 0x010f, 0x00b6,
+0x019c, 0x0025, 0x02ee, 0x0357,
+0x02c1, 0x0378, 0x01b3, 0x000a,
+0x029f, 0x0326, 0x01ed, 0x0054,
+0x01c2, 0x007b, 0x02b0, 0x0309,
+0x00e4, 0x015d, 0x0396, 0x022f,
+0x03b9, 0x0200, 0x00cb, 0x0172,
+0x03e7, 0x025e, 0x0095, 0x012c,
+0x00ba, 0x0103, 0x03c8, 0x0271,
+0x035b, 0x02e2, 0x0029, 0x0190,
+0x0006, 0x01bf, 0x0374, 0x02cd,
+0x0058, 0x01e1, 0x032a, 0x0293,
+0x0305, 0x02bc, 0x0077, 0x01ce,
+0x027a, 0x03c3, 0x0108, 0x00b1,
+0x0127, 0x009e, 0x0255, 0x03ec,
+0x0179, 0x00c0, 0x020b, 0x03b2,
+0x0224, 0x039d, 0x0156, 0x00ef,
+0x01c5, 0x007c, 0x02b7, 0x030e,
+0x0298, 0x0321, 0x01ea, 0x0053,
+0x02c6, 0x037f, 0x01b4, 0x000d,
+0x019b, 0x0022, 0x02e9, 0x0350,
+0x00bd, 0x0104, 0x03cf, 0x0276,
+0x03e0, 0x0259, 0x0092, 0x012b,
+0x03be, 0x0207, 0x00cc, 0x0175,
+0x00e3, 0x015a, 0x0391, 0x0228,
+0x0302, 0x02bb, 0x0070, 0x01c9,
+0x005f, 0x01e6, 0x032d, 0x0294,
+0x0001, 0x01b8, 0x0373, 0x02ca,
+0x035c, 0x02e5, 0x002e, 0x0197,
+0x024d, 0x03f4, 0x013f, 0x0086,
+0x0110, 0x00a9, 0x0262, 0x03db,
+0x014e, 0x00f7, 0x023c, 0x0385,
+0x0213, 0x03aa, 0x0161, 0x00d8,
+0x01f2, 0x004b, 0x0280, 0x0339,
+0x02af, 0x0316, 0x01dd, 0x0064,
+0x02f1, 0x0348, 0x0183, 0x003a,
+0x01ac, 0x0015, 0x02de, 0x0367,
+0x008a, 0x0133, 0x03f8, 0x0241,
+0x03d7, 0x026e, 0x00a5, 0x011c,
+0x0389, 0x0230, 0x00fb, 0x0142,
+0x00d4, 0x016d, 0x03a6, 0x021f,
+0x0335, 0x028c, 0x0047, 0x01fe,
+0x0068, 0x01d1, 0x031a, 0x02a3,
+0x0036, 0x018f, 0x0344, 0x02fd,
+0x036b, 0x02d2, 0x0019, 0x01a0,
+0x0359, 0x02e0, 0x002b, 0x0192,
+0x0004, 0x01bd, 0x0376, 0x02cf,
+0x005a, 0x01e3, 0x0328, 0x0291,
+0x0307, 0x02be, 0x0075, 0x01cc,
+0x00e6, 0x015f, 0x0394, 0x022d,
+0x03bb, 0x0202, 0x00c9, 0x0170,
+0x03e5, 0x025c, 0x0097, 0x012e,
+0x00b8, 0x0101, 0x03ca, 0x0273,
+0x019e, 0x0027, 0x02ec, 0x0355,
+0x02c3, 0x037a, 0x01b1, 0x0008,
+0x029d, 0x0324, 0x01ef, 0x0056,
+0x01c0, 0x0079, 0x02b2, 0x030b,
+0x0221, 0x0398, 0x0153, 0x00ea,
+0x017c, 0x00c5, 0x020e, 0x03b7,
+0x0122, 0x009b, 0x0250, 0x03e9,
+0x027f, 0x03c6, 0x010d, 0x00b4,
+0x036e, 0x02d7, 0x001c, 0x01a5,
+0x0033, 0x018a, 0x0341, 0x02f8,
+0x006d, 0x01d4, 0x031f, 0x02a6,
+0x0330, 0x0289, 0x0042, 0x01fb,
+0x00d1, 0x0168, 0x03a3, 0x021a,
+0x038c, 0x0235, 0x00fe, 0x0147,
+0x03d2, 0x026b, 0x00a0, 0x0119,
+0x008f, 0x0136, 0x03fd, 0x0244,
+0x01a9, 0x0010, 0x02db, 0x0362,
+0x02f4, 0x034d, 0x0186, 0x003f,
+0x02aa, 0x0313, 0x01d8, 0x0061,
+0x01f7, 0x004e, 0x0285, 0x033c,
+0x0216, 0x03af, 0x0164, 0x00dd,
+0x014b, 0x00f2, 0x0239, 0x0380,
+0x0115, 0x00ac, 0x0267, 0x03de,
+0x0248, 0x03f1, 0x013a, 0x0083,
+0x0337, 0x028e, 0x0045, 0x01fc,
+0x006a, 0x01d3, 0x0318, 0x02a1,
+0x0034, 0x018d, 0x0346, 0x02ff,
+0x0369, 0x02d0, 0x001b, 0x01a2,
+0x0088, 0x0131, 0x03fa, 0x0243,
+0x03d5, 0x026c, 0x00a7, 0x011e,
+0x038b, 0x0232, 0x00f9, 0x0140,
+0x00d6, 0x016f, 0x03a4, 0x021d,
+0x01f0, 0x0049, 0x0282, 0x033b,
+0x02ad, 0x0314, 0x01df, 0x0066,
+0x02f3, 0x034a, 0x0181, 0x0038,
+0x01ae, 0x0017, 0x02dc, 0x0365,
+0x024f, 0x03f6, 0x013d, 0x0084,
+0x0112, 0x00ab, 0x0260, 0x03d9,
+0x014c, 0x00f5, 0x023e, 0x0387,
+0x0211, 0x03a8, 0x0163, 0x00da,
+0x0300, 0x02b9, 0x0072, 0x01cb,
+0x005d, 0x01e4, 0x032f, 0x0296,
+0x0003, 0x01ba, 0x0371, 0x02c8,
+0x035e, 0x02e7, 0x002c, 0x0195,
+0x00bf, 0x0106, 0x03cd, 0x0274,
+0x03e2, 0x025b, 0x0090, 0x0129,
+0x03bc, 0x0205, 0x00ce, 0x0177,
+0x00e1, 0x0158, 0x0393, 0x022a,
+0x01c7, 0x007e, 0x02b5, 0x030c,
+0x029a, 0x0323, 0x01e8, 0x0051,
+0x02c4, 0x037d, 0x01b6, 0x000f,
+0x0199, 0x0020, 0x02eb, 0x0352,
+0x0278, 0x03c1, 0x010a, 0x00b3,
+0x0125, 0x009c, 0x0257, 0x03ee,
+0x017b, 0x00c2, 0x0209, 0x03b0,
+0x0226, 0x039f, 0x0154, 0x00ed,
+0x0385, 0x023c, 0x00f7, 0x014e,
+0x00d8, 0x0161, 0x03aa, 0x0213,
+0x0086, 0x013f, 0x03f4, 0x024d,
+0x03db, 0x0262, 0x00a9, 0x0110,
+0x003a, 0x0183, 0x0348, 0x02f1,
+0x0367, 0x02de, 0x0015, 0x01ac,
+0x0339, 0x0280, 0x004b, 0x01f2,
+0x0064, 0x01dd, 0x0316, 0x02af,
+0x0142, 0x00fb, 0x0230, 0x0389,
+0x021f, 0x03a6, 0x016d, 0x00d4,
+0x0241, 0x03f8, 0x0133, 0x008a,
+0x011c, 0x00a5, 0x026e, 0x03d7,
+0x02fd, 0x0344, 0x018f, 0x0036,
+0x01a0, 0x0019, 0x02d2, 0x036b,
+0x01fe, 0x0047, 0x028c, 0x0335,
+0x02a3, 0x031a, 0x01d1, 0x0068,
+0x03b2, 0x020b, 0x00c0, 0x0179,
+0x00ef, 0x0156, 0x039d, 0x0224,
+0x00b1, 0x0108, 0x03c3, 0x027a,
+0x03ec, 0x0255, 0x009e, 0x0127,
+0x000d, 0x01b4, 0x037f, 0x02c6,
+0x0350, 0x02e9, 0x0022, 0x019b,
+0x030e, 0x02b7, 0x007c, 0x01c5,
+0x0053, 0x01ea, 0x0321, 0x0298,
+0x0175, 0x00cc, 0x0207, 0x03be,
+0x0228, 0x0391, 0x015a, 0x00e3,
+0x0276, 0x03cf, 0x0104, 0x00bd,
+0x012b, 0x0092, 0x0259, 0x03e0,
+0x02ca, 0x0373, 0x01b8, 0x0001,
+0x0197, 0x002e, 0x02e5, 0x035c,
+0x01c9, 0x0070, 0x02bb, 0x0302,
+0x0294, 0x032d, 0x01e6, 0x005f,
+0x03eb, 0x0252, 0x0099, 0x0120,
+0x00b6, 0x010f, 0x03c4, 0x027d,
+0x00e8, 0x0151, 0x039a, 0x0223,
+0x03b5, 0x020c, 0x00c7, 0x017e,
+0x0054, 0x01ed, 0x0326, 0x029f,
+0x0309, 0x02b0, 0x007b, 0x01c2,
+0x0357, 0x02ee, 0x0025, 0x019c,
+0x000a, 0x01b3, 0x0378, 0x02c1,
+0x012c, 0x0095, 0x025e, 0x03e7,
+0x0271, 0x03c8, 0x0103, 0x00ba,
+0x022f, 0x0396, 0x015d, 0x00e4,
+0x0172, 0x00cb, 0x0200, 0x03b9,
+0x0293, 0x032a, 0x01e1, 0x0058,
+0x01ce, 0x0077, 0x02bc, 0x0305,
+0x0190, 0x0029, 0x02e2, 0x035b,
+0x02cd, 0x0374, 0x01bf, 0x0006,
+0x03dc, 0x0265, 0x00ae, 0x0117,
+0x0081, 0x0138, 0x03f3, 0x024a,
+0x00df, 0x0166, 0x03ad, 0x0214,
+0x0382, 0x023b, 0x00f0, 0x0149,
+0x0063, 0x01da, 0x0311, 0x02a8,
+0x033e, 0x0287, 0x004c, 0x01f5,
+0x0360, 0x02d9, 0x0012, 0x01ab,
+0x003d, 0x0184, 0x034f, 0x02f6,
+0x011b, 0x00a2, 0x0269, 0x03d0,
+0x0246, 0x03ff, 0x0134, 0x008d,
+0x0218, 0x03a1, 0x016a, 0x00d3,
+0x0145, 0x00fc, 0x0237, 0x038e,
+0x02a4, 0x031d, 0x01d6, 0x006f,
+0x01f9, 0x0040, 0x028b, 0x0332,
+0x01a7, 0x001e, 0x02d5, 0x036c,
+0x02fa, 0x0343, 0x0188, 0x0031,
+0x02e1, 0x0358, 0x0193, 0x002a,
+0x01bc, 0x0005, 0x02ce, 0x0377,
+0x01e2, 0x005b, 0x0290, 0x0329,
+0x02bf, 0x0306, 0x01cd, 0x0074,
+0x015e, 0x00e7, 0x022c, 0x0395,
+0x0203, 0x03ba, 0x0171, 0x00c8,
+0x025d, 0x03e4, 0x012f, 0x0096,
+0x0100, 0x00b9, 0x0272, 0x03cb,
+0x0026, 0x019f, 0x0354, 0x02ed,
+0x037b, 0x02c2, 0x0009, 0x01b0,
+0x0325, 0x029c, 0x0057, 0x01ee,
+0x0078, 0x01c1, 0x030a, 0x02b3,
+0x0399, 0x0220, 0x00eb, 0x0152,
+0x00c4, 0x017d, 0x03b6, 0x020f,
+0x009a, 0x0123, 0x03e8, 0x0251,
+0x03c7, 0x027e, 0x00b5, 0x010c,
+0x02d6, 0x036f, 0x01a4, 0x001d,
+0x018b, 0x0032, 0x02f9, 0x0340,
+0x01d5, 0x006c, 0x02a7, 0x031e,
+0x0288, 0x0331, 0x01fa, 0x0043,
+0x0169, 0x00d0, 0x021b, 0x03a2,
+0x0234, 0x038d, 0x0146, 0x00ff,
+0x026a, 0x03d3, 0x0118, 0x00a1,
+0x0137, 0x008e, 0x0245, 0x03fc,
+0x0011, 0x01a8, 0x0363, 0x02da,
+0x034c, 0x02f5, 0x003e, 0x0187,
+0x0312, 0x02ab, 0x0060, 0x01d9,
+0x004f, 0x01f6, 0x033d, 0x0284,
+0x03ae, 0x0217, 0x00dc, 0x0165,
+0x00f3, 0x014a, 0x0381, 0x0238,
+0x00ad, 0x0114, 0x03df, 0x0266,
+0x03f0, 0x0249, 0x0082, 0x013b,
+0x028f, 0x0336, 0x01fd, 0x0044,
+0x01d2, 0x006b, 0x02a0, 0x0319,
+0x018c, 0x0035, 0x02fe, 0x0347,
+0x02d1, 0x0368, 0x01a3, 0x001a,
+0x0130, 0x0089, 0x0242, 0x03fb,
+0x026d, 0x03d4, 0x011f, 0x00a6,
+0x0233, 0x038a, 0x0141, 0x00f8,
+0x016e, 0x00d7, 0x021c, 0x03a5,
+0x0048, 0x01f1, 0x033a, 0x0283,
+0x0315, 0x02ac, 0x0067, 0x01de,
+0x034b, 0x02f2, 0x0039, 0x0180,
+0x0016, 0x01af, 0x0364, 0x02dd,
+0x03f7, 0x024e, 0x0085, 0x013c,
+0x00aa, 0x0113, 0x03d8, 0x0261,
+0x00f4, 0x014d, 0x0386, 0x023f,
+0x03a9, 0x0210, 0x00db, 0x0162,
+0x02b8, 0x0301, 0x01ca, 0x0073,
+0x01e5, 0x005c, 0x0297, 0x032e,
+0x01bb, 0x0002, 0x02c9, 0x0370,
+0x02e6, 0x035f, 0x0194, 0x002d,
+0x0107, 0x00be, 0x0275, 0x03cc,
+0x025a, 0x03e3, 0x0128, 0x0091,
+0x0204, 0x03bd, 0x0176, 0x00cf,
+0x0159, 0x00e0, 0x022b, 0x0392,
+0x007f, 0x01c6, 0x030d, 0x02b4,
+0x0322, 0x029b, 0x0050, 0x01e9,
+0x037c, 0x02c5, 0x000e, 0x01b7,
+0x0021, 0x0198, 0x0353, 0x02ea,
+0x03c0, 0x0279, 0x00b2, 0x010b,
+0x009d, 0x0124, 0x03ef, 0x0256,
+0x00c3, 0x017a, 0x03b1, 0x0208,
+0x039e, 0x0227, 0x00ec, 0x0155,
+0x023d, 0x0384, 0x014f, 0x00f6,
+0x0160, 0x00d9, 0x0212, 0x03ab,
+0x013e, 0x0087, 0x024c, 0x03f5,
+0x0263, 0x03da, 0x0111, 0x00a8,
+0x0182, 0x003b, 0x02f0, 0x0349,
+0x02df, 0x0366, 0x01ad, 0x0014,
+0x0281, 0x0338, 0x01f3, 0x004a,
+0x01dc, 0x0065, 0x02ae, 0x0317,
+0x00fa, 0x0143, 0x0388, 0x0231,
+0x03a7, 0x021e, 0x00d5, 0x016c,
+0x03f9, 0x0240, 0x008b, 0x0132,
+0x00a4, 0x011d, 0x03d6, 0x026f,
+0x0345, 0x02fc, 0x0037, 0x018e,
+0x0018, 0x01a1, 0x036a, 0x02d3,
+0x0046, 0x01ff, 0x0334, 0x028d,
+0x031b, 0x02a2, 0x0069, 0x01d0,
+0x020a, 0x03b3, 0x0178, 0x00c1,
+0x0157, 0x00ee, 0x0225, 0x039c,
+0x0109, 0x00b0, 0x027b, 0x03c2,
+0x0254, 0x03ed, 0x0126, 0x009f,
+0x01b5, 0x000c, 0x02c7, 0x037e,
+0x02e8, 0x0351, 0x019a, 0x0023,
+0x02b6, 0x030f, 0x01c4, 0x007d,
+0x01eb, 0x0052, 0x0299, 0x0320,
+0x00cd, 0x0174, 0x03bf, 0x0206,
+0x0390, 0x0229, 0x00e2, 0x015b,
+0x03ce, 0x0277, 0x00bc, 0x0105,
+0x0093, 0x012a, 0x03e1, 0x0258,
+0x0372, 0x02cb, 0x0000, 0x01b9,
+0x002f, 0x0196, 0x035d, 0x02e4,
+0x0071, 0x01c8, 0x0303, 0x02ba,
+0x032c, 0x0295, 0x005e, 0x01e7,
+0x0253, 0x03ea, 0x0121, 0x0098,
+0x010e, 0x00b7, 0x027c, 0x03c5,
+0x0150, 0x00e9, 0x0222, 0x039b,
+0x020d, 0x03b4, 0x017f, 0x00c6,
+0x01ec, 0x0055, 0x029e, 0x0327,
+0x02b1, 0x0308, 0x01c3, 0x007a,
+0x02ef, 0x0356, 0x019d, 0x0024,
+0x01b2, 0x000b, 0x02c0, 0x0379,
+0x0094, 0x012d, 0x03e6, 0x025f,
+0x03c9, 0x0270, 0x00bb, 0x0102,
+0x0397, 0x022e, 0x00e5, 0x015c,
+0x00ca, 0x0173, 0x03b8, 0x0201,
+0x032b, 0x0292, 0x0059, 0x01e0,
+0x0076, 0x01cf, 0x0304, 0x02bd,
+0x0028, 0x0191, 0x035a, 0x02e3,
+0x0375, 0x02cc, 0x0007, 0x01be,
+0x0264, 0x03dd, 0x0116, 0x00af,
+0x0139, 0x0080, 0x024b, 0x03f2,
+0x0167, 0x00de, 0x0215, 0x03ac,
+0x023a, 0x0383, 0x0148, 0x00f1,
+0x01db, 0x0062, 0x02a9, 0x0310,
+0x0286, 0x033f, 0x01f4, 0x004d,
+0x02d8, 0x0361, 0x01aa, 0x0013,
+0x0185, 0x003c, 0x02f7, 0x034e,
+0x00a3, 0x011a, 0x03d1, 0x0268,
+0x03fe, 0x0247, 0x008c, 0x0135,
+0x03a0, 0x0219, 0x00d2, 0x016b,
+0x00fd, 0x0144, 0x038f, 0x0236,
+0x031c, 0x02a5, 0x006e, 0x01d7,
+0x0041, 0x01f8, 0x0333, 0x028a,
+0x001f, 0x01a6, 0x036d, 0x02d4,
+0x0342, 0x02fb, 0x0030, 0x0189,
+0x0029, 0x0190, 0x035b, 0x02e2,
+0x0374, 0x02cd, 0x0006, 0x01bf,
+0x032a, 0x0293, 0x0058, 0x01e1,
+0x0077, 0x01ce, 0x0305, 0x02bc,
+0x0396, 0x022f, 0x00e4, 0x015d,
+0x00cb, 0x0172, 0x03b9, 0x0200,
+0x0095, 0x012c, 0x03e7, 0x025e,
+0x03c8, 0x0271, 0x00ba, 0x0103,
+0x02ee, 0x0357, 0x019c, 0x0025,
+0x01b3, 0x000a, 0x02c1, 0x0378,
+0x01ed, 0x0054, 0x029f, 0x0326,
+0x02b0, 0x0309, 0x01c2, 0x007b,
+0x0151, 0x00e8, 0x0223, 0x039a,
+0x020c, 0x03b5, 0x017e, 0x00c7,
+0x0252, 0x03eb, 0x0120, 0x0099,
+0x010f, 0x00b6, 0x027d, 0x03c4,
+0x001e, 0x01a7, 0x036c, 0x02d5,
+0x0343, 0x02fa, 0x0031, 0x0188,
+0x031d, 0x02a4, 0x006f, 0x01d6,
+0x0040, 0x01f9, 0x0332, 0x028b,
+0x03a1, 0x0218, 0x00d3, 0x016a,
+0x00fc, 0x0145, 0x038e, 0x0237,
+0x00a2, 0x011b, 0x03d0, 0x0269,
+0x03ff, 0x0246, 0x008d, 0x0134,
+0x02d9, 0x0360, 0x01ab, 0x0012,
+0x0184, 0x003d, 0x02f6, 0x034f,
+0x01da, 0x0063, 0x02a8, 0x0311,
+0x0287, 0x033e, 0x01f5, 0x004c,
+0x0166, 0x00df, 0x0214, 0x03ad,
+0x023b, 0x0382, 0x0149, 0x00f0,
+0x0265, 0x03dc, 0x0117, 0x00ae,
+0x0138, 0x0081, 0x024a, 0x03f3,
+0x0047, 0x01fe, 0x0335, 0x028c,
+0x031a, 0x02a3, 0x0068, 0x01d1,
+0x0344, 0x02fd, 0x0036, 0x018f,
+0x0019, 0x01a0, 0x036b, 0x02d2,
+0x03f8, 0x0241, 0x008a, 0x0133,
+0x00a5, 0x011c, 0x03d7, 0x026e,
+0x00fb, 0x0142, 0x0389, 0x0230,
+0x03a6, 0x021f, 0x00d4, 0x016d,
+0x0280, 0x0339, 0x01f2, 0x004b,
+0x01dd, 0x0064, 0x02af, 0x0316,
+0x0183, 0x003a, 0x02f1, 0x0348,
+0x02de, 0x0367, 0x01ac, 0x0015,
+0x013f, 0x0086, 0x024d, 0x03f4,
+0x0262, 0x03db, 0x0110, 0x00a9,
+0x023c, 0x0385, 0x014e, 0x00f7,
+0x0161, 0x00d8, 0x0213, 0x03aa,
+0x0070, 0x01c9, 0x0302, 0x02bb,
+0x032d, 0x0294, 0x005f, 0x01e6,
+0x0373, 0x02ca, 0x0001, 0x01b8,
+0x002e, 0x0197, 0x035c, 0x02e5,
+0x03cf, 0x0276, 0x00bd, 0x0104,
+0x0092, 0x012b, 0x03e0, 0x0259,
+0x00cc, 0x0175, 0x03be, 0x0207,
+0x0391, 0x0228, 0x00e3, 0x015a,
+0x02b7, 0x030e, 0x01c5, 0x007c,
+0x01ea, 0x0053, 0x0298, 0x0321,
+0x01b4, 0x000d, 0x02c6, 0x037f,
+0x02e9, 0x0350, 0x019b, 0x0022,
+0x0108, 0x00b1, 0x027a, 0x03c3,
+0x0255, 0x03ec, 0x0127, 0x009e,
+0x020b, 0x03b2, 0x0179, 0x00c0,
+0x0156, 0x00ef, 0x0224, 0x039d,
+0x00f5, 0x014c, 0x0387, 0x023e,
+0x03a8, 0x0211, 0x00da, 0x0163,
+0x03f6, 0x024f, 0x0084, 0x013d,
+0x00ab, 0x0112, 0x03d9, 0x0260,
+0x034a, 0x02f3, 0x0038, 0x0181,
+0x0017, 0x01ae, 0x0365, 0x02dc,
+0x0049, 0x01f0, 0x033b, 0x0282,
+0x0314, 0x02ad, 0x0066, 0x01df,
+0x0232, 0x038b, 0x0140, 0x00f9,
+0x016f, 0x00d6, 0x021d, 0x03a4,
+0x0131, 0x0088, 0x0243, 0x03fa,
+0x026c, 0x03d5, 0x011e, 0x00a7,
+0x018d, 0x0034, 0x02ff, 0x0346,
+0x02d0, 0x0369, 0x01a2, 0x001b,
+0x028e, 0x0337, 0x01fc, 0x0045,
+0x01d3, 0x006a, 0x02a1, 0x0318,
+0x00c2, 0x017b, 0x03b0, 0x0209,
+0x039f, 0x0226, 0x00ed, 0x0154,
+0x03c1, 0x0278, 0x00b3, 0x010a,
+0x009c, 0x0125, 0x03ee, 0x0257,
+0x037d, 0x02c4, 0x000f, 0x01b6,
+0x0020, 0x0199, 0x0352, 0x02eb,
+0x007e, 0x01c7, 0x030c, 0x02b5,
+0x0323, 0x029a, 0x0051, 0x01e8,
+0x0205, 0x03bc, 0x0177, 0x00ce,
+0x0158, 0x00e1, 0x022a, 0x0393,
+0x0106, 0x00bf, 0x0274, 0x03cd,
+0x025b, 0x03e2, 0x0129, 0x0090,
+0x01ba, 0x0003, 0x02c8, 0x0371,
+0x02e7, 0x035e, 0x0195, 0x002c,
+0x02b9, 0x0300, 0x01cb, 0x0072,
+0x01e4, 0x005d, 0x0296, 0x032f,
+0x009b, 0x0122, 0x03e9, 0x0250,
+0x03c6, 0x027f, 0x00b4, 0x010d,
+0x0398, 0x0221, 0x00ea, 0x0153,
+0x00c5, 0x017c, 0x03b7, 0x020e,
+0x0324, 0x029d, 0x0056, 0x01ef,
+0x0079, 0x01c0, 0x030b, 0x02b2,
+0x0027, 0x019e, 0x0355, 0x02ec,
+0x037a, 0x02c3, 0x0008, 0x01b1,
+0x025c, 0x03e5, 0x012e, 0x0097,
+0x0101, 0x00b8, 0x0273, 0x03ca,
+0x015f, 0x00e6, 0x022d, 0x0394,
+0x0202, 0x03bb, 0x0170, 0x00c9,
+0x01e3, 0x005a, 0x0291, 0x0328,
+0x02be, 0x0307, 0x01cc, 0x0075,
+0x02e0, 0x0359, 0x0192, 0x002b,
+0x01bd, 0x0004, 0x02cf, 0x0376,
+0x00ac, 0x0115, 0x03de, 0x0267,
+0x03f1, 0x0248, 0x0083, 0x013a,
+0x03af, 0x0216, 0x00dd, 0x0164,
+0x00f2, 0x014b, 0x0380, 0x0239,
+0x0313, 0x02aa, 0x0061, 0x01d8,
+0x004e, 0x01f7, 0x033c, 0x0285,
+0x0010, 0x01a9, 0x0362, 0x02db,
+0x034d, 0x02f4, 0x003f, 0x0186,
+0x026b, 0x03d2, 0x0119, 0x00a0,
+0x0136, 0x008f, 0x0244, 0x03fd,
+0x0168, 0x00d1, 0x021a, 0x03a3,
+0x0235, 0x038c, 0x0147, 0x00fe,
+0x01d4, 0x006d, 0x02a6, 0x031f,
+0x0289, 0x0330, 0x01fb, 0x0042,
+0x02d7, 0x036e, 0x01a5, 0x001c,
+0x018a, 0x0033, 0x02f8, 0x0341,
+0x0191, 0x0028, 0x02e3, 0x035a,
+0x02cc, 0x0375, 0x01be, 0x0007,
+0x0292, 0x032b, 0x01e0, 0x0059,
+0x01cf, 0x0076, 0x02bd, 0x0304,
+0x022e, 0x0397, 0x015c, 0x00e5,
+0x0173, 0x00ca, 0x0201, 0x03b8,
+0x012d, 0x0094, 0x025f, 0x03e6,
+0x0270, 0x03c9, 0x0102, 0x00bb,
+0x0356, 0x02ef, 0x0024, 0x019d,
+0x000b, 0x01b2, 0x0379, 0x02c0,
+0x0055, 0x01ec, 0x0327, 0x029e,
+0x0308, 0x02b1, 0x007a, 0x01c3,
+0x00e9, 0x0150, 0x039b, 0x0222,
+0x03b4, 0x020d, 0x00c6, 0x017f,
+0x03ea, 0x0253, 0x0098, 0x0121,
+0x00b7, 0x010e, 0x03c5, 0x027c,
+0x01a6, 0x001f, 0x02d4, 0x036d,
+0x02fb, 0x0342, 0x0189, 0x0030,
+0x02a5, 0x031c, 0x01d7, 0x006e,
+0x01f8, 0x0041, 0x028a, 0x0333,
+0x0219, 0x03a0, 0x016b, 0x00d2,
+0x0144, 0x00fd, 0x0236, 0x038f,
+0x011a, 0x00a3, 0x0268, 0x03d1,
+0x0247, 0x03fe, 0x0135, 0x008c,
+0x0361, 0x02d8, 0x0013, 0x01aa,
+0x003c, 0x0185, 0x034e, 0x02f7,
+0x0062, 0x01db, 0x0310, 0x02a9,
+0x033f, 0x0286, 0x004d, 0x01f4,
+0x00de, 0x0167, 0x03ac, 0x0215,
+0x0383, 0x023a, 0x00f1, 0x0148,
+0x03dd, 0x0264, 0x00af, 0x0116,
+0x0080, 0x0139, 0x03f2, 0x024b,
+0x01ff, 0x0046, 0x028d, 0x0334,
+0x02a2, 0x031b, 0x01d0, 0x0069,
+0x02fc, 0x0345, 0x018e, 0x0037,
+0x01a1, 0x0018, 0x02d3, 0x036a,
+0x0240, 0x03f9, 0x0132, 0x008b,
+0x011d, 0x00a4, 0x026f, 0x03d6,
+0x0143, 0x00fa, 0x0231, 0x0388,
+0x021e, 0x03a7, 0x016c, 0x00d5,
+0x0338, 0x0281, 0x004a, 0x01f3,
+0x0065, 0x01dc, 0x0317, 0x02ae,
+0x003b, 0x0182, 0x0349, 0x02f0,
+0x0366, 0x02df, 0x0014, 0x01ad,
+0x0087, 0x013e, 0x03f5, 0x024c,
+0x03da, 0x0263, 0x00a8, 0x0111,
+0x0384, 0x023d, 0x00f6, 0x014f,
+0x00d9, 0x0160, 0x03ab, 0x0212,
+0x01c8, 0x0071, 0x02ba, 0x0303,
+0x0295, 0x032c, 0x01e7, 0x005e,
+0x02cb, 0x0372, 0x01b9, 0x0000,
+0x0196, 0x002f, 0x02e4, 0x035d,
+0x0277, 0x03ce, 0x0105, 0x00bc,
+0x012a, 0x0093, 0x0258, 0x03e1,
+0x0174, 0x00cd, 0x0206, 0x03bf,
+0x0229, 0x0390, 0x015b, 0x00e2,
+0x030f, 0x02b6, 0x007d, 0x01c4,
+0x0052, 0x01eb, 0x0320, 0x0299,
+0x000c, 0x01b5, 0x037e, 0x02c7,
+0x0351, 0x02e8, 0x0023, 0x019a,
+0x00b0, 0x0109, 0x03c2, 0x027b,
+0x03ed, 0x0254, 0x009f, 0x0126,
+0x03b3, 0x020a, 0x00c1, 0x0178,
+0x00ee, 0x0157, 0x039c, 0x0225,
+0x014d, 0x00f4, 0x023f, 0x0386,
+0x0210, 0x03a9, 0x0162, 0x00db,
+0x024e, 0x03f7, 0x013c, 0x0085,
+0x0113, 0x00aa, 0x0261, 0x03d8,
+0x02f2, 0x034b, 0x0180, 0x0039,
+0x01af, 0x0016, 0x02dd, 0x0364,
+0x01f1, 0x0048, 0x0283, 0x033a,
+0x02ac, 0x0315, 0x01de, 0x0067,
+0x038a, 0x0233, 0x00f8, 0x0141,
+0x00d7, 0x016e, 0x03a5, 0x021c,
+0x0089, 0x0130, 0x03fb, 0x0242,
+0x03d4, 0x026d, 0x00a6, 0x011f,
+0x0035, 0x018c, 0x0347, 0x02fe,
+0x0368, 0x02d1, 0x001a, 0x01a3,
+0x0336, 0x028f, 0x0044, 0x01fd,
+0x006b, 0x01d2, 0x0319, 0x02a0,
+0x017a, 0x00c3, 0x0208, 0x03b1,
+0x0227, 0x039e, 0x0155, 0x00ec,
+0x0279, 0x03c0, 0x010b, 0x00b2,
+0x0124, 0x009d, 0x0256, 0x03ef,
+0x02c5, 0x037c, 0x01b7, 0x000e,
+0x0198, 0x0021, 0x02ea, 0x0353,
+0x01c6, 0x007f, 0x02b4, 0x030d,
+0x029b, 0x0322, 0x01e9, 0x0050,
+0x03bd, 0x0204, 0x00cf, 0x0176,
+0x00e0, 0x0159, 0x0392, 0x022b,
+0x00be, 0x0107, 0x03cc, 0x0275,
+0x03e3, 0x025a, 0x0091, 0x0128,
+0x0002, 0x01bb, 0x0370, 0x02c9,
+0x035f, 0x02e6, 0x002d, 0x0194,
+0x0301, 0x02b8, 0x0073, 0x01ca,
+0x005c, 0x01e5, 0x032e, 0x0297,
+0x0123, 0x009a, 0x0251, 0x03e8,
+0x027e, 0x03c7, 0x010c, 0x00b5,
+0x0220, 0x0399, 0x0152, 0x00eb,
+0x017d, 0x00c4, 0x020f, 0x03b6,
+0x029c, 0x0325, 0x01ee, 0x0057,
+0x01c1, 0x0078, 0x02b3, 0x030a,
+0x019f, 0x0026, 0x02ed, 0x0354,
+0x02c2, 0x037b, 0x01b0, 0x0009,
+0x03e4, 0x025d, 0x0096, 0x012f,
+0x00b9, 0x0100, 0x03cb, 0x0272,
+0x00e7, 0x015e, 0x0395, 0x022c,
+0x03ba, 0x0203, 0x00c8, 0x0171,
+0x005b, 0x01e2, 0x0329, 0x0290,
+0x0306, 0x02bf, 0x0074, 0x01cd,
+0x0358, 0x02e1, 0x002a, 0x0193,
+0x0005, 0x01bc, 0x0377, 0x02ce,
+0x0114, 0x00ad, 0x0266, 0x03df,
+0x0249, 0x03f0, 0x013b, 0x0082,
+0x0217, 0x03ae, 0x0165, 0x00dc,
+0x014a, 0x00f3, 0x0238, 0x0381,
+0x02ab, 0x0312, 0x01d9, 0x0060,
+0x01f6, 0x004f, 0x0284, 0x033d,
+0x01a8, 0x0011, 0x02da, 0x0363,
+0x02f5, 0x034c, 0x0187, 0x003e,
+0x03d3, 0x026a, 0x00a1, 0x0118,
+0x008e, 0x0137, 0x03fc, 0x0245,
+0x00d0, 0x0169, 0x03a2, 0x021b,
+0x038d, 0x0234, 0x00ff, 0x0146,
+0x006c, 0x01d5, 0x031e, 0x02a7,
+0x0331, 0x0288, 0x0043, 0x01fa,
+0x036f, 0x02d6, 0x001d, 0x01a4,
+0x0032, 0x018b, 0x0340, 0x02f9,
+0x030b, 0x02b2, 0x0079, 0x01c0,
+0x0056, 0x01ef, 0x0324, 0x029d,
+0x0008, 0x01b1, 0x037a, 0x02c3,
+0x0355, 0x02ec, 0x0027, 0x019e,
+0x00b4, 0x010d, 0x03c6, 0x027f,
+0x03e9, 0x0250, 0x009b, 0x0122,
+0x03b7, 0x020e, 0x00c5, 0x017c,
+0x00ea, 0x0153, 0x0398, 0x0221,
+0x01cc, 0x0075, 0x02be, 0x0307,
+0x0291, 0x0328, 0x01e3, 0x005a,
+0x02cf, 0x0376, 0x01bd, 0x0004,
+0x0192, 0x002b, 0x02e0, 0x0359,
+0x0273, 0x03ca, 0x0101, 0x00b8,
+0x012e, 0x0097, 0x025c, 0x03e5,
+0x0170, 0x00c9, 0x0202, 0x03bb,
+0x022d, 0x0394, 0x015f, 0x00e6,
+0x033c, 0x0285, 0x004e, 0x01f7,
+0x0061, 0x01d8, 0x0313, 0x02aa,
+0x003f, 0x0186, 0x034d, 0x02f4,
+0x0362, 0x02db, 0x0010, 0x01a9,
+0x0083, 0x013a, 0x03f1, 0x0248,
+0x03de, 0x0267, 0x00ac, 0x0115,
+0x0380, 0x0239, 0x00f2, 0x014b,
+0x00dd, 0x0164, 0x03af, 0x0216,
+0x01fb, 0x0042, 0x0289, 0x0330,
+0x02a6, 0x031f, 0x01d4, 0x006d,
+0x02f8, 0x0341, 0x018a, 0x0033,
+0x01a5, 0x001c, 0x02d7, 0x036e,
+0x0244, 0x03fd, 0x0136, 0x008f,
+0x0119, 0x00a0, 0x026b, 0x03d2,
+0x0147, 0x00fe, 0x0235, 0x038c,
+0x021a, 0x03a3, 0x0168, 0x00d1,
+0x0365, 0x02dc, 0x0017, 0x01ae,
+0x0038, 0x0181, 0x034a, 0x02f3,
+0x0066, 0x01df, 0x0314, 0x02ad,
+0x033b, 0x0282, 0x0049, 0x01f0,
+0x00da, 0x0163, 0x03a8, 0x0211,
+0x0387, 0x023e, 0x00f5, 0x014c,
+0x03d9, 0x0260, 0x00ab, 0x0112,
+0x0084, 0x013d, 0x03f6, 0x024f,
+0x01a2, 0x001b, 0x02d0, 0x0369,
+0x02ff, 0x0346, 0x018d, 0x0034,
+0x02a1, 0x0318, 0x01d3, 0x006a,
+0x01fc, 0x0045, 0x028e, 0x0337,
+0x021d, 0x03a4, 0x016f, 0x00d6,
+0x0140, 0x00f9, 0x0232, 0x038b,
+0x011e, 0x00a7, 0x026c, 0x03d5,
+0x0243, 0x03fa, 0x0131, 0x0088,
+0x0352, 0x02eb, 0x0020, 0x0199,
+0x000f, 0x01b6, 0x037d, 0x02c4,
+0x0051, 0x01e8, 0x0323, 0x029a,
+0x030c, 0x02b5, 0x007e, 0x01c7,
+0x00ed, 0x0154, 0x039f, 0x0226,
+0x03b0, 0x0209, 0x00c2, 0x017b,
+0x03ee, 0x0257, 0x009c, 0x0125,
+0x00b3, 0x010a, 0x03c1, 0x0278,
+0x0195, 0x002c, 0x02e7, 0x035e,
+0x02c8, 0x0371, 0x01ba, 0x0003,
+0x0296, 0x032f, 0x01e4, 0x005d,
+0x01cb, 0x0072, 0x02b9, 0x0300,
+0x022a, 0x0393, 0x0158, 0x00e1,
+0x0177, 0x00ce, 0x0205, 0x03bc,
+0x0129, 0x0090, 0x025b, 0x03e2,
+0x0274, 0x03cd, 0x0106, 0x00bf,
+0x03d7, 0x026e, 0x00a5, 0x011c,
+0x008a, 0x0133, 0x03f8, 0x0241,
+0x00d4, 0x016d, 0x03a6, 0x021f,
+0x0389, 0x0230, 0x00fb, 0x0142,
+0x0068, 0x01d1, 0x031a, 0x02a3,
+0x0335, 0x028c, 0x0047, 0x01fe,
+0x036b, 0x02d2, 0x0019, 0x01a0,
+0x0036, 0x018f, 0x0344, 0x02fd,
+0x0110, 0x00a9, 0x0262, 0x03db,
+0x024d, 0x03f4, 0x013f, 0x0086,
+0x0213, 0x03aa, 0x0161, 0x00d8,
+0x014e, 0x00f7, 0x023c, 0x0385,
+0x02af, 0x0316, 0x01dd, 0x0064,
+0x01f2, 0x004b, 0x0280, 0x0339,
+0x01ac, 0x0015, 0x02de, 0x0367,
+0x02f1, 0x0348, 0x0183, 0x003a,
+0x03e0, 0x0259, 0x0092, 0x012b,
+0x00bd, 0x0104, 0x03cf, 0x0276,
+0x00e3, 0x015a, 0x0391, 0x0228,
+0x03be, 0x0207, 0x00cc, 0x0175,
+0x005f, 0x01e6, 0x032d, 0x0294,
+0x0302, 0x02bb, 0x0070, 0x01c9,
+0x035c, 0x02e5, 0x002e, 0x0197,
+0x0001, 0x01b8, 0x0373, 0x02ca,
+0x0127, 0x009e, 0x0255, 0x03ec,
+0x027a, 0x03c3, 0x0108, 0x00b1,
+0x0224, 0x039d, 0x0156, 0x00ef,
+0x0179, 0x00c0, 0x020b, 0x03b2,
+0x0298, 0x0321, 0x01ea, 0x0053,
+0x01c5, 0x007c, 0x02b7, 0x030e,
+0x019b, 0x0022, 0x02e9, 0x0350,
+0x02c6, 0x037f, 0x01b4, 0x000d,
+0x03b9, 0x0200, 0x00cb, 0x0172,
+0x00e4, 0x015d, 0x0396, 0x022f,
+0x00ba, 0x0103, 0x03c8, 0x0271,
+0x03e7, 0x025e, 0x0095, 0x012c,
+0x0006, 0x01bf, 0x0374, 0x02cd,
+0x035b, 0x02e2, 0x0029, 0x0190,
+0x0305, 0x02bc, 0x0077, 0x01ce,
+0x0058, 0x01e1, 0x032a, 0x0293,
+0x017e, 0x00c7, 0x020c, 0x03b5,
+0x0223, 0x039a, 0x0151, 0x00e8,
+0x027d, 0x03c4, 0x010f, 0x00b6,
+0x0120, 0x0099, 0x0252, 0x03eb,
+0x02c1, 0x0378, 0x01b3, 0x000a,
+0x019c, 0x0025, 0x02ee, 0x0357,
+0x01c2, 0x007b, 0x02b0, 0x0309,
+0x029f, 0x0326, 0x01ed, 0x0054,
+0x038e, 0x0237, 0x00fc, 0x0145,
+0x00d3, 0x016a, 0x03a1, 0x0218,
+0x008d, 0x0134, 0x03ff, 0x0246,
+0x03d0, 0x0269, 0x00a2, 0x011b,
+0x0031, 0x0188, 0x0343, 0x02fa,
+0x036c, 0x02d5, 0x001e, 0x01a7,
+0x0332, 0x028b, 0x0040, 0x01f9,
+0x006f, 0x01d6, 0x031d, 0x02a4,
+0x0149, 0x00f0, 0x023b, 0x0382,
+0x0214, 0x03ad, 0x0166, 0x00df,
+0x024a, 0x03f3, 0x0138, 0x0081,
+0x0117, 0x00ae, 0x0265, 0x03dc,
+0x02f6, 0x034f, 0x0184, 0x003d,
+0x01ab, 0x0012, 0x02d9, 0x0360,
+0x01f5, 0x004c, 0x0287, 0x033e,
+0x02a8, 0x0311, 0x01da, 0x0063,
+0x02b3, 0x030a, 0x01c1, 0x0078,
+0x01ee, 0x0057, 0x029c, 0x0325,
+0x01b0, 0x0009, 0x02c2, 0x037b,
+0x02ed, 0x0354, 0x019f, 0x0026,
+0x010c, 0x00b5, 0x027e, 0x03c7,
+0x0251, 0x03e8, 0x0123, 0x009a,
+0x020f, 0x03b6, 0x017d, 0x00c4,
+0x0152, 0x00eb, 0x0220, 0x0399,
+0x0074, 0x01cd, 0x0306, 0x02bf,
+0x0329, 0x0290, 0x005b, 0x01e2,
+0x0377, 0x02ce, 0x0005, 0x01bc,
+0x002a, 0x0193, 0x0358, 0x02e1,
+0x03cb, 0x0272, 0x00b9, 0x0100,
+0x0096, 0x012f, 0x03e4, 0x025d,
+0x00c8, 0x0171, 0x03ba, 0x0203,
+0x0395, 0x022c, 0x00e7, 0x015e,
+0x0284, 0x033d, 0x01f6, 0x004f,
+0x01d9, 0x0060, 0x02ab, 0x0312,
+0x0187, 0x003e, 0x02f5, 0x034c,
+0x02da, 0x0363, 0x01a8, 0x0011,
+0x013b, 0x0082, 0x0249, 0x03f0,
+0x0266, 0x03df, 0x0114, 0x00ad,
+0x0238, 0x0381, 0x014a, 0x00f3,
+0x0165, 0x00dc, 0x0217, 0x03ae,
+0x0043, 0x01fa, 0x0331, 0x0288,
+0x031e, 0x02a7, 0x006c, 0x01d5,
+0x0340, 0x02f9, 0x0032, 0x018b,
+0x001d, 0x01a4, 0x036f, 0x02d6,
+0x03fc, 0x0245, 0x008e, 0x0137,
+0x00a1, 0x0118, 0x03d3, 0x026a,
+0x00ff, 0x0146, 0x038d, 0x0234,
+0x03a2, 0x021b, 0x00d0, 0x0169,
+0x02dd, 0x0364, 0x01af, 0x0016,
+0x0180, 0x0039, 0x02f2, 0x034b,
+0x01de, 0x0067, 0x02ac, 0x0315,
+0x0283, 0x033a, 0x01f1, 0x0048,
+0x0162, 0x00db, 0x0210, 0x03a9,
+0x023f, 0x0386, 0x014d, 0x00f4,
+0x0261, 0x03d8, 0x0113, 0x00aa,
+0x013c, 0x0085, 0x024e, 0x03f7,
+0x001a, 0x01a3, 0x0368, 0x02d1,
+0x0347, 0x02fe, 0x0035, 0x018c,
+0x0319, 0x02a0, 0x006b, 0x01d2,
+0x0044, 0x01fd, 0x0336, 0x028f,
+0x03a5, 0x021c, 0x00d7, 0x016e,
+0x00f8, 0x0141, 0x038a, 0x0233,
+0x00a6, 0x011f, 0x03d4, 0x026d,
+0x03fb, 0x0242, 0x0089, 0x0130,
+0x02ea, 0x0353, 0x0198, 0x0021,
+0x01b7, 0x000e, 0x02c5, 0x037c,
+0x01e9, 0x0050, 0x029b, 0x0322,
+0x02b4, 0x030d, 0x01c6, 0x007f,
+0x0155, 0x00ec, 0x0227, 0x039e,
+0x0208, 0x03b1, 0x017a, 0x00c3,
+0x0256, 0x03ef, 0x0124, 0x009d,
+0x010b, 0x00b2, 0x0279, 0x03c0,
+0x002d, 0x0194, 0x035f, 0x02e6,
+0x0370, 0x02c9, 0x0002, 0x01bb,
+0x032e, 0x0297, 0x005c, 0x01e5,
+0x0073, 0x01ca, 0x0301, 0x02b8,
+0x0392, 0x022b, 0x00e0, 0x0159,
+0x00cf, 0x0176, 0x03bd, 0x0204,
+0x0091, 0x0128, 0x03e3, 0x025a,
+0x03cc, 0x0275, 0x00be, 0x0107,
+0x026f, 0x03d6, 0x011d, 0x00a4,
+0x0132, 0x008b, 0x0240, 0x03f9,
+0x016c, 0x00d5, 0x021e, 0x03a7,
+0x0231, 0x0388, 0x0143, 0x00fa,
+0x01d0, 0x0069, 0x02a2, 0x031b,
+0x028d, 0x0334, 0x01ff, 0x0046,
+0x02d3, 0x036a, 0x01a1, 0x0018,
+0x018e, 0x0037, 0x02fc, 0x0345,
+0x00a8, 0x0111, 0x03da, 0x0263,
+0x03f5, 0x024c, 0x0087, 0x013e,
+0x03ab, 0x0212, 0x00d9, 0x0160,
+0x00f6, 0x014f, 0x0384, 0x023d,
+0x0317, 0x02ae, 0x0065, 0x01dc,
+0x004a, 0x01f3, 0x0338, 0x0281,
+0x0014, 0x01ad, 0x0366, 0x02df,
+0x0349, 0x02f0, 0x003b, 0x0182,
+0x0258, 0x03e1, 0x012a, 0x0093,
+0x0105, 0x00bc, 0x0277, 0x03ce,
+0x015b, 0x00e2, 0x0229, 0x0390,
+0x0206, 0x03bf, 0x0174, 0x00cd,
+0x01e7, 0x005e, 0x0295, 0x032c,
+0x02ba, 0x0303, 0x01c8, 0x0071,
+0x02e4, 0x035d, 0x0196, 0x002f,
+0x01b9, 0x0000, 0x02cb, 0x0372,
+0x009f, 0x0126, 0x03ed, 0x0254,
+0x03c2, 0x027b, 0x00b0, 0x0109,
+0x039c, 0x0225, 0x00ee, 0x0157,
+0x00c1, 0x0178, 0x03b3, 0x020a,
+0x0320, 0x0299, 0x0052, 0x01eb,
+0x007d, 0x01c4, 0x030f, 0x02b6,
+0x0023, 0x019a, 0x0351, 0x02e8,
+0x037e, 0x02c7, 0x000c, 0x01b5,
+0x0201, 0x03b8, 0x0173, 0x00ca,
+0x015c, 0x00e5, 0x022e, 0x0397,
+0x0102, 0x00bb, 0x0270, 0x03c9,
+0x025f, 0x03e6, 0x012d, 0x0094,
+0x01be, 0x0007, 0x02cc, 0x0375,
+0x02e3, 0x035a, 0x0191, 0x0028,
+0x02bd, 0x0304, 0x01cf, 0x0076,
+0x01e0, 0x0059, 0x0292, 0x032b,
+0x00c6, 0x017f, 0x03b4, 0x020d,
+0x039b, 0x0222, 0x00e9, 0x0150,
+0x03c5, 0x027c, 0x00b7, 0x010e,
+0x0098, 0x0121, 0x03ea, 0x0253,
+0x0379, 0x02c0, 0x000b, 0x01b2,
+0x0024, 0x019d, 0x0356, 0x02ef,
+0x007a, 0x01c3, 0x0308, 0x02b1,
+0x0327, 0x029e, 0x0055, 0x01ec,
+0x0236, 0x038f, 0x0144, 0x00fd,
+0x016b, 0x00d2, 0x0219, 0x03a0,
+0x0135, 0x008c, 0x0247, 0x03fe,
+0x0268, 0x03d1, 0x011a, 0x00a3,
+0x0189, 0x0030, 0x02fb, 0x0342,
+0x02d4, 0x036d, 0x01a6, 0x001f,
+0x028a, 0x0333, 0x01f8, 0x0041,
+0x01d7, 0x006e, 0x02a5, 0x031c,
+0x00f1, 0x0148, 0x0383, 0x023a,
+0x03ac, 0x0215, 0x00de, 0x0167,
+0x03f2, 0x024b, 0x0080, 0x0139,
+0x00af, 0x0116, 0x03dd, 0x0264,
+0x034e, 0x02f7, 0x003c, 0x0185,
+0x0013, 0x01aa, 0x0361, 0x02d8,
+0x004d, 0x01f4, 0x033f, 0x0286,
+0x0310, 0x02a9, 0x0062, 0x01db,
+0x007b, 0x01c2, 0x0309, 0x02b0,
+0x0326, 0x029f, 0x0054, 0x01ed,
+0x0378, 0x02c1, 0x000a, 0x01b3,
+0x0025, 0x019c, 0x0357, 0x02ee,
+0x03c4, 0x027d, 0x00b6, 0x010f,
+0x0099, 0x0120, 0x03eb, 0x0252,
+0x00c7, 0x017e, 0x03b5, 0x020c,
+0x039a, 0x0223, 0x00e8, 0x0151,
+0x02bc, 0x0305, 0x01ce, 0x0077,
+0x01e1, 0x0058, 0x0293, 0x032a,
+0x01bf, 0x0006, 0x02cd, 0x0374,
+0x02e2, 0x035b, 0x0190, 0x0029,
+0x0103, 0x00ba, 0x0271, 0x03c8,
+0x025e, 0x03e7, 0x012c, 0x0095,
+0x0200, 0x03b9, 0x0172, 0x00cb,
+0x015d, 0x00e4, 0x022f, 0x0396,
+0x004c, 0x01f5, 0x033e, 0x0287,
+0x0311, 0x02a8, 0x0063, 0x01da,
+0x034f, 0x02f6, 0x003d, 0x0184,
+0x0012, 0x01ab, 0x0360, 0x02d9,
+0x03f3, 0x024a, 0x0081, 0x0138,
+0x00ae, 0x0117, 0x03dc, 0x0265,
+0x00f0, 0x0149, 0x0382, 0x023b,
+0x03ad, 0x0214, 0x00df, 0x0166,
+0x028b, 0x0332, 0x01f9, 0x0040,
+0x01d6, 0x006f, 0x02a4, 0x031d,
+0x0188, 0x0031, 0x02fa, 0x0343,
+0x02d5, 0x036c, 0x01a7, 0x001e,
+0x0134, 0x008d, 0x0246, 0x03ff,
+0x0269, 0x03d0, 0x011b, 0x00a2,
+0x0237, 0x038e, 0x0145, 0x00fc,
+0x016a, 0x00d3, 0x0218, 0x03a1,
+0x0015, 0x01ac, 0x0367, 0x02de,
+0x0348, 0x02f1, 0x003a, 0x0183,
+0x0316, 0x02af, 0x0064, 0x01dd,
+0x004b, 0x01f2, 0x0339, 0x0280,
+0x03aa, 0x0213, 0x00d8, 0x0161,
+0x00f7, 0x014e, 0x0385, 0x023c,
+0x00a9, 0x0110, 0x03db, 0x0262,
+0x03f4, 0x024d, 0x0086, 0x013f,
+0x02d2, 0x036b, 0x01a0, 0x0019,
+0x018f, 0x0036, 0x02fd, 0x0344,
+0x01d1, 0x0068, 0x02a3, 0x031a,
+0x028c, 0x0335, 0x01fe, 0x0047,
+0x016d, 0x00d4, 0x021f, 0x03a6,
+0x0230, 0x0389, 0x0142, 0x00fb,
+0x026e, 0x03d7, 0x011c, 0x00a5,
+0x0133, 0x008a, 0x0241, 0x03f8,
+0x0022, 0x019b, 0x0350, 0x02e9,
+0x037f, 0x02c6, 0x000d, 0x01b4,
+0x0321, 0x0298, 0x0053, 0x01ea,
+0x007c, 0x01c5, 0x030e, 0x02b7,
+0x039d, 0x0224, 0x00ef, 0x0156,
+0x00c0, 0x0179, 0x03b2, 0x020b,
+0x009e, 0x0127, 0x03ec, 0x0255,
+0x03c3, 0x027a, 0x00b1, 0x0108,
+0x02e5, 0x035c, 0x0197, 0x002e,
+0x01b8, 0x0001, 0x02ca, 0x0373,
+0x01e6, 0x005f, 0x0294, 0x032d,
+0x02bb, 0x0302, 0x01c9, 0x0070,
+0x015a, 0x00e3, 0x0228, 0x0391,
+0x0207, 0x03be, 0x0175, 0x00cc,
+0x0259, 0x03e0, 0x012b, 0x0092,
+0x0104, 0x00bd, 0x0276, 0x03cf,
+0x00a7, 0x011e, 0x03d5, 0x026c,
+0x03fa, 0x0243, 0x0088, 0x0131,
+0x03a4, 0x021d, 0x00d6, 0x016f,
+0x00f9, 0x0140, 0x038b, 0x0232,
+0x0318, 0x02a1, 0x006a, 0x01d3,
+0x0045, 0x01fc, 0x0337, 0x028e,
+0x001b, 0x01a2, 0x0369, 0x02d0,
+0x0346, 0x02ff, 0x0034, 0x018d,
+0x0260, 0x03d9, 0x0112, 0x00ab,
+0x013d, 0x0084, 0x024f, 0x03f6,
+0x0163, 0x00da, 0x0211, 0x03a8,
+0x023e, 0x0387, 0x014c, 0x00f5,
+0x01df, 0x0066, 0x02ad, 0x0314,
+0x0282, 0x033b, 0x01f0, 0x0049,
+0x02dc, 0x0365, 0x01ae, 0x0017,
+0x0181, 0x0038, 0x02f3, 0x034a,
+0x0090, 0x0129, 0x03e2, 0x025b,
+0x03cd, 0x0274, 0x00bf, 0x0106,
+0x0393, 0x022a, 0x00e1, 0x0158,
+0x00ce, 0x0177, 0x03bc, 0x0205,
+0x032f, 0x0296, 0x005d, 0x01e4,
+0x0072, 0x01cb, 0x0300, 0x02b9,
+0x002c, 0x0195, 0x035e, 0x02e7,
+0x0371, 0x02c8, 0x0003, 0x01ba,
+0x0257, 0x03ee, 0x0125, 0x009c,
+0x010a, 0x00b3, 0x0278, 0x03c1,
+0x0154, 0x00ed, 0x0226, 0x039f,
+0x0209, 0x03b0, 0x017b, 0x00c2,
+0x01e8, 0x0051, 0x029a, 0x0323,
+0x02b5, 0x030c, 0x01c7, 0x007e,
+0x02eb, 0x0352, 0x0199, 0x0020,
+0x01b6, 0x000f, 0x02c4, 0x037d,
+0x00c9, 0x0170, 0x03bb, 0x0202,
+0x0394, 0x022d, 0x00e6, 0x015f,
+0x03ca, 0x0273, 0x00b8, 0x0101,
+0x0097, 0x012e, 0x03e5, 0x025c,
+0x0376, 0x02cf, 0x0004, 0x01bd,
+0x002b, 0x0192, 0x0359, 0x02e0,
+0x0075, 0x01cc, 0x0307, 0x02be,
+0x0328, 0x0291, 0x005a, 0x01e3,
+0x020e, 0x03b7, 0x017c, 0x00c5,
+0x0153, 0x00ea, 0x0221, 0x0398,
+0x010d, 0x00b4, 0x027f, 0x03c6,
+0x0250, 0x03e9, 0x0122, 0x009b,
+0x01b1, 0x0008, 0x02c3, 0x037a,
+0x02ec, 0x0355, 0x019e, 0x0027,
+0x02b2, 0x030b, 0x01c0, 0x0079,
+0x01ef, 0x0056, 0x029d, 0x0324,
+0x00fe, 0x0147, 0x038c, 0x0235,
+0x03a3, 0x021a, 0x00d1, 0x0168,
+0x03fd, 0x0244, 0x008f, 0x0136,
+0x00a0, 0x0119, 0x03d2, 0x026b,
+0x0341, 0x02f8, 0x0033, 0x018a,
+0x001c, 0x01a5, 0x036e, 0x02d7,
+0x0042, 0x01fb, 0x0330, 0x0289,
+0x031f, 0x02a6, 0x006d, 0x01d4,
+0x0239, 0x0380, 0x014b, 0x00f2,
+0x0164, 0x00dd, 0x0216, 0x03af,
+0x013a, 0x0083, 0x0248, 0x03f1,
+0x0267, 0x03de, 0x0115, 0x00ac,
+0x0186, 0x003f, 0x02f4, 0x034d,
+0x02db, 0x0362, 0x01a9, 0x0010,
+0x0285, 0x033c, 0x01f7, 0x004e,
+0x01d8, 0x0061, 0x02aa, 0x0313,
+0x01c3, 0x007a, 0x02b1, 0x0308,
+0x029e, 0x0327, 0x01ec, 0x0055,
+0x02c0, 0x0379, 0x01b2, 0x000b,
+0x019d, 0x0024, 0x02ef, 0x0356,
+0x027c, 0x03c5, 0x010e, 0x00b7,
+0x0121, 0x0098, 0x0253, 0x03ea,
+0x017f, 0x00c6, 0x020d, 0x03b4,
+0x0222, 0x039b, 0x0150, 0x00e9,
+0x0304, 0x02bd, 0x0076, 0x01cf,
+0x0059, 0x01e0, 0x032b, 0x0292,
+0x0007, 0x01be, 0x0375, 0x02cc,
+0x035a, 0x02e3, 0x0028, 0x0191,
+0x00bb, 0x0102, 0x03c9, 0x0270,
+0x03e6, 0x025f, 0x0094, 0x012d,
+0x03b8, 0x0201, 0x00ca, 0x0173,
+0x00e5, 0x015c, 0x0397, 0x022e,
+0x01f4, 0x004d, 0x0286, 0x033f,
+0x02a9, 0x0310, 0x01db, 0x0062,
+0x02f7, 0x034e, 0x0185, 0x003c,
+0x01aa, 0x0013, 0x02d8, 0x0361,
+0x024b, 0x03f2, 0x0139, 0x0080,
+0x0116, 0x00af, 0x0264, 0x03dd,
+0x0148, 0x00f1, 0x023a, 0x0383,
+0x0215, 0x03ac, 0x0167, 0x00de,
+0x0333, 0x028a, 0x0041, 0x01f8,
+0x006e, 0x01d7, 0x031c, 0x02a5,
+0x0030, 0x0189, 0x0342, 0x02fb,
+0x036d, 0x02d4, 0x001f, 0x01a6,
+0x008c, 0x0135, 0x03fe, 0x0247,
+0x03d1, 0x0268, 0x00a3, 0x011a,
+0x038f, 0x0236, 0x00fd, 0x0144,
+0x00d2, 0x016b, 0x03a0, 0x0219,
+0x01ad, 0x0014, 0x02df, 0x0366,
+0x02f0, 0x0349, 0x0182, 0x003b,
+0x02ae, 0x0317, 0x01dc, 0x0065,
+0x01f3, 0x004a, 0x0281, 0x0338,
+0x0212, 0x03ab, 0x0160, 0x00d9,
+0x014f, 0x00f6, 0x023d, 0x0384,
+0x0111, 0x00a8, 0x0263, 0x03da,
+0x024c, 0x03f5, 0x013e, 0x0087,
+0x036a, 0x02d3, 0x0018, 0x01a1,
+0x0037, 0x018e, 0x0345, 0x02fc,
+0x0069, 0x01d0, 0x031b, 0x02a2,
+0x0334, 0x028d, 0x0046, 0x01ff,
+0x00d5, 0x016c, 0x03a7, 0x021e,
+0x0388, 0x0231, 0x00fa, 0x0143,
+0x03d6, 0x026f, 0x00a4, 0x011d,
+0x008b, 0x0132, 0x03f9, 0x0240,
+0x019a, 0x0023, 0x02e8, 0x0351,
+0x02c7, 0x037e, 0x01b5, 0x000c,
+0x0299, 0x0320, 0x01eb, 0x0052,
+0x01c4, 0x007d, 0x02b6, 0x030f,
+0x0225, 0x039c, 0x0157, 0x00ee,
+0x0178, 0x00c1, 0x020a, 0x03b3,
+0x0126, 0x009f, 0x0254, 0x03ed,
+0x027b, 0x03c2, 0x0109, 0x00b0,
+0x035d, 0x02e4, 0x002f, 0x0196,
+0x0000, 0x01b9, 0x0372, 0x02cb,
+0x005e, 0x01e7, 0x032c, 0x0295,
+0x0303, 0x02ba, 0x0071, 0x01c8,
+0x00e2, 0x015b, 0x0390, 0x0229,
+0x03bf, 0x0206, 0x00cd, 0x0174,
+0x03e1, 0x0258, 0x0093, 0x012a,
+0x00bc, 0x0105, 0x03ce, 0x0277,
+0x011f, 0x00a6, 0x026d, 0x03d4,
+0x0242, 0x03fb, 0x0130, 0x0089,
+0x021c, 0x03a5, 0x016e, 0x00d7,
+0x0141, 0x00f8, 0x0233, 0x038a,
+0x02a0, 0x0319, 0x01d2, 0x006b,
+0x01fd, 0x0044, 0x028f, 0x0336,
+0x01a3, 0x001a, 0x02d1, 0x0368,
+0x02fe, 0x0347, 0x018c, 0x0035,
+0x03d8, 0x0261, 0x00aa, 0x0113,
+0x0085, 0x013c, 0x03f7, 0x024e,
+0x00db, 0x0162, 0x03a9, 0x0210,
+0x0386, 0x023f, 0x00f4, 0x014d,
+0x0067, 0x01de, 0x0315, 0x02ac,
+0x033a, 0x0283, 0x0048, 0x01f1,
+0x0364, 0x02dd, 0x0016, 0x01af,
+0x0039, 0x0180, 0x034b, 0x02f2,
+0x0128, 0x0091, 0x025a, 0x03e3,
+0x0275, 0x03cc, 0x0107, 0x00be,
+0x022b, 0x0392, 0x0159, 0x00e0,
+0x0176, 0x00cf, 0x0204, 0x03bd,
+0x0297, 0x032e, 0x01e5, 0x005c,
+0x01ca, 0x0073, 0x02b8, 0x0301,
+0x0194, 0x002d, 0x02e6, 0x035f,
+0x02c9, 0x0370, 0x01bb, 0x0002,
+0x03ef, 0x0256, 0x009d, 0x0124,
+0x00b2, 0x010b, 0x03c0, 0x0279,
+0x00ec, 0x0155, 0x039e, 0x0227,
+0x03b1, 0x0208, 0x00c3, 0x017a,
+0x0050, 0x01e9, 0x0322, 0x029b,
+0x030d, 0x02b4, 0x007f, 0x01c6,
+0x0353, 0x02ea, 0x0021, 0x0198,
+0x000e, 0x01b7, 0x037c, 0x02c5,
+0x0171, 0x00c8, 0x0203, 0x03ba,
+0x022c, 0x0395, 0x015e, 0x00e7,
+0x0272, 0x03cb, 0x0100, 0x00b9,
+0x012f, 0x0096, 0x025d, 0x03e4,
+0x02ce, 0x0377, 0x01bc, 0x0005,
+0x0193, 0x002a, 0x02e1, 0x0358,
+0x01cd, 0x0074, 0x02bf, 0x0306,
+0x0290, 0x0329, 0x01e2, 0x005b,
+0x03b6, 0x020f, 0x00c4, 0x017d,
+0x00eb, 0x0152, 0x0399, 0x0220,
+0x00b5, 0x010c, 0x03c7, 0x027e,
+0x03e8, 0x0251, 0x009a, 0x0123,
+0x0009, 0x01b0, 0x037b, 0x02c2,
+0x0354, 0x02ed, 0x0026, 0x019f,
+0x030a, 0x02b3, 0x0078, 0x01c1,
+0x0057, 0x01ee, 0x0325, 0x029c,
+0x0146, 0x00ff, 0x0234, 0x038d,
+0x021b, 0x03a2, 0x0169, 0x00d0,
+0x0245, 0x03fc, 0x0137, 0x008e,
+0x0118, 0x00a1, 0x026a, 0x03d3,
+0x02f9, 0x0340, 0x018b, 0x0032,
+0x01a4, 0x001d, 0x02d6, 0x036f,
+0x01fa, 0x0043, 0x0288, 0x0331,
+0x02a7, 0x031e, 0x01d5, 0x006c,
+0x0381, 0x0238, 0x00f3, 0x014a,
+0x00dc, 0x0165, 0x03ae, 0x0217,
+0x0082, 0x013b, 0x03f0, 0x0249,
+0x03df, 0x0266, 0x00ad, 0x0114,
+0x003e, 0x0187, 0x034c, 0x02f5,
+0x0363, 0x02da, 0x0011, 0x01a8,
+0x033d, 0x0284, 0x004f, 0x01f6,
+0x0060, 0x01d9, 0x0312, 0x02ab,
+0x0052, 0x01eb, 0x0320, 0x0299,
+0x030f, 0x02b6, 0x007d, 0x01c4,
+0x0351, 0x02e8, 0x0023, 0x019a,
+0x000c, 0x01b5, 0x037e, 0x02c7,
+0x03ed, 0x0254, 0x009f, 0x0126,
+0x00b0, 0x0109, 0x03c2, 0x027b,
+0x00ee, 0x0157, 0x039c, 0x0225,
+0x03b3, 0x020a, 0x00c1, 0x0178,
+0x0295, 0x032c, 0x01e7, 0x005e,
+0x01c8, 0x0071, 0x02ba, 0x0303,
+0x0196, 0x002f, 0x02e4, 0x035d,
+0x02cb, 0x0372, 0x01b9, 0x0000,
+0x012a, 0x0093, 0x0258, 0x03e1,
+0x0277, 0x03ce, 0x0105, 0x00bc,
+0x0229, 0x0390, 0x015b, 0x00e2,
+0x0174, 0x00cd, 0x0206, 0x03bf,
+0x0065, 0x01dc, 0x0317, 0x02ae,
+0x0338, 0x0281, 0x004a, 0x01f3,
+0x0366, 0x02df, 0x0014, 0x01ad,
+0x003b, 0x0182, 0x0349, 0x02f0,
+0x03da, 0x0263, 0x00a8, 0x0111,
+0x0087, 0x013e, 0x03f5, 0x024c,
+0x00d9, 0x0160, 0x03ab, 0x0212,
+0x0384, 0x023d, 0x00f6, 0x014f,
+0x02a2, 0x031b, 0x01d0, 0x0069,
+0x01ff, 0x0046, 0x028d, 0x0334,
+0x01a1, 0x0018, 0x02d3, 0x036a,
+0x02fc, 0x0345, 0x018e, 0x0037,
+0x011d, 0x00a4, 0x026f, 0x03d6,
+0x0240, 0x03f9, 0x0132, 0x008b,
+0x021e, 0x03a7, 0x016c, 0x00d5,
+0x0143, 0x00fa, 0x0231, 0x0388,
+0x003c, 0x0185, 0x034e, 0x02f7,
+0x0361, 0x02d8, 0x0013, 0x01aa,
+0x033f, 0x0286, 0x004d, 0x01f4,
+0x0062, 0x01db, 0x0310, 0x02a9,
+0x0383, 0x023a, 0x00f1, 0x0148,
+0x00de, 0x0167, 0x03ac, 0x0215,
+0x0080, 0x0139, 0x03f2, 0x024b,
+0x03dd, 0x0264, 0x00af, 0x0116,
+0x02fb, 0x0342, 0x0189, 0x0030,
+0x01a6, 0x001f, 0x02d4, 0x036d,
+0x01f8, 0x0041, 0x028a, 0x0333,
+0x02a5, 0x031c, 0x01d7, 0x006e,
+0x0144, 0x00fd, 0x0236, 0x038f,
+0x0219, 0x03a0, 0x016b, 0x00d2,
+0x0247, 0x03fe, 0x0135, 0x008c,
+0x011a, 0x00a3, 0x0268, 0x03d1,
+0x000b, 0x01b2, 0x0379, 0x02c0,
+0x0356, 0x02ef, 0x0024, 0x019d,
+0x0308, 0x02b1, 0x007a, 0x01c3,
+0x0055, 0x01ec, 0x0327, 0x029e,
+0x03b4, 0x020d, 0x00c6, 0x017f,
+0x00e9, 0x0150, 0x039b, 0x0222,
+0x00b7, 0x010e, 0x03c5, 0x027c,
+0x03ea, 0x0253, 0x0098, 0x0121,
+0x02cc, 0x0375, 0x01be, 0x0007,
+0x0191, 0x0028, 0x02e3, 0x035a,
+0x01cf, 0x0076, 0x02bd, 0x0304,
+0x0292, 0x032b, 0x01e0, 0x0059,
+0x0173, 0x00ca, 0x0201, 0x03b8,
+0x022e, 0x0397, 0x015c, 0x00e5,
+0x0270, 0x03c9, 0x0102, 0x00bb,
+0x012d, 0x0094, 0x025f, 0x03e6,
+0x008e, 0x0137, 0x03fc, 0x0245,
+0x03d3, 0x026a, 0x00a1, 0x0118,
+0x038d, 0x0234, 0x00ff, 0x0146,
+0x00d0, 0x0169, 0x03a2, 0x021b,
+0x0331, 0x0288, 0x0043, 0x01fa,
+0x006c, 0x01d5, 0x031e, 0x02a7,
+0x0032, 0x018b, 0x0340, 0x02f9,
+0x036f, 0x02d6, 0x001d, 0x01a4,
+0x0249, 0x03f0, 0x013b, 0x0082,
+0x0114, 0x00ad, 0x0266, 0x03df,
+0x014a, 0x00f3, 0x0238, 0x0381,
+0x0217, 0x03ae, 0x0165, 0x00dc,
+0x01f6, 0x004f, 0x0284, 0x033d,
+0x02ab, 0x0312, 0x01d9, 0x0060,
+0x02f5, 0x034c, 0x0187, 0x003e,
+0x01a8, 0x0011, 0x02da, 0x0363,
+0x00b9, 0x0100, 0x03cb, 0x0272,
+0x03e4, 0x025d, 0x0096, 0x012f,
+0x03ba, 0x0203, 0x00c8, 0x0171,
+0x00e7, 0x015e, 0x0395, 0x022c,
+0x0306, 0x02bf, 0x0074, 0x01cd,
+0x005b, 0x01e2, 0x0329, 0x0290,
+0x0005, 0x01bc, 0x0377, 0x02ce,
+0x0358, 0x02e1, 0x002a, 0x0193,
+0x027e, 0x03c7, 0x010c, 0x00b5,
+0x0123, 0x009a, 0x0251, 0x03e8,
+0x017d, 0x00c4, 0x020f, 0x03b6,
+0x0220, 0x0399, 0x0152, 0x00eb,
+0x01c1, 0x0078, 0x02b3, 0x030a,
+0x029c, 0x0325, 0x01ee, 0x0057,
+0x02c2, 0x037b, 0x01b0, 0x0009,
+0x019f, 0x0026, 0x02ed, 0x0354,
+0x00e0, 0x0159, 0x0392, 0x022b,
+0x03bd, 0x0204, 0x00cf, 0x0176,
+0x03e3, 0x025a, 0x0091, 0x0128,
+0x00be, 0x0107, 0x03cc, 0x0275,
+0x035f, 0x02e6, 0x002d, 0x0194,
+0x0002, 0x01bb, 0x0370, 0x02c9,
+0x005c, 0x01e5, 0x032e, 0x0297,
+0x0301, 0x02b8, 0x0073, 0x01ca,
+0x0227, 0x039e, 0x0155, 0x00ec,
+0x017a, 0x00c3, 0x0208, 0x03b1,
+0x0124, 0x009d, 0x0256, 0x03ef,
+0x0279, 0x03c0, 0x010b, 0x00b2,
+0x0198, 0x0021, 0x02ea, 0x0353,
+0x02c5, 0x037c, 0x01b7, 0x000e,
+0x029b, 0x0322, 0x01e9, 0x0050,
+0x01c6, 0x007f, 0x02b4, 0x030d,
+0x00d7, 0x016e, 0x03a5, 0x021c,
+0x038a, 0x0233, 0x00f8, 0x0141,
+0x03d4, 0x026d, 0x00a6, 0x011f,
+0x0089, 0x0130, 0x03fb, 0x0242,
+0x0368, 0x02d1, 0x001a, 0x01a3,
+0x0035, 0x018c, 0x0347, 0x02fe,
+0x006b, 0x01d2, 0x0319, 0x02a0,
+0x0336, 0x028f, 0x0044, 0x01fd,
+0x0210, 0x03a9, 0x0162, 0x00db,
+0x014d, 0x00f4, 0x023f, 0x0386,
+0x0113, 0x00aa, 0x0261, 0x03d8,
+0x024e, 0x03f7, 0x013c, 0x0085,
+0x01af, 0x0016, 0x02dd, 0x0364,
+0x02f2, 0x034b, 0x0180, 0x0039,
+0x02ac, 0x0315, 0x01de, 0x0067,
+0x01f1, 0x0048, 0x0283, 0x033a,
+0x01ea, 0x0053, 0x0298, 0x0321,
+0x02b7, 0x030e, 0x01c5, 0x007c,
+0x02e9, 0x0350, 0x019b, 0x0022,
+0x01b4, 0x000d, 0x02c6, 0x037f,
+0x0255, 0x03ec, 0x0127, 0x009e,
+0x0108, 0x00b1, 0x027a, 0x03c3,
+0x0156, 0x00ef, 0x0224, 0x039d,
+0x020b, 0x03b2, 0x0179, 0x00c0,
+0x032d, 0x0294, 0x005f, 0x01e6,
+0x0070, 0x01c9, 0x0302, 0x02bb,
+0x002e, 0x0197, 0x035c, 0x02e5,
+0x0373, 0x02ca, 0x0001, 0x01b8,
+0x0092, 0x012b, 0x03e0, 0x0259,
+0x03cf, 0x0276, 0x00bd, 0x0104,
+0x0391, 0x0228, 0x00e3, 0x015a,
+0x00cc, 0x0175, 0x03be, 0x0207,
+0x01dd, 0x0064, 0x02af, 0x0316,
+0x0280, 0x0339, 0x01f2, 0x004b,
+0x02de, 0x0367, 0x01ac, 0x0015,
+0x0183, 0x003a, 0x02f1, 0x0348,
+0x0262, 0x03db, 0x0110, 0x00a9,
+0x013f, 0x0086, 0x024d, 0x03f4,
+0x0161, 0x00d8, 0x0213, 0x03aa,
+0x023c, 0x0385, 0x014e, 0x00f7,
+0x031a, 0x02a3, 0x0068, 0x01d1,
+0x0047, 0x01fe, 0x0335, 0x028c,
+0x0019, 0x01a0, 0x036b, 0x02d2,
+0x0344, 0x02fd, 0x0036, 0x018f,
+0x00a5, 0x011c, 0x03d7, 0x026e,
+0x03f8, 0x0241, 0x008a, 0x0133,
+0x03a6, 0x021f, 0x00d4, 0x016d,
+0x00fb, 0x0142, 0x0389, 0x0230,
+0x0184, 0x003d, 0x02f6, 0x034f,
+0x02d9, 0x0360, 0x01ab, 0x0012,
+0x0287, 0x033e, 0x01f5, 0x004c,
+0x01da, 0x0063, 0x02a8, 0x0311,
+0x023b, 0x0382, 0x0149, 0x00f0,
+0x0166, 0x00df, 0x0214, 0x03ad,
+0x0138, 0x0081, 0x024a, 0x03f3,
+0x0265, 0x03dc, 0x0117, 0x00ae,
+0x0343, 0x02fa, 0x0031, 0x0188,
+0x001e, 0x01a7, 0x036c, 0x02d5,
+0x0040, 0x01f9, 0x0332, 0x028b,
+0x031d, 0x02a4, 0x006f, 0x01d6,
+0x00fc, 0x0145, 0x038e, 0x0237,
+0x03a1, 0x0218, 0x00d3, 0x016a,
+0x03ff, 0x0246, 0x008d, 0x0134,
+0x00a2, 0x011b, 0x03d0, 0x0269,
+0x01b3, 0x000a, 0x02c1, 0x0378,
+0x02ee, 0x0357, 0x019c, 0x0025,
+0x02b0, 0x0309, 0x01c2, 0x007b,
+0x01ed, 0x0054, 0x029f, 0x0326,
+0x020c, 0x03b5, 0x017e, 0x00c7,
+0x0151, 0x00e8, 0x0223, 0x039a,
+0x010f, 0x00b6, 0x027d, 0x03c4,
+0x0252, 0x03eb, 0x0120, 0x0099,
+0x0374, 0x02cd, 0x0006, 0x01bf,
+0x0029, 0x0190, 0x035b, 0x02e2,
+0x0077, 0x01ce, 0x0305, 0x02bc,
+0x032a, 0x0293, 0x0058, 0x01e1,
+0x00cb, 0x0172, 0x03b9, 0x0200,
+0x0396, 0x022f, 0x00e4, 0x015d,
+0x03c8, 0x0271, 0x00ba, 0x0103,
+0x0095, 0x012c, 0x03e7, 0x025e,
+0x0136, 0x008f, 0x0244, 0x03fd,
+0x026b, 0x03d2, 0x0119, 0x00a0,
+0x0235, 0x038c, 0x0147, 0x00fe,
+0x0168, 0x00d1, 0x021a, 0x03a3,
+0x0289, 0x0330, 0x01fb, 0x0042,
+0x01d4, 0x006d, 0x02a6, 0x031f,
+0x018a, 0x0033, 0x02f8, 0x0341,
+0x02d7, 0x036e, 0x01a5, 0x001c,
+0x03f1, 0x0248, 0x0083, 0x013a,
+0x00ac, 0x0115, 0x03de, 0x0267,
+0x00f2, 0x014b, 0x0380, 0x0239,
+0x03af, 0x0216, 0x00dd, 0x0164,
+0x004e, 0x01f7, 0x033c, 0x0285,
+0x0313, 0x02aa, 0x0061, 0x01d8,
+0x034d, 0x02f4, 0x003f, 0x0186,
+0x0010, 0x01a9, 0x0362, 0x02db,
+0x0101, 0x00b8, 0x0273, 0x03ca,
+0x025c, 0x03e5, 0x012e, 0x0097,
+0x0202, 0x03bb, 0x0170, 0x00c9,
+0x015f, 0x00e6, 0x022d, 0x0394,
+0x02be, 0x0307, 0x01cc, 0x0075,
+0x01e3, 0x005a, 0x0291, 0x0328,
+0x01bd, 0x0004, 0x02cf, 0x0376,
+0x02e0, 0x0359, 0x0192, 0x002b,
+0x03c6, 0x027f, 0x00b4, 0x010d,
+0x009b, 0x0122, 0x03e9, 0x0250,
+0x00c5, 0x017c, 0x03b7, 0x020e,
+0x0398, 0x0221, 0x00ea, 0x0153,
+0x0079, 0x01c0, 0x030b, 0x02b2,
+0x0324, 0x029d, 0x0056, 0x01ef,
+0x037a, 0x02c3, 0x0008, 0x01b1,
+0x0027, 0x019e, 0x0355, 0x02ec,
+0x0158, 0x00e1, 0x022a, 0x0393,
+0x0205, 0x03bc, 0x0177, 0x00ce,
+0x025b, 0x03e2, 0x0129, 0x0090,
+0x0106, 0x00bf, 0x0274, 0x03cd,
+0x02e7, 0x035e, 0x0195, 0x002c,
+0x01ba, 0x0003, 0x02c8, 0x0371,
+0x01e4, 0x005d, 0x0296, 0x032f,
+0x02b9, 0x0300, 0x01cb, 0x0072,
+0x039f, 0x0226, 0x00ed, 0x0154,
+0x00c2, 0x017b, 0x03b0, 0x0209,
+0x009c, 0x0125, 0x03ee, 0x0257,
+0x03c1, 0x0278, 0x00b3, 0x010a,
+0x0020, 0x0199, 0x0352, 0x02eb,
+0x037d, 0x02c4, 0x000f, 0x01b6,
+0x0323, 0x029a, 0x0051, 0x01e8,
+0x007e, 0x01c7, 0x030c, 0x02b5,
+0x016f, 0x00d6, 0x021d, 0x03a4,
+0x0232, 0x038b, 0x0140, 0x00f9,
+0x026c, 0x03d5, 0x011e, 0x00a7,
+0x0131, 0x0088, 0x0243, 0x03fa,
+0x02d0, 0x0369, 0x01a2, 0x001b,
+0x018d, 0x0034, 0x02ff, 0x0346,
+0x01d3, 0x006a, 0x02a1, 0x0318,
+0x028e, 0x0337, 0x01fc, 0x0045,
+0x03a8, 0x0211, 0x00da, 0x0163,
+0x00f5, 0x014c, 0x0387, 0x023e,
+0x00ab, 0x0112, 0x03d9, 0x0260,
+0x03f6, 0x024f, 0x0084, 0x013d,
+0x0017, 0x01ae, 0x0365, 0x02dc,
+0x034a, 0x02f3, 0x0038, 0x0181,
+0x0314, 0x02ad, 0x0066, 0x01df,
+0x0049, 0x01f0, 0x033b, 0x0282,
+0x0322, 0x029b, 0x0050, 0x01e9,
+0x007f, 0x01c6, 0x030d, 0x02b4,
+0x0021, 0x0198, 0x0353, 0x02ea,
+0x037c, 0x02c5, 0x000e, 0x01b7,
+0x009d, 0x0124, 0x03ef, 0x0256,
+0x03c0, 0x0279, 0x00b2, 0x010b,
+0x039e, 0x0227, 0x00ec, 0x0155,
+0x00c3, 0x017a, 0x03b1, 0x0208,
+0x01e5, 0x005c, 0x0297, 0x032e,
+0x02b8, 0x0301, 0x01ca, 0x0073,
+0x02e6, 0x035f, 0x0194, 0x002d,
+0x01bb, 0x0002, 0x02c9, 0x0370,
+0x025a, 0x03e3, 0x0128, 0x0091,
+0x0107, 0x00be, 0x0275, 0x03cc,
+0x0159, 0x00e0, 0x022b, 0x0392,
+0x0204, 0x03bd, 0x0176, 0x00cf,
+0x0315, 0x02ac, 0x0067, 0x01de,
+0x0048, 0x01f1, 0x033a, 0x0283,
+0x0016, 0x01af, 0x0364, 0x02dd,
+0x034b, 0x02f2, 0x0039, 0x0180,
+0x00aa, 0x0113, 0x03d8, 0x0261,
+0x03f7, 0x024e, 0x0085, 0x013c,
+0x03a9, 0x0210, 0x00db, 0x0162,
+0x00f4, 0x014d, 0x0386, 0x023f,
+0x01d2, 0x006b, 0x02a0, 0x0319,
+0x028f, 0x0336, 0x01fd, 0x0044,
+0x02d1, 0x0368, 0x01a3, 0x001a,
+0x018c, 0x0035, 0x02fe, 0x0347,
+0x026d, 0x03d4, 0x011f, 0x00a6,
+0x0130, 0x0089, 0x0242, 0x03fb,
+0x016e, 0x00d7, 0x021c, 0x03a5,
+0x0233, 0x038a, 0x0141, 0x00f8,
+0x034c, 0x02f5, 0x003e, 0x0187,
+0x0011, 0x01a8, 0x0363, 0x02da,
+0x004f, 0x01f6, 0x033d, 0x0284,
+0x0312, 0x02ab, 0x0060, 0x01d9,
+0x00f3, 0x014a, 0x0381, 0x0238,
+0x03ae, 0x0217, 0x00dc, 0x0165,
+0x03f0, 0x0249, 0x0082, 0x013b,
+0x00ad, 0x0114, 0x03df, 0x0266,
+0x018b, 0x0032, 0x02f9, 0x0340,
+0x02d6, 0x036f, 0x01a4, 0x001d,
+0x0288, 0x0331, 0x01fa, 0x0043,
+0x01d5, 0x006c, 0x02a7, 0x031e,
+0x0234, 0x038d, 0x0146, 0x00ff,
+0x0169, 0x00d0, 0x021b, 0x03a2,
+0x0137, 0x008e, 0x0245, 0x03fc,
+0x026a, 0x03d3, 0x0118, 0x00a1,
+0x037b, 0x02c2, 0x0009, 0x01b0,
+0x0026, 0x019f, 0x0354, 0x02ed,
+0x0078, 0x01c1, 0x030a, 0x02b3,
+0x0325, 0x029c, 0x0057, 0x01ee,
+0x00c4, 0x017d, 0x03b6, 0x020f,
+0x0399, 0x0220, 0x00eb, 0x0152,
+0x03c7, 0x027e, 0x00b5, 0x010c,
+0x009a, 0x0123, 0x03e8, 0x0251,
+0x01bc, 0x0005, 0x02ce, 0x0377,
+0x02e1, 0x0358, 0x0193, 0x002a,
+0x02bf, 0x0306, 0x01cd, 0x0074,
+0x01e2, 0x005b, 0x0290, 0x0329,
+0x0203, 0x03ba, 0x0171, 0x00c8,
+0x015e, 0x00e7, 0x022c, 0x0395,
+0x0100, 0x00b9, 0x0272, 0x03cb,
+0x025d, 0x03e4, 0x012f, 0x0096,
+0x03fe, 0x0247, 0x008c, 0x0135,
+0x00a3, 0x011a, 0x03d1, 0x0268,
+0x00fd, 0x0144, 0x038f, 0x0236,
+0x03a0, 0x0219, 0x00d2, 0x016b,
+0x0041, 0x01f8, 0x0333, 0x028a,
+0x031c, 0x02a5, 0x006e, 0x01d7,
+0x0342, 0x02fb, 0x0030, 0x0189,
+0x001f, 0x01a6, 0x036d, 0x02d4,
+0x0139, 0x0080, 0x024b, 0x03f2,
+0x0264, 0x03dd, 0x0116, 0x00af,
+0x023a, 0x0383, 0x0148, 0x00f1,
+0x0167, 0x00de, 0x0215, 0x03ac,
+0x0286, 0x033f, 0x01f4, 0x004d,
+0x01db, 0x0062, 0x02a9, 0x0310,
+0x0185, 0x003c, 0x02f7, 0x034e,
+0x02d8, 0x0361, 0x01aa, 0x0013,
+0x03c9, 0x0270, 0x00bb, 0x0102,
+0x0094, 0x012d, 0x03e6, 0x025f,
+0x00ca, 0x0173, 0x03b8, 0x0201,
+0x0397, 0x022e, 0x00e5, 0x015c,
+0x0076, 0x01cf, 0x0304, 0x02bd,
+0x032b, 0x0292, 0x0059, 0x01e0,
+0x0375, 0x02cc, 0x0007, 0x01be,
+0x0028, 0x0191, 0x035a, 0x02e3,
+0x010e, 0x00b7, 0x027c, 0x03c5,
+0x0253, 0x03ea, 0x0121, 0x0098,
+0x020d, 0x03b4, 0x017f, 0x00c6,
+0x0150, 0x00e9, 0x0222, 0x039b,
+0x02b1, 0x0308, 0x01c3, 0x007a,
+0x01ec, 0x0055, 0x029e, 0x0327,
+0x01b2, 0x000b, 0x02c0, 0x0379,
+0x02ef, 0x0356, 0x019d, 0x0024,
+0x0390, 0x0229, 0x00e2, 0x015b,
+0x00cd, 0x0174, 0x03bf, 0x0206,
+0x0093, 0x012a, 0x03e1, 0x0258,
+0x03ce, 0x0277, 0x00bc, 0x0105,
+0x002f, 0x0196, 0x035d, 0x02e4,
+0x0372, 0x02cb, 0x0000, 0x01b9,
+0x032c, 0x0295, 0x005e, 0x01e7,
+0x0071, 0x01c8, 0x0303, 0x02ba,
+0x0157, 0x00ee, 0x0225, 0x039c,
+0x020a, 0x03b3, 0x0178, 0x00c1,
+0x0254, 0x03ed, 0x0126, 0x009f,
+0x0109, 0x00b0, 0x027b, 0x03c2,
+0x02e8, 0x0351, 0x019a, 0x0023,
+0x01b5, 0x000c, 0x02c7, 0x037e,
+0x01eb, 0x0052, 0x0299, 0x0320,
+0x02b6, 0x030f, 0x01c4, 0x007d,
+0x03a7, 0x021e, 0x00d5, 0x016c,
+0x00fa, 0x0143, 0x0388, 0x0231,
+0x00a4, 0x011d, 0x03d6, 0x026f,
+0x03f9, 0x0240, 0x008b, 0x0132,
+0x0018, 0x01a1, 0x036a, 0x02d3,
+0x0345, 0x02fc, 0x0037, 0x018e,
+0x031b, 0x02a2, 0x0069, 0x01d0,
+0x0046, 0x01ff, 0x0334, 0x028d,
+0x0160, 0x00d9, 0x0212, 0x03ab,
+0x023d, 0x0384, 0x014f, 0x00f6,
+0x0263, 0x03da, 0x0111, 0x00a8,
+0x013e, 0x0087, 0x024c, 0x03f5,
+0x02df, 0x0366, 0x01ad, 0x0014,
+0x0182, 0x003b, 0x02f0, 0x0349,
+0x01dc, 0x0065, 0x02ae, 0x0317,
+0x0281, 0x0338, 0x01f3, 0x004a,
+0x029a, 0x0323, 0x01e8, 0x0051,
+0x01c7, 0x007e, 0x02b5, 0x030c,
+0x0199, 0x0020, 0x02eb, 0x0352,
+0x02c4, 0x037d, 0x01b6, 0x000f,
+0x0125, 0x009c, 0x0257, 0x03ee,
+0x0278, 0x03c1, 0x010a, 0x00b3,
+0x0226, 0x039f, 0x0154, 0x00ed,
+0x017b, 0x00c2, 0x0209, 0x03b0,
+0x005d, 0x01e4, 0x032f, 0x0296,
+0x0300, 0x02b9, 0x0072, 0x01cb,
+0x035e, 0x02e7, 0x002c, 0x0195,
+0x0003, 0x01ba, 0x0371, 0x02c8,
+0x03e2, 0x025b, 0x0090, 0x0129,
+0x00bf, 0x0106, 0x03cd, 0x0274,
+0x00e1, 0x0158, 0x0393, 0x022a,
+0x03bc, 0x0205, 0x00ce, 0x0177,
+0x02ad, 0x0314, 0x01df, 0x0066,
+0x01f0, 0x0049, 0x0282, 0x033b,
+0x01ae, 0x0017, 0x02dc, 0x0365,
+0x02f3, 0x034a, 0x0181, 0x0038,
+0x0112, 0x00ab, 0x0260, 0x03d9,
+0x024f, 0x03f6, 0x013d, 0x0084,
+0x0211, 0x03a8, 0x0163, 0x00da,
+0x014c, 0x00f5, 0x023e, 0x0387,
+0x006a, 0x01d3, 0x0318, 0x02a1,
+0x0337, 0x028e, 0x0045, 0x01fc,
+0x0369, 0x02d0, 0x001b, 0x01a2,
+0x0034, 0x018d, 0x0346, 0x02ff,
+0x03d5, 0x026c, 0x00a7, 0x011e,
+0x0088, 0x0131, 0x03fa, 0x0243,
+0x00d6, 0x016f, 0x03a4, 0x021d,
+0x038b, 0x0232, 0x00f9, 0x0140,
+0x02f4, 0x034d, 0x0186, 0x003f,
+0x01a9, 0x0010, 0x02db, 0x0362,
+0x01f7, 0x004e, 0x0285, 0x033c,
+0x02aa, 0x0313, 0x01d8, 0x0061,
+0x014b, 0x00f2, 0x0239, 0x0380,
+0x0216, 0x03af, 0x0164, 0x00dd,
+0x0248, 0x03f1, 0x013a, 0x0083,
+0x0115, 0x00ac, 0x0267, 0x03de,
+0x0033, 0x018a, 0x0341, 0x02f8,
+0x036e, 0x02d7, 0x001c, 0x01a5,
+0x0330, 0x0289, 0x0042, 0x01fb,
+0x006d, 0x01d4, 0x031f, 0x02a6,
+0x038c, 0x0235, 0x00fe, 0x0147,
+0x00d1, 0x0168, 0x03a3, 0x021a,
+0x008f, 0x0136, 0x03fd, 0x0244,
+0x03d2, 0x026b, 0x00a0, 0x0119,
+0x02c3, 0x037a, 0x01b1, 0x0008,
+0x019e, 0x0027, 0x02ec, 0x0355,
+0x01c0, 0x0079, 0x02b2, 0x030b,
+0x029d, 0x0324, 0x01ef, 0x0056,
+0x017c, 0x00c5, 0x020e, 0x03b7,
+0x0221, 0x0398, 0x0153, 0x00ea,
+0x027f, 0x03c6, 0x010d, 0x00b4,
+0x0122, 0x009b, 0x0250, 0x03e9,
+0x0004, 0x01bd, 0x0376, 0x02cf,
+0x0359, 0x02e0, 0x002b, 0x0192,
+0x0307, 0x02be, 0x0075, 0x01cc,
+0x005a, 0x01e3, 0x0328, 0x0291,
+0x03bb, 0x0202, 0x00c9, 0x0170,
+0x00e6, 0x015f, 0x0394, 0x022d,
+0x00b8, 0x0101, 0x03ca, 0x0273,
+0x03e5, 0x025c, 0x0097, 0x012e,
+0x0246, 0x03ff, 0x0134, 0x008d,
+0x011b, 0x00a2, 0x0269, 0x03d0,
+0x0145, 0x00fc, 0x0237, 0x038e,
+0x0218, 0x03a1, 0x016a, 0x00d3,
+0x01f9, 0x0040, 0x028b, 0x0332,
+0x02a4, 0x031d, 0x01d6, 0x006f,
+0x02fa, 0x0343, 0x0188, 0x0031,
+0x01a7, 0x001e, 0x02d5, 0x036c,
+0x0081, 0x0138, 0x03f3, 0x024a,
+0x03dc, 0x0265, 0x00ae, 0x0117,
+0x0382, 0x023b, 0x00f0, 0x0149,
+0x00df, 0x0166, 0x03ad, 0x0214,
+0x033e, 0x0287, 0x004c, 0x01f5,
+0x0063, 0x01da, 0x0311, 0x02a8,
+0x003d, 0x0184, 0x034f, 0x02f6,
+0x0360, 0x02d9, 0x0012, 0x01ab,
+0x0271, 0x03c8, 0x0103, 0x00ba,
+0x012c, 0x0095, 0x025e, 0x03e7,
+0x0172, 0x00cb, 0x0200, 0x03b9,
+0x022f, 0x0396, 0x015d, 0x00e4,
+0x01ce, 0x0077, 0x02bc, 0x0305,
+0x0293, 0x032a, 0x01e1, 0x0058,
+0x02cd, 0x0374, 0x01bf, 0x0006,
+0x0190, 0x0029, 0x02e2, 0x035b,
+0x00b6, 0x010f, 0x03c4, 0x027d,
+0x03eb, 0x0252, 0x0099, 0x0120,
+0x03b5, 0x020c, 0x00c7, 0x017e,
+0x00e8, 0x0151, 0x039a, 0x0223,
+0x0309, 0x02b0, 0x007b, 0x01c2,
+0x0054, 0x01ed, 0x0326, 0x029f,
+0x000a, 0x01b3, 0x0378, 0x02c1,
+0x0357, 0x02ee, 0x0025, 0x019c,
+0x0228, 0x0391, 0x015a, 0x00e3,
+0x0175, 0x00cc, 0x0207, 0x03be,
+0x012b, 0x0092, 0x0259, 0x03e0,
+0x0276, 0x03cf, 0x0104, 0x00bd,
+0x0197, 0x002e, 0x02e5, 0x035c,
+0x02ca, 0x0373, 0x01b8, 0x0001,
+0x0294, 0x032d, 0x01e6, 0x005f,
+0x01c9, 0x0070, 0x02bb, 0x0302,
+0x00ef, 0x0156, 0x039d, 0x0224,
+0x03b2, 0x020b, 0x00c0, 0x0179,
+0x03ec, 0x0255, 0x009e, 0x0127,
+0x00b1, 0x0108, 0x03c3, 0x027a,
+0x0350, 0x02e9, 0x0022, 0x019b,
+0x000d, 0x01b4, 0x037f, 0x02c6,
+0x0053, 0x01ea, 0x0321, 0x0298,
+0x030e, 0x02b7, 0x007c, 0x01c5,
+0x021f, 0x03a6, 0x016d, 0x00d4,
+0x0142, 0x00fb, 0x0230, 0x0389,
+0x011c, 0x00a5, 0x026e, 0x03d7,
+0x0241, 0x03f8, 0x0133, 0x008a,
+0x01a0, 0x0019, 0x02d2, 0x036b,
+0x02fd, 0x0344, 0x018f, 0x0036,
+0x02a3, 0x031a, 0x01d1, 0x0068,
+0x01fe, 0x0047, 0x028c, 0x0335,
+0x00d8, 0x0161, 0x03aa, 0x0213,
+0x0385, 0x023c, 0x00f7, 0x014e,
+0x03db, 0x0262, 0x00a9, 0x0110,
+0x0086, 0x013f, 0x03f4, 0x024d,
+0x0367, 0x02de, 0x0015, 0x01ac,
+0x003a, 0x0183, 0x0348, 0x02f1,
+0x0064, 0x01dd, 0x0316, 0x02af,
+0x0339, 0x0280, 0x004b, 0x01f2,
+};
+#endif /*USE_RDS_HW_DECODER*/
+
+#endif /*fm_rds.h*/
diff --git a/drivers/media/radio/s610/radio-s610.c b/drivers/media/radio/s610/radio-s610.c
new file mode 100644 (file)
index 0000000..07817a3
--- /dev/null
@@ -0,0 +1,2609 @@
+/*
+ * drivers/media/radio/s610/radio-s610.c -- V4L2 driver for S610 chips
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
+#include <linux/wakelock.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <soc/samsung/exynos-pmu.h>
+#include <linux/mcu_ipc.h>
+
+#ifdef CONFIG_SCSC_FM
+#include <scsc/scsc_mx.h>
+#endif
+
+#include "fm_low_struc.h"
+#include "radio-s610.h"
+
+//#include "../../../../sound/soc/samsung/abox/abox.h"
+
+static int radio_region;
+module_param(radio_region, int, 0);
+MODULE_PARM_DESC(radio_region, "Region: 0=Europe/US, 1=Japan");
+
+struct s610_radio;
+
+/* global variable for radio structure */
+struct s610_radio *gradio;
+
+#define        FAC_VALUE       16000
+
+static int s610_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
+static int s610_radio_s_ctrl(struct v4l2_ctrl *ctrl);
+static int s610_core_set_power_state(struct s610_radio *radio,
+               u8 next_state);
+int fm_set_mute_mode(struct s610_radio *radio, u8 mute_mode_toset);
+static int fm_radio_runtime_resume(struct device *dev);
+void enable_FM_mux_clk_aud(struct s610_radio *radio);
+void disable_clk_gating(struct s610_radio *radio);
+
+signed int exynos_get_fm_open_status(void);
+signed int shared_fm_open_cnt;
+
+extern fm_conf_ini_values low_fm_conf_init;
+u32 *fm_spur_init;
+u32 *fm_spur_trf_init;
+u32 *fm_dual_clk_init;
+u32 vol_level_init[FM_RX_VOLUME_GAIN_STEP] = {
+       0, 16, 23, 32, 45, 64, 90, 128, 181, 256, 362, 512, 724, 1024, 1447, 2047 };
+
+static const struct v4l2_ctrl_ops s610_ctrl_ops = {
+               .s_ctrl                 = s610_radio_s_ctrl,
+               .g_volatile_ctrl       = s610_radio_g_volatile_ctrl,
+
+};
+
+enum s610_ctrl_idx {
+       S610_IDX_CH_SPACING  = 0x01,
+       S610_IDX_CH_BAND    =  0x02,
+       S610_IDX_SOFT_STEREO_BLEND = 0x03,
+       S610_IDX_SOFT_STEREO_BLEND_COEFF = 0x04,
+       S610_IDX_SOFT_MUTE_COEFF = 0x05,
+       S610_IDX_RSSI_CURR      = 0x06,
+       S610_IDX_SNR_CURR       = 0x07,
+       S610_IDX_SEEK_CANCEL    = 0x08,
+       S610_IDX_SEEK_MODE      = 0x09,
+       S610_IDX_RDS_ON = 0x0A,
+       S610_IDX_IF_COUNT1 = 0x0B,
+       S610_IDX_IF_COUNT2 = 0x0C,
+       S610_IDX_RSSI_TH        = 0x0D,
+       S610_IDX_KERNEL_VER     = 0x0E,
+       S610_IDX_SOFT_STEREO_BLEND_REF  = 0x0F
+};
+
+static struct v4l2_ctrl_config s610_ctrls[] = {
+               /**
+                * S610 during its station seeking(or tuning) process uses several
+                * parameters to detrmine if "the station" is valid:
+                *
+                *      - Signal's RSSI(in dBuV) must be greater than
+                *      #V4L2_CID_S610_RSSI_THRESHOLD
+                */
+               [S610_IDX_CH_SPACING] = {/*0x01*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_CH_SPACING,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Channel Spacing",
+                               .min    = 0,
+                               .max    = 2,
+                               .step   = 1,
+               },
+               [S610_IDX_CH_BAND] = { /*0x02*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_CH_BAND,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Channel Band",
+                               .min    = 0,
+                               .max    = 1,
+                               .step   = 1,
+               },
+               [S610_IDX_SOFT_STEREO_BLEND] = { /*0x03*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_SOFT_STEREO_BLEND,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Soft Stereo Blend",
+                               .min    =     0,
+                               .max    = 1,
+                               .step   = 1,
+               },
+               [S610_IDX_SOFT_STEREO_BLEND_COEFF] = { /*0x04*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_SOFT_STEREO_BLEND_COEFF,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Soft Stereo Blend COEFF",
+                               .min    =     0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_SOFT_MUTE_COEFF] = { /*0x05*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_SOFT_MUTE_COEFF,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Soft Mute COEFF Set",
+                               .min    =     0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_RSSI_CURR] = { /*0x06*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_RSSI_CURR,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "RSSI Current",
+                               .min    =     0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_SNR_CURR] = { /*0x07*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_SNR_CURR,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "SNR Current",
+                               .min    =     0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_SEEK_CANCEL] = { /*0x08*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_SEEK_CANCEL,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Seek Cancel",
+                               .min    =     0,
+                               .max    = 1,
+                               .step   = 1,
+               },
+               [S610_IDX_SEEK_MODE] = { /*0x09*/
+                               .ops    = &s610_ctrl_ops,
+                               .id     = V4L2_CID_S610_SEEK_MODE,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Seek Mode",
+                               .min    =     0,
+                               .max    = 4,
+                               .step   = 1,
+               },
+               [S610_IDX_RDS_ON] = { /*0x0A*/
+                               .ops    = &s610_ctrl_ops,
+                               .id = V4L2_CID_S610_RDS_ON,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "RDS ON",
+                               .min    =         0,
+                               .max    = 0x0F,
+                               .step   = 1,
+               },
+               [S610_IDX_IF_COUNT1] = { /*0x0B*/
+                               .ops    = &s610_ctrl_ops,
+                               .id = V4L2_CID_S610_IF_COUNT1,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "IF_COUNT1",
+                               .min    =         0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_IF_COUNT2] = { /*0x0C*/
+                               .ops    = &s610_ctrl_ops,
+                               .id = V4L2_CID_S610_IF_COUNT2,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "IF_COUNT2",
+                               .min    =         0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_RSSI_TH] = { /*0x0D*/
+                               .ops    = &s610_ctrl_ops,
+                               .id = V4L2_CID_S610_RSSI_TH,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "RSSI Th",
+                               .min    =         0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_KERNEL_VER] = { /*0x0E*/
+                               .ops    = &s610_ctrl_ops,
+                               .id = V4L2_CID_S610_KERNEL_VER,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "KERNEL_VER",
+                               .min    =         0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+               [S610_IDX_SOFT_STEREO_BLEND_REF] = { /*0x0F*/
+                               .ops    = &s610_ctrl_ops,
+                               .id = V4L2_CID_S610_SOFT_STEREO_BLEND_REF,
+                               .type   = V4L2_CTRL_TYPE_INTEGER,
+                               .name   = "Soft Stereo Blend Ref",
+                               .min    =         0,
+                               .max    = 0xffff,
+                               .step   = 1,
+               },
+};
+
+static const struct v4l2_frequency_band s610_bands[] = {
+               [0] = {
+                               .type           = V4L2_TUNER_RADIO,
+                               .index          = S610_BAND_FM,
+                               .capability     = V4L2_TUNER_CAP_LOW
+                               | V4L2_TUNER_CAP_STEREO
+                               | V4L2_TUNER_CAP_RDS
+                               | V4L2_TUNER_CAP_RDS_BLOCK_IO
+                               | V4L2_TUNER_CAP_FREQ_BANDS,
+                               /* default region Eu/US */
+                               .rangelow       = 87500*FAC_VALUE,
+                               .rangehigh      = 108000*FAC_VALUE,
+                               .modulation     = V4L2_BAND_MODULATION_FM,
+               },
+               [1] = {
+                               .type           = V4L2_TUNER_RADIO,
+                               .index          = S610_BAND_FM,
+                               .capability     = V4L2_TUNER_CAP_LOW
+                               | V4L2_TUNER_CAP_STEREO
+                               | V4L2_TUNER_CAP_RDS
+                               | V4L2_TUNER_CAP_RDS_BLOCK_IO
+                               | V4L2_TUNER_CAP_FREQ_BANDS,
+                               /* default region Eu/US */
+                               .rangelow       = 76000*FAC_VALUE,
+                               .rangehigh      = 90000*FAC_VALUE,
+                               .modulation     = V4L2_BAND_MODULATION_FM,
+               },
+};
+
+/* Region info */
+static struct region_info region_configs[] = {
+               /* Europe/US */
+               {
+                       .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
+                       .bot_freq = 87500,      /* 87.5 MHz */
+                       .top_freq = 108000,     /* 108 MHz */
+                       .fm_band = 0,
+               },
+               /* Japan */
+               {
+                       .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
+                       .bot_freq = 76000,      /* 76 MHz */
+                       .top_freq = 90000,      /* 90 MHz */
+                       .fm_band = 1,
+               },
+};
+
+static inline bool s610_radio_freq_is_inside_of_the_band(u32 freq, int band)
+{
+       return freq >= region_configs[radio_region].bot_freq &&
+                       freq <= region_configs[radio_region].top_freq;
+}
+
+static inline struct s610_radio *
+v4l2_dev_to_radio(struct v4l2_device *d)
+{
+       return container_of(d, struct s610_radio, v4l2dev);
+}
+
+static inline struct s610_radio *
+v4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d)
+{
+       return container_of(d, struct s610_radio, ctrl_handler);
+}
+
+/*
+ * s610_vidioc_querycap - query device capabilities
+ */
+static int s610_radio_querycap(struct file *file, void *priv,
+               struct v4l2_capability *capability)
+{
+       struct s610_radio *radio = video_drvdata(file);
+
+       FUNC_ENTRY(radio);
+
+       s610_core_lock(radio->core);
+
+       strlcpy(capability->driver, radio->v4l2dev.name,
+                       sizeof(capability->driver));
+       strlcpy(capability->card,   DRIVER_CARD, sizeof(capability->card));
+       snprintf(capability->bus_info, sizeof(capability->bus_info),
+                       "platform:%s", radio->v4l2dev.name);
+
+       capability->device_caps = V4L2_CAP_TUNER
+                       | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE
+                       | V4L2_CAP_READWRITE | V4L2_CAP_HW_FREQ_SEEK;
+
+       capability->capabilities = capability->device_caps
+                       | V4L2_CAP_DEVICE_CAPS;
+
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return 0;
+}
+
+static int s610_radio_enum_freq_bands(struct file *file, void *priv,
+               struct v4l2_frequency_band *band)
+{
+       int ret;
+       struct s610_radio *radio = video_drvdata(file);
+
+       FUNC_ENTRY(radio);
+
+       if (band->tuner != 0)
+               return -EINVAL;
+
+       s610_core_lock(radio->core);
+
+       if (band->index == S610_BAND_FM) {
+               *band = s610_bands[radio_region];
+               ret = 0;
+       } else {
+               ret = -EINVAL;
+       }
+
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_g_tuner(struct file *file, void *priv,
+               struct v4l2_tuner *tuner)
+{
+       int ret;
+       struct s610_radio *radio = video_drvdata(file);
+       u16 payload;
+
+       FUNC_ENTRY(radio);
+
+       if (tuner->index != 0)
+               return -EINVAL;
+
+       s610_core_lock(radio->core);
+
+       tuner->type       = V4L2_TUNER_RADIO;
+       tuner->capability = V4L2_TUNER_CAP_LOW
+                       | V4L2_TUNER_CAP_STEREO
+                       | V4L2_TUNER_CAP_HWSEEK_BOUNDED
+                       | V4L2_TUNER_CAP_HWSEEK_WRAP
+                       | V4L2_TUNER_CAP_HWSEEK_PROG_LIM;
+
+
+       strlcpy(tuner->name, "FM", sizeof(tuner->name));
+
+       tuner->rxsubchans = V4L2_TUNER_SUB_RDS;
+       tuner->capability |= V4L2_TUNER_CAP_RDS
+                       | V4L2_TUNER_CAP_RDS_BLOCK_IO
+                       | V4L2_TUNER_CAP_FREQ_BANDS;
+
+       /* Read register : MONO, Stereo mode */
+       /*ret = low_get_most_mode(radio, &payload);*/
+       payload = radio->low->fm_state.force_mono ?
+                       0 : MODE_MASK_MONO_STEREO;
+       radio->audmode = payload;
+       tuner->audmode = radio->audmode;
+       tuner->afc = 1;
+       tuner->rangelow = s610_bands[radio_region].rangelow;
+       tuner->rangehigh = s610_bands[radio_region].rangehigh;
+
+       ret = low_get_search_lvl(radio, &payload);
+       if (ret != 0) {
+               dev_err(radio->v4l2dev.dev,
+                       "Failed to read reg for SEARCH_LVL\n");
+               ret = -EIO;
+               tuner->signal = 0;
+       } else {
+               tuner->signal = 0;
+               if (payload & 0x80)
+                       tuner->signal = 0xFF00;
+               else
+                       tuner->signal = 0;
+
+               tuner->signal |= (payload & 0xFF);
+       }
+
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_s_tuner(struct file *file, void *priv,
+               const struct v4l2_tuner *tuner)
+{
+       int ret;
+       struct s610_radio *radio = video_drvdata(file);
+       u16 payload;
+
+       FUNC_ENTRY(radio);
+
+       if (tuner->index != 0)
+               return -EINVAL;
+
+       s610_core_lock(radio->core);
+
+       if (tuner->audmode == V4L2_TUNER_MODE_MONO ||
+                       tuner->audmode == V4L2_TUNER_MODE_STEREO)
+               radio->audmode = tuner->audmode;
+       else
+               radio->audmode = V4L2_TUNER_MODE_STEREO;
+
+       payload = radio->audmode;
+       ret = low_set_most_mode(radio, payload);
+       if (ret != 0) {
+               dev_err(radio->v4l2dev.dev,
+                       "Failed to write reg for MOST MODE clear\n");
+               ret = -EIO;
+       }
+
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_pretune(struct s610_radio *radio)
+{
+       int ret = 0;
+       u16 payload;
+
+       FUNC_ENTRY(radio);
+
+       /*ret = low_get_flag(radio, &payload);*/
+       payload = fm_get_flags(radio);
+
+       payload = 0;
+       ret = low_set_mute_state(radio, payload);
+       if (ret != 0) {
+               dev_err(radio->v4l2dev.dev,
+                               "Failed to write reg for MUTE state clean\n");
+               return -EIO;
+       }
+
+       payload = radio->low->fm_state.force_mono ?
+                       0 : MODE_MASK_MONO_STEREO;
+
+       payload = 0;
+       ret = low_set_most_blend(radio, payload);
+       if (ret != 0) {
+               dev_err(radio->v4l2dev.dev,
+                               "Failed to write reg for MOST blend clean\n");
+               return -EIO;
+       }
+
+       payload = 0;
+       fm_set_band(radio, payload);
+
+       payload = 0;
+       ret = low_set_demph_mode(radio, payload);
+       if (ret != 0) {
+               dev_err(radio->v4l2dev.dev,
+                       "Failed to write reg for FM_SUBADDR_DEMPH_MODE clean\n");
+               return -EIO;
+       }
+
+       payload = 0;
+       radio->low->fm_state.use_rbds =
+               ((payload & RDS_SYSTEM_MASK_RDS) != 0);
+       radio->low->fm_state.save_eblks =
+               ((payload & RDS_SYSTEM_MASK_EBLK) == 0);
+
+       radio->low->fm_state.rds_mem_thresh = RDS_MEM_MAX_THRESH;
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_g_frequency(struct file *file, void *priv,
+               struct v4l2_frequency *f)
+{
+       struct s610_radio *radio = video_drvdata(file);
+       u32 payload;
+
+       FUNC_ENTRY(radio);
+
+       if (f->tuner != 0 || f->type  != V4L2_TUNER_RADIO)
+               return -EINVAL;
+
+       s610_core_lock(radio->core);
+
+       payload = radio->low->fm_state.freq;
+       f->frequency = payload;
+       f->frequency *= FAC_VALUE;
+
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       FDEBUG(radio,
+               "%s():freq.tuner:%d, type:%d, freq:%d, real-freq:%d\n",
+               __func__, f->tuner, f->type, f->frequency,
+               f->frequency/FAC_VALUE);
+
+       return 0;
+}
+
+int fm_set_frequency(struct s610_radio *radio, u32 freq)
+{
+       unsigned long timeleft;
+       u32 curr_frq, intr_flag;
+       u32 curr_frq_in_khz;
+       int ret;
+       u32 payload;
+       u16 payload16;
+
+       FUNC_ENTRY(radio);
+
+       /* Calculate frequency with offset and set*/
+       payload = real_to_api(freq);
+
+       low_set_freq(radio, payload);
+
+       /* Read flags - just to clear any pending interrupts if we had */
+       payload = fm_get_flags(radio);
+
+       /* Enable FR, BL interrupts */
+       intr_flag = radio->irq_mask;
+       radio->irq_mask = (FM_EVENT_TUNED | FM_EVENT_BD_LMT);
+
+       low_get_search_lvl(radio, (u16 *) &payload16);
+       if (!payload16) {
+               payload16 = FM_DEFAULT_RSSI_THRESHOLD;
+               low_set_search_lvl(radio, (u16) payload16);
+       }
+
+       if (radio->low->fm_config.search_conf.normal_ifca_m == 0)
+               radio->low->fm_config.search_conf.normal_ifca_m =
+                       low_fm_conf_init.search_conf.normal_ifca_m;
+
+       if (radio->low->fm_config.search_conf.weak_ifca_m == 0)
+               radio->low->fm_config.search_conf.weak_ifca_m =
+                       low_fm_conf_init.search_conf.weak_ifca_m;
+
+       FDEBUG(radio, "%s(): ifcount:W-%d N-%d\n",      __func__,
+               radio->low->fm_config.search_conf.weak_ifca_m,
+               radio->low->fm_config.search_conf.normal_ifca_m);
+
+       if (!radio->low->fm_config.mute_coeffs_soft) {
+               radio->low->fm_config.mute_coeffs_soft =
+                       low_fm_conf_init.mute_coeffs_soft;
+       }
+
+       if (!radio->low->fm_config.blend_coeffs_soft) {
+               radio->low->fm_config.blend_coeffs_soft =
+                       low_fm_conf_init.blend_coeffs_soft;
+       }
+
+       if (!radio->low->fm_config.blend_coeffs_switch) {
+               radio->low->fm_config.blend_coeffs_switch =
+                       low_fm_conf_init.blend_coeffs_switch;
+       }
+
+       if (!radio->low->fm_config.blend_coeffs_dis)
+               radio->low->fm_config.blend_coeffs_dis =
+               low_fm_conf_init.blend_coeffs_dis;
+
+       fm_set_blend_mute(radio);
+
+       init_completion(&radio->flags_set_fr_comp);
+
+       /* Start tune */
+       payload = FM_TUNER_PRESET_MODE;
+       ret = low_set_tuner_mode(radio, payload);
+       if (ret != 0) {
+               ret = -EIO;
+               goto exit;
+       }
+
+       timeleft = jiffies_to_msecs(FM_DRV_TURN_TIMEOUT);
+       /* Wait for tune ended interrupt */
+       timeleft = wait_for_completion_timeout(&radio->flags_set_fr_comp,
+                       FM_DRV_TURN_TIMEOUT);
+
+       if (!timeleft) {
+               dev_err(radio->v4l2dev.dev,
+                               "Timeout(%d sec),didn't get tune ended int\n",
+                               jiffies_to_msecs(FM_DRV_TURN_TIMEOUT) / 1000);
+               ret = -ETIMEDOUT;
+               goto exit;
+       }
+
+       /* Read freq back to confirm */
+       curr_frq = radio->low->fm_state.freq;
+       curr_frq_in_khz = curr_frq;
+       if (curr_frq_in_khz != freq) {
+               dev_err(radio->v4l2dev.dev,
+                               "Set Freq (%d) but requested freq (%d)\n",
+                               curr_frq_in_khz, freq);
+               ret = -ENODATA;
+               goto exit;
+       }
+
+       FDEBUG(radio,
+                       "%s():--> Set frequency: %d Read frequency:%d\n",
+                       __func__,  freq, curr_frq_in_khz);
+
+       /* Update local cache  */
+       radio->freq = curr_frq_in_khz;
+exit:
+       /* Re-enable default FM interrupts */
+       radio->irq_mask = intr_flag;
+
+       FDEBUG(radio, "wait_atomic: %d\n", radio->wait_atomic);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_s_frequency(struct file *file, void *priv,
+               const struct v4l2_frequency *f)
+{
+       int ret;
+       u32 freq = f->frequency;
+       struct s610_radio *radio = video_drvdata(file);
+
+       FUNC_ENTRY(radio);
+
+       if (f->tuner != 0 ||
+                       f->type  != V4L2_TUNER_RADIO)
+               return -EINVAL;
+
+       if (!wake_lock_active(&radio->wakelock))
+               wake_lock(&radio->wakelock);
+
+       s610_core_lock(radio->core);
+       FDEBUG(radio, "%s():freq:%d, real-freq:%d\n",
+                       __func__, f->frequency, f->frequency/FAC_VALUE);
+
+       freq /= FAC_VALUE;
+
+       freq = clamp(freq,
+                       region_configs[radio_region].bot_freq,
+                       region_configs[radio_region].top_freq);
+       if (!s610_radio_freq_is_inside_of_the_band(freq,
+                       radio_region)) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       ret = fm_set_frequency(radio, freq);
+
+unlock:
+       s610_core_unlock(radio->core);
+
+       if (wake_lock_active(&radio->wakelock))
+               wake_unlock(&radio->wakelock);
+
+       FUNC_EXIT(radio);
+
+       FDEBUG(radio,
+                       "%s():v4l2_frequency.tuner:%d,type:%d,frequency:%d\n",
+                       __func__, f->tuner, f->type, freq);
+
+       return ret;
+}
+
+int fm_rx_seek(struct s610_radio *radio, u32 seek_upward,
+               u32 wrap_around, u32 spacing, u32 freq_low, u32 freq_hi)
+{
+       u32 curr_frq, save_freq;
+       u32 payload, int_reason, intr_flag, tune_mode;
+       u32 space, upward;
+       u16 payload16;
+       unsigned long timeleft;
+       int ret;
+       int     bl_1st, bl_2nd, bl_3nd;
+
+       FUNC_ENTRY(radio);
+
+       radio->seek_freq = 0;
+       radio->wrap_around = 0;
+       bl_1st = 0;
+       bl_2nd = 0;
+       bl_3nd = 0;
+
+       payload = fm_get_flags(radio);
+
+       /* Set channel spacing */
+       if (spacing > 0 && spacing <= 50)
+               payload = 0; /*CHANNEL_SPACING_50KHZ*/
+       else if (spacing > 50 && spacing <= 100)
+               payload = 1; /*CHANNEL_SPACING_100KHZ*/
+       else
+               payload = 2; /*CHANNEL_SPACING_200KHZ;*/
+
+       FDEBUG(radio, "%s(): init: spacing: %d\n", __func__, payload);
+
+       radio->low->fm_tuner_state.freq_step =
+                       radio->low->fm_freq_steps[payload];
+       radio->region.chanl_space = 50 * (1 << payload);
+
+       /* Check the offset in order to be aligned to the channel spacing*/
+       space = radio->region.chanl_space;
+
+       /* Set search direction (0:Seek Up, 1:Seek Down) */
+       payload = (seek_upward ? FM_SEARCH_DIRECTION_UP :
+               FM_SEARCH_DIRECTION_DOWN);
+       upward = payload;
+       radio->low->fm_state.search_down =
+                       !!(payload & SEARCH_DIR_MASK);
+       FDEBUG(radio, "%s(): direction: %d\n",  __func__, payload);
+
+       save_freq = freq_low;
+       radio->seek_freq = save_freq;
+       tune_mode = FM_TUNER_AUTONOMOUS_SEARCH_MODE_NEXT;
+
+       if (radio->low->fm_state.rssi_limit_search == 0)
+               low_set_search_lvl(radio, (u16)FM_DEFAULT_RSSI_THRESHOLD);
+
+       if (radio->low->fm_config.search_conf.normal_ifca_m == 0)
+               radio->low->fm_config.search_conf.normal_ifca_m =
+                       low_fm_conf_init.search_conf.normal_ifca_m;
+
+       if (radio->low->fm_config.search_conf.weak_ifca_m == 0)
+               radio->low->fm_config.search_conf.weak_ifca_m =
+                       low_fm_conf_init.search_conf.weak_ifca_m;
+
+               FDEBUG(radio, "%s(): ifcount:W-%d N-%d\n",      __func__,
+               radio->low->fm_config.search_conf.weak_ifca_m,
+               radio->low->fm_config.search_conf.normal_ifca_m);
+
+again:
+       curr_frq = freq_low;
+       if ((freq_low == region_configs[radio_region].bot_freq)
+               && (upward == FM_SEARCH_DIRECTION_DOWN))
+               curr_frq = region_configs[radio_region].top_freq;
+
+       if ((freq_low == region_configs[radio_region].top_freq)
+               && (upward == FM_SEARCH_DIRECTION_UP))
+               curr_frq = region_configs[radio_region].bot_freq;
+
+       payload = curr_frq;
+       low_set_freq(radio, payload);
+
+       FDEBUG(radio, "%s(): curr_freq: %d, freq hi: %d\n",
+               __func__, curr_frq, freq_hi);
+
+       /* Enable FR, BL interrupts */
+       intr_flag = radio->irq_mask;
+       radio->irq_mask = (FM_EVENT_TUNED | FM_EVENT_BD_LMT);
+       low_get_search_lvl(radio, (u16 *) &payload16);
+       if (!payload16) {
+               payload16 = FM_DEFAULT_RSSI_THRESHOLD;
+               low_set_search_lvl(radio, (u16) payload16);
+       }
+       FDEBUG(radio, "%s(): SEARCH_LVL1: 0x%x\n",  __func__, payload16);
+
+       if (!radio->low->fm_config.mute_coeffs_soft) {
+               radio->low->fm_config.mute_coeffs_soft =
+                       low_fm_conf_init.mute_coeffs_soft;
+       }
+
+       if (!radio->low->fm_config.blend_coeffs_soft) {
+               radio->low->fm_config.blend_coeffs_soft =
+                       low_fm_conf_init.blend_coeffs_soft;
+       }
+
+       if (!radio->low->fm_config.blend_coeffs_switch) {
+               radio->low->fm_config.blend_coeffs_switch =
+                       low_fm_conf_init.blend_coeffs_switch;
+       }
+
+       if (!radio->low->fm_config.blend_coeffs_dis)
+               radio->low->fm_config.blend_coeffs_dis =
+               low_fm_conf_init.blend_coeffs_dis;
+
+       fm_set_blend_mute(radio);
+
+       reinit_completion(&radio->flags_seek_fr_comp);
+
+       payload = tune_mode;
+       FDEBUG(radio,
+                       "%s(): turn start mode: 0x%x\n",  __func__, payload);
+       ret = low_set_tuner_mode(radio, (u16) payload);
+       if (ret != 0)
+               return -EIO;
+
+       /* Wait for tune ended interrupt */
+       timeleft =
+               wait_for_completion_timeout(&radio->flags_seek_fr_comp,
+               FM_DRV_SEEK_TIMEOUT);
+
+       /* seek cancel status */
+       if (radio->seek_status == FM_TUNER_STOP_SEARCH_MODE) {
+               dev_info(radio->dev, ">>> rev seek cancel");
+               return -ENODATA;
+       }
+
+       FDEBUG(radio,
+               "FDEBUG > Seek done rev complete!! freq %d, irq_flag: 0x%x, bl:%d\n",
+               radio->low->fm_state.freq, radio->irq_flag, bl_1st);
+
+       int_reason = radio->low->fm_state.flags &
+                       (FM_EVENT_TUNED | FM_EVENT_BD_LMT);
+
+       if ((save_freq == region_configs[radio_region].bot_freq)
+               || (save_freq == region_configs[radio_region].top_freq))
+                       bl_1st = 1;
+
+       if ((save_freq == region_configs[radio_region].bot_freq)
+               && (upward == FM_SEARCH_DIRECTION_UP)) {
+               bl_1st = 0;
+               bl_2nd = 1;
+               tune_mode = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
+       }
+       if ((save_freq == region_configs[radio_region].top_freq)
+               && (upward == FM_SEARCH_DIRECTION_DOWN)) {
+               bl_1st = 0;
+               bl_2nd = 1;
+               tune_mode = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
+       }
+
+       if ((int_reason & FM_EVENT_BD_LMT) && (bl_1st == 0) && (bl_3nd == 0)) {
+               if (wrap_around) {
+                       freq_low = radio->low->fm_state.freq;
+                       bl_1st = 1;
+                       if (bl_2nd)
+                               bl_3nd = 1;
+
+                       /* Re-enable default FM interrupts */
+                       radio->irq_mask = intr_flag;
+                       radio->wrap_around = 1;
+                       FDEBUG(radio, "> bl set %d, %d, save %d\n",
+                               radio->seek_freq, radio->wrap_around, save_freq);
+
+                       goto again;
+               } else
+                       ret = -EINVAL;
+       }
+
+       /* Read freq to know where operation tune operation stopped */
+       payload = radio->low->fm_state.freq;
+       radio->freq = payload;
+
+       dev_info(radio->v4l2dev.dev, "Seek freq %d\n", radio->freq);
+
+       radio->seek_freq = 0;
+       radio->wrap_around = 0;
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_s_hw_freq_seek(struct file *file, void *priv,
+               const struct v4l2_hw_freq_seek *seek)
+{
+       int ret = 0;
+       struct s610_radio *radio = video_drvdata(file);
+       u32 seek_low, seek_hi, seek_spacing;
+
+       FUNC_ENTRY(radio);
+
+       if (file->f_flags & O_NONBLOCK)
+               return -EAGAIN;
+
+       if (seek->tuner != 0 ||
+                       seek->type  != V4L2_TUNER_RADIO)
+               return -EINVAL;
+
+       if (seek->rangelow >= seek->rangehigh)
+               ret = -EINVAL;
+
+       seek_low = radio->low->fm_state.freq;
+       if (seek->seek_upward)
+               seek_hi = region_configs[radio_region].top_freq;
+       else
+               seek_hi = region_configs[radio_region].bot_freq;
+
+       seek_spacing = seek->spacing / 1000;
+       FDEBUG(radio, "%s(): get freq low: %d, freq hi: %d\n",
+               __func__, seek_low, seek_hi);
+       FDEBUG(radio,
+                       "%s(): upward:%d, warp: %d, spacing: %d\n",  __func__,
+               seek->seek_upward, seek->wrap_around, seek->spacing);
+
+       if (!wake_lock_active(&radio->wakelock))
+               wake_lock(&radio->wakelock);
+
+       ret = fm_rx_seek(radio, seek->seek_upward, seek->wrap_around,
+                       seek_spacing, seek_low, seek_hi);
+       if (ret < 0)
+               dev_err(radio->v4l2dev.dev, "RX seek failed - %d\n", ret);
+
+       if (wake_lock_active(&radio->wakelock))
+               wake_unlock(&radio->wakelock);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+/* Configures mute mode (Mute Off/On/Attenuate) */
+int fm_set_mute_mode(struct s610_radio *radio, u8 mute_mode_toset)
+{
+       u16 payload = radio->mute_mode;
+       int ret = 0;
+
+       FUNC_ENTRY(radio);
+
+       radio->mute_mode = mute_mode_toset;
+
+       switch (radio->mute_mode) {
+       case FM_MUTE_ON:
+               payload = FM_RX_AC_MUTE_MODE;
+               break;
+
+       case FM_MUTE_OFF:
+               payload = FM_RX_UNMUTE_MODE;
+               break;
+       }
+
+       /* Write register : mute */
+       ret = low_set_mute_state(radio, payload);
+       if (ret != 0)
+               ret = -EIO;
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+/* Enable/Disable RDS */
+int fm_set_rds_mode(struct s610_radio *radio, u8 rds_en_dis)
+{
+       int ret;
+       u16 payload;
+       int ii;
+       u32 fifo_tmp;
+
+       FUNC_ENTRY(radio);
+
+       if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {
+               dev_err(radio->v4l2dev.dev, "Invalid rds option\n");
+               return -EINVAL;
+       }
+
+       if (rds_en_dis == FM_RDS_ENABLE) {
+               if (!wake_lock_active(&radio->rdswakelock))
+                       wake_lock(&radio->rdswakelock);
+               mdelay(100);
+               atomic_set(&radio->is_rds_new, 0);
+               radio->rds_cnt_mod = 0;
+               radio->rds_n_count = 0;
+               radio->rds_r_count = 0;
+               radio->rds_read_cnt = 0;
+               radio->rds_sync_loss_cnt = 0;
+
+               init_waitqueue_head(&radio->core->rds_read_queue);
+
+               payload = radio->core->power_mode;
+               payload |= S610_POWER_ON_RDS;
+               ret = s610_core_set_power_state(radio,
+                               payload);
+               if (ret != 0) {
+                       dev_err(radio->v4l2dev.dev,
+                                       "Failed to set for RDS power state\n");
+                       return -EIO;
+               }
+
+               /* Write register : RDS on */
+               /* Turn on RDS and RDS circuit */
+               fm_rds_on(radio);
+
+               /* Write register : clear RDS cache */
+               /* flush RDS buffer */
+               payload = FM_RX_RDS_FLUSH_FIFO;
+               ret = low_set_rds_cntr(radio, payload);
+               if (ret != 0) {
+                       dev_err(radio->v4l2dev.dev,
+                                       "Failed to write reg for default RDS_CNTR\n");
+                       return -EIO;
+               }
+
+               /* Write register : clear panding interrupt flags */
+               /* Read flags - just to clear any pending interrupts. */
+               payload = fm_get_flags(radio);
+
+               /* Write register : set RDS memory depth */
+               /* Set RDS FIFO threshold value */
+               if (radio->rds_parser_enable)
+                       radio->low->fm_state.rds_mem_thresh = RDS_MEM_MAX_THRESH_PARSER;
+               else
+               radio->low->fm_state.rds_mem_thresh = RDS_MEM_MAX_THRESH;
+
+               /* Write register : set RDS interrupt enable */
+               /* Enable RDS interrupt */
+               radio->irq_mask |= FM_EVENT_BUF_FUL;
+
+               /* Update our local flag */
+               radio->rds_flag = FM_RDS_ENABLE;
+
+               /* RDS parser reset */
+               if (radio->rds_parser_enable)
+                       fm_rds_parser_reset(&(radio->pi));
+
+               /* FIFO clear */
+               for (ii = 0; ii < 32; ii++)
+                       fifo_tmp = fmspeedy_get_reg_work(0xFFF3C0);
+
+#ifdef RDS_POLLING_ENABLE
+               fm_rds_periodic_update((unsigned long) radio);
+#endif /*RDS_POLLING_ENABLE*/
+       } else if (
+               rds_en_dis == FM_RDS_DISABLE) {
+               payload = radio->core->power_mode;
+               payload &= ~S610_POWER_ON_RDS;
+               ret = s610_core_set_power_state(radio,
+                               payload);
+               if (ret != 0) {
+                       dev_err(radio->v4l2dev.dev,
+                                       "Failed to set for RDS power state\n");
+                       return -EIO;
+               }
+
+               /* Write register : RDS off */
+               /* Turn off RX RDS */
+               fm_rds_off(radio);
+
+               /* Update RDS local cache */
+               radio->irq_mask &= ~(FM_EVENT_BUF_FUL);
+               radio->rds_flag = FM_RDS_DISABLE;
+#ifdef RDS_POLLING_ENABLE
+               fm_rds_periodic_cancel((unsigned long) radio);
+#endif /*RDS_POLLING_ENABLE*/
+
+               /* Service pending read */
+               wake_up_interruptible(&radio->core->rds_read_queue);
+
+               if (wake_lock_active(&radio->rdswakelock))
+                       wake_unlock(&radio->rdswakelock);
+       }
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+int fm_set_deemphasis(struct s610_radio *radio, u16 vol_to_set)
+{
+       int ret = 0;
+       u16 payload;
+
+       payload = vol_to_set;
+       /* Write register : deemphasis */
+       ret = low_set_demph_mode(radio, payload);
+       if (ret != 0)
+               return -EIO;
+
+       radio->deemphasis_mode = vol_to_set;
+
+       return ret;
+}
+
+static int s610_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct s610_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler);
+       u16 payload;
+       int ret = 0;
+
+       FUNC_ENTRY(radio);
+       if (!radio)
+               return -ENODEV;
+
+       s610_core_lock(radio->core);
+
+       switch (ctrl->id) {
+       case V4L2_CID_S610_CH_SPACING:
+               payload = radio->low->fm_tuner_state.freq_step / 100;
+               FDEBUG(radio, "%s(), FREQ_STEP val:%d, ret : %d\n",
+                               __func__, payload,  ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_CH_BAND:
+               payload = radio->low->fm_state.band;
+               FDEBUG(radio, "%s(), BAND val:%d, ret : %d\n",
+                               __func__, payload, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_SOFT_STEREO_BLEND:
+               payload = radio->low->fm_state.use_switched_blend ?
+                       MODE_MASK_BLEND : 0;
+               FDEBUG(radio, "%s(), MOST_BLEND val:%d, ret : %d\n",
+                               __func__, ctrl->val, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_SOFT_STEREO_BLEND_COEFF:
+               payload = radio->low->fm_config.blend_coeffs_soft;
+               FDEBUG(radio, "%s(),BLEND_COEFF_SOFT val:%d, ret: %d\n",
+                               __func__, ctrl->val, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_SOFT_MUTE_COEFF:
+               payload = radio->low->fm_config.mute_coeffs_soft;
+               FDEBUG(radio, "%s(), MUTE_COEFF_SOFT val:%d, ret: %d\n",
+                               __func__, ctrl->val, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_RSSI_CURR:
+               fm_update_rssi(radio);
+               ctrl->val  = radio->low->fm_state.rssi;
+               FDEBUG(radio, "%s(), RSSI_CURR val:%d, ret : %d\n",
+                               __func__, ctrl->val, ret);
+               break;
+       case V4L2_CID_S610_SNR_CURR:
+               radio->low->fm_state.snr = fmspeedy_get_reg(0xFFF2C5);
+               payload = radio->low->fm_state.snr;
+               FDEBUG(radio, "%s(), SNR_CURR val:%d, ret : %d\n",
+                               __func__, payload, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_SEEK_CANCEL:
+               ctrl->val = 0;
+               break;
+       case V4L2_CID_S610_SEEK_MODE:
+               if (radio->seek_mode == FM_TUNER_AUTONOMOUS_SEARCH_MODE_NEXT)
+                       ctrl->val = 4;
+               else
+                       ctrl->val = radio->seek_mode;
+
+               FDEBUG(radio, "%s(), SEEK_MODE val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_RDS_ON:
+               if (radio->low->fm_state.fm_pwr_on & S610_POWER_ON_RDS)
+                       ctrl->val = FM_RDS_ENABLE;
+               else
+                       ctrl->val = FM_RDS_DISABLE;
+               FDEBUG(radio, "%s(), RDS_ON:%d, ret: %d\n", __func__,
+                               ctrl->val, ret);
+               break;
+       case V4L2_CID_S610_IF_COUNT1:
+               payload = radio->low->fm_config.search_conf.weak_ifca_m;
+               FDEBUG(radio, "%s(), IF_CNT1 val:%d, ret : %d\n",
+                       __func__, ctrl->val, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_IF_COUNT2:
+               payload = radio->low->fm_config.search_conf.normal_ifca_m;
+               FDEBUG(radio, "%s(), IF_CNT2 val:%d, ret : %d\n",
+                       __func__, ctrl->val, ret);
+               ctrl->val = payload;
+               break;
+       case V4L2_CID_S610_RSSI_TH:
+               ctrl->val  = radio->low->fm_state.rssi;
+               FDEBUG(radio, "%s(), RSSI_CURR val:%d, ret : %d\n",
+                               __func__, ctrl->val, ret);
+               break;
+       case V4L2_CID_S610_KERNEL_VER:
+               ctrl->val |= FM_RADIO_RDS_PARSER_VER_CHECK;
+               FDEBUG(radio, "%s(), KERNEL_VER val:%d, ret : %d\n",
+                               __func__, ctrl->val, ret);
+               break;
+       case V4L2_CID_S610_SOFT_STEREO_BLEND_REF:
+               ctrl->val = radio->rssi_ref_enable;
+               FDEBUG(radio, "%s(), SOFT_STEREO_BLEND_REF val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+
+       default:
+               ret = -EINVAL;
+               dev_err(radio->v4l2dev.dev,
+                               "g_volatile_ctrl Unknown IOCTL: %d\n",
+                               (int) ctrl->id);
+               break;
+       }
+
+       s610_core_unlock(radio->core);
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static int s610_radio_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       int ret = 0;
+       u16 payload = 0;
+       struct s610_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler);
+
+       FUNC_ENTRY(radio);
+
+       s610_core_lock(radio->core);
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:  /* set mute */
+               ret = fm_set_mute_mode(radio, (u8)ctrl->val);
+               FDEBUG(radio, "%s(), mute val:%d, ret : %d\n", __func__,
+                               ctrl->val, ret);
+               if (ret != 0)
+                       ret = -EIO;
+               break;
+       case V4L2_CID_AUDIO_VOLUME:  /* set volume gain */
+               fm_set_audio_gain(radio, (u8)ctrl->val);
+               FDEBUG(radio, "%s(), volume val:%d, ret : %d\n", __func__,
+                               ctrl->val, ret);
+               break;
+       case V4L2_CID_TUNE_DEEMPHASIS:
+               if (ctrl->val == 0)
+                       break;
+               payload = (u16)ctrl->val;
+               if (ctrl->val > 0)
+                       payload -= 1;
+               ret = fm_set_deemphasis(radio, payload);
+               FDEBUG(radio, "%s(), deemphasis val:%d, ret : %d, payload:%d\n", __func__,
+                               ctrl->val, ret, payload);
+               if (ret != 0)
+                       ret = -EINVAL;
+               break;
+       case V4L2_CID_S610_CH_SPACING:
+               radio->freq_step = ctrl->val;
+               radio->low->fm_tuner_state.freq_step =
+               radio->low->fm_freq_steps[ctrl->val];
+               FDEBUG(radio, "%s(), FREQ_STEP val:%d, ret : %d\n",
+                       __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_CH_BAND:
+               radio_region = ctrl->val;
+               radio->radio_region = ctrl->val;
+               payload = ctrl->val;
+               fm_set_band(radio, payload);
+               FDEBUG(radio, "%s(), BAND val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_SOFT_STEREO_BLEND:
+               ret = low_set_most_blend(radio, ctrl->val);
+               FDEBUG(radio, "%s(), MOST_BLEND val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               if (ret != 0)
+                       ret = -EIO;
+               break;
+       case V4L2_CID_S610_SOFT_STEREO_BLEND_COEFF:
+               radio->low->fm_config.blend_coeffs_soft = (u16)ctrl->val;
+               fm_set_blend_mute(radio);
+               FDEBUG(radio, "%s(), BLEND_COEFF_SOFT val:%d,ret: %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_SOFT_MUTE_COEFF:
+               radio->low->fm_config.mute_coeffs_soft = (u16)ctrl->val;
+               fm_set_blend_mute(radio);
+               FDEBUG(radio, "%s(), SOFT_MUTE_COEFF val:%d, ret: %d\n",
+                               __func__, ctrl->val, ret);
+               break;
+       case V4L2_CID_S610_RSSI_CURR:
+               ctrl->val = 0;
+               break;
+       case V4L2_CID_S610_SEEK_CANCEL:
+               if (ctrl->val) {
+                       payload = FM_TUNER_STOP_SEARCH_MODE;
+                       low_set_tuner_mode(radio, payload);
+                       FDEBUG(radio, "%s(), SEEK_CANCEL val:%d, ret: %d\n",
+                                       __func__, ctrl->val,  ret);
+                       radio->seek_mode = FM_TUNER_STOP_SEARCH_MODE;
+               }
+               break;
+       case V4L2_CID_S610_SEEK_MODE:
+               if (ctrl->val == 4)
+                       radio->seek_mode = FM_TUNER_AUTONOMOUS_SEARCH_MODE_NEXT;
+               else
+                       radio->seek_mode = ctrl->val;
+
+               FDEBUG(radio, "%s(), SEEK_MODE val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_RDS_ON:
+               payload = (u16)ctrl->val;
+               if (payload & RDS_PARSER_ENABLE) {
+                       radio->rds_parser_enable = TRUE;
+               } else {
+                       radio->rds_parser_enable = FALSE;
+               }
+               payload &= FM_RDS_ENABLE;
+               ret = fm_set_rds_mode(radio, payload);
+               FDEBUG(radio, "%s(), RDS_RECEPTION:%d, ret:%d parser:%d\n", __func__,
+                               payload, ret, radio->rds_parser_enable);
+               if (ret != 0)
+                       ret = -EINVAL;
+               break;
+       case V4L2_CID_S610_SNR_CURR:
+               ctrl->val = 0;
+               break;
+       case V4L2_CID_S610_IF_COUNT1:
+               radio->low->fm_config.search_conf.weak_ifca_m =
+                               (u16)ctrl->val;
+               FDEBUG(radio, "%s(), IF_CNT1  val:%d, ret : %d\n",
+                       __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_IF_COUNT2:
+               radio->low->fm_config.search_conf.normal_ifca_m =
+                               (u16)ctrl->val;
+               FDEBUG(radio, "%s(), IF_CNT2  val:%d, ret : %d\n",
+                       __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_RSSI_TH:
+               low_set_search_lvl(radio, (u16)ctrl->val);
+               FDEBUG(radio, "%s(), RSSI_TH val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+       case V4L2_CID_S610_KERNEL_VER:
+               ctrl->val = 0;
+               break;
+       case V4L2_CID_S610_SOFT_STEREO_BLEND_REF:
+               payload = (u16)ctrl->val;
+               if (payload & RSSI_REF_ENABLE)
+                       radio->rssi_ref_enable = TRUE;
+               else
+                       radio->rssi_ref_enable = FALSE;
+
+               FDEBUG(radio, "%s(), SOFT_STEREO_BLEND_REF val:%d, ret : %d\n",
+                               __func__, ctrl->val,  ret);
+               break;
+
+       default:
+               ret = -EINVAL;
+               dev_err(radio->v4l2dev.dev, "s_ctrl Unknown IOCTL: 0x%x\n",
+                               (int)ctrl->id);
+               break;
+       }
+       s610_core_unlock(radio->core);
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int s610_radio_g_register(struct file *file, void *fh,
+               struct v4l2_dbg_register *reg)
+{
+       struct s610_radio *radio = video_drvdata(file);
+
+       FUNC_ENTRY(radio);
+
+       s610_core_lock(radio->core);
+
+       reg->size = 4;
+       reg->val = (__u64)fmspeedy_get_reg((u32)reg->reg);
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return 0;
+}
+static int s610_radio_s_register(struct file *file, void *fh,
+               const struct v4l2_dbg_register *reg)
+{
+       struct s610_radio *radio = video_drvdata(file);
+
+       FUNC_ENTRY(radio);
+
+       s610_core_lock(radio->core);
+       fmspeedy_set_reg((u32)reg->reg, (u32)reg->val);
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return 0;
+}
+#endif
+
+int s610_core_set_power_state(struct s610_radio *radio,
+               u8 next_state)
+{
+       int ret = 0;
+
+       FUNC_ENTRY(radio);
+
+       ret = low_set_power(radio, next_state);
+       if (ret != 0) {
+               dev_err(radio->v4l2dev.dev,
+                       "Failed to write reg for power on\n");
+               ret = -EIO;
+               goto ret_power;
+       }
+       radio->core->power_mode |= next_state;
+
+ret_power:
+       FUNC_EXIT(radio);
+       return ret;
+}
+
+void  fm_prepare(struct s610_radio *radio)
+{
+       FUNC_ENTRY(radio);
+
+       spin_lock_init(&radio->rds_buff_lock);
+
+       /* Initial FM device variables  */
+       /* Region info */
+       radio->region = region_configs[radio_region];
+       radio->mute_mode = FM_MUTE_OFF;
+       radio->rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF;
+       radio->rds_flag = FM_RDS_DISABLE;
+       radio->freq = FM_UNDEFINED_FREQ;
+       radio->rds_mode = FM_RDS_SYSTEM_RDS;
+       radio->af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF;
+       radio->core->power_mode = S610_POWER_DOWN;
+       radio->seek_weak_rssi = SCAN_WEAK_SIG_THRESHOLD;
+       /* Reset RDS buffer cache if need */
+
+       /* Initial wait queue for rds read */
+       init_waitqueue_head(&radio->core->rds_read_queue);
+       radio->idle_cnt_mod = 0;
+       radio->rds_new_stat = 0;
+
+       FUNC_EXIT(radio);
+
+}
+
+#ifdef USE_FM_LNA_ENABLE
+static int set_eLNA_gpio(struct s610_radio *radio, int stat)
+{
+       if (!gpio_is_valid(radio->elna_gpio)) {
+               return -EINVAL;
+       } else {
+               dev_info(radio->v4l2dev.dev, "%s(%d)\n", __func__, stat);
+               gpio_set_value(radio->elna_gpio, stat);
+       }
+
+       return 0;
+}
+#endif /* USE_FM_LNA_ENABLE */
+
+static int s610_radio_fops_open(struct file *file)
+{
+       int ret;
+       struct s610_radio *radio = video_drvdata(file);
+       static bool run_once = true;
+
+       FUNC_ENTRY(radio);
+
+#ifdef CONFIG_SCSC_FM
+       /* Start FM/WLBT LDO */
+       ret = mx250_fm_request();
+       if (ret < 0) {
+               dev_err(radio->v4l2dev.dev,
+                       "mx250_fm_request() failed with err %d\n", ret);
+               return ret;
+       }
+#endif /* CONFIG_SCSC_FM */
+
+       shared_fm_open_cnt++;
+
+#ifdef USE_FM_LNA_ENABLE
+       if (radio->elna_gpio != -EINVAL) {
+               ret = set_eLNA_gpio(radio, GPIO_HIGH);
+               if (ret)
+                       dev_info(radio->v4l2dev.dev,
+                                       "Failed to set gpio for eLNA\n");
+       }
+#endif /* USE_FM_LNA_ENABLE */
+
+       ret = v4l2_fh_open(file);
+       if (ret)
+               return ret;
+
+       if (v4l2_fh_is_singular_file(file)) {
+               s610_core_lock(radio->core);
+               atomic_set(&radio->is_doing, 0);
+               atomic_set(&radio->is_rds_doing, 0);
+
+#ifdef USE_AUDIO_PM
+               if (radio->a_dev) {
+                       ret = pm_runtime_get_sync(radio->a_dev);
+                       if (ret < 0) {
+                               dev_err(radio->v4l2dev.dev,
+                                       "audio get_sync not work: not suspend %d\n", ret);
+                               goto err_open;
+                       }
+
+//                     abox_request_cpu_gear_sync(radio->a_dev,
+//                             dev_get_drvdata(radio->a_dev), radio->dev, ABOX_CPU_GEAR_MAX);
+               }
+#endif /* USE_AUDIO_PM */
+
+               ret = pm_runtime_get_sync(radio->dev);
+               if (ret < 0) {
+                       dev_err(radio->v4l2dev.dev,
+                               "get_sync failed with err %d\n", ret);
+                       goto err_open;
+               }
+
+               fmspeedy_wakeup();
+
+               if (run_once) {
+                       fm_ds_set(0);
+                       fm_aux_pll_off();
+
+                       run_once = false;
+               }
+
+               fm_get_version_number();
+
+               /* Initail fm low structure */
+               ret = init_low_struc(radio);
+               if (ret < 0) {
+                       dev_err(radio->v4l2dev.dev,
+                               "Failed to init reg for initial struc\n");
+                       goto err_open;
+               }
+
+               /* Booting fm */
+               ret = fm_boot(radio);
+               if (ret < 0) {
+                       dev_err(radio->v4l2dev.dev,
+                               "Failed to set reg for FM boot\n");
+                       goto err_open;
+               }
+
+               fm_prepare(radio);
+
+               ret = s610_core_set_power_state(radio,
+                               S610_POWER_ON_FM);
+               if (ret < 0) {
+                       dev_err(radio->v4l2dev.dev,
+                               "Failed to write reg for power on\n");
+                       goto err_open;
+               }
+
+               ret = s610_radio_pretune(radio);
+               if (ret < 0) {
+                       dev_err(radio->v4l2dev.dev,
+                               "Failed to write reg for preturn\n");
+                       goto power_down;
+               }
+
+               radio->core->power_mode = S610_POWER_ON_FM;
+
+               mdelay(100);
+
+#ifndef RDS_POLLING_ENABLE
+               /* Speedy master interrupt enable */
+               fm_speedy_m_int_enable();
+#else
+               fm_speedy_m_int_disable();
+#endif /*RDS_POLLING_ENABLE*/
+               s610_core_unlock(radio->core);
+               /*Must be done after s610_core_unlock to prevent a deadlock*/
+               v4l2_ctrl_handler_setup(&radio->ctrl_handler);
+       }
+
+       fm_set_audio_gain(radio, 7);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+
+power_down:
+       s610_core_set_power_state(radio,
+                       S610_POWER_DOWN);
+
+err_open:
+       s610_core_unlock(radio->core);
+       v4l2_fh_release(file);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+
+}
+
+static int s610_radio_fops_release(struct file *file)
+{
+       int ret = 0;
+       struct s610_radio *radio = video_drvdata(file);
+
+       FUNC_ENTRY(radio);
+
+       if (v4l2_fh_is_singular_file(file)) {
+               s610_core_lock(radio->core);
+               s610_core_set_power_state(radio, S610_POWER_DOWN);
+
+               /* Speedy master interrupt disable */
+               fm_speedy_m_int_disable();
+
+               /* FM demod power off */
+               fm_power_off();
+
+               cancel_delayed_work_sync(&radio->dwork_sig2);
+               cancel_delayed_work_sync(&radio->dwork_tune);
+
+#ifdef ENABLE_RDS_WORK_QUEUE
+               cancel_work_sync(&radio->work);
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+#ifdef IDLE_POLLING_ENABLE
+               fm_idle_periodic_cancel((unsigned long) radio);
+#endif /*IDLE_POLLING_ENABLE*/
+#ifdef RDS_POLLING_ENABLE
+               fm_rds_periodic_cancel((unsigned long) radio);
+#endif /*RDS_POLLING_ENABLE*/
+/*#ifdef       ENABLE_IF_WORK_QUEUE
+               cancel_work_sync(&radio->if_work);
+#endif*/       /*ENABLE_IF_WORK_QUEUE*/
+
+               pm_runtime_put_sync(radio->dev);
+#ifdef USE_AUDIO_PM
+               if (radio->a_dev) {
+//                     abox_request_cpu_gear_sync(radio->a_dev,
+//                             dev_get_drvdata(radio->a_dev), radio->dev, ABOX_CPU_GEAR_MIN);
+
+                       pm_runtime_put_sync(radio->a_dev);
+               }
+#endif /* USE_AUDIO_PM */
+               s610_core_unlock(radio->core);
+       }
+
+       if (wake_lock_active(&radio->wakelock))
+               wake_unlock(&radio->wakelock);
+
+       if (wake_lock_active(&radio->rdswakelock))
+               wake_unlock(&radio->rdswakelock);
+
+       ret = v4l2_fh_release(file);
+
+#ifdef USE_FM_LNA_ENABLE
+       if (radio->elna_gpio != -EINVAL) {
+               ret = set_eLNA_gpio(radio, GPIO_LOW);
+               if (ret)
+                       dev_info(radio->v4l2dev.dev,
+                                       "Failed to set gpio for eLNA\n");
+       }
+#endif /* USE_FM_LNA_ENABLE */
+
+#ifdef CONFIG_SCSC_FM
+               /* Stop FM/WLBT LDO */
+               ret = mx250_fm_release();
+               if (ret < 0)
+                       dev_err(radio->v4l2dev.dev,
+                               "mx250_fm_release() failed with err %d\n", ret);
+#endif
+
+       shared_fm_open_cnt--;
+
+       return ret;
+}
+
+static ssize_t s610_radio_fops_read(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       int ret;
+       size_t rsize, blocks;
+       struct s610_radio *radio = video_drvdata(file);
+       size_t rds_max_thresh;
+
+       FUNC_ENTRY(radio);
+
+       ret = wait_event_interruptible(radio->core->rds_read_queue,
+                       atomic_read(&radio->is_rds_new));
+       if (ret < 0)
+               return -EINTR;
+
+       if (!atomic_read(&radio->is_rds_new)) {
+               radio->rds_new_stat++;
+               return -ERESTARTSYS;
+       }
+
+       if (s610_core_lock_interruptible(radio->core))
+               return -ERESTARTSYS;
+
+       if (radio->rds_parser_enable)
+#ifdef USE_RINGBUFF_API
+               rds_max_thresh = ringbuf_bytes_used(&radio->rds_rb);
+#else
+               rds_max_thresh = RDS_MEM_MAX_THRESH_PARSER;
+#endif /* USE_RINGBUFF_API */
+       else
+       rds_max_thresh = RDS_MEM_MAX_THRESH;
+
+       /* Turn on RDS mode */
+       memset(radio->rds_buf, 0, 480);
+
+       rsize = rds_max_thresh;
+       rsize = min(rsize, count);
+
+       blocks = rsize/FM_RDS_BLK_SIZE;
+
+       ret = fm_read_rds_data(radio, radio->rds_buf,
+               (int)rsize, (u16 *)&blocks);
+       if (ret == 0) {
+               dev_err(radio->v4l2dev.dev, ">>> Failed to read rds mode\n");
+               goto read_unlock;
+       }
+
+       /* always transfer rds complete blocks */
+       if (copy_to_user(buf, &radio->rds_buf, rsize))
+               ret = -EFAULT;
+
+       if (radio->rds_parser_enable) {
+               RDSEBUG(radio, "RDS RD done:%08d:%08d fifo err:%08d type(%02X) len(%02X)",
+                       radio->rds_read_cnt, radio->rds_n_count, radio->rds_fifo_err_cnt,
+                       radio->rds_buf[0], radio->rds_buf[1]);
+       } else {
+       if ((radio->rds_buf[2]&0x07) != RDS_BLKTYPE_A) {
+               radio->rds_reset_cnt++;
+               if (radio->rds_reset_cnt > radio->low->fm_state.rds_unsync_blk_cnt) {
+                       fm_set_rds_mode(radio, FM_RDS_DISABLE);
+                       mdelay(10);
+                       fm_set_rds_mode(radio, FM_RDS_ENABLE);
+                       radio->rds_reset_cnt = 0;
+                       RDSEBUG(radio, "RDS reset! cause of block type invalid");
+               }
+               RDSEBUG(radio, "RDS block type invalid! %02d, %08d",
+                       (radio->rds_buf[2]&0x07), radio->rds_reset_cnt);
+       }
+
+       RDSEBUG(radio, "RDS RD done:%08d:%08d fifo err:%08d block type0:%02X,%02X",
+               radio->rds_read_cnt, radio->rds_n_count, radio->rds_fifo_err_cnt,
+               (radio->rds_buf[2]&0x11)>>3, radio->rds_buf[2]&0x07);
+       }
+
+       radio->rds_read_cnt++;
+       ret = rsize;
+read_unlock:
+       atomic_set(&radio->is_rds_new, 0);
+       atomic_set(&gradio->is_doing, 0);
+       atomic_set(&gradio->is_rds_doing, 0);
+
+       s610_core_unlock(radio->core);
+
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static unsigned int s610_radio_fops_poll(struct file *file,
+               struct poll_table_struct *pts)
+{
+       struct s610_radio *radio = video_drvdata(file);
+       unsigned long req_events = poll_requested_events(pts);
+       unsigned int ret = v4l2_ctrl_poll(file, pts);
+
+       FUNC_ENTRY(radio);
+
+       if (req_events & (POLLIN | POLLRDNORM)) {
+               poll_wait(file, &radio->core->rds_read_queue, pts);
+
+               if (atomic_read(&radio->is_rds_new))
+                       ret = POLLIN | POLLRDNORM;
+       }
+
+       FDEBUG(radio, "POLL RET: 0x%x pwmode: %d freq: %d",
+               ret,
+               radio->core->power_mode,
+               radio->freq);
+       FUNC_EXIT(radio);
+
+       return ret;
+}
+
+static const struct v4l2_file_operations s610_fops = {
+               .owner                  = THIS_MODULE,
+               .read                   = s610_radio_fops_read,
+               .poll                   = s610_radio_fops_poll,
+               .unlocked_ioctl         = video_ioctl2,
+               .open                   = s610_radio_fops_open,
+               .release                = s610_radio_fops_release,
+};
+
+static const struct v4l2_ioctl_ops S610_ioctl_ops = {
+               .vidioc_querycap                = s610_radio_querycap,
+               .vidioc_g_tuner                 = s610_radio_g_tuner,
+               .vidioc_s_tuner                 = s610_radio_s_tuner,
+
+               .vidioc_g_frequency             = s610_radio_g_frequency,
+               .vidioc_s_frequency             = s610_radio_s_frequency,
+               .vidioc_s_hw_freq_seek          = s610_radio_s_hw_freq_seek,
+               .vidioc_enum_freq_bands         = s610_radio_enum_freq_bands,
+
+               .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
+               .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+               .vidioc_g_register              = s610_radio_g_register,
+               .vidioc_s_register              = s610_radio_s_register,
+#endif
+};
+
+static const struct video_device s610_viddev_template = {
+               .fops                   = &s610_fops,
+               .name                   = DRIVER_NAME,
+               .release                = video_device_release_empty,
+};
+
+static int s610_radio_add_new_custom(struct s610_radio *radio,
+               enum s610_ctrl_idx idx, unsigned long flag)
+{
+       int ret;
+       struct v4l2_ctrl *ctrl;
+
+       ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler,
+                       &s610_ctrls[idx],
+                       NULL);
+
+       ret = radio->ctrl_handler.error;
+       if (ctrl == NULL && ret) {
+               dev_err(radio->v4l2dev.dev,
+                               "Could not initialize '%s' control %d\n",
+                               s610_ctrls[idx].name, ret);
+               return -EINTR;
+       }
+       if (flag && (ctrl != NULL))
+               ctrl->flags |= flag;
+
+       return ret;
+}
+
+#ifndef RDS_POLLING_ENABLE
+static irqreturn_t s610_hw_irq_handle(int irq, void *devid)
+{
+       struct s610_radio *radio = devid;
+       u32 int_stat;
+
+       spin_lock(&radio->slock);
+
+       int_stat = __raw_readl(radio->fmspeedy_base+FMSPDY_STAT);
+
+       if (int_stat & FM_SLV_INT)
+               fm_isr(radio);
+
+       spin_unlock(&radio->slock);
+
+       return IRQ_HANDLED;
+}
+#endif /* RDS_POLLING_ENABLE */
+
+MODULE_ALIAS("platform:s610-radio");
+static const struct of_device_id exynos_fm_of_match[] = {
+               {
+                               .compatible = "samsung,exynos9610-fm",
+               },
+               {},
+};
+MODULE_DEVICE_TABLE(of, exynos_fm_of_match);
+
+signed int exynos_get_fm_open_status(void)
+{
+       pr_info("%s(): shared fm open count: %d", __func__, shared_fm_open_cnt);
+       return (shared_fm_open_cnt);
+}
+
+EXPORT_SYMBOL(exynos_get_fm_open_status);
+
+/* bit 28:
+  Global hardware automatic clock gating enable on CMU module : CMU
+*/
+#define ENABLE_AUTOMATIC_CLKGATING     28
+void disable_clk_gating(struct s610_radio *radio)
+{
+       void __iomem *cmu_disaud_800;
+       u32 cmu_disaud_800_val;
+
+       FUNC_ENTRY(radio);
+
+       cmu_disaud_800 = radio->disaud_cmu_base+0x800;
+       
+       /* Read register */
+       cmu_disaud_800_val = read32(cmu_disaud_800);
+       FDEBUG(radio,
+               "cmu_disaud_800: 0x%08X,\n",
+               cmu_disaud_800_val);
+       
+       if (GetBits(cmu_disaud_800, ENABLE_AUTOMATIC_CLKGATING, 1))
+               SetBits(cmu_disaud_800, ENABLE_AUTOMATIC_CLKGATING, 1, 0);
+
+       /* Read register */
+       cmu_disaud_800_val = read32(cmu_disaud_800);
+       FDEBUG(radio,
+               "cmu_disaud_800: 0x%08X,\n",
+               cmu_disaud_800_val);
+
+       FUNC_EXIT(radio);
+}
+
+void enable_FM_mux_clk_aud(struct s610_radio *radio)
+{
+       void __iomem *cmu_disaud_100c;
+       u32 cmu_disaud_100c_val;
+
+       FUNC_ENTRY(radio);
+
+       cmu_disaud_100c = radio->disaud_cmu_base+0x100C;
+
+       /* Read register */
+       cmu_disaud_100c_val = read32(cmu_disaud_100c);
+       FDEBUG(radio,
+               "befor: cmu_disaud_100c: 0x%08X,\n",
+               cmu_disaud_100c_val);
+
+       if (GetBits(cmu_disaud_100c, ENABLE_AUTOMATIC_CLKGATING, 1))
+               SetBits(cmu_disaud_100c, ENABLE_AUTOMATIC_CLKGATING, 1, 0);
+
+       /* Read register */
+       cmu_disaud_100c_val = read32(cmu_disaud_100c);
+       FDEBUG(radio,
+               "after: cmu_disaud_100c: 0x%08X,\n",
+               cmu_disaud_100c_val);
+
+       FUNC_EXIT(radio);
+}
+
+#ifdef USE_AUDIO_PM
+static struct device_node *exynos_audio_parse_dt(struct s610_radio *radio)
+{
+       struct platform_device *pdev = NULL;
+       struct device_node *np = NULL;
+
+       np = of_find_compatible_node(NULL, NULL, "samsung,abox");
+       if (!np) {
+               dev_err(radio->dev, "abox device is not available\n");
+               return NULL;
+       }
+
+       pdev = of_find_device_by_node(np);
+       if (!pdev) {
+               dev_err(radio->dev, "%s: failed to get audio platform_device\n", __func__);
+               return NULL;
+       }
+       radio->a_dev = &pdev->dev;
+
+       return np;
+}
+#endif /* USE_AUDIO_PM */
+
+static int s610_radio_probe(struct platform_device *pdev)
+{
+       unsigned long ret;
+       struct s610_radio *radio;
+       struct v4l2_ctrl *ctrl;
+       const struct of_device_id *match;
+       struct resource *resource;
+       struct device *dev = &pdev->dev;
+       static atomic_t instance = ATOMIC_INIT(0);
+       struct clk *clk;
+       struct device_node *dnode;
+#ifdef USE_FM_LNA_ENABLE
+       int elna_gpio = 0;
+#endif /*USE_FM_LNA_ENABLE*/
+
+       dnode = dev->of_node;
+
+       dev_info(&pdev->dev, ">>> start FM Radio probe\n");
+
+       radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL);
+       if (!radio)
+               return -ENOMEM;
+
+       radio->core = devm_kzalloc(&pdev->dev, sizeof(struct s610_core),
+                       GFP_KERNEL);
+       if (!radio->core) {
+               ret =  -ENOMEM;
+               goto alloc_err0;
+       }
+
+       radio->low = devm_kzalloc(&pdev->dev, sizeof(struct s610_low),
+                       GFP_KERNEL);
+       if (!radio->low) {
+               ret =  -ENOMEM;
+               goto alloc_err1;
+       }
+       
+       clk = devm_clk_get(&pdev->dev, "qch_fm");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               ret = PTR_ERR(clk);
+               goto alloc_err2;
+       }
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare enable clock\n");
+               goto alloc_err2;
+       }
+
+       ret = clk_enable(clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable clock\n");
+               goto alloc_err2;
+       }
+       radio->clk = clk;
+
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       pm_runtime_get_sync(dev);
+
+       spin_lock_init(&radio->slock);
+       spin_lock_init(&radio->rds_lock);
+
+       /* Init flags FR BL init_completion */
+       init_completion(&radio->flags_set_fr_comp);
+       init_completion(&radio->flags_seek_fr_comp);
+
+       v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance);
+
+       ret = v4l2_device_register(&pdev->dev, &radio->v4l2dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register v4l2_device.\n");
+               goto alloc_err3;
+       }
+
+       match = of_match_node(exynos_fm_of_match, dev->of_node);
+       if (!match) {
+               dev_err(&pdev->dev, "failed to match node\n");
+               ret =  -EINVAL;
+               goto alloc_err4;
+       }
+
+       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       radio->fmspeedy_base = devm_ioremap_resource(&pdev->dev, resource);
+       if (IS_ERR(radio->fmspeedy_base)) {
+               dev_err(&pdev->dev,
+                       "Failed to request memory region\n");
+               ret = -EBUSY;
+               goto alloc_err4;
+       }
+
+       resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       radio->disaud_cmu_base = devm_ioremap_resource(&pdev->dev, resource);
+       if (IS_ERR(radio->disaud_cmu_base)) {
+               dev_err(&pdev->dev,
+                       "Failed to request memory region\n");
+               ret = -EBUSY;
+               goto alloc_err4;
+       }
+
+#ifndef RDS_POLLING_ENABLE
+       /*save to global variavle  fm speedy physical address */
+       resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!resource) {
+               dev_err(&pdev->dev, "failed to get IRQ resource\n");
+               ret = -ENOENT;
+               goto alloc_err4;
+       }
+
+       ret = devm_request_irq(&pdev->dev,
+                       resource->start,
+                       s610_hw_irq_handle,
+                       0,
+                       pdev->name,
+                       radio);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to install irq\n");
+               goto alloc_err4;
+       }
+       radio->irq = resource->start;
+#endif /* RDS_POLLING_ENABLE */
+
+       radio->dev = dev;
+       radio->pdev = pdev;
+       gradio = radio;
+
+#ifdef USE_FM_LNA_ENABLE
+       elna_gpio = of_get_named_gpio(dnode, "elna_gpio", 0);
+       if (!gpio_is_valid(elna_gpio)) {
+               dev_err(dev, "Disable elna_gpio control\n");
+               elna_gpio = -EINVAL;
+       } else {
+               ret = gpio_request_one(elna_gpio, GPIOF_OUT_INIT_LOW, "LNA_GPIO_EN");
+               if (ret) {
+                       dev_err(dev, "Disable elna_gpio control\n");
+                       elna_gpio = -EINVAL;
+               } else
+                       dev_info(dev, "Enable elna_gpio control\n");
+       }
+       radio->elna_gpio = elna_gpio;
+#endif /* USE_FM_LNA_ENABLE */
+
+#ifdef USE_AUDIO_PM
+       if (!exynos_audio_parse_dt(radio)) {
+               ret = -EINVAL;
+               goto alloc_err4;
+       }
+#endif /* USE_AUDIO_PM */
+
+       memcpy(&radio->videodev, &s610_viddev_template,
+                       sizeof(struct video_device));
+
+       radio->videodev.v4l2_dev  = &radio->v4l2dev;
+       radio->videodev.ioctl_ops = &S610_ioctl_ops;
+
+       video_set_drvdata(&radio->videodev, radio);
+       platform_set_drvdata(pdev, radio);
+
+       radio->v4l2dev.ctrl_handler = &radio->ctrl_handler;
+
+       v4l2_ctrl_handler_init(&radio->ctrl_handler,
+                       1 + ARRAY_SIZE(s610_ctrls));
+       /* Set control */
+       ret = s610_radio_add_new_custom(radio, S610_IDX_CH_SPACING,
+                       0); /*0x01*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_CH_BAND,
+                       0); /*0x02*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SOFT_STEREO_BLEND,
+                       0); /*0x03*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SOFT_STEREO_BLEND_COEFF,
+                       0); /*0x04*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SOFT_MUTE_COEFF,
+                       0); /*0x05*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_RSSI_CURR,
+                       V4L2_CTRL_FLAG_VOLATILE); /*0x06*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SNR_CURR,
+                       V4L2_CTRL_FLAG_VOLATILE); /*0x07*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SEEK_CANCEL,
+                       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE); /*0x08*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SEEK_MODE,
+                       0); /*0x09*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_RDS_ON,
+                       0); /*0x0A*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_IF_COUNT1,
+                       0); /*0x0B*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_IF_COUNT2,
+                       0); /*0x0C*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_RSSI_TH,
+                       0); /*0x0D*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_KERNEL_VER,
+                       V4L2_CTRL_FLAG_VOLATILE); /*0x0E*/
+       if (ret < 0)
+               goto exit;
+       ret = s610_radio_add_new_custom(radio, S610_IDX_SOFT_STEREO_BLEND_REF,
+                       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE|V4L2_CTRL_FLAG_VOLATILE); /*0x0F*/
+       if (ret < 0)
+               goto exit;
+
+       ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &s610_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 4, 1, 1);
+       ret = radio->ctrl_handler.error;
+       if (ctrl == NULL && ret) {
+               dev_err(&pdev->dev,
+                       "Could not initialize V4L2_CID_AUDIO_MUTE control %d\n",
+                       (int)ret);
+               goto exit;
+       }
+
+       ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &s610_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 1);
+       ret = radio->ctrl_handler.error;
+       if (ctrl == NULL && ret) {
+               dev_err(&pdev->dev,
+                       "Could not initialize V4L2_CID_AUDIO_VOLUME control %d\n",
+                       (int)ret);
+               goto exit;
+       }
+
+       ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, &s610_ctrl_ops,
+                       V4L2_CID_TUNE_DEEMPHASIS,
+                       V4L2_DEEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_75_uS);
+       ret = radio->ctrl_handler.error;
+       if (ctrl == NULL && ret) {
+               dev_err(&pdev->dev,
+                       "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n",
+                       (int)ret);
+               goto exit;
+       }
+
+       /* register video device */
+       ret = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Could not register video device\n");
+               goto exit;
+       }
+
+       mutex_init(&radio->lock);
+       s610_core_lock_init(radio->core);
+
+       /*init_waitqueue_head(&radio->core->rds_read_queue);*/
+
+       INIT_DELAYED_WORK(&radio->dwork_sig2, s610_sig2_work);
+       INIT_DELAYED_WORK(&radio->dwork_tune, s610_tune_work);
+#ifdef RDS_POLLING_ENABLE
+       INIT_DELAYED_WORK(&radio->dwork_rds_poll, s610_rds_poll_work);
+#endif /*RDS_POLLING_ENABLE*/
+#ifdef IDLE_POLLING_ENABLE
+       INIT_DELAYED_WORK(&radio->dwork_idle_poll, s610_idle_poll_work);
+#endif /*IDLE_POLLING_ENABLE*/
+
+#ifdef ENABLE_RDS_WORK_QUEUE
+       INIT_WORK(&radio->work, s610_rds_work);
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+
+#ifndef        RDS_POLLING_ENABLE
+#ifdef ENABLE_IF_WORK_QUEUE
+       INIT_WORK(&radio->if_work, s610_if_work);
+#endif /*ENABLE_IF_WORK_QUEUE*/
+#endif /* RDS_POLLING_ENABLE */
+
+#ifdef USE_AUDIO_PM
+       if (radio->a_dev)
+               pm_runtime_get_sync(radio->a_dev);
+#endif /* USE_AUDIO_PM */
+
+       /* all aux pll off for WIFI/BT */
+       radio->rfchip_ver = S620_REV_0;
+
+       ret = of_property_read_u32(dnode, "fm_iclk_aux", &radio->iclkaux);
+       if (ret)
+               radio->iclkaux = 1;
+       dev_info(radio->dev, "iClk Aux: %d\n", radio->iclkaux);
+
+       ret = of_property_read_u32(dnode, "num-tcon-freq", &radio->tc_on);
+       if (ret) {
+               radio->tc_on = 0;
+               goto skip_tc_off;
+       }
+
+       fm_spur_init = devm_kzalloc(&pdev->dev, radio->tc_on * sizeof(*fm_spur_init),
+                                       GFP_KERNEL);
+       if (!fm_spur_init) {
+               dev_err(radio->dev, "Mem alloc failed for TC ON freq values, TC off\n");
+               radio->tc_on = 0;
+               goto skip_tc_off;
+       }
+
+       if (of_property_read_u32_array(dnode, "val-tcon-freq", fm_spur_init, radio->tc_on)) {
+               dev_err(radio->dev, "Getting val-tcon-freq values faild, TC off\n");
+               radio->tc_on = 0;
+               goto skip_tc_off;
+       }
+       dev_info(radio->dev, "number TC On Freq: %d\n", radio->tc_on);
+skip_tc_off:
+
+       ret = of_property_read_u32(dnode, "num-trfon-freq", &radio->trf_on);
+       if (ret) {
+               radio->trf_on = 0;
+               goto skip_trf_off;
+       }
+
+       fm_spur_trf_init = devm_kzalloc(&pdev->dev, radio->trf_on * sizeof(*fm_spur_trf_init),
+                                       GFP_KERNEL);
+       if (!fm_spur_trf_init) {
+               dev_err(radio->dev, "Mem alloc failed for TRF ON freq values, TRF off\n");
+               radio->trf_on = 0;
+               goto skip_trf_off;
+       }
+
+       if (of_property_read_u32_array(dnode, "val-trfon-freq", fm_spur_trf_init, radio->trf_on)) {
+               dev_err(radio->dev, "Getting val-trfon-freq values faild, TRF off\n");
+               radio->trf_on = 0;
+               goto skip_trf_off;
+       }
+       dev_info(radio->dev, "number TRF On Freq: %d\n", radio->trf_on);
+skip_trf_off:
+
+       ret = of_property_read_u32(dnode, "num-dual-clkon-freq", &radio->dual_clk_on);
+       if (ret) {
+               radio->dual_clk_on = 0;
+               goto skip_dual_clk_off;
+       }
+
+       fm_dual_clk_init = devm_kzalloc(&pdev->dev, radio->dual_clk_on * sizeof(*fm_dual_clk_init),
+                                       GFP_KERNEL);
+       if (!fm_dual_clk_init) {
+               dev_err(radio->dev, "Mem alloc failed for DUAL CLK ON freq values, DUAL CLK off\n");
+               radio->dual_clk_on = 0;
+               goto skip_dual_clk_off;
+       }
+
+       if (of_property_read_u32_array(dnode, "val-dual-clkon-freq", fm_dual_clk_init, radio->dual_clk_on)) {
+               dev_err(radio->dev, "Getting val-dual-clkon-freq values faild, DUAL CLK off\n");
+               radio->dual_clk_on = 0;
+               goto skip_dual_clk_off;
+       }
+       dev_info(radio->dev, "number DUAL CLK On Freq: %d\n", radio->dual_clk_on);
+skip_dual_clk_off:
+
+       radio->vol_num = FM_RX_VOLUME_GAIN_STEP;
+       radio->vol_level_mod = vol_level_init;
+
+       ret = of_property_read_u32(dnode, "num-volume-level", &radio->vol_num);
+       if (ret) {
+               goto skip_vol_sel;
+       }
+
+       radio->vol_level_tmp = devm_kzalloc(&pdev->dev, radio->vol_num * sizeof(u32),
+                                       GFP_KERNEL);
+       if (!radio->vol_level_tmp) {
+               dev_err(radio->dev, "Mem alloc failed for Volume level values, Volume Level default setting\n");
+               goto skip_vol_sel;
+       }
+
+       if (of_property_read_u32_array(dnode, "val-vol-level", radio->vol_level_tmp, radio->vol_num)) {
+               dev_err(radio->dev, "Getting val-vol-level values faild, Volume Level default stting\n");
+               kfree(radio->vol_level_tmp);
+               radio->vol_num = FM_RX_VOLUME_GAIN_STEP;
+               goto skip_vol_sel;
+       }
+       radio->vol_level_mod = radio->vol_level_tmp;
+
+skip_vol_sel:
+       dev_info(radio->dev, "volume select num: %d\n", radio->vol_num);
+
+       ret = of_property_read_u32(dnode, "vol_3db_on", &radio->vol_3db_att);
+       if (ret)
+               radio->vol_3db_att = 0;
+       dev_info(radio->dev, "volume -3dB: %d\n", radio->vol_3db_att);
+
+       ret = of_property_read_u32(dnode, "rssi_est_on", &radio->rssi_est_on);
+       if (ret)
+               radio->rssi_est_on = 0;
+       dev_info(radio->dev, "rssi_est_on: %d\n", radio->rssi_est_on);
+
+       ret = of_property_read_u32(dnode, "sw_mute_weak", &radio->sw_mute_weak);
+       if (ret)
+               radio->sw_mute_weak = 0;
+       dev_info(radio->dev, "sw_mute_weak: %d\n", radio->sw_mute_weak);
+
+       ret = of_property_read_u32(dnode, "without_elna", &radio->without_elna);
+       if (ret)
+               radio->without_elna = 0;
+       dev_info(radio->dev, "without eLNA: %d\n", radio->without_elna);
+
+       ret = of_property_read_u16(dnode, "rssi_adjust", &radio->rssi_adjust);
+       if (ret)
+               radio->rssi_adjust= 0;
+       dev_info(radio->dev, "rssi adjust: %d\n", radio->rssi_adjust);
+
+#ifdef USE_AUDIO_PM
+       if (radio->a_dev)
+               pm_runtime_put_sync(radio->a_dev);
+#endif /* USE_AUDIO_PM */
+
+       pm_runtime_put_sync(dev);
+
+       wake_lock_init(&radio->wakelock, WAKE_LOCK_SUSPEND, "fm_wake");
+       wake_lock_init(&radio->rdswakelock, WAKE_LOCK_SUSPEND, "fm_rdswake");
+
+       dev_info(&pdev->dev, "end FM probe.\n");
+       return 0;
+
+exit:
+       v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
+
+alloc_err4:
+#ifdef USE_FM_LNA_ENABLE
+       if (radio->elna_gpio != -EINVAL)
+               gpio_free(radio->elna_gpio);
+#endif /*USE_FM_LNA_ENABLE*/
+
+       v4l2_device_unregister(&radio->v4l2dev);
+
+alloc_err3:
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_set_suspended(&pdev->dev);
+       clk_disable(radio->clk);
+       clk_unregister(clk);
+
+alloc_err2:
+       kfree(radio->low);
+
+alloc_err1:
+       kfree(radio->core);
+
+alloc_err0:
+       kfree(radio);
+
+       return ret;
+}
+
+static int s610_radio_remove(struct platform_device *pdev)
+{
+       struct s610_radio *radio = platform_get_drvdata(pdev);
+
+       if (radio) {
+               clk_disable(radio->clk);
+               clk_unregister(radio->clk);
+               clk_put(radio->clk);
+
+               pm_runtime_disable(&pdev->dev);
+               pm_runtime_set_suspended(&pdev->dev);
+
+               v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
+               video_unregister_device(&radio->videodev);
+               v4l2_device_unregister(&radio->v4l2dev);
+#ifdef USE_FM_LNA_ENABLE
+       if (radio->elna_gpio != -EINVAL)
+               gpio_free(radio->elna_gpio);
+#endif /*USE_FM_LNA_ENABLE*/
+               wake_lock_destroy(&radio->wakelock);
+               wake_lock_destroy(&radio->rdswakelock);
+
+               kfree(radio->vol_level_tmp);
+               kfree(radio->low);
+               kfree(radio->core);
+               kfree(radio);
+       }
+
+       return 0;
+}
+
+#define AUD_PLL_RATE_HZ_BYPASS         (26000000)
+#define AUD_PLL_RATE_HZ_FOR_48000      (1179648040)
+static int fm_radio_clk_enable(struct s610_radio *radio)
+{
+       unsigned long ret = 0;
+
+       if (radio->clk) {
+               ret = clk_enable(radio->clk);
+               if (ret)
+                       return ret;
+       } else {
+               dev_err(radio->v4l2dev.dev,
+                       "%s: fm radio clk_enable failed\n", __func__);
+               ret = -EIO;
+       }
+
+       if (clk_get_rate(radio->clk_pll) <= AUD_PLL_RATE_HZ_BYPASS) {
+               ret = clk_set_rate(radio->clk_pll, AUD_PLL_RATE_HZ_FOR_48000);
+               if (IS_ERR_VALUE(ret))
+                       dev_info(radio->dev, "setting pll clock to 0 is failed: %lu\n", ret);
+               dev_info(radio->dev, "pll clock: %lu\n", clk_get_rate(radio->clk_pll));
+       }
+
+       dev_info(radio->dev, "FM clock: %lu\n", clk_get_rate(radio->clk));
+
+       return ret;
+}
+
+static void fm_radio_clk_disable(struct s610_radio *radio)
+{
+       if (radio->clk) {
+               clk_disable(radio->clk);
+       } else {
+               dev_err(radio->v4l2dev.dev,
+                       "%s: fm radio clk_disable failed\n", __func__);
+       }
+}
+
+#ifdef CONFIG_PM
+static int fm_radio_runtime_suspend(struct device *dev)
+{
+       struct s610_radio *radio = dev_get_drvdata(dev);
+
+       FUNC_ENTRY(radio);
+
+       fm_radio_clk_disable(radio);
+
+       return 0;
+}
+
+static int fm_radio_runtime_resume(struct device *dev)
+{
+       struct s610_radio *radio = dev_get_drvdata(dev);
+       int ret = 0;
+
+       FUNC_ENTRY(radio);
+
+       ret = fm_radio_clk_enable(radio);
+       if (ret) {
+               dev_err(dev, "%s: clk_enable failed\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int fm_radio_suspend(struct device *dev)
+{
+       struct s610_radio *radio = dev_get_drvdata(dev);
+
+       FUNC_ENTRY(radio);
+
+       if (pm_runtime_suspended(dev))
+               return 0;
+
+       fm_radio_clk_disable(radio);
+
+       pm_runtime_put_sync(radio->dev);
+
+       return 0;
+}
+
+static int fm_radio_resume(struct device *dev)
+{
+       struct s610_radio *radio = dev_get_drvdata(dev);
+       int ret = 0;
+
+       FUNC_ENTRY(radio);
+
+       ret = pm_runtime_get_sync(radio->dev);
+       if (ret < 0) {
+               dev_err(dev, "get_sync failed with err %d\n", ret);
+               return ret;
+       }
+
+       ret = fm_radio_clk_enable(radio);
+       if (ret) {
+               dev_err(dev, "%s: clk_enable failed\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fm_radio_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(fm_radio_suspend, fm_radio_resume)
+       SET_RUNTIME_PM_OPS(fm_radio_runtime_suspend,
+                       fm_radio_runtime_resume, NULL)
+};
+
+#define DEV_PM_OPS     (&fm_radio_dev_pm_ops)
+
+static struct platform_driver s610_radio_driver = {
+               .driver         = {
+                               .name   = DRIVER_NAME,
+                               .owner  = THIS_MODULE,
+                               .of_match_table = exynos_fm_of_match,
+                               .pm     = DEV_PM_OPS,
+               },
+               .probe          = s610_radio_probe,
+               .remove         = s610_radio_remove,
+};
+static int __init init_s610_radio(void)
+{
+       platform_driver_register(&s610_radio_driver);
+
+       return 0;
+}
+
+static void __exit exit_s610_radio(void)
+{
+       platform_driver_unregister(&s610_radio_driver);
+}
+
+late_initcall(init_s610_radio);
+module_exit(exit_s610_radio);
+MODULE_AUTHOR("Youngjoon Chung, <young11@samsung.com>");
+MODULE_DESCRIPTION("Driver for S610 FM Radio in Exynos9610");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/s610/radio-s610.h b/drivers/media/radio/s610/radio-s610.h
new file mode 100644 (file)
index 0000000..e99754a
--- /dev/null
@@ -0,0 +1,553 @@
+#ifndef RADIO_S610_H
+#define RADIO_S610_H
+
+#define DRIVER_NAME "s610-radio"
+#define DRIVER_CARD "S610 FM Receiver"
+
+#define        ENABLE_RDS_WORK_QUEUE
+#undef ENABLE_RDS_WORK_QUEUE
+
+#define        ENABLE_IF_WORK_QUEUE
+/*#undef       ENABLE_IF_WORK_QUEUE*/
+
+#define        USE_FM_LNA_ENABLE
+/*#undef       USE_FM_LNA_ENABLE*/
+
+#define        RDS_POLLING_ENABLE
+
+#define IDLE_POLLING_ENABLE
+
+#define USE_AUDIO_PM
+
+/* DEBUG :: Print debug for debug *******/
+#define  SUPPORT_FM_DEBUG
+#define  SUPPORT_API_DEBUG
+#undef SUPPORT_FM_DEBUG
+#undef SUPPORT_API_DEBUG
+
+#ifdef SUPPORT_FM_DEBUG
+#define FDEBUG(fm, fmt, args...) dev_info(fm->dev, fmt, ##args)
+#define FUNC_ENTRY(fm) dev_info(fm->dev, "+ %s(): entry\n", __func__)
+#define FUNC_EXIT(fm) dev_info(fm->dev, "- %s(): exit\n", __func__)
+#else
+#define FDEBUG(fm, fmt, args...)
+#define FUNC_ENTRY(fm)
+#define FUNC_EXIT(fm)
+#endif /*SUPPORT_FM_DEBUG*/
+
+#ifdef SUPPORT_API_DEBUG
+#define API_ENTRY(fm) dev_info(fm->dev, "> API: %s(): entry\n", __func__)
+#define API_EXIT(fm) dev_info(fm->dev, "< API: %s(): exit\n", __func__)
+#define APIEBUG(fm, fmt, args...) dev_info(fm->dev, fmt, ##args)
+#else
+#define API_ENTRY(fm)
+#define API_EXIT(fm)
+#define APIEBUG(fm, fmt, args...)
+#endif
+
+#define  SUPPORT_RDS_DEBUG
+#undef SUPPORT_RDS_DEBUG
+
+#ifdef SUPPORT_RDS_DEBUG
+#define RDSEBUG(fm, fmt, args...) dev_info(fm->dev, fmt, ##args)
+#define RDS_ENTRY(fm) dev_info(fm->dev, "> RDS: %s(): entry\n", __func__)
+#define RDS_EXIT(fm) dev_info(fm->dev, "< RDS: %s(): exit\n", __func__)
+#else
+#define RDSEBUG(fm, fmt, args...)
+#define RDS_ENTRY(fm)
+#define RDS_EXIT(fm)
+#endif /*SUPPORT_RDS_DEBUG*/
+
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/wakelock.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+
+#include <linux/i2c.h>
+#include "fm_low_struc.h"
+
+#define V4L2_CID_USER_S610_BASE                (0x00980900 + 0x1070)
+enum s610_ctrl_id {
+       V4L2_CID_S610_CH_SPACING  = (V4L2_CID_USER_S610_BASE + 0x01),
+       V4L2_CID_S610_CH_BAND    = (V4L2_CID_USER_S610_BASE + 0x02),
+       V4L2_CID_S610_SOFT_STEREO_BLEND = (V4L2_CID_USER_S610_BASE + 0x03),
+       V4L2_CID_S610_SOFT_STEREO_BLEND_COEFF = (V4L2_CID_USER_S610_BASE+0x04),
+       V4L2_CID_S610_SOFT_MUTE_COEFF = (V4L2_CID_USER_S610_BASE + 0x5),
+       V4L2_CID_S610_RSSI_CURR = (V4L2_CID_USER_S610_BASE + 0x06),
+       V4L2_CID_S610_SNR_CURR  = (V4L2_CID_USER_S610_BASE + 0x07),
+       V4L2_CID_S610_SEEK_CANCEL       = (V4L2_CID_USER_S610_BASE + 0x08),
+       V4L2_CID_S610_SEEK_MODE = (V4L2_CID_USER_S610_BASE + 0x09),
+       V4L2_CID_S610_RDS_ON = (V4L2_CID_USER_S610_BASE + 0x0A),
+       V4L2_CID_S610_IF_COUNT1 = (V4L2_CID_USER_S610_BASE + 0x0B),
+       V4L2_CID_S610_IF_COUNT2 = (V4L2_CID_USER_S610_BASE + 0x0C),
+       V4L2_CID_S610_RSSI_TH = (V4L2_CID_USER_S610_BASE + 0x0D),
+       V4L2_CID_S610_KERNEL_VER = (V4L2_CID_USER_S610_BASE + 0x0E),
+       V4L2_CID_S610_SOFT_STEREO_BLEND_REF = (V4L2_CID_USER_S610_BASE+0x0F),
+};
+
+enum fm_flag_get {
+       FM_EVENT_TUNED      =   (1 << 0),
+       FM_EVENT_BD_LMT     =   (1 << 1),
+       FM_EVENT_SYN_LOS  =     (1 << 2),
+       FM_EVENT_BUF_FUL            =   (1 << 3),
+       FM_EVENT_AUD_PAU            =   (1 << 4),
+       FM_EVENT_CH_STAT            =   (1 << 5)
+};
+
+/* Tunner modes */
+enum fm_tuner_mode {
+       FM_TUNER_STOP_SEARCH_MODE       = 0,
+       FM_TUNER_PRESET_MODE            = 1,
+       FM_TUNER_AUTONOMOUS_SEARCH_MODE = 2,
+       FM_TUNER_AUTONOMOUS_SEARCH_MODE_NEXT    = 10
+};
+
+/* channel spacing */
+enum fm_channel_spacing {
+       FM_CHANNEL_SPACING_50KHZ = 1,
+       FM_CHANNEL_SPACING_100KHZ = 2,
+       FM_CHANNEL_SPACING_200KHZ = 4
+};
+
+#define FM_FREQ_MUL 50
+
+/* Mute modes */
+enum fm_mute_mode {
+       FM_MUTE_ON   =          0,
+       FM_MUTE_OFF   =         1,
+       FM_MUTE_ATTENUATE       = 2
+};
+
+/* Register set mute bits */
+enum fm_reg_mute {
+       FM_RX_UNMUTE_MODE               = 0x00,
+       FM_RX_RF_DEP_MODE               = 0x01,
+       FM_RX_AC_MUTE_MODE              = 0x02,
+       FM_RX_HARD_MUTE_LEFT_MODE       = 0x04,
+       FM_RX_HARD_MUTE_RIGHT_MODE      = 0x08,
+       FM_RX_SOFT_MUTE_FORCE_MODE      = 0x10
+};
+
+/* FM RDS modes */
+enum fm_rds_mode {
+       FM_RDS_DISABLE  = 0,
+       FM_RDS_ENABLE   = 1
+};
+
+/* FM RDS Parser */
+#define MAX_PS  8
+#define MAX_RT  64
+#define MAX_RTP_TAG 2
+
+struct rtp_tag_info {
+       u32 content_type;
+       u32 start_pos;
+       u32 len;
+};
+
+struct rtp_info {
+       u8 toggle;
+       u8 running;
+       u8 validated;
+       struct rtp_tag_info tag[2];
+};
+
+struct fm_rds_parser_info {
+       u32 pi_idx;
+       u16 pi_buf[2];
+
+       u32 ecc_idx;
+       u8 ecc_buf[2];
+
+       u32 af_idx;
+       u16 af_buf[2];
+
+       u8 ps_segment;
+       u8 ps_len;
+       u32 ps_idx;
+       u8 ps_buf[3][MAX_PS + 1];
+       u8 ps_err[3][MAX_PS / 2];
+       u8 ps_candidate[MAX_PS + 1];
+
+       u8 rt_segment;
+       u8 rt_len;
+       u32 rt_idx;
+       u8 rt_buf[3][MAX_RT + 1];
+       u8 rt_err[3][MAX_RT / 2];
+       u8 rt_candidate[MAX_RT + 1];
+       u8 rt_change;
+       u8 rt_validated;
+
+       u8 rtp_drop_blk;
+       u8 rtp_code_group;
+       u16 rtp_raw_data[3];
+       struct rtp_info rtp_data;
+
+       u8 grp;
+       bool drop_blk;
+       u8 rds_event;
+};
+
+/* RF dependent mute mode */
+#define FM_RX_RF_DEPENDENT_MUTE_ON     1
+#define FM_RX_RF_DEPENDENT_MUTE_OFF    0
+
+#define FM_DRV_TURN_TIMEOUT            (5*HZ)  /* 5 seconds */
+#define FM_DRV_SEEK_TIMEOUT            (20*HZ) /* 10 seconds */
+
+/* Min and Max volume */
+#define FM_RX_VOLUME_MIN       0
+#define FM_RX_VOLUME_MAX       70
+
+/* Volume gain step */
+#define FM_RX_VOLUME_GAIN_STEP 16
+
+#define FM_SEARCH_DIRECTION_UP 0
+#define FM_SEARCH_DIRECTION_DOWN       1
+
+/* undefined freq */
+#define FM_UNDEFINED_FREQ                 0xFFFFFFFF
+
+/* RDS system type (RDS/RBDS) */
+#define FM_RDS_SYSTEM_RDS              0
+#define FM_RDS_SYSTEM_RBDS             1
+
+/* AF on/off */
+#define FM_RX_RDS_AF_SWITCH_MODE_ON    1
+#define FM_RX_RDS_AF_SWITCH_MODE_OFF   0
+
+/* RX RDS */
+#define FM_RX_RDS_FLUSH_FIFO           0x1
+#define FM_RX_RDS_FIFO_THRESHOLD       48      /* tuples */
+#define FM_RX_RDS_FIFO_THRESHOLD_PARSER        100     /* tuples */
+#define FM_RDS_BLK_SIZE                3       /* 3 bytes */
+
+/* default RSSI value for init */
+#define FM_DEFAULT_RSSI_THRESHOLD      0x8E
+
+/* Reset pre-tune value */
+#define RESET_PRETUNE_VALUE     103500
+
+#define GPIO_LOW               0
+#define GPIO_HIGH              1
+
+struct s610_radio;
+
+enum s610_power_state {
+       S610_POWER_DOWN         = 0,
+       S610_POWER_ON_FM                = 1,
+       S610_POWER_ON_RDS       = 2
+};
+
+/* FM region (Europe/US, Japan) info */
+struct region_info {
+       u32 chanl_space;
+       u32 bot_freq;
+       u32 top_freq;
+       u8 fm_band;
+};
+
+struct s610_core {
+       int chip_id;
+       struct mutex cmd_lock; /* for serializing fm radio operations */
+/*     enum s610_power_state power_state;*/
+       u8 power_mode;
+       wait_queue_head_t  rds_read_queue;
+};
+
+/**
+ * s610_core_lock() - lock the core device to get an exclusive access
+ * to it.
+ */
+static inline void s610_core_lock_init(struct s610_core *core)
+{
+       mutex_init(&core->cmd_lock);
+}
+
+/**
+ * s610_core_lock() - lock the core device to get an exclusive access
+ * to it.
+ */
+static inline void s610_core_lock(struct s610_core *core)
+{
+       mutex_lock(&core->cmd_lock);
+}
+
+/**
+ * s610_core_lock() - lock the core device to get an exclusive access
+ * to it.
+ */
+static inline  int __must_check s610_core_lock_interruptible(
+               struct s610_core *core)
+{
+       int ret;
+
+       ret = mutex_lock_interruptible(&core->cmd_lock);
+       return ret;
+}
+
+/**
+ * s610_core_unlock() - unlock the core device to relinquish an
+ * exclusive access to it.
+ */
+static inline void s610_core_unlock(struct s610_core *core)
+{
+       mutex_unlock(&core->cmd_lock);
+}
+
+static inline int api_to_real(int freq)
+{
+       return freq;
+}
+
+static inline int real_to_api(int freq)
+{
+       return freq;
+}
+
+#define FREQ_MUL (10000000 / 625)
+
+enum s610_freq_bands {
+       S610_BAND_FM,
+       S610_BAND_AM,
+};
+
+struct ringbuf_t {
+       u8 *buf;
+       u8 *head, *tail;
+       int size;
+};
+
+/**
+ * struct s610_radio - radio device
+ *
+ * @core: Pointer to underlying core device
+ * @videodev: Pointer to video device created by V4L2 subsystem
+ * @ops: Vtable of functions. See struct s610_radio_ops for details
+ * @kref: Reference counter
+ * @core_lock: An r/w semaphore to brebvent the deletion of underlying
+ * core structure is the radio device is being used
+ */
+struct s610_radio {
+       struct v4l2_device v4l2dev;
+       struct video_device videodev;
+       struct v4l2_ctrl_handler ctrl_handler;
+
+       struct s610_core  *core;
+       struct s610_low  *low;
+
+       u32 audmode;
+
+       int radio_region;
+       struct region_info region;      /* Current selected band */
+       u8 mute_mode;   /* Current mute mode */
+       u32 freq;       /* Current RX frquency */
+       u8 deemphasis_mode; /* Current deemphasis mode */
+       u8 rf_depend_mute;      /* RF dependent soft mute mode */
+       u16 volume;     /* Current volume level */
+       u16 rssi_threshold;     /* Current RSSI threshold level */
+       u8 rds_mode;    /* RDS operation mode (RDS/RDBS) */
+       u8 af_mode;     /* Alternate frequency on/off */
+
+       u16 irq_flag;   /* FM interrupt flag */
+       u16 irq_mask;   /* FM interrupt mask */
+       unsigned int irq;    /* AP interrupt line */
+
+       /* flags FR BL completion handler */
+       struct completion flags_set_fr_comp;
+       struct completion flags_seek_fr_comp;
+
+       /* set if counter completion handler */
+       struct completion set_if_cnt_comp;
+
+       spinlock_t slock; /* To protect access to buffer */
+       spinlock_t rds_lock; /* To protect access to buffer */
+
+       struct wake_lock        wakelock;
+       struct wake_lock        rdswakelock;
+       atomic_t is_doing;
+       atomic_t is_rds_new;
+       atomic_t is_rds_doing;
+       int     wait_atomic;
+       int     wait_atomic_rds;
+
+       spinlock_t rds_buff_lock; /* To protect access to RDS buffer */
+       u8 rds_flag;    /* RX RDS on/off status */
+       u8 rds_new;             /* RX RDS new data status */
+       u8 rds_buf[480];
+       struct ringbuf_t rds_rb;
+       struct fm_rds_parser_info pi;
+       struct mutex    lock;
+       struct workqueue_struct *work_queue;
+       struct workqueue_struct *if_work_queue;
+
+#ifdef ENABLE_RDS_WORK_QUEUE
+       struct work_struct work;
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+#ifdef ENABLE_IF_WORK_QUEUE
+       struct work_struct if_work;
+#endif /*ENABLE_IF_WORK_QUEUE*/
+
+       struct delayed_work dwork_sig2;
+       struct delayed_work dwork_tune;
+#ifdef RDS_POLLING_ENABLE
+       struct delayed_work dwork_rds_poll;
+#endif /*RDS_POLLING_ENABLE*/
+#ifdef IDLE_POLLING_ENABLE
+       struct delayed_work dwork_idle_poll;
+#endif /*IDLE_POLLING_ENABLE*/
+       u16 dwork_idle_counter;
+       u16 dwork_rds_counter;
+       u16 dwork_sig2_counter;
+       u16 dwork_tune_counter;
+       u16 switch_rssi;
+       u16 idle_fniarg;
+       u16 sig2_fniarg;
+       u16 tune_fniarg;
+
+       u16 freq_step;
+       u8 speedy_error;
+       u16 seek_mode;
+       u32 seek_freq;  /* seek start frquency */
+       u32 seek_status;
+       int seek_weak_rssi;
+       u32 wrap_around;
+       u32 iclkaux;
+       void __iomem *fmspeedy_base;
+       void __iomem *disaud_cmu_base;
+#ifdef USE_FM_LNA_ENABLE
+       int elna_gpio;
+#endif /* USE_FM_EXTERN_PLL */
+       struct platform_device *pdev;
+       struct device *dev;
+#ifdef USE_AUDIO_PM
+       struct device *a_dev;
+#endif /* USE_AUDIO_PM */
+       struct clk *clk;
+       struct clk *clk_pll;
+       struct clk *mux_aud_fm;
+       struct clk *clk_aud_fm;
+       int tc_on;
+       int trf_on;
+       int dual_clk_on;
+       int vol_num;
+       u32 *vol_level_mod;
+       u32 *vol_level_tmp;
+       int vol_3db_att;
+       int rssi_est_on;
+       int sw_mute_weak;
+       u32 rfchip_ver;
+       int without_elna;
+       u16 rssi_adjust;
+       bool rssi_ref_enable;
+       u32 agc_enable;
+/*     debug print counter */
+       int idle_cnt_mod;
+       int rds_cnt_mod;
+       fm_rds_block_type_enum block_seq;
+
+/* Test RDS log */
+       bool invalid_rssi;
+       int rds_n_count;
+       int rds_r_count;
+       int rds_new_stat;
+       int rds_read_cnt;
+       int rds_fifo_err_cnt;
+       int rds_fifo_rd_cnt;
+       int rds_reset_cnt;
+       int rds_sync_loss_cnt;
+       int rb_used;
+       bool rds_parser_enable;
+       int rds_gcnt;
+/* Test RDS log end */
+};
+
+extern bool fm_radio_on(struct s610_radio *radio);
+extern void fm_radio_off(struct s610_radio *radio);
+extern void fm_rds_on(struct s610_radio *radio);
+extern void fm_rds_off(struct s610_radio *radio);
+extern void fm_set_band(struct s610_radio *radio, u8 index);
+extern int fm_boot(struct s610_radio *radio);
+extern void fm_power_off(void);
+
+extern int low_get_search_lvl(struct s610_radio *radio, u16 *value);
+extern u16 fm_get_flags(struct s610_radio *radio);
+extern int low_set_if_limit(struct s610_radio *radio, u16 value);
+extern int low_set_search_lvl(struct s610_radio *radio, u16 value);
+extern int low_set_freq(struct s610_radio *radio, u32 value);
+extern int low_set_tuner_mode(struct s610_radio *radio, u16 value);
+extern int low_set_mute_state(struct s610_radio *radio, u16 value);
+extern int low_set_most_mode(struct s610_radio *radio, u16 value);
+extern int low_set_most_blend(struct s610_radio *radio, u16 value);
+extern int low_set_pause_lvl(struct s610_radio *radio, u16 value);
+extern int low_set_pause_dur(struct s610_radio *radio, u16 value);
+extern int low_set_demph_mode(struct s610_radio *radio, u16 value);
+extern int low_set_rds_cntr(struct s610_radio *radio, u16 value);
+extern int low_set_power(struct s610_radio *radio, u16 value);
+extern u8 aggr_rssi_device_to_host(u16 val);
+extern void fm_isr(struct s610_radio *radio);
+extern void cancel_tuner_timer(struct s610_radio *radio);
+extern int init_low_struc(struct s610_radio *radio);
+extern void fm_speedy_m_int_enable(void);
+extern void fm_speedy_m_int_disable(void);
+#ifdef ENABLE_RDS_WORK_QUEUE
+extern void s610_rds_work(struct work_struct *work);
+#endif /*ENABLE_RDS_WORK_QUEUE*/
+#ifdef ENABLE_IF_WORK_QUEUE
+extern void s610_if_work(struct work_struct *work);
+#endif /*ENABLE_IF_WORK_QUEUE*/
+
+extern void s610_sig2_work(struct work_struct *work);
+extern void s610_tune_work(struct work_struct *work);
+#ifdef RDS_POLLING_ENABLE
+extern void s610_rds_poll_work(struct work_struct *work);
+extern void fm_rds_periodic_update(unsigned long data);
+extern void fm_rds_periodic_cancel(unsigned long data);
+#endif /*RDS_POLLING_ENABLE*/
+#ifdef IDLE_POLLING_ENABLE
+extern void s610_idle_poll_work(struct work_struct *work);
+extern void fm_idle_periodic_update(unsigned long data);
+extern void fm_idle_periodic_cancel(unsigned long data);
+#endif /*IDLE_POLLING_ENABLE*/
+
+extern void fm_set_blend_mute(struct s610_radio *radio);
+
+extern u32 fmspeedy_get_reg(u32 addr);
+extern int fmspeedy_set_reg(u32 addr, u32 data);
+extern u32 fmspeedy_get_reg_field(u32 addr, u32 shift, u32 mask);
+
+extern void fm_update_rssi(struct s610_radio *radio);
+extern void fm_update_snr(struct s610_radio *radio);
+extern void fm_set_audio_gain(struct s610_radio *radio, u16 gain);
+extern void fm_aux_pll_off(void);
+extern void fmspeedy_wakeup(void);
+extern int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
+               u16 *blocks);
+extern u32 fmspeedy_get_reg_work(u32 addr);
+extern signed int exynos_get_fm_open_status(void);
+extern void fm_ds_set(u32 data);
+extern void fm_get_version_number(void);
+extern int ringbuf_bytes_used(const struct ringbuf_t *rb);
+extern void fm_rds_parser_reset(struct fm_rds_parser_info *pi);
+#endif /* RADIO_S610_H */
+