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

#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>

#include "iris_buffer.h"
#include "iris_instance.h"
#include "iris_power.h"
#include "iris_vpu_buffer.h"

#define PIXELS_4K 4096
#define MAX_WIDTH 4096
#define MAX_HEIGHT 2304
#define Y_STRIDE_ALIGN 128
#define UV_STRIDE_ALIGN 128
#define Y_SCANLINE_ALIGN 32
#define UV_SCANLINE_ALIGN 16
#define UV_SCANLINE_ALIGN_QC08C 32
#define META_STRIDE_ALIGNED 64
#define META_SCANLINE_ALIGNED 16
#define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16))

/*
 * NV12:
 * YUV 4:2:0 image with a plane of 8 bit Y samples followed
 * by an interleaved U/V plane containing 8 bit 2x2 subsampled
 * colour difference samples.
 *
 * <-Y/UV_Stride (aligned to 128)->
 * <------- Width ------->
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          y_scanlines (aligned to 32)
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
 * . . . . . . . . . . . . . . . .              |
 * . . . . . . . . . . . . . . . .              |
 * . . . . . . . . . . . . . . . .              |
 * . . . . . . . . . . . . . . . .              V
 * U V U V U V U V U V U V . . . .  ^
 * U V U V U V U V U V U V . . . .  |
 * U V U V U V U V U V U V . . . .  |
 * U V U V U V U V U V U V . . . .  uv_scanlines (aligned to 16)
 * . . . . . . . . . . . . . . . .  |
 * . . . . . . . . . . . . . . . .  V
 * . . . . . . . . . . . . . . . .  --> Buffer size aligned to 4K
 *
 * y_stride : Width aligned to 128
 * uv_stride : Width aligned to 128
 * y_scanlines: Height aligned to 32
 * uv_scanlines: Height/2 aligned to 16
 * Total size = align((y_stride * y_scanlines
 *          + uv_stride * uv_scanlines , 4096)
 *
 * Note: All the alignments are hardware requirements.
 */
static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst)
{
        u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines;
        struct v4l2_format *f;

        if (inst->domain == DECODER)
                f = inst->fmt_dst;
        else
                f = inst->fmt_src;

        y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
        uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
        y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN);
        uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN);
        y_plane = y_stride * y_scanlines;
        uv_plane = uv_stride * uv_scanlines;

        return ALIGN(y_plane + uv_plane, PIXELS_4K);
}

