Merge "msm: vidc: Add support for decoder downscaling"
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index d33a085..30ada3e 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -275,12 +275,18 @@
 	case HAL_BUFFER_OUTPUT:
 		buffer = HFI_BUFFER_OUTPUT;
 		break;
+	case HAL_BUFFER_OUTPUT2:
+		buffer = HFI_BUFFER_OUTPUT2;
+		break;
 	case HAL_BUFFER_EXTRADATA_INPUT:
 		buffer = HFI_BUFFER_EXTRADATA_INPUT;
 		break;
 	case HAL_BUFFER_EXTRADATA_OUTPUT:
 		buffer = HFI_BUFFER_EXTRADATA_OUTPUT;
 		break;
+	case HAL_BUFFER_EXTRADATA_OUTPUT2:
+		buffer = HFI_BUFFER_EXTRADATA_OUTPUT2;
+		break;
 	case HAL_BUFFER_INTERNAL_SCRATCH:
 		buffer = HFI_BUFFER_INTERNAL_SCRATCH;
 		break;
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 101b148..abdb039 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -957,6 +957,7 @@
 		data_done.output_done.packet_buffer1 = pkt->packet_buffer;
 		data_done.output_done.extra_data_buffer =
 			pkt->extra_data_buffer;
+		data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT;
 		dprintk(VIDC_DBG, "FBD: Received buf: %p, of len: %d\n",
 				   pkt->packet_buffer, pkt->filled_len);
 	} else if (is_decoder == 1) {
diff --git a/drivers/media/platform/msm/vidc/msm_smem.h b/drivers/media/platform/msm/vidc/msm_smem.h
index 4425909..dc384bc 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.h
+++ b/drivers/media/platform/msm/vidc/msm_smem.h
@@ -31,16 +31,16 @@
 enum hal_buffer {
 	HAL_BUFFER_INPUT = 0x1,
 	HAL_BUFFER_OUTPUT = 0x2,
-	HAL_BUFFER_OUTPUT2 = 0x2,
-	HAL_BUFFER_EXTRADATA_INPUT = 0x4,
-	HAL_BUFFER_EXTRADATA_OUTPUT = 0x8,
-	HAL_BUFFER_EXTRADATA_OUTPUT2 = 0x8,
-	HAL_BUFFER_INTERNAL_SCRATCH = 0x10,
-	HAL_BUFFER_INTERNAL_SCRATCH_1 = 0x20,
-	HAL_BUFFER_INTERNAL_SCRATCH_2 = 0x40,
-	HAL_BUFFER_INTERNAL_PERSIST = 0x80,
-	HAL_BUFFER_INTERNAL_PERSIST_1 = 0x100,
-	HAL_BUFFER_INTERNAL_CMD_QUEUE = 0x200,
+	HAL_BUFFER_OUTPUT2 = 0x4,
+	HAL_BUFFER_EXTRADATA_INPUT = 0x8,
+	HAL_BUFFER_EXTRADATA_OUTPUT = 0x10,
+	HAL_BUFFER_EXTRADATA_OUTPUT2 = 0x20,
+	HAL_BUFFER_INTERNAL_SCRATCH = 0x40,
+	HAL_BUFFER_INTERNAL_SCRATCH_1 = 0x80,
+	HAL_BUFFER_INTERNAL_SCRATCH_2 = 0x100,
+	HAL_BUFFER_INTERNAL_PERSIST = 0x200,
+	HAL_BUFFER_INTERNAL_PERSIST_1 = 0x400,
+	HAL_BUFFER_INTERNAL_CMD_QUEUE = 0x800,
 };
 
 struct msm_smem {
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 3582d78..f4fdfe7 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -307,6 +307,22 @@
 		.qmenu = NULL,
 		.cluster = 0,
 	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE,
+		.name = "Video decoder multi stream",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum =
+			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY,
+		.maximum =
+			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY,
+		.default_value =
+			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.step = 1,
+		.qmenu = NULL,
+		.cluster = 0,
+	},
 };
 
 #define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -496,7 +512,8 @@
 				b->m.planes[i].length);
 			}
 			buffer_info.buffer_size = b->m.planes[0].length;
-			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+			buffer_info.buffer_type =
+				msm_comm_get_hal_output_buffer(inst);
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr =
 				b->m.planes[0].m.userptr;
