root/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#include <linux/bitfield.h>

#include "iris_hfi_gen2.h"
#include "iris_hfi_gen2_packet.h"

#define UNSPECIFIED_COLOR_FORMAT 5
#define NUM_SYS_INIT_PACKETS 8
#define NUM_COMV_AV1 18

#define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \
        NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32)))

#define SYS_IFPC_PKT_SIZE (sizeof(struct iris_hfi_header) + \
        sizeof(struct iris_hfi_packet) + sizeof(u32))

#define SYS_NO_PAYLOAD_PKT_SIZE (sizeof(struct iris_hfi_header) + \
        sizeof(struct iris_hfi_packet))

static int iris_hfi_gen2_sys_init(struct iris_core *core)
{
        struct iris_hfi_header *hdr;
        int ret;

        hdr = kzalloc(SYS_INIT_PKT_SIZE, GFP_KERNEL);
        if (!hdr)
                return -ENOMEM;

        iris_hfi_gen2_packet_sys_init(core, hdr);
        ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);

        kfree(hdr);

        return ret;
}

static int iris_hfi_gen2_sys_image_version(struct iris_core *core)
{
        struct iris_hfi_header *hdr;
        int ret;

        hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
        if (!hdr)
                return -ENOMEM;

        iris_hfi_gen2_packet_image_version(core, hdr);
        ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);

        kfree(hdr);

        return ret;
}

static int iris_hfi_gen2_sys_interframe_powercollapse(struct iris_core *core)
{
        struct iris_hfi_header *hdr;
        int ret;

        hdr = kzalloc(SYS_IFPC_PKT_SIZE, GFP_KERNEL);
        if (!hdr)
                return -ENOMEM;

        iris_hfi_gen2_packet_sys_interframe_powercollapse(core, hdr);
        ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);

        kfree(hdr);

        return ret;
}

static int iris_hfi_gen2_sys_pc_prep(struct iris_core *core)
{
        struct iris_hfi_header *hdr;
        int ret;

        hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
        if (!hdr)
                return -ENOMEM;

        iris_hfi_gen2_packet_sys_pc_prep(core, hdr);
        ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);

        kfree(hdr);

        return ret;
}

static u32 iris_hfi_gen2_get_port(struct iris_inst *inst, u32 plane)
{
        if (inst->domain == DECODER) {
                switch (plane) {
                case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
                        return HFI_PORT_BITSTREAM;
                case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
                        return HFI_PORT_RAW;
                default:
                        return HFI_PORT_NONE;
                }
        } else {
                switch (plane) {
                case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
                        return HFI_PORT_RAW;
                case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
                        return HFI_PORT_BITSTREAM;
                default:
                        return HFI_PORT_NONE;
                }
        }
}

static u32 iris_hfi_gen2_get_port_from_buf_type(struct iris_inst *inst,
                                                enum iris_buffer_type buffer_type)
{
        if (inst->domain == DECODER) {
                switch (buffer_type) {
                case BUF_INPUT:
                case BUF_BIN:
                case BUF_COMV:
                case BUF_NON_COMV:
                case BUF_LINE:
                case BUF_PARTIAL:
                        return HFI_PORT_BITSTREAM;
                case BUF_OUTPUT:
                case BUF_DPB:
                        return HFI_PORT_RAW;
                case BUF_PERSIST:
                default:
                        return HFI_PORT_NONE;
                }
        } else {
                switch (buffer_type) {
                case BUF_INPUT:
                case BUF_VPSS:
                        return HFI_PORT_RAW;
                case BUF_OUTPUT:
                case BUF_BIN:
                case BUF_COMV:
                case BUF_NON_COMV:
                case BUF_LINE:
                case BUF_SCRATCH_2:
                        return HFI_PORT_BITSTREAM;
                case BUF_ARP:
                default:
                        return HFI_PORT_NONE;
                }
        }
}

static int iris_hfi_gen2_session_set_property(struct iris_inst *inst, u32 packet_type, u32 flag,
                                              u32 plane, u32 payload_type, void *payload,
                                              u32 payload_size)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);

        iris_hfi_gen2_packet_session_property(inst,
                                              packet_type,
                                              flag,
                                              plane,
                                              payload_type,
                                              payload,
                                              payload_size);

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_set_raw_resolution(struct iris_inst *inst, u32 plane)
{
        u32 resolution = inst->enc_raw_width << 16 | inst->enc_raw_height;
        u32 port = iris_hfi_gen2_get_port(inst, plane);

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_RAW_RESOLUTION,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_32_PACKED,
                                                  &resolution,
                                                  sizeof(u32));
}

