Loading drivers/media/radio/silabs/radio-silabs.c +435 −22 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ struct silabs_fm_device { /* regional settings */ enum silabs_region_t region; /* power mode */ int lp_mode; bool lp_mode; int handle_irq; /* global lock */ struct mutex lock; Loading @@ -91,10 +91,12 @@ struct silabs_fm_device { /* work queue */ struct workqueue_struct *wqueue; struct workqueue_struct *wqueue_scan; struct workqueue_struct *wqueue_af; struct workqueue_struct *wqueue_rds; struct work_struct rds_worker; struct delayed_work work; struct delayed_work work_scan; struct delayed_work work_af; /* wait queue for blocking event read */ wait_queue_head_t event_queue; /* wait queue for raw rds read */ Loading Loading @@ -123,12 +125,17 @@ struct silabs_fm_device { u8 utf_8_flag; u8 rt_ert_flag; u8 formatting_dir; bool is_af_jump_enabled; bool is_af_tune_in_progress; u8 af_rssi_th; /* allowed rssi is 0-127 */ struct silabs_af_info af_info; }; static int silabs_fm_request_irq(struct silabs_fm_device *radio); static int tune(struct silabs_fm_device *radio, u32 freq); static int silabs_seek(struct silabs_fm_device *radio, int dir, int wrap); static int cancel_seek(struct silabs_fm_device *radio); static int configure_interrupts(struct silabs_fm_device *radio, u8 val); static void silabs_fm_q_event(struct silabs_fm_device *radio, enum silabs_evt_t event); Loading Loading @@ -1201,6 +1208,226 @@ static void silabs_raw_rds_handler(struct silabs_fm_device *radio) break; } } static int set_hard_mute(struct silabs_fm_device *radio, bool val) { int retval = 0; if (val == true) { retval = set_property(radio, RX_HARD_MUTE_PROP, HARD_MUTE_MASK); if (retval < 0) FMDERR("%s: set_hard_mute failed with error %d\n", __func__, retval); } else { retval = set_property(radio, RX_HARD_MUTE_PROP, 0); if (retval < 0) FMDERR("%s: set_hard_mute failed with error %d\n", __func__, retval); } return retval; } static int get_rssi(struct silabs_fm_device *radio, u8 *prssi) { int retval = 0; mutex_lock(&radio->lock); memset(radio->write_buf, 0, WRITE_REG_NUM); /* track command that is being sent to chip.*/ radio->cmd = FM_RSQ_STATUS_CMD; radio->write_buf[0] = FM_RSQ_STATUS_CMD; radio->write_buf[1] = 1; retval = send_cmd(radio, RSQ_STATUS_CMD_LEN); if (retval < 0) FMDERR("%s: get_rsq_status failed with error %d\n", __func__, retval); FMDBG("%s: rssi is %d\n", __func__, radio->read_buf[4]); *prssi = radio->read_buf[4]; mutex_unlock(&radio->lock); return retval; } static void reset_af_info(struct silabs_fm_device *radio) { radio->af_info.is_new_af_list = false; radio->af_info.cnt = 0; radio->af_info.index = 0; radio->af_info.size = 0; radio->af_info.orig_freq_khz = 0; memset(radio->af_info.af_list, 0, sizeof(radio->af_info.af_list)); } static void update_af_list(struct silabs_fm_device *radio) { u8 first_byte, sec_byte; u16 af_data = radio->block[2]; first_byte = af_data >> 8; sec_byte = af_data & 0xFF; if (first_byte >= MIN_AF_CNT_CODE && first_byte <= MAX_AF_CNT_CODE) { /* AF count. */ reset_af_info(radio); radio->af_info.cnt = first_byte - NO_AF_CNT_CODE; radio->af_info.orig_freq_khz = radio->tuned_freq_khz; FMDBG("%s: current freq is %u, AF cnt is %u\n", __func__, radio->tuned_freq_khz, radio->af_info.cnt); radio->af_info.af_list[radio->af_info.size++] = SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte); FMDBG("%s: AF is %u\n", __func__, SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte)); } else if (first_byte >= MIN_AF_FREQ_CODE && first_byte <= MAX_AF_FREQ_CODE) { /* update the AF list */ radio->af_info.af_list[radio->af_info.size++] = SCALE_AF_CODE_TO_FREQ_KHZ(first_byte); FMDBG("%s: first AF is %u\n", __func__, SCALE_AF_CODE_TO_FREQ_KHZ(first_byte)); if (radio->af_info.size < radio->af_info.cnt) { radio->af_info.af_list[radio->af_info.size++] = SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte); FMDBG("%s: second AF is %u\n", __func__, SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte)); } } } static void silabs_af_tune(struct work_struct *work) { struct silabs_fm_device *radio; int retval = 0; u8 rssi = 0; u32 freq = 0; radio = container_of(work, struct silabs_fm_device, work_af.work); if (radio->af_info.size == 0) { FMDBG("%s: Empty AF list\n", __func__); radio->is_af_tune_in_progress = false; return; } /* Disable all other interrupts except STC */ retval = configure_interrupts(radio, ENABLE_STC_INTERRUPTS); /* Mute until AF tuning finishes */ retval = set_hard_mute(radio, true); while (1) { if (radio->mode != FM_RECV) { FMDERR("%s: Drv is not in proper state\n", __func__); goto end; } if (radio->seek_tune_status != NO_SEEK_TUNE_PENDING) { FMDBG("%s: manual tune, search issued\n", __func__); break; } if (radio->is_af_jump_enabled != true) { FMDBG("%s: AF jump is disabled\n", __func__); break; } /* If no more AFs left, tune to original frequency and break */ if (radio->af_info.index >= radio->af_info.size) { FMDBG("%s: No more AFs, tuning to original freq %u\n", __func__, radio->af_info.orig_freq_khz); freq = radio->af_info.orig_freq_khz; retval = tune(radio, freq); if (retval < 0) { FMDERR("%s: tune failed, error %d\n", __func__, retval); goto err_tune_fail; } /* wait for tune to finish */ if (!wait_for_completion_timeout(&radio->sync_req_done, msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { FMDERR("%s: didn't receive STC for tune\n", __func__); /* FM is not correct state */ continue; } else FMDBG("%s: received STC for tune\n", __func__); goto err_tune_fail; } freq = radio->af_info.af_list[radio->af_info.index++]; FMDBG("%s: tuning to freq %u\n", __func__, freq); retval = tune(radio, freq); if (retval < 0) { FMDERR("%s: tune failed, error %d\n", __func__, retval); goto err_tune_fail; } /* wait for tune to finish */ if (!wait_for_completion_timeout(&radio->sync_req_done, msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { FMDERR("%s: didn't receive STC for tune\n", __func__); /* FM is not correct state */ continue; } else FMDBG("%s: received STC for tune\n", __func__); retval = get_rssi(radio, &rssi); if (retval < 0) { FMDERR("%s: getting rssi failed\n", __func__); goto err_tune_fail; } if (rssi >= radio->af_rssi_th) { FMDBG("%s: found AF freq(%u) >= AF threshold\n", __func__, freq); /* Notify FM UI about the new freq */ FMDBG("%s: posting TUNE_SUCC event\n", __func__); silabs_fm_q_event(radio, SILABS_EVT_TUNE_SUCC); break; } } err_tune_fail: /* * At this point, we are tuned to either original freq or AF with >= * AF rssi threshold */ reset_af_info(radio); radio->is_af_tune_in_progress = false; /* Clear the stale RDS int bit. */ get_rds_status(radio); retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); /* Clear the stale RSQ int bit. */ get_rssi(radio, &rssi); retval = configure_interrupts(radio, ENABLE_RSQ_INTERRUPTS); end: return; } /* When RDS interrupt is received, read and process RDS data. */ static void rds_handler(struct work_struct *worker) { Loading Loading @@ -1232,6 +1459,7 @@ static void rds_handler(struct work_struct *worker) switch (grp_type) { case RDS_TYPE_0A: update_af_list(radio); /* fall through */ case RDS_TYPE_0B: addr = (radio->block[1] & PS_MASK) * NO_OF_CHARS_IN_EACH_ADD; Loading Loading @@ -1290,15 +1518,16 @@ static int configure_interrupts(struct silabs_fm_device *radio, u8 val) prop_val = 0; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("In %s, error disabling interrupts\n", __func__); FMDERR("%s: error disabling interrupts\n", __func__); break; case ENABLE_STC_RDS_INTERRUPTS: /* enable interrupts. */ /* enable STC and RDS interrupts. */ prop_val = RDS_INT_BIT_MASK | STC_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("In %s, error enabling interrupts\n", __func__); FMDERR("%s: error enabling STC, RDS interrupts\n", __func__); break; case ENABLE_STC_INTERRUPTS: /* enable STC interrupts only. */ Loading @@ -1306,7 +1535,51 @@ static int configure_interrupts(struct silabs_fm_device *radio, u8 val) retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("In %s, error enabling interrupts\n", __func__); FMDERR("%s: error enabling STC interrupts\n", __func__); break; case ENABLE_RDS_INTERRUPTS: /* enable RDS interrupts. */ prop_val = RDS_INT_BIT_MASK | STC_INT_BIT_MASK; if (radio->is_af_jump_enabled) prop_val |= RSQ_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error enabling RDS interrupts\n", __func__); break; case DISABLE_RDS_INTERRUPTS: /* disable RDS interrupts. */ prop_val = STC_INT_BIT_MASK; if (radio->is_af_jump_enabled) prop_val |= RSQ_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error disabling RDS interrupts\n", __func__); break; case ENABLE_RSQ_INTERRUPTS: /* enable RSQ interrupts. */ prop_val = RSQ_INT_BIT_MASK | STC_INT_BIT_MASK; if (radio->lp_mode != true) prop_val |= RDS_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error enabling RSQ interrupts\n", __func__); break; case DISABLE_RSQ_INTERRUPTS: /* disable RSQ interrupts. */ prop_val = STC_INT_BIT_MASK; if (radio->lp_mode != true) prop_val |= RDS_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error disabling RSQ interrupts\n", __func__); break; default: FMDERR("%s: invalid value %u\n", __func__, val); Loading Loading @@ -1358,20 +1631,44 @@ static int initialize_recv(struct silabs_fm_device *radio) { int retval = 0; retval = set_property(radio, FM_SEEK_TUNE_SNR_THRESHOLD_PROP, 2); retval = set_property(radio, FM_SEEK_TUNE_SNR_THRESHOLD_PROP, DEFAULT_SNR_TH); if (retval < 0) { FMDERR("%s: FM_SEEK_TUNE_SNR_THRESHOLD_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } retval = set_property(radio, FM_SEEK_TUNE_RSSI_THRESHOLD_PROP, 7); retval = set_property(radio, FM_SEEK_TUNE_RSSI_THRESHOLD_PROP, DEFAULT_RSSI_TH); if (retval < 0) { FMDERR("%s: FM_SEEK_TUNE_RSSI_THRESHOLD_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } retval = set_property(radio, FM_RSQ_RSSI_LO_THRESHOLD_PROP, DEFAULT_AF_RSSI_LOW_TH); if (retval < 0) { FMDERR("%s: FM_RSQ_RSSI_LO_THRESHOLD_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } radio->af_rssi_th = DEFAULT_AF_RSSI_LOW_TH; retval = set_property(radio, FM_RSQ_INT_SOURCE_PROP, RSSI_LOW_TH_INT_BIT_MASK); if (retval < 0) { FMDERR("%s: FM_RSQ_INT_SOURCE_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } set_prop_fail: return retval; Loading Loading @@ -1630,9 +1927,33 @@ static void silabs_fm_q_event(struct silabs_fm_device *radio, wake_up_interruptible(&radio->event_queue); } static int clear_stc_int(struct silabs_fm_device *radio) { int retval = 0; mutex_lock(&radio->lock); memset(radio->write_buf, 0, WRITE_REG_NUM); /* track command that is being sent to chip. */ radio->cmd = FM_TUNE_STATUS_CMD; radio->write_buf[0] = FM_TUNE_STATUS_CMD; radio->write_buf[1] = INTACK_MASK; retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); if (retval < 0) FMDERR("%s: clear_stc_int fail, error %d\n", __func__, retval); mutex_unlock(&radio->lock); return retval; } static void silabs_interrupts_handler(struct silabs_fm_device *radio) { int retval = 0; u8 rssi = 0; if (unlikely(radio == NULL)) { FMDERR("%s:radio is null", __func__); Loading @@ -1653,11 +1974,7 @@ static void silabs_interrupts_handler(struct silabs_fm_device *radio) FMDBG("%s: successfully read the resp from soc, status byte is %x\n", __func__, radio->read_buf[0]); if (radio->read_buf[0] & RDS_INT_BIT_MASK) { FMDBG("RDS interrupt received\n"); schedule_work(&radio->rds_worker); return; } if (radio->read_buf[0] & STC_INT_BIT_MASK) { FMDBG("%s: STC bit set for cmd %x\n", __func__, radio->cmd); if (radio->seek_tune_status == TUNE_PENDING) { Loading @@ -1682,8 +1999,53 @@ static void silabs_interrupts_handler(struct silabs_fm_device *radio) */ FMDBG("In %s, signalling scan thread\n", __func__); complete(&radio->sync_req_done); } else if (radio->is_af_tune_in_progress == true) { /* * when AF tune is going on and STC int is set, signal * so that AF tune can proceed. */ FMDBG("In %s, signalling AF tune thread\n", __func__); complete(&radio->sync_req_done); } /* clear the STC interrupt. */ clear_stc_int(radio); reset_rds(radio); /* Clear the existing RDS data */ return; } if (radio->read_buf[0] & RSQ_INT_BIT_MASK) { FMDBG("RSQ interrupt received, clearing the RSQ int bit\n"); /* clear RSQ, RDS interrupt bits until AF tune is complete. */ (void)get_rssi(radio, &rssi); (void)get_rds_status(radio); /* Don't process RSQ until AF tune is complete. */ if (radio->is_af_tune_in_progress == true) return; if (radio->is_af_jump_enabled && radio->af_info.size != 0 && rssi <= radio->af_rssi_th) { radio->is_af_tune_in_progress = true; FMDBG("%s: Queuing to AF work Q, freq %u, rssi %u\n", __func__, radio->tuned_freq_khz, rssi); queue_delayed_work(radio->wqueue_af, &radio->work_af, msecs_to_jiffies(SILABS_DELAY_MSEC)); } return; } if (radio->read_buf[0] & RDS_INT_BIT_MASK) { FMDBG("RDS interrupt received\n"); /* Don't process RDS until AF tune is complete. */ if (radio->is_af_tune_in_progress == true) { /* clear RDS int bit and return */ get_rds_status(radio); return; } schedule_work(&radio->rds_worker); return; } return; } Loading @@ -1710,6 +2072,8 @@ static void silabs_fm_disable_irq(struct silabs_fm_device *radio) flush_workqueue(radio->wqueue); cancel_delayed_work_sync(&radio->work_scan); flush_workqueue(radio->wqueue_scan); cancel_delayed_work_sync(&radio->work_af); flush_workqueue(radio->wqueue_af); } static irqreturn_t silabs_fm_isr(int irq, void *dev_id) Loading Loading @@ -1768,6 +2132,7 @@ static int silabs_fm_fops_open(struct file *file) INIT_DELAYED_WORK(&radio->work, read_int_stat); INIT_DELAYED_WORK(&radio->work_scan, silabs_scan); INIT_DELAYED_WORK(&radio->work_af, silabs_af_tune); INIT_WORK(&radio->rds_worker, rds_handler); init_completion(&radio->sync_req_done); Loading Loading @@ -2242,20 +2607,43 @@ static int silabs_fm_vidioc_s_ctrl(struct file *file, void *priv, case V4L2_CID_PRIVATE_SILABS_LP_MODE: FMDBG("In %s, V4L2_CID_PRIVATE_SILABS_LP_MODE, val is %d\n", __func__, ctrl->value); if (ctrl->value) if (ctrl->value) { /* disable RDS interrupts */ retval = configure_interrupts(radio, ENABLE_STC_INTERRUPTS); else ENABLE_RDS_INTERRUPTS); radio->lp_mode = true; } else { /* enable RDS interrupts */ retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); DISABLE_RDS_INTERRUPTS); radio->lp_mode = false; } if (retval < 0) { FMDERR("In %s, setting low power mode failed %d\n", __func__, retval); goto end; } break; case V4L2_CID_PRIVATE_SILABS_AF_JUMP: FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_JUMP, val is %d\n", __func__, ctrl->value); if (ctrl->value) /* enable RSQ interrupts */ retval = configure_interrupts(radio, ENABLE_RSQ_INTERRUPTS); else /* disable RSQ interrupts */ retval = configure_interrupts(radio, DISABLE_RSQ_INTERRUPTS); if (retval < 0) { FMDERR("%s: setting AF jump mode failed %d\n", __func__, retval); goto end; } /* Save the AF jump state */ radio->is_af_jump_enabled = ctrl->value; break; default: retval = -EINVAL; break; Loading Loading @@ -2475,8 +2863,13 @@ static int silabs_fm_vidioc_s_frequency(struct file *file, void *priv, retval = tune(radio, f); /* save the current frequency if tune is successful. */ if (retval > 0) if (retval > 0) { radio->tuned_freq_khz = f; /* Clear AF list */ reset_af_info(radio); cancel_delayed_work_sync(&radio->work_af); flush_workqueue(radio->wqueue_af); } return retval; } Loading Loading @@ -2511,7 +2904,7 @@ static int silabs_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv, radio->seek_tune_status = SEEK_PENDING; return silabs_seek(radio, dir, WRAP_ENABLE); retval = silabs_seek(radio, dir, WRAP_ENABLE); } else if (radio->g_search_mode == 1) { /* scan */ Loading @@ -2525,6 +2918,13 @@ static int silabs_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv, __func__, radio->g_search_mode); } if (retval > 0) { /* Clear AF list */ reset_af_info(radio); cancel_delayed_work_sync(&radio->work_af); flush_workqueue(radio->wqueue_af); } return retval; } Loading Loading @@ -2839,6 +3239,7 @@ static int silabs_fm_probe(struct i2c_client *client, radio->wqueue = NULL; radio->wqueue_scan = NULL; radio->wqueue_af = NULL; radio->wqueue_rds = NULL; /* video device allocation */ Loading Loading @@ -2875,8 +3276,8 @@ static int silabs_fm_probe(struct i2c_client *client, /* initializing the device count */ atomic_set(&radio->users, 1); /* radio initializes to low power mode */ radio->lp_mode = 1; /* radio initializes to normal mode */ radio->lp_mode = 0; radio->handle_irq = 1; /* init lock */ mutex_init(&radio->lock); Loading Loading @@ -2908,6 +3309,15 @@ static int silabs_fm_probe(struct i2c_client *client, retval = -ENOMEM; goto err_wqueue_scan; } FMDBG("%s: creating work q for af\n", __func__); radio->wqueue_af = create_singlethread_workqueue("sifmradioaf"); if (!radio->wqueue_af) { retval = -ENOMEM; goto err_wqueue_af; } radio->wqueue_rds = create_singlethread_workqueue("sifmradiords"); if (!radio->wqueue_rds) { Loading @@ -2928,6 +3338,8 @@ static int silabs_fm_probe(struct i2c_client *client, err_all: destroy_workqueue(radio->wqueue_rds); err_wqueue_rds: destroy_workqueue(radio->wqueue_af); err_wqueue_af: destroy_workqueue(radio->wqueue_scan); err_wqueue_scan: destroy_workqueue(radio->wqueue); Loading Loading @@ -2972,6 +3384,7 @@ static int silabs_fm_remove(struct i2c_client *client) /* disable irq */ destroy_workqueue(radio->wqueue); destroy_workqueue(radio->wqueue_scan); destroy_workqueue(radio->wqueue_af); destroy_workqueue(radio->wqueue_rds); video_unregister_device(radio->videodev); Loading drivers/media/radio/silabs/radio-silabs.h +47 −3 Original line number Diff line number Diff line Loading @@ -95,6 +95,25 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define PTY_MASK 0x1F #define NO_OF_CHARS_IN_EACH_ADD 2 #define MIN_SNR 0 #define MAX_SNR 127 #define MIN_RSSI 0 #define MAX_RSSI 127 #define DEFAULT_SNR_TH 2 #define DEFAULT_RSSI_TH 7 #define DEFAULT_AF_RSSI_LOW_TH 25 #define MAX_NO_OF_AF 25 #define MIN_AF_FREQ_CODE 1 #define MAX_AF_FREQ_CODE 204 /* 25 AFs supported for a freq. 224 means 1 AF. 225 means 2 AFs and so on */ #define NO_AF_CNT_CODE 224 #define MIN_AF_CNT_CODE 225 #define MAX_AF_CNT_CODE 249 /* freqs are divided by 10. */ #define SCALE_AF_CODE_TO_FREQ_KHZ(x) (87500 + (x*100)) /* commands */ #define POWER_UP_CMD 0x01 #define GET_REV_CMD 0x10 Loading Loading @@ -123,7 +142,8 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define FM_DEEMPHASIS_PROP 0x1100 #define FM_CHANNEL_FILTER_PROP 0x1102 #define FM_ANTENNA_INPUT_PROP 0x1107 #define FM_RSQ_INT_SOURCE_PROP 0x1200 #define FM_RSQ_RSSI_LO_THRESHOLD_PROP 0x1204 #define FM_SEEK_BAND_BOTTOM_PROP 0x1400 #define FM_SEEK_BAND_TOP_PROP 0x1401 Loading @@ -135,6 +155,7 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define FM_RDS_INT_FIFO_COUNT_PROP 0x1501 #define FM_RDS_CONFIG_PROP 0x1502 #define RX_HARD_MUTE_PROP 0x4001 /* BIT MASKS */ #define ENABLE_CTS_INT_MASK (1 << 7) Loading @@ -155,7 +176,10 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define RSQ_INT_BIT_MASK (1 << 3) #define RDS_INT_BIT_MASK (1 << 2) #define STC_INT_BIT_MASK 1 #define RSSI_LOW_TH_INT_BIT_MASK 1 #define RDS_INT_DISABLE_MASK 0x9 #define RSQ_INT_DISABLE_MASK 0x5 #define HARD_MUTE_MASK 0x3 #define DCLK_FALLING_EDGE_MASK (1 << 7) Loading @@ -168,6 +192,7 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define TUNE_FREQ_CMD_LEN 5 #define SEEK_CMD_LEN 2 #define TUNE_STATUS_CMD_LEN 2 #define RSQ_STATUS_CMD_LEN 2 #define HIGH_BYTE_16BIT(x) (x >> 8) #define LOW_BYTE_16BIT(x) (x & 0xFF) Loading Loading @@ -379,7 +404,11 @@ enum silabs_region_t { enum silabs_interrupts_t { DISABLE_ALL_INTERRUPTS, ENABLE_STC_RDS_INTERRUPTS, ENABLE_STC_INTERRUPTS ENABLE_STC_INTERRUPTS, ENABLE_RDS_INTERRUPTS, DISABLE_RDS_INTERRUPTS, ENABLE_RSQ_INTERRUPTS, DISABLE_RSQ_INTERRUPTS }; struct silabs_fm_recv_conf_req { Loading @@ -390,6 +419,21 @@ struct silabs_fm_recv_conf_req { __u16 band_high_limit; }; struct silabs_af_info { /* Flag to identify repeated AF list */ bool is_new_af_list; /* no. of AFs in the list. */ u8 cnt; /* actual size of the list */ u8 size; /* index of currently tuned station in the AF list. */ u8 index; /* freq to which AF list belongs to. */ u32 orig_freq_khz; /* AF list */ u32 af_list[MAX_NO_OF_AF]; }; static inline bool is_valid_chan_spacing(int spacing) { if ((spacing == 0) || Loading Loading
drivers/media/radio/silabs/radio-silabs.c +435 −22 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ struct silabs_fm_device { /* regional settings */ enum silabs_region_t region; /* power mode */ int lp_mode; bool lp_mode; int handle_irq; /* global lock */ struct mutex lock; Loading @@ -91,10 +91,12 @@ struct silabs_fm_device { /* work queue */ struct workqueue_struct *wqueue; struct workqueue_struct *wqueue_scan; struct workqueue_struct *wqueue_af; struct workqueue_struct *wqueue_rds; struct work_struct rds_worker; struct delayed_work work; struct delayed_work work_scan; struct delayed_work work_af; /* wait queue for blocking event read */ wait_queue_head_t event_queue; /* wait queue for raw rds read */ Loading Loading @@ -123,12 +125,17 @@ struct silabs_fm_device { u8 utf_8_flag; u8 rt_ert_flag; u8 formatting_dir; bool is_af_jump_enabled; bool is_af_tune_in_progress; u8 af_rssi_th; /* allowed rssi is 0-127 */ struct silabs_af_info af_info; }; static int silabs_fm_request_irq(struct silabs_fm_device *radio); static int tune(struct silabs_fm_device *radio, u32 freq); static int silabs_seek(struct silabs_fm_device *radio, int dir, int wrap); static int cancel_seek(struct silabs_fm_device *radio); static int configure_interrupts(struct silabs_fm_device *radio, u8 val); static void silabs_fm_q_event(struct silabs_fm_device *radio, enum silabs_evt_t event); Loading Loading @@ -1201,6 +1208,226 @@ static void silabs_raw_rds_handler(struct silabs_fm_device *radio) break; } } static int set_hard_mute(struct silabs_fm_device *radio, bool val) { int retval = 0; if (val == true) { retval = set_property(radio, RX_HARD_MUTE_PROP, HARD_MUTE_MASK); if (retval < 0) FMDERR("%s: set_hard_mute failed with error %d\n", __func__, retval); } else { retval = set_property(radio, RX_HARD_MUTE_PROP, 0); if (retval < 0) FMDERR("%s: set_hard_mute failed with error %d\n", __func__, retval); } return retval; } static int get_rssi(struct silabs_fm_device *radio, u8 *prssi) { int retval = 0; mutex_lock(&radio->lock); memset(radio->write_buf, 0, WRITE_REG_NUM); /* track command that is being sent to chip.*/ radio->cmd = FM_RSQ_STATUS_CMD; radio->write_buf[0] = FM_RSQ_STATUS_CMD; radio->write_buf[1] = 1; retval = send_cmd(radio, RSQ_STATUS_CMD_LEN); if (retval < 0) FMDERR("%s: get_rsq_status failed with error %d\n", __func__, retval); FMDBG("%s: rssi is %d\n", __func__, radio->read_buf[4]); *prssi = radio->read_buf[4]; mutex_unlock(&radio->lock); return retval; } static void reset_af_info(struct silabs_fm_device *radio) { radio->af_info.is_new_af_list = false; radio->af_info.cnt = 0; radio->af_info.index = 0; radio->af_info.size = 0; radio->af_info.orig_freq_khz = 0; memset(radio->af_info.af_list, 0, sizeof(radio->af_info.af_list)); } static void update_af_list(struct silabs_fm_device *radio) { u8 first_byte, sec_byte; u16 af_data = radio->block[2]; first_byte = af_data >> 8; sec_byte = af_data & 0xFF; if (first_byte >= MIN_AF_CNT_CODE && first_byte <= MAX_AF_CNT_CODE) { /* AF count. */ reset_af_info(radio); radio->af_info.cnt = first_byte - NO_AF_CNT_CODE; radio->af_info.orig_freq_khz = radio->tuned_freq_khz; FMDBG("%s: current freq is %u, AF cnt is %u\n", __func__, radio->tuned_freq_khz, radio->af_info.cnt); radio->af_info.af_list[radio->af_info.size++] = SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte); FMDBG("%s: AF is %u\n", __func__, SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte)); } else if (first_byte >= MIN_AF_FREQ_CODE && first_byte <= MAX_AF_FREQ_CODE) { /* update the AF list */ radio->af_info.af_list[radio->af_info.size++] = SCALE_AF_CODE_TO_FREQ_KHZ(first_byte); FMDBG("%s: first AF is %u\n", __func__, SCALE_AF_CODE_TO_FREQ_KHZ(first_byte)); if (radio->af_info.size < radio->af_info.cnt) { radio->af_info.af_list[radio->af_info.size++] = SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte); FMDBG("%s: second AF is %u\n", __func__, SCALE_AF_CODE_TO_FREQ_KHZ(sec_byte)); } } } static void silabs_af_tune(struct work_struct *work) { struct silabs_fm_device *radio; int retval = 0; u8 rssi = 0; u32 freq = 0; radio = container_of(work, struct silabs_fm_device, work_af.work); if (radio->af_info.size == 0) { FMDBG("%s: Empty AF list\n", __func__); radio->is_af_tune_in_progress = false; return; } /* Disable all other interrupts except STC */ retval = configure_interrupts(radio, ENABLE_STC_INTERRUPTS); /* Mute until AF tuning finishes */ retval = set_hard_mute(radio, true); while (1) { if (radio->mode != FM_RECV) { FMDERR("%s: Drv is not in proper state\n", __func__); goto end; } if (radio->seek_tune_status != NO_SEEK_TUNE_PENDING) { FMDBG("%s: manual tune, search issued\n", __func__); break; } if (radio->is_af_jump_enabled != true) { FMDBG("%s: AF jump is disabled\n", __func__); break; } /* If no more AFs left, tune to original frequency and break */ if (radio->af_info.index >= radio->af_info.size) { FMDBG("%s: No more AFs, tuning to original freq %u\n", __func__, radio->af_info.orig_freq_khz); freq = radio->af_info.orig_freq_khz; retval = tune(radio, freq); if (retval < 0) { FMDERR("%s: tune failed, error %d\n", __func__, retval); goto err_tune_fail; } /* wait for tune to finish */ if (!wait_for_completion_timeout(&radio->sync_req_done, msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { FMDERR("%s: didn't receive STC for tune\n", __func__); /* FM is not correct state */ continue; } else FMDBG("%s: received STC for tune\n", __func__); goto err_tune_fail; } freq = radio->af_info.af_list[radio->af_info.index++]; FMDBG("%s: tuning to freq %u\n", __func__, freq); retval = tune(radio, freq); if (retval < 0) { FMDERR("%s: tune failed, error %d\n", __func__, retval); goto err_tune_fail; } /* wait for tune to finish */ if (!wait_for_completion_timeout(&radio->sync_req_done, msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { FMDERR("%s: didn't receive STC for tune\n", __func__); /* FM is not correct state */ continue; } else FMDBG("%s: received STC for tune\n", __func__); retval = get_rssi(radio, &rssi); if (retval < 0) { FMDERR("%s: getting rssi failed\n", __func__); goto err_tune_fail; } if (rssi >= radio->af_rssi_th) { FMDBG("%s: found AF freq(%u) >= AF threshold\n", __func__, freq); /* Notify FM UI about the new freq */ FMDBG("%s: posting TUNE_SUCC event\n", __func__); silabs_fm_q_event(radio, SILABS_EVT_TUNE_SUCC); break; } } err_tune_fail: /* * At this point, we are tuned to either original freq or AF with >= * AF rssi threshold */ reset_af_info(radio); radio->is_af_tune_in_progress = false; /* Clear the stale RDS int bit. */ get_rds_status(radio); retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); /* Clear the stale RSQ int bit. */ get_rssi(radio, &rssi); retval = configure_interrupts(radio, ENABLE_RSQ_INTERRUPTS); end: return; } /* When RDS interrupt is received, read and process RDS data. */ static void rds_handler(struct work_struct *worker) { Loading Loading @@ -1232,6 +1459,7 @@ static void rds_handler(struct work_struct *worker) switch (grp_type) { case RDS_TYPE_0A: update_af_list(radio); /* fall through */ case RDS_TYPE_0B: addr = (radio->block[1] & PS_MASK) * NO_OF_CHARS_IN_EACH_ADD; Loading Loading @@ -1290,15 +1518,16 @@ static int configure_interrupts(struct silabs_fm_device *radio, u8 val) prop_val = 0; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("In %s, error disabling interrupts\n", __func__); FMDERR("%s: error disabling interrupts\n", __func__); break; case ENABLE_STC_RDS_INTERRUPTS: /* enable interrupts. */ /* enable STC and RDS interrupts. */ prop_val = RDS_INT_BIT_MASK | STC_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("In %s, error enabling interrupts\n", __func__); FMDERR("%s: error enabling STC, RDS interrupts\n", __func__); break; case ENABLE_STC_INTERRUPTS: /* enable STC interrupts only. */ Loading @@ -1306,7 +1535,51 @@ static int configure_interrupts(struct silabs_fm_device *radio, u8 val) retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("In %s, error enabling interrupts\n", __func__); FMDERR("%s: error enabling STC interrupts\n", __func__); break; case ENABLE_RDS_INTERRUPTS: /* enable RDS interrupts. */ prop_val = RDS_INT_BIT_MASK | STC_INT_BIT_MASK; if (radio->is_af_jump_enabled) prop_val |= RSQ_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error enabling RDS interrupts\n", __func__); break; case DISABLE_RDS_INTERRUPTS: /* disable RDS interrupts. */ prop_val = STC_INT_BIT_MASK; if (radio->is_af_jump_enabled) prop_val |= RSQ_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error disabling RDS interrupts\n", __func__); break; case ENABLE_RSQ_INTERRUPTS: /* enable RSQ interrupts. */ prop_val = RSQ_INT_BIT_MASK | STC_INT_BIT_MASK; if (radio->lp_mode != true) prop_val |= RDS_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error enabling RSQ interrupts\n", __func__); break; case DISABLE_RSQ_INTERRUPTS: /* disable RSQ interrupts. */ prop_val = STC_INT_BIT_MASK; if (radio->lp_mode != true) prop_val |= RDS_INT_BIT_MASK; retval = set_property(radio, GPO_IEN_PROP, prop_val); if (retval < 0) FMDERR("%s: error disabling RSQ interrupts\n", __func__); break; default: FMDERR("%s: invalid value %u\n", __func__, val); Loading Loading @@ -1358,20 +1631,44 @@ static int initialize_recv(struct silabs_fm_device *radio) { int retval = 0; retval = set_property(radio, FM_SEEK_TUNE_SNR_THRESHOLD_PROP, 2); retval = set_property(radio, FM_SEEK_TUNE_SNR_THRESHOLD_PROP, DEFAULT_SNR_TH); if (retval < 0) { FMDERR("%s: FM_SEEK_TUNE_SNR_THRESHOLD_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } retval = set_property(radio, FM_SEEK_TUNE_RSSI_THRESHOLD_PROP, 7); retval = set_property(radio, FM_SEEK_TUNE_RSSI_THRESHOLD_PROP, DEFAULT_RSSI_TH); if (retval < 0) { FMDERR("%s: FM_SEEK_TUNE_RSSI_THRESHOLD_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } retval = set_property(radio, FM_RSQ_RSSI_LO_THRESHOLD_PROP, DEFAULT_AF_RSSI_LOW_TH); if (retval < 0) { FMDERR("%s: FM_RSQ_RSSI_LO_THRESHOLD_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } radio->af_rssi_th = DEFAULT_AF_RSSI_LOW_TH; retval = set_property(radio, FM_RSQ_INT_SOURCE_PROP, RSSI_LOW_TH_INT_BIT_MASK); if (retval < 0) { FMDERR("%s: FM_RSQ_INT_SOURCE_PROP fail error %d\n", __func__, retval); goto set_prop_fail; } set_prop_fail: return retval; Loading Loading @@ -1630,9 +1927,33 @@ static void silabs_fm_q_event(struct silabs_fm_device *radio, wake_up_interruptible(&radio->event_queue); } static int clear_stc_int(struct silabs_fm_device *radio) { int retval = 0; mutex_lock(&radio->lock); memset(radio->write_buf, 0, WRITE_REG_NUM); /* track command that is being sent to chip. */ radio->cmd = FM_TUNE_STATUS_CMD; radio->write_buf[0] = FM_TUNE_STATUS_CMD; radio->write_buf[1] = INTACK_MASK; retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); if (retval < 0) FMDERR("%s: clear_stc_int fail, error %d\n", __func__, retval); mutex_unlock(&radio->lock); return retval; } static void silabs_interrupts_handler(struct silabs_fm_device *radio) { int retval = 0; u8 rssi = 0; if (unlikely(radio == NULL)) { FMDERR("%s:radio is null", __func__); Loading @@ -1653,11 +1974,7 @@ static void silabs_interrupts_handler(struct silabs_fm_device *radio) FMDBG("%s: successfully read the resp from soc, status byte is %x\n", __func__, radio->read_buf[0]); if (radio->read_buf[0] & RDS_INT_BIT_MASK) { FMDBG("RDS interrupt received\n"); schedule_work(&radio->rds_worker); return; } if (radio->read_buf[0] & STC_INT_BIT_MASK) { FMDBG("%s: STC bit set for cmd %x\n", __func__, radio->cmd); if (radio->seek_tune_status == TUNE_PENDING) { Loading @@ -1682,8 +1999,53 @@ static void silabs_interrupts_handler(struct silabs_fm_device *radio) */ FMDBG("In %s, signalling scan thread\n", __func__); complete(&radio->sync_req_done); } else if (radio->is_af_tune_in_progress == true) { /* * when AF tune is going on and STC int is set, signal * so that AF tune can proceed. */ FMDBG("In %s, signalling AF tune thread\n", __func__); complete(&radio->sync_req_done); } /* clear the STC interrupt. */ clear_stc_int(radio); reset_rds(radio); /* Clear the existing RDS data */ return; } if (radio->read_buf[0] & RSQ_INT_BIT_MASK) { FMDBG("RSQ interrupt received, clearing the RSQ int bit\n"); /* clear RSQ, RDS interrupt bits until AF tune is complete. */ (void)get_rssi(radio, &rssi); (void)get_rds_status(radio); /* Don't process RSQ until AF tune is complete. */ if (radio->is_af_tune_in_progress == true) return; if (radio->is_af_jump_enabled && radio->af_info.size != 0 && rssi <= radio->af_rssi_th) { radio->is_af_tune_in_progress = true; FMDBG("%s: Queuing to AF work Q, freq %u, rssi %u\n", __func__, radio->tuned_freq_khz, rssi); queue_delayed_work(radio->wqueue_af, &radio->work_af, msecs_to_jiffies(SILABS_DELAY_MSEC)); } return; } if (radio->read_buf[0] & RDS_INT_BIT_MASK) { FMDBG("RDS interrupt received\n"); /* Don't process RDS until AF tune is complete. */ if (radio->is_af_tune_in_progress == true) { /* clear RDS int bit and return */ get_rds_status(radio); return; } schedule_work(&radio->rds_worker); return; } return; } Loading @@ -1710,6 +2072,8 @@ static void silabs_fm_disable_irq(struct silabs_fm_device *radio) flush_workqueue(radio->wqueue); cancel_delayed_work_sync(&radio->work_scan); flush_workqueue(radio->wqueue_scan); cancel_delayed_work_sync(&radio->work_af); flush_workqueue(radio->wqueue_af); } static irqreturn_t silabs_fm_isr(int irq, void *dev_id) Loading Loading @@ -1768,6 +2132,7 @@ static int silabs_fm_fops_open(struct file *file) INIT_DELAYED_WORK(&radio->work, read_int_stat); INIT_DELAYED_WORK(&radio->work_scan, silabs_scan); INIT_DELAYED_WORK(&radio->work_af, silabs_af_tune); INIT_WORK(&radio->rds_worker, rds_handler); init_completion(&radio->sync_req_done); Loading Loading @@ -2242,20 +2607,43 @@ static int silabs_fm_vidioc_s_ctrl(struct file *file, void *priv, case V4L2_CID_PRIVATE_SILABS_LP_MODE: FMDBG("In %s, V4L2_CID_PRIVATE_SILABS_LP_MODE, val is %d\n", __func__, ctrl->value); if (ctrl->value) if (ctrl->value) { /* disable RDS interrupts */ retval = configure_interrupts(radio, ENABLE_STC_INTERRUPTS); else ENABLE_RDS_INTERRUPTS); radio->lp_mode = true; } else { /* enable RDS interrupts */ retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); DISABLE_RDS_INTERRUPTS); radio->lp_mode = false; } if (retval < 0) { FMDERR("In %s, setting low power mode failed %d\n", __func__, retval); goto end; } break; case V4L2_CID_PRIVATE_SILABS_AF_JUMP: FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_JUMP, val is %d\n", __func__, ctrl->value); if (ctrl->value) /* enable RSQ interrupts */ retval = configure_interrupts(radio, ENABLE_RSQ_INTERRUPTS); else /* disable RSQ interrupts */ retval = configure_interrupts(radio, DISABLE_RSQ_INTERRUPTS); if (retval < 0) { FMDERR("%s: setting AF jump mode failed %d\n", __func__, retval); goto end; } /* Save the AF jump state */ radio->is_af_jump_enabled = ctrl->value; break; default: retval = -EINVAL; break; Loading Loading @@ -2475,8 +2863,13 @@ static int silabs_fm_vidioc_s_frequency(struct file *file, void *priv, retval = tune(radio, f); /* save the current frequency if tune is successful. */ if (retval > 0) if (retval > 0) { radio->tuned_freq_khz = f; /* Clear AF list */ reset_af_info(radio); cancel_delayed_work_sync(&radio->work_af); flush_workqueue(radio->wqueue_af); } return retval; } Loading Loading @@ -2511,7 +2904,7 @@ static int silabs_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv, radio->seek_tune_status = SEEK_PENDING; return silabs_seek(radio, dir, WRAP_ENABLE); retval = silabs_seek(radio, dir, WRAP_ENABLE); } else if (radio->g_search_mode == 1) { /* scan */ Loading @@ -2525,6 +2918,13 @@ static int silabs_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv, __func__, radio->g_search_mode); } if (retval > 0) { /* Clear AF list */ reset_af_info(radio); cancel_delayed_work_sync(&radio->work_af); flush_workqueue(radio->wqueue_af); } return retval; } Loading Loading @@ -2839,6 +3239,7 @@ static int silabs_fm_probe(struct i2c_client *client, radio->wqueue = NULL; radio->wqueue_scan = NULL; radio->wqueue_af = NULL; radio->wqueue_rds = NULL; /* video device allocation */ Loading Loading @@ -2875,8 +3276,8 @@ static int silabs_fm_probe(struct i2c_client *client, /* initializing the device count */ atomic_set(&radio->users, 1); /* radio initializes to low power mode */ radio->lp_mode = 1; /* radio initializes to normal mode */ radio->lp_mode = 0; radio->handle_irq = 1; /* init lock */ mutex_init(&radio->lock); Loading Loading @@ -2908,6 +3309,15 @@ static int silabs_fm_probe(struct i2c_client *client, retval = -ENOMEM; goto err_wqueue_scan; } FMDBG("%s: creating work q for af\n", __func__); radio->wqueue_af = create_singlethread_workqueue("sifmradioaf"); if (!radio->wqueue_af) { retval = -ENOMEM; goto err_wqueue_af; } radio->wqueue_rds = create_singlethread_workqueue("sifmradiords"); if (!radio->wqueue_rds) { Loading @@ -2928,6 +3338,8 @@ static int silabs_fm_probe(struct i2c_client *client, err_all: destroy_workqueue(radio->wqueue_rds); err_wqueue_rds: destroy_workqueue(radio->wqueue_af); err_wqueue_af: destroy_workqueue(radio->wqueue_scan); err_wqueue_scan: destroy_workqueue(radio->wqueue); Loading Loading @@ -2972,6 +3384,7 @@ static int silabs_fm_remove(struct i2c_client *client) /* disable irq */ destroy_workqueue(radio->wqueue); destroy_workqueue(radio->wqueue_scan); destroy_workqueue(radio->wqueue_af); destroy_workqueue(radio->wqueue_rds); video_unregister_device(radio->videodev); Loading
drivers/media/radio/silabs/radio-silabs.h +47 −3 Original line number Diff line number Diff line Loading @@ -95,6 +95,25 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define PTY_MASK 0x1F #define NO_OF_CHARS_IN_EACH_ADD 2 #define MIN_SNR 0 #define MAX_SNR 127 #define MIN_RSSI 0 #define MAX_RSSI 127 #define DEFAULT_SNR_TH 2 #define DEFAULT_RSSI_TH 7 #define DEFAULT_AF_RSSI_LOW_TH 25 #define MAX_NO_OF_AF 25 #define MIN_AF_FREQ_CODE 1 #define MAX_AF_FREQ_CODE 204 /* 25 AFs supported for a freq. 224 means 1 AF. 225 means 2 AFs and so on */ #define NO_AF_CNT_CODE 224 #define MIN_AF_CNT_CODE 225 #define MAX_AF_CNT_CODE 249 /* freqs are divided by 10. */ #define SCALE_AF_CODE_TO_FREQ_KHZ(x) (87500 + (x*100)) /* commands */ #define POWER_UP_CMD 0x01 #define GET_REV_CMD 0x10 Loading Loading @@ -123,7 +142,8 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define FM_DEEMPHASIS_PROP 0x1100 #define FM_CHANNEL_FILTER_PROP 0x1102 #define FM_ANTENNA_INPUT_PROP 0x1107 #define FM_RSQ_INT_SOURCE_PROP 0x1200 #define FM_RSQ_RSSI_LO_THRESHOLD_PROP 0x1204 #define FM_SEEK_BAND_BOTTOM_PROP 0x1400 #define FM_SEEK_BAND_TOP_PROP 0x1401 Loading @@ -135,6 +155,7 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define FM_RDS_INT_FIFO_COUNT_PROP 0x1501 #define FM_RDS_CONFIG_PROP 0x1502 #define RX_HARD_MUTE_PROP 0x4001 /* BIT MASKS */ #define ENABLE_CTS_INT_MASK (1 << 7) Loading @@ -155,7 +176,10 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define RSQ_INT_BIT_MASK (1 << 3) #define RDS_INT_BIT_MASK (1 << 2) #define STC_INT_BIT_MASK 1 #define RSSI_LOW_TH_INT_BIT_MASK 1 #define RDS_INT_DISABLE_MASK 0x9 #define RSQ_INT_DISABLE_MASK 0x5 #define HARD_MUTE_MASK 0x3 #define DCLK_FALLING_EDGE_MASK (1 << 7) Loading @@ -168,6 +192,7 @@ const unsigned char MAX_SRCH_MODE = 0x01; #define TUNE_FREQ_CMD_LEN 5 #define SEEK_CMD_LEN 2 #define TUNE_STATUS_CMD_LEN 2 #define RSQ_STATUS_CMD_LEN 2 #define HIGH_BYTE_16BIT(x) (x >> 8) #define LOW_BYTE_16BIT(x) (x & 0xFF) Loading Loading @@ -379,7 +404,11 @@ enum silabs_region_t { enum silabs_interrupts_t { DISABLE_ALL_INTERRUPTS, ENABLE_STC_RDS_INTERRUPTS, ENABLE_STC_INTERRUPTS ENABLE_STC_INTERRUPTS, ENABLE_RDS_INTERRUPTS, DISABLE_RDS_INTERRUPTS, ENABLE_RSQ_INTERRUPTS, DISABLE_RSQ_INTERRUPTS }; struct silabs_fm_recv_conf_req { Loading @@ -390,6 +419,21 @@ struct silabs_fm_recv_conf_req { __u16 band_high_limit; }; struct silabs_af_info { /* Flag to identify repeated AF list */ bool is_new_af_list; /* no. of AFs in the list. */ u8 cnt; /* actual size of the list */ u8 size; /* index of currently tuned station in the AF list. */ u8 index; /* freq to which AF list belongs to. */ u32 orig_freq_khz; /* AF list */ u32 af_list[MAX_NO_OF_AF]; }; static inline bool is_valid_chan_spacing(int spacing) { if ((spacing == 0) || Loading