root/drivers/media/platform/chips-media/wave5/wave5-helper.c
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Wave5 series multi-standard codec IP - decoder interface
 *
 * Copyright (C) 2021-2023 CHIPS&MEDIA INC
 */

#include "wave5-helper.h"

#define DEFAULT_BS_SIZE(width, height) ((width) * (height) / 8 * 3)

const char *state_to_str(enum vpu_instance_state state)
{
        switch (state) {
        case VPU_INST_STATE_NONE:
                return "NONE";
        case VPU_INST_STATE_OPEN:
                return "OPEN";
        case VPU_INST_STATE_INIT_SEQ:
                return "INIT_SEQ";
        case VPU_INST_STATE_PIC_RUN:
                return "PIC_RUN";
        case VPU_INST_STATE_STOP:
                return "STOP";
        default:
                return "UNKNOWN";
        }
}

int wave5_kfifo_alloc(struct vpu_instance *inst)
{
        return kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
}

void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp)
{
        int i;

        /*
         * For Wave515 SRAM memory is allocated at
         * wave5_vpu_dec_register_device() and freed at
         * wave5_vpu_dec_unregister_device().
         */
        if (list_is_singular(&inst->list) &&
            inst->dev->product_code != WAVE515_CODE)
                wave5_vdi_free_sram(inst->dev);

        for (i = 0; i < inst->fbc_buf_count; i++)
                wave5_vpu_dec_reset_framebuffer(inst, i);

        wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
        v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl);
        if (inst->v4l2_fh.vdev) {
                v4l2_fh_del(&inst->v4l2_fh, filp);
                v4l2_fh_exit(&inst->v4l2_fh);
        }
        kfifo_free(&inst->irq_status);
        ida_free(&inst->dev->inst_ida, inst->id);
        kfree(inst->codec_info);
        kfree(inst);
}

int wave5_vpu_release_device(struct file *filp,
                             int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
                             char *name)
{
        struct vpu_instance *inst = file_to_vpu_inst(filp);
        int ret = 0;
        unsigned long flags;

        v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
        /*
         * To prevent Null reference exception, the existing irq handler were
         * separated to two modules.
         * One is to queue interrupt reason into the irq handler,
         * the other is irq_thread to call the wave5_vpu_dec_finish_decode
         * to get decoded frame.
         * The list of instances should be protected between all flow of the
         * decoding process, but to protect the list in the irq_handler, spin lock
         * should be used, and mutex should be used in the irq_thread because spin lock
         * is not able to be used because mutex is already being used
         * in the wave5_vpu_dec_finish_decode.
         * So the spin lock and mutex were used to protect the list in the release function.
         */
        ret = mutex_lock_interruptible(&inst->dev->irq_lock);
        if (ret)
                return ret;
        spin_lock_irqsave(&inst->dev->irq_spinlock, flags);
        list_del_init(&inst->list);
        spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags);
        mutex_unlock(&inst->dev->irq_lock);
        if (inst->state != VPU_INST_STATE_NONE) {
                u32 fail_res;

                ret = close_func(inst, &fail_res);
                if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) {
                        dev_err(inst->dev->dev, "%s close failed, device is still running\n",
                                name);
                        return -EBUSY;
                }
                if (ret && ret != -EIO) {
                        dev_err(inst->dev->dev, "%s close, fail: %d\n", name, ret);
                        return ret;
                }
        }

        wave5_cleanup_instance(inst, filp);

        return ret;
}

int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
                         const struct vb2_ops *ops)
{
        struct vpu_instance *inst = priv;
        int ret;

        src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
        src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->ops = ops;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
        src_vq->drv_priv = inst;
        src_vq->lock = &inst->dev->dev_lock;
        src_vq->dev = inst->dev->v4l2_dev.dev;
        ret = vb2_queue_init(src_vq);
        if (ret)
                return ret;

        dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->ops = ops;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
        dst_vq->drv_priv = inst;
        dst_vq->lock = &inst->dev->dev_lock;
        dst_vq->dev = inst->dev->v4l2_dev.dev;
        ret = vb2_queue_init(dst_vq);
        if (ret)
                return ret;

        return 0;
}