static inline u32 iris_hfi_get_aligned_resolution(struct iris_inst *inst, u32 width, u32 height)
{
        u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;

        return (ALIGN(width, codec_align) << 16 | ALIGN(height, codec_align));
}

static int iris_hfi_gen2_set_bitstream_resolution(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        enum hfi_packet_payload_info payload_type;
        u32 width, height;
        u32 resolution;

        if (inst->domain == DECODER) {
                width = inst->fmt_src->fmt.pix_mp.width;
                height = inst->fmt_src->fmt.pix_mp.height;
                resolution = iris_hfi_get_aligned_resolution(inst, width, height);
                inst_hfi_gen2->src_subcr_params.bitstream_resolution = resolution;
                payload_type = HFI_PAYLOAD_U32;
        } else {
                if (is_rotation_90_or_270(inst)) {
                        width = inst->enc_scale_height;
                        height = inst->enc_scale_width;
                } else {
                        width = inst->enc_scale_width;
                        height = inst->enc_scale_height;
                }
                resolution = iris_hfi_get_aligned_resolution(inst, width, height);
                inst_hfi_gen2->dst_subcr_params.bitstream_resolution = resolution;
                payload_type = HFI_PAYLOAD_32_PACKED;
        }

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_BITSTREAM_RESOLUTION,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  payload_type,
                                                  &resolution,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 bottom_offset, right_offset;
        u32 left_offset, top_offset;
        u32 payload[2], codec_align;

        if (inst->domain == DECODER) {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        bottom_offset = (inst->fmt_src->fmt.pix_mp.height - inst->crop.height);
                        right_offset = (inst->fmt_src->fmt.pix_mp.width - inst->crop.width);
                        left_offset = inst->crop.left;
                        top_offset = inst->crop.top;
                } else {
                        bottom_offset = (inst->fmt_dst->fmt.pix_mp.height - inst->compose.height);
                        right_offset = (inst->fmt_dst->fmt.pix_mp.width - inst->compose.width);
                        left_offset = inst->compose.left;
                        top_offset = inst->compose.top;
                }
        } else {
                codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        bottom_offset = (inst->enc_raw_height - inst->crop.height);
                        right_offset = (inst->enc_raw_width - inst->crop.width);
                        left_offset = inst->crop.left;
                        top_offset = inst->crop.top;
                } else {
                        if (is_rotation_90_or_270(inst)) {
                                bottom_offset = (ALIGN(inst->enc_scale_width, codec_align) -
                                                inst->enc_scale_width);
                                right_offset = (ALIGN(inst->enc_scale_height, codec_align) -
                                               inst->enc_scale_height);
                        } else {
                                bottom_offset = (ALIGN(inst->enc_scale_height, codec_align) -
                                                inst->enc_scale_height);
                                right_offset = (ALIGN(inst->enc_scale_width, codec_align) -
                                               inst->enc_scale_width);
                        }
                        left_offset = 0;
                        top_offset = 0;
                }
        }

        payload[0] = FIELD_PREP(GENMASK(31, 16), left_offset) | top_offset;
        payload[1] = FIELD_PREP(GENMASK(31, 16), right_offset) | bottom_offset;
        inst_hfi_gen2->src_subcr_params.crop_offsets[0] = payload[0];
        inst_hfi_gen2->src_subcr_params.crop_offsets[1] = payload[1];

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_CROP_OFFSETS,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_64_PACKED,
                                                  &payload,
                                                  sizeof(u64));
}