@@ -580,7 +597,8 @@
 				b->m.planes[i].length);
 			}
 			buffer_info.buffer_size = b->m.planes[0].length;
-			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+			buffer_info.buffer_type =
+				msm_comm_get_hal_output_buffer(inst);
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr =
 				 b->m.planes[0].m.userptr;
@@ -668,7 +686,6 @@
 int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
 {
 	const struct msm_vidc_format *fmt = NULL;
-	struct hal_frame_size frame_sz;
 	struct hfi_device *hdev;
 	int stride, scanlines;
 	int extra_idx = 0;
@@ -690,8 +707,22 @@
 		f->fmt.pix_mp.pixelformat = fmt->fourcc;
 		f->fmt.pix_mp.num_planes = fmt->num_planes;
 		if (inst->in_reconfig == true) {
-			inst->prop.height[CAPTURE_PORT] = inst->reconfig_height;
-			inst->prop.width[CAPTURE_PORT] = inst->reconfig_width;
+			if (msm_comm_get_stream_output_mode(inst) ==
+				HAL_VIDEO_DECODER_PRIMARY) {
+				inst->prop.height[CAPTURE_PORT] =
+					inst->reconfig_height;
+				inst->prop.width[CAPTURE_PORT] =
+					inst->reconfig_width;
+				inst->prop.height[OUTPUT_PORT] =
+					inst->reconfig_height;
+				inst->prop.width[OUTPUT_PORT] =
+					inst->reconfig_width;
+			} else {
+				inst->prop.height[OUTPUT_PORT] =
+					inst->reconfig_height;
+				inst->prop.width[OUTPUT_PORT] =
+					inst->reconfig_width;
+			}
 			rc = msm_vidc_check_session_supported(inst);
 			if (rc) {
 				dprintk(VIDC_ERR,
@@ -703,11 +734,6 @@
 		f->fmt.pix_mp.width = inst->prop.width[CAPTURE_PORT];
 		stride = inst->prop.width[CAPTURE_PORT];
 		scanlines = inst->prop.height[CAPTURE_PORT];
-		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
-		frame_sz.width = inst->prop.width[CAPTURE_PORT];
-		frame_sz.height = inst->prop.height[CAPTURE_PORT];
-		dprintk(VIDC_DBG, "width = %d, height = %d\n",
-				frame_sz.width, frame_sz.height);
 		rc = msm_comm_try_get_bufreqs(inst);
 		if (rc) {
 			dprintk(VIDC_ERR,
@@ -733,7 +759,8 @@
 					"Color format not recognized\n");
 			}
 			buff_req_buffer =
-				get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+				get_buff_req_buffer(inst,
+					msm_comm_get_hal_output_buffer(inst));
 			if (buff_req_buffer)
 				f->fmt.pix_mp.plane_fmt[0].sizeimage =
 				buff_req_buffer->buffer_size;
@@ -855,9 +882,24 @@
 		}
 		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
-		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
-		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_PRIMARY) {
+			inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
+			inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+		}
 		inst->fmts[fmt->type] = fmt;
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY) {
+			frame_sz.buffer_type = HAL_BUFFER_OUTPUT2;
+			frame_sz.width = inst->prop.width[CAPTURE_PORT];
+			frame_sz.height = inst->prop.height[CAPTURE_PORT];
+			dprintk(VIDC_DBG,
+				"buffer type = %d width = %d, height = %d\n",
+				frame_sz.buffer_type, frame_sz.width,
+				frame_sz.height);
+			ret = msm_comm_try_set_prop(inst,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		}
 		ret = ret || msm_comm_try_get_bufreqs(inst);
 		if (ret) {
 			for (i = 0; i < fmt->num_planes; ++i) {
@@ -868,7 +910,8 @@
 			}
 		} else {
 			buff_req_buffer =
-				get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+				get_buff_req_buffer(inst,
+					msm_comm_get_hal_output_buffer(inst));
 			if (buff_req_buffer)
 				f->fmt.pix_mp.plane_fmt[0].sizeimage =
 				buff_req_buffer->buffer_size;
@@ -895,8 +938,11 @@
 	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
 		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
-		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
-		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_PRIMARY) {
+			inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
+			inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		}
 		rc = msm_vidc_check_session_supported(inst);
 		if (rc) {
 			dprintk(VIDC_ERR,
@@ -923,6 +969,10 @@
 		frame_sz.buffer_type = HAL_BUFFER_INPUT;
 		frame_sz.width = inst->prop.width[OUTPUT_PORT];
 		frame_sz.height = inst->prop.height[OUTPUT_PORT];
+		dprintk(VIDC_DBG,
+			"buffer type = %d width = %d, height = %d\n",
+			frame_sz.buffer_type, frame_sz.width,
+			frame_sz.height);
 		msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
 
 		max_input_size = fmt->get_frame_size(0,
@@ -1055,7 +1105,8 @@
 			break;
 		}
 		mutex_lock(&inst->lock);
-		bufreq = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+		bufreq = get_buff_req_buffer(inst,
+			msm_comm_get_hal_output_buffer(inst));
 		if (!bufreq) {
 			dprintk(VIDC_ERR,
 				"No buffer requirement for buffer type %x\n",
@@ -1067,7 +1118,8 @@
 		*num_buffers = max(*num_buffers, bufreq->buffer_count_min);
 		if (*num_buffers != bufreq->buffer_count_actual) {
 			property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
-			new_buf_count.buffer_type = HAL_BUFFER_OUTPUT;
+			new_buf_count.buffer_type =
+				msm_comm_get_hal_output_buffer(inst);
 			new_buf_count.buffer_count_actual = *num_buffers;
 			rc = call_hfi_op(hdev, session_set_property,
 				inst->session, property_id, &new_buf_count);
@@ -1097,14 +1149,75 @@
 	return rc;
 }
 
+static int msm_vdec_queue_output_buffers(struct msm_vidc_inst *inst)
+{
+	struct internal_buf *binfo;
+	struct hfi_device *hdev;
+	struct msm_smem *handle;
+	struct vidc_frame_data frame_data = {0};
+	struct hal_buffer_requirements *output_buf, *extradata_buf;
+	int rc = 0;
+	hdev = inst->core->device;
+
+	output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+	if (!output_buf) {
+		dprintk(VIDC_DBG,
+			"This output buffer not required, buffer_type: %x\n",
+			HAL_BUFFER_OUTPUT);
+		return 0;
+	}
+	dprintk(VIDC_DBG,
+		"output: num = %d, size = %d\n",
+		output_buf->buffer_count_actual,
+		output_buf->buffer_size);
+
+	extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT);
+	if (!extradata_buf) {
+		dprintk(VIDC_DBG,
+			"This extradata buffer not required, buffer_type: %x\n",
+			HAL_BUFFER_EXTRADATA_OUTPUT);
+		return 0;
+	}
+
+	hdev = inst->core->device;
+
+	mutex_lock(&inst->lock);
+	if (!list_empty(&inst->outputbufs)) {
+		list_for_each_entry(binfo, &inst->outputbufs, list) {
+			if (!binfo) {
+				dprintk(VIDC_ERR, "Invalid parameter\n");
+				mutex_unlock(&inst->lock);
+				return -EINVAL;
+			}
+			handle = binfo->handle;
+			frame_data.alloc_len = output_buf->buffer_size;
+			frame_data.filled_len = 0;
+			frame_data.offset = 0;
+			frame_data.device_addr = handle->device_addr;
+			frame_data.flags = 0;
+			frame_data.extradata_addr = handle->device_addr +
+				output_buf->buffer_size;
+			frame_data.buffer_type = HAL_BUFFER_OUTPUT;
+			rc = call_hfi_op(hdev, session_ftb,
+					(void *) inst->session, &frame_data);
+			binfo->buffer_ownership = FIRMWARE;
+		}
+	}
+	mutex_unlock(&inst->lock);
+	return 0;
+}
+
 static inline int start_streaming(struct msm_vidc_inst *inst)
 {
 	int rc = 0;
 	struct vb2_buf_entry *temp;
+	struct hfi_device *hdev;
 	struct list_head *ptr, *next;
+
+	hdev = inst->core->device;
 	inst->in_reconfig = false;
-	if (inst->capability.pixelprocess_capabilities &
-		HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY)
 		rc = msm_comm_check_scaling_supported(inst);
 	if (rc) {
 		dprintk(VIDC_ERR, "H/w scaling is not in valid range");
@@ -1123,6 +1236,15 @@
 		goto fail_start;
 	}
 
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY) {
+		rc = msm_comm_set_output_buffers(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set output buffers: %d\n", rc);
+			goto fail_start;
+		}
+	}
 	mutex_lock(&inst->core->sync_lock);
 	msm_comm_scale_clocks_and_bus(inst);
 	mutex_unlock(&inst->core->sync_lock);
@@ -1133,7 +1255,15 @@
 			"Failed to move inst: %p to start done state\n", inst);
 		goto fail_start;
 	}
-
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY) {
+		rc = msm_vdec_queue_output_buffers(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to queue output buffers: %d\n", rc);
+			goto fail_start;
+		}
+	}
 	mutex_lock(&inst->sync_lock);
 	if (!list_empty(&inst->pendingq)) {
 		list_for_each_safe(ptr, next, &inst->pendingq) {
@@ -1389,6 +1519,7 @@
 	struct hfi_device *hdev;
 	struct hal_extradata_enable extra;
 	struct hal_buffer_alloc_mode alloc_mode;
+	struct hal_multi_stream multi_stream;
 
 	if (!inst || !inst->core || !inst->core->device) {
 		dprintk(VIDC_ERR, "%s invalid parameters", __func__);
@@ -1516,7 +1647,70 @@
 		pdata = &alloc_mode;
 		inst->buffer_mode_set[CAPTURE_PORT] = alloc_mode.buffer_mode;
 		break;
-
+	case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE:
+		if (ctrl->val && !(inst->capability.pixelprocess_capabilities &
+				HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)) {
+			dprintk(VIDC_ERR, "Downscaling not supported: 0x%x",
+				ctrl->id);
+			rc = -ENOTSUPP;
+			break;
+		}
+		switch (ctrl->val) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY:
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT;
+			multi_stream.enable = true;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed : Enabling OUTPUT port : %d\n",
+					rc);
+				break;
+			}
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT2;
+			multi_stream.enable = false;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"Failed:Disabling OUTPUT2 port : %d\n",
+					rc);
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY:
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT2;
+			multi_stream.enable = true;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed :Enabling OUTPUT2 port : %d\n",
+					rc);
+				break;
+			}
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT;
+			multi_stream.enable = false;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"Failed :Disabling OUTPUT port : %d\n",
+					rc);
+			break;
+		default:
+			dprintk(VIDC_ERR,
+				"Failed : Unsupported multi stream setting\n");
+			rc = -ENOTSUPP;
+			break;
+		}
+		break;
 	default:
 		break;
 	}
