Forráskód Böngészése

video: driver: add fence support

Add fence support for decoder output buffers for
the consumer to use it to improve latency.

Change-Id: I7384b4a9793248988a2d2d546a535f26636d5bb3
Signed-off-by: Maheshwar Ajja <[email protected]>
Maheshwar Ajja 3 éve
szülő
commit
ef829ce44f

+ 1 - 0
Kbuild

@@ -69,6 +69,7 @@ msm_video-objs += driver/vidc/src/msm_vidc_v4l2.o \
                   driver/vidc/src/msm_vidc_platform.o \
                   driver/vidc/src/msm_vidc_debug.o \
                   driver/vidc/src/msm_vidc_memory.o \
+                  driver/vidc/src/msm_vidc_fence.o \
                   driver/vidc/src/venus_hfi.o \
                   driver/vidc/src/hfi_packet.o \
                   driver/vidc/src/venus_hfi_response.o

+ 21 - 0
driver/vidc/inc/msm_vidc_fence.h

@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __H_MSM_VIDC_FENCE_H__
+#define __H_MSM_VIDC_FENCE_H__
+
+#include "msm_vidc_inst.h"
+#include "msm_vidc_buffer.h"
+
+int msm_vidc_fence_create(struct msm_vidc_inst *inst,
+		struct msm_vidc_buffer *buf);
+int msm_vidc_fence_signal(struct msm_vidc_inst *inst,
+		struct msm_vidc_buffer *buf);
+void msm_vidc_fence_destroy(struct msm_vidc_inst *inst,
+		struct msm_vidc_buffer *buf);
+int msm_vidc_fence_init(struct msm_vidc_inst *inst);
+void msm_vidc_fence_deinit(struct msm_vidc_inst *inst);
+
+#endif // __H_MSM_VIDC_FENCE_H__

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

@@ -153,6 +153,7 @@ struct msm_vidc_inst {
 	struct msm_vidc_statistics         stats;
 	struct msm_vidc_inst_capability   *capabilities;
 	struct completion                  completions[MAX_SIGNAL];
+	struct msm_vidc_fence_context      fence;
 	enum priority_level                priority_level;
 	u32                                firmware_priority;
 	bool                               active;

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

@@ -9,6 +9,9 @@
 #include <linux/version.h>
 #include <linux/bits.h>
 #include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/sync_file.h>
+#include <linux/dma-fence.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -780,6 +783,20 @@ struct msm_vidc_power {
 	u32                    fw_cf;
 };
 
