| /* |
| * Copyright(c) 2018 NXP. All rights reserved. |
| * |
| * The code contained herein is licensed under the GNU General Public |
| * License. You may obtain a copy of the GNU General Public License |
| * Version 2 or later at the following locations: |
| * |
| * http://www.opensource.org/licenses/gpl-license.html |
| * http://www.gnu.org/copyleft/gpl.html |
| * |
| * @file vpu_encoder_ctrl.c |
| * |
| * Author Ming Qian<ming.qian@nxp.com> |
| */ |
| |
| #define TAG "[VPU Encoder Ctrl]\t " |
| #include <media/v4l2-ctrls.h> |
| |
| #include "vpu_encoder_b0.h" |
| #include "vpu_encoder_ctrl.h" |
| |
| // H264 level is maped like level 5.1 to uLevel 51, except level 1b to uLevel 14 |
| const u_int32 h264_level[] = { |
| [V4L2_MPEG_VIDEO_H264_LEVEL_1_0] = 10, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_1B] = 14, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_1_1] = 11, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_1_2] = 12, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_1_3] = 13, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_2_0] = 20, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_2_1] = 21, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_2_2] = 22, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_3_0] = 30, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_3_1] = 31, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_3_2] = 32, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_4_0] = 40, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_4_1] = 41, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_4_2] = 42, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_5_0] = 50, |
| [V4L2_MPEG_VIDEO_H264_LEVEL_5_1] = 51 |
| }; |
| |
| static int set_h264_profile(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| mutex_lock(&ctx->instance_mutex); |
| switch (ctrl->val) { |
| case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: |
| vpu_dbg(LVL_CTRL, "set h264 profile baseline\n"); |
| param->eProfile = MEDIAIP_ENC_PROF_H264_BP; |
| break; |
| case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: |
| vpu_dbg(LVL_CTRL, "set h264 profile main\n"); |
| param->eProfile = MEDIAIP_ENC_PROF_H264_MP; |
| break; |
| case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: |
| vpu_dbg(LVL_CTRL, "set h264 profile high\n"); |
| param->eProfile = MEDIAIP_ENC_PROF_H264_HP; |
| break; |
| default: |
| vpu_err("not support H264 profile %d, set to main\n", |
| ctrl->val); |
| param->eProfile = MEDIAIP_ENC_PROF_H264_MP; |
| break; |
| } |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_h264_level(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| mutex_lock(&ctx->instance_mutex); |
| param->uLevel = h264_level[ctrl->val]; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| vpu_dbg(LVL_CTRL, "set h264 level to %d (%d)\n", |
| ctrl->val, h264_level[ctrl->val]); |
| |
| return 0; |
| } |
| |
| static int set_bitrate_mode(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| mutex_lock(&ctx->instance_mutex); |
| switch (ctrl->val) { |
| case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: |
| vpu_dbg(LVL_CTRL, "set bitrate mode VBR\n"); |
| param->eBitRateMode = |
| MEDIAIP_ENC_BITRATECONTROLMODE_CONSTANT_QP; |
| break; |
| case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: |
| vpu_dbg(LVL_CTRL, "set bitrate mode CBR\n"); |
| param->eBitRateMode = MEDIAIP_ENC_BITRATECONTROLMODE_CBR; |
| break; |
| default: |
| vpu_err("not support bitrate mode %d, set to cbr\n", |
| ctrl->val); |
| param->eBitRateMode = MEDIAIP_ENC_BITRATECONTROLMODE_CBR; |
| break; |
| } |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_bitrate(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set bitrate %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| param->uTargetBitrate = ctrl->val / BITRATE_COEF; |
| if (param->uMaxBitRate < param->uTargetBitrate) |
| param->uMaxBitRate = param->uTargetBitrate; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_bitrate_peak(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set peak bitrate %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| param->uMaxBitRate = ctrl->val / BITRATE_COEF; |
| if (param->uTargetBitrate > param->uMaxBitRate) |
| param->uTargetBitrate = param->uMaxBitRate; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_gop_size(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set gop size %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| param->uIFrameInterval = ctrl->val; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_i_period(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set iframe interval %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| param->uIFrameInterval = ctrl->val; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int get_gop_size(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "get gop size\n"); |
| ctrl->val = param->uIFrameInterval; |
| |
| return 0; |
| } |
| |
| static int set_b_frames(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set bframes %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| param->uGopBLength = ctrl->val; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_qp(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set qp %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| param->uInitSliceQP = ctrl->val; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int get_min_buffers_for_output(struct v4l2_ctrl *ctrl) |
| { |
| vpu_dbg(LVL_CTRL, "get min buffers for output\n"); |
| |
| ctrl->val = MIN_BUFFER_COUNT; |
| |
| return 0; |
| } |
| |
| static int set_display_re_ordering(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| struct vpu_attr *attr = get_vpu_ctx_attr(ctx); |
| pMEDIAIP_ENC_PARAM param = &attr->param; |
| |
| vpu_dbg(LVL_CTRL, "set lowlatencymode %d\n", ctrl->val); |
| mutex_lock(&ctx->instance_mutex); |
| if (ctrl->val) |
| param->uLowLatencyMode = 1; |
| else |
| param->uLowLatencyMode = 0; |
| mutex_unlock(&ctx->instance_mutex); |
| |
| return 0; |
| } |
| |
| static int set_force_key_frame(struct v4l2_ctrl *ctrl) |
| { |
| struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); |
| |
| vpu_dbg(LVL_CTRL, "force key frame\n"); |
| set_bit(VPU_ENC_STATUS_KEY_FRAME, &ctx->status); |
| |
| return 0; |
| } |
| |
| static int add_ctrl_h264_profile(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_h264_profile_ops = { |
| .s_ctrl = set_h264_profile, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, |
| &ctrl_h264_profile_ops, |
| V4L2_CID_MPEG_VIDEO_H264_PROFILE, |
| V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, |
| 0xa, |
| V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); |
| if (!ctrl) { |
| vpu_err("add ctrl h264 profile fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_h264_level(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_h264_level_ops = { |
| .s_ctrl = set_h264_level, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, |
| &ctrl_h264_level_ops, |
| V4L2_CID_MPEG_VIDEO_H264_LEVEL, |
| V4L2_MPEG_VIDEO_H264_LEVEL_5_1, |
| 0x0, |
| V4L2_MPEG_VIDEO_H264_LEVEL_4_0); |
| if (!ctrl) { |
| vpu_err("add ctrl h264 level fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_bitrate_mode(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_bitrate_mode_ops = { |
| .s_ctrl = set_bitrate_mode, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, |
| &ctrl_bitrate_mode_ops, |
| V4L2_CID_MPEG_VIDEO_BITRATE_MODE, |
| V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, |
| 0x0, |
| V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); |
| if (!ctrl) { |
| vpu_err("add ctrl bitrate mode fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_bitrate(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_bitrate_ops = { |
| .s_ctrl = set_bitrate, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_bitrate_ops, |
| V4L2_CID_MPEG_VIDEO_BITRATE, |
| BITRATE_LOW_THRESHOLD * BITRATE_COEF, |
| BITRATE_HIGH_THRESHOLD * BITRATE_COEF, |
| BITRATE_COEF, |
| BITRATE_DEFAULT_TARGET * BITRATE_COEF); |
| if (!ctrl) { |
| vpu_err("add ctrl bitrate fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_bitrate_peak(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_bitrate_ops = { |
| .s_ctrl = set_bitrate_peak, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_bitrate_ops, |
| V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, |
| BITRATE_LOW_THRESHOLD * BITRATE_COEF, |
| BITRATE_HIGH_THRESHOLD * BITRATE_COEF, |
| BITRATE_COEF, |
| BITRATE_DEFAULT_PEAK * BITRATE_COEF); |
| if (!ctrl) { |
| vpu_err("add ctrl bitrate peak fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_gop_size(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_gop_ops = { |
| .s_ctrl = set_gop_size, |
| .g_volatile_ctrl = get_gop_size, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_gop_ops, |
| V4L2_CID_MPEG_VIDEO_GOP_SIZE, |
| GOP_L_THRESHOLD, |
| GOP_H_THRESHOLD, |
| 1, |
| GOP_DEFAULT); |
| if (!ctrl) { |
| vpu_err("add ctrl gop size fail\n"); |
| return -EINVAL; |
| } |
| |
| ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; |
| ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; |
| |
| return 0; |
| } |
| |
| static int add_ctrl_i_period(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_i_period_ops = { |
| .s_ctrl = set_i_period, |
| .g_volatile_ctrl = get_gop_size, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_i_period_ops, |
| V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, |
| GOP_L_THRESHOLD, |
| GOP_H_THRESHOLD, |
| 1, |
| GOP_DEFAULT); |
| |
| if (!ctrl) { |
| vpu_err("add ctrl i period fail\n"); |
| return -EINVAL; |
| } |
| |
| ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; |
| ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; |
| |
| return 0; |
| } |
| |
| static int add_ctrl_b_frames(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_b_frames = { |
| .s_ctrl = set_b_frames, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_b_frames, |
| V4L2_CID_MPEG_VIDEO_B_FRAMES, |
| BFRAMES_L_THRESHOLD, |
| BFRAMES_H_THRESHOLD, |
| 1, |
| BFRAMES_DEFAULT); |
| |
| if (!ctrl) { |
| vpu_err("add ctrl b frames fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_i_frame_qp(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_iframe_qp_ops = { |
| .s_ctrl = set_qp, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_iframe_qp_ops, |
| V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, |
| QP_MIN, |
| QP_MAX, |
| 1, |
| QP_DEFAULT); |
| if (!ctrl) { |
| vpu_err("add ctrl h264 I frame qp fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_p_frame_qp(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_pframe_qp_ops = { |
| .s_ctrl = set_qp, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_pframe_qp_ops, |
| V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, |
| QP_MIN, |
| QP_MAX, |
| 1, |
| QP_DEFAULT); |
| if (!ctrl) { |
| vpu_err("add ctrl h264 P frame qp fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_b_frame_qp(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_bframe_qp_ops = { |
| .s_ctrl = set_qp, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_bframe_qp_ops, |
| V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, |
| QP_MIN, |
| QP_MAX, |
| 1, |
| QP_DEFAULT); |
| if (!ctrl) { |
| vpu_err("add ctrl h264 B frame qp fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_min_buffers_for_output(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops ctrl_min_buffers_ops = { |
| .g_volatile_ctrl = get_min_buffers_for_output, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &ctrl_min_buffers_ops, |
| V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, |
| 1, |
| 32, |
| 1, |
| MIN_BUFFER_COUNT); |
| if (!ctrl) { |
| vpu_err("add ctrl min buffers for output fail\n"); |
| return -EINVAL; |
| } |
| |
| ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; |
| |
| return 0; |
| } |
| |
| static int add_ctrl_display_re_ordering(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops re_ordering_ops = { |
| .s_ctrl = set_display_re_ordering, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &re_ordering_ops, |
| V4L2_CID_MPEG_VIDEO_H264_ASO, |
| 0, 1, 1, 1); |
| if (!ctrl) { |
| vpu_err("add ctrl display re ordering fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int add_ctrl_force_key_frame(struct vpu_ctx *ctx) |
| { |
| static const struct v4l2_ctrl_ops force_key_frame_ops = { |
| .s_ctrl = set_force_key_frame, |
| }; |
| struct v4l2_ctrl *ctrl; |
| |
| ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, |
| &force_key_frame_ops, |
| V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, |
| 0, 0, 0, 0); |
| if (!ctrl) { |
| vpu_err("add ctrl force key frame fail\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int vpu_enc_register_ctrls(struct vpu_ctx *ctx) |
| { |
| add_ctrl_h264_profile(ctx); |
| add_ctrl_h264_level(ctx); |
| add_ctrl_bitrate_mode(ctx); |
| add_ctrl_bitrate(ctx); |
| add_ctrl_bitrate_peak(ctx); |
| add_ctrl_gop_size(ctx); |
| add_ctrl_i_period(ctx); |
| add_ctrl_b_frames(ctx); |
| add_ctrl_i_frame_qp(ctx); |
| add_ctrl_p_frame_qp(ctx); |
| add_ctrl_b_frame_qp(ctx); |
| add_ctrl_min_buffers_for_output(ctx); |
| add_ctrl_display_re_ordering(ctx); |
| add_ctrl_force_key_frame(ctx); |
| |
| return 0; |
| } |
| |
| int vpu_enc_setup_ctrls(struct vpu_ctx *ctx) |
| { |
| vpu_log_func(); |
| |
| v4l2_ctrl_handler_init(&ctx->ctrl_handler, 11); |
| vpu_enc_register_ctrls(ctx); |
| if (ctx->ctrl_handler.error) { |
| vpu_err("control initialization error (%d)\n", |
| ctx->ctrl_handler.error); |
| return -EINVAL; |
| } |
| ctx->ctrl_inited = true; |
| return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); |
| } |
| |
| int vpu_enc_free_ctrls(struct vpu_ctx *ctx) |
| { |
| vpu_log_func(); |
| |
| if (ctx->ctrl_inited) { |
| v4l2_ctrl_handler_free(&ctx->ctrl_handler); |
| ctx->ctrl_inited = false; |
| } |
| |
| return 0; |
| } |