/*
 * QC08C:
 * Compressed Macro-tile format for NV12.
 * Contains 4 planes in the following order -
 * (A) Y_Meta_Plane
 * (B) Y_UBWC_Plane
 * (C) UV_Meta_Plane
 * (D) UV_UBWC_Plane
 *
 * Y_Meta_Plane consists of meta information to decode compressed
 * tile data in Y_UBWC_Plane.
 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
 * UBWC decoder block will use the Y_Meta_Plane data together with
 * Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples.
 *
 * UV_Meta_Plane consists of meta information to decode compressed
 * tile data in UV_UBWC_Plane.
 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
 * UBWC decoder block will use UV_Meta_Plane data together with
 * UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2
 * subsampled color difference samples.
 *
 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
 * and randomly accessible. There is no dependency between tiles.
 *
 * <----- y_meta_stride ----> (aligned to 64)
 * <-------- Width ------>
 * M M M M M M M M M M M M . .      ^           ^
 * M M M M M M M M M M M M . .      |           |
 * M M M M M M M M M M M M . .      Height      |
 * M M M M M M M M M M M M . .      |         y_meta_scanlines  (aligned to 16)
 * M M M M M M M M M M M M . .      |           |
 * M M M M M M M M M M M M . .      |           |
 * M M M M M M M M M M M M . .      |           |
 * M M M M M M M M M M M M . .      V           |
 * . . . . . . . . . . . . . .                  |
 * . . . . . . . . . . . . . .                  |
 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
 * . . . . . . . . . . . . . .                  V
 * <--Compressed tile y_stride---> (aligned to 128)
 * <------- Width ------->
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile y_scanlines (aligned to 32)
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
 * . . . . . . . . . . . . . . . .              |
 * . . . . . . . . . . . . . . . .              |
 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
 * . . . . . . . . . . . . . . . .              V
 * <----- uv_meta_stride ---->  (aligned to 64)
 * M M M M M M M M M M M M . .      ^
 * M M M M M M M M M M M M . .      |
 * M M M M M M M M M M M M . .      |
 * M M M M M M M M M M M M . .      uv_meta_scanlines (aligned to 16)
 * . . . . . . . . . . . . . .      |
 * . . . . . . . . . . . . . .      V
 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
 * <--Compressed tile uv_stride---> (aligned to 128)
 * U* V* U* V* U* V* U* V* . . . .  ^
 * U* V* U* V* U* V* U* V* . . . .  |
 * U* V* U* V* U* V* U* V* . . . .  |
 * U* V* U* V* U* V* U* V* . . . .  uv_scanlines (aligned to 32)
 * . . . . . . . . . . . . . . . .  |
 * . . . . . . . . . . . . . . . .  V
 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
 *
 * y_stride: width aligned to 128
 * uv_stride: width aligned to 128
 * y_scanlines: height aligned to 32
 * uv_scanlines: height aligned to 32
 * y_plane: buffer size aligned to 4096
 * uv_plane: buffer size aligned to 4096
 * y_meta_stride: width aligned to 64
 * y_meta_scanlines: height aligned to 16
 * y_meta_plane: buffer size aligned to 4096
 * uv_meta_stride: width aligned to 64
 * uv_meta_scanlines: height aligned to 16
 * uv_meta_plane: buffer size aligned to 4096
 *
 * Total size = align( y_plane + uv_plane +
 *           y_meta_plane + uv_meta_plane, 4096)
 *
 * Note: All the alignments are hardware requirements.
 */
static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst)
{
        u32 y_plane, uv_plane, y_stride, uv_stride;
        u32 uv_meta_stride, uv_meta_plane;
        u32 y_meta_stride, y_meta_plane;
        struct v4l2_format *f = NULL;

        if (inst->domain == DECODER)
                f = inst->fmt_dst;
        else
                f = inst->fmt_src;

        y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, META_STRIDE_ALIGNED >> 1),
                              META_STRIDE_ALIGNED);
        y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height,
                                                          META_SCANLINE_ALIGNED >> 1),
                                             META_SCANLINE_ALIGNED);
        y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K);

        y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
        y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN), PIXELS_4K);

        uv_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width / 2, META_STRIDE_ALIGNED >> 2),
                               META_STRIDE_ALIGNED);
        uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height / 2,
                                                            META_SCANLINE_ALIGNED >> 1),
                                               META_SCANLINE_ALIGNED);
        uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K);

        uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
        uv_plane = ALIGN(uv_stride * ALIGN(f->fmt.pix_mp.height / 2, UV_SCANLINE_ALIGN_QC08C),
                         PIXELS_4K);

        return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K);
}

static u32 iris_dec_bitstream_buffer_size(struct iris_inst *inst)
{
        struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
        u32 base_res_mbs = NUM_MBS_4K;
        u32 frame_size, num_mbs;
        u32 div_factor = 2;

        num_mbs = iris_get_mbpf(inst);
        if (num_mbs > NUM_MBS_4K) {
                div_factor = 4;
                base_res_mbs = caps->max_mbpf;
        } else {
                if (inst->codec == V4L2_PIX_FMT_VP9)
                        div_factor = 1;
        }

        /*
         * frame_size = YUVsize / div_factor
         * where YUVsize = resolution_in_MBs * MBs_in_pixel * 3 / 2
         */
        frame_size = base_res_mbs * (16 * 16) * 3 / 2 / div_factor;

        return ALIGN(frame_size, PIXELS_4K);
}