+struct msm_vidc_fence_context {
+        char name[MAX_NAME_LENGTH];
+        u64 ctx_num;
+        u64 seq_num;
+};
+
+struct msm_vidc_fence {
+        struct dma_fence dma_fence;
+        char name[MAX_NAME_LENGTH];
+        spinlock_t lock;
+        struct sync_file *sync_file;
+        int fd;
+};
+
 struct msm_vidc_alloc {
 	struct list_head            list;
 	enum msm_vidc_buffer_type   type;
@@ -827,6 +844,7 @@ struct msm_vidc_buffer {
 	u32                                flags;
 	u64                                timestamp;
 	enum msm_vidc_buffer_attributes    attr;
+	struct msm_vidc_fence             *fence;
 };
 
 struct msm_vidc_buffers {

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

@@ -16,6 +16,7 @@
 #include "msm_vidc_debug.h"
 #include "msm_vidc_control.h"
 #include "msm_vidc_power.h"
+#include "msm_vidc_fence.h"
 #include "msm_vidc_memory.h"
 #include "venus_hfi_response.h"
 #include "msm_vidc.h"
@@ -980,6 +981,10 @@ void *msm_vidc_open(void *vidc_core, u32 session_type)
 	if (rc)
 		goto error;
 
+	rc = msm_vidc_fence_init(inst);
+	if (rc)
+		goto error;
+
 	rc = msm_vidc_add_session(inst);
 	if (rc) {
 		i_vpr_e(inst, "%s: failed to get session id\n", __func__);

+ 21 - 3
driver/vidc/src/msm_vidc_driver.c

@@ -19,6 +19,7 @@
 #include "msm_vidc.h"
 #include "msm_vdec.h"
 #include "msm_venc.h"
+#include "msm_vidc_fence.h"
 #include "venus_hfi.h"
 #include "venus_hfi_response.h"
 #include "hfi_packet.h"
@@ -3255,6 +3256,7 @@ int msm_vidc_queue_buffer_single(struct msm_vidc_inst *inst, struct vb2_buffer *
 	int rc = 0;
 	struct msm_vidc_buffer *buf;
 	enum msm_vidc_allow allow;
+	const int fence_enabled = 0;
 
 	if (!inst || !vb2) {
 		d_vpr_e("%s: invalid params\n", __func__);
@@ -3265,21 +3267,35 @@ int msm_vidc_queue_buffer_single(struct msm_vidc_inst *inst, struct vb2_buffer *
 	if (!buf)
 		return -EINVAL;
 
+	if (fence_enabled && is_decode_session(inst) &&
+	    is_output_buffer(buf->type)) {
+		rc = msm_vidc_fence_create(inst, buf);
+		if (rc)
+			return rc;
+	}
+
 	allow = msm_vidc_allow_qbuf(inst, vb2->type);
 	if (allow == MSM_VIDC_DISALLOW) {
 		i_vpr_e(inst, "%s: qbuf not allowed\n", __func__);
-		return -EINVAL;
+		rc = -EINVAL;
+		goto exit;
 	} else if (allow == MSM_VIDC_DEFER) {
 		print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
-		return 0;
+		rc = 0;
+		goto exit;
 	}
 
 	msm_vidc_scale_power(inst, is_input_buffer(buf->type));
 
 	rc = msm_vidc_queue_buffer(inst, buf);
 	if (rc)
-		return rc;
+		goto exit;
 
+exit:
+	if (rc) {
+		i_vpr_e(inst, "%s: qbuf failed\n", __func__);
+		msm_vidc_fence_destroy(inst, buf);
+	}
 	return rc;
 }
 
@@ -5196,6 +5212,7 @@ void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst)
 
 		list_for_each_entry_safe(buf, dummy, &buffers->list, list) {
 			print_vidc_buffer(VIDC_ERR, "err ", "destroying ", inst, buf);
+			msm_vidc_fence_destroy(inst, buf);
 			if (!(buf->attr & MSM_VIDC_ATTR_BUFFER_DONE))
 				msm_vidc_vb2_buffer_done(inst, buf);
 			msm_vidc_put_driver_buf(inst, buf);
@@ -5244,6 +5261,7 @@ static void msm_vidc_close_helper(struct kref *kref)
 		struct msm_vidc_inst, kref);
 
 	i_vpr_h(inst, "%s()\n", __func__);
+	msm_vidc_fence_deinit(inst);
 	msm_vidc_event_queue_deinit(inst);
 	msm_vidc_vb2_queue_deinit(inst);
 	msm_vidc_debugfs_deinit_inst(inst);

+ 162 - 0
driver/vidc/src/msm_vidc_fence.c

@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "msm_vidc_fence.h"
+#include "msm_vidc_debug.h"
+
+static const char *msm_vidc_dma_fence_get_driver_name(struct dma_fence *df)
+{
+	struct msm_vidc_fence *fence;
+
+	if (df) {
+		fence = container_of(df, struct msm_vidc_fence, dma_fence);
+		return fence->name;
+	}
+	return "msm_vidc_dma_fence_get_driver_name: invalid fence";
+}
+
+static const char *msm_vidc_dma_fence_get_timeline_name(struct dma_fence *df)
+{
+	struct msm_vidc_fence *fence;
+
+	if (df) {
+		fence = container_of(df, struct msm_vidc_fence, dma_fence);
+		return fence->name;
+	}
+	return "msm_vidc_dma_fence_get_timeline_name: invalid fence";
+}
+
+static void msm_vidc_dma_fence_release(struct dma_fence *df)
+{
+	struct msm_vidc_fence *fence;
+
+	if (df) {
+		fence = container_of(df, struct msm_vidc_fence, dma_fence);
+		d_vpr_l("%s: name %s\n", __func__, fence->name);
+		kfree(fence);
+	} else {
+		d_vpr_e("%s: invalid fence\n", __func__);
+	}
+}
+
+static const struct dma_fence_ops msm_vidc_dma_fence_ops = {
+	.get_driver_name = msm_vidc_dma_fence_get_driver_name,
+	.get_timeline_name = msm_vidc_dma_fence_get_timeline_name,
+	.release = msm_vidc_dma_fence_release,
+};
+
+int msm_vidc_fence_create(struct msm_vidc_inst *inst,
+		struct msm_vidc_buffer *buf)
+{
+	int rc = 0;
+	struct msm_vidc_fence *fence;
+
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+	if (!fence)
+		return -ENOMEM;
+
+	spin_lock_init(&fence->lock);
+	dma_fence_init(&fence->dma_fence, &msm_vidc_dma_fence_ops,
+		&fence->lock, inst->fence.ctx_num, inst->fence.seq_num++);
+	snprintf(fence->name, sizeof(fence->name), "%s: %llu",
+		inst->fence.name, inst->fence.seq_num);
+
+	fence->fd = get_unused_fd_flags(0);
+	if (fence->fd < 0) {
+		i_vpr_e(inst, "%s: getting fd (%d) failed\n", __func__,
+			fence->fd);
+		rc = -EINVAL;
+		goto err_fd;
+	}
+	fence->sync_file = sync_file_create(&fence->dma_fence);
+	if (!fence->sync_file) {
+		i_vpr_e(inst, "%s: sync_file_create failed\n", __func__);
+		rc = -EINVAL;
+		goto err_sync_file;
+	}
+	fd_install(fence->fd, fence->sync_file->file);
+
+	buf->fence = fence;
+	i_vpr_h(inst, "%s: created %s\n", __func__, fence->name);
+	return 0;
+
+err_sync_file:
+	put_unused_fd(fence->fd);
+err_fd:
+	dma_fence_put(&fence->dma_fence);
+	buf->fence = NULL;
+	return rc;
+}
+
+int msm_vidc_fence_signal(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;
+	}
+	if (!buf->fence)
+		return 0;
+
+	i_vpr_l(inst, "%s: fence %s\n", __func__, buf->fence->name);
+	dma_fence_signal(&buf->fence->dma_fence);
+	dma_fence_put(&buf->fence->dma_fence);
+	buf->fence = NULL;
+
+	return rc;
+}
+
+void msm_vidc_fence_destroy(struct msm_vidc_inst *inst,
+		struct msm_vidc_buffer *buf)
+{
+	if (!inst || !buf) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return;
+	}
+	if (!buf->fence)
+		return;
+
+	i_vpr_e(inst, "%s: fence %s\n", __func__, buf->fence->name);
+	dma_fence_set_error(&buf->fence->dma_fence, -EINVAL);
+	dma_fence_signal(&buf->fence->dma_fence);
+	dma_fence_put(&buf->fence->dma_fence);
+	buf->fence = NULL;
+}
+
+int msm_vidc_fence_init(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	inst->fence.ctx_num = dma_fence_context_alloc(1);
+	snprintf(inst->fence.name, sizeof(inst->fence.name),
+		"msm_vidc_fence: %s: %llu", inst->debug_str,
+		inst->fence.ctx_num);
+	i_vpr_h(inst, "%s: %s\n", __func__, inst->fence.name);
+
+       return rc;
+}
+
+void msm_vidc_fence_deinit(struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return;
+	}
+	i_vpr_h(inst, "%s: %s\n", __func__, inst->fence.name);
+	inst->fence.ctx_num = 0;
+	snprintf(inst->fence.name, sizeof(inst->fence.name), "%s", "");
+}

+ 4 - 0
driver/vidc/src/venus_hfi_response.c

@@ -13,6 +13,7 @@
 #include "msm_vdec.h"
 #include "msm_vidc_control.h"
 #include "msm_vidc_memory.h"
+#include "msm_vidc_fence.h"
 
 #define in_range(range, val) (((range.begin) < (val)) && ((range.end) > (val)))
 
@@ -823,6 +824,9 @@ static int handle_output_buffer(struct msm_vidc_inst *inst,
 		return 0;
 	}
 
+	/* signal the fence asap */
+	msm_vidc_fence_signal(inst, buf);
+
 	buf->data_offset = buffer->data_offset;
 	buf->data_size = buffer->data_size;
 	buf->timestamp = buffer->timestamp;