msm: vidc: handle release ref events for Dynamic Buffers

For dynamic output buffers, firmware can hold on to a
output buffer reference after releasing it to
driver. The buffer is marked as read only and sent
back to client. The driver maintains the reference
of this buffer and decrements it when firmware
sends the release reference event. The client is
notified of the release reference event.

Change-Id: Ie3c014af255e09f6dac9b3b853c9994b97d830b6
Signed-off-by: Arun Menon <avmenon@codeaurora.org>
(cherry picked from commit 83399dcd749ed49e6f67224e580da74bc6fa5d9c)
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 3f89d8b..d930d26 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -163,6 +163,39 @@
 	callback(VIDC_EVENT_CHANGE, &cmd_done);
 }
 
+static void hfi_process_evt_release_buffer_ref(
+		msm_vidc_callback callback, u32 device_id,
+		struct hfi_msg_event_notify_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+	struct msm_vidc_cb_event event_notify = {0};
+
+	struct hfi_msg_release_buffer_ref_event_packet *data;
+
+	dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY - release_buffer_reference");
+	if (sizeof(struct hfi_msg_event_notify_packet)
+		> pkt->size) {
+		dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size");
+		return;
+	}
+
+	data = (struct hfi_msg_release_buffer_ref_event_packet *)
+				pkt->rg_ext_event_data;
+
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id =
+		((struct hal_session *)data->output_tag)->session_id;
+	cmd_done.status = VIDC_ERR_NONE;
+	cmd_done.size = sizeof(struct msm_vidc_cb_event);
+
+	event_notify.hal_event_type = HAL_EVENT_RELEASE_BUFFER_REFERENCE;
+	event_notify.packet_buffer = data->packet_buffer;
+	event_notify.exra_data_buffer = data->exra_data_buffer;
+	cmd_done.data = &event_notify;
+	callback(VIDC_EVENT_CHANGE, &cmd_done);
+}
+
 static void hfi_process_sys_error(
 		msm_vidc_callback callback, u32 device_id)
 {
@@ -229,6 +262,10 @@
 	case HFI_EVENT_SESSION_PROPERTY_CHANGED:
 		dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED");
 		break;
+	case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
+		dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE\n");
+		hfi_process_evt_release_buffer_ref(callback, device_id, pkt);
+		break;
 	default:
 		dprintk(VIDC_WARN, "hal_process_event_notify:unkown_event_id");
 		break;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index fe6cfe8..b7034e0 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -31,6 +31,8 @@
 		V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT
 #define V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT \
 		V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT
+#define V4L2_EVENT_RELEASE_BUFFER_REFERENCE \
+		V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE
 
 #define NUM_MBS_PER_SEC(__height, __width, __fps) ({\
 	(__height >> 4) * (__width >> 4) * __fps; \
@@ -471,6 +473,60 @@
 		case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
 			event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
 			break;
+		case HAL_EVENT_RELEASE_BUFFER_REFERENCE:
+			{
+				struct v4l2_event buf_event = {0};
+				struct buffer_info *binfo = NULL;
+				u32 *ptr = NULL;
+
+				dprintk(VIDC_DBG,
+					"%s - inst: %p buffer: %p extra: %p\n",
+					__func__, inst,
+					event_notify->packet_buffer,
+					event_notify->exra_data_buffer);
+
+				/*
+				* Get the buffer_info entry for the
+				* device address.
+				*/
+				binfo = device_to_uvaddr(inst,
+					&inst->registered_bufs,
+					(u32)event_notify->packet_buffer);
+				if (!binfo) {
+					dprintk(VIDC_ERR,
+						"%s buffer not found in registered list\n",
+						__func__);
+					return;
+				}
+
+				/* Fill event data to be sent to client*/
+				buf_event.type =
+					V4L2_EVENT_RELEASE_BUFFER_REFERENCE;
+				ptr = (u32 *)buf_event.u.data;
+				ptr[0] = binfo->fd[0];
+				ptr[1] = binfo->buff_off[0];
+
+				dprintk(VIDC_DBG,
+					"RELEASE REFERENCE EVENT FROM F/W - fd = %d offset = %d\n",
+					ptr[0], ptr[1]);
+
+				/* Decrement buffer reference count*/
+				buf_ref_put(inst, binfo);
+
+				/*
+				* Release buffer and remove from list
+				* if reference goes to zero.
+				*/
+				if (unmap_and_deregister_buf(inst, binfo))
+					dprintk(VIDC_ERR,
+					"%s: buffer unmap failed\n", __func__);
+
+				/*send event to client*/
+				v4l2_event_queue_fh(&inst->event_handler,
+					&buf_event);
+				wake_up(&inst->kernel_event_queue);
+				return;
+			}
 		default:
 			break;
 		}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 42d6a37..a8f2c3e 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -284,8 +284,11 @@
 struct buffer_info *device_to_uvaddr(struct msm_vidc_inst *inst,
 			struct list_head *list, u32 device_addr);
 int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo);
+int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo);
 int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
 				struct buffer_info *binfo);
 int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
 			struct buffer_info *binfo);
+int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
+			struct buffer_info *binfo);
 #endif
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index af58e22..fc6d14d 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -19,6 +19,7 @@
 
 #define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3)
 #define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4)
+#define HFI_EVENT_RELEASE_BUFFER_REFERENCE (HFI_OX_BASE + 0x6)
 
 #define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES	\
 	(HFI_OX_BASE + 0x1)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index e5d6040..1c9b71d 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -864,6 +864,7 @@
 enum hal_event_type {
 	HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES,
 	HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES,
+	HAL_EVENT_RELEASE_BUFFER_REFERENCE,
 	HAL_UNUSED_SEQCHG = 0x10000000,
 };
 
@@ -932,6 +933,8 @@
 	u32 height;
 	u32 width;
 	u32 hal_event_type;
+	u8 *packet_buffer;
+	u8 *exra_data_buffer;
 };
 
 /* Data callback structure */
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 050a98d..6589a7cf 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -849,6 +849,12 @@
 	u32 rg_ext_event_data[1];
 };
 
+struct hfi_msg_release_buffer_ref_event_packet {
+	u8 *packet_buffer;
+	u8 *exra_data_buffer;
+	u32 output_tag;
+};
+
 struct hfi_msg_sys_init_done_packet {
 	u32 size;
 	u32 packet_type;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 433624f..bc19bf2 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -2452,7 +2452,8 @@
 		(V4L2_EVENT_MSM_VIDC_START + 3)
 #define V4L2_EVENT_MSM_VIDC_CLOSE_DONE	(V4L2_EVENT_MSM_VIDC_START + 4)
 #define V4L2_EVENT_MSM_VIDC_SYS_ERROR	(V4L2_EVENT_MSM_VIDC_START + 5)
-
+#define V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE \
+		(V4L2_EVENT_MSM_VIDC_START + 6)
 
 /* Payload for V4L2_EVENT_VSYNC */
 struct v4l2_event_vsync {