static int iris_hfi_gen2_set_bit_depth(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 bitdepth = BIT_DEPTH_8;

        inst_hfi_gen2->src_subcr_params.bit_depth = bitdepth;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32,
                                                  &bitdepth,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_coded_frames(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 coded_frames = 0;

        if (inst->fw_caps[CODED_FRAMES].value == CODED_FRAMES_PROGRESSIVE)
                coded_frames = HFI_BITMASK_FRAME_MBS_ONLY_FLAG;
        inst_hfi_gen2->src_subcr_params.coded_frames = coded_frames;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_CODED_FRAMES,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32,
                                                  &coded_frames,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_min_output_count(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 min_output = inst->buffers[BUF_OUTPUT].min_count;
        u32 port = iris_hfi_gen2_get_port(inst, plane);

        inst_hfi_gen2->src_subcr_params.fw_min_count = min_output;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32,
                                                  &min_output,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_picture_order_count(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 poc = 0;

        inst_hfi_gen2->src_subcr_params.pic_order_cnt = poc;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_PIC_ORDER_CNT_TYPE,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32,
                                                  &poc,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_colorspace(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        struct v4l2_pix_format_mplane *pixmp = &inst->fmt_src->fmt.pix_mp;
        u32 video_signal_type_present_flag = 0, color_info;
        u32 matrix_coeff = HFI_MATRIX_COEFF_RESERVED;
        u32 video_format = UNSPECIFIED_COLOR_FORMAT;
        u32 full_range = V4L2_QUANTIZATION_DEFAULT;
        u32 transfer_char = HFI_TRANSFER_RESERVED;
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 colour_description_present_flag = 0;
        u32 primaries = HFI_PRIMARIES_RESERVED;

        if (pixmp->colorspace != V4L2_COLORSPACE_DEFAULT ||
            pixmp->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT ||
            pixmp->xfer_func != V4L2_XFER_FUNC_DEFAULT) {
                colour_description_present_flag = 1;
                video_signal_type_present_flag = 1;
                primaries = iris_hfi_gen2_get_color_primaries(pixmp->colorspace);
                matrix_coeff = iris_hfi_gen2_get_matrix_coefficients(pixmp->ycbcr_enc);
                transfer_char = iris_hfi_gen2_get_transfer_char(pixmp->xfer_func);
        }

        if (pixmp->quantization != V4L2_QUANTIZATION_DEFAULT) {
                video_signal_type_present_flag = 1;
                full_range = pixmp->quantization == V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0;
        }

        color_info = iris_hfi_gen2_get_color_info(matrix_coeff, transfer_char, primaries,
                                                  colour_description_present_flag, full_range,
                                                  video_format, video_signal_type_present_flag);

        inst_hfi_gen2->src_subcr_params.color_info = color_info;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_SIGNAL_COLOR_INFO,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_32_PACKED,
                                                  &color_info,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_profile(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
        u32 profile = 0;

        switch (inst->codec) {
        case V4L2_PIX_FMT_HEVC:
                profile = inst->fw_caps[PROFILE_HEVC].value;
                break;
        case V4L2_PIX_FMT_VP9:
                profile = inst->fw_caps[PROFILE_VP9].value;
                break;
        case V4L2_PIX_FMT_H264:
                profile = inst->fw_caps[PROFILE_H264].value;
                break;
        case V4L2_PIX_FMT_AV1:
                profile = inst->fw_caps[PROFILE_AV1].value;
                break;
        }

        inst_hfi_gen2->src_subcr_params.profile = profile;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_PROFILE,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32_ENUM,
                                                  &profile,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_level(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
        u32 level = 0;

        switch (inst->codec) {
        case V4L2_PIX_FMT_HEVC:
                level = inst->fw_caps[LEVEL_HEVC].value;
                break;
        case V4L2_PIX_FMT_VP9:
                level = inst->fw_caps[LEVEL_VP9].value;
                break;
        case V4L2_PIX_FMT_H264:
                level = inst->fw_caps[LEVEL_H264].value;
                break;
        case V4L2_PIX_FMT_AV1:
                level = inst->fw_caps[LEVEL_AV1].value;
                break;
        }

        inst_hfi_gen2->src_subcr_params.level = level;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_LEVEL,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32_ENUM,
                                                  &level,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_opb_enable(struct iris_inst *inst, u32 plane)
{
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 opb_enable = iris_split_mode_enabled(inst);

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_OPB_ENABLE,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32,
                                                  &opb_enable,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane)
{
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 hfi_colorformat, pixelformat;

        if (inst->domain == DECODER) {
                pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
                hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ?
                        HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC;
        } else {
                pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat;
                hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ?
                        HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC;
        }

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_COLOR_FORMAT,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32_ENUM,
                                                  &hfi_colorformat,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 plane)
{
        u32 pixelformat, stride_y, stride_uv, scanline_y, scanline_uv;
        u32 port = iris_hfi_gen2_get_port(inst, plane);
        u32 payload[2];

        if (inst->domain == DECODER) {
                pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
                stride_y = inst->fmt_dst->fmt.pix_mp.width;
                scanline_y = inst->fmt_dst->fmt.pix_mp.height;
        } else {
                pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat;
                stride_y = ALIGN(inst->fmt_src->fmt.pix_mp.width, 128);
                scanline_y = ALIGN(inst->fmt_src->fmt.pix_mp.height, 32);
        }

        stride_uv = stride_y;
        scanline_uv = scanline_y / 2;

        if (pixelformat != V4L2_PIX_FMT_NV12)
                return 0;

        payload[0] = stride_y << 16 | scanline_y;
        payload[1] = stride_uv << 16 | scanline_uv;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_LINEAR_STRIDE_SCANLINE,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_64_PACKED,
                                                  &payload,
                                                  sizeof(u64));
}

static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane)
{
        u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 tier = inst->fw_caps[TIER].value;

        tier = (inst->codec == V4L2_PIX_FMT_AV1) ? inst->fw_caps[TIER_AV1].value :
                                                        inst->fw_caps[TIER].value;
        inst_hfi_gen2->src_subcr_params.tier = tier;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_TIER,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32_ENUM,
                                                  &tier,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_frame_rate(struct iris_inst *inst, u32 plane)
{
        u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
        u32 frame_rate = inst->frame_rate << 16;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_FRAME_RATE,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_Q16,
                                                  &frame_rate,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_film_grain(struct iris_inst *inst, u32 plane)
{
        u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 film_grain = inst->fw_caps[FILM_GRAIN].value;

        inst_hfi_gen2->src_subcr_params.film_grain = film_grain;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_AV1_FILM_GRAIN_PRESENT,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32_ENUM,
                                                  &film_grain,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane)
{
        u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 super_block = inst->fw_caps[SUPER_BLOCK].value;

        inst_hfi_gen2->src_subcr_params.super_block = super_block;

        return iris_hfi_gen2_session_set_property(inst,
                                                  HFI_PROP_AV1_SUPER_BLOCK_ENABLED,
                                                  HFI_HOST_FLAGS_NONE,
                                                  port,
                                                  HFI_PAYLOAD_U32_ENUM,
                                                  &super_block,
                                                  sizeof(u32));
}

static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane)
{
        const struct iris_platform_data *pdata = inst->core->iris_platform_data;
        u32 config_params_size = 0, i, j;
        const u32 *config_params = NULL;
        int ret;

        static const struct iris_hfi_prop_type_handle prop_type_handle_arr[] = {
                {HFI_PROP_RAW_RESOLUTION,             iris_hfi_gen2_set_raw_resolution         },
                {HFI_PROP_BITSTREAM_RESOLUTION,       iris_hfi_gen2_set_bitstream_resolution   },
                {HFI_PROP_CROP_OFFSETS,               iris_hfi_gen2_set_crop_offsets           },
                {HFI_PROP_CODED_FRAMES,               iris_hfi_gen2_set_coded_frames           },
                {HFI_PROP_LUMA_CHROMA_BIT_DEPTH,      iris_hfi_gen2_set_bit_depth              },
                {HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, iris_hfi_gen2_set_min_output_count       },
                {HFI_PROP_PIC_ORDER_CNT_TYPE,         iris_hfi_gen2_set_picture_order_count    },
                {HFI_PROP_SIGNAL_COLOR_INFO,          iris_hfi_gen2_set_colorspace             },
                {HFI_PROP_PROFILE,                    iris_hfi_gen2_set_profile                },
                {HFI_PROP_LEVEL,                      iris_hfi_gen2_set_level                  },
                {HFI_PROP_OPB_ENABLE,                 iris_hfi_gen2_set_opb_enable             },
                {HFI_PROP_COLOR_FORMAT,               iris_hfi_gen2_set_colorformat            },
                {HFI_PROP_LINEAR_STRIDE_SCANLINE,     iris_hfi_gen2_set_linear_stride_scanline },
                {HFI_PROP_TIER,                       iris_hfi_gen2_set_tier                   },
                {HFI_PROP_FRAME_RATE,                 iris_hfi_gen2_set_frame_rate             },
                {HFI_PROP_AV1_FILM_GRAIN_PRESENT,     iris_hfi_gen2_set_film_grain             },
                {HFI_PROP_AV1_SUPER_BLOCK_ENABLED,    iris_hfi_gen2_set_super_block            },
                {HFI_PROP_OPB_ENABLE,                 iris_hfi_gen2_set_opb_enable             },
        };

        if (inst->domain == DECODER) {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        if (inst->codec == V4L2_PIX_FMT_H264) {
                                config_params = pdata->dec_input_config_params_default;
                                config_params_size = pdata->dec_input_config_params_default_size;
                        } else if (inst->codec == V4L2_PIX_FMT_HEVC) {
                                config_params = pdata->dec_input_config_params_hevc;
                                config_params_size = pdata->dec_input_config_params_hevc_size;
                        } else if (inst->codec == V4L2_PIX_FMT_VP9) {
                                config_params = pdata->dec_input_config_params_vp9;
                                config_params_size = pdata->dec_input_config_params_vp9_size;
                        } else if (inst->codec == V4L2_PIX_FMT_AV1) {
                                config_params = pdata->dec_input_config_params_av1;
                                config_params_size = pdata->dec_input_config_params_av1_size;
                        } else {
                                return -EINVAL;
                        }
                } else {
                        config_params = pdata->dec_output_config_params;
                        config_params_size = pdata->dec_output_config_params_size;
                }
        } else {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        config_params = pdata->enc_input_config_params;
                        config_params_size = pdata->enc_input_config_params_size;
                } else {
                        config_params = pdata->enc_output_config_params;
                        config_params_size = pdata->enc_output_config_params_size;
                }
        }

        if (!config_params || !config_params_size)
                return -EINVAL;

        for (i = 0; i < config_params_size; i++) {
                for (j = 0; j < ARRAY_SIZE(prop_type_handle_arr); j++) {
                        if (prop_type_handle_arr[j].type == config_params[i]) {
                                ret = prop_type_handle_arr[j].handle(inst, plane);
                                if (ret)
                                        return ret;
                                break;
                        }
                }
        }

        return 0;
}

static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 codec = 0;

        switch (inst->codec) {
        case V4L2_PIX_FMT_H264:
                if (inst->domain == ENCODER)
                        codec = HFI_CODEC_ENCODE_AVC;
                else
                        codec = HFI_CODEC_DECODE_AVC;
                break;
        case V4L2_PIX_FMT_HEVC:
                if (inst->domain == ENCODER)
                        codec = HFI_CODEC_ENCODE_HEVC;
                else
                        codec = HFI_CODEC_DECODE_HEVC;
                break;
        case V4L2_PIX_FMT_VP9:
                codec = HFI_CODEC_DECODE_VP9;
                break;
        case V4L2_PIX_FMT_AV1:
                codec = HFI_CODEC_DECODE_AV1;
        }

        iris_hfi_gen2_packet_session_property(inst,
                                              HFI_PROP_CODEC,
                                              HFI_HOST_FLAGS_NONE,
                                              HFI_PORT_NONE,
                                              HFI_PAYLOAD_U32_ENUM,
                                              &codec,
                                              sizeof(u32));

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_set_default_header(struct iris_inst *inst)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 default_header = false;

        iris_hfi_gen2_packet_session_property(inst,
                                              HFI_PROP_DEC_DEFAULT_HEADER,
                                              HFI_HOST_FLAGS_NONE,
                                              HFI_PORT_BITSTREAM,
                                              HFI_PAYLOAD_U32,
                                              &default_header,
                                              sizeof(u32));

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_open(struct iris_inst *inst)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        int ret;

        if (inst->state != IRIS_INST_DEINIT)
                return -EALREADY;

        inst_hfi_gen2->ipsc_properties_set = false;
        inst_hfi_gen2->opsc_properties_set = false;

        inst_hfi_gen2->packet = kzalloc(4096, GFP_KERNEL);
        if (!inst_hfi_gen2->packet)
                return -ENOMEM;

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_OPEN,
                                             HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED,
                                             HFI_PORT_NONE,
                                             0,
                                             HFI_PAYLOAD_U32,
                                             &inst->session_id,
                                             sizeof(u32));

        ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                       inst_hfi_gen2->packet->size);
        if (ret)
                goto fail_free_packet;

        ret = iris_hfi_gen2_session_set_codec(inst);
        if (ret)
                goto fail_free_packet;

        if (inst->domain == DECODER) {
                ret = iris_hfi_gen2_session_set_default_header(inst);
                if (ret)
                        goto fail_free_packet;
        }

        return 0;

fail_free_packet:
        kfree(inst_hfi_gen2->packet);
        inst_hfi_gen2->packet = NULL;

        return ret;
}

static int iris_hfi_gen2_session_close(struct iris_inst *inst)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        int ret;

        if (!inst_hfi_gen2->packet)
                return -EINVAL;

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_CLOSE,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED |
                                             HFI_HOST_FLAGS_NON_DISCARDABLE),
                                             HFI_PORT_NONE,
                                             inst->session_id,
                                             HFI_PAYLOAD_NONE,
                                             NULL,
                                             0);

        ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                       inst_hfi_gen2->packet->size);

        kfree(inst_hfi_gen2->packet);
        inst_hfi_gen2->packet = NULL;

        return ret;
}

