Implement all iris v4l2 ioctls ops supported by encoder. Add state checks to ensure ioctl are allowed in valid instance state only. Codec format can be changed by client during s_fmt. Update the v4l2 control values according to the updated codec format. Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx> --- drivers/media/platform/qcom/vcodec/iris/Makefile | 1 + .../media/platform/qcom/vcodec/iris/iris_core.h | 8 +- .../media/platform/qcom/vcodec/iris/iris_ctrls.c | 59 ++- .../media/platform/qcom/vcodec/iris/iris_helpers.c | 89 +++- .../media/platform/qcom/vcodec/iris/iris_helpers.h | 3 + .../platform/qcom/vcodec/iris/iris_instance.h | 5 + .../media/platform/qcom/vcodec/iris/iris_probe.c | 3 +- .../media/platform/qcom/vcodec/iris/iris_state.c | 5 +- .../media/platform/qcom/vcodec/iris/iris_vdec.c | 80 +--- .../media/platform/qcom/vcodec/iris/iris_venc.c | 525 +++++++++++++++++++++ .../media/platform/qcom/vcodec/iris/iris_venc.h | 24 + .../media/platform/qcom/vcodec/iris/iris_vidc.c | 415 ++++++++++++++-- .../platform/qcom/vcodec/iris/platform_common.h | 2 + .../platform/qcom/vcodec/iris/platform_sm8550.c | 1 + 14 files changed, 1088 insertions(+), 132 deletions(-) create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_venc.c create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_venc.h diff --git a/drivers/media/platform/qcom/vcodec/iris/Makefile b/drivers/media/platform/qcom/vcodec/iris/Makefile index 4c8f8f6..b95d627 100644 --- a/drivers/media/platform/qcom/vcodec/iris/Makefile +++ b/drivers/media/platform/qcom/vcodec/iris/Makefile @@ -6,6 +6,7 @@ iris-objs += iris_probe.o \ iris_vidc.o \ iris_vb2.o \ iris_vdec.o \ + iris_venc.o \ iris_state.o \ iris_ctrls.o \ iris_helpers.o \ diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_core.h b/drivers/media/platform/qcom/vcodec/iris/iris_core.h index c56eb24..baced21 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_core.h +++ b/drivers/media/platform/qcom/vcodec/iris/iris_core.h @@ -25,7 +25,8 @@ * @vdev_dec: iris video device structure for decoder * @vdev_enc: iris video device structure for encoder * @v4l2_file_ops: iris v4l2 file ops - * @v4l2_ioctl_ops: iris v4l2 ioctl ops + * @v4l2_ioctl_ops_dec: iris v4l2 ioctl ops for decoder + * @v4l2_ioctl_ops_enc: iris v4l2 ioctl ops for encoder * @bus_tbl: table of iris buses * @bus_count: count of iris buses * @power_domain_tbl: table of iris power domains @@ -52,6 +53,7 @@ * @packet_id: id of packet * @vpu_ops: a pointer to vpu ops * @session_ops: a pointer to session level ops + * @enc_codecs_count: supported codec count for encoder * @dec_codecs_count: supported codec count for decoder * @platform_data: a structure for platform data * @cap: an array for supported core capabilities @@ -69,7 +71,8 @@ struct iris_core { struct video_device *vdev_dec; struct video_device *vdev_enc; const struct v4l2_file_operations *v4l2_file_ops; - const struct v4l2_ioctl_ops *v4l2_ioctl_ops; + const struct v4l2_ioctl_ops *v4l2_ioctl_ops_dec; + const struct v4l2_ioctl_ops *v4l2_ioctl_ops_enc; struct bus_info *bus_tbl; u32 bus_count; struct power_domain_info *power_domain_tbl; @@ -97,6 +100,7 @@ struct iris_core { const struct vpu_ops *vpu_ops; const struct vpu_session_ops *session_ops; u32 dec_codecs_count; + u32 enc_codecs_count; struct platform_data *platform_data; struct plat_core_cap cap[CORE_CAP_MAX + 1]; struct plat_inst_caps *inst_caps; diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_ctrls.c b/drivers/media/platform/qcom/vcodec/iris/iris_ctrls.c index a648cc1..af99ac73 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/vcodec/iris/iris_ctrls.c @@ -217,7 +217,7 @@ static int set_dynamic_property(struct iris_inst *inst, return ret; } -static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +static int iris_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { enum plat_inst_cap_type cap_id; struct iris_inst *inst = NULL; @@ -242,7 +242,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) return ret; } -static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) +static int iris_op_s_ctrl(struct v4l2_ctrl *ctrl) { struct cap_entry *entry = NULL, *temp = NULL; struct list_head children_list, firmware_list; @@ -273,7 +273,8 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) cap[cap_id].flags |= CAP_FLAG_CLIENT_SET; - if (!inst->vb2q_src->streaming) { + if ((inst->domain == ENCODER && !inst->vb2q_dst->streaming) || + (inst->domain == DECODER && !inst->vb2q_src->streaming)) { inst->cap[cap_id].value = ctrl->val; } else { ret = adjust_dynamic_property(inst, cap_id, ctrl, @@ -300,16 +301,16 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) } static const struct v4l2_ctrl_ops ctrl_ops = { - .s_ctrl = vdec_op_s_ctrl, - .g_volatile_ctrl = vdec_op_g_volatile_ctrl, + .s_ctrl = iris_op_s_ctrl, + .g_volatile_ctrl = iris_op_g_volatile_ctrl, }; int ctrls_init(struct iris_inst *inst, bool init) { int num_ctrls = 0, ctrl_idx = 0; + u64 codecs_count, step_or_mask; struct plat_inst_cap *cap; struct iris_core *core; - u64 step_or_mask; int idx = 0; int ret = 0; @@ -324,8 +325,11 @@ int ctrls_init(struct iris_inst *inst, bool init) return -EINVAL; if (init) { + codecs_count = inst->domain == ENCODER ? + core->enc_codecs_count : + core->dec_codecs_count; ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, - INST_CAP_MAX * core->dec_codecs_count); + INST_CAP_MAX * codecs_count); if (ret) return ret; } @@ -451,29 +455,49 @@ static int update_inst_capability(struct plat_inst_cap *in, int iris_init_instance_caps(struct iris_core *core) { struct plat_inst_cap *inst_plat_cap_data = NULL; - u8 dec_codecs_count = 0; - int num_inst_cap; - u32 dec_valid_codecs; + u8 enc_codecs_count = 0, dec_codecs_count = 0; + u32 enc_valid_codecs, dec_valid_codecs; int i, j, check_bit = 0; + u8 codecs_count = 0; + int num_inst_cap; int ret = 0; inst_plat_cap_data = core->platform_data->inst_cap_data; if (!inst_plat_cap_data) return -EINVAL; + enc_valid_codecs = core->cap[ENC_CODECS].value; + enc_codecs_count = hweight32(enc_valid_codecs); + core->enc_codecs_count = enc_codecs_count; + dec_valid_codecs = core->cap[DEC_CODECS].value; dec_codecs_count = hweight32(dec_valid_codecs); core->dec_codecs_count = dec_codecs_count; + codecs_count = enc_codecs_count + dec_codecs_count; core->inst_caps = devm_kzalloc(core->dev, - dec_codecs_count * sizeof(struct plat_inst_caps), + codecs_count * sizeof(struct plat_inst_caps), GFP_KERNEL); if (!core->inst_caps) return -ENOMEM; - for (i = 0; i < dec_codecs_count; i++) { + for (i = 0; i < enc_codecs_count; i++) { + while (check_bit < (sizeof(enc_valid_codecs) * 8)) { + if (enc_valid_codecs & BIT(check_bit)) { + core->inst_caps[i].domain = ENCODER; + core->inst_caps[i].codec = enc_valid_codecs & + BIT(check_bit); + check_bit++; + break; + } + check_bit++; + } + } + + for (; i < codecs_count; i++) { while (check_bit < (sizeof(dec_valid_codecs) * 8)) { if (dec_valid_codecs & BIT(check_bit)) { + core->inst_caps[i].domain = DECODER; core->inst_caps[i].codec = dec_valid_codecs & BIT(check_bit); check_bit++; @@ -486,9 +510,9 @@ int iris_init_instance_caps(struct iris_core *core) num_inst_cap = core->platform_data->inst_cap_data_size; for (i = 0; i < num_inst_cap; i++) { - for (j = 0; j < dec_codecs_count; j++) { - if ((inst_plat_cap_data[i].codec & - core->inst_caps[j].codec)) { + for (j = 0; j < codecs_count; j++) { + if ((inst_plat_cap_data[i].domain & core->inst_caps[j].domain) && + (inst_plat_cap_data[i].codec & core->inst_caps[j].codec)) { ret = update_inst_capability(&inst_plat_cap_data[i], &core->inst_caps[j]); if (ret) @@ -503,11 +527,14 @@ int iris_init_instance_caps(struct iris_core *core) int get_inst_capability(struct iris_inst *inst) { struct iris_core *core; + u32 codecs_count = 0; int i; core = inst->core; - for (i = 0; i < core->dec_codecs_count; i++) { + codecs_count = core->enc_codecs_count + core->dec_codecs_count; + + for (i = 0; i < codecs_count; i++) { if (core->inst_caps[i].codec == inst->codec) { memcpy(&inst->cap[0], &core->inst_caps[i].cap[0], (INST_CAP_MAX + 1) * sizeof(struct plat_inst_cap)); diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c index d9d22e2..c84bb51 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c +++ b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c @@ -7,6 +7,7 @@ #include "hfi_defines.h" #include "iris_core.h" +#include "iris_ctrls.h" #include "iris_helpers.h" #include "iris_hfi.h" #include "iris_hfi_packet.h" @@ -273,7 +274,7 @@ int close_session(struct iris_inst *inst) return ret; } -static int check_core_mbps_mbpf(struct iris_inst *inst) +int check_core_mbps_mbpf(struct iris_inst *inst) { u32 mbpf = 0, mbps = 0, total_mbpf = 0, total_mbps = 0; struct iris_core *core; @@ -284,7 +285,9 @@ static int check_core_mbps_mbpf(struct iris_inst *inst) mutex_lock(&core->lock); list_for_each_entry(instance, &core->instances, list) { - fps = inst->cap[QUEUED_RATE].value >> 16; + fps = max3(inst->cap[QUEUED_RATE].value >> 16, + inst->cap[FRAME_RATE].value >> 16, + inst->cap[OPERATING_RATE].value >> 16); mbpf = get_mbpf(inst); mbps = mbpf * fps; total_mbpf += mbpf; @@ -814,6 +817,88 @@ int session_streamoff(struct iris_inst *inst, u32 plane) return ret; } +int process_resume(struct iris_inst *inst) +{ + enum iris_inst_sub_state clear_sub_state = IRIS_INST_SUB_NONE; + int ret; + + if (inst->sub_state & IRIS_INST_SUB_DRC && + inst->sub_state & IRIS_INST_SUB_DRC_LAST) { + clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST; + + if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { + ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE); + if (ret) + return ret; + clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE; + } + if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) { + ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE); + if (ret) + return ret; + clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE; + } + } else if (inst->sub_state & IRIS_INST_SUB_DRAIN && + inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) { + clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST; + if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { + ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_DRAIN); + if (ret) + return ret; + clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE; + } + if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) { + ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_DRAIN); + if (ret) + return ret; + clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE; + } + } + + ret = iris_inst_change_sub_state(inst, clear_sub_state, 0); + + return ret; +} + +int codec_change(struct iris_inst *inst, u32 v4l2_codec) +{ + bool session_init = false; + int ret; + + if (!inst->codec) + session_init = true; + + if (inst->codec && + ((inst->domain == DECODER && inst->fmt_src->fmt.pix_mp.pixelformat == v4l2_codec) || + (inst->domain == ENCODER && inst->fmt_dst->fmt.pix_mp.pixelformat == v4l2_codec))) + return 0; + + inst->codec = v4l2_codec_to_driver(inst, v4l2_codec); + if (!inst->codec) + return -EINVAL; + + if (inst->domain == DECODER) + inst->fmt_src->fmt.pix_mp.pixelformat = v4l2_codec; + else if (inst->domain == ENCODER) + inst->fmt_dst->fmt.pix_mp.pixelformat = v4l2_codec; + + ret = get_inst_capability(inst); + if (ret) + return ret; + + ret = ctrls_init(inst, session_init); + if (ret) + return ret; + + ret = update_buffer_count(inst, INPUT_MPLANE); + if (ret) + return ret; + + ret = update_buffer_count(inst, OUTPUT_MPLANE); + + return ret; +} + int iris_pm_get(struct iris_core *core) { int ret; diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h index c628b2e..39cec8c 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h +++ b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h @@ -41,6 +41,7 @@ enum codec_type v4l2_codec_to_driver(struct iris_inst *inst, u32 v4l2_codec); u32 v4l2_colorformat_from_driver(struct iris_inst *inst, enum colorformat_type colorformat); enum colorformat_type v4l2_colorformat_to_driver(struct iris_inst *inst, u32 v4l2_colorformat); struct vb2_queue *get_vb2q(struct iris_inst *inst, u32 type); +int check_core_mbps_mbpf(struct iris_inst *inst); int check_session_supported(struct iris_inst *inst); struct iris_buffer *get_driver_buf(struct iris_inst *inst, u32 plane, u32 index); @@ -51,6 +52,8 @@ int iris_vb2_buffer_done(struct iris_inst *inst, int iris_release_nonref_buffers(struct iris_inst *inst); void iris_destroy_buffers(struct iris_inst *inst); int session_streamoff(struct iris_inst *inst, u32 plane); +int process_resume(struct iris_inst *inst); +int codec_change(struct iris_inst *inst, u32 v4l2_codec); int iris_pm_get(struct iris_core *core); int iris_pm_put(struct iris_core *core, bool autosuspend); int iris_pm_get_put(struct iris_core *core); diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_instance.h b/drivers/media/platform/qcom/vcodec/iris/iris_instance.h index 70f4c7d..a1547c5 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_instance.h +++ b/drivers/media/platform/qcom/vcodec/iris/iris_instance.h @@ -8,6 +8,7 @@ #include <media/v4l2-ctrls.h> +#include "hfi_defines.h" #include "iris_buffer.h" #include "iris_common.h" #include "iris_core.h" @@ -29,6 +30,7 @@ * @fmt_dst: structure of v4l2_format for destination * @ctrl_handler: reference of v4l2 ctrl handler * @crop: structure of crop info + * @compose: structure of compose info * @packet: HFI packet * @packet_size: HFI packet size * @completions: structure of signal completions @@ -36,6 +38,7 @@ * @num_ctrls: supported number of controls * @caps_list: list head of capability * @codec: codec type + * @domain: domain type: encoder or decoder * @buffers: structure of buffer info * @fw_min_count: minimnum count of buffers needed by fw * @state: instance state @@ -70,6 +73,7 @@ struct iris_inst { struct v4l2_format *fmt_dst; struct v4l2_ctrl_handler ctrl_handler; struct rect_desc crop; + struct rect_desc compose; void *packet; u32 packet_size; struct completion completions[MAX_SIGNAL]; @@ -77,6 +81,7 @@ struct iris_inst { u32 num_ctrls; struct list_head caps_list; enum codec_type codec; + enum domain_type domain; struct iris_buffers_info buffers; u32 fw_min_count; enum iris_inst_state state; diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c index b487e83..49d2701 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c +++ b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c @@ -42,13 +42,13 @@ static int iris_register_video_device(struct iris_core *core, enum domain_type t vdev->release = video_device_release; vdev->fops = core->v4l2_file_ops; - vdev->ioctl_ops = core->v4l2_ioctl_ops; vdev->vfl_dir = VFL_DIR_M2M; vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; if (type == DECODER) { strscpy(vdev->name, "qcom-iris-decoder", sizeof(vdev->name)); + vdev->ioctl_ops = core->v4l2_ioctl_ops_dec; ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) @@ -57,6 +57,7 @@ static int iris_register_video_device(struct iris_core *core, enum domain_type t core->vdev_dec = vdev; } else if (type == ENCODER) { strscpy(vdev->name, "qcom-iris-encoder", sizeof(vdev->name)); + vdev->ioctl_ops = core->v4l2_ioctl_ops_enc; ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_state.c b/drivers/media/platform/qcom/vcodec/iris/iris_state.c index 952ba2a..e853aec 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_state.c +++ b/drivers/media/platform/qcom/vcodec/iris/iris_state.c @@ -198,8 +198,9 @@ bool allow_s_ctrl(struct iris_inst *inst, u32 cap_id) { return ((inst->state == IRIS_INST_OPEN) || ((inst->cap[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED) && - (inst->state == IRIS_INST_INPUT_STREAMING || - inst->state == IRIS_INST_STREAMING))); + ((inst->state == IRIS_INST_INPUT_STREAMING && inst->domain == DECODER) || + (inst->state == IRIS_INST_OUTPUT_STREAMING && inst->domain == ENCODER) || + (inst->state == IRIS_INST_STREAMING)))); } int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane) diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_vdec.c b/drivers/media/platform/qcom/vcodec/iris/iris_vdec.c index 371615e..300d0e9 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/vcodec/iris/iris_vdec.c @@ -22,39 +22,6 @@ struct vdec_prop_type_handle { int (*handle)(struct iris_inst *inst); }; -static int vdec_codec_change(struct iris_inst *inst, u32 v4l2_codec) -{ - bool session_init = false; - int ret; - - if (!inst->codec) - session_init = true; - - if (inst->codec && inst->fmt_src->fmt.pix_mp.pixelformat == v4l2_codec) - return 0; - - inst->codec = v4l2_codec_to_driver(inst, v4l2_codec); - if (!inst->codec) - return -EINVAL; - - inst->fmt_src->fmt.pix_mp.pixelformat = v4l2_codec; - ret = get_inst_capability(inst); - if (ret) - return ret; - - ret = ctrls_init(inst, session_init); - if (ret) - return ret; - - ret = update_buffer_count(inst, INPUT_MPLANE); - if (ret) - return ret; - - ret = update_buffer_count(inst, OUTPUT_MPLANE); - - return ret; -} - int vdec_inst_init(struct iris_inst *inst) { struct v4l2_format *f; @@ -93,7 +60,7 @@ int vdec_inst_init(struct iris_inst *inst) inst->buffers.output.size = f->fmt.pix_mp.plane_fmt[0].sizeimage; inst->fw_min_count = 0; - ret = vdec_codec_change(inst, inst->fmt_src->fmt.pix_mp.pixelformat); + ret = codec_change(inst, inst->fmt_src->fmt.pix_mp.pixelformat); return ret; } @@ -233,7 +200,7 @@ int vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) if (f->type == INPUT_MPLANE) { if (inst->fmt_src->fmt.pix_mp.pixelformat != f->fmt.pix_mp.pixelformat) { - ret = vdec_codec_change(inst, f->fmt.pix_mp.pixelformat); + ret = codec_change(inst, f->fmt.pix_mp.pixelformat); if (ret) return ret; } @@ -1304,49 +1271,6 @@ int vdec_qbuf(struct iris_inst *inst, struct vb2_buffer *vb2) return ret; } -static int process_resume(struct iris_inst *inst) -{ - enum iris_inst_sub_state clear_sub_state = IRIS_INST_SUB_NONE; - int ret; - - if (inst->sub_state & IRIS_INST_SUB_DRC && - inst->sub_state & IRIS_INST_SUB_DRC_LAST) { - clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST; - - if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { - ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE); - if (ret) - return ret; - clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE; - } - if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) { - ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE); - if (ret) - return ret; - clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE; - } - } else if (inst->sub_state & IRIS_INST_SUB_DRAIN && - inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) { - clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST; - if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { - ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_DRAIN); - if (ret) - return ret; - clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE; - } - if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) { - ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_DRAIN); - if (ret) - return ret; - clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE; - } - } - - ret = iris_inst_change_sub_state(inst, clear_sub_state, 0); - - return ret; -} - int vdec_start_cmd(struct iris_inst *inst) { int ret; diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_venc.c b/drivers/media/platform/qcom/vcodec/iris/iris_venc.c new file mode 100644 index 0000000..802db40 --- /dev/null +++ b/drivers/media/platform/qcom/vcodec/iris/iris_venc.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "hfi_defines.h" +#include "iris_buffer.h" +#include "iris_common.h" +#include "iris_ctrls.h" +#include "iris_helpers.h" +#include "iris_hfi.h" +#include "iris_hfi_packet.h" +#include "iris_power.h" +#include "iris_venc.h" + +int venc_inst_init(struct iris_inst *inst) +{ + struct v4l2_format *f; + int ret; + + inst->fmt_src = kzalloc(sizeof(*inst->fmt_src), GFP_KERNEL); + if (!inst->fmt_src) + return -ENOMEM; + + inst->fmt_dst = kzalloc(sizeof(*inst->fmt_dst), GFP_KERNEL); + if (!inst->fmt_dst) + return -ENOMEM; + + inst->vb2q_src = kzalloc(sizeof(*inst->vb2q_src), GFP_KERNEL); + if (!inst->vb2q_src) + return -ENOMEM; + + inst->vb2q_dst = kzalloc(sizeof(*inst->vb2q_dst), GFP_KERNEL); + if (!inst->vb2q_dst) + return -ENOMEM; + + f = inst->fmt_dst; + f->type = OUTPUT_MPLANE; + f->fmt.pix_mp.width = DEFAULT_WIDTH; + f->fmt.pix_mp.height = DEFAULT_HEIGHT; + f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; + f->fmt.pix_mp.num_planes = 1; + f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; + f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + inst->buffers.output.min_count = iris_get_buf_min_count(inst, BUF_OUTPUT); + inst->buffers.output.actual_count = inst->buffers.output.min_count; + inst->buffers.output.size = f->fmt.pix_mp.plane_fmt[0].sizeimage; + + inst->crop.left = 0; + inst->crop.top = 0; + inst->crop.width = f->fmt.pix_mp.width; + inst->crop.height = f->fmt.pix_mp.height; + + inst->compose.left = 0; + inst->compose.top = 0; + inst->compose.width = f->fmt.pix_mp.width; + inst->compose.height = f->fmt.pix_mp.height; + + f = inst->fmt_src; + f->type = INPUT_MPLANE; + f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_QC08C; + f->fmt.pix_mp.width = DEFAULT_WIDTH; + f->fmt.pix_mp.height = DEFAULT_HEIGHT; + f->fmt.pix_mp.num_planes = 1; + f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128); + f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT); + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + inst->buffers.input.min_count = iris_get_buf_min_count(inst, BUF_INPUT); + inst->buffers.input.actual_count = inst->buffers.input.min_count; + inst->buffers.input.size = f->fmt.pix_mp.plane_fmt[0].sizeimage; + + inst->hfi_rc_type = HFI_RC_VBR_CFR; + inst->hfi_layer_type = HFI_HIER_P_SLIDING_WINDOW; + + ret = codec_change(inst, inst->fmt_dst->fmt.pix_mp.pixelformat); + + return ret; +} + +void venc_inst_deinit(struct iris_inst *inst) +{ + kfree(inst->fmt_dst); + kfree(inst->fmt_src); +} + +int venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) +{ + struct iris_core *core; + u32 array[32] = {0}; + u32 i = 0; + + core = inst->core; + + if (f->type == OUTPUT_MPLANE) { + u32 codecs = core->cap[ENC_CODECS].value; + u32 idx = 0; + + for (i = 0; i <= 31; i++) { + if (codecs & BIT(i)) { + if (idx >= ARRAY_SIZE(array)) + break; + array[idx] = codecs & BIT(i); + idx++; + } + } + if (!array[f->index]) + return -EINVAL; + f->pixelformat = v4l2_codec_from_driver(inst, array[f->index]); + if (!f->pixelformat) + return -EINVAL; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strscpy(f->description, "codec", sizeof(f->description)); + } else if (f->type == INPUT_MPLANE) { + u32 formats = inst->cap[PIX_FMTS].step_or_mask; + u32 idx = 0; + + for (i = 0; i <= 31; i++) { + if (formats & BIT(i)) { + if (idx >= ARRAY_SIZE(array)) + break; + array[idx] = formats & BIT(i); + idx++; + } + } + if (!array[f->index]) + return -EINVAL; + f->pixelformat = v4l2_colorformat_from_driver(inst, array[f->index]); + if (!f->pixelformat) + return -EINVAL; + strscpy(f->description, "colorformat", sizeof(f->description)); + } + + memset(f->reserved, 0, sizeof(f->reserved)); + + return 0; +} + +int venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + u32 pix_fmt; + + memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); + + if (f->type == INPUT_MPLANE) { + pix_fmt = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat); + if (!pix_fmt) { + f->fmt.pix_mp.pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat; + f->fmt.pix_mp.width = inst->fmt_src->fmt.pix_mp.width; + f->fmt.pix_mp.height = inst->fmt_src->fmt.pix_mp.height; + } + } else if (f->type == OUTPUT_MPLANE) { + pix_fmt = v4l2_codec_to_driver(inst, f->fmt.pix_mp.pixelformat); + if (!pix_fmt) { + f->fmt.pix_mp.width = inst->fmt_dst->fmt.pix_mp.width; + f->fmt.pix_mp.height = inst->fmt_dst->fmt.pix_mp.height; + f->fmt.pix_mp.pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; + } + } else { + return -EINVAL; + } + + if (pixmp->field == V4L2_FIELD_ANY) + pixmp->field = V4L2_FIELD_NONE; + pixmp->num_planes = 1; + + return 0; +} + +static int venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f) +{ + struct v4l2_format *fmt; + enum codec_type codec; + u32 codec_align; + u32 width, height; + int ret = 0; + + venc_try_fmt(inst, f); + + fmt = inst->fmt_dst; + if (fmt->fmt.pix_mp.pixelformat != f->fmt.pix_mp.pixelformat) { + ret = codec_change(inst, f->fmt.pix_mp.pixelformat); + if (ret) + return ret; + } + fmt->type = OUTPUT_MPLANE; + + codec = v4l2_codec_to_driver(inst, f->fmt.pix_mp.pixelformat); + + codec_align = (codec == HEVC) ? 32 : 16; + width = inst->compose.width; + height = inst->compose.height; + if (inst->cap[ROTATION].value == 90 || inst->cap[ROTATION].value == 270) { + width = inst->compose.height; + height = inst->compose.width; + } + fmt->fmt.pix_mp.width = ALIGN(width, codec_align); + fmt->fmt.pix_mp.height = ALIGN(height, codec_align); + fmt->fmt.pix_mp.num_planes = 1; + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0; + fmt->fmt.pix_mp.plane_fmt[0].sizeimage = + iris_get_buffer_size(inst, BUF_OUTPUT); + if (f->fmt.pix_mp.colorspace != V4L2_COLORSPACE_DEFAULT && + f->fmt.pix_mp.colorspace != V4L2_COLORSPACE_REC709) + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; + fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; + fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; + fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; + inst->buffers.output.min_count = iris_get_buf_min_count(inst, BUF_OUTPUT); + if (inst->buffers.output.actual_count < + inst->buffers.output.min_count) { + inst->buffers.output.actual_count = + inst->buffers.output.min_count; + } + inst->buffers.output.size = + fmt->fmt.pix_mp.plane_fmt[0].sizeimage; + + memcpy(f, fmt, sizeof(struct v4l2_format)); + + return ret; +} + +static int venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f) +{ + u32 pix_fmt, width, height, size, bytesperline; + struct v4l2_format *fmt, *output_fmt; + int ret = 0; + + venc_try_fmt(inst, f); + + pix_fmt = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat); + inst->cap[PIX_FMTS].value = pix_fmt; + + width = f->fmt.pix_mp.width; + height = f->fmt.pix_mp.height; + + bytesperline = pix_fmt == FMT_TP10C ? + ALIGN(ALIGN(f->fmt.pix_mp.width, 192) * 4 / 3, 256) : + ALIGN(f->fmt.pix_mp.width, 128); + + fmt = inst->fmt_src; + fmt->type = INPUT_MPLANE; + fmt->fmt.pix_mp.width = width; + fmt->fmt.pix_mp.height = height; + fmt->fmt.pix_mp.num_planes = 1; + fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = bytesperline; + size = iris_get_buffer_size(inst, BUF_INPUT); + fmt->fmt.pix_mp.plane_fmt[0].sizeimage = size; + fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; + fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; + fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; + + output_fmt = inst->fmt_dst; + output_fmt->fmt.pix_mp.colorspace = fmt->fmt.pix_mp.colorspace; + output_fmt->fmt.pix_mp.xfer_func = fmt->fmt.pix_mp.xfer_func; + output_fmt->fmt.pix_mp.ycbcr_enc = fmt->fmt.pix_mp.ycbcr_enc; + output_fmt->fmt.pix_mp.quantization = fmt->fmt.pix_mp.quantization; + + inst->buffers.input.min_count = iris_get_buf_min_count(inst, BUF_INPUT); + if (inst->buffers.input.actual_count < + inst->buffers.input.min_count) { + inst->buffers.input.actual_count = + inst->buffers.input.min_count; + } + inst->buffers.input.size = size; + + if (f->fmt.pix_mp.width != inst->crop.width || + f->fmt.pix_mp.height != inst->crop.height) { + inst->crop.top = 0; + inst->crop.left = 0; + inst->crop.width = f->fmt.pix_mp.width; + inst->crop.height = f->fmt.pix_mp.height; + + inst->compose.top = 0; + inst->compose.left = 0; + inst->compose.width = f->fmt.pix_mp.width; + inst->compose.height = f->fmt.pix_mp.height; + + ret = venc_s_fmt_output(inst, output_fmt); + if (ret) + return ret; + } + + memcpy(f, fmt, sizeof(struct v4l2_format)); + + return ret; +} + +int venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f) +{ + int ret; + + if (f->type == INPUT_MPLANE) + ret = venc_s_fmt_input(inst, f); + else if (f->type == OUTPUT_MPLANE) + ret = venc_s_fmt_output(inst, f); + else + ret = -EINVAL; + + return ret; +} + +int venc_s_selection(struct iris_inst *inst, struct v4l2_selection *s) +{ + struct v4l2_format *output_fmt; + int ret; + + if (s->type != INPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (s->r.left || s->r.top) { + s->r.left = 0; + s->r.top = 0; + } + if (s->r.width > inst->fmt_src->fmt.pix_mp.width) + s->r.width = inst->fmt_src->fmt.pix_mp.width; + + if (s->r.height > inst->fmt_src->fmt.pix_mp.height) + s->r.height = inst->fmt_src->fmt.pix_mp.height; + + inst->crop.left = s->r.left; + inst->crop.top = s->r.top; + inst->crop.width = s->r.width; + inst->crop.height = s->r.height; + inst->compose.left = inst->crop.left; + inst->compose.top = inst->crop.top; + inst->compose.width = inst->crop.width; + inst->compose.height = inst->crop.height; + output_fmt = inst->fmt_dst; + ret = venc_s_fmt_output(inst, output_fmt); + if (ret) + return ret; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->r.left < inst->crop.left) + s->r.left = inst->crop.left; + + if (s->r.top < inst->crop.top) + s->r.top = inst->crop.top; + + if (s->r.width > inst->crop.width) + s->r.width = inst->crop.width; + + if (s->r.height > inst->crop.height) + s->r.height = inst->crop.height; + inst->compose.left = s->r.left; + inst->compose.top = s->r.top; + inst->compose.width = s->r.width; + inst->compose.height = s->r.height; + + output_fmt = inst->fmt_dst; + ret = venc_s_fmt_output(inst, output_fmt); + if (ret) + return ret; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm) +{ + struct v4l2_fract *timeperframe = NULL; + u32 q16_rate, max_rate, default_rate; + u64 us_per_frame = 0, input_rate = 0; + bool is_frame_rate = false; + int ret = 0; + + if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + timeperframe = &s_parm->parm.output.timeperframe; + max_rate = inst->cap[OPERATING_RATE].max >> 16; + default_rate = inst->cap[OPERATING_RATE].value >> 16; + s_parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + } else { + timeperframe = &s_parm->parm.capture.timeperframe; + is_frame_rate = true; + max_rate = inst->cap[FRAME_RATE].max >> 16; + default_rate = inst->cap[FRAME_RATE].value >> 16; + s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + } + + if (!timeperframe->denominator || !timeperframe->numerator) { + if (!timeperframe->numerator) + timeperframe->numerator = 1; + if (!timeperframe->denominator) + timeperframe->denominator = default_rate; + } + + us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; + us_per_frame = div64_u64(us_per_frame, timeperframe->denominator); + + if (!us_per_frame) { + ret = -EINVAL; + goto exit; + } + + input_rate = (u64)USEC_PER_SEC; + input_rate = div64_u64(input_rate, us_per_frame); + + q16_rate = (u32)input_rate << 16; + if (is_frame_rate) + inst->cap[FRAME_RATE].value = q16_rate; + else + inst->cap[OPERATING_RATE].value = q16_rate; + + if ((s_parm->type == INPUT_MPLANE && inst->vb2q_src->streaming) || + (s_parm->type == OUTPUT_MPLANE && inst->vb2q_dst->streaming)) { + ret = check_core_mbps_mbpf(inst); + if (ret) + goto reset_rate; + ret = input_rate > max_rate; + if (ret) { + ret = -ENOMEM; + goto reset_rate; + } + } + + if (is_frame_rate) + inst->cap[FRAME_RATE].flags |= CAP_FLAG_CLIENT_SET; + else + inst->cap[OPERATING_RATE].flags |= CAP_FLAG_CLIENT_SET; + + if (inst->vb2q_dst->streaming) { + ret = iris_hfi_set_property(inst, + HFI_PROP_FRAME_RATE, + HFI_HOST_FLAGS_NONE, + HFI_PORT_BITSTREAM, + HFI_PAYLOAD_Q16, + &q16_rate, + sizeof(u32)); + if (ret) + goto exit; + } + + return ret; + +reset_rate: + if (ret) { + if (is_frame_rate) + inst->cap[FRAME_RATE].value = default_rate << 16; + else + inst->cap[OPERATING_RATE].value = default_rate << 16; + } +exit: + return ret; +} + +int venc_g_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm) +{ + struct v4l2_fract *timeperframe = NULL; + + if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + timeperframe = &s_parm->parm.output.timeperframe; + timeperframe->numerator = 1; + timeperframe->denominator = + inst->cap[OPERATING_RATE].value >> 16; + s_parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + } else { + timeperframe = &s_parm->parm.capture.timeperframe; + timeperframe->numerator = 1; + timeperframe->denominator = + inst->cap[FRAME_RATE].value >> 16; + s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + } + + return 0; +} + +int venc_subscribe_event(struct iris_inst *inst, + const struct v4l2_event_subscription *sub) +{ + int ret; + + switch (sub->type) { + case V4L2_EVENT_EOS: + ret = v4l2_event_subscribe(&inst->fh, sub, MAX_EVENTS, NULL); + break; + case V4L2_EVENT_CTRL: + ret = v4l2_ctrl_subscribe_event(&inst->fh, sub); + break; + default: + return -EINVAL; + } + + return ret; +} + +int venc_start_cmd(struct iris_inst *inst) +{ + vb2_clear_last_buffer_dequeued(inst->vb2q_dst); + + return process_resume(inst); +} + +int venc_stop_cmd(struct iris_inst *inst) +{ + int ret; + + ret = iris_hfi_drain(inst, INPUT_MPLANE); + if (ret) + return ret; + + ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN); + + iris_scale_power(inst); + + return ret; +} diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_venc.h b/drivers/media/platform/qcom/vcodec/iris/iris_venc.h new file mode 100644 index 0000000..24da63f --- /dev/null +++ b/drivers/media/platform/qcom/vcodec/iris/iris_venc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _IRIS_VENC_H_ +#define _IRIS_VENC_H_ + +#include "iris_instance.h" + +int venc_inst_init(struct iris_inst *inst); +void venc_inst_deinit(struct iris_inst *inst); +int venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f); +int venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f); +int venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f); +int venc_s_selection(struct iris_inst *inst, struct v4l2_selection *s); +int venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm); +int venc_g_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm); +int venc_subscribe_event(struct iris_inst *inst, + const struct v4l2_event_subscription *sub); +int venc_start_cmd(struct iris_inst *inst); +int venc_stop_cmd(struct iris_inst *inst); + +#endif diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c b/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c index 437d6b4..aa19978 100644 --- a/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c @@ -15,6 +15,7 @@ #include "iris_instance.h" #include "iris_power.h" #include "iris_vdec.h" +#include "iris_venc.h" #include "iris_vidc.h" #include "iris_vb2.h" @@ -30,7 +31,11 @@ static int vidc_v4l2_fh_init(struct iris_inst *inst) if (inst->fh.vdev) return -EINVAL; - v4l2_fh_init(&inst->fh, core->vdev_dec); + if (inst->domain == ENCODER) + v4l2_fh_init(&inst->fh, core->vdev_enc); + else if (inst->domain == DECODER) + v4l2_fh_init(&inst->fh, core->vdev_dec); + inst->fh.ctrl_handler = &inst->ctrl_handler; v4l2_fh_add(&inst->fh); @@ -160,9 +165,20 @@ int vidc_open(struct file *filp) { struct iris_core *core = video_drvdata(filp); struct iris_inst *inst = NULL; + struct video_device *vdev; + u32 session_type = 0; int i = 0; int ret; + vdev = video_devdata(filp); + if (strcmp(vdev->name, "qcom-iris-decoder") == 0) + session_type = DECODER; + else if (strcmp(vdev->name, "qcom-iris-encoder") == 0) + session_type = ENCODER; + + if (session_type != DECODER && session_type != ENCODER) + return -EINVAL; + ret = iris_pm_get(core); if (ret) return ret; @@ -182,6 +198,7 @@ int vidc_open(struct file *filp) } inst->core = core; + inst->domain = session_type; inst->session_id = hash32_ptr(inst); inst->ipsc_properties_set = false; inst->opsc_properties_set = false; @@ -213,7 +230,12 @@ int vidc_open(struct file *filp) if (ret) goto fail_remove_session; - vdec_inst_init(inst); + if (inst->domain == DECODER) + ret = vdec_inst_init(inst); + else if (inst->domain == ENCODER) + ret = venc_inst_init(inst); + if (ret) + goto fail_fh_deinit; ret = vidc_vb2_queue_init(inst); if (ret) @@ -238,7 +260,11 @@ int vidc_open(struct file *filp) iris_core_deinit(core); vidc_vb2_queue_deinit(inst); fail_inst_deinit: - vdec_inst_deinit(inst); + if (inst->domain == DECODER) + vdec_inst_deinit(inst); + else if (inst->domain == ENCODER) + venc_inst_deinit(inst); +fail_fh_deinit: vidc_v4l2_fh_deinit(inst); fail_remove_session: vidc_remove_session(inst); @@ -264,7 +290,11 @@ int vidc_close(struct file *filp) core = inst->core; v4l2_ctrl_handler_free(&inst->ctrl_handler); - vdec_inst_deinit(inst); + if (inst->domain == DECODER) + vdec_inst_deinit(inst); + else if (inst->domain == ENCODER) + venc_inst_deinit(inst); + mutex_lock(&inst->lock); iris_pm_get(core); close_session(inst); @@ -342,7 +372,7 @@ static __poll_t vidc_poll(struct file *filp, struct poll_table_struct *pt) static int vidc_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f) { struct iris_inst *inst; - int ret; + int ret = 0; inst = get_vidc_inst(filp, fh); if (!inst) @@ -354,7 +384,10 @@ static int vidc_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f) goto unlock; } - ret = vdec_enum_fmt(inst, f); + if (inst->domain == DECODER) + ret = vdec_enum_fmt(inst, f); + else if (inst->domain == ENCODER) + ret = venc_enum_fmt(inst, f); unlock: mutex_unlock(&inst->lock); @@ -365,7 +398,7 @@ static int vidc_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f) static int vidc_try_fmt(struct file *filp, void *fh, struct v4l2_format *f) { struct iris_inst *inst; - int ret; + int ret = 0; inst = get_vidc_inst(filp, fh); if (!inst) @@ -382,7 +415,10 @@ static int vidc_try_fmt(struct file *filp, void *fh, struct v4l2_format *f) goto unlock; } - ret = vdec_try_fmt(inst, f); + if (inst->domain == DECODER) + ret = vdec_try_fmt(inst, f); + else if (inst->domain == ENCODER) + ret = venc_try_fmt(inst, f); unlock: mutex_unlock(&inst->lock); @@ -393,7 +429,7 @@ static int vidc_try_fmt(struct file *filp, void *fh, struct v4l2_format *f) static int vidc_s_fmt(struct file *filp, void *fh, struct v4l2_format *f) { struct iris_inst *inst; - int ret; + int ret = 0; inst = get_vidc_inst(filp, fh); if (!inst) @@ -410,7 +446,10 @@ static int vidc_s_fmt(struct file *filp, void *fh, struct v4l2_format *f) goto unlock; } - ret = vdec_s_fmt(inst, f); + if (inst->domain == DECODER) + ret = vdec_s_fmt(inst, f); + else if (inst->domain == ENCODER) + ret = venc_s_fmt(inst, f); unlock: mutex_unlock(&inst->lock); @@ -488,6 +527,70 @@ static int vidc_enum_framesizes(struct file *filp, void *fh, return ret; } +static int vidc_enum_frameintervals(struct file *filp, void *fh, + struct v4l2_frmivalenum *fival) + +{ + enum colorformat_type colorfmt; + struct iris_inst *inst; + struct iris_core *core; + u32 fps, mbpf; + int ret = 0; + + inst = get_vidc_inst(filp, fh); + if (!inst || !fival) + return -EINVAL; + + mutex_lock(&inst->lock); + if (IS_SESSION_ERROR(inst)) { + ret = -EBUSY; + goto unlock; + } + + if (inst->domain == DECODER) { + ret = -ENOTTY; + goto unlock; + } + + core = inst->core; + + if (fival->index) { + ret = -EINVAL; + goto unlock; + } + + colorfmt = v4l2_colorformat_to_driver(inst, fival->pixel_format); + if (colorfmt == FMT_NONE) { + ret = -EINVAL; + goto unlock; + } + + if (fival->width > inst->cap[FRAME_WIDTH].max || + fival->width < inst->cap[FRAME_WIDTH].min || + fival->height > inst->cap[FRAME_HEIGHT].max || + fival->height < inst->cap[FRAME_HEIGHT].min) { + ret = -EINVAL; + goto unlock; + } + + mbpf = NUM_MBS_PER_FRAME(fival->height, fival->width); + fps = core->cap[MAX_MBPS].value / mbpf; + + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + fival->stepwise.min.numerator = 1; + fival->stepwise.min.denominator = + min_t(u32, fps, inst->cap[FRAME_RATE].max); + fival->stepwise.max.numerator = 1; + fival->stepwise.max.denominator = 1; + fival->stepwise.step.numerator = 1; + fival->stepwise.step.denominator = inst->cap[FRAME_RATE].max; + +unlock: + mutex_unlock(&inst->lock); + + return ret; +} + static int vidc_reqbufs(struct file *filp, void *fh, struct v4l2_requestbuffers *b) { struct vb2_queue *vb2q = NULL; @@ -751,7 +854,11 @@ static int vidc_querycap(struct file *filp, void *fh, struct v4l2_capability *ca strscpy(cap->driver, VIDC_DRV_NAME, sizeof(cap->driver)); strscpy(cap->bus_info, VIDC_BUS_NAME, sizeof(cap->bus_info)); memset(cap->reserved, 0, sizeof(cap->reserved)); - strscpy(cap->card, "iris_decoder", sizeof(cap->card)); + + if (inst->domain == DECODER) + strscpy(cap->card, "iris_decoder", sizeof(cap->card)); + else if (inst->domain == ENCODER) + strscpy(cap->card, "iris_encoder", sizeof(cap->card)); unlock: mutex_unlock(&inst->lock); @@ -839,7 +946,7 @@ static int vidc_querymenu(struct file *filp, void *fh, struct v4l2_querymenu *qm static int vidc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { struct iris_inst *inst; - int ret; + int ret = 0; inst = container_of(fh, struct iris_inst, fh); @@ -849,7 +956,10 @@ static int vidc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subs goto unlock; } - ret = vdec_subscribe_event(inst, sub); + if (inst->domain == DECODER) + ret = vdec_subscribe_event(inst, sub); + else if (inst->domain == ENCODER) + ret = venc_subscribe_event(inst, sub); unlock: mutex_unlock(&inst->lock); @@ -893,28 +1003,152 @@ static int vidc_g_selection(struct file *filp, void *fh, struct v4l2_selection * goto unlock; } - if (s->type != OUTPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (s->type != OUTPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + inst->domain == DECODER) { ret = -EINVAL; goto unlock; } - switch (s->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE: - s->r.left = inst->crop.left; - s->r.top = inst->crop.top; - s->r.width = inst->crop.width; - s->r.height = inst->crop.height; - break; - default: + if (s->type != INPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + inst->domain == ENCODER) { ret = -EINVAL; + goto unlock; } + if (inst->domain == DECODER) { + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE: + s->r.left = inst->crop.left; + s->r.top = inst->crop.top; + s->r.width = inst->crop.width; + s->r.height = inst->crop.height; + break; + default: + ret = -EINVAL; + break; + } + } else if (inst->domain == ENCODER) { + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + s->r.left = inst->crop.left; + s->r.top = inst->crop.top; + s->r.width = inst->crop.width; + s->r.height = inst->crop.height; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE: + s->r.left = inst->compose.left; + s->r.top = inst->compose.top; + s->r.width = inst->compose.width; + s->r.height = inst->compose.height; + break; + default: + ret = -EINVAL; + break; + } + } + +unlock: + mutex_unlock(&inst->lock); + + return ret; +} + +static int vidc_s_selection(struct file *filp, void *fh, struct v4l2_selection *s) +{ + struct iris_inst *inst; + int ret = 0; + + inst = get_vidc_inst(filp, fh); + if (!inst || !s) + return -EINVAL; + + mutex_lock(&inst->lock); + if (IS_SESSION_ERROR(inst)) { + ret = -EBUSY; + goto unlock; + } + if (inst->domain == DECODER) { + ret = -EINVAL; + goto unlock; + } else if (inst->domain == ENCODER) { + ret = venc_s_selection(inst, s); + } + +unlock: + mutex_unlock(&inst->lock); + + return ret; +} + +static int vidc_s_parm(struct file *filp, void *fh, struct v4l2_streamparm *a) +{ + struct iris_inst *inst; + int ret = 0; + + inst = get_vidc_inst(filp, fh); + if (!inst || !a) + return -EINVAL; + + mutex_lock(&inst->lock); + if (IS_SESSION_ERROR(inst)) { + ret = -EBUSY; + goto unlock; + } + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = -EINVAL; + goto unlock; + } + + if (inst->domain == ENCODER) + ret = venc_s_param(inst, a); + else + ret = -EINVAL; + +unlock: + mutex_unlock(&inst->lock); + + return ret; +} + +static int vidc_g_parm(struct file *filp, void *fh, struct v4l2_streamparm *a) +{ + struct iris_inst *inst; + int ret = 0; + + inst = get_vidc_inst(filp, fh); + if (!inst || !a) + return -EINVAL; + + mutex_lock(&inst->lock); + if (IS_SESSION_ERROR(inst)) { + ret = -EBUSY; + goto unlock; + } + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = -EINVAL; + goto unlock; + } + + if (inst->domain == ENCODER) + ret = venc_g_param(inst, a); + else + ret = -EINVAL; + unlock: mutex_unlock(&inst->lock); @@ -955,6 +1189,39 @@ static int vidc_try_dec_cmd(struct file *filp, void *fh, return ret; } +static int vidc_try_enc_cmd(struct file *filp, void *fh, + struct v4l2_encoder_cmd *enc) +{ + struct iris_inst *inst; + int ret = 0; + + inst = get_vidc_inst(filp, fh); + if (!inst || !enc) + return -EINVAL; + + mutex_lock(&inst->lock); + if (IS_SESSION_ERROR(inst)) { + ret = -EBUSY; + goto unlock; + } + + if (inst->domain != ENCODER) { + ret = -ENOTTY; + goto unlock; + } + + if (enc->cmd != V4L2_ENC_CMD_STOP && enc->cmd != V4L2_ENC_CMD_START) { + ret = -EINVAL; + goto unlock; + } + enc->flags = 0; + +unlock: + mutex_unlock(&inst->lock); + + return ret; +} + static int vidc_dec_cmd(struct file *filp, void *fh, struct v4l2_decoder_cmd *dec) { @@ -1002,6 +1269,60 @@ static int vidc_dec_cmd(struct file *filp, void *fh, return ret; } +static int vidc_enc_cmd(struct file *filp, void *fh, + struct v4l2_encoder_cmd *enc) +{ + struct iris_inst *inst; + int ret = 0; + + inst = get_vidc_inst(filp, fh); + if (!inst || !enc) + return -EINVAL; + + mutex_lock(&inst->lock); + if (IS_SESSION_ERROR(inst)) { + ret = -EBUSY; + goto unlock; + } + + if (inst->domain != ENCODER) { + ret = -ENOTTY; + goto unlock; + } + + if (enc->cmd != V4L2_ENC_CMD_START && + enc->cmd != V4L2_ENC_CMD_STOP) { + ret = -EINVAL; + goto unlock; + } + + if (enc->cmd == V4L2_ENC_CMD_STOP && inst->state == IRIS_INST_OPEN) { + ret = 0; + goto unlock; + } + + if (!allow_cmd(inst, enc->cmd)) { + ret = -EBUSY; + goto unlock; + } + + ret = iris_pm_get(inst->core); + if (ret) + goto unlock; + + if (enc->cmd == V4L2_ENC_CMD_START) + ret = venc_start_cmd(inst); + else if (enc->cmd == V4L2_ENC_CMD_STOP) + ret = venc_stop_cmd(inst); + + iris_pm_put(inst->core, true); + +unlock: + mutex_unlock(&inst->lock); + + return ret; +} + static struct v4l2_file_operations v4l2_file_ops = { .owner = THIS_MODULE, .open = vidc_open, @@ -1027,7 +1348,7 @@ static struct vb2_mem_ops iris_vb2_mem_ops = { .unmap_dmabuf = iris_vb2_unmap_dmabuf, }; -static const struct v4l2_ioctl_ops v4l2_ioctl_ops = { +static const struct v4l2_ioctl_ops v4l2_ioctl_ops_dec = { .vidioc_enum_fmt_vid_cap = vidc_enum_fmt, .vidioc_enum_fmt_vid_out = vidc_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = vidc_try_fmt, @@ -1055,12 +1376,44 @@ static const struct v4l2_ioctl_ops v4l2_ioctl_ops = { .vidioc_decoder_cmd = vidc_dec_cmd, }; +static const struct v4l2_ioctl_ops v4l2_ioctl_ops_enc = { + .vidioc_enum_fmt_vid_cap = vidc_enum_fmt, + .vidioc_enum_fmt_vid_out = vidc_enum_fmt, + .vidioc_try_fmt_vid_cap_mplane = vidc_try_fmt, + .vidioc_try_fmt_vid_out_mplane = vidc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vidc_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vidc_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vidc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidc_g_fmt, + .vidioc_enum_framesizes = vidc_enum_framesizes, + .vidioc_enum_frameintervals = vidc_enum_frameintervals, + .vidioc_reqbufs = vidc_reqbufs, + .vidioc_querybuf = vidc_querybuf, + .vidioc_create_bufs = vidc_create_bufs, + .vidioc_prepare_buf = vidc_prepare_buf, + .vidioc_qbuf = vidc_qbuf, + .vidioc_dqbuf = vidc_dqbuf, + .vidioc_streamon = vidc_streamon, + .vidioc_streamoff = vidc_streamoff, + .vidioc_querycap = vidc_querycap, + .vidioc_queryctrl = vidc_queryctrl, + .vidioc_querymenu = vidc_querymenu, + .vidioc_subscribe_event = vidc_subscribe_event, + .vidioc_unsubscribe_event = vidc_unsubscribe_event, + .vidioc_g_selection = vidc_g_selection, + .vidioc_s_selection = vidc_s_selection, + .vidioc_s_parm = vidc_s_parm, + .vidioc_g_parm = vidc_g_parm, + .vidioc_try_encoder_cmd = vidc_try_enc_cmd, + .vidioc_encoder_cmd = vidc_enc_cmd, +}; + int init_ops(struct iris_core *core) { core->v4l2_file_ops = &v4l2_file_ops; core->vb2_ops = &iris_vb2_ops; core->vb2_mem_ops = &iris_vb2_mem_ops; - core->v4l2_ioctl_ops = &v4l2_ioctl_ops; - + core->v4l2_ioctl_ops_dec = &v4l2_ioctl_ops_dec; + core->v4l2_ioctl_ops_enc = &v4l2_ioctl_ops_enc; return 0; } diff --git a/drivers/media/platform/qcom/vcodec/iris/platform_common.h b/drivers/media/platform/qcom/vcodec/iris/platform_common.h index 443894c..effecbb 100644 --- a/drivers/media/platform/qcom/vcodec/iris/platform_common.h +++ b/drivers/media/platform/qcom/vcodec/iris/platform_common.h @@ -115,6 +115,7 @@ struct iris_inst_power { enum plat_core_cap_type { CORE_CAP_NONE = 0, DEC_CODECS, + ENC_CODECS, MAX_SESSION_COUNT, MAX_MBPF, MAX_MBPS, @@ -249,6 +250,7 @@ struct plat_inst_cap { struct plat_inst_caps { enum codec_type codec; + enum domain_type domain; struct plat_inst_cap cap[INST_CAP_MAX + 1]; }; diff --git a/drivers/media/platform/qcom/vcodec/iris/platform_sm8550.c b/drivers/media/platform/qcom/vcodec/iris/platform_sm8550.c index 6d5192a..ef0aad7 100644 --- a/drivers/media/platform/qcom/vcodec/iris/platform_sm8550.c +++ b/drivers/media/platform/qcom/vcodec/iris/platform_sm8550.c @@ -63,6 +63,7 @@ static struct color_format_info color_format_data_sm8550[] = { static struct plat_core_cap core_data_sm8550[] = { {DEC_CODECS, H264 | HEVC | VP9}, + {ENC_CODECS, H264 | HEVC}, {MAX_SESSION_COUNT, 16}, {MAX_MBPF, 278528}, /* ((8192x4352)/256) * 2 */ {MAX_MBPS, 7833600}, /* max_load 7680x4320@60fps */ -- 2.7.4