Ver código fonte

video: driver: enable decoder batching support

Added change to enable decoder batching feature.

Batching willbe enabled only when below conditions were met.

[1] platform supports batching
[2] decode session
[3] realtime session
[4] non-thumbnail session
[5] non-heif session
[6] 2-stage decode only(low_latency not supported).

Change-Id: I54b601814c3b5fa2077dc41c5b0ac84964c2674a
Signed-off-by: Govindaraj Rajagopal <[email protected]>
Govindaraj Rajagopal 4 anos atrás
pai
commit
a7670d7f42

+ 2 - 0
driver/platform/waipio/src/msm_vidc_waipio.c

@@ -166,8 +166,10 @@ static struct msm_platform_inst_capability instance_data_waipio[] = {
 	/* (4096 * 2304) / 256 */
 	{LOSSLESS_MBPF, ENC, H264|HEVC, 64, 36864, 1, 36864},
 	/* Batch Mode Decode */
+	/* TODO: update with new values based on updated voltage corner */
 	{BATCH_MBPF, DEC, CODECS_ALL, 64, 34816, 1, 34816},
 	/* (4096 * 2304) / 256 */
+	{BATCH_FPS, DEC, CODECS_ALL, 1, 120, 1, 120},
 	{SECURE_MBPF, ENC|DEC, CODECS_ALL, 64, 36864, 1, 36864},
 	/* ((1920 * 1088) / 256) * 480 fps */
 	{MBPS, ENC, CODECS_ALL, 64, 3916800, 1, 3916800},

+ 4 - 3
driver/variant/iris2/src/msm_vidc_iris2.c

@@ -12,6 +12,7 @@
 #include "msm_vidc_inst.h"
 #include "msm_vidc_core.h"
 #include "msm_vidc_driver.h"
+#include "msm_vidc_control.h"
 #include "msm_vidc_dt.h"
 #include "msm_vidc_internal.h"
 #include "msm_vidc_buffer.h"
@@ -564,7 +565,7 @@ int msm_vidc_decide_work_mode_iris2(struct msm_vidc_inst* inst)
 
 	i_vpr_h(inst, "Configuring work mode = %u low latency = %u",
 		work_mode, lowlatency);
-	inst->capabilities->cap[STAGE].value = work_mode;
+	msm_vidc_update_cap_value(inst, STAGE, work_mode, __func__);
 
 	/* TODO If Encode then Set Low Latency (Enable/Disable)
 	 * and Update internal cap struct
@@ -609,7 +610,7 @@ int msm_vidc_decide_work_route_iris2(struct msm_vidc_inst* inst)
 	}
 
 	i_vpr_h(inst, "Configuring work route = %u", work_route);
-	inst->capabilities->cap[PIPE].value = work_route;
+	msm_vidc_update_cap_value(inst, PIPE, work_route, __func__);
 
 	return 0;
 }
@@ -643,7 +644,7 @@ int msm_vidc_decide_quality_mode_iris2(struct msm_vidc_inst* inst)
 		(mbpf <= max_hq_mbpf && mbps <= max_hq_mbps))
 		mode = MSM_VIDC_MAX_QUALITY_MODE;
 
-	capability->cap[QUALITY_MODE].value = mode;
+	msm_vidc_update_cap_value(inst, QUALITY_MODE, mode, __func__);
 
 	return 0;
 }

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

@@ -29,5 +29,6 @@ int msm_vdec_init_input_subcr_params(struct msm_vidc_inst *inst);
 int msm_vdec_input_port_settings_change(struct msm_vidc_inst *inst);
 int msm_vdec_output_port_settings_change(struct msm_vidc_inst *inst);
 int msm_vdec_process_cmd(struct msm_vidc_inst *inst, u32 cmd);
+int msm_vidc_queue_buffer_batch(struct msm_vidc_inst *inst);
 
 #endif // _MSM_VDEC_H_

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

@@ -70,5 +70,7 @@ int msm_vidc_v4l2_menu_to_hfi(struct msm_vidc_inst *inst,
 	enum msm_vidc_inst_capability_type cap_id, u32 *value);
 int msm_vidc_v4l2_to_hfi_enum(struct msm_vidc_inst *inst,
 	enum msm_vidc_inst_capability_type cap_id, u32 *value);
+int msm_vidc_update_cap_value(struct msm_vidc_inst *inst, u32 cap,
+	s32 adjusted_val, const char *func);
 
 #endif

+ 1 - 1
driver/vidc/inc/msm_vidc_core.h

@@ -88,8 +88,8 @@ struct msm_vidc_core {
 	struct workqueue_struct               *device_workq;
 	struct delayed_work                    pm_work;
 	struct workqueue_struct               *pm_workq;
+	struct workqueue_struct               *batch_workq;
 	struct delayed_work                    fw_unload_work;
-	struct delayed_work                    batch_work;
 	struct work_struct                     ssr_work;
 	struct msm_vidc_core_power             power;
 	struct msm_vidc_ssr                    ssr;

+ 7 - 1
driver/vidc/inc/msm_vidc_driver.h

@@ -162,6 +162,11 @@ static inline bool is_realtime_session(struct msm_vidc_inst *inst)
 	return !inst->capabilities->cap[PRIORITY].value;
 }
 
+static inline bool is_lowlatency_session(struct msm_vidc_inst *inst)
+{
+	return !!(inst->capabilities->cap[LOWLATENCY_MODE].value);
+}
+
 static inline bool is_active_session(u64 prev, u64 curr)
 {
 	u64 ts_delta;
@@ -270,7 +275,7 @@ int msm_vidc_map_driver_buf(struct msm_vidc_inst *inst,
 	struct msm_vidc_buffer *buf);
 int msm_vidc_put_driver_buf(struct msm_vidc_inst *inst,
 	struct msm_vidc_buffer *buf);
-int msm_vidc_queue_buffer(struct msm_vidc_inst *inst, struct vb2_buffer *vb2);
+int msm_vidc_queue_buffer_single(struct msm_vidc_inst *inst, struct vb2_buffer *vb2);
 int msm_vidc_destroy_internal_buffer(struct msm_vidc_inst *inst,
 	struct msm_vidc_buffer *buffer);
 void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst);
@@ -317,5 +322,6 @@ int msm_vidc_init_instance_caps(struct msm_vidc_core* core);
 int msm_vidc_deinit_core_caps(struct msm_vidc_core* core);
 int msm_vidc_deinit_instance_caps(struct msm_vidc_core* core);
 int msm_vidc_update_debug_str(struct msm_vidc_inst *inst);
+bool msm_vidc_allow_decode_batch(struct msm_vidc_inst *inst);
 #endif // _MSM_VIDC_DRIVER_H_
 

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

@@ -307,6 +307,7 @@ enum msm_vidc_inst_capability_type {
 	MBPF,
 	LOSSLESS_MBPF,
 	BATCH_MBPF,
+	BATCH_FPS,
 	SECURE_MBPF,
 	MBPS,
 	POWER_SAVE_MBPS,

+ 96 - 20
driver/vidc/src/msm_vdec.c

@@ -18,6 +18,9 @@
 #include "msm_vidc_control.h"
 #include "venus_hfi.h"
 #include "hfi_packet.h"
+/* TODO: update based on clips */
+#define MAX_DEC_BATCH_SIZE 6
+#define SKIP_BATCH_WINDOW 100
 
 u32 msm_vdec_subscribe_for_psc_avc[] = {
 	HFI_PROP_BITSTREAM_RESOLUTION,
@@ -206,7 +209,7 @@ static int msm_vdec_set_bit_depth(struct msm_vidc_inst *inst,
 		bitdepth = 10 << 16 | 10;
 
 	inst->subcr_params[port].bit_depth = bitdepth;
-	inst->capabilities->cap[BIT_DEPTH].value = bitdepth;
+	msm_vidc_update_cap_value(inst, BIT_DEPTH, bitdepth, __func__);
 	i_vpr_h(inst, "%s: bit depth: %d", __func__, bitdepth);
 	rc = venus_hfi_session_property(inst,
 			HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
@@ -1393,17 +1396,17 @@ static int msm_vdec_read_input_subcr_params(struct msm_vidc_inst *inst)
 	inst->crop.width = inst->fmts[INPUT_PORT].fmt.pix_mp.width -
 		((subsc_params.crop_offsets[1] >> 16) & 0xFFFF);
 
-	inst->capabilities->cap[PROFILE].value = subsc_params.profile;
-	inst->capabilities->cap[LEVEL].value = subsc_params.level;
-	inst->capabilities->cap[HEVC_TIER].value = subsc_params.tier;
-	inst->capabilities->cap[POC].value = subsc_params.pic_order_cnt;
-	inst->capabilities->cap[BIT_DEPTH].value = subsc_params.bit_depth;
+	msm_vidc_update_cap_value(inst, PROFILE, subsc_params.profile, __func__);
+	msm_vidc_update_cap_value(inst, LEVEL, subsc_params.level, __func__);
+	msm_vidc_update_cap_value(inst, HEVC_TIER, subsc_params.tier, __func__);
+	msm_vidc_update_cap_value(inst, POC, subsc_params.pic_order_cnt, __func__);
+	msm_vidc_update_cap_value(inst, BIT_DEPTH, subsc_params.bit_depth, __func__);
 	if (subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)
-		inst->capabilities->cap[CODED_FRAMES].value =
-			CODED_FRAMES_PROGRESSIVE;
+		msm_vidc_update_cap_value(inst, CODED_FRAMES, CODED_FRAMES_PROGRESSIVE, __func__);
 	else
-		inst->capabilities->cap[CODED_FRAMES].value =
-			CODED_FRAMES_INTERLACE;
+		msm_vidc_update_cap_value(inst, CODED_FRAMES, CODED_FRAMES_INTERLACE, __func__);
+
+	inst->decode_batch.enable = msm_vidc_allow_decode_batch(inst);
 
 	return 0;
 }
@@ -1543,6 +1546,8 @@ int msm_vdec_streamon_input(struct msm_vidc_inst *inst)
 	if (rc)
 		goto error;
 
+	inst->decode_batch.enable = msm_vidc_allow_decode_batch(inst);
+
 	return 0;
 
 error:
@@ -1551,6 +1556,33 @@ error:
 	return rc;
 }
 
+static int schedule_batch_work(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+
+	if (!inst || !inst->core) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+	core = inst->core;
+	cancel_delayed_work(&inst->decode_batch.work);
+	queue_delayed_work(core->batch_workq, &inst->decode_batch.work,
+		msecs_to_jiffies(core->capabilities[DECODE_BATCH_TIMEOUT].value));
+
+	return 0;
+}
+
+static int cancel_batch_work(struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		d_vpr_e("%s: Invalid arguments\n", __func__);
+		return -EINVAL;
+	}
+	cancel_delayed_work(&inst->decode_batch.work);
+
+	return 0;
+}
+
 int msm_vdec_streamoff_output(struct msm_vidc_inst *inst)
 {
 	int rc = 0;
@@ -1560,6 +1592,8 @@ int msm_vdec_streamoff_output(struct msm_vidc_inst *inst)
 		return -EINVAL;
 	}
 
+	/* cancel pending batch work */
+	cancel_batch_work(inst);
 	rc = msm_vidc_session_streamoff(inst, OUTPUT_PORT);
 	if (rc)
 		return rc;
@@ -1754,6 +1788,8 @@ int msm_vdec_streamon_output(struct msm_vidc_inst *inst)
 	if (rc)
 		goto error;
 
+	inst->decode_batch.enable = msm_vidc_allow_decode_batch(inst);
+
 	return 0;
 
 error:
@@ -1765,13 +1801,43 @@ error:
 static int msm_vdec_qbuf_batch(struct msm_vidc_inst *inst,
 	struct vb2_buffer *vb2)
 {
-	int rc = 0;
+	struct msm_vidc_buffer *buf;
+	enum msm_vidc_allow allow;
+	int count, rc;
 
-	if (!inst || !vb2) {
+	if (!inst || !vb2 || !inst->decode_batch.size) {
 		d_vpr_e("%s: invalid params\n", __func__);
 		return -EINVAL;
 	}
-	i_vpr_h(inst, "%s()\n", __func__);
+
+	buf = msm_vidc_get_driver_buf(inst, vb2);
+	if (!buf)
+		return -EINVAL;
+
+	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;
+	} else if (allow == MSM_VIDC_DEFER) {
+		print_vidc_buffer(VIDC_HIGH, "high", "qbuf deferred", inst, buf);
+		return 0;
+	}
+
+	/* do not defer buffers initially to avoid latency issues */
+	if (inst->power.buffer_counter > SKIP_BATCH_WINDOW) {
+		count = msm_vidc_num_buffers(inst, MSM_VIDC_BUF_OUTPUT, MSM_VIDC_ATTR_DEFERRED);
+		if (count < inst->decode_batch.size) {
+			print_vidc_buffer(VIDC_HIGH, "high", "batch-qbuf deferred", inst, buf);
+			schedule_batch_work(inst);
+			return 0;
+		}
+
+		cancel_batch_work(inst);
+	}
+
+	rc = msm_vidc_queue_buffer_batch(inst);
+	if (rc)
+		return rc;
 
 	return rc;
 }
@@ -1780,10 +1846,16 @@ int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb2)
 {
 	int rc = 0;
 
-	if (inst->decode_batch.enable)
+	if (!inst || !vb2) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	/* batch decoder output & meta buffer only */
+	if (inst->decode_batch.enable && vb2->type == OUTPUT_MPLANE)
 		rc = msm_vdec_qbuf_batch(inst, vb2);
 	else
-		rc = msm_vidc_queue_buffer(inst, vb2);
+		rc = msm_vidc_queue_buffer_single(inst, vb2);
 
 	return rc;
 }