@@ -1537,6 +1731,10 @@
 	int rc = 0, c = 0;
 	struct msm_vidc_inst *inst = container_of(ctrl->handler,
 				struct msm_vidc_inst, ctrl_handler);
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+		return -EINVAL;
+	}
 	rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
 	if (rc) {
 		dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index ca17390..f9b5519 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -2365,6 +2365,7 @@
 	int i;
 	u32 height, width;
 	int extra_idx = 0;
+	struct hal_buffer_requirements *buff_req_buffer;
 	if (!inst || !f) {
 		dprintk(VIDC_ERR,
 			"Invalid input, inst = %p, format = %p\n", inst, f);
@@ -2391,9 +2392,12 @@
 		}
 		extra_idx = EXTRADATA_IDX(fmt->num_planes);
 		if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+			buff_req_buffer =
+				get_buff_req_buffer(inst,
+					HAL_BUFFER_EXTRADATA_OUTPUT);
 			f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
-				inst->buff_req.buffer
-				[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
+				buff_req_buffer ?
+				buff_req_buffer->buffer_size : 0;
 		}
 		for (i = 0; i < fmt->num_planes; ++i) {
 			inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f2f0f5d..6ed94e4 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -1098,6 +1098,7 @@
 	INIT_LIST_HEAD(&inst->persistbufs);
 	INIT_LIST_HEAD(&inst->ctrl_clusters);
 	INIT_LIST_HEAD(&inst->registered_bufs);
+	INIT_LIST_HEAD(&inst->outputbufs);
 	init_waitqueue_head(&inst->kernel_event_queue);
 	inst->state = MSM_VIDC_CORE_UNINIT_DONE;
 	inst->core = core;
@@ -1204,6 +1205,17 @@
 				mutex_lock(&inst->lock);
 			}
 		}
