6aa0829104
Add initial support for the Wave515 multi-decoder IP. For now it is only able to decode HEVC Main/Main10 profile videos into YUV420. This was tested on FPGA prototype, so wave5_dt_ids[] was not expanded. Users of the real hardware with Wave515 IP will have to * provide firmware specific to their SoC * add struct wave5_match_data like this: static const struct wave5_match_data platform_name_wave515_data = { .flags = WAVE5_IS_DEC, .fw_name = "cnm/wave515_platform_name_fw.bin", .sram_size = (71 * 1024), }; * add item to wave5_dt_ids[] like this: { .compatible = "vendor,soc-wave515", .data = &platform_name_wave515_data, }, * describe new compatible in Documentation/devicetree/bindings/media/cnm,wave521c.yaml Signed-off-by: Ivan Bornyakov <brnkv.i1@gmail.com> Signed-off-by: Sebastian Fricke <sebastian.fricke@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
233 lines
6.1 KiB
C
233 lines
6.1 KiB
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"
|
|
|
|
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";
|
|
}
|
|
}
|
|
|
|
void wave5_cleanup_instance(struct vpu_instance *inst)
|
|
{
|
|
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);
|
|
v4l2_fh_exit(&inst->v4l2_fh);
|
|
}
|
|
list_del_init(&inst->list);
|
|
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 = wave5_to_vpu_inst(filp->private_data);
|
|
struct vpu_device *dev = inst->dev;
|
|
int ret = 0;
|
|
|
|
v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
|
|
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);
|
|
if (dev->irq < 0) {
|
|
ret = mutex_lock_interruptible(&dev->dev_lock);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (list_empty(&dev->instances)) {
|
|
dev_dbg(dev->dev, "Disabling the hrtimer\n");
|
|
hrtimer_cancel(&dev->hrtimer);
|
|
}
|
|
|
|
mutex_unlock(&dev->dev_lock);
|
|
}
|
|
|
|
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 = wave5_to_vpu_inst(fh);
|
|
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);
|
|
}
|
|
}
|