int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
{
        struct vpu_instance *inst = wave5_to_vpu_inst(fh);
        bool is_decoder = inst->type == VPU_INST_TYPE_DEC;

        dev_dbg(inst->dev->dev, "%s: [%s] type: %u id: %u | flags: %u\n", __func__,
                is_decoder ? "decoder" : "encoder", sub->type, sub->id, sub->flags);

        switch (sub->type) {
        case V4L2_EVENT_EOS:
                return v4l2_event_subscribe(fh, sub, 0, NULL);
        case V4L2_EVENT_SOURCE_CHANGE:
                if (is_decoder)
                        return v4l2_src_change_event_subscribe(fh, sub);
                return -EINVAL;
        case V4L2_EVENT_CTRL:
                return v4l2_ctrl_subscribe_event(fh, sub);
        default:
                return -EINVAL;
        }
}

int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
{
        struct vpu_instance *inst = file_to_vpu_inst(file);
        int i;

        f->fmt.pix_mp.width = inst->src_fmt.width;
        f->fmt.pix_mp.height = inst->src_fmt.height;
        f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
        f->fmt.pix_mp.field = inst->src_fmt.field;
        f->fmt.pix_mp.flags = inst->src_fmt.flags;
        f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
        for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
                f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline;
                f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage;
        }

        f->fmt.pix_mp.colorspace = inst->colorspace;
        f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
        f->fmt.pix_mp.quantization = inst->quantization;
        f->fmt.pix_mp.xfer_func = inst->xfer_func;

        return 0;
}

const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
                                            const struct vpu_format fmt_list[MAX_FMTS])
{
        unsigned int index;

        for (index = 0; index < MAX_FMTS; index++) {
                if (fmt_list[index].v4l2_pix_fmt == v4l2_pix_fmt)
                        return &fmt_list[index];
        }

        return NULL;
}

const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
                                                   const struct vpu_format fmt_list[MAX_FMTS])
{
        if (idx >= MAX_FMTS)
                return NULL;

        if (!fmt_list[idx].v4l2_pix_fmt)
                return NULL;

        return &fmt_list[idx];
}

enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type)
{
        switch (v4l2_pix_fmt) {
        case V4L2_PIX_FMT_H264:
                return type == VPU_INST_TYPE_DEC ? W_AVC_DEC : W_AVC_ENC;
        case V4L2_PIX_FMT_HEVC:
                return type == VPU_INST_TYPE_DEC ? W_HEVC_DEC : W_HEVC_ENC;
        default:
                return STD_UNKNOWN;
        }
}

void wave5_return_bufs(struct vb2_queue *q, u32 state)
{
        struct vpu_instance *inst = vb2_get_drv_priv(q);
        struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
        struct v4l2_ctrl_handler v4l2_ctrl_hdl = inst->v4l2_ctrl_hdl;
        struct vb2_v4l2_buffer *vbuf;

        for (;;) {
                if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
                        vbuf = v4l2_m2m_src_buf_remove(m2m_ctx);
                else
                        vbuf = v4l2_m2m_dst_buf_remove(m2m_ctx);
                if (!vbuf)
                        return;
                v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &v4l2_ctrl_hdl);
                v4l2_m2m_buf_done(vbuf, state);
        }
}

void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp,
                          int pix_fmt_type,
                          unsigned int width,
                          unsigned int height,
                          const struct v4l2_frmsize_stepwise *frmsize)
{
        v4l2_apply_frmsize_constraints(&width, &height, frmsize);

        if (pix_fmt_type == VPU_FMT_TYPE_CODEC) {
                pix_mp->width = width;
                pix_mp->height = height;
                pix_mp->num_planes = 1;
                pix_mp->plane_fmt[0].bytesperline = 0;
                pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_BS_SIZE(width, height),
                                                     pix_mp->plane_fmt[0].sizeimage);
        } else {
                v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, width, height);
        }
        pix_mp->flags = 0;
        pix_mp->field = V4L2_FIELD_NONE;
}