static int iris_hfi_gen2_session_subscribe_mode(struct iris_inst *inst,
                                                u32 cmd, u32 plane, u32 payload_type,
                                                void *payload, u32 payload_size)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);

        iris_hfi_gen2_packet_session_command(inst,
                                             cmd,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             payload_type,
                                             payload,
                                             payload_size);

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        struct hfi_subscription_params subsc_params;
        u32 prop_type, payload_size, payload_type;
        struct iris_core *core = inst->core;
        const u32 *change_param = NULL;
        u32 change_param_size = 0;
        u32 payload[32] = {0};
        u32 hfi_port = 0, i;
        int ret;

        if (inst->domain == ENCODER)
                return 0;

        if ((V4L2_TYPE_IS_OUTPUT(plane) && inst_hfi_gen2->ipsc_properties_set) ||
            (V4L2_TYPE_IS_CAPTURE(plane) && inst_hfi_gen2->opsc_properties_set)) {
                dev_dbg(core->dev, "%cPSC already set\n", V4L2_TYPE_IS_OUTPUT(plane) ? 'I' : 'O');
                return 0;
        }

        switch (inst->codec) {
        case V4L2_PIX_FMT_H264:
                change_param = core->iris_platform_data->dec_input_config_params_default;
                change_param_size =
                        core->iris_platform_data->dec_input_config_params_default_size;
                break;
        case V4L2_PIX_FMT_HEVC:
                change_param = core->iris_platform_data->dec_input_config_params_hevc;
                change_param_size =
                        core->iris_platform_data->dec_input_config_params_hevc_size;
                break;
        case V4L2_PIX_FMT_VP9:
                change_param = core->iris_platform_data->dec_input_config_params_vp9;
                change_param_size =
                        core->iris_platform_data->dec_input_config_params_vp9_size;
                break;
        case V4L2_PIX_FMT_AV1:
                change_param = core->iris_platform_data->dec_input_config_params_av1;
                change_param_size =
                        core->iris_platform_data->dec_input_config_params_av1_size;
                break;
        }

        payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE;

        for (i = 0; i < change_param_size; i++)
                payload[i + 1] = change_param[i];

        ret = iris_hfi_gen2_session_subscribe_mode(inst,
                                                   HFI_CMD_SUBSCRIBE_MODE,
                                                   plane,
                                                   HFI_PAYLOAD_U32_ARRAY,
                                                   &payload[0],
                                                   ((change_param_size + 1) * sizeof(u32)));
        if (ret)
                return ret;

        if (V4L2_TYPE_IS_OUTPUT(plane)) {
                inst_hfi_gen2->ipsc_properties_set = true;
        } else {
                hfi_port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
                memcpy(&inst_hfi_gen2->dst_subcr_params,
                       &inst_hfi_gen2->src_subcr_params,
                       sizeof(inst_hfi_gen2->src_subcr_params));
                subsc_params = inst_hfi_gen2->dst_subcr_params;
                for (i = 0; i < change_param_size; i++) {
                        payload[0] = 0;
                        payload[1] = 0;
                        payload_size = 0;
                        payload_type = 0;
                        prop_type = change_param[i];
                        switch (prop_type) {
                        case HFI_PROP_BITSTREAM_RESOLUTION:
                                payload[0] = subsc_params.bitstream_resolution;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_CROP_OFFSETS:
                                payload[0] = subsc_params.crop_offsets[0];
                                payload[1] = subsc_params.crop_offsets[1];
                                payload_size = sizeof(u64);
                                payload_type = HFI_PAYLOAD_64_PACKED;
                                break;
                        case HFI_PROP_CODED_FRAMES:
                                payload[0] = subsc_params.coded_frames;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_LUMA_CHROMA_BIT_DEPTH:
                                payload[0] = subsc_params.bit_depth;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT:
                                payload[0] = subsc_params.fw_min_count;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_PIC_ORDER_CNT_TYPE:
                                payload[0] = subsc_params.pic_order_cnt;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_SIGNAL_COLOR_INFO:
                                payload[0] = subsc_params.color_info;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_PROFILE:
                                payload[0] = subsc_params.profile;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_LEVEL:
                                payload[0] = subsc_params.level;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_TIER:
                                payload[0] = subsc_params.tier;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_AV1_FILM_GRAIN_PRESENT:
                                payload[0] = subsc_params.film_grain;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        case HFI_PROP_AV1_SUPER_BLOCK_ENABLED:
                                payload[0] = subsc_params.super_block;
                                payload_size = sizeof(u32);
                                payload_type = HFI_PAYLOAD_U32;
                                break;
                        default:
                                prop_type = 0;
                                ret = -EINVAL;
                                break;
                        }
                        if (prop_type) {
                                ret = iris_hfi_gen2_session_set_property(inst,
                                                                         prop_type,
                                                                         HFI_HOST_FLAGS_NONE,
                                                                         hfi_port,
                                                                         payload_type,
                                                                         &payload,
                                                                         payload_size);
                                if (ret)
                                        return ret;
                        }
                }
                inst_hfi_gen2->opsc_properties_set = true;
        }

        return 0;
}

