media: chips-media: wave5: Add the v4l2 layer
Add the decoder and encoder implementing the v4l2 API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config Signed-off-by: Sebastian Fricke <sebastian.fricke@collabora.com> Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Signed-off-by: Robert Beckett <bob.beckett@collabora.com> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
This commit is contained in:
parent
45d1a2b932
commit
9707a6254a
@ -3,3 +3,4 @@
|
|||||||
comment "Chips&Media media platform drivers"
|
comment "Chips&Media media platform drivers"
|
||||||
|
|
||||||
source "drivers/media/platform/chips-media/coda/Kconfig"
|
source "drivers/media/platform/chips-media/coda/Kconfig"
|
||||||
|
source "drivers/media/platform/chips-media/wave5/Kconfig"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
obj-y += coda/
|
obj-y += coda/
|
||||||
|
obj-y += wave5/
|
||||||
|
12
drivers/media/platform/chips-media/wave5/Kconfig
Normal file
12
drivers/media/platform/chips-media/wave5/Kconfig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
config VIDEO_WAVE_VPU
|
||||||
|
tristate "Chips&Media Wave Codec Driver"
|
||||||
|
depends on VIDEO_DEV
|
||||||
|
select VIDEOBUF2_DMA_CONTIG
|
||||||
|
select VIDEOBUF2_VMALLOC
|
||||||
|
select V4L2_MEM2MEM_DEV
|
||||||
|
help
|
||||||
|
Chips&Media stateful encoder and decoder driver.
|
||||||
|
The driver supports HEVC and H264 formats.
|
||||||
|
To compile this driver as modules, choose M here: the
|
||||||
|
modules will be called wave5.
|
10
drivers/media/platform/chips-media/wave5/Makefile
Normal file
10
drivers/media/platform/chips-media/wave5/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
obj-$(CONFIG_VIDEO_WAVE_VPU) += wave5.o
|
||||||
|
wave5-objs += wave5-hw.o \
|
||||||
|
wave5-vpuapi.o \
|
||||||
|
wave5-vdi.o \
|
||||||
|
wave5-vpu-dec.o \
|
||||||
|
wave5-vpu.o \
|
||||||
|
wave5-vpu-enc.o \
|
||||||
|
wave5-helper.o
|
213
drivers/media/platform/chips-media/wave5/wave5-helper.c
Normal file
213
drivers/media/platform/chips-media/wave5/wave5-helper.c
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
if (list_is_singular(&inst->list))
|
||||||
|
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);
|
||||||
|
|
||||||
|
v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
|
||||||
|
if (inst->state != VPU_INST_STATE_NONE) {
|
||||||
|
u32 fail_res;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
31
drivers/media/platform/chips-media/wave5/wave5-helper.h
Normal file
31
drivers/media/platform/chips-media/wave5/wave5-helper.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||||
|
/*
|
||||||
|
* Wave5 series multi-standard codec IP - basic types
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WAVE_HELPER_H__
|
||||||
|
#define __WAVE_HELPER_H__
|
||||||
|
|
||||||
|
#include "wave5-vpu.h"
|
||||||
|
|
||||||
|
#define FMT_TYPES 2
|
||||||
|
#define MAX_FMTS 12
|
||||||
|
|
||||||
|
const char *state_to_str(enum vpu_instance_state state);
|
||||||
|
void wave5_cleanup_instance(struct vpu_instance *inst);
|
||||||
|
int wave5_vpu_release_device(struct file *filp,
|
||||||
|
int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
|
||||||
|
char *name);
|
||||||
|
int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
|
||||||
|
const struct vb2_ops *ops);
|
||||||
|
int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);
|
||||||
|
int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f);
|
||||||
|
const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
|
||||||
|
const struct vpu_format fmt_list[MAX_FMTS]);
|
||||||
|
const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
|
||||||
|
const struct vpu_format fmt_list[MAX_FMTS]);
|
||||||
|
enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type);
|
||||||
|
void wave5_return_bufs(struct vb2_queue *q, u32 state);
|
||||||
|
#endif
|
1932
drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
Normal file
1932
drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
Normal file
File diff suppressed because it is too large
Load Diff
1794
drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
Normal file
1794
drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
Normal file
File diff suppressed because it is too large
Load Diff
291
drivers/media/platform/chips-media/wave5/wave5-vpu.c
Normal file
291
drivers/media/platform/chips-media/wave5/wave5-vpu.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||||
|
/*
|
||||||
|
* Wave5 series multi-standard codec IP - platform driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
|
||||||
|
*/
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include "wave5-vpu.h"
|
||||||
|
#include "wave5-regdefine.h"
|
||||||
|
#include "wave5-vpuconfig.h"
|
||||||
|
#include "wave5.h"
|
||||||
|
|
||||||
|
#define VPU_PLATFORM_DEVICE_NAME "vdec"
|
||||||
|
#define VPU_CLK_NAME "vcodec"
|
||||||
|
|
||||||
|
#define WAVE5_IS_ENC BIT(0)
|
||||||
|
#define WAVE5_IS_DEC BIT(1)
|
||||||
|
|
||||||
|
struct wave5_match_data {
|
||||||
|
int flags;
|
||||||
|
const char *fw_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = wait_for_completion_timeout(&inst->irq_done,
|
||||||
|
msecs_to_jiffies(timeout));
|
||||||
|
if (!ret)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
reinit_completion(&inst->irq_done);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
u32 seq_done;
|
||||||
|
u32 cmd_done;
|
||||||
|
u32 irq_reason;
|
||||||
|
struct vpu_instance *inst;
|
||||||
|
struct vpu_device *dev = dev_id;
|
||||||
|
|
||||||
|
if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
|
||||||
|
irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
|
||||||
|
wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
|
||||||
|
wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
|
||||||
|
|
||||||
|
list_for_each_entry(inst, &dev->instances, list) {
|
||||||
|
seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
|
||||||
|
cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
|
||||||
|
|
||||||
|
if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
|
||||||
|
irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
|
||||||
|
if (seq_done & BIT(inst->id)) {
|
||||||
|
seq_done &= ~BIT(inst->id);
|
||||||
|
wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO,
|
||||||
|
seq_done);
|
||||||
|
complete(&inst->irq_done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irq_reason & BIT(INT_WAVE5_DEC_PIC) ||
|
||||||
|
irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
|
||||||
|
if (cmd_done & BIT(inst->id)) {
|
||||||
|
cmd_done &= ~BIT(inst->id);
|
||||||
|
wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
|
||||||
|
cmd_done);
|
||||||
|
inst->ops->finish_process(inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wave5_vpu_clear_interrupt(inst, irq_reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
|
||||||
|
u32 *revision)
|
||||||
|
{
|
||||||
|
const struct firmware *fw;
|
||||||
|
int ret;
|
||||||
|
unsigned int product_id;
|
||||||
|
|
||||||
|
ret = request_firmware(&fw, fw_name, dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "request_firmware, fail: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wave5_vpu_init_with_bitcode(dev, (u8 *)fw->data, fw->size);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "vpu_init_with_bitcode, fail: %d\n", ret);
|
||||||
|
release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
release_firmware(fw);
|
||||||
|
|
||||||
|
ret = wave5_vpu_get_version_info(dev, revision, &product_id);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "vpu_get_version_info fail: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "%s: enum product_id: %08x, fw revision: %u\n",
|
||||||
|
__func__, product_id, *revision);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wave5_vpu_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct vpu_device *dev;
|
||||||
|
const struct wave5_match_data *match_data;
|
||||||
|
u32 fw_revision;
|
||||||
|
|
||||||
|
match_data = device_get_match_data(&pdev->dev);
|
||||||
|
if (!match_data) {
|
||||||
|
dev_err(&pdev->dev, "missing device match data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* physical addresses limited to 32 bits */
|
||||||
|
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to set DMA mask: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||||
|
if (!dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev->vdb_register = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (IS_ERR(dev->vdb_register))
|
||||||
|
return PTR_ERR(dev->vdb_register);
|
||||||
|
ida_init(&dev->inst_ida);
|
||||||
|
|
||||||
|
mutex_init(&dev->dev_lock);
|
||||||
|
mutex_init(&dev->hw_lock);
|
||||||
|
dev_set_drvdata(&pdev->dev, dev);
|
||||||
|
dev->dev = &pdev->dev;
|
||||||
|
|
||||||
|
ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks);
|
||||||
|
|
||||||
|
/* continue without clock, assume externally managed */
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_warn(&pdev->dev, "Getting clocks, fail: %d\n", ret);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
dev->num_clks = ret;
|
||||||
|
|
||||||
|
ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_property_read_u32(pdev->dev.of_node, "sram-size",
|
||||||
|
&dev->sram_size);
|
||||||
|
if (ret) {
|
||||||
|
dev_warn(&pdev->dev, "sram-size not found\n");
|
||||||
|
dev->sram_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
|
||||||
|
if (!dev->sram_pool)
|
||||||
|
dev_warn(&pdev->dev, "sram node not found\n");
|
||||||
|
|
||||||
|
dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER);
|
||||||
|
ret = wave5_vdi_init(&pdev->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "wave5_vdi_init, fail: %d\n", ret);
|
||||||
|
goto err_clk_dis;
|
||||||
|
}
|
||||||
|
dev->product = wave5_vpu_get_product_id(dev);
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&dev->instances);
|
||||||
|
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret);
|
||||||
|
goto err_vdi_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_data->flags & WAVE5_IS_DEC) {
|
||||||
|
ret = wave5_vpu_dec_register_device(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "wave5_vpu_dec_register_device, fail: %d\n", ret);
|
||||||
|
goto err_v4l2_unregister;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match_data->flags & WAVE5_IS_ENC) {
|
||||||
|
ret = wave5_vpu_enc_register_device(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "wave5_vpu_enc_register_device, fail: %d\n", ret);
|
||||||
|
goto err_dec_unreg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (dev->irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to get irq resource\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto err_enc_unreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL,
|
||||||
|
wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
|
||||||
|
goto err_enc_unreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret);
|
||||||
|
goto err_enc_unreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "Added wave5 driver with caps: %s %s\n",
|
||||||
|
(match_data->flags & WAVE5_IS_ENC) ? "'ENCODE'" : "",
|
||||||
|
(match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : "");
|
||||||
|
dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code);
|
||||||
|
dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_enc_unreg:
|
||||||
|
if (match_data->flags & WAVE5_IS_ENC)
|
||||||
|
wave5_vpu_enc_unregister_device(dev);
|
||||||
|
err_dec_unreg:
|
||||||
|
if (match_data->flags & WAVE5_IS_DEC)
|
||||||
|
wave5_vpu_dec_unregister_device(dev);
|
||||||
|
err_v4l2_unregister:
|
||||||
|
v4l2_device_unregister(&dev->v4l2_dev);
|
||||||
|
err_vdi_release:
|
||||||
|
wave5_vdi_release(&pdev->dev);
|
||||||
|
err_clk_dis:
|
||||||
|
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wave5_vpu_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
|
mutex_destroy(&dev->dev_lock);
|
||||||
|
mutex_destroy(&dev->hw_lock);
|
||||||
|
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
|
||||||
|
wave5_vpu_enc_unregister_device(dev);
|
||||||
|
wave5_vpu_dec_unregister_device(dev);
|
||||||
|
v4l2_device_unregister(&dev->v4l2_dev);
|
||||||
|
wave5_vdi_release(&pdev->dev);
|
||||||
|
ida_destroy(&dev->inst_ida);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wave5_match_data ti_wave521c_data = {
|
||||||
|
.flags = WAVE5_IS_ENC | WAVE5_IS_DEC,
|
||||||
|
.fw_name = "cnm/wave521c_k3_codec_fw.bin",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id wave5_dt_ids[] = {
|
||||||
|
{ .compatible = "ti,k3-j721s2-wave521c", .data = &ti_wave521c_data },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, wave5_dt_ids);
|
||||||
|
|
||||||
|
static struct platform_driver wave5_vpu_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = VPU_PLATFORM_DEVICE_NAME,
|
||||||
|
.of_match_table = of_match_ptr(wave5_dt_ids),
|
||||||
|
},
|
||||||
|
.probe = wave5_vpu_probe,
|
||||||
|
.remove = wave5_vpu_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(wave5_vpu_driver);
|
||||||
|
MODULE_DESCRIPTION("chips&media VPU V4L2 driver");
|
||||||
|
MODULE_LICENSE("Dual BSD/GPL");
|
83
drivers/media/platform/chips-media/wave5/wave5-vpu.h
Normal file
83
drivers/media/platform/chips-media/wave5/wave5-vpu.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||||
|
/*
|
||||||
|
* Wave5 series multi-standard codec IP - basic types
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
|
||||||
|
*/
|
||||||
|
#ifndef __VPU_DRV_H__
|
||||||
|
#define __VPU_DRV_H__
|
||||||
|
|
||||||
|
#include <media/v4l2-ctrls.h>
|
||||||
|
#include <media/v4l2-ioctl.h>
|
||||||
|
#include <media/v4l2-event.h>
|
||||||
|
#include <media/v4l2-fh.h>
|
||||||
|
#include <media/videobuf2-v4l2.h>
|
||||||
|
#include <media/videobuf2-dma-contig.h>
|
||||||
|
#include <media/videobuf2-vmalloc.h>
|
||||||
|
#include "wave5-vpuconfig.h"
|
||||||
|
#include "wave5-vpuapi.h"
|
||||||
|
|
||||||
|
#define VPU_BUF_SYNC_TO_DEVICE 0
|
||||||
|
#define VPU_BUF_SYNC_FROM_DEVICE 1
|
||||||
|
|
||||||
|
struct vpu_src_buffer {
|
||||||
|
struct v4l2_m2m_buffer v4l2_m2m_buf;
|
||||||
|
struct list_head list;
|
||||||
|
bool consumed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vpu_dst_buffer {
|
||||||
|
struct v4l2_m2m_buffer v4l2_m2m_buf;
|
||||||
|
bool display;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum vpu_fmt_type {
|
||||||
|
VPU_FMT_TYPE_CODEC = 0,
|
||||||
|
VPU_FMT_TYPE_RAW = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vpu_format {
|
||||||
|
unsigned int v4l2_pix_fmt;
|
||||||
|
unsigned int max_width;
|
||||||
|
unsigned int min_width;
|
||||||
|
unsigned int max_height;
|
||||||
|
unsigned int min_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh)
|
||||||
|
{
|
||||||
|
return container_of(vfh, struct vpu_instance, v4l2_fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct vpu_instance *wave5_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl)
|
||||||
|
{
|
||||||
|
return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct vpu_src_buffer *wave5_to_vpu_src_buf(struct vb2_v4l2_buffer *vbuf)
|
||||||
|
{
|
||||||
|
return container_of(vbuf, struct vpu_src_buffer, v4l2_m2m_buf.vb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct vpu_dst_buffer *wave5_to_vpu_dst_buf(struct vb2_v4l2_buffer *vbuf)
|
||||||
|
{
|
||||||
|
return container_of(vbuf, struct vpu_dst_buffer, v4l2_m2m_buf.vb);
|
||||||
|
}
|
||||||
|
|
||||||
|
int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout);
|
||||||
|
|
||||||
|
int wave5_vpu_dec_register_device(struct vpu_device *dev);
|
||||||
|
void wave5_vpu_dec_unregister_device(struct vpu_device *dev);
|
||||||
|
int wave5_vpu_enc_register_device(struct vpu_device *dev);
|
||||||
|
void wave5_vpu_enc_unregister_device(struct vpu_device *dev);
|
||||||
|
static inline bool wave5_vpu_both_queues_are_streaming(struct vpu_instance *inst)
|
||||||
|
{
|
||||||
|
struct vb2_queue *vq_cap =
|
||||||
|
v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
|
||||||
|
struct vb2_queue *vq_out =
|
||||||
|
v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
||||||
|
|
||||||
|
return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user