radio: iris: adding support for TX functionality

This patch adds the functionality for FM enable, disable,
tuning, configuration and RDS functionality for FM TX

Signed-off-by: Ankur Nandwani <ankurn@codeaurora.org>
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 2194b48..2d208c3 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -55,6 +55,11 @@
 	int xfr_in_progress;
 	struct completion sync_xfr_start;
 	int tune_req;
+	unsigned int mode;
+
+	__u16 pi;
+	__u8 pty;
+	__u8 ps_repeatcount;
 
 	struct video_device *videodev;
 
@@ -75,8 +80,10 @@
 	struct hci_fm_search_rds_station_req srch_rds;
 	struct hci_fm_search_station_list_req srch_st_list;
 	struct hci_fm_recv_conf_req recv_conf;
+	struct hci_fm_trans_conf_req_struct trans_conf;
 	struct hci_fm_rds_grp_req rds_grp;
 	unsigned char g_search_mode;
+	unsigned int tone_freq;
 	unsigned char g_scan_time;
 	unsigned int g_antenna;
 	unsigned int g_rds_grp_proc_ps;
@@ -548,6 +555,28 @@
 	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
 }
 
+static int hci_fm_tone_generator(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
+		HCI_FM_SET_INTERNAL_TONE_GENRATOR);
+	return radio_hci_send_cmd(hdev, opcode,
+			sizeof(radio->tone_freq), &radio->tone_freq);
+}
+
+static int hci_fm_enable_trans_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_ENABLE_TRANS_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
 static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev,
 	unsigned long param)
 {
@@ -558,6 +587,16 @@
 	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
 }
 
+static int hci_fm_disable_trans_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_DISABLE_TRANS_REQ);
+	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
 static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev,
 	unsigned long param)
 {
@@ -582,6 +621,20 @@
 		recv_conf_req);
 }
 
+static int hci_set_fm_trans_conf_req(struct radio_hci_dev *hdev,
+	unsigned long param)
+{
+	__u16 opcode = 0;
+
+	struct hci_fm_trans_conf_req_struct *trans_conf_req =
+		(struct hci_fm_trans_conf_req_struct *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_SET_TRANS_CONF_REQ);
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*trans_conf_req)),
+		trans_conf_req);
+}
+
 static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev,
 		unsigned long param)
 {
@@ -605,6 +658,35 @@
 		mute_mode_req);
 }
 
+
+static int hci_trans_ps_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_tx_ps *tx_ps_req =
+		(struct hci_fm_tx_ps *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_RDS_PS_REQ);
+
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_ps_req)),
+		tx_ps_req);
+}
+
+static int hci_trans_rt_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	__u16 opcode = 0;
+	struct hci_fm_tx_rt *tx_rt_req =
+		(struct hci_fm_tx_rt *) param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
+		HCI_OCF_FM_RDS_RT_REQ);
+
+	return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_rt_req)),
+		tx_rt_req);
+}
+
 static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev,
 		unsigned long param)
 {
@@ -973,6 +1055,18 @@
 	return ret;
 }
 