static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
{
        struct iris_core *core = inst->core;
        u32 subscribe_prop_size = 0, i;
        const u32 *subcribe_prop = NULL;
        u32 payload[32] = {0};

        payload[0] = HFI_MODE_PROPERTY;

        if (inst->domain == ENCODER)
                return 0;

        if (V4L2_TYPE_IS_OUTPUT(plane)) {
                subscribe_prop_size = core->iris_platform_data->dec_input_prop_size;
                subcribe_prop = core->iris_platform_data->dec_input_prop;
        } else {
                switch (inst->codec) {
                case V4L2_PIX_FMT_H264:
                        subcribe_prop = core->iris_platform_data->dec_output_prop_avc;
                        subscribe_prop_size =
                                core->iris_platform_data->dec_output_prop_avc_size;
                        break;
                case V4L2_PIX_FMT_HEVC:
                        subcribe_prop = core->iris_platform_data->dec_output_prop_hevc;
                        subscribe_prop_size =
                                core->iris_platform_data->dec_output_prop_hevc_size;
                        break;
                case V4L2_PIX_FMT_VP9:
                        subcribe_prop = core->iris_platform_data->dec_output_prop_vp9;
                        subscribe_prop_size =
                                core->iris_platform_data->dec_output_prop_vp9_size;
                        break;
                case V4L2_PIX_FMT_AV1:
                        subcribe_prop = core->iris_platform_data->dec_output_prop_av1;
                        subscribe_prop_size =
                                core->iris_platform_data->dec_output_prop_av1_size;
                        break;
                }
        }

        for (i = 0; i < subscribe_prop_size; i++)
                payload[i + 1] = subcribe_prop[i];

        return iris_hfi_gen2_session_subscribe_mode(inst,
                                                    HFI_CMD_SUBSCRIBE_MODE,
                                                    plane,
                                                    HFI_PAYLOAD_U32_ARRAY,
                                                    &payload[0],
                                                    (subscribe_prop_size + 1) * sizeof(u32));
}

