radio: iris: Add support for RT Plus, eRT feature.

- Allow Soc to interrupt host for RT Plus, eRT data
 - Parse the RDS group for RT Plus, eRT
 - Queue the parsed data and event for RT Plus, eRT
   to upper layer
CRs-fixed: 419248

Signed-off-by: Ayaz Ahmad <aahmad@codeaurora.org>

(cherry picked from commit 89265111440674132f59c3aa2938a0aefbfc84fb)

Change-Id: I44010cebd5d7de7ae3a4b076971d24d65aa4fb91
Signed-off-by: Sudhir Sharma <sudsha@codeaurora.org>
This commit is contained in:
Ayaz Ahmad 2012-10-05 19:39:11 +05:30 committed by Stephen Boyd
parent 8d0d063173
commit 124d6f7020
3 changed files with 339 additions and 8 deletions

View file

@ -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 @@ struct iris_device {
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 @@ static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev,
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_search_station_list
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 @@ static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev,
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 @@ void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb)
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 @@ static int iris_vidioc_s_ctrl(struct file *file, void *priv,
}
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;

View file

@ -363,6 +363,53 @@ struct hci_fm_ch_det_threshold {
#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 @@ struct hci_ev_tune_status {
__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 @@ enum iris_evt_t {
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 @@ enum iris_region_t {
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 @@ enum iris_buf_t {
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 {

View file

@ -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)