+static int hci_set_fm_trans_conf(struct hci_fm_trans_conf_req_struct *arg,
+		struct radio_hci_dev *hdev)
+{
+	int ret = 0;
+	struct hci_fm_trans_conf_req_struct *set_trans_conf = arg;
+
+	ret = radio_hci_request(hdev, hci_set_fm_trans_conf_req, (unsigned
+		long)set_trans_conf, RADIO_HCI_TIMEOUT);
+
+	return ret;
+}
+
 static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev)
 {
 	int ret = 0;
@@ -1184,6 +1278,9 @@
 	int ret = 0;
 	unsigned long arg = 0;
 
+	if (!hdev)
+		return -ENODEV;
+
 	switch (cmd) {
 	case HCI_FM_ENABLE_RECV_CMD:
 		ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg,
@@ -1250,6 +1347,16 @@
 			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
 		break;
 
+	case HCI_FM_ENABLE_TRANS_CMD:
+		ret = radio_hci_request(hdev, hci_fm_enable_trans_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
+	case HCI_FM_DISABLE_TRANS_CMD:
+		ret = radio_hci_request(hdev, hci_fm_disable_trans_req, arg,
+			msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
+
 	default:
 		ret = -EINVAL;
 		break;
@@ -1322,6 +1429,22 @@
 	radio_hci_req_complete(hdev, rsp->status);
 }
 
+
+static void hci_cc_fm_trans_set_conf_rsp(struct radio_hci_dev *hdev,
+	struct sk_buff *skb)
+{
+	struct hci_fm_conf_rsp  *rsp = (void *)skb->data;
+	struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+	if (rsp->status)
+		return;
+
+	iris_q_event(radio, HCI_EV_CMD_COMPLETE);
+
+	radio_hci_req_complete(hdev, rsp->status);
+}
+
+
 static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev,
 		struct sk_buff *skb)
 {
@@ -1490,11 +1613,16 @@
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
 	case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
 	case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
 	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
 	case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
 	case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
+	case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR):
 		hci_cc_rsp(hdev, skb);
 		break;
 
@@ -1534,6 +1662,10 @@
 		hci_cc_dbg_param_rsp(hdev, skb);
 		break;
 
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_SET_TRANS_CONF_REQ):
+		hci_cc_fm_trans_set_conf_rsp(hdev, skb);
+		break;
+
 	default:
 		FMDERR("%s opcode 0x%x", hdev->name, opcode);
 		break;
@@ -1812,52 +1944,42 @@
 	return retval;
 }
 
-static int iris_set_region(struct iris_device *radio, int req_region)
+static int iris_recv_set_region(struct iris_device *radio, int req_region)
 {
 	int retval;
 	radio->region = req_region;
 
 	switch (radio->region) {
 	case IRIS_REGION_US:
-		{
-			radio->recv_conf.band_low_limit = 88100;
-			radio->recv_conf.band_high_limit = 108000;
-			radio->recv_conf.emphasis = 0;
-			radio->recv_conf.hlsi = 0;
-			radio->recv_conf.ch_spacing = 0;
-			radio->recv_conf.rds_std = 0;
-		}
+		radio->recv_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
 		break;
 	case IRIS_REGION_EU:
-		{
-			radio->recv_conf.band_low_limit = 88100;
-			radio->recv_conf.band_high_limit = 108000;
-			radio->recv_conf.emphasis = 0;
-			radio->recv_conf.hlsi = 0;
-			radio->recv_conf.ch_spacing = 0;
-			radio->recv_conf.rds_std = 0;
-		}
+		radio->recv_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
 		break;
 	case IRIS_REGION_JAPAN:
-		{
-			radio->recv_conf.band_low_limit = 76000;
-			radio->recv_conf.band_high_limit = 108000;
-			radio->recv_conf.emphasis = 0;
-			radio->recv_conf.hlsi = 0;
-			radio->recv_conf.ch_spacing = 0;
-		}
+		radio->recv_conf.band_low_limit =
+			REGION_JAPAN_STANDARD_BAND_HIGH;
+		radio->recv_conf.band_high_limit =
+			REGION_JAPAN_STANDARD_BAND_LOW;
+		break;
+	case IRIS_REGION_JAPAN_WIDE:
+		radio->recv_conf.band_low_limit =
+			REGION_JAPAN_WIDE_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_JAPAN_WIDE_BAND_HIGH;
 		break;
 	default:
-		{
-			radio->recv_conf.emphasis = 0;
-			radio->recv_conf.hlsi = 0;
-			radio->recv_conf.ch_spacing = 0;
-			radio->recv_conf.rds_std = 0;
-		}
+		/* The user specifies the value.
+		   So nothing needs to be done */
 		break;
 	}
 
-
 	retval = hci_set_fm_recv_conf(
 			&radio->recv_conf,
 			radio->fm_hdev);
@@ -1865,6 +1987,47 @@
 	return retval;
 }
 