static int iris_hfi_gen2_session_start(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        int ret = 0;

        ret = iris_hfi_gen2_subscribe_change_param(inst, plane);
        if (ret)
                return ret;

        ret = iris_hfi_gen2_subscribe_property(inst, plane);
        if (ret)
                return ret;

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_START,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             HFI_PAYLOAD_NONE,
                                             NULL,
                                             0);

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_stop(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        int ret = 0;

        if (!inst_hfi_gen2->packet)
                return -EINVAL;

        reinit_completion(&inst->completion);

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_STOP,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED |
                                             HFI_HOST_FLAGS_NON_DISCARDABLE),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             HFI_PAYLOAD_NONE,
                                             NULL,
                                             0);

        ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                       inst_hfi_gen2->packet->size);
        if (ret)
                return ret;

        return iris_wait_for_session_response(inst, false);
}

static int iris_hfi_gen2_session_pause(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_PAUSE,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             HFI_PAYLOAD_NONE,
                                             NULL,
                                             0);

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_resume_drc(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 payload = HFI_CMD_SETTINGS_CHANGE;

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_RESUME,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             HFI_PAYLOAD_U32,
                                             &payload,
                                             sizeof(u32));

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_resume_drain(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        u32 payload = HFI_CMD_DRAIN;

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_RESUME,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             HFI_PAYLOAD_U32,
                                             &payload,
                                             sizeof(u32));

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_drain(struct iris_inst *inst, u32 plane)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);

        if (!V4L2_TYPE_IS_OUTPUT(plane))
                return 0;

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_DRAIN,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED |
                                             HFI_HOST_FLAGS_NON_DISCARDABLE),
                                             iris_hfi_gen2_get_port(inst, plane),
                                             inst->session_id,
                                             HFI_PAYLOAD_NONE,
                                             NULL,
                                             0);

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type buffer_type)
{
        switch (buffer_type) {
        case BUF_INPUT:
                if (domain == DECODER)
                        return HFI_BUFFER_BITSTREAM;
                else
                        return HFI_BUFFER_RAW;
        case BUF_OUTPUT:
                if (domain == DECODER)
                        return HFI_BUFFER_RAW;
                else
                        return HFI_BUFFER_BITSTREAM;
        case BUF_BIN:
                return HFI_BUFFER_BIN;
        case BUF_COMV:
                return HFI_BUFFER_COMV;
        case BUF_NON_COMV:
                return HFI_BUFFER_NON_COMV;
        case BUF_LINE:
                return HFI_BUFFER_LINE;
        case BUF_DPB:
        case BUF_SCRATCH_2:
                return HFI_BUFFER_DPB;
        case BUF_PERSIST:
                return HFI_BUFFER_PERSIST;
        case BUF_ARP:
                return HFI_BUFFER_ARP;
        case BUF_VPSS:
                return HFI_BUFFER_VPSS;
        case BUF_PARTIAL:
                return HFI_BUFFER_PARTIAL_DATA;
        default:
                return 0;
        }
}

