Merge "radio: iris: Add support for RT Plus, eRT feature"
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index ac143b1..fde7cb7 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -39,6 +39,17 @@
#include <asm/unaligned.h>
static unsigned int rds_buf = 100;
+static int oda_agt;
+static int grp_mask;
+static int rt_plus_carrier = -1;
+static int ert_carrier = -1;
+static unsigned char ert_buf[256];
+static unsigned char ert_len;
+static unsigned char c_byt_pair_index;
+static char utf_8_flag;
+static char rt_ert_flag;
+static char formatting_dir;
+
module_param(rds_buf, uint, 0);
MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
@@ -108,7 +119,11 @@
static struct video_device *priv_videodev;
static int iris_do_calibration(struct iris_device *radio);
-
+static void hci_buff_ert(struct iris_device *radio,
+ struct rds_grp_data *rds_buf);
+static void hci_ev_rt_plus(struct iris_device *radio,
+ struct rds_grp_data rds_buf);
+static void hci_ev_ert(struct iris_device *radio);
static int update_spur_table(struct iris_device *radio);
static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
{
@@ -921,6 +936,20 @@
return radio_hci_send_cmd(hdev, opcode, 0, NULL);
}
+static int hci_fm_rds_grp_mask_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ struct hci_fm_rds_grp_req *fm_grp_mask =
+ (struct hci_fm_rds_grp_req *)param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_RDS_GRP);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(*fm_grp_mask),
+ fm_grp_mask);
+}
+
static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
unsigned long param)
{
@@ -1313,7 +1342,13 @@
static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
struct radio_hci_dev *hdev)
{
- return 0;
+ int ret = 0;
+ struct hci_fm_rds_grp_req *fm_grp_mask = arg;
+
+ ret = radio_hci_request(hdev, hci_fm_rds_grp_mask_req, (unsigned
+ long)fm_grp_mask, RADIO_HCI_TIMEOUT);
+
+ return ret;
}
static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
@@ -2078,6 +2113,234 @@
iris_q_event(radio, IRIS_EVT_MONO);
}
+static void hci_ev_raw_rds_group_data(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct iris_device *radio;
+ unsigned char blocknum, index;
+ struct rds_grp_data temp;
+ unsigned int mask_bit;
+ unsigned short int aid, agt, gtc;
+ unsigned short int carrier;
+
+ radio = video_get_drvdata(video_get_dev());
+ index = RDSGRP_DATA_OFFSET;
+
+ for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) {
+ temp.rdsBlk[blocknum].rdsLsb =
+ (skb->data[index]);
+ temp.rdsBlk[blocknum].rdsMsb =
+ (skb->data[index+1]);
+ index = index + 2;
+ }
+
+ aid = AID(temp.rdsBlk[3].rdsLsb, temp.rdsBlk[3].rdsMsb);
+ gtc = GTC(temp.rdsBlk[1].rdsMsb);
+ agt = AGT(temp.rdsBlk[1].rdsLsb);
+
+ if (gtc == GRP_3A) {
+ switch (aid) {
+ case ERT_AID:
+ /* calculate the grp mask for RDS grp
+ * which will contain actual eRT text
+ *
+ * Bit Pos 0 1 2 3 4 5 6 7
+ * Grp Type 0A 0B 1A 1B 2A 2B 3A 3B
+ *
+ * similary for rest grps
+ */
+ mask_bit = (((agt >> 1) << 1) + (agt & 1));
+ oda_agt = (1 << mask_bit);
+ utf_8_flag = (temp.rdsBlk[2].rdsLsb & 1);
+ formatting_dir = EXTRACT_BIT(temp.rdsBlk[2].rdsLsb,
+ ERT_FORMAT_DIR_BIT);
+ if (ert_carrier != agt)
+ iris_q_event(radio, IRIS_EVT_NEW_ODA);
+ ert_carrier = agt;
+ break;
+ case RT_PLUS_AID:
+ /* calculate the grp mask for RDS grp
+ * which will contain actual eRT text
+ *
+ * Bit Pos 0 1 2 3 4 5 6 7
+ * Grp Type 0A 0B 1A 1B 2A 2B 3A 3B
+ *
+ * similary for rest grps
+ */
+ mask_bit = (((agt >> 1) << 1) + (agt & 1));
+ oda_agt = (1 << mask_bit);
+ /*Extract 5th bit of MSB (b7b6b5b4b3b2b1b0)*/
+ rt_ert_flag = EXTRACT_BIT(temp.rdsBlk[2].rdsMsb,
+ RT_ERT_FLAG_BIT);
+ if (rt_plus_carrier != agt)
+ iris_q_event(radio, IRIS_EVT_NEW_ODA);
+ rt_plus_carrier = agt;
+ break;
+ default:
+ oda_agt = 0;
+ break;
+ }
+ } else {
+ carrier = gtc;
+ if ((carrier == rt_plus_carrier))
+ hci_ev_rt_plus(radio, temp);
+ else if (carrier == ert_carrier)
+ hci_buff_ert(radio, &temp);
+ }
+}
+
+static void hci_buff_ert(struct iris_device *radio,
+ struct rds_grp_data *rds_buf)
+{
+ int i;
+ unsigned short int info_byte = 0;
+ unsigned short int byte_pair_index;
+
+ byte_pair_index = AGT(rds_buf->rdsBlk[1].rdsLsb);
+ if (byte_pair_index == 0) {
+ c_byt_pair_index = 0;
+ ert_len = 0;
+ }
+ if (c_byt_pair_index == byte_pair_index) {
+ c_byt_pair_index++;
+ for (i = 2; i <= 3; i++) {
+ info_byte = rds_buf->rdsBlk[i].rdsLsb;
+ info_byte |= (rds_buf->rdsBlk[i].rdsMsb << 8);
+ ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsMsb;
+ ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsLsb;
+ if ((utf_8_flag == 0)
+ && (info_byte == CARRIAGE_RETURN)) {
+ ert_len -= 2;
+ break;
+ } else if ((utf_8_flag == 1)
+ &&
+ (rds_buf->rdsBlk[i].rdsMsb
+ == CARRIAGE_RETURN)) {
+ info_byte = CARRIAGE_RETURN;
+ ert_len -= 2;
+ break;
+ } else if ((utf_8_flag == 1)
+ &&
+ (rds_buf->rdsBlk[i].rdsLsb
+ == CARRIAGE_RETURN)) {
+ info_byte = CARRIAGE_RETURN;
+ ert_len--;
+ break;
+ }
+ }
+ if ((byte_pair_index == MAX_ERT_SEGMENT) ||
+ (info_byte == CARRIAGE_RETURN)) {
+ hci_ev_ert(radio);
+ c_byt_pair_index = 0;
+ ert_len = 0;
+ }
+ } else {
+ ert_len = 0;
+ c_byt_pair_index = 0;
+ }
+}
+static void hci_ev_ert(struct iris_device *radio)
+
+{
+ char *data = NULL;
+
+ if (ert_len <= 0)
+ return;
+ data = kmalloc((ert_len + 3), GFP_ATOMIC);
+ if (data != NULL) {
+ data[0] = ert_len;
+ data[1] = utf_8_flag;
+ data[2] = formatting_dir;
+ memcpy((data + 3), ert_buf, ert_len);
+ iris_q_evt_data(radio, data, (ert_len + 3), IRIS_BUF_ERT);
+ iris_q_event(radio, IRIS_EVT_NEW_ERT);
+ kfree(data);
+ }
+}
+
+static void hci_ev_rt_plus(struct iris_device *radio,
+ struct rds_grp_data rds_buf)
+{
+ char tag_type1, tag_type2;
+ char *data = NULL;
+ int len = 0;
+ unsigned short int agt;
+
+ agt = AGT(rds_buf.rdsBlk[1].rdsLsb);
+ /*right most 3 bits of Lsb of block 2
+ * and left most 3 bits of Msb of block 3
+ */
+ tag_type1 = (((agt & TAG1_MSB_MASK) << TAG1_MSB_OFFSET) |
+ (rds_buf.rdsBlk[2].rdsMsb >> TAG1_LSB_OFFSET));
+
+ /*right most 1 bit of lsb of 3rd block
+ * and left most 5 bits of Msb of 4th block
+ */
+ tag_type2 = (((rds_buf.rdsBlk[2].rdsLsb & TAG2_MSB_MASK)
+ << TAG2_MSB_OFFSET) |
+ (rds_buf.rdsBlk[3].rdsMsb >> TAG2_LSB_OFFSET));
+
+ if (tag_type1 != DUMMY_CLASS)
+ len += RT_PLUS_LEN_1_TAG;
+ if (tag_type2 != DUMMY_CLASS)
+ len += RT_PLUS_LEN_1_TAG;
+
+ if (len != 0) {
+ len += 2;
+ data = kmalloc(len, GFP_ATOMIC);
+ } else {
+ FMDERR("Len is zero\n");
+ return ;
+ }
+ if (data != NULL) {
+ data[0] = len;
+ len = 1;
+ data[len++] = rt_ert_flag;
+ if (tag_type1 != DUMMY_CLASS) {
+ data[len++] = tag_type1;
+ /*start position of tag1
+ *right most 5 bits of msb of 3rd block
+ *and left most bit of lsb of 3rd block
+ */
+ data[len++] = (((rds_buf.rdsBlk[2].rdsMsb &
+ TAG1_POS_MSB_MASK)
+ << TAG1_POS_MSB_OFFSET)
+ |
+ (rds_buf.rdsBlk[2].rdsLsb >>
+ TAG1_POS_LSB_OFFSET));
+ /*length of tag1
+ *left most 6 bits of lsb of 3rd block
+ */
+ data[len++] = ((rds_buf.rdsBlk[2].rdsLsb
+ >> TAG1_LEN_OFFSET)
+ &
+ TAG1_LEN_MASK) + 1;
+ }
+ if (tag_type2 != DUMMY_CLASS) {
+ data[len++] = tag_type2;
+ /*start position of tag2
+ *right most 3 bit of msb of 4th block
+ *and left most 3 bits of lsb of 4th block
+ */
+ data[len++] = (((rds_buf.rdsBlk[3].rdsMsb
+ & TAG2_POS_MSB_MASK)
+ << TAG2_POS_MSB_OFFSET)
+ |
+ (rds_buf.rdsBlk[3].rdsLsb
+ >> TAG2_POS_LSB_OFFSET));
+ /*length of tag2
+ *right most 5 bits of lsb of 4th block
+ */
+ data[len++] = (rds_buf.rdsBlk[3].rdsLsb
+ & TAG2_LEN_MASK) + 1;
+ }
+ iris_q_evt_data(radio, data, len, IRIS_BUF_RT_PLUS);
+ iris_q_event(radio, IRIS_EVT_NEW_RT_PLUS);
+ kfree(data);
+ } else {
+ FMDERR("memory allocation failed\n");
+ }
+}
static inline void hci_ev_program_service(struct radio_hci_dev *hdev,
struct sk_buff *skb)
@@ -2217,6 +2480,7 @@
hci_ev_service_available(hdev, skb);
break;
case HCI_EV_RDS_RX_DATA:
+ hci_ev_raw_rds_group_data(hdev, skb);
break;
case HCI_EV_PROGRAM_SERVICE:
hci_ev_program_service(hdev, skb);
@@ -2984,8 +3248,13 @@
}
break;
case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
- radio->rds_grp.rds_grp_enable_mask = ctrl->value;
+ grp_mask = (grp_mask | oda_agt | ctrl->value);
+ radio->rds_grp.rds_grp_enable_mask = grp_mask;
+ radio->rds_grp.rds_buf_size = 1;
+ radio->rds_grp.en_rds_change_filter = 0;
retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("error in setting group mask\n");
break;
case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index db69518..0efeff4 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -363,6 +363,53 @@
#define HCI_REQ_CANCELED 2
#define HCI_REQ_STATUS 3
+#define MAX_RAW_RDS_GRPS 21
+
+#define RDSGRP_DATA_OFFSET 0x1
+
+/*RT PLUS*/
+#define DUMMY_CLASS 0
+#define RT_PLUS_LEN_1_TAG 3
+#define RT_ERT_FLAG_BIT 5
+
+/*TAG1*/
+#define TAG1_MSB_OFFSET 3
+#define TAG1_MSB_MASK 7
+#define TAG1_LSB_OFFSET 5
+#define TAG1_POS_MSB_MASK 31
+#define TAG1_POS_MSB_OFFSET 1
+#define TAG1_POS_LSB_OFFSET 7
+#define TAG1_LEN_OFFSET 1
+#define TAG1_LEN_MASK 63
+
+/*TAG2*/
+#define TAG2_MSB_OFFSET 5
+#define TAG2_MSB_MASK 1
+#define TAG2_LSB_OFFSET 3
+#define TAG2_POS_MSB_MASK 7
+#define TAG2_POS_MSB_OFFSET 3
+#define TAG2_POS_LSB_OFFSET 5
+#define TAG2_LEN_MASK 31
+
+#define AGT_MASK 31
+/*Extract 5 left most bits of lsb of 2nd block*/
+#define AGT(x) (x & AGT_MASK)
+/*16 bits of 4th block*/
+#define AID(lsb, msb) ((msb << 8) | (lsb))
+/*Extract 5 right most bits of msb of 2nd block*/
+#define GTC(blk2msb) (blk2msb >> 3)
+
+#define GRP_3A 0x6
+#define RT_PLUS_AID 0x4bd7
+
+/*ERT*/
+#define ERT_AID 0x6552
+#define CARRIAGE_RETURN 0x000D
+#define MAX_ERT_SEGMENT 31
+#define ERT_FORMAT_DIR_BIT 1
+
+#define EXTRACT_BIT(data, bit_pos) ((data & (1 << bit_pos)) >> bit_pos)
+
struct hci_ev_tune_status {
__u8 sub_event;
__le32 station_freq;
@@ -375,9 +422,19 @@
__u8 intf_det_th;
} __packed;
+struct rds_blk_data {
+ __u8 rdsMsb;
+ __u8 rdsLsb;
+ __u8 blockStatus;
+} __packed;
+
+struct rds_grp_data {
+ struct rds_blk_data rdsBlk[4];
+} __packed;
+
struct hci_ev_rds_rx_data {
__u8 num_rds_grps;
- __u8 rds_grp_data[12];
+ struct rds_grp_data rds_grp_data[MAX_RAW_RDS_GRPS];
} __packed;
struct hci_ev_prg_service {
@@ -628,7 +685,10 @@
IRIS_EVT_NEW_AF_LIST,
IRIS_EVT_TXRDSDAT,
IRIS_EVT_TXRDSDONE,
- IRIS_EVT_RADIO_DISABLED
+ IRIS_EVT_RADIO_DISABLED,
+ IRIS_EVT_NEW_ODA,
+ IRIS_EVT_NEW_RT_PLUS,
+ IRIS_EVT_NEW_ERT,
};
enum emphasis_type {
FM_RX_EMP75 = 0x0,
@@ -660,7 +720,7 @@
IRIS_REGION_OTHER
};
-#define STD_BUF_SIZE (128)
+#define STD_BUF_SIZE (256)
enum iris_buf_t {
IRIS_BUF_SRCH_LIST,
@@ -674,7 +734,9 @@
IRIS_BUF_RDS_CNTRS,
IRIS_BUF_RD_DEFAULT,
IRIS_BUF_CAL_DATA,
- IRIS_BUF_MAX
+ IRIS_BUF_RT_PLUS,
+ IRIS_BUF_ERT,
+ IRIS_BUF_MAX,
};
enum iris_xfr_t {
diff --git a/include/media/tavarua.h b/include/media/tavarua.h
index 1cccb2b..881b851 100644
--- a/include/media/tavarua.h
+++ b/include/media/tavarua.h
@@ -52,7 +52,7 @@
#define SRCH_MASK (1 << SRCH200KHZ_OFFSET)
/* Standard buffer size */
-#define STD_BUF_SIZE (128)
+#define STD_BUF_SIZE (256)
/* Search direction */
#define SRCH_DIR_UP (0)
#define SRCH_DIR_DOWN (1)