+		if (!list_empty(&inst->outputbufs)) {
+			list_for_each_safe(ptr, next, &inst->outputbufs) {
+				buf = list_entry(ptr, struct internal_buf,
+						list);
+				list_del(&buf->list);
+				mutex_unlock(&inst->lock);
+				msm_smem_free(inst->mem_client, buf->handle);
+				kfree(buf);
+				mutex_lock(&inst->lock);
+			}
+		}
 		if (inst->extradata_handle) {
 			mutex_unlock(&inst->lock);
 			msm_smem_free(inst->mem_client, inst->extradata_handle);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 03788e4..42460fa 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -82,6 +82,21 @@
 	}
 	return false;
 }
+enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst)
+{
+	if (inst->session_type == MSM_VIDC_DECODER) {
+		int rc = 0;
+		struct v4l2_control ctrl = {
+			.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE
+		};
+		rc = v4l2_g_ctrl(&inst->ctrl_handler, &ctrl);
+		if (!rc && ctrl.value)
+			return HAL_VIDEO_DECODER_SECONDARY;
+	}
+	return HAL_VIDEO_DECODER_PRIMARY;
+
+
+}
 static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst)
 {
 	int height, width;
@@ -559,8 +574,13 @@
 				"V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n");
 			inst->prop.height[CAPTURE_PORT] = event_notify->height;
 			inst->prop.width[CAPTURE_PORT] = event_notify->width;