+
+static int iris_trans_set_region(struct iris_device *radio, int req_region)
+{
+	int retval;
+	radio->region = req_region;
+
+	switch (radio->region) {
+	case IRIS_REGION_US:
+		radio->trans_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->trans_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
+		break;
+	case IRIS_REGION_EU:
+		radio->trans_conf.band_low_limit =
+			REGION_US_EU_BAND_LOW;
+		radio->trans_conf.band_high_limit =
+			REGION_US_EU_BAND_HIGH;
+		break;
+	case IRIS_REGION_JAPAN:
+		radio->trans_conf.band_low_limit =
+			REGION_JAPAN_STANDARD_BAND_HIGH;
+		radio->trans_conf.band_high_limit =
+			REGION_JAPAN_STANDARD_BAND_LOW;
+		break;
+	case IRIS_REGION_JAPAN_WIDE:
+		radio->recv_conf.band_low_limit =
+			REGION_JAPAN_WIDE_BAND_LOW;
+		radio->recv_conf.band_high_limit =
+			REGION_JAPAN_WIDE_BAND_HIGH;
+	default:
+		break;
+	}
+
+	retval = hci_set_fm_trans_conf(
+			&radio->trans_conf,
+				radio->fm_hdev);
+	return retval;
+}
+
+
 static int iris_set_freq(struct iris_device *radio, unsigned int freq)
 {
 
@@ -1940,40 +2103,44 @@
 	case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
 		break;
 	case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
-		retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
-							 radio->fm_hdev);
-		if (retval < 0)
-			FMDERR("Error get FM recv conf"
-				" %d\n", retval);
-		else
+		if (radio->mode == FM_RECV) {
 			ctrl->value = radio->recv_conf.emphasis;
+		} else if (radio->mode == FM_TRANS) {
+			ctrl->value = radio->trans_conf.emphasis;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDS_STD:
-		retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
-				 radio->fm_hdev);
-		if (retval < 0)
-			FMDERR("Error get FM recv conf"
-				" %d\n", retval);
-		else
+		if (radio->mode == FM_RECV) {
 			ctrl->value = radio->recv_conf.rds_std;
+		} else if (radio->mode == FM_TRANS) {
+			ctrl->value = radio->trans_conf.rds_std;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_SPACING:
-		retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
-				radio->fm_hdev);
-		if (retval < 0)
-			FMDERR("Error get FM recv conf"
-				" %d\n", retval);
-		else
+		if (radio->mode == FM_RECV) {
 			ctrl->value = radio->recv_conf.ch_spacing;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDSON:
-		retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
-				radio->fm_hdev);
-		if (retval < 0)
-			FMDERR("Error get FM recv conf"
-				" %d\n", retval);
-		else
+		if (radio->mode == FM_RECV) {
 			ctrl->value = radio->recv_conf.rds_std;
+		} else {
+			FMDERR("Error in radio mode"
+				" %d\n", retval);
+			return -EINVAL;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
 		ctrl->value = radio->rds_grp.rds_grp_enable_mask;
@@ -2005,7 +2172,60 @@
 static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
 			struct v4l2_ext_controls *ctrl)
 {
-	return -ENOTSUPP;
+	int retval = 0;
+	int bytes_to_copy;
+	struct hci_fm_tx_ps tx_ps;
+	struct hci_fm_tx_rt tx_rt;
+
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
+	char *data = NULL;
+
+	switch ((ctrl->controls[0]).id) {
+	case V4L2_CID_RDS_TX_PS_NAME:
+		FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
+		/*Pass a sample PS string */
+
+		memset(tx_ps.ps_data, 0, MAX_PS_LENGTH);
+		bytes_to_copy = min((int)(ctrl->controls[0]).size,
+			MAX_PS_LENGTH);
+		data = (ctrl->controls[0]).string;
+
+		if (copy_from_user(tx_ps.ps_data,
+				data, bytes_to_copy))
+				return -EFAULT;
+		tx_ps.ps_control =  0x01;
+		tx_ps.pi = radio->pi;
+		tx_ps.pty = radio->pty;
+		tx_ps.ps_repeatcount = radio->ps_repeatcount;
+		tx_ps.ps_len = bytes_to_copy;
+
+		retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
+				(unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
+		break;
+	case V4L2_CID_RDS_TX_RADIO_TEXT:
+		bytes_to_copy =
+		    min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
+		data = (ctrl->controls[0]).string;
+
+		memset(tx_rt.rt_data, 0, MAX_RT_LENGTH);
+
+		if (copy_from_user(tx_rt.rt_data,
+				data, bytes_to_copy))
+				return -EFAULT;
+
+		tx_rt.rt_control =  0x01;
+		tx_rt.pi = radio->pi;
+		tx_rt.pty = radio->pty;
+		tx_rt.ps_len = bytes_to_copy;
+
+		retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
+				(unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
+		break;
+	default:
+		FMDBG("Shouldn't reach here\n");
+		retval = -1;
+	}
+	return retval;
 }
 
 static int iris_vidioc_s_ctrl(struct file *file, void *priv,
@@ -2015,6 +2235,7 @@
 	int retval = 0;
 	unsigned int rds_grps_proc = 0;
 	__u8 temp_val = 0;
+	unsigned long arg = 0;
 	radio->recv_conf.emphasis = 0;
 	radio->recv_conf.ch_spacing = 0;
 	radio->recv_conf.hlsi = 0;
@@ -2024,6 +2245,12 @@
 
 
 	switch (ctrl->id) {
+	case VL2_CID_PRIVATE_IRIS_TX_TONE:
+		radio->tone_freq = ctrl->value;
+		retval = radio_hci_request(radio->fm_hdev,
+				hci_fm_tone_generator, arg,
+				msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+		break;
 	case V4L2_CID_AUDIO_VOLUME:
 		break;
 	case V4L2_CID_AUDIO_MUTE:
@@ -2046,41 +2273,74 @@
 		iris_search(radio, ctrl->value, SRCH_DIR_UP);
 		break;
 	case V4L2_CID_PRIVATE_IRIS_STATE:
-		if (ctrl->value == FM_RECV) {
+		switch (ctrl->value) {
+		case FM_RECV:
 			retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
 							 radio->fm_hdev);
-			if (retval < 0) {
-				FMDERR("Error while enabling FM"
+
+			radio->mode = FM_RECV;
+
+			if (retval < 0)
+				FMDERR("Error while enabling RECV FM"
 							" %d\n", retval);
-			} else {
-				radio->mute_mode.soft_mute = CTRL_ON;
-				retval = hci_set_fm_mute_mode(
-							&radio->mute_mode,
+			radio->mute_mode.soft_mute = CTRL_ON;
+			retval = hci_set_fm_mute_mode(
+						&radio->mute_mode,
 							radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Failed to enable Smute\n");
+			radio->stereo_mode.stereo_mode = CTRL_OFF;
+			radio->stereo_mode.sig_blend = CTRL_ON;
+			radio->stereo_mode.intf_blend = CTRL_ON;
+			radio->stereo_mode.most_switch = CTRL_ON;
+			retval = hci_set_fm_stereo_mode(
+						&radio->stereo_mode,
+							radio->fm_hdev);
+			if (retval < 0)
+				FMDERR("Failed to set stereo mode\n");
+
+		case FM_TRANS:
+			retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
+							 radio->fm_hdev);
+			radio->mode = FM_TRANS;
+
+			if (retval < 0)
+				FMDERR("Error while enabling TRANS FM"
+							" %d\n", retval);
+		case FM_OFF:
+			switch (radio->mode) {
+			case FM_RECV:
+				retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
+						radio->fm_hdev);
 				if (retval < 0)
-					FMDERR("Failed to enable Smute\n");
-				radio->stereo_mode.stereo_mode = CTRL_OFF;
-				radio->stereo_mode.sig_blend = CTRL_ON;
-				radio->stereo_mode.intf_blend = CTRL_ON;
-				radio->stereo_mode.most_switch = CTRL_ON;
-				retval = hci_set_fm_stereo_mode(
-							&radio->stereo_mode,
-							radio->fm_hdev);
+					FMDERR("Err on disable recv FM"
+						   " %d\n", retval);
+				break;
+			case FM_TRANS:
+				retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
+						radio->fm_hdev);
+
 				if (retval < 0)
-					FMDERR("Failed to stereo mode\n");
-			}
-		} else if (ctrl->value == FM_OFF) {
-			retval = hci_cmd(
-							HCI_FM_DISABLE_RECV_CMD,
-							radio->fm_hdev);
-			if (retval < 0) {
-				FMDERR("Error on disable FM"
+					FMDERR("Err disabling trans FM"
 						" %d\n", retval);
+				break;
+			default:
+				retval = -EINVAL;
 			}
+		default:
+			retval = -EINVAL;
 		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_REGION:
-		retval = iris_set_region(radio, ctrl->value);
+		if (radio->mode == FM_RECV) {
+			retval = iris_recv_set_region(radio, ctrl->value);
+		} else {
+			if (radio->mode == FM_TRANS)
+				retval = iris_trans_set_region(radio,
+						ctrl->value);
+			else
+				retval = -EINVAL;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
 		temp_val = ctrl->value;
@@ -2106,13 +2366,23 @@
 		break;
 	case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
 		radio->recv_conf.emphasis = ctrl->value;
-		retval =
-		hci_set_fm_recv_conf(&radio->recv_conf, radio->fm_hdev);
+		retval = hci_set_fm_recv_conf(
+				&radio->recv_conf,
+					radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Error in setting emphasis");
+			break;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDS_STD:
 		radio->recv_conf.rds_std = ctrl->value;
-		retval =
-		hci_set_fm_recv_conf(&radio->recv_conf, radio->fm_hdev);
+		retval = hci_set_fm_recv_conf(
+				&radio->recv_conf,
+					radio->fm_hdev);
+		if (retval < 0) {
+			FMDERR("Error in setting RDS_STD");
+			break;
+		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDSON:
 		radio->recv_conf.rds_std = ctrl->value;
@@ -2147,14 +2417,17 @@
 		retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
 		break;
 	case V4L2_CID_RDS_TX_PTY:
+		radio->pty = ctrl->value;
 		break;
 	case V4L2_CID_RDS_TX_PI:
+		radio->pi = ctrl->value;
 		break;
 	case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME:
 		break;
 	case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT:
 		break;
 	case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT:
+		radio->ps_repeatcount = ctrl->value;
 		break;
 	case V4L2_CID_TUNE_POWER_LEVEL:
 		break;
@@ -2206,8 +2479,8 @@
 static int iris_vidioc_g_tuner(struct file *file, void *priv,
 		struct v4l2_tuner *tuner)
 {
-	struct iris_device *radio = video_get_drvdata(video_devdata(file));
 	int retval;
+	struct iris_device *radio = video_get_drvdata(video_devdata(file));
 	if (tuner->index > 0)
 		return -EINVAL;
 
@@ -2231,25 +2504,39 @@
 		struct v4l2_tuner *tuner)
 {
 	struct iris_device *radio = video_get_drvdata(video_devdata(file));
-	int retval;
+	int retval = 0;
 	if (tuner->index > 0)
 		return -EINVAL;
 
-	radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
-	radio->recv_conf.band_high_limit = tuner->rangehigh / TUNE_PARAM;
-	if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
-		radio->stereo_mode.stereo_mode = 0x01;
-		retval = hci_set_fm_stereo_mode(
-				&radio->stereo_mode,
-				radio->fm_hdev);
+	if (radio->mode == FM_RECV) {
+		radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
+		radio->recv_conf.band_high_limit =
+			tuner->rangehigh / TUNE_PARAM;
+		if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
+			radio->stereo_mode.stereo_mode = 0x01;
+			retval = hci_set_fm_stereo_mode(
+					&radio->stereo_mode,
+					radio->fm_hdev);
+		} else {
+			radio->stereo_mode.stereo_mode = 0x00;
+			retval = hci_set_fm_stereo_mode(
+					&radio->stereo_mode,
+					radio->fm_hdev);
+		}
+		if (retval < 0)
+			FMDERR(": set tuner failed with %d\n", retval);
+		return retval;
 	} else {
-		radio->stereo_mode.stereo_mode = 0x00;
-		retval = hci_set_fm_stereo_mode(
-				&radio->stereo_mode,
-				radio->fm_hdev);
+		if (radio->mode == FM_TRANS) {
+			radio->trans_conf.band_low_limit =
+				tuner->rangelow / TUNE_PARAM;
+			radio->trans_conf.band_high_limit =
+				tuner->rangehigh / TUNE_PARAM;
+		} else {
+			  return -EINVAL;
+		}
 	}
-	if (retval < 0)
-		FMDERR(": set tuner failed with %d\n", retval);
+
 	return retval;
 }
 
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index 9353a99..8dd1ee6 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -126,6 +126,15 @@
 #define HCI_OCF_FM_EN_WAN_AVD_CTRL          0x0014
 #define HCI_OCF_FM_EN_NOTCH_CTRL            0x0015
 
+/* HCI trans control commans opcode*/
+#define HCI_OCF_FM_ENABLE_TRANS_REQ         0x0001
+#define HCI_OCF_FM_DISABLE_TRANS_REQ        0x0002
+#define HCI_OCF_FM_GET_TRANS_CONF_REQ       0x0003
+#define HCI_OCF_FM_SET_TRANS_CONF_REQ       0x0004
+#define HCI_OCF_FM_RDS_RT_REQ               0x0008
+#define HCI_OCF_FM_RDS_PS_REQ               0x0009
+
+
 /* HCI common control commands opcode */
 #define HCI_OCF_FM_TUNE_STATION_REQ         0x0001
 #define HCI_OCF_FM_DEFAULT_DATA_READ        0x0002
@@ -143,9 +152,11 @@
 #define HCI_OCF_FM_SSBI_PEEK_REG            0x0004
 #define HCI_OCF_FM_SSBI_POKE_REG            0x0005
 #define HCI_OCF_FM_STATION_DBG_PARAM        0x0007
+#define HCI_FM_SET_INTERNAL_TONE_GENRATOR   0x0008
 
 /* Opcode OGF */
 #define HCI_OGF_FM_RECV_CTRL_CMD_REQ            0x0013
+#define HCI_OGF_FM_TRANS_CTRL_CMD_REQ           0x0014
 #define HCI_OGF_FM_COMMON_CTRL_CMD_REQ          0x0015
 #define HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ    0x0016
 #define HCI_OGF_FM_TEST_CMD_REQ                 0x0017
@@ -157,6 +168,8 @@
 #define hci_opcode_ocf(op)		(op & 0x03ff)
 #define hci_recv_ctrl_cmd_op_pack(ocf) \
 	(__u16) hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, ocf)
+#define hci_trans_ctrl_cmd_op_pack(ocf) \
+	(__u16) hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, ocf)
 #define hci_common_cmd_op_pack(ocf)	\
 	(__u16) hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, ocf)
 #define hci_status_param_op_pack(ocf)	\
@@ -178,6 +191,13 @@
 #define HCI_FM_RESET_CMD 10
 #define HCI_FM_GET_FEATURES_CMD 11
 #define HCI_FM_STATION_DBG_PARAM_CMD 12
+#define HCI_FM_ENABLE_TRANS_CMD 13
+#define HCI_FM_DISABLE_TRANS_CMD 14
+
+
+/* Defines for FM TX*/
+#define TX_PS_DATA_LENGTH 96
+#define TX_RT_DATA_LENGTH 64
 
 /* ----- HCI Command request ----- */
 struct hci_fm_recv_conf_req {
@@ -189,6 +209,41 @@
 	__u32	band_high_limit;
 } __packed;
 
+/* ----- HCI Command request ----- */
+struct hci_fm_trans_conf_req_struct {
+	__u8	emphasis;
+	__u8	rds_std;
+	__u32	band_low_limit;
+	__u32	band_high_limit;
+} __packed;
+
+
+/* ----- HCI Command request ----- */
+struct hci_fm_tx_ps {
+	__u8    ps_control;
+	__u16	pi;
+	__u8	pty;
+	__u8	ps_repeatcount;
+	__u8	ps_len;
+	__u8    ps_data[TX_PS_DATA_LENGTH];
+} __packed;
+
+struct hci_fm_tx_rt {
+	__u8    rt_control;
+	__u16	pi;
+	__u8	pty;
+	__u8	ps_len;
+	__u8    rt_data[TX_RT_DATA_LENGTH];
+} __packed;
+
+struct hci_fm_get_trans_conf_rsp {
+	__u8    status;
+	__u8	emphasis;
+	__u8	rds_std;
+	__u32	band_low_limit;
+	__u32	band_high_limit;
+} __packed;
+
 struct hci_fm_mute_mode_req {
 	__u8	hard_mute;
 	__u8	soft_mute;
@@ -479,6 +534,8 @@
 	VL2_CID_PRIVATE_IRIS_TX_TONE,
 };
 
+
+
 enum iris_evt_t {
 	IRIS_EVT_RADIO_READY,
 	IRIS_EVT_TUNE_SUCC,
@@ -499,6 +556,27 @@
 	IRIS_EVT_TXRDSDAT,
 	IRIS_EVT_TXRDSDONE
 };
+enum emphasis_type {
+	FM_RX_EMP75 = 0x0,
+	FM_RX_EMP50 = 0x1
+};
+
+enum channel_space_type {
+	FM_RX_SPACE_200KHZ = 0x0,
+	FM_RX_SPACE_100KHZ = 0x1,
+	FM_RX_SPACE_50KHZ = 0x2
+};
+
+enum high_low_injection {
+	AUTO_HI_LO_INJECTION = 0x0,
+	LOW_SIDE_INJECTION = 0x1,
+	HIGH_SIDE_INJECTION = 0x2
+};
+
+enum fm_rds_type {
+	FM_RX_RDBS_SYSTEM = 0x0,
+	FM_RX_RDS_SYSTEM = 0x1
+};
 
 enum iris_region_t {
 	IRIS_REGION_US,
@@ -554,6 +632,15 @@
 	RDS_AF_JUMP,
 };
 
+
+/* Band limits */
+#define REGION_US_EU_BAND_LOW              87500
+#define REGION_US_EU_BAND_HIGH             107900
+#define REGION_JAPAN_STANDARD_BAND_LOW     76000
+#define REGION_JAPAN_STANDARD_BAND_HIGH    90000
+#define REGION_JAPAN_WIDE_BAND_LOW         90000
+#define REGION_JAPAN_WIDE_BAND_HIGH        108000
+
 #define SRCH_MODE	0x07
 #define SRCH_DIR	0x08 /* 0-up 1-down */
 #define SCAN_DWELL	0x70
@@ -571,6 +658,12 @@
 #define RDS_ON		0x01
 #define RDS_BUF_SZ  100
 
+/* constants */
+#define  RDS_BLOCKS_NUM	(4)
+#define BYTES_PER_BLOCK	(3)
+#define MAX_PS_LENGTH	(96)
+#define MAX_RT_LENGTH	(64)
+
 /* Search direction */
 #define SRCH_DIR_UP		(0)
 #define SRCH_DIR_DOWN		(1)