From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 03 Jan 2024 09:43:39 +0000 Subject: [PATCH] update kernel to 5.10.198 --- kernel/drivers/media/platform/qcom/venus/vdec.c | 919 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 692 insertions(+), 227 deletions(-) diff --git a/kernel/drivers/media/platform/qcom/venus/vdec.c b/kernel/drivers/media/platform/qcom/venus/vdec.c index e3972db..c437a92 100644 --- a/kernel/drivers/media/platform/qcom/venus/vdec.c +++ b/kernel/drivers/media/platform/qcom/venus/vdec.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/clk.h> #include <linux/module.h> @@ -29,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "vdec.h" +#include "pm_helpers.h" /* * Three resons to keep MPLANE formats (despite that the number of planes @@ -46,42 +38,52 @@ .pixfmt = V4L2_PIX_FMT_MPEG4, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_H263, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_H264, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VP8, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VP9, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_XVID, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_HEVC, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, }; @@ -142,6 +144,7 @@ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; + u32 szimage; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -155,6 +158,8 @@ else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); + if (!fmt) + return NULL; } pixmp->width = clamp(pixmp->width, frame_width_min(inst), @@ -170,14 +175,17 @@ pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, - pixmp->width, - pixmp->height); + szimage = venus_helper_get_framesz(pixmp->pixelformat, pixmp->width, + pixmp->height); - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pfmt[0].sizeimage = szimage; pfmt[0].bytesperline = ALIGN(pixmp->width, 128); - else + } else { + pfmt[0].sizeimage = clamp_t(u32, pfmt[0].sizeimage, 0, SZ_8M); + pfmt[0].sizeimage = max(pfmt[0].sizeimage, szimage); pfmt[0].bytesperline = 0; + } return fmt; } @@ -191,33 +199,56 @@ return 0; } +static int vdec_check_src_change(struct venus_inst *inst) +{ + int ret; + + if (inst->subscriptions & V4L2_EVENT_SOURCE_CHANGE && + inst->codec_state == VENUS_DEC_STATE_INIT && + !inst->reconfig) + return -EINVAL; + + if (inst->subscriptions & V4L2_EVENT_SOURCE_CHANGE) + return 0; + + /* + * The code snippet below is a workaround for backward compatibility + * with applications which doesn't support V4L2 events. It will be + * dropped in future once those applications are fixed. + */ + + if (inst->codec_state != VENUS_DEC_STATE_INIT) + goto done; + + ret = wait_event_timeout(inst->reconf_wait, inst->reconfig, + msecs_to_jiffies(100)); + if (!ret) + return -EINVAL; + + if (!(inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) || + !inst->reconfig) + dev_dbg(inst->core->dev, VDBGH "wrong state\n"); + +done: + return 0; +} + static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct venus_inst *inst = to_inst(file); const struct venus_format *fmt = NULL; struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + int ret; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fmt = inst->fmt_cap; else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) fmt = inst->fmt_out; - if (inst->reconfig) { - struct v4l2_format format = {}; - - inst->out_width = inst->reconfig_width; - inst->out_height = inst->reconfig_height; - inst->reconfig = false; - - format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt; - format.fmt.pix_mp.width = inst->out_width; - format.fmt.pix_mp.height = inst->out_height; - - vdec_try_fmt_common(inst, &format); - - inst->width = format.fmt.pix_mp.width; - inst->height = format.fmt.pix_mp.height; + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = vdec_check_src_change(inst); + if (ret) + return ret; } pixmp->pixelformat = fmt->pixfmt; @@ -247,6 +278,14 @@ const struct venus_format *fmt; struct v4l2_format format; u32 pixfmt_out = 0, pixfmt_cap = 0; + struct vb2_queue *q; + + q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); + if (!q) + return -EINVAL; + + if (vb2_is_busy(q)) + return -EBUSY; orig_pixmp = *pixmp; @@ -275,6 +314,7 @@ inst->ycbcr_enc = pixmp->ycbcr_enc; inst->quantization = pixmp->quantization; inst->xfer_func = pixmp->xfer_func; + inst->input_buf_size = pixmp->plane_fmt[0].sizeimage; } memset(&format, 0, sizeof(format)); @@ -341,9 +381,9 @@ static int vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - strlcpy(cap->driver, "qcom-venus", sizeof(cap->driver)); - strlcpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card)); - strlcpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); + strscpy(cap->driver, "qcom-venus", sizeof(cap->driver)); + strscpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card)); + strscpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); return 0; } @@ -360,6 +400,7 @@ return -EINVAL; f->pixelformat = fmt->pixfmt; + f->flags = fmt->flags; return 0; } @@ -431,11 +472,18 @@ static int vdec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { + struct venus_inst *inst = container_of(fh, struct venus_inst, fh); + int ret; + switch (sub->type) { case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 2, NULL); case V4L2_EVENT_SOURCE_CHANGE: - return v4l2_src_change_event_subscribe(fh, sub); + ret = v4l2_src_change_event_subscribe(fh, sub); + if (ret) + return ret; + inst->subscriptions |= V4L2_EVENT_SOURCE_CHANGE; + return 0; case V4L2_EVENT_CTRL: return v4l2_ctrl_subscribe_event(fh, sub); default: @@ -444,45 +492,45 @@ } static int -vdec_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) -{ - switch (cmd->cmd) { - case V4L2_DEC_CMD_STOP: - if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK) - return -EINVAL; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) { struct venus_inst *inst = to_inst(file); + struct vb2_queue *dst_vq; struct hfi_frame_data fdata = {0}; int ret; - ret = vdec_try_decoder_cmd(file, fh, cmd); + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd); if (ret) return ret; mutex_lock(&inst->lock); - /* - * Implement V4L2_DEC_CMD_STOP by enqueue an empty buffer on decoder - * input to signal EOS. - */ - if (!(inst->streamon_out & inst->streamon_cap)) - goto unlock; + if (cmd->cmd == V4L2_DEC_CMD_STOP) { + /* + * Implement V4L2_DEC_CMD_STOP by enqueue an empty buffer on + * decoder input to signal EOS. + */ + if (!(inst->streamon_out && inst->streamon_cap)) + goto unlock; - fdata.buffer_type = HFI_BUFFER_INPUT; - fdata.flags |= HFI_BUFFERFLAG_EOS; - fdata.device_addr = 0xdeadbeef; + fdata.buffer_type = HFI_BUFFER_INPUT; + fdata.flags |= HFI_BUFFERFLAG_EOS; + fdata.device_addr = 0xdeadb000; - ret = hfi_session_process_buf(inst, &fdata); + ret = hfi_session_process_buf(inst, &fdata); + + if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) { + inst->codec_state = VENUS_DEC_STATE_DRAIN; + inst->drain_active = true; + } + } else if (cmd->cmd == V4L2_DEC_CMD_START && + inst->codec_state == VENUS_DEC_STATE_STOPPED) { + dst_vq = v4l2_m2m_get_vq(inst->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + vb2_clear_last_buffer_dequeued(dst_vq); + + inst->codec_state = VENUS_DEC_STATE_DECODING; + } unlock: mutex_unlock(&inst->lock); @@ -491,8 +539,8 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = { .vidioc_querycap = vdec_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt, - .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt, + .vidioc_enum_fmt_vid_cap = vdec_enum_fmt, + .vidioc_enum_fmt_vid_out = vdec_enum_fmt, .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, @@ -513,9 +561,67 @@ .vidioc_enum_framesizes = vdec_enum_framesizes, .vidioc_subscribe_event = vdec_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_try_decoder_cmd = vdec_try_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, .vidioc_decoder_cmd = vdec_decoder_cmd, }; + +static int vdec_pm_get(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + int ret; + + mutex_lock(&core->pm_lock); + ret = pm_runtime_get_sync(dev); + mutex_unlock(&core->pm_lock); + + return ret < 0 ? ret : 0; +} + +static int vdec_pm_put(struct venus_inst *inst, bool autosuspend) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + int ret; + + mutex_lock(&core->pm_lock); + + if (autosuspend) + ret = pm_runtime_put_autosuspend(dev); + else + ret = pm_runtime_put_sync(dev); + + mutex_unlock(&core->pm_lock); + + return ret < 0 ? ret : 0; +} + +static int vdec_pm_get_put(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + int ret = 0; + + mutex_lock(&core->pm_lock); + + if (pm_runtime_suspended(dev)) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto error; + + ret = pm_runtime_put_autosuspend(dev); + } + +error: + mutex_unlock(&core->pm_lock); + + return ret < 0 ? ret : 0; +} + +static void vdec_pm_touch(struct venus_inst *inst) +{ + pm_runtime_mark_last_busy(inst->core->dev_dec); +} static int vdec_set_properties(struct venus_inst *inst) { @@ -540,6 +646,7 @@ { struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; + struct hfi_buffer_requirements bufreq; u32 width = inst->out_width; u32 height = inst->out_height; u32 out_fmt, out2_fmt; @@ -548,10 +655,6 @@ int ret; ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); - if (ret) - return ret; - - ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1); if (ret) return ret; @@ -619,6 +722,23 @@ } if (IS_V3(core) || IS_V4(core)) { + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + return ret; + + if (bufreq.size > inst->output_buf_size) + return -EINVAL; + + if (inst->dpb_fmt) { + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT2, + &bufreq); + if (ret) + return ret; + + if (bufreq.size > inst->output2_buf_size) + return -EINVAL; + } + if (inst->output2_buf_size) { ret = venus_helper_set_bufsize(inst, inst->output2_buf_size, @@ -643,20 +763,22 @@ return 0; } -static int vdec_init_session(struct venus_inst *inst) +static int vdec_session_init(struct venus_inst *inst) { int ret; ret = hfi_session_init(inst, inst->fmt_out->pixfmt); - if (ret) + if (ret == -EINVAL) + return 0; + else if (ret) return ret; - ret = venus_helper_set_input_resolution(inst, inst->out_width, - inst->out_height); + ret = venus_helper_set_input_resolution(inst, frame_width_min(inst), + frame_height_min(inst)); if (ret) goto deinit; - ret = venus_helper_set_color_format(inst, inst->fmt_cap->pixfmt); + ret = venus_helper_init_codec_freq_data(inst); if (ret) goto deinit; @@ -675,26 +797,19 @@ *in_num = *out_num = 0; - ret = vdec_init_session(inst); - if (ret) - return ret; - ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) - goto deinit; + return ret; *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); if (ret) - goto deinit; + return ret; *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); -deinit: - hfi_session_deinit(inst); - - return ret; + return 0; } static int vdec_queue_setup(struct vb2_queue *q, @@ -727,7 +842,19 @@ return 0; } + ret = vdec_pm_get(inst); + if (ret) + return ret; + + ret = vdec_session_init(inst); + if (ret) + goto put_power; + ret = vdec_num_buffers(inst, &in_num, &out_num); + if (ret) + goto put_power; + + ret = vdec_pm_put(inst, false); if (ret) return ret; @@ -737,6 +864,7 @@ sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, inst->out_width, inst->out_height); + sizes[0] = max(sizes[0], inst->input_buf_size); inst->input_buf_size = sizes[0]; *num_buffers = max(*num_buffers, in_num); inst->num_input_bufs = *num_buffers; @@ -750,12 +878,21 @@ inst->output_buf_size = sizes[0]; *num_buffers = max(*num_buffers, out_num); inst->num_output_bufs = *num_buffers; + + mutex_lock(&inst->lock); + if (inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) + inst->codec_state = VENUS_DEC_STATE_STOPPED; + mutex_unlock(&inst->lock); break; default: ret = -EINVAL; break; } + return ret; + +put_power: + vdec_pm_put(inst, false); return ret; } @@ -786,6 +923,137 @@ return 0; } +static int vdec_start_capture(struct venus_inst *inst) +{ + int ret; + + if (!inst->streamon_out) + return 0; + + if (inst->codec_state == VENUS_DEC_STATE_DECODING) { + if (inst->reconfig) + goto reconfigure; + + venus_helper_queue_dpb_bufs(inst); + venus_helper_process_initial_cap_bufs(inst); + inst->streamon_cap = 1; + return 0; + } + + if (inst->codec_state != VENUS_DEC_STATE_STOPPED) + return 0; + +reconfigure: + ret = vdec_output_conf(inst); + if (ret) + return ret; + + ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, + VB2_MAX_FRAME, VB2_MAX_FRAME); + if (ret) + return ret; + + ret = venus_helper_intbufs_realloc(inst); + if (ret) + goto err; + + ret = venus_helper_alloc_dpb_bufs(inst); + if (ret) + goto err; + + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + goto free_dpb_bufs; + + ret = venus_helper_process_initial_cap_bufs(inst); + if (ret) + goto free_dpb_bufs; + + venus_pm_load_scale(inst); + + inst->next_buf_last = false; + + ret = hfi_session_continue(inst); + if (ret) + goto free_dpb_bufs; + + inst->codec_state = VENUS_DEC_STATE_DECODING; + + if (inst->drain_active) + inst->codec_state = VENUS_DEC_STATE_DRAIN; + + inst->streamon_cap = 1; + inst->sequence_cap = 0; + inst->reconfig = false; + inst->drain_active = false; + + return 0; + +free_dpb_bufs: + venus_helper_free_dpb_bufs(inst); +err: + return ret; +} + +static int vdec_start_output(struct venus_inst *inst) +{ + int ret; + + if (inst->codec_state == VENUS_DEC_STATE_SEEK) { + ret = venus_helper_process_initial_out_bufs(inst); + if (inst->next_buf_last) + inst->codec_state = VENUS_DEC_STATE_DRC; + else + inst->codec_state = VENUS_DEC_STATE_DECODING; + goto done; + } + + if (inst->codec_state == VENUS_DEC_STATE_INIT || + inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) { + ret = venus_helper_process_initial_out_bufs(inst); + goto done; + } + + if (inst->codec_state != VENUS_DEC_STATE_DEINIT) + return -EINVAL; + + venus_helper_init_instance(inst); + inst->sequence_out = 0; + inst->reconfig = false; + inst->next_buf_last = false; + + ret = vdec_set_properties(inst); + if (ret) + return ret; + + ret = vdec_output_conf(inst); + if (ret) + return ret; + + ret = vdec_verify_conf(inst); + if (ret) + return ret; + + ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, + VB2_MAX_FRAME, VB2_MAX_FRAME); + if (ret) + return ret; + + ret = venus_helper_vb2_start_streaming(inst); + if (ret) + return ret; + + ret = venus_helper_process_initial_out_bufs(inst); + if (ret) + return ret; + + inst->codec_state = VENUS_DEC_STATE_INIT; + +done: + inst->streamon_out = 1; + return ret; +} + static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct venus_inst *inst = vb2_get_drv_priv(q); @@ -793,74 +1061,220 @@ mutex_lock(&inst->lock); - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - inst->streamon_out = 1; - else - inst->streamon_cap = 1; + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = vdec_start_capture(inst); + } else { + ret = vdec_pm_get(inst); + if (ret) + goto error; - if (!(inst->streamon_out & inst->streamon_cap)) { - mutex_unlock(&inst->lock); - return 0; + ret = venus_pm_acquire_core(inst); + if (ret) + goto put_power; + + ret = vdec_pm_put(inst, true); + if (ret) + goto error; + + ret = vdec_start_output(inst); } - venus_helper_init_instance(inst); - - inst->reconfig = false; - inst->sequence_cap = 0; - inst->sequence_out = 0; - - ret = vdec_init_session(inst); if (ret) - goto bufs_done; - - ret = vdec_set_properties(inst); - if (ret) - goto deinit_sess; - - ret = vdec_output_conf(inst); - if (ret) - goto deinit_sess; - - ret = vdec_verify_conf(inst); - if (ret) - goto deinit_sess; - - ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - VB2_MAX_FRAME, VB2_MAX_FRAME); - if (ret) - goto deinit_sess; - - ret = venus_helper_alloc_dpb_bufs(inst); - if (ret) - goto deinit_sess; - - ret = venus_helper_vb2_start_streaming(inst); - if (ret) - goto deinit_sess; + goto error; mutex_unlock(&inst->lock); - return 0; -deinit_sess: - hfi_session_deinit(inst); -bufs_done: - venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - inst->streamon_out = 0; - else - inst->streamon_cap = 0; +put_power: + vdec_pm_put(inst, false); +error: + venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED); mutex_unlock(&inst->lock); return ret; } +static void vdec_cancel_dst_buffers(struct venus_inst *inst) +{ + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_dst_buf_remove(inst->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); +} + +static int vdec_stop_capture(struct venus_inst *inst) +{ + int ret = 0; + + switch (inst->codec_state) { + case VENUS_DEC_STATE_DECODING: + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); + fallthrough; + case VENUS_DEC_STATE_DRAIN: + inst->codec_state = VENUS_DEC_STATE_STOPPED; + inst->drain_active = false; + fallthrough; + case VENUS_DEC_STATE_SEEK: + vdec_cancel_dst_buffers(inst); + break; + case VENUS_DEC_STATE_DRC: + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); + inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; + venus_helper_free_dpb_bufs(inst); + break; + default: + break; + } + + return ret; +} + +static int vdec_stop_output(struct venus_inst *inst) +{ + int ret = 0; + + switch (inst->codec_state) { + case VENUS_DEC_STATE_DECODING: + case VENUS_DEC_STATE_DRAIN: + case VENUS_DEC_STATE_STOPPED: + case VENUS_DEC_STATE_DRC: + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); + inst->codec_state = VENUS_DEC_STATE_SEEK; + break; + case VENUS_DEC_STATE_INIT: + case VENUS_DEC_STATE_CAPTURE_SETUP: + ret = hfi_session_flush(inst, HFI_FLUSH_INPUT, true); + break; + default: + break; + } + + return ret; +} + +static void vdec_stop_streaming(struct vb2_queue *q) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + int ret = -EINVAL; + + mutex_lock(&inst->lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + ret = vdec_stop_capture(inst); + else + ret = vdec_stop_output(inst); + + venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR); + + if (ret) + goto unlock; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 0; + else + inst->streamon_cap = 0; + +unlock: + mutex_unlock(&inst->lock); +} + +static void vdec_session_release(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + int ret, abort = 0; + + vdec_pm_get(inst); + + mutex_lock(&inst->lock); + inst->codec_state = VENUS_DEC_STATE_DEINIT; + + ret = hfi_session_stop(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = hfi_session_unload_res(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = venus_helper_unregister_bufs(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = venus_helper_intbufs_free(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = hfi_session_deinit(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + + if (inst->session_error || core->sys_error) + abort = 1; + + if (abort) + hfi_session_abort(inst); + + venus_helper_free_dpb_bufs(inst); + venus_pm_load_scale(inst); + INIT_LIST_HEAD(&inst->registeredbufs); + mutex_unlock(&inst->lock); + + venus_pm_release_core(inst); + vdec_pm_put(inst, false); +} + +static int vdec_buf_init(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + inst->buf_count++; + + return venus_helper_vb2_buf_init(vb); +} + +static void vdec_buf_cleanup(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct venus_buffer *buf = to_venus_buffer(vbuf); + + mutex_lock(&inst->lock); + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (!list_empty(&inst->registeredbufs)) + list_del_init(&buf->reg_list); + mutex_unlock(&inst->lock); + + inst->buf_count--; + if (!inst->buf_count) + vdec_session_release(inst); +} + +static void vdec_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS }; + + vdec_pm_get_put(inst); + + mutex_lock(&inst->lock); + + if (inst->next_buf_last && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + inst->codec_state == VENUS_DEC_STATE_DRC) { + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vbuf->sequence = inst->sequence_cap++; + vbuf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, 0); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + v4l2_event_queue_fh(&inst->fh, &eos); + inst->next_buf_last = false; + mutex_unlock(&inst->lock); + return; + } + + mutex_unlock(&inst->lock); + + venus_helper_vb2_buf_queue(vb); +} + static const struct vb2_ops vdec_vb2_ops = { .queue_setup = vdec_queue_setup, - .buf_init = venus_helper_vb2_buf_init, + .buf_init = vdec_buf_init, + .buf_cleanup = vdec_buf_cleanup, .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = vdec_start_streaming, - .stop_streaming = venus_helper_vb2_stop_streaming, - .buf_queue = venus_helper_vb2_buf_queue, + .stop_streaming = vdec_stop_streaming, + .buf_queue = vdec_vb2_buf_queue, }; static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, @@ -871,6 +1285,8 @@ struct vb2_v4l2_buffer *vbuf; struct vb2_buffer *vb; unsigned int type; + + vdec_pm_touch(inst); if (buf_type == HFI_BUFFER_INPUT) type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; @@ -883,12 +1299,10 @@ vbuf->flags = flags; vbuf->field = V4L2_FIELD_NONE; + vb = &vbuf->vb2_buf; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - unsigned int opb_sz = venus_helper_get_opb_size(inst); - - vb = &vbuf->vb2_buf; - vb2_set_plane_payload(vb, 0, bytesused ? : opb_sz); + vb2_set_plane_payload(vb, 0, bytesused); vb->planes[0].data_offset = data_offset; vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; @@ -897,10 +1311,20 @@ const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; v4l2_event_queue_fh(&inst->fh, &ev); + + if (inst->codec_state == VENUS_DEC_STATE_DRAIN) { + inst->drain_active = false; + inst->codec_state = VENUS_DEC_STATE_STOPPED; + } } + + if (!bytesused) + state = VB2_BUF_STATE_ERROR; } else { vbuf->sequence = inst->sequence_out++; } + + venus_helper_get_ts_metadata(inst, timestamp_us, vbuf); if (hfi_flags & HFI_BUFFERFLAG_READONLY) venus_helper_acquire_buf_ref(vbuf); @@ -908,7 +1332,79 @@ if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT) state = VB2_BUF_STATE_ERROR; + if (hfi_flags & HFI_BUFFERFLAG_DROP_FRAME) { + state = VB2_BUF_STATE_ERROR; + vb2_set_plane_payload(vb, 0, 0); + vb->timestamp = 0; + } + v4l2_m2m_buf_done(vbuf, state); +} + +static void vdec_event_change(struct venus_inst *inst, + struct hfi_event_data *ev_data, bool sufficient) +{ + static const struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; + struct device *dev = inst->core->dev_dec; + struct v4l2_format format = {}; + + mutex_lock(&inst->lock); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt; + format.fmt.pix_mp.width = ev_data->width; + format.fmt.pix_mp.height = ev_data->height; + + vdec_try_fmt_common(inst, &format); + + inst->width = format.fmt.pix_mp.width; + inst->height = format.fmt.pix_mp.height; + + inst->out_width = ev_data->width; + inst->out_height = ev_data->height; + + if (inst->bit_depth != ev_data->bit_depth) + inst->bit_depth = ev_data->bit_depth; + + dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n", + sufficient ? "" : "not", ev_data->width, ev_data->height); + + switch (inst->codec_state) { + case VENUS_DEC_STATE_INIT: + inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; + break; + case VENUS_DEC_STATE_DECODING: + case VENUS_DEC_STATE_DRAIN: + inst->codec_state = VENUS_DEC_STATE_DRC; + break; + default: + break; + } + + /* + * The assumption is that the firmware have to return the last buffer + * before this event is received in the v4l2 driver. Also the firmware + * itself doesn't mark the last decoder output buffer with HFI EOS flag. + */ + + if (inst->codec_state == VENUS_DEC_STATE_DRC) { + int ret; + + inst->next_buf_last = true; + + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, false); + if (ret) + dev_dbg(dev, VDBGH "flush output error %d\n", ret); + } + + inst->next_buf_last = true; + inst->reconfig = true; + v4l2_event_queue_fh(&inst->fh, &ev); + wake_up(&inst->reconf_wait); + + mutex_unlock(&inst->lock); } static void vdec_event_notify(struct venus_inst *inst, u32 event, @@ -916,9 +1412,8 @@ { struct venus_core *core = inst->core; struct device *dev = core->dev_dec; - static const struct v4l2_event ev = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; + + vdec_pm_touch(inst); switch (event) { case EVT_SESSION_ERROR: @@ -928,18 +1423,10 @@ case EVT_SYS_EVENT_CHANGE: switch (data->event_type) { case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES: - hfi_session_continue(inst); - dev_dbg(dev, "event sufficient resources\n"); + vdec_event_change(inst, data, true); break; case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES: - inst->reconfig_height = data->height; - inst->reconfig_width = data->width; - inst->reconfig = true; - - v4l2_event_queue_fh(&inst->fh, &ev); - - dev_dbg(dev, "event not sufficient resources (%ux%u)\n", - data->width, data->height); + vdec_event_change(inst, data, false); break; case HFI_EVENT_RELEASE_BUFFER_REFERENCE: venus_helper_release_buf_ref(inst, data->tag); @@ -953,27 +1440,38 @@ } } +static void vdec_flush_done(struct venus_inst *inst) +{ + dev_dbg(inst->core->dev_dec, VDBGH "flush done\n"); +} + static const struct hfi_inst_ops vdec_hfi_ops = { .buf_done = vdec_buf_done, .event_notify = vdec_event_notify, + .flush_done = vdec_flush_done, }; static void vdec_inst_init(struct venus_inst *inst) { + inst->hfi_codec = HFI_VIDEO_CODEC_H264; inst->fmt_out = &vdec_formats[6]; inst->fmt_cap = &vdec_formats[0]; - inst->width = 1280; - inst->height = ALIGN(720, 32); - inst->out_width = 1280; - inst->out_height = 720; + inst->width = frame_width_min(inst); + inst->height = ALIGN(frame_height_min(inst), 32); + inst->out_width = frame_width_min(inst); + inst->out_height = frame_height_min(inst); inst->fps = 30; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 30; - inst->hfi_codec = HFI_VIDEO_CODEC_H264; + inst->opb_buftype = HFI_BUFFER_OUTPUT; +} + +static void vdec_m2m_device_run(void *priv) +{ } static const struct v4l2_m2m_ops vdec_m2m_ops = { - .device_run = venus_helper_m2m_device_run, + .device_run = vdec_m2m_device_run, .job_abort = venus_helper_m2m_job_abort, }; @@ -991,7 +1489,7 @@ src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; - src_vq->min_buffers_needed = 1; + src_vq->min_buffers_needed = 0; src_vq->dev = inst->core->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -1005,15 +1503,9 @@ dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; - dst_vq->min_buffers_needed = 1; + dst_vq->min_buffers_needed = 0; dst_vq->dev = inst->core->dev; - ret = vb2_queue_init(dst_vq); - if (ret) { - vb2_queue_release(src_vq); - return ret; - } - - return 0; + return vb2_queue_init(dst_vq); } static int vdec_open(struct file *file) @@ -1035,16 +1527,17 @@ inst->core = core; inst->session_type = VIDC_SESSION_TYPE_DEC; inst->num_output_bufs = 1; - + inst->codec_state = VENUS_DEC_STATE_DEINIT; + inst->buf_count = 0; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; + inst->bit_depth = VIDC_BITDEPTH_8; + init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); - - ret = pm_runtime_get_sync(core->dev_dec); - if (ret < 0) - goto err_free_inst; ret = vdec_ctrl_init(inst); if (ret) - goto err_put_sync; + goto err_free; ret = hfi_session_create(inst, &vdec_hfi_ops); if (ret) @@ -1083,9 +1576,7 @@ hfi_session_destroy(inst); err_ctrl_deinit: vdec_ctrl_deinit(inst); -err_put_sync: - pm_runtime_put_sync(core->dev_dec); -err_free_inst: +err_free: kfree(inst); return ret; } @@ -1093,6 +1584,8 @@ static int vdec_close(struct file *file) { struct venus_inst *inst = to_inst(file); + + vdec_pm_get(inst); v4l2_m2m_ctx_release(inst->m2m_ctx); v4l2_m2m_release(inst->m2m_dev); @@ -1102,7 +1595,7 @@ v4l2_fh_del(&inst->fh); v4l2_fh_exit(&inst->fh); - pm_runtime_put_sync(inst->core->dev_dec); + vdec_pm_put(inst, false); kfree(inst); return 0; @@ -1131,25 +1624,19 @@ if (!core) return -EPROBE_DEFER; - if (IS_V3(core) || IS_V4(core)) { - core->core0_clk = devm_clk_get(dev, "core"); - if (IS_ERR(core->core0_clk)) - return PTR_ERR(core->core0_clk); - } - - if (IS_V4(core)) { - core->core0_bus_clk = devm_clk_get(dev, "bus"); - if (IS_ERR(core->core0_bus_clk)) - return PTR_ERR(core->core0_bus_clk); - } - platform_set_drvdata(pdev, core); + + if (core->pm_ops->vdec_get) { + ret = core->pm_ops->vdec_get(dev); + if (ret) + return ret; + } vdev = video_device_alloc(); if (!vdev) return -ENOMEM; - strlcpy(vdev->name, "qcom-venus-decoder", sizeof(vdev->name)); + strscpy(vdev->name, "qcom-venus-decoder", sizeof(vdev->name)); vdev->release = video_device_release; vdev->fops = &vdec_fops; vdev->ioctl_ops = &vdec_ioctl_ops; @@ -1157,7 +1644,7 @@ vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; @@ -1165,6 +1652,8 @@ core->dev_dec = dev; video_set_drvdata(vdev, core); + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); return 0; @@ -1181,57 +1670,33 @@ video_unregister_device(core->vdev_dec); pm_runtime_disable(core->dev_dec); + if (core->pm_ops->vdec_put) + core->pm_ops->vdec_put(core->dev_dec); + return 0; } static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - if (IS_V1(core)) - return 0; + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_OFF); - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - if (IS_V4(core)) - clk_disable_unprepare(core->core0_bus_clk); - - clk_disable_unprepare(core->core0_clk); - - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + return ret; } static __maybe_unused int vdec_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - if (IS_V1(core)) - return 0; + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_ON); - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core0_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core0_bus_clk); - - if (ret) - goto err_unprepare_core0; - - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); - -err_unprepare_core0: - clk_disable_unprepare(core->core0_clk); -err_power_disable: - venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); return ret; } -- Gitblit v1.6.2