-			inst->prop.height[OUTPUT_PORT] = event_notify->height;
-			inst->prop.width[OUTPUT_PORT] = event_notify->width;
+			if (!msm_comm_get_stream_output_mode(inst) ==
+				HAL_VIDEO_DECODER_SECONDARY) {
+				inst->prop.height[OUTPUT_PORT] =
+					event_notify->height;
+				inst->prop.width[OUTPUT_PORT] =
+					event_notify->width;
+			}
 		}
 		rc = msm_vidc_check_session_supported(inst);
 		if (!rc) {
@@ -658,13 +678,51 @@
 			"Failed to get valid response for release resource\n");
 	}
 }
+void validate_output_buffers(struct msm_vidc_inst *inst)
+{
+	struct internal_buf *binfo;
+	u32 buffers_owned_by_driver = 0;
+	struct hal_buffer_requirements *output_buf;
+	output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+	if (!output_buf) {
+		dprintk(VIDC_DBG,
+			"This output buffer not required, buffer_type: %x\n",
+			HAL_BUFFER_OUTPUT);
+		return;
+	}
+	list_for_each_entry(binfo, &inst->outputbufs, list) {
+		if (!binfo) {
+			dprintk(VIDC_ERR, "Invalid parameter\n");
+			return;
+		}
+		if (binfo->buffer_ownership != DRIVER) {
+			dprintk(VIDC_ERR,
+					"Failed : This buffer is with FW 0x%lx\n",
+					binfo->handle->device_addr);
+			return;
+		}
+		buffers_owned_by_driver++;
+	}
+	if (buffers_owned_by_driver != output_buf->buffer_count_actual)
+		dprintk(VIDC_ERR,
+			"OUTPUT Buffer count mismatch %d of %d",
+			buffers_owned_by_driver,
+			output_buf->buffer_count_actual);
 
+	return;
+}
 static void handle_session_flush(enum command_response cmd, void *data)
 {
 	struct msm_vidc_cb_cmd_done *response = data;
 	struct msm_vidc_inst *inst;
 	if (response) {
 		inst = (struct msm_vidc_inst *)response->session_id;
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY) {
+			mutex_lock(&inst->lock);
+			validate_output_buffers(inst);
+			mutex_unlock(&inst->lock);
+		}
 		msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
 	} else {
 		dprintk(VIDC_ERR, "Failed to get valid response for flush\n");
@@ -988,12 +1046,55 @@
 	}
 }
 