static u32 iris_enc_bitstream_buffer_size(struct iris_inst *inst)
{
        u32 aligned_width, aligned_height, bitstream_size, yuv_size;
        int bitrate_mode, frame_rc;
        struct v4l2_format *f;

        f = inst->fmt_dst;

        bitrate_mode = inst->fw_caps[BITRATE_MODE].value;
        frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value;

        aligned_width = ALIGN(f->fmt.pix_mp.width, 32);
        aligned_height = ALIGN(f->fmt.pix_mp.height, 32);
        bitstream_size = aligned_width * aligned_height * 3;
        yuv_size = (aligned_width * aligned_height * 3) >> 1;
        if (aligned_width * aligned_height > (4096 * 2176))
                /* bitstream_size = 0.25 * yuv_size; */
                bitstream_size = (bitstream_size >> 3);
        else if (aligned_width * aligned_height > (1280 * 720))
                /* bitstream_size = 0.5 * yuv_size; */
                bitstream_size = (bitstream_size >> 2);

        if ((!frame_rc || bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) &&
            bitstream_size < yuv_size)
                bitstream_size = (bitstream_size << 1);

        return ALIGN(bitstream_size, 4096);
}

int iris_get_buffer_size(struct iris_inst *inst,
                         enum iris_buffer_type buffer_type)
{
        if (inst->domain == DECODER) {
                switch (buffer_type) {
                case BUF_INPUT:
                        return iris_dec_bitstream_buffer_size(inst);
                case BUF_OUTPUT:
                        if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
                                return iris_yuv_buffer_size_qc08c(inst);
                        else
                                return iris_yuv_buffer_size_nv12(inst);
                case BUF_DPB:
                        return iris_yuv_buffer_size_qc08c(inst);
                default:
                        return 0;
                }
        } else {
                switch (buffer_type) {
                case BUF_INPUT:
                        if (inst->fmt_src->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
                                return iris_yuv_buffer_size_qc08c(inst);
                        else
                                return iris_yuv_buffer_size_nv12(inst);
                case BUF_OUTPUT:
                        return iris_enc_bitstream_buffer_size(inst);
                default:
                        return 0;
                }
        }
}

static void iris_fill_internal_buf_info(struct iris_inst *inst,
                                        enum iris_buffer_type buffer_type)
{
        struct iris_buffers *buffers = &inst->buffers[buffer_type];

        buffers->size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, buffer_type);
        buffers->min_count = iris_vpu_buf_count(inst, buffer_type);
}

void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
{
        const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
        const u32 *internal_buf_type;
        u32 internal_buffer_count, i;

        if (inst->domain == DECODER) {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->dec_ip_int_buf_tbl;
                        internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
                        for (i = 0; i < internal_buffer_count; i++)
                                iris_fill_internal_buf_info(inst, internal_buf_type[i]);
                } else {
                        internal_buf_type = platform_data->dec_op_int_buf_tbl;
                        internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
                        for (i = 0; i < internal_buffer_count; i++)
                                iris_fill_internal_buf_info(inst, internal_buf_type[i]);
                }
        } else {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->enc_ip_int_buf_tbl;
                        internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
                        for (i = 0; i < internal_buffer_count; i++)
                                iris_fill_internal_buf_info(inst, internal_buf_type[i]);
                } else {
                        internal_buf_type = platform_data->enc_op_int_buf_tbl;
                        internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
                        for (i = 0; i < internal_buffer_count; i++)
                                iris_fill_internal_buf_info(inst, internal_buf_type[i]);
                }
        }
}

static int iris_create_internal_buffer(struct iris_inst *inst,
                                       enum iris_buffer_type buffer_type, u32 index)
{
        struct iris_buffers *buffers = &inst->buffers[buffer_type];
        struct iris_core *core = inst->core;
        struct iris_buffer *buffer;

        if (!buffers->size)
                return 0;

        buffer = kzalloc_obj(*buffer);
        if (!buffer)
                return -ENOMEM;

