Эх сурвалжийг харах

video: driver: skip lazy unmap for dec output buffers

- add support to skip lazy unmapping for decoder
  output buffers only. Map twice for these buffers.
- v4l2 client owned RO SMMU mapped buffers are
  maintained separately in read_only list.
- For FBD with RO flag from fw, add buffer to read_only
  list. Else remove from this list.
- During FTB, if buffer present in read_only list,
  add RO flag to hfi buffer and queue to fw.
- During streamoff output, unmap all buffers except
  those present in read_only list.
- During close, unmap completely and clean up read_only
  list, output list.

Change-Id: Iacee7d298dfbff0b9cb6f17dff27ad98574b489b
Signed-off-by: Darshana Patil <[email protected]>
Darshana Patil 4 жил өмнө
parent
commit
19cdce1711

+ 2 - 0
driver/vidc/inc/msm_vidc_driver.h

@@ -310,6 +310,8 @@ int msm_vidc_destroy_internal_buffer(struct msm_vidc_inst *inst,
 void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst);
 int msm_vidc_flush_buffers(struct msm_vidc_inst* inst,
 	enum msm_vidc_buffer_type type);
+int msm_vidc_flush_delayed_unmap_buffers(struct msm_vidc_inst *inst,
+		enum msm_vidc_buffer_type type);
 struct msm_vidc_buffer *get_meta_buffer(struct msm_vidc_inst *inst,
 	struct msm_vidc_buffer *vbuf);
 struct msm_vidc_inst *get_inst_ref(struct msm_vidc_core *core,

+ 1 - 0
driver/vidc/inc/msm_vidc_inst.h

@@ -56,6 +56,7 @@ struct msm_vidc_mappings_info {
 struct msm_vidc_buffers_info {
 	struct msm_vidc_buffers        input;
 	struct msm_vidc_buffers        output;
+	struct msm_vidc_buffers        read_only;
 	struct msm_vidc_buffers        input_meta;
 	struct msm_vidc_buffers        output_meta;
 	struct msm_vidc_buffers        bin;

+ 2 - 0
driver/vidc/inc/msm_vidc_internal.h

@@ -141,6 +141,7 @@ enum msm_vidc_buffer_type {
 	MSM_VIDC_BUF_OUTPUT        = 2,
 	MSM_VIDC_BUF_INPUT_META    = 3,
 	MSM_VIDC_BUF_OUTPUT_META   = 4,
+	MSM_VIDC_BUF_READ_ONLY     = 5,
 	MSM_VIDC_BUF_QUEUE         = 10,
 	MSM_VIDC_BUF_BIN           = 20,
 	MSM_VIDC_BUF_ARP           = 21,
@@ -757,6 +758,7 @@ struct msm_vidc_map {
 	u64                         device_addr;
 	struct sg_table            *table;
 	struct dma_buf_attachment  *attach;
+	u32                         skip_delayed_unmap:1;
 };
 
 struct msm_vidc_mappings {

+ 2 - 0
driver/vidc/inc/msm_vidc_memory.h

@@ -17,6 +17,8 @@ int msm_vidc_memory_map(struct msm_vidc_core *core,
 	struct msm_vidc_map *map);
 int msm_vidc_memory_unmap(struct msm_vidc_core *core,
 	struct msm_vidc_map *map);
+int msm_vidc_memory_unmap_completely(struct msm_vidc_core *core,
+	struct msm_vidc_map *map);
 struct dma_buf *msm_vidc_memory_get_dmabuf(int fd);
 void msm_vidc_memory_put_dmabuf(void *dmabuf);
 

+ 1 - 0
driver/vidc/src/msm_vidc.c

@@ -749,6 +749,7 @@ void *msm_vidc_open(void *vidc_core, u32 session_type)
 	INIT_LIST_HEAD(&inst->buffers.input_meta.list);
 	INIT_LIST_HEAD(&inst->buffers.output.list);
 	INIT_LIST_HEAD(&inst->buffers.output_meta.list);
+	INIT_LIST_HEAD(&inst->buffers.read_only.list);
 	INIT_LIST_HEAD(&inst->buffers.bin.list);
 	INIT_LIST_HEAD(&inst->buffers.arp.list);
 	INIT_LIST_HEAD(&inst->buffers.comv.list);

+ 230 - 108
driver/vidc/src/msm_vidc_driver.c

@@ -881,6 +881,8 @@ struct msm_vidc_buffers *msm_vidc_get_buffers(
 		return &inst->buffers.output;
 	case MSM_VIDC_BUF_OUTPUT_META:
 		return &inst->buffers.output_meta;
+	case MSM_VIDC_BUF_READ_ONLY:
+		return &inst->buffers.read_only;
 	case MSM_VIDC_BUF_BIN:
 		return &inst->buffers.bin;
 	case MSM_VIDC_BUF_ARP:
@@ -1747,6 +1749,65 @@ static int vb2_buffer_to_driver(struct vb2_buffer *vb2,
 	return rc;
 }
 
+int msm_vidc_process_readonly_buffers(struct msm_vidc_inst *inst,
+	struct msm_vidc_buffer *buf)
+{
+	int rc = 0;
+	struct msm_vidc_buffer *ro_buf, *dummy;
+	struct msm_vidc_buffers *ro_buffers;
+
+	if (!inst || !buf) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!is_decode_session(inst) || !is_output_buffer(buf->type))
+		return 0;
+
+	ro_buffers = msm_vidc_get_buffers(inst, MSM_VIDC_BUF_READ_ONLY, __func__);
+	if (!ro_buffers)
+		return -EINVAL;
+
+	/*
+	 * check if buffer present in ro_buffers list
+	 * if present: add ro flag to buf and remove from ro_buffers list
+	 * if not present: do nothing
+	 */
+	list_for_each_entry_safe(ro_buf, dummy, &ro_buffers->list, list) {
+		if (ro_buf->device_addr == buf->device_addr) {
+			buf->attr |= MSM_VIDC_ATTR_READ_ONLY;
+			print_vidc_buffer(VIDC_LOW, "low", "ro buf removed", inst, ro_buf);
+			list_del(&ro_buf->list);
+			kfree(ro_buf);
+			break;
+		}
+	}
+	return rc;
+}
+
+int msm_vidc_unmap_buffers(struct msm_vidc_inst *inst,
+	enum msm_vidc_buffer_type type)
+{
+	int rc = 0;
+	struct msm_vidc_mappings *mappings;
+	struct msm_vidc_map *map, *dummy;
+
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	mappings = msm_vidc_get_mappings(inst, type, __func__);
+	if (!mappings)
+		return -EINVAL;
+
+	list_for_each_entry_safe(map, dummy, &mappings->list, list) {
+		msm_vidc_memory_unmap_completely(inst->core, map);
+	}
+
+	return rc;
+}
+
 int msm_vidc_unmap_driver_buf(struct msm_vidc_inst *inst,
 	struct msm_vidc_buffer *buf)
 {
@@ -1786,32 +1847,16 @@ int msm_vidc_unmap_driver_buf(struct msm_vidc_inst *inst,
 	if (!map->refcount) {
 		list_del(&map->list);
 		kfree(map);
+		map = NULL;
+	} else {
+		/* we should not be here except decoder output buffer */
+		if (!is_decode_session(inst) || !is_output_buffer(buf->type)) {
+			print_vidc_buffer(VIDC_ERR, "err ", "non zero refcount found", inst, buf);
+			return -EINVAL;
+		}
 	}
 
-	return 0;
-}
-
-int msm_vidc_put_driver_buf(struct msm_vidc_inst *inst,
-	struct msm_vidc_buffer *buf)
-{
-	int rc = 0;
-
-	if (!inst || !buf) {
-		d_vpr_e("%s: invalid params\n", __func__);
-		return -EINVAL;
-	}
-
-	rc = msm_vidc_unmap_driver_buf(inst, buf);
-	if (rc)
-		return rc;
-
-	msm_vidc_memory_put_dmabuf(buf->dmabuf);
-
-	/* delete the buffer from buffers->list */
-	list_del(&buf->list);
-	kfree(buf);
-
-	return 0;
+	return rc;
 }
 
 int msm_vidc_map_driver_buf(struct msm_vidc_inst *inst,
@@ -1819,7 +1864,7 @@ int msm_vidc_map_driver_buf(struct msm_vidc_inst *inst,
 {
 	int rc = 0;
 	struct msm_vidc_mappings *mappings;
-	struct msm_vidc_map *map = NULL;
+	struct msm_vidc_map *map;
 	bool found = false;
 
 	if (!inst || !buf) {
@@ -1831,52 +1876,78 @@ int msm_vidc_map_driver_buf(struct msm_vidc_inst *inst,
 	if (!mappings)
 		return -EINVAL;
 
-	/* check if it is an existing one */
+	/*
+	 * new buffer: map twice for lazy unmap feature sake
+	 * existing buffer: map once
+	 */
 	list_for_each_entry(map, &mappings->list, list) {
 		if (map->dmabuf == buf->dmabuf) {
 			found = true;
 			break;
 		}
 	}
-	if (found) {
-		/* skip mapping for RO buffer */
-		if (!(buf->attr & MSM_VIDC_ATTR_READ_ONLY)) {
+	if (!found) {
+		/* new buffer case */
+		map = kzalloc(sizeof(struct msm_vidc_map), GFP_KERNEL);
+		if (!map) {
+			i_vpr_e(inst, "%s: alloc failed\n", __func__);
+			return -ENOMEM;
+		}
+		INIT_LIST_HEAD(&map->list);
+		map->type = buf->type;
+		map->dmabuf = msm_vidc_memory_get_dmabuf(buf->fd);
+		if (!map->dmabuf)
+			return -EINVAL;
+		map->region = msm_vidc_get_buffer_region(inst, buf->type, __func__);
+		/* lazy unmap feature not needed for decoder output buffers */
+		if (is_decode_session(inst) && is_output_buffer(buf->type)) {
+			map->skip_delayed_unmap = 1;
 			rc = msm_vidc_memory_map(inst->core, map);
-			if (rc)
-				return -ENOMEM;
-			buf->device_addr = map->device_addr;
+			if (rc) {
+				msm_vidc_memory_put_dmabuf(map->dmabuf);
+				kfree(map);
+				return rc;
+			}
 		}
-		return 0;
-	}
-	map = kzalloc(sizeof(struct msm_vidc_map), GFP_KERNEL);
-	if (!map) {
-		i_vpr_e(inst, "%s: alloc failed\n", __func__);
-		return -ENOMEM;
+		list_add_tail(&map->list, &mappings->list);
 	}
-	INIT_LIST_HEAD(&map->list);
-	map->type = buf->type;
-	map->dmabuf = buf->dmabuf;
-	map->region = msm_vidc_get_buffer_region(inst, buf->type, __func__);
 	rc = msm_vidc_memory_map(inst->core, map);
-	if (rc) {
-		kfree(map);
-		return -ENOMEM;
-	}
+	if (rc)
+		return rc;
+
 	buf->device_addr = map->device_addr;
-	list_add_tail(&map->list, &mappings->list);
 
 	return 0;
 }
 
+int msm_vidc_put_driver_buf(struct msm_vidc_inst *inst,
+	struct msm_vidc_buffer *buf)
+{
+	int rc = 0;
+
+	if (!inst || !buf) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	msm_vidc_unmap_driver_buf(inst, buf);
+
+	msm_vidc_memory_put_dmabuf(buf->dmabuf);
+
+	/* delete the buffer from buffers->list */
+	list_del(&buf->list);
+	kfree(buf);
+
+	return rc;
+}
+
 struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
 	struct vb2_buffer *vb2)
 {
 	int rc = 0;
 	struct msm_vidc_buffer *buf = NULL;
 	struct msm_vidc_buffers *buffers;
-	struct dma_buf *dmabuf;
 	enum msm_vidc_buffer_type buf_type;
-	bool found = false;
 
 	if (!inst || !vb2) {
 		d_vpr_e("%s: invalid params\n", __func__);
@@ -1891,58 +1962,25 @@ struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
 	if (!buffers)
 		return NULL;
 
-	dmabuf = msm_vidc_memory_get_dmabuf(vb2->planes[0].m.fd);
-	if (!dmabuf)
+	buf = kzalloc(sizeof(struct msm_vidc_buffer), GFP_KERNEL);
+	if (!buf) {
+		i_vpr_e(inst, "%s: alloc failed\n", __func__);
 		return NULL;
-
-	/* check if it is an existing buffer */
-	list_for_each_entry(buf, &buffers->list, list) {
-		if (buf->dmabuf == dmabuf &&
-		    buf->data_offset == vb2->planes[0].data_offset) {
-			found = true;
-			break;
-		}
-	}
-	if (found) {
-		/* only YUV buffers are allowed to repeat */
-		if ((is_decode_session(inst) && vb2->type != OUTPUT_MPLANE) ||
-		    (is_encode_session(inst) && vb2->type != INPUT_MPLANE)) {
-			print_vidc_buffer(VIDC_ERR, "err ",
-				"existing buffer", inst, buf);
-			goto error;
-		}
-		/* for decoder, YUV with RO flag are allowed to repeat */
-		if (is_decode_session(inst) &&
-		    !(buf->attr & MSM_VIDC_ATTR_READ_ONLY)) {
-			print_vidc_buffer(VIDC_ERR, "err ",
-				"existing buffer without RO flag", inst, buf);
-			goto error;
-		}
-		/* for encoder, treat the repeated buffer as new buffer */
-		if (is_encode_session(inst) && vb2->type == INPUT_MPLANE)
-			found = false;
 	}
-	if (!found) {
-		buf = kzalloc(sizeof(struct msm_vidc_buffer), GFP_KERNEL);
-		if (!buf) {
-			i_vpr_e(inst, "%s: alloc failed\n", __func__);
-			goto error;
-		}
-		buf->dmabuf = dmabuf;
-		INIT_LIST_HEAD(&buf->list);
-		list_add_tail(&buf->list, &buffers->list);
-	} else {
-		/* reset all attributes except read only */
-		buf->attr &= MSM_VIDC_ATTR_READ_ONLY;
-	}
-
-	/* treat every buffer as deferred buffer initially */
-	buf->attr |= MSM_VIDC_ATTR_DEFERRED;
+	INIT_LIST_HEAD(&buf->list);
+	list_add_tail(&buf->list, &buffers->list);
 
 	rc = vb2_buffer_to_driver(vb2, buf);
 	if (rc)
 		goto error;
 
+	buf->dmabuf = msm_vidc_memory_get_dmabuf(buf->fd);
+	if (!buf->dmabuf)
+		goto error;
+
+	/* treat every buffer as deferred buffer initially */
+	buf->attr |= MSM_VIDC_ATTR_DEFERRED;
+
 	rc = msm_vidc_map_driver_buf(inst, buf);
 	if (rc)
 		goto error;
@@ -1950,9 +1988,9 @@ struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
 	return buf;
 
 error:
-	msm_vidc_memory_put_dmabuf(dmabuf);
-	if (!found)
-		kfree(buf);
+	msm_vidc_memory_put_dmabuf(buf->dmabuf);
+	list_del(&buf->list);
+	kfree(buf);
 	return NULL;
 }
 
@@ -2159,6 +2197,12 @@ static int msm_vidc_queue_buffer(struct msm_vidc_inst *inst, struct msm_vidc_buf
 		msm_vidc_update_cap_value(inst, CODEC_CONFIG, 0, __func__);
 	}
 
+	if (is_decode_session(inst) && is_output_buffer(buf->type)) {
+		rc = msm_vidc_process_readonly_buffers(inst, buf);
+		if (rc)
+			return rc;
+	}
+
 	print_vidc_buffer(VIDC_HIGH, "high", "qbuf", inst, buf);
 	meta = get_meta_buffer(inst, buf);
 	if (meta)
@@ -3039,6 +3083,7 @@ int msm_vidc_session_streamoff(struct msm_vidc_inst *inst,
 
 	/* flush deferred buffers */
 	msm_vidc_flush_buffers(inst, buffer_type);
+	msm_vidc_flush_delayed_unmap_buffers(inst, buffer_type);
 	return 0;
 
 error:
@@ -3694,7 +3739,7 @@ int msm_vidc_flush_buffers(struct msm_vidc_inst* inst,
 		buffer_type[1] = MSM_VIDC_BUF_OUTPUT;
 	} else {
 		i_vpr_h(inst, "%s: invalid buffer type %d\n",
-				__func__, type);
+			__func__, type);
 		return -EINVAL;
 	}
 
@@ -3706,8 +3751,9 @@ int msm_vidc_flush_buffers(struct msm_vidc_inst* inst,
 		list_for_each_entry_safe(buf, dummy, &buffers->list, list) {
 			if (buf->attr & MSM_VIDC_ATTR_QUEUED ||
 				buf->attr & MSM_VIDC_ATTR_DEFERRED) {
-				print_vidc_buffer(VIDC_ERR, "err ", "flushing buffer", inst, buf);
-				msm_vidc_vb2_buffer_done(inst, buf);
+				print_vidc_buffer(VIDC_HIGH, "high", "flushing buffer", inst, buf);
+				if (!(buf->attr & MSM_VIDC_ATTR_BUFFER_DONE))
+					msm_vidc_vb2_buffer_done(inst, buf);
 				msm_vidc_put_driver_buf(inst, buf);
 			}
 		}
@@ -3716,15 +3762,75 @@ int msm_vidc_flush_buffers(struct msm_vidc_inst* inst,
 	return rc;
 }
 
+int msm_vidc_flush_delayed_unmap_buffers(struct msm_vidc_inst *inst,
+		enum msm_vidc_buffer_type type)
+{
+	int rc = 0;
+	struct msm_vidc_mappings *maps;
+	struct msm_vidc_map *map, *dummy;
+	struct msm_vidc_buffer *ro_buf, *ro_dummy;
+	enum msm_vidc_buffer_type buffer_type[2];
+	int i;
+	bool found = false;
+
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (type == MSM_VIDC_BUF_INPUT) {
+		buffer_type[0] = MSM_VIDC_BUF_INPUT_META;
+		buffer_type[1] = MSM_VIDC_BUF_INPUT;
+	} else if (type == MSM_VIDC_BUF_OUTPUT) {
+		buffer_type[0] = MSM_VIDC_BUF_OUTPUT_META;
+		buffer_type[1] = MSM_VIDC_BUF_OUTPUT;
+	} else {
+		i_vpr_h(inst, "%s: invalid buffer type %d\n",
+			__func__, type);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(buffer_type); i++) {
+		maps = msm_vidc_get_mappings(inst, buffer_type[i], __func__);
+		if (!maps)
+			return -EINVAL;
+
+		list_for_each_entry_safe(map, dummy, &maps->list, list) {
+			/*
+			 * decoder output bufs will have skip_delayed_unmap = true
+			 * unmap all decoder output buffers except those present in
+			 * read_only buffers list
+			 */
+			if (!map->skip_delayed_unmap)
+				continue;
+			found = false;
+			list_for_each_entry_safe(ro_buf, ro_dummy,
+					&inst->buffers.read_only.list, list) {
+				if (map->dmabuf == ro_buf->dmabuf) {
+					found = true;
+					break;
+				}
+			}
+			/* completely unmap */
+			if (!found)
+				msm_vidc_memory_unmap_completely(inst->core, map);
+		}
+	}
+
+	return rc;
+}
+
 void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst)
 {
 	struct msm_vidc_buffers *buffers;
 	struct msm_vidc_buffer *buf, *dummy;
-	static const enum msm_vidc_buffer_type buf_types[] = {
+	static const enum msm_vidc_buffer_type ext_buf_types[] = {
 		MSM_VIDC_BUF_INPUT,
 		MSM_VIDC_BUF_OUTPUT,
 		MSM_VIDC_BUF_INPUT_META,
 		MSM_VIDC_BUF_OUTPUT_META,
+	};
+	static const enum msm_vidc_buffer_type internal_buf_types[] = {
 		MSM_VIDC_BUF_BIN,
 		MSM_VIDC_BUF_ARP,
 		MSM_VIDC_BUF_COMV,
@@ -3741,19 +3847,35 @@ void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst)
 		return;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(buf_types); i++) {
-		buffers = msm_vidc_get_buffers(inst, buf_types[i], __func__);
+	for (i = 0; i < ARRAY_SIZE(internal_buf_types); i++) {
+		buffers = msm_vidc_get_buffers(inst, internal_buf_types[i], __func__);
 		if (!buffers)
 			continue;
 		list_for_each_entry_safe(buf, dummy, &buffers->list, list) {
 			i_vpr_h(inst,
-				"destroying buffer: type %d idx %d fd %d addr %#x size %d\n",
+				"destroying internal buffer: type %d idx %d fd %d addr %#x size %d\n",
 				buf->type, buf->index, buf->fd, buf->device_addr, buf->buffer_size);
-			if (is_internal_buffer(buf->type))
-				msm_vidc_destroy_internal_buffer(inst, buf);
-			else
-				msm_vidc_put_driver_buf(inst, buf);
+			msm_vidc_destroy_internal_buffer(inst, buf);
+		}
+		msm_vidc_unmap_buffers(inst, internal_buf_types[i]);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ext_buf_types); i++) {
+		buffers = msm_vidc_get_buffers(inst, ext_buf_types[i], __func__);
+		if (!buffers)
+			continue;
+
+		list_for_each_entry_safe(buf, dummy, &buffers->list, list) {
+			print_vidc_buffer(VIDC_ERR, "err", "destroying ", inst, buf);
+			msm_vidc_put_driver_buf(inst, buf);
 		}
+		msm_vidc_unmap_buffers(inst, ext_buf_types[i]);
+	}
+
+	list_for_each_entry_safe(buf, dummy, &inst->buffers.read_only.list, list) {
+		print_vidc_buffer(VIDC_ERR, "err", "destroying ro buffer", inst, buf);
+		list_del(&buf->list);
+		kfree(buf);
 	}
 }
 

+ 46 - 6
driver/vidc/src/msm_vidc_memory.c

@@ -116,11 +116,14 @@ int msm_vidc_memory_map(struct msm_vidc_core *core, struct msm_vidc_map *map)
 		goto error_attach;
 	}
 
-	/*
-	 * Get the scatterlist for the given attachment
-	 * Mapping of sg is taken care by map attachment
-	 */
-	attach->dma_map_attrs = DMA_ATTR_DELAYED_UNMAP;
+	if (!map->skip_delayed_unmap) {
+		/*
+		 * Get the scatterlist for the given attachment
+		 * Mapping of sg is taken care by map attachment
+		 */
+		attach->dma_map_attrs |= DMA_ATTR_DELAYED_UNMAP;
+	}
+
 	/*
 	 * We do not need dma_map function to perform cache operations
 	 * on the whole buffer size and hence pass skip sync flag.
@@ -148,6 +151,11 @@ int msm_vidc_memory_map(struct msm_vidc_core *core, struct msm_vidc_map *map)
 	map->table = table;
 	map->attach = attach;
 	map->refcount++;
+
+	d_vpr_l(
+		"%s: type %d device_addr %#x refcount %d region %d\n",
+		__func__, map->type, map->device_addr, map->refcount, map->region);
+
 	return 0;
 
 error_sg:
@@ -159,7 +167,8 @@ error_cb:
 	return rc;
 }
 
-int msm_vidc_memory_unmap(struct msm_vidc_core *core, struct msm_vidc_map *map)
+int msm_vidc_memory_unmap(struct msm_vidc_core *core,
+	struct msm_vidc_map *map)
 {
 	int rc = 0;
 
@@ -178,6 +187,10 @@ int msm_vidc_memory_unmap(struct msm_vidc_core *core, struct msm_vidc_map *map)
 	if (map->refcount)
 		goto exit;
 
+	d_vpr_l(
+		"%s: type %d device_addr %#x refcount %d region %d\n",
+		__func__, map->type, map->device_addr, map->refcount, map->region);
+
 	dma_buf_unmap_attachment(map->attach, map->table, DMA_BIDIRECTIONAL);
 	dma_buf_detach(map->dmabuf, map->attach);
 
@@ -190,6 +203,33 @@ exit:
 	return rc;
 }
 
+int msm_vidc_memory_unmap_completely(struct msm_vidc_core *core,
+	struct msm_vidc_map *map)
+{
+	int rc = 0;
+
+	if (!core || !map) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!map->refcount)
+		return 0;
+
+	while (map->refcount) {
+		rc = msm_vidc_memory_unmap(core, map);
+		if (rc)
+			break;
+		if (!map->refcount) {
+			list_del(&map->list);
+			kfree(map);
+			map = NULL;
+			break;
+		}
+	}
+	return rc;
+}
+
 int msm_vidc_memory_alloc(struct msm_vidc_core *core, struct msm_vidc_alloc *mem)
 {
 	int rc = 0;

+ 81 - 20
driver/vidc/src/venus_hfi_response.c

@@ -481,6 +481,61 @@ static int get_driver_buffer_flags(struct msm_vidc_inst *inst, u32 hfi_flags)
 	return driver_flags;
 }
 
+static int handle_read_only_buffer(struct msm_vidc_inst *inst,
+	struct msm_vidc_buffer *buffer)
+{
+	struct msm_vidc_buffer *ro_buf;
+	struct msm_vidc_buffers *ro_buffers;
+	bool found = false;
+
+	if (!inst || !buffer) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!is_decode_session(inst) || !is_output_buffer(buffer->type))
+		return 0;
+
+	ro_buffers = msm_vidc_get_buffers(inst, MSM_VIDC_BUF_READ_ONLY, __func__);
+	if (!ro_buffers)
+		return -EINVAL;
+
+	list_for_each_entry(ro_buf, &ro_buffers->list, list) {
+		if (ro_buf->device_addr == buffer->device_addr) {
+			found = true;
+			break;
+		}
+	}
+
+	/*
+	 * RO flag: add to read_only list if buffer is not present
+	 *          if present, do nothing
+	 * Without RO flag: remove buffer from read_only list if present
+	 *          if not present, do not error out
+	 */
+	if (buffer->attr & MSM_VIDC_ATTR_READ_ONLY) {
+		if (!found) {
+			ro_buf = kmemdup(buffer, sizeof(struct msm_vidc_buffer), GFP_KERNEL);
+			if (!ro_buf) {
+				i_vpr_e(inst, "%s: buffer alloc failed\n", __func__);
+				return -ENOMEM;
+			}
+			INIT_LIST_HEAD(&ro_buf->list);
+			list_add_tail(&ro_buf->list, &ro_buffers->list);
+			print_vidc_buffer(VIDC_LOW, "low", "ro buf added", inst, ro_buf);
+		}
+	} else {
+		if (found) {
+			print_vidc_buffer(VIDC_LOW, "low", "ro buf deleted", inst, ro_buf);
+			list_del(&ro_buf->list);
+			kfree(ro_buf);
+			ro_buf = NULL;
+		}
+	}
+
+	return 0;
+}
+
 static int handle_input_buffer(struct msm_vidc_inst *inst,
 	struct hfi_buffer *buffer)
 {
@@ -502,14 +557,15 @@ static int handle_input_buffer(struct msm_vidc_inst *inst,
 
 	found = false;
 	list_for_each_entry(buf, &buffers->list, list) {
-		if (buf->device_addr == buffer->base_address) {
+		if (buf->index == buffer->index) {
 			found = true;
 			break;
 		}
 	}
 	if (!found) {
-		i_vpr_e(inst, "%s: buffer not found for idx %d addr %#x\n",
-			__func__, buffer->index, buffer->base_address);
+		i_vpr_e(inst, "%s: invalid buffer idx %d addr %#x data_offset %d\n",
+			__func__, buffer->index, buffer->base_address,
+			buffer->data_offset);
 		return -EINVAL;
 	}
 
@@ -556,14 +612,15 @@ static int handle_output_buffer(struct msm_vidc_inst *inst,
 
 	found = false;
 	list_for_each_entry(buf, &buffers->list, list) {
-		if (buf->device_addr == buffer->base_address) {
+		if (buf->index == buffer->index) {
 			found = true;
 			break;
 		}
 	}
 	if (!found) {
-		i_vpr_e(inst, "%s: invalid idx %d daddr %#x\n",
-			__func__, buffer->index, buffer->base_address);
+		i_vpr_e(inst, "%s: invalid idx %d daddr %#x data_offset %d\n",
+			__func__, buffer->index, buffer->base_address,
+			buffer->data_offset);
 		return -EINVAL;
 	}
 	buf->data_offset = buffer->data_offset;
@@ -615,11 +672,15 @@ static int handle_output_buffer(struct msm_vidc_inst *inst,
 		}
 	}
 
-	if (buffer->flags & HFI_BUF_FW_FLAG_READONLY)
-		buf->attr |= MSM_VIDC_ATTR_READ_ONLY;
-	else
-		buf->attr &= ~MSM_VIDC_ATTR_READ_ONLY;
-
+	if (is_decode_session(inst)) {
+		if (buffer->flags & HFI_BUF_FW_FLAG_READONLY)
+			buf->attr |= MSM_VIDC_ATTR_READ_ONLY;
+		else
+			buf->attr &= ~MSM_VIDC_ATTR_READ_ONLY;
+		rc = handle_read_only_buffer(inst, buf);
+		if (rc)
+			return rc;
+	}
 
 	buf->flags = 0;
 	buf->flags = get_driver_buffer_flags(inst, buffer->flags);
@@ -651,14 +712,15 @@ static int handle_input_metadata_buffer(struct msm_vidc_inst *inst,
 
 	found = false;
 	list_for_each_entry(buf, &buffers->list, list) {
-		if (buf->device_addr == buffer->base_address) {
+		if (buf->index == buffer->index) {
 			found = true;
 			break;
 		}
 	}
 	if (!found) {
-		i_vpr_e(inst, "%s: invalid idx %d daddr %#x\n",
-			__func__, buffer->index, buffer->base_address);
+		i_vpr_e(inst, "%s: invalid idx %d daddr %#x data_offset %d\n",
+			__func__, buffer->index, buffer->base_address,
+			buffer->data_offset);
 		return -EINVAL;
 	}
 	/* attach dequeued flag for, only last frame in the batch */
@@ -701,14 +763,15 @@ static int handle_output_metadata_buffer(struct msm_vidc_inst *inst,
 
 	found = false;
 	list_for_each_entry(buf, &buffers->list, list) {
-		if (buf->device_addr == buffer->base_address) {
+		if (buf->index == buffer->index) {
 			found = true;
 			break;
 		}
 	}
 	if (!found) {
-		i_vpr_e(inst, "%s: invalid idx %d daddr %#x\n",
-			__func__, buffer->index, buffer->base_address);
+		i_vpr_e(inst, "%s: invalid idx %d daddr %#x data_offset %d\n",
+			__func__, buffer->index, buffer->base_address,
+			buffer->data_offset);
 		return -EINVAL;
 	}
 
@@ -756,9 +819,7 @@ static int handle_dequeue_buffers(struct msm_vidc_inst* inst)
 					buf->attr |= MSM_VIDC_ATTR_BUFFER_DONE;
 					msm_vidc_vb2_buffer_done(inst, buf);
 				}
-				/* do not unmap / delete read only buffer */
-				if (!(buf->attr & MSM_VIDC_ATTR_READ_ONLY))
-					msm_vidc_put_driver_buf(inst, buf);
+				msm_vidc_put_driver_buf(inst, buf);
 			}
 		}
 	}