+static int handle_multi_stream_buffers(struct msm_vidc_inst *inst,
+	u32 dev_addr)
+{
+	struct internal_buf *binfo;
+	struct msm_smem *handle;
+	bool found = false;
+	mutex_lock(&inst->lock);
+	list_for_each_entry(binfo, &inst->outputbufs, list) {
+		if (!binfo) {
+			dprintk(VIDC_ERR, "Invalid parameter\n");
+			break;
+		}
+		handle = binfo->handle;
+		if (handle && dev_addr == handle->device_addr) {
+			if (binfo->buffer_ownership == DRIVER) {
+				dprintk(VIDC_ERR,
+					"FW returned same buffer : 0x%x\n",
+					dev_addr);
+				break;
+			}
+				binfo->buffer_ownership = DRIVER;
+			found = true;
+			break;
+		}
+	}
+	if (!found)
+		dprintk(VIDC_ERR,
+			"Failed to find output buffer in queued list: 0x%x\n",
+			dev_addr);
+	mutex_unlock(&inst->lock);
+	return 0;
+}
+
+enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst)
+{
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY)
+		return HAL_BUFFER_OUTPUT2;
+	else
+		return HAL_BUFFER_OUTPUT;
+}
+
 static void handle_fbd(enum command_response cmd, void *data)
 {
 	struct msm_vidc_cb_data_done *response = data;
 	struct msm_vidc_inst *inst;
-	struct vb2_buffer *vb;
+	struct vb2_buffer *vb = NULL;
 	struct vidc_hal_fbd *fill_buf_done;
+	enum hal_buffer buffer_type;
 
 	if (!response) {
 		dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
@@ -1001,8 +1102,18 @@
 	}
 	inst = (struct msm_vidc_inst *)response->session_id;
 	fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
-	vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT],
-		(u32)fill_buf_done->packet_buffer1);
+	buffer_type = msm_comm_get_hal_output_buffer(inst);
+	if (fill_buf_done->buffer_type == buffer_type)
+		vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT],
+			(u32)fill_buf_done->packet_buffer1);
+	else {
+		if (handle_multi_stream_buffers(inst,
+			(u32)fill_buf_done->packet_buffer1))
+			dprintk(VIDC_ERR,
+				"Failed : Output buffer not found 0x%x\n",
+				(u32)fill_buf_done->packet_buffer1);
+		return;
+	}
 	if (vb) {
 		vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
 		vb->v4l2_planes[0].data_offset = fill_buf_done->offset1;
@@ -1838,6 +1949,110 @@
 	return NULL;
 }
 