        INIT_LIST_HEAD(&buffer->list);
        buffer->type = buffer_type;
        buffer->index = index;
        buffer->buffer_size = buffers->size;
        buffer->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;

        buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size,
                                         &buffer->device_addr, GFP_KERNEL, buffer->dma_attrs);
        if (!buffer->kvaddr) {
                kfree(buffer);
                return -ENOMEM;
        }

        list_add_tail(&buffer->list, &buffers->list);

        return 0;
}

int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
{
        const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
        u32 internal_buffer_count, i, j;
        struct iris_buffers *buffers;
        const u32 *internal_buf_type;
        int ret;

        if (inst->domain == DECODER) {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->dec_ip_int_buf_tbl;
                        internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
                } else {
                        internal_buf_type = platform_data->dec_op_int_buf_tbl;
                        internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
                }
        } else {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->enc_ip_int_buf_tbl;
                        internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
                } else {
                        internal_buf_type = platform_data->enc_op_int_buf_tbl;
                        internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
                }
        }

        for (i = 0; i < internal_buffer_count; i++) {
                buffers = &inst->buffers[internal_buf_type[i]];
                for (j = 0; j < buffers->min_count; j++) {
                        ret = iris_create_internal_buffer(inst, internal_buf_type[i], j);
                        if (ret)
                                return ret;
                }
        }

        return 0;
}

int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
{
        const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
        int ret;

        ret = hfi_ops->session_queue_buf(inst, buf);
        if (ret)
                return ret;

        buf->attr &= ~BUF_ATTR_DEFERRED;
        buf->attr |= BUF_ATTR_QUEUED;

        return 0;
}

int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type)
{
        struct iris_buffer *buffer, *next;
        struct iris_buffers *buffers;
        int ret = 0;

        buffers = &inst->buffers[buffer_type];
        list_for_each_entry_safe(buffer, next, &buffers->list, list) {
                if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
                        continue;
                if (buffer->attr & BUF_ATTR_QUEUED)
                        continue;

                if (buffer->attr & BUF_ATTR_DEFERRED) {
                        ret = iris_queue_buffer(inst, buffer);
                        if (ret)
                                return ret;
                }
        }

        return ret;
}

int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
{
        const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
        struct iris_buffer *buffer, *next;
        struct iris_buffers *buffers;
        const u32 *internal_buf_type;
        u32 internal_buffer_count, i;
        int ret;

        if (inst->domain == DECODER) {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->dec_ip_int_buf_tbl;
                        internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
                } else {
                        internal_buf_type = platform_data->dec_op_int_buf_tbl;
                        internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
                }
        } else {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->enc_ip_int_buf_tbl;
                        internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
                } else {
                        internal_buf_type = platform_data->enc_op_int_buf_tbl;
                        internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
                }
        }

        for (i = 0; i < internal_buffer_count; i++) {
                buffers = &inst->buffers[internal_buf_type[i]];
                list_for_each_entry_safe(buffer, next, &buffers->list, list) {
                        if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
                                continue;
                        if (buffer->attr & BUF_ATTR_QUEUED)
                                continue;
                        if (buffer->type == BUF_DPB && inst->state != IRIS_INST_STREAMING) {
                                buffer->attr |= BUF_ATTR_DEFERRED;
                                continue;
                        }
                        ret = iris_queue_buffer(inst, buffer);
                        if (ret)
                                return ret;
                }
        }

        return 0;
}

int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
{
        struct iris_core *core = inst->core;

        list_del(&buffer->list);
        dma_free_attrs(core->dev, buffer->buffer_size, buffer->kvaddr,
                       buffer->device_addr, buffer->dma_attrs);
        kfree(buffer);

        return 0;
}

