--- /dev/null
+/****************************************************************************
+ 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;
+}
--- /dev/null
+#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*/
--- /dev/null
+/*
+ * 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");