+static int set_output_buffers(struct msm_vidc_inst *inst,
+	enum hal_buffer buffer_type)
+{
+	int rc = 0;
+	struct msm_smem *handle;
+	struct internal_buf *binfo;
+	struct vidc_buffer_addr_info buffer_info;
+	u32 smem_flags = 0, buffer_size;
+	struct hal_buffer_requirements *output_buf, *extradata_buf;
+	int i;
+	struct hfi_device *hdev;
+
+	hdev = inst->core->device;
+
+	output_buf = get_buff_req_buffer(inst, buffer_type);
+	if (!output_buf) {
+		dprintk(VIDC_DBG,
+			"This output buffer not required, buffer_type: %x\n",
+			buffer_type);
+		return 0;
+	}
+	dprintk(VIDC_DBG,
+		"output: num = %d, size = %d\n",
+		output_buf->buffer_count_actual,
+		output_buf->buffer_size);
+
+	extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT);
+	if (!extradata_buf) {
+		dprintk(VIDC_DBG,
+			"This extradata buffer not required, buffer_type: %x\n",
+			buffer_type);
+		return 0;
+	}
+	dprintk(VIDC_DBG,
+		"extradata: num = %d, size = %d\n",
+		extradata_buf->buffer_count_actual,
+		extradata_buf->buffer_size);
+
+	buffer_size = output_buf->buffer_size + extradata_buf->buffer_size;
+	if (inst->flags & VIDC_SECURE)
+		smem_flags |= SMEM_SECURE;
+
+	if (output_buf->buffer_size) {
+		for (i = 0; i < output_buf->buffer_count_actual;
+				i++) {
+			handle = msm_smem_alloc(inst->mem_client,
+					buffer_size, 1, smem_flags,
+					buffer_type, 0);
+			if (!handle) {
+				dprintk(VIDC_ERR,
+					"Failed to allocate output memory\n");
+				rc = -ENOMEM;
+				goto err_no_mem;
+			}
+			rc = msm_smem_cache_operations(inst->mem_client,
+					handle, SMEM_CACHE_CLEAN);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Failed to clean cache may cause undefined behavior\n");
+			}
+			binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+			if (!binfo) {
+				dprintk(VIDC_ERR, "Out of memory\n");
+				rc = -ENOMEM;
+				goto fail_kzalloc;
+			}
+			mutex_lock(&inst->lock);
+			binfo->handle = handle;
+			buffer_info.buffer_size = output_buf->buffer_size;
+			buffer_info.buffer_type = buffer_type;
+			binfo->buffer_type = buffer_type;
+			buffer_info.num_buffers = 1;
+			binfo->buffer_ownership = DRIVER;
+			buffer_info.align_device_addr = handle->device_addr;
+			buffer_info.extradata_addr = handle->device_addr +
+				output_buf->buffer_size;
+			buffer_info.extradata_size = extradata_buf->buffer_size;
+			dprintk(VIDC_DBG, "Output buffer address: %x",
+					buffer_info.align_device_addr);
+			dprintk(VIDC_DBG, "Output extradata address: %x",
+					buffer_info.extradata_addr);
+			rc = call_hfi_op(hdev, session_set_buffers,
+					(void *) inst->session, &buffer_info);
+			mutex_unlock(&inst->lock);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"%s : session_set_buffers failed",
+					__func__);
+				goto fail_set_buffers;
+			}
+			mutex_lock(&inst->lock);
+			list_add_tail(&binfo->list, &inst->outputbufs);
+			mutex_unlock(&inst->lock);
+		}
+	}
+	return rc;
+fail_set_buffers:
+	kfree(binfo);
+fail_kzalloc:
+	msm_smem_free(inst->mem_client, handle);
+err_no_mem:
+	return rc;
+}
+
 static int set_scratch_buffers(struct msm_vidc_inst *inst,
 	enum hal_buffer buffer_type)
 {
@@ -2228,7 +2443,8 @@
 			frame_data.filled_len = 0;
 			frame_data.offset = 0;
 			frame_data.alloc_len = vb->v4l2_planes[0].length;
-			frame_data.buffer_type = HAL_BUFFER_OUTPUT;
+			frame_data.buffer_type =
+				msm_comm_get_hal_output_buffer(inst);
 			extra_idx =
 			EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
 			if (extra_idx && (extra_idx < VIDEO_MAX_PLANES) &&
@@ -2322,6 +2538,62 @@
 	mutex_unlock(&inst->sync_lock);
 	return rc;
 }
+int msm_comm_release_output_buffers(struct msm_vidc_inst *inst)
+{
+	struct msm_smem *handle;
+	struct list_head *ptr, *next;
+	struct internal_buf *buf;
+	struct vidc_buffer_addr_info buffer_info;
+	int rc = 0;
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %p\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %p\n", core);
+		return -EINVAL;
+	}
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
+		return -EINVAL;
+	}
+	mutex_lock(&inst->lock);
+	if (!list_empty(&inst->outputbufs)) {
+		list_for_each_safe(ptr, next, &inst->outputbufs) {
+			buf = list_entry(ptr, struct internal_buf,
+					list);
+			handle = buf->handle;
+			buffer_info.buffer_size = handle->size;
+			buffer_info.buffer_type = buf->buffer_type;
+			buffer_info.num_buffers = 1;
+			buffer_info.align_device_addr = handle->device_addr;
+			if (inst->state != MSM_VIDC_CORE_INVALID &&
+					core->state != VIDC_CORE_INVALID) {
+				buffer_info.response_required = false;
+				rc = call_hfi_op(hdev, session_release_buffers,
+					(void *)inst->session, &buffer_info);
+				if (rc)
+					dprintk(VIDC_WARN,
+						"Rel output buf fail:0x%x, %d",
+						buffer_info.align_device_addr,
+						buffer_info.buffer_size);
+			}
+			list_del(&buf->list);
+			mutex_unlock(&inst->lock);
+			msm_smem_free(inst->mem_client, buf->handle);
+			kfree(buf);
+			mutex_lock(&inst->lock);
+		}
+	}
+	mutex_unlock(&inst->lock);
+	return rc;
+}
 
 int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
 {
@@ -2496,6 +2768,26 @@
 	return rc;
 }
 