static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force)
{
        const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
        struct iris_buffer *buf, *next;
        struct iris_buffers *buffers;
        const u32 *internal_buf_type;
        u32 i, len;
        int ret;

        if (inst->domain == DECODER) {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->dec_ip_int_buf_tbl;
                        len = platform_data->dec_ip_int_buf_tbl_size;
                } else {
                        internal_buf_type = platform_data->dec_op_int_buf_tbl;
                        len = platform_data->dec_op_int_buf_tbl_size;
                }
        } else {
                if (V4L2_TYPE_IS_OUTPUT(plane)) {
                        internal_buf_type = platform_data->enc_ip_int_buf_tbl;
                        len = platform_data->enc_ip_int_buf_tbl_size;
                } else {
                        internal_buf_type = platform_data->enc_op_int_buf_tbl;
                        len = platform_data->enc_op_int_buf_tbl_size;
                }
        }

        for (i = 0; i < len; i++) {
                buffers = &inst->buffers[internal_buf_type[i]];
                list_for_each_entry_safe(buf, next, &buffers->list, list) {
                        /*
                         * during stream on, skip destroying internal(DPB) buffer
                         * if firmware did not return it.
                         * during close, destroy all buffers irrespectively.
                         */
                        if (!force && buf->attr & BUF_ATTR_QUEUED)
                                continue;

                        ret = iris_destroy_internal_buffer(inst, buf);
                        if (ret)
                                return ret;
                }
        }

        if (force) {
                if (inst->domain == DECODER)
                        buffers = &inst->buffers[BUF_PERSIST];
                else
                        buffers = &inst->buffers[BUF_ARP];

                list_for_each_entry_safe(buf, next, &buffers->list, list) {
                        ret = iris_destroy_internal_buffer(inst, buf);
                        if (ret)
                                return ret;
                }
        }

        return 0;
}

int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane)
{
        return iris_destroy_internal_buffers(inst, plane, true);
}

int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane)
{
        return iris_destroy_internal_buffers(inst, plane, false);
}

static int iris_release_internal_buffers(struct iris_inst *inst,
                                         enum iris_buffer_type buffer_type)
{
        const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
        struct iris_buffers *buffers = &inst->buffers[buffer_type];
        struct iris_buffer *buffer, *next;
        int ret;

        list_for_each_entry_safe(buffer, next, &buffers->list, list) {
                if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
                        continue;
                if (!(buffer->attr & BUF_ATTR_QUEUED))
                        continue;
                ret = hfi_ops->session_release_buf(inst, buffer);
                if (ret)
                        return ret;
                buffer->attr |= BUF_ATTR_PENDING_RELEASE;
        }

        return 0;
}

static int iris_release_input_internal_buffers(struct iris_inst *inst)
{
        const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
        const u32 *internal_buf_type;
        u32 internal_buffer_count, i;
        int ret;

        if (inst->domain == DECODER) {
                internal_buf_type = platform_data->dec_ip_int_buf_tbl;
                internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
        } else {
                internal_buf_type = platform_data->enc_ip_int_buf_tbl;
                internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
        }

        for (i = 0; i < internal_buffer_count; i++) {
                ret = iris_release_internal_buffers(inst, internal_buf_type[i]);
                if (ret)
                        return ret;
        }

        return 0;
}

int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst, enum iris_buffer_type buffer_type)
{
        struct iris_buffers *buffers = &inst->buffers[buffer_type];
        struct iris_buffer *buffer, *next;
        int ret;
        u32 i;

        if (!list_empty(&buffers->list))
                return 0;

        iris_fill_internal_buf_info(inst, buffer_type);

        for (i = 0; i < buffers->min_count; i++) {
                ret = iris_create_internal_buffer(inst, buffer_type, i);
                if (ret)
                        return ret;
        }

        list_for_each_entry_safe(buffer, next, &buffers->list, list) {
                if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
                        continue;
                if (buffer->attr & BUF_ATTR_QUEUED)
                        continue;
                ret = iris_queue_buffer(inst, buffer);
                if (ret)
                        return ret;
        }

        return 0;
}