static int iris_set_num_comv(struct iris_inst *inst)
{
        struct platform_inst_caps *caps;
        struct iris_core *core = inst->core;
        u32 num_comv;

        caps = core->iris_platform_data->inst_caps;

        /*
         * AV1 needs more comv buffers than other codecs.
         * Update accordingly.
         */
        num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ?
                                NUM_COMV_AV1 : caps->num_comv;

        return core->hfi_ops->session_set_property(inst,
                                                   HFI_PROP_COMV_BUFFER_COUNT,
                                                   HFI_HOST_FLAGS_NONE,
                                                   HFI_PORT_BITSTREAM,
                                                   HFI_PAYLOAD_U32,
                                                   &num_comv, sizeof(u32));
}

static void iris_hfi_gen2_get_buffer(u32 domain, struct iris_buffer *buffer,
                                     struct iris_hfi_buffer *buf)
{
        memset(buf, 0, sizeof(*buf));
        buf->type = iris_hfi_gen2_buf_type_from_driver(domain, buffer->type);
        buf->index = buffer->index;
        buf->base_address = buffer->device_addr;
        buf->addr_offset = 0;
        buf->buffer_size = buffer->buffer_size;

        if (domain == DECODER && buffer->type == BUF_INPUT)
                buf->buffer_size = ALIGN(buffer->buffer_size, 256);
        buf->data_offset = buffer->data_offset;
        buf->data_size = buffer->data_size;
        if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
                buf->flags |= HFI_BUF_HOST_FLAG_RELEASE;
        buf->flags |= HFI_BUF_HOST_FLAGS_CB_NON_SECURE;
        buf->timestamp = buffer->timestamp;
}