+int msm_comm_set_output_buffers(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+		return -EINVAL;
+	}
+
+	if (msm_comm_release_output_buffers(inst))
+		dprintk(VIDC_WARN, "Failed to release output buffers\n");
+
+	rc = set_output_buffers(inst, HAL_BUFFER_OUTPUT);
+	if (rc)
+		goto error;
+	return rc;
+error:
+	msm_comm_release_output_buffers(inst);
+	return rc;
+}
+
 int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
 {
 	int rc = 0;
@@ -2710,6 +3002,11 @@
 		}
 		rc = call_hfi_op(hdev, session_flush, inst->session,
 				HAL_FLUSH_OUTPUT);
+		if (!rc && (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY))
+			rc = call_hfi_op(hdev, session_flush, inst->session,
+				HAL_FLUSH_OUTPUT2);
+
 	} else {
 		if (!list_empty(&inst->pendingq)) {
 			/*If flush is called after queueing buffers but before
@@ -2886,8 +3183,8 @@
 	u32 input_height, input_width, output_height, output_width;
 
 	if (!inst->capability.scale_x.min || !inst->capability.scale_x.max ||
-			!inst->capability.scale_y.min ||
-			!inst->capability.scale_y.max) {
+		!inst->capability.scale_y.min ||
+		!inst->capability.scale_y.max) {
 		dprintk(VIDC_ERR, "%s : Invalid scaling ratios",
 				__func__);
 		return -ENOTSUPP;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 6ee773a..195fa7e 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -31,6 +31,7 @@
 	enum hal_property ptype, void *pdata);
 int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst);
 int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst);
+int msm_comm_set_output_buffers(struct msm_vidc_inst *inst);
 int msm_comm_qbuf(struct vb2_buffer *vb);
 void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst);
 int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags);
@@ -46,7 +47,10 @@
 #define IS_PRIV_CTRL(idx) (\
 		(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
 		V4L2_CTRL_DRIVER_PRIV(idx))
+
 int msm_comm_check_scaling_supported(struct msm_vidc_inst *inst);
 int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst);
+enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst);
+enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst);
 
 #endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 3f607e9..2b1471c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -100,10 +100,18 @@
 	struct vb2_buffer *buf;
 };
 
+enum buffer_owner {
+	DRIVER,
+	FIRMWARE,
+	CLIENT,
+	MAX_OWNER
+};
+
 struct internal_buf {
 	struct list_head list;
 	enum hal_buffer buffer_type;
 	struct msm_smem *handle;
+	enum buffer_owner buffer_ownership;
 };
 
 struct msm_vidc_format {
@@ -214,6 +222,7 @@
 	struct list_head pendingq;
 	struct list_head internalbufs;
 	struct list_head persistbufs;
+	struct list_head outputbufs;
 	struct buffer_requirements buff_req;
 	void *mem_client;
 	struct v4l2_ctrl_handler ctrl_handler;
@@ -232,6 +241,7 @@
 	struct msm_vidc_debug debug;
 	struct buf_count count;
 	enum msm_vidc_modes flags;
+	u32 multi_stream_mode;
 	struct msm_vidc_core_capability capability;
 	enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM];
 	struct list_head registered_bufs;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 05d1840..cc07806 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -186,6 +186,14 @@
 	HAL_UNUSED_DOMAIN = 0x10000000,
 };
 
+enum multi_stream {
+	HAL_VIDEO_DECODER_NONE = 0x00000000,
+	HAL_VIDEO_DECODER_PRIMARY = 0x00000001,
+	HAL_VIDEO_DECODER_SECONDARY = 0x00000002,
+	HAL_VIDEO_DECODER_BOTH_OUTPUTS = 0x00000004,
+	HAL_VIDEO_UNUSED_OUTPUTS = 0x10000000,
+};
+
 enum hal_core_capabilities {
 	HAL_VIDEO_ENCODER_ROTATION_CAPABILITY = 0x00000001,
 	HAL_VIDEO_ENCODER_SCALING_CAPABILITY = 0x00000002,
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index e6804e1..7e4ceb3 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -813,6 +813,11 @@
 		*min = 0;
 		*max = *step = 1;
 		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE:
+		*type = V4L2_CTRL_TYPE_BOOLEAN;
+		*min = 0;
+		*max = *step = 1;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index db508fe..560accb 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1897,6 +1897,12 @@
 	V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED = 0,
 	V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED = 1
 };
+#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 35)
+enum v4l2_mpeg_vidc_video_decoder_multi_stream {
+	V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY = 1,
+};
 
 /*  Camera class control IDs */
 #define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)