int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst)
{
        int ret;

        iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);

        ret = iris_release_input_internal_buffers(inst);
        if (ret)
                return ret;

        ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
        if (ret)
                return ret;

        return iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
}

int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type)
{
        struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
        struct v4l2_m2m_buffer *buffer, *n;
        struct iris_buffer *buf;
        int ret;

        iris_scale_power(inst);

        if (buf_type == BUF_INPUT) {
                v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
                        buf = to_iris_buffer(&buffer->vb);
                        if (!(buf->attr & BUF_ATTR_DEFERRED))
                                continue;
                        ret = iris_queue_buffer(inst, buf);
                        if (ret)
                                return ret;
                }
        } else {
                v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
                        buf = to_iris_buffer(&buffer->vb);
                        if (!(buf->attr & BUF_ATTR_DEFERRED))
                                continue;
                        ret = iris_queue_buffer(inst, buf);
                        if (ret)
                                return ret;
                }
        }

        return 0;
}

void iris_vb2_queue_error(struct iris_inst *inst)
{
        struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
        struct vb2_queue *q;

        q = v4l2_m2m_get_src_vq(m2m_ctx);
        vb2_queue_error(q);
        q = v4l2_m2m_get_dst_vq(m2m_ctx);
        vb2_queue_error(q);
}

static struct vb2_v4l2_buffer *
iris_helper_find_buf(struct iris_inst *inst, u32 type, u32 idx)
{
        struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;

        if (V4L2_TYPE_IS_OUTPUT(type))
                return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx);
        else
                return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx);
}

static void iris_get_ts_metadata(struct iris_inst *inst, u64 timestamp_ns,
                                 struct vb2_v4l2_buffer *vbuf)
{
        u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
        u32 i;

        for (i = 0; i < ARRAY_SIZE(inst->tss); ++i) {
                if (inst->tss[i].ts_ns != timestamp_ns)
                        continue;

                vbuf->flags &= ~mask;
                vbuf->flags |= inst->tss[i].flags;
                vbuf->timecode = inst->tss[i].tc;
                return;
        }

        vbuf->flags &= ~mask;
        vbuf->flags |= inst->tss[inst->metadata_idx].flags;
        vbuf->timecode = inst->tss[inst->metadata_idx].tc;
}

int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
{
        struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
        struct vb2_v4l2_buffer *vbuf;
        struct vb2_buffer *vb2;
        u32 type, state;

        switch (buf->type) {
        case BUF_INPUT:
                type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
                break;
        case BUF_OUTPUT:
                type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
                break;
        default:
                return 0; /* Internal DPB Buffers */
        }

        vbuf = iris_helper_find_buf(inst, type, buf->index);
        if (!vbuf)
                return -EINVAL;

        vb2 = &vbuf->vb2_buf;

        vbuf->flags |= buf->flags;

        if (buf->flags & V4L2_BUF_FLAG_ERROR) {
                state = VB2_BUF_STATE_ERROR;
                vb2_set_plane_payload(vb2, 0, 0);
                vb2->timestamp = 0;
                v4l2_m2m_buf_done(vbuf, state);
                return 0;
        }

        if (V4L2_TYPE_IS_CAPTURE(type)) {
                vb2_set_plane_payload(vb2, 0, buf->data_size);
                vbuf->sequence = inst->sequence_cap++;
                iris_get_ts_metadata(inst, buf->timestamp, vbuf);
        } else {
                vbuf->sequence = inst->sequence_out++;
        }

        if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
                if (!v4l2_m2m_has_stopped(m2m_ctx)) {
                        const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };

                        v4l2_event_queue_fh(&inst->fh, &ev);
                        v4l2_m2m_mark_stopped(m2m_ctx);
                }
                inst->last_buffer_dequeued = true;
        }

        state = VB2_BUF_STATE_DONE;
        vb2->timestamp = buf->timestamp;
        v4l2_m2m_buf_done(vbuf, state);

        return 0;
}