@@ -2142,11 +2214,9 @@ set_default:
 	i_vpr_h(inst, "%s: type %u value %#x\n",
 		__func__, s_parm->type, q16_rate);
 
-	if (is_frame_rate) {
-		capability->cap[FRAME_RATE].value = q16_rate;
-	} else {
-		capability->cap[OPERATING_RATE].value = q16_rate;
-	}
+	msm_vidc_update_cap_value(inst,
+		is_frame_rate ? FRAME_RATE : OPERATING_RATE,
+		q16_rate, __func__);
 
 exit:
 	return rc;
@@ -2290,6 +2360,10 @@ int msm_vdec_inst_init(struct msm_vidc_inst *inst)
 	core = inst->core;
 
 	INIT_DELAYED_WORK(&inst->decode_batch.work, msm_vidc_batch_handler);
+	if (core->capabilities[DECODE_BATCH].value) {
+		inst->decode_batch.enable = true;
+		inst->decode_batch.size = MAX_DEC_BATCH_SIZE;
+	}
 
 	f = &inst->fmts[INPUT_PORT];
 	f->type = INPUT_MPLANE;
@@ -2372,6 +2446,8 @@ int msm_vdec_inst_deinit(struct msm_vidc_inst *inst)
 		d_vpr_e("%s: invalid params\n", __func__);
 		return -EINVAL;
 	}
