diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c index 53eb85ccf62..23d11c30854 100644 --- a/drivers/media/radio/radio-iris.c +++ b/drivers/media/radio/radio-iris.c @@ -88,6 +88,7 @@ struct iris_device { unsigned char power_mode; int search_on; unsigned int tone_freq; + unsigned char spur_table_size; unsigned char g_scan_time; unsigned int g_antenna; unsigned int g_rds_grp_proc_ps; @@ -101,11 +102,13 @@ struct iris_device { struct hci_fm_sig_threshold_rsp sig_th; struct hci_fm_ch_det_threshold ch_det_threshold; struct hci_fm_data_rd_rsp default_data; + struct hci_fm_spur_data spur_data; }; static struct video_device *priv_videodev; static int iris_do_calibration(struct iris_device *radio); +static int update_spur_table(struct iris_device *radio); static struct v4l2_queryctrl iris_v4l2_queryctrl[] = { { .id = V4L2_CID_AUDIO_VOLUME, @@ -2896,6 +2899,7 @@ static int iris_vidioc_s_ctrl(struct file *file, void *priv, FMDERR("get frequency failed %d\n", retval); break; case FM_OFF: + radio->spur_table_size = 0; switch (radio->mode) { case FM_RECV: retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD, @@ -3248,12 +3252,116 @@ static int iris_vidioc_s_ctrl(struct file *file, void *priv, */ retval = 0; break; + case V4L2_CID_PRIVATE_SPUR_FREQ: + if (radio->spur_table_size >= MAX_SPUR_FREQ_LIMIT) { + FMDERR("%s: Spur Table Full!\n", __func__); + retval = -1; + } else + radio->spur_data.freq[radio->spur_table_size] = + ctrl->value; + break; + case V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI: + if (radio->spur_table_size >= MAX_SPUR_FREQ_LIMIT) { + FMDERR("%s: Spur Table Full!\n", __func__); + retval = -1; + } else + radio->spur_data.rmssi[radio->spur_table_size] = + ctrl->value; + break; + case V4L2_CID_PRIVATE_SPUR_SELECTION: + if (radio->spur_table_size >= MAX_SPUR_FREQ_LIMIT) { + FMDERR("%s: Spur Table Full!\n", __func__); + retval = -1; + } else { + radio->spur_data.enable[radio->spur_table_size] = + ctrl->value; + radio->spur_table_size++; + } + break; + case V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE: + update_spur_table(radio); + break; default: retval = -EINVAL; } return retval; } +static int update_spur_table(struct iris_device *radio) +{ + struct hci_fm_def_data_wr_req default_data; + int len = 0, index = 0, offset = 0, i = 0; + int retval = 0, temp = 0, cnt = 0; + + memset(&default_data, 0, sizeof(default_data)); + + /* Pass the mode of SPUR_CLK */ + default_data.mode = CKK_SPUR; + + temp = radio->spur_table_size; + for (cnt = 0; cnt < (temp / 5); cnt++) { + offset = 0; + /* + * Program the spur entries in spur table in following order: + * Spur index + * Length of the spur data + * Spur Data: + * MSB of the spur frequency + * LSB of the spur frequency + * Enable/Disable the spur frequency + * RMSSI value of the spur frequency + */ + default_data.data[offset++] = ENTRY_0 + cnt; + for (i = 0; i < SPUR_ENTRIES_PER_ID; i++) { + default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( + radio->spur_data.freq[index]), 0); + default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( + radio->spur_data.freq[index]), 1); + default_data.data[offset++] = + radio->spur_data.enable[index]; + default_data.data[offset++] = + radio->spur_data.rmssi[index]; + index++; + } + len = (SPUR_ENTRIES_PER_ID * SPUR_DATA_SIZE); + default_data.length = (len + 1); + retval = hci_def_data_write(&default_data, radio->fm_hdev); + if (retval < 0) { + FMDBG("%s: Failed to configure entries for ID : %d\n", + __func__, default_data.data[0]); + return retval; + } + } + + /* Compute balance SPUR frequencies to be programmed */ + temp %= SPUR_ENTRIES_PER_ID; + if (temp > 0) { + offset = 0; + default_data.data[offset++] = (radio->spur_table_size / 5); + for (i = 0; i < temp; i++) { + default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( + radio->spur_data.freq[index]), 0); + default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( + radio->spur_data.freq[index]), 1); + default_data.data[offset++] = + radio->spur_data.enable[index]; + default_data.data[offset++] = + radio->spur_data.rmssi[index]; + index++; + } + len = (temp * SPUR_DATA_SIZE); + default_data.length = (len + 1); + retval = hci_def_data_write(&default_data, radio->fm_hdev); + if (retval < 0) { + FMDERR("%s: Failed to configure entries for ID : %d\n", + __func__, default_data.data[0]); + return retval; + } + } + + return retval; +} + static int iris_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h index b5e8f2e223a..25a1d84a816 100644 --- a/include/media/radio-iris.h +++ b/include/media/radio-iris.h @@ -489,6 +489,29 @@ struct hci_fm_dbg_param_rsp { __u8 in_det_out; } __packed; +#define CLKSPURID_INDEX0 0 +#define CLKSPURID_INDEX1 5 +#define CLKSPURID_INDEX2 10 +#define CLKSPURID_INDEX3 15 +#define CLKSPURID_INDEX4 20 +#define CLKSPURID_INDEX5 25 + +#define MAX_SPUR_FREQ_LIMIT 30 +#define CKK_SPUR 0x3B +#define SPUR_DATA_SIZE 0x4 +#define SPUR_ENTRIES_PER_ID 0x5 + +#define COMPUTE_SPUR(val) ((((val) - (76000)) / (50))) +#define GET_FREQ(val, bit) ((bit == 1) ? ((val) >> 8) : ((val) & 0xFF)) +#define GET_SPUR_ENTRY_LEVEL(val) ((val) / (5)) + +struct hci_fm_spur_data { + __u32 freq[MAX_SPUR_FREQ_LIMIT]; + __s8 rmssi[MAX_SPUR_FREQ_LIMIT]; + __u8 enable[MAX_SPUR_FREQ_LIMIT]; +} __packed; + + /* HCI dev events */ #define RADIO_HCI_DEV_REG 1 #define RADIO_HCI_DEV_WRITE 2 @@ -572,6 +595,10 @@ enum v4l2_cid_private_iris_t { V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, V4L2_CID_PRIVATE_SINR_THRESHOLD, V4L2_CID_PRIVATE_SINR_SAMPLES, + V4L2_CID_PRIVATE_SPUR_FREQ, + V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI, + V4L2_CID_PRIVATE_SPUR_SELECTION, + V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE, /*using private CIDs under userclass*/ V4L2_CID_PRIVATE_IRIS_READ_DEFAULT = 0x00980928, @@ -680,6 +707,14 @@ enum search_t { RDS_AF_JUMP, }; +enum spur_entry_levels { + ENTRY_0, + ENTRY_1, + ENTRY_2, + ENTRY_3, + ENTRY_4, + ENTRY_5, +}; /* Band limits */ #define REGION_US_EU_BAND_LOW 87500 @@ -774,6 +809,7 @@ struct hci_cc_do_calibration_rsp { #define RDS_SYNC_INTR (1 << 1) #define AUDIO_CTRL_INTR (1 << 2) #define AF_JUMP_ENABLE (1 << 4) + int hci_def_data_read(struct hci_fm_def_data_rd_req *arg, struct radio_hci_dev *hdev); int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,