static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        struct iris_hfi_buffer hfi_buffer;
        u32 port;
        int ret;

        iris_hfi_gen2_get_buffer(inst->domain, buffer, &hfi_buffer);
        if (buffer->type == BUF_COMV) {
                ret = iris_set_num_comv(inst);
                if (ret)
                        return ret;
        }

        port = iris_hfi_gen2_get_port_from_buf_type(inst, buffer->type);
        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_BUFFER,
                                             HFI_HOST_FLAGS_INTR_REQUIRED,
                                             port,
                                             inst->session_id,
                                             HFI_PAYLOAD_STRUCTURE,
                                             &hfi_buffer,
                                             sizeof(hfi_buffer));

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
{
        struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
        struct iris_hfi_buffer hfi_buffer;
        u32 port;

        iris_hfi_gen2_get_buffer(inst->domain, buffer, &hfi_buffer);
        hfi_buffer.flags |= HFI_BUF_HOST_FLAG_RELEASE;
        port = iris_hfi_gen2_get_port_from_buf_type(inst, buffer->type);

        iris_hfi_gen2_packet_session_command(inst,
                                             HFI_CMD_BUFFER,
                                             (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
                                             HFI_HOST_FLAGS_INTR_REQUIRED),
                                             port,
                                             inst->session_id,
                                             HFI_PAYLOAD_STRUCTURE,
                                             &hfi_buffer,
                                             sizeof(hfi_buffer));

        return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
                                        inst_hfi_gen2->packet->size);
}

static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
        .sys_init = iris_hfi_gen2_sys_init,
        .sys_image_version = iris_hfi_gen2_sys_image_version,
        .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse,
        .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
        .session_open = iris_hfi_gen2_session_open,
        .session_set_config_params = iris_hfi_gen2_session_set_config_params,
        .session_set_property = iris_hfi_gen2_session_set_property,
        .session_start = iris_hfi_gen2_session_start,
        .session_queue_buf = iris_hfi_gen2_session_queue_buffer,
        .session_release_buf = iris_hfi_gen2_session_release_buffer,
        .session_pause = iris_hfi_gen2_session_pause,
        .session_resume_drc = iris_hfi_gen2_session_resume_drc,
        .session_stop = iris_hfi_gen2_session_stop,
        .session_drain = iris_hfi_gen2_session_drain,
        .session_resume_drain = iris_hfi_gen2_session_resume_drain,
        .session_close = iris_hfi_gen2_session_close,
};

void iris_hfi_gen2_command_ops_init(struct iris_core *core)
{
        core->hfi_ops = &iris_hfi_gen2_command_ops;
}

struct iris_inst *iris_hfi_gen2_get_instance(void)
{
        struct iris_inst_hfi_gen2 *out;

        /* The allocation is intentionally larger than struct iris_inst. */
        out = kzalloc_obj(*out);

        return &out->inst;
}