| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. |
| */ |
| |
| #include <linux/types.h> |
| #include <media/v4l2-mem2mem.h> |
| |
| #include "iris_ctrls.h" |
| #include "iris_instance.h" |
| |
| static inline bool iris_valid_cap_id(enum platform_inst_fw_cap_type cap_id) |
| { |
| return cap_id >= 1 && cap_id < INST_FW_CAP_MAX; |
| } |
| |
| static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) |
| { |
| switch (id) { |
| case V4L2_CID_MPEG_VIDEO_H264_PROFILE: |
| return PROFILE_H264; |
| case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: |
| return PROFILE_HEVC; |
| case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: |
| return PROFILE_VP9; |
| case V4L2_CID_MPEG_VIDEO_H264_LEVEL: |
| return LEVEL_H264; |
| case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: |
| return LEVEL_HEVC; |
| case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: |
| return LEVEL_VP9; |
| case V4L2_CID_MPEG_VIDEO_HEVC_TIER: |
| return TIER; |
| default: |
| return INST_FW_CAP_MAX; |
| } |
| } |
| |
| static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) |
| { |
| if (!iris_valid_cap_id(cap_id)) |
| return 0; |
| |
| switch (cap_id) { |
| case PROFILE_H264: |
| return V4L2_CID_MPEG_VIDEO_H264_PROFILE; |
| case PROFILE_HEVC: |
| return V4L2_CID_MPEG_VIDEO_HEVC_PROFILE; |
| case PROFILE_VP9: |
| return V4L2_CID_MPEG_VIDEO_VP9_PROFILE; |
| case LEVEL_H264: |
| return V4L2_CID_MPEG_VIDEO_H264_LEVEL; |
| case LEVEL_HEVC: |
| return V4L2_CID_MPEG_VIDEO_HEVC_LEVEL; |
| case LEVEL_VP9: |
| return V4L2_CID_MPEG_VIDEO_VP9_LEVEL; |
| case TIER: |
| return V4L2_CID_MPEG_VIDEO_HEVC_TIER; |
| default: |
| return 0; |
| } |
| } |
| |
| static int iris_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) |
| { |
| struct iris_inst *inst = container_of(ctrl->handler, struct iris_inst, ctrl_handler); |
| enum platform_inst_fw_cap_type cap_id; |
| struct platform_inst_fw_cap *cap; |
| struct vb2_queue *q; |
| |
| cap = &inst->fw_caps[0]; |
| cap_id = iris_get_cap_id(ctrl->id); |
| if (!iris_valid_cap_id(cap_id)) |
| return -EINVAL; |
| |
| q = v4l2_m2m_get_src_vq(inst->m2m_ctx); |
| if (vb2_is_streaming(q) && |
| (!(inst->fw_caps[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED))) |
| return -EINVAL; |
| |
| cap[cap_id].flags |= CAP_FLAG_CLIENT_SET; |
| |
| inst->fw_caps[cap_id].value = ctrl->val; |
| |
| return 0; |
| } |
| |
| static const struct v4l2_ctrl_ops iris_ctrl_ops = { |
| .s_ctrl = iris_vdec_op_s_ctrl, |
| }; |
| |
| int iris_ctrls_init(struct iris_inst *inst) |
| { |
| struct platform_inst_fw_cap *cap = &inst->fw_caps[0]; |
| u32 num_ctrls = 0, ctrl_idx = 0, idx = 0; |
| u32 v4l2_id; |
| int ret; |
| |
| for (idx = 1; idx < INST_FW_CAP_MAX; idx++) { |
| if (iris_get_v4l2_id(cap[idx].cap_id)) |
| num_ctrls++; |
| } |
| |
| /* Adding 1 to num_ctrls to include V4L2_CID_MIN_BUFFERS_FOR_CAPTURE */ |
| |
| ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls + 1); |
| if (ret) |
| return ret; |
| |
| for (idx = 1; idx < INST_FW_CAP_MAX; idx++) { |
| struct v4l2_ctrl *ctrl; |
| |
| v4l2_id = iris_get_v4l2_id(cap[idx].cap_id); |
| if (!v4l2_id) |
| continue; |
| |
| if (ctrl_idx >= num_ctrls) { |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| if (cap[idx].flags & CAP_FLAG_MENU) { |
| ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, |
| &iris_ctrl_ops, |
| v4l2_id, |
| cap[idx].max, |
| ~(cap[idx].step_or_mask), |
| cap[idx].value); |
| } else { |
| ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, |
| &iris_ctrl_ops, |
| v4l2_id, |
| cap[idx].min, |
| cap[idx].max, |
| cap[idx].step_or_mask, |
| cap[idx].value); |
| } |
| if (!ctrl) { |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| ctrl_idx++; |
| } |
| |
| v4l2_ctrl_new_std(&inst->ctrl_handler, NULL, |
| V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 4); |
| |
| ret = inst->ctrl_handler.error; |
| if (ret) |
| goto error; |
| |
| return 0; |
| error: |
| v4l2_ctrl_handler_free(&inst->ctrl_handler); |
| |
| return ret; |
| } |
| |
| void iris_session_init_caps(struct iris_core *core) |
| { |
| struct platform_inst_fw_cap *caps; |
| u32 i, num_cap, cap_id; |
| |
| caps = core->iris_platform_data->inst_fw_caps; |
| num_cap = core->iris_platform_data->inst_fw_caps_size; |
| |
| for (i = 0; i < num_cap; i++) { |
| cap_id = caps[i].cap_id; |
| if (!iris_valid_cap_id(cap_id)) |
| continue; |
| |
| core->inst_fw_caps[cap_id].cap_id = caps[i].cap_id; |
| core->inst_fw_caps[cap_id].min = caps[i].min; |
| core->inst_fw_caps[cap_id].max = caps[i].max; |
| core->inst_fw_caps[cap_id].step_or_mask = caps[i].step_or_mask; |
| core->inst_fw_caps[cap_id].value = caps[i].value; |
| core->inst_fw_caps[cap_id].flags = caps[i].flags; |
| core->inst_fw_caps[cap_id].hfi_id = caps[i].hfi_id; |
| core->inst_fw_caps[cap_id].set = caps[i].set; |
| } |
| } |
| |
| static u32 iris_get_port_info(struct iris_inst *inst, |
| enum platform_inst_fw_cap_type cap_id) |
| { |
| if (inst->fw_caps[cap_id].flags & CAP_FLAG_INPUT_PORT) |
| return HFI_PORT_BITSTREAM; |
| else if (inst->fw_caps[cap_id].flags & CAP_FLAG_OUTPUT_PORT) |
| return HFI_PORT_RAW; |
| |
| return HFI_PORT_NONE; |
| } |
| |
| int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) |
| { |
| const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; |
| u32 hfi_value = inst->fw_caps[cap_id].value; |
| u32 hfi_id = inst->fw_caps[cap_id].hfi_id; |
| |
| return hfi_ops->session_set_property(inst, hfi_id, |
| HFI_HOST_FLAGS_NONE, |
| iris_get_port_info(inst, cap_id), |
| HFI_PAYLOAD_U32_ENUM, |
| &hfi_value, sizeof(u32)); |
| } |
| |
| int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) |
| { |
| const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; |
| u32 hfi_value = inst->fw_caps[cap_id].value; |
| u32 hfi_id = inst->fw_caps[cap_id].hfi_id; |
| |
| return hfi_ops->session_set_property(inst, hfi_id, |
| HFI_HOST_FLAGS_NONE, |
| iris_get_port_info(inst, cap_id), |
| HFI_PAYLOAD_U32, |
| &hfi_value, sizeof(u32)); |
| } |
| |
| int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) |
| { |
| const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; |
| struct v4l2_format *inp_f = inst->fmt_src; |
| u32 hfi_id = inst->fw_caps[cap_id].hfi_id; |
| u32 height = inp_f->fmt.pix_mp.height; |
| u32 width = inp_f->fmt.pix_mp.width; |
| u32 work_mode = STAGE_2; |
| |
| if (iris_res_is_less_than(width, height, 1280, 720)) |
| work_mode = STAGE_1; |
| |
| return hfi_ops->session_set_property(inst, hfi_id, |
| HFI_HOST_FLAGS_NONE, |
| iris_get_port_info(inst, cap_id), |
| HFI_PAYLOAD_U32, |
| &work_mode, sizeof(u32)); |
| } |
| |
| int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) |
| { |
| const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; |
| u32 work_route = inst->fw_caps[PIPE].value; |
| u32 hfi_id = inst->fw_caps[cap_id].hfi_id; |
| |
| return hfi_ops->session_set_property(inst, hfi_id, |
| HFI_HOST_FLAGS_NONE, |
| iris_get_port_info(inst, cap_id), |
| HFI_PAYLOAD_U32, |
| &work_route, sizeof(u32)); |
| } |
| |
| int iris_set_properties(struct iris_inst *inst, u32 plane) |
| { |
| const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; |
| struct platform_inst_fw_cap *cap; |
| int ret; |
| u32 i; |
| |
| ret = hfi_ops->session_set_config_params(inst, plane); |
| if (ret) |
| return ret; |
| |
| for (i = 1; i < INST_FW_CAP_MAX; i++) { |
| cap = &inst->fw_caps[i]; |
| if (!iris_valid_cap_id(cap->cap_id)) |
| continue; |
| |
| if (cap->cap_id && cap->set) |
| cap->set(inst, i); |
| } |
| |
| return 0; |
| } |