+	/* cancel pending batch work */
+	cancel_batch_work(inst);
 	rc = msm_vidc_ctrl_deinit(inst);
 
 	return rc;

+ 6 - 11
driver/vidc/src/msm_venc.c

@@ -353,10 +353,8 @@ static int msm_venc_set_csc(struct msm_vidc_inst* inst,
 		return -EINVAL;
 	}
 
-	if (msm_venc_csc_required(inst))
-		inst->capabilities->cap[CSC].value = 1;
-	else
-		inst->capabilities->cap[CSC].value = 0;
+	msm_vidc_update_cap_value(inst, CSC,
+		msm_venc_csc_required(inst) ? 1 : 0, __func__);
 
 	csc = inst->capabilities->cap[CSC].value;
 	i_vpr_h(inst, "%s: csc: %u\n", __func__, csc);
@@ -955,7 +953,7 @@ int msm_venc_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb2)
 {
 	int rc = 0;
 
-	rc = msm_vidc_queue_buffer(inst, vb2);
+	rc = msm_vidc_queue_buffer_single(inst, vb2);
 	if (rc)
 		return rc;
 
@@ -1590,12 +1588,9 @@ set_default:
 	i_vpr_h(inst, "%s: type %u value %#x\n",
 		__func__, s_parm->type, q16_rate);
 
-	if (!is_frame_rate) {
-		capability->cap[OPERATING_RATE].value = q16_rate;
-		goto exit;
-	} else {
-		capability->cap[FRAME_RATE].value = q16_rate;
-	}
+	msm_vidc_update_cap_value(inst,
+		is_frame_rate ? FRAME_RATE : OPERATING_RATE,
+		q16_rate, __func__);
 
 	/*
 	 * In static case, frame rate is set via

+ 8 - 3
driver/vidc/src/msm_vidc_control.c

@@ -359,9 +359,14 @@ static bool is_parent_available(struct msm_vidc_inst* inst,
 	return false;
 }
 
-static int msm_vidc_update_cap_value(struct msm_vidc_inst* inst, u32 cap,
+int msm_vidc_update_cap_value(struct msm_vidc_inst *inst, u32 cap,
 	s32 adjusted_val, const char *func)
 {
+	if (!inst || !inst->capabilities) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
 	if (inst->capabilities->cap[cap].value != adjusted_val)
 		i_vpr_h(inst,
 			"%s: updated database value from %#x to %#x\n",
@@ -508,7 +513,7 @@ static int msm_vidc_adjust_dynamic_property(struct msm_vidc_inst *inst,
 		if (rc)
 			goto exit;
 	} else if (ctrl) {
-		capability->cap[cap_id].value = ctrl->val;
+		msm_vidc_update_cap_value(inst, cap_id, ctrl->val, __func__);
 	}
 
 	/* add children if cap value modified */
@@ -741,7 +746,7 @@ int msm_v4l2_op_s_ctrl(struct v4l2_ctrl *ctrl)
 	capability->cap[cap_id].flags |= CAP_FLAG_CLIENT_SET;
 	/* Static setting */
 	if (!inst->vb2q[OUTPUT_PORT].streaming) {
-		capability->cap[cap_id].value = ctrl->val;
+		msm_vidc_update_cap_value(inst, cap_id, ctrl->val, __func__);
 
 		if (is_meta_ctrl(ctrl->id))
 			msm_vidc_update_meta_port_settings(inst);

+ 195 - 30
driver/vidc/src/msm_vidc_driver.c

@@ -11,6 +11,7 @@
 #include "msm_vidc_driver.h"
 #include "msm_vidc_platform.h"
 #include "msm_vidc_internal.h"
+#include "msm_vidc_control.h"
 #include "msm_vidc_memory.h"
 #include "msm_vidc_debug.h"
 #include "msm_vidc_power.h"
@@ -20,6 +21,7 @@
 #include "venus_hfi.h"
 #include "venus_hfi_response.h"
 #include "hfi_packet.h"
+extern struct msm_vidc_core *g_core;
 
 #define COUNT_BITS(a, out) {       \
 	while ((a) >= 1) {          \
@@ -1024,6 +1026,8 @@ bool msm_vidc_allow_streamoff(struct msm_vidc_inst *inst, u32 type)
 
 enum msm_vidc_allow msm_vidc_allow_qbuf(struct msm_vidc_inst *inst, u32 type)
 {
+	int port = 0;
+
 	if (!inst) {
 		d_vpr_e("%s: invalid params\n", __func__);
 		return MSM_VIDC_DISALLOW;
@@ -1032,6 +1036,15 @@ enum msm_vidc_allow msm_vidc_allow_qbuf(struct msm_vidc_inst *inst, u32 type)
 		i_vpr_e(inst, "%s: inst in error state\n", __func__);
 		return MSM_VIDC_DISALLOW;
 	}
+
+	port = v4l2_type_to_driver_port(inst, type, __func__);
+	if (port < 0)
+		return MSM_VIDC_DISALLOW;
+
+	/* defer queuing if streamon not completed */
+	if (!inst->vb2q[port].streaming)
+		return MSM_VIDC_DEFER;
+
 	if (type == INPUT_META_PLANE || type == OUTPUT_META_PLANE)
 		return MSM_VIDC_DEFER;
 
@@ -1679,6 +1692,9 @@ struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
 		buf->attr &= MSM_VIDC_ATTR_READ_ONLY;
 	}
 
+	/* treat every buffer as deferred buffer initially */
+	buf->attr |= MSM_VIDC_ATTR_DEFERRED;
+
 	rc = vb2_buffer_to_driver(vb2, buf);
 	if (rc)
 		goto error;
@@ -1736,7 +1752,7 @@ bool msm_vidc_is_super_buffer(struct msm_vidc_inst *inst)
 	struct msm_vidc_inst_capability *capability = NULL;
 
 	if (!inst || !inst->capabilities) {
-		i_vpr_e(inst, "%s: Invalid params\n", __func__);
+		d_vpr_e("%s: Invalid params\n", __func__);
 		return false;
 	}
 
@@ -1745,49 +1761,98 @@ bool msm_vidc_is_super_buffer(struct msm_vidc_inst *inst)
 	return !!capability->cap[SUPER_FRAME].value;
 }
 
-int msm_vidc_queue_buffer(struct msm_vidc_inst *inst, struct vb2_buffer *vb2)
+static bool is_single_session(struct msm_vidc_inst *inst)
 {
-	int rc = 0;
-	struct msm_vidc_buffer *buf;
-	struct msm_vidc_buffer *meta;
-	enum msm_vidc_allow allow;
-	int port;
+	struct msm_vidc_core *core;
+	u32 count = 0;
 
-	if (!inst || !vb2) {
+	if (!inst) {
+		d_vpr_e("%s: Invalid params\n", __func__);
+		return false;
+	}
+	core = inst->core;
+
+	core_lock(core, __func__);
+	list_for_each_entry(inst, &core->instances, list)
+		count++;
+	core_unlock(core, __func__);
+
+	return count == 1;
+}
+
+bool msm_vidc_allow_decode_batch(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+	bool allow = false;
+
+	if (!inst || !inst->core) {
 		d_vpr_e("%s: invalid params\n", __func__);
 		return -EINVAL;
 	}
+	core = inst->core;
 
-	buf = msm_vidc_get_driver_buf(inst, vb2);
-	if (!buf)
-		return -EINVAL;
+	allow = inst->decode_batch.enable;
+	if (!allow) {
+		i_vpr_h(inst, "%s: batching already disabled\n", __func__);
+		goto exit;
+	}
 
-	/* skip queuing if streamon not completed */
-	port = v4l2_type_to_driver_port(inst, vb2->type, __func__);
-	if (port < 0)
-		return -EINVAL;
+	allow = core->capabilities[DECODE_BATCH].value;
+	if (!allow) {
+		i_vpr_h(inst, "%s: core doesn't support batching\n", __func__);
+		goto exit;
+	}
 
-	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;
+	allow = is_single_session(inst);
+	if (!allow) {
+		i_vpr_h(inst, "%s: multiple sessions running\n", __func__);
+		goto exit;
 	}
 
-	if (!inst->vb2q[port].streaming || allow == MSM_VIDC_DEFER) {
-		buf->attr |= MSM_VIDC_ATTR_DEFERRED;
-		print_vidc_buffer(VIDC_HIGH, "high", "qbuf deferred", inst, buf);
-		return 0;
+	allow = is_decode_session(inst);
+	if (!allow) {
+		i_vpr_h(inst, "%s: not a decoder session\n", __func__);
+		goto exit;
 	}
 
-	if (is_decode_session(inst) &&
-			inst->capabilities->cap[CODEC_CONFIG].value) {
-		buf->flags |= MSM_VIDC_BUF_FLAG_CODECCONFIG;
-		inst->capabilities->cap[CODEC_CONFIG].value = 0;
+	allow = !is_thumbnail_session(inst);
+	if (!allow) {
+		i_vpr_h(inst, "%s: thumbnail session\n", __func__);
+		goto exit;
 	}
 
-	if (buf->type == MSM_VIDC_BUF_INPUT) {
-		inst->power.buffer_counter++;
-		msm_vidc_scale_power(inst, true);
+	allow = is_realtime_session(inst);
+	if (!allow) {
+		i_vpr_h(inst, "%s: non-realtime session\n", __func__);
+		goto exit;
+	}
+
+	allow = !is_lowlatency_session(inst);
+	if (!allow) {
+		i_vpr_h(inst, "%s: lowlatency session\n", __func__);
+		goto exit;
+	}
+
+exit:
+	i_vpr_h(inst, "%s: batching %s\n", __func__, allow ? "enabled" : "disabled");
+
+	return allow;
+}
+
+static int msm_vidc_queue_buffer(struct msm_vidc_inst *inst, struct msm_vidc_buffer *buf)
+{
+	struct msm_vidc_buffer *meta;
+	int rc = 0;
+
+	if (!inst || !buf || !inst->capabilities) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	if (is_decode_session(inst) && is_input_buffer(buf->type) &&
+		inst->capabilities->cap[CODEC_CONFIG].value) {
+		buf->flags |= MSM_VIDC_BUF_FLAG_CODECCONFIG;
+		msm_vidc_update_cap_value(inst, CODEC_CONFIG, 0, __func__);
 	}
 
 	print_vidc_buffer(VIDC_HIGH, "high", "qbuf", inst, buf);
@@ -1819,6 +1884,70 @@ int msm_vidc_queue_buffer(struct msm_vidc_inst *inst, struct vb2_buffer *vb2)
 	else if (buf->type == MSM_VIDC_BUF_OUTPUT)
 		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FTB);
 
+	return 0;
+}
+
+int msm_vidc_queue_buffer_batch(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_buffers *buffers;
+	struct msm_vidc_buffer *buf;
+	int rc = 0;
+
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	buffers = msm_vidc_get_buffers(inst, MSM_VIDC_BUF_OUTPUT, __func__);
+	if (!buffers)
+		return -EINVAL;
+
+	msm_vidc_scale_power(inst, true);
+
+	list_for_each_entry(buf, &buffers->list, list) {
+		if (!(buf->attr & MSM_VIDC_ATTR_DEFERRED))
+			continue;
+		rc = msm_vidc_queue_buffer(inst, buf);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+int msm_vidc_queue_buffer_single(struct msm_vidc_inst *inst, struct vb2_buffer *vb2)
+{
+	int rc = 0;
+	struct msm_vidc_buffer *buf;
+	enum msm_vidc_allow allow;
+
+	if (!inst || !vb2) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	buf = msm_vidc_get_driver_buf(inst, vb2);
+	if (!buf)
+		return -EINVAL;
+
+	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;
+	} else if (allow == MSM_VIDC_DEFER) {
+		print_vidc_buffer(VIDC_HIGH, "high", "qbuf deferred", inst, buf);
+		return 0;
+	}
+
+	if (buf->type == MSM_VIDC_BUF_INPUT) {
+		inst->power.buffer_counter++;
+		msm_vidc_scale_power(inst, true);
+	}
+
+	rc = msm_vidc_queue_buffer(inst, buf);
+	if (rc)
+		return rc;
+
 	return rc;
 }
 
@@ -2558,6 +2687,9 @@ int msm_vidc_session_streamoff(struct msm_vidc_inst *inst,
 		rc = -EINVAL;
 		goto error;
 	}
+
+	/* flush deferred buffers */
+	msm_vidc_flush_buffers(inst, buffer_type);
 	return 0;
 
 error:
@@ -3106,6 +3238,39 @@ void msm_vidc_fw_unload_handler(struct work_struct *work)
 
 void msm_vidc_batch_handler(struct work_struct *work)
 {
+	struct msm_vidc_inst *inst;
+	enum msm_vidc_allow allow;
+	int rc = 0;
+
+	inst = container_of(work, struct msm_vidc_inst, decode_batch.work.work);
+	inst = get_inst_ref(g_core, inst);
+	if (!inst) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return;
+	}
+
+	inst_lock(inst, __func__);
+	if (is_session_error(inst)) {
+		i_vpr_e(inst, "%s: failled. Session error\n", __func__);
+		goto exit;
+	}
+
+	allow = msm_vidc_allow_qbuf(inst, OUTPUT_MPLANE);
+	if (allow != MSM_VIDC_ALLOW) {
+		i_vpr_e(inst, "%s: not allowed in state: %s\n", __func__, state_name(inst->state));
+		goto exit;
+	}
+
+	i_vpr_h(inst, "%s: queue pending batch buffers\n", __func__);
+	rc = msm_vidc_queue_buffer_batch(inst);
+	if (rc) {
+		i_vpr_e(inst, "%s: batch qbufs failed\n", __func__);
+		msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR, __func__);
+	}
+
+exit:
+	inst_unlock(inst, __func__);
+	put_inst(inst);
 }
 
 int msm_vidc_flush_buffers(struct msm_vidc_inst* inst,

+ 25 - 2
driver/vidc/src/msm_vidc_probe.c

@@ -180,12 +180,19 @@ static int msm_vidc_deinitialize_core(struct msm_vidc_core *core)
 	mutex_destroy(&core->lock);
 	msm_vidc_change_core_state(core, MSM_VIDC_CORE_DEINIT, __func__);
 
+	if (core->batch_workq)
+		destroy_workqueue(core->batch_workq);
+
 	if (core->pm_workq)
 		destroy_workqueue(core->pm_workq);
 
 	if (core->device_workq)
 		destroy_workqueue(core->device_workq);
 
+	core->batch_workq = NULL;
+	core->pm_workq = NULL;
+	core->device_workq = NULL;
+
 	return rc;
 }
 
@@ -211,7 +218,13 @@ static int msm_vidc_initialize_core(struct msm_vidc_core *core)
 	core->pm_workq = create_singlethread_workqueue("pm_workq");
 	if (!core->pm_workq) {
 		d_vpr_e("%s: create pm workq failed\n", __func__);
-		destroy_workqueue(core->device_workq);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	core->batch_workq = create_singlethread_workqueue("batch_workq");
+	if (!core->batch_workq) {
+		d_vpr_e("%s: create batch workq failed\n", __func__);
 		rc = -EINVAL;
 		goto exit;
 	}
@@ -224,10 +237,20 @@ static int msm_vidc_initialize_core(struct msm_vidc_core *core)
 	INIT_WORK(&core->smmu_fault_work, msm_vidc_smmu_fault_work_handler);
 	INIT_DELAYED_WORK(&core->pm_work, venus_hfi_pm_work_handler);
 	INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler);
-	INIT_DELAYED_WORK(&core->batch_work, msm_vidc_batch_handler);
 	INIT_WORK(&core->ssr_work, msm_vidc_ssr_handler);
 
+	return 0;
 exit:
+	if (core->batch_workq)
+		destroy_workqueue(core->batch_workq);
+	if (core->pm_workq)
+		destroy_workqueue(core->pm_workq);
+	if (core->device_workq)
+		destroy_workqueue(core->device_workq);
+	core->batch_workq = NULL;
+	core->pm_workq = NULL;
+	core->device_workq = NULL;
+
 	return rc;
 }