Hi Paul, A modest review round. Hope it helps! I will be reviewing the driver some more later. On 18 June 2018 at 11:58, Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> wrote: > This introduces the Sunxi-Cedrus VPU driver that supports the VPU found > in Allwinner SoCs, also known as Video Engine. It is implemented through > a v4l2 m2m decoder device and a media device (used for media requests). > So far, it only supports MPEG2 decoding. > > Since this VPU is stateless, synchronization with media requests is > required in order to ensure consistency between frame headers that > contain metadata about the frame to process and the raw slice data that > is used to generate the frame. > > This driver was made possible thanks to the long-standing effort > carried out by the linux-sunxi community in the interest of reverse > engineering, documenting and implementing support for Allwinner VPU. > > Signed-off-by: Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > > create mode 100644 drivers/media/platform/sunxi/Kconfig > create mode 100644 drivers/media/platform/sunxi/Makefile > create mode 100644 drivers/media/platform/sunxi/cedrus/Kconfig > create mode 100644 drivers/media/platform/sunxi/cedrus/Makefile > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus.c > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus.h > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_dec.c > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_dec.h > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_hw.c > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_hw.h > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.c > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.h > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_regs.h > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_video.c > create mode 100644 drivers/media/platform/sunxi/cedrus/cedrus_video.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 9c125f705f78..b47dee397475 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -656,6 +656,13 @@ L: linux-crypto@xxxxxxxxxxxxxxx > S: Maintained > F: drivers/crypto/sunxi-ss/ > > +ALLWINNER VPU DRIVER > +M: Maxime Ripard <maxime.ripard@xxxxxxxxxxx> > +M: Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > +L: linux-media@xxxxxxxxxxxxxxx > +S: Maintained > +F: drivers/media/platform/sunxi/cedrus/ > + > ALPHA PORT > M: Richard Henderson <rth@xxxxxxxxxxx> > M: Ivan Kokshaysky <ink@xxxxxxxxxxxxxxxxxxxx> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig > index c7a1cf8a1b01..26da88bcd87b 100644 > --- a/drivers/media/platform/Kconfig > +++ b/drivers/media/platform/Kconfig > @@ -31,6 +31,8 @@ source "drivers/media/platform/davinci/Kconfig" > > source "drivers/media/platform/omap/Kconfig" > > +source "drivers/media/platform/sunxi/Kconfig" > + > config VIDEO_SH_VOU > tristate "SuperH VOU video output driver" > depends on MEDIA_CAMERA_SUPPORT > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile > index 932515df4477..01ac00adcbc4 100644 > --- a/drivers/media/platform/Makefile > +++ b/drivers/media/platform/Makefile > @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/ > obj-y += omap/ > > obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ > +obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ > > obj-$(CONFIG_VIDEO_XILINX) += xilinx/ > > diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig > new file mode 100644 > index 000000000000..a639b0949826 > --- /dev/null > +++ b/drivers/media/platform/sunxi/Kconfig > @@ -0,0 +1,15 @@ > +config VIDEO_SUNXI > + bool "Allwinner sunXi family Video Devices" > + depends on ARCH_SUNXI You could add || COMPILE_TEST, to get covered by build tests. > + help > + If you have an Allwinner SoC based on the sunXi family, say Y. > + > + Note that this option doesn't include new drivers in the > + kernel: saying N will just cause Kconfig to skip all the > + questions about Allwinner media devices. > + > +if VIDEO_SUNXI > + > +source "drivers/media/platform/sunxi/cedrus/Kconfig" > + > +endif > diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile > new file mode 100644 > index 000000000000..cee2846c3ecf > --- /dev/null > +++ b/drivers/media/platform/sunxi/Makefile > @@ -0,0 +1 @@ > +obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += cedrus/ > diff --git a/drivers/media/platform/sunxi/cedrus/Kconfig b/drivers/media/platform/sunxi/cedrus/Kconfig > new file mode 100644 > index 000000000000..870a4b64a45c > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/Kconfig > @@ -0,0 +1,13 @@ > +config VIDEO_SUNXI_CEDRUS > + tristate "Allwinner Cedrus VPU driver" > + depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER > + depends on HAS_DMA > + select VIDEOBUF2_DMA_CONTIG > + select MEDIA_REQUEST_API > + select V4L2_MEM2MEM_DEV > + help > + Support for the VPU found in Allwinner SoCs, also known as the Cedar > + video engine. > + > + To compile this driver as a module, choose M here: the module > + will be called cedrus. > diff --git a/drivers/media/platform/sunxi/cedrus/Makefile b/drivers/media/platform/sunxi/cedrus/Makefile > new file mode 100644 > index 000000000000..632a0be90ed7 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/Makefile > @@ -0,0 +1,3 @@ > +obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += cedrus.o > + > +cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o cedrus_mpeg2.o > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus.c b/drivers/media/platform/sunxi/cedrus/cedrus.c > new file mode 100644 > index 000000000000..1718db1b549b > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus.c > @@ -0,0 +1,327 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#include <linux/platform_device.h> > +#include <linux/module.h> > +#include <linux/of.h> > + > +#include <media/videobuf2-dma-contig.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-mem2mem.h> > + > +#include "cedrus.h" > +#include "cedrus_video.h" > +#include "cedrus_dec.h" > +#include "cedrus_hw.h" > + > +static int cedrus_s_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct cedrus_ctx *ctx = > + container_of(ctrl->handler, struct cedrus_ctx, hdl); > + struct cedrus_dev *dev = ctx->dev; > + > + switch (ctrl->id) { > + case V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_HEADER: > + /* This is kept in memory and used directly. */ > + break; > + default: > + v4l2_err(&dev->v4l2_dev, "Invalid control to set\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static const struct v4l2_ctrl_ops cedrus_ctrl_ops = { > + .s_ctrl = cedrus_s_ctrl, > +}; > + > +static const struct cedrus_control controls[] = { > + [CEDRUS_CTRL_DEC_MPEG2_SLICE_HEADER] = { > + .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_HEADER, > + .elem_size = sizeof(struct v4l2_ctrl_mpeg2_slice_header), > + }, > +}; > + > +static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) > +{ > + struct v4l2_ctrl_handler *hdl = &ctx->hdl; > + unsigned int num_ctrls = ARRAY_SIZE(controls); > + unsigned int i; > + > + v4l2_ctrl_handler_init(hdl, num_ctrls); > + if (hdl->error) { > + v4l2_err(&dev->v4l2_dev, > + "Failed to initialize control handler\n"); > + return hdl->error; > + } > + > + for (i = 0; i < num_ctrls; i++) { > + struct v4l2_ctrl_config cfg = { 0 }; > + > + cfg.ops = &cedrus_ctrl_ops; > + cfg.elem_size = controls[i].elem_size; > + cfg.id = controls[i].id; > + > + ctx->ctrls[i] = v4l2_ctrl_new_custom(hdl, &cfg, NULL); > + if (hdl->error) { > + v4l2_err(&dev->v4l2_dev, > + "Failed to create new custom control\n"); > + > + v4l2_ctrl_handler_free(hdl); > + return hdl->error; > + } > + } > + > + ctx->fh.ctrl_handler = hdl; > + v4l2_ctrl_handler_setup(hdl); > + > + return 0; > +} > + > +static int cedrus_open(struct file *file) > +{ > + struct cedrus_dev *dev = video_drvdata(file); > + struct cedrus_ctx *ctx = NULL; > + int ret; > + > + if (mutex_lock_interruptible(&dev->dev_mutex)) > + return -ERESTARTSYS; > + > + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); > + if (!ctx) { > + mutex_unlock(&dev->dev_mutex); > + return -ENOMEM; > + } > + > + INIT_WORK(&ctx->run_work, cedrus_device_work); > + This cedrus_device_work looks odd, but I think it's already cleaned up in your WIP github branch. > + INIT_LIST_HEAD(&ctx->src_list); > + INIT_LIST_HEAD(&ctx->dst_list); > + > + v4l2_fh_init(&ctx->fh, video_devdata(file)); > + file->private_data = &ctx->fh; > + ctx->dev = dev; > + > + ret = cedrus_init_ctrls(dev, ctx); > + if (ret) > + goto err_free; > + > + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, > + &cedrus_queue_init); > + if (IS_ERR(ctx->fh.m2m_ctx)) { > + ret = PTR_ERR(ctx->fh.m2m_ctx); > + goto err_ctrls; > + } > + > + v4l2_fh_add(&ctx->fh); > + > + mutex_unlock(&dev->dev_mutex); > + > + return 0; > + > +err_ctrls: > + v4l2_ctrl_handler_free(&ctx->hdl); > +err_free: > + kfree(ctx); > + mutex_unlock(&dev->dev_mutex); > + > + return ret; > +} > + > +static int cedrus_release(struct file *file) > +{ > + struct cedrus_dev *dev = video_drvdata(file); > + struct cedrus_ctx *ctx = container_of(file->private_data, > + struct cedrus_ctx, fh); > + > + mutex_lock(&dev->dev_mutex); > + > + v4l2_fh_del(&ctx->fh); > + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); > + > + v4l2_ctrl_handler_free(&ctx->hdl); > + > + v4l2_fh_exit(&ctx->fh); > + v4l2_fh_exit(&ctx->fh); > + > + kfree(ctx); > + > + mutex_unlock(&dev->dev_mutex); > + > + return 0; > +} > + > +static const struct v4l2_file_operations cedrus_fops = { > + .owner = THIS_MODULE, > + .open = cedrus_open, > + .release = cedrus_release, > + .poll = v4l2_m2m_fop_poll, > + .unlocked_ioctl = video_ioctl2, > + .mmap = v4l2_m2m_fop_mmap, > +}; > + > +static const struct video_device cedrus_video_device = { > + .name = CEDRUS_NAME, > + .vfl_dir = VFL_DIR_M2M, > + .fops = &cedrus_fops, > + .ioctl_ops = &cedrus_ioctl_ops, > + .minor = -1, > + .release = video_device_release_empty, > +}; > + > +static const struct v4l2_m2m_ops cedrus_m2m_ops = { > + .device_run = cedrus_device_run, > + .job_abort = cedrus_job_abort, > +}; > + > +static const struct media_device_ops cedrus_m2m_media_ops = { > + .req_validate = vb2_request_validate, > + .req_queue = vb2_m2m_request_queue, > +}; > + > +static int cedrus_probe(struct platform_device *pdev) > +{ > + struct cedrus_dev *dev; > + struct video_device *vfd; > + int ret; > + > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + dev->dev = &pdev->dev; > + dev->pdev = pdev; > + > + ret = cedrus_hw_probe(dev); > + if (ret) { > + dev_err(&pdev->dev, "Failed to probe hardware\n"); > + return ret; > + } > + > + mutex_init(&dev->dev_mutex); > + spin_lock_init(&dev->irq_lock); > + > + dev->vfd = cedrus_video_device; > + vfd = &dev->vfd; > + vfd->lock = &dev->dev_mutex; > + vfd->v4l2_dev = &dev->v4l2_dev; > + > + dev->mdev.dev = &pdev->dev; > + strlcpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model)); > + > + media_device_init(&dev->mdev); > + dev->mdev.ops = &cedrus_m2m_media_ops; > + dev->v4l2_dev.mdev = &dev->mdev; > + dev->pad[0].flags = MEDIA_PAD_FL_SINK; > + dev->pad[1].flags = MEDIA_PAD_FL_SOURCE; > + > + ret = media_entity_pads_init(&vfd->entity, 2, dev->pad); > + if (ret) { > + dev_err(&pdev->dev, "Failed to initialize media entity pads\n"); > + return ret; > + } > + > + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); > + if (ret) { > + dev_err(&pdev->dev, "Failed to register V4L2 device\n"); > + return ret; > + } > + > + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); > + goto err_v4l2; > + } > + > + video_set_drvdata(vfd, dev); > + snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name); > + > + v4l2_info(&dev->v4l2_dev, > + "Device registered as /dev/video%d\n", vfd->num); > + > + platform_set_drvdata(pdev, dev); > + > + dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops); > + if (IS_ERR(dev->m2m_dev)) { > + v4l2_err(&dev->v4l2_dev, > + "Failed to initialize V4L2 M2M device\n"); > + ret = PTR_ERR(dev->m2m_dev); > + goto err_video; > + } > + > + ret = media_device_register(&dev->mdev); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to register media device\n"); > + goto err_m2m; > + } > + > + return 0; > + > +err_m2m: > + v4l2_m2m_release(dev->m2m_dev); > +err_video: > + video_unregister_device(&dev->vfd); > +err_v4l2: > + v4l2_device_unregister(&dev->v4l2_dev); > + > + return ret; > +} > + > +static int cedrus_remove(struct platform_device *pdev) > +{ > + struct cedrus_dev *dev = platform_get_drvdata(pdev); > + > + v4l2_info(&dev->v4l2_dev, "Removing " CEDRUS_NAME); > + > + if (media_devnode_is_registered(dev->mdev.devnode)) { > + media_device_unregister(&dev->mdev); > + media_device_cleanup(&dev->mdev); > + } > + > + v4l2_m2m_release(dev->m2m_dev); > + video_unregister_device(&dev->vfd); > + v4l2_device_unregister(&dev->v4l2_dev); > + cedrus_hw_remove(dev); > + > + return 0; > +} > + > +#ifdef CONFIG_OF > +static const struct of_device_id of_cedrus_match[] = { > + { .compatible = "allwinner,sun4i-a10-video-engine" }, > + { .compatible = "allwinner,sun5i-a13-video-engine" }, > + { .compatible = "allwinner,sun7i-a20-video-engine" }, > + { .compatible = "allwinner,sun8i-a33-video-engine" }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, of_cedrus_match); > +#endif > + > +static struct platform_driver cedrus_driver = { > + .probe = cedrus_probe, > + .remove = cedrus_remove, > + .driver = { > + .name = CEDRUS_NAME, > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(of_cedrus_match), > + }, > +}; > +module_platform_driver(cedrus_driver); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_AUTHOR("Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("Sunxi-Cedrus VPU driver"); > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus.h b/drivers/media/platform/sunxi/cedrus/cedrus.h > new file mode 100644 > index 000000000000..106bda44a3c3 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus.h > @@ -0,0 +1,117 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#ifndef _CEDRUS_H_ > +#define _CEDRUS_H_ > + > +#include <linux/platform_device.h> > + > +#include <media/videobuf2-v4l2.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-ctrls.h> > + > +#define CEDRUS_NAME "cedrus" > + > +enum cedrus_control_id { > + CEDRUS_CTRL_DEC_MPEG2_SLICE_HEADER = 0, > + CEDRUS_CTRL_MAX, > +}; > + > +struct cedrus_control { > + u32 id; > + u32 elem_size; > +}; > + > +struct cedrus_fmt { > + u32 fourcc; > + int depth; > + u32 types; > + unsigned int num_planes; > +}; > + > +struct cedrus_mpeg2_run { > + const struct v4l2_ctrl_mpeg2_slice_header *hdr; > +}; > + > +struct cedrus_run { > + struct vb2_v4l2_buffer *src; > + struct vb2_v4l2_buffer *dst; > + > + union { > + struct cedrus_mpeg2_run mpeg2; > + }; > +}; > + > +struct cedrus_ctx { > + struct v4l2_fh fh; > + struct cedrus_dev *dev; > + > + struct cedrus_fmt *vpu_src_fmt; > + struct v4l2_pix_format_mplane src_fmt; > + struct cedrus_fmt *vpu_dst_fmt; > + struct v4l2_pix_format_mplane dst_fmt; > + > + struct v4l2_ctrl_handler hdl; > + struct v4l2_ctrl *ctrls[CEDRUS_CTRL_MAX]; > + > + struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME]; > + > + int job_abort; > + > + struct work_struct try_schedule_work; > + struct work_struct run_work; > + struct list_head src_list; > + struct list_head dst_list; > +}; > + > +struct cedrus_buffer { > + struct vb2_v4l2_buffer vb; > + enum vb2_buffer_state state; > + struct list_head list; > +}; > + > +struct cedrus_dev { > + struct v4l2_device v4l2_dev; > + struct video_device vfd; > + struct media_device mdev; > + struct media_pad pad[2]; > + struct platform_device *pdev; > + struct device *dev; > + struct v4l2_m2m_dev *m2m_dev; > + > + /* Device file mutex */ > + struct mutex dev_mutex; > + /* Interrupt spinlock */ > + spinlock_t irq_lock; > + > + void __iomem *base; > + > + struct clk *mod_clk; > + struct clk *ahb_clk; > + struct clk *ram_clk; > + > + struct reset_control *rstc; > +}; > + > +static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val) > +{ > + writel(val, dev->base + reg); > +} > + > +static inline u32 cedrus_read(struct cedrus_dev *dev, u32 reg) > +{ > + return readl(dev->base + reg); > +} > + > +#endif > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_dec.c b/drivers/media/platform/sunxi/cedrus/cedrus_dec.c > new file mode 100644 > index 000000000000..bd9727ae9f63 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_dec.c > @@ -0,0 +1,170 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#include <media/videobuf2-dma-contig.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-event.h> > +#include <media/v4l2-mem2mem.h> > + > +#include "cedrus.h" > +#include "cedrus_mpeg2.h" > +#include "cedrus_dec.h" > +#include "cedrus_hw.h" > + > +static inline void *get_ctrl_ptr(struct cedrus_ctx *ctx, > + enum cedrus_control_id id) > +{ > + struct v4l2_ctrl *ctrl = ctx->ctrls[id]; > + > + return ctrl->p_cur.p; > +} > + > +void cedrus_device_work(struct work_struct *work) > +{ > + struct cedrus_ctx *ctx = container_of(work, > + struct cedrus_ctx, run_work); > + struct cedrus_dev *dev = ctx->dev; > + struct cedrus_buffer *buffer_entry; > + struct vb2_v4l2_buffer *src_buf, *dst_buf; > + unsigned long flags; > + > + spin_lock_irqsave(&ctx->dev->irq_lock, flags); > + > + if (list_empty(&ctx->src_list) || list_empty(&ctx->dst_list)) { > + v4l2_err(&dev->v4l2_dev, > + "Empty source and/or destination buffer lists\n"); > + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); > + return; > + } > + > + buffer_entry = list_last_entry(&ctx->src_list, struct cedrus_buffer, list); > + list_del(ctx->src_list.prev); > + > + src_buf = &buffer_entry->vb; > + v4l2_m2m_buf_done(src_buf, buffer_entry->state); > + > + buffer_entry = list_last_entry(&ctx->dst_list, struct cedrus_buffer, list); > + list_del(ctx->dst_list.prev); > + > + dst_buf = &buffer_entry->vb; > + v4l2_m2m_buf_done(dst_buf, buffer_entry->state); > + > + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); > + > + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); > +} > + > +void cedrus_device_run(void *priv) > +{ > + struct cedrus_ctx *ctx = priv; > + struct cedrus_dev *dev = ctx->dev; > + struct cedrus_run run = { 0 }; > + struct media_request *src_req; > + unsigned long flags; > + > + run.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); > + if (!run.src) { > + v4l2_err(&dev->v4l2_dev, "No source buffer to prepare\n"); I believe it's a mem2mem core nasty bug if you can't get a source or destination buffer in .device_run. Perhaps catch it with WARN or BUG? Or just let the kernel burn, like other drivers do :-) > + return; > + } > + > + run.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); > + if (!run.dst) { > + v4l2_err(&dev->v4l2_dev, "No destination buffer to prepare\n"); > + return; > + } > + > + /* Apply request(s) controls if needed. */ > + src_req = run.src->vb2_buf.req_obj.req; > + > + if (src_req) > + v4l2_ctrl_request_setup(src_req, &ctx->hdl); > + > + ctx->job_abort = 0; > + > + spin_lock_irqsave(&ctx->dev->irq_lock, flags); > + > + switch (ctx->vpu_src_fmt->fourcc) { > + case V4L2_PIX_FMT_MPEG2_SLICE: > + if (!ctx->ctrls[CEDRUS_CTRL_DEC_MPEG2_SLICE_HEADER]) { > + v4l2_err(&dev->v4l2_dev, > + "Invalid MPEG2 frame header control\n"); > + ctx->job_abort = 1; > + goto unlock_complete; > + } > + > + run.mpeg2.hdr = get_ctrl_ptr(ctx, CEDRUS_CTRL_DEC_MPEG2_SLICE_HEADER); > + cedrus_mpeg2_setup(ctx, &run); > + break; > + > + default: > + ctx->job_abort = 1; > + } > + > +unlock_complete: > + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); > + > + /* Complete request(s) controls if needed. */ > + > + if (src_req) > + v4l2_ctrl_request_complete(src_req, &ctx->hdl); > + > + spin_lock_irqsave(&ctx->dev->irq_lock, flags); > + > + if (!ctx->job_abort) { > + if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG2_SLICE) > + cedrus_mpeg2_trigger(ctx); > + } else { > + v4l2_m2m_buf_done(run.src, VB2_BUF_STATE_ERROR); > + v4l2_m2m_buf_done(run.dst, VB2_BUF_STATE_ERROR); > + } > + > + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); > + > + if (ctx->job_abort) > + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); > +} > + > +void cedrus_job_abort(void *priv) > +{ > + struct cedrus_ctx *ctx = priv; > + struct vb2_v4l2_buffer *src_buf, *dst_buf; > + unsigned long flags; > + > + ctx->job_abort = 1; > + If you can't actually cancel the DMA operation, then perhaps you want to simply not define .job_abort, and wait for the running operation (if any) to finish. See https://www.mail-archive.com/linux-media@xxxxxxxxxxxxxxx/msg132881.html It should also cleanup your impementation. The whole ctx->job_abort dance looks kinda cumbersome. > + /* > + * V4L2 M2M and request API cleanup is done here while hardware state > + * cleanup is done in the interrupt context. Doing all the cleanup in > + * the interrupt context is a bit risky, since the job_abort call might > + * originate from the release hook, where interrupts have already been > + * disabled. > + */ > + > + spin_lock_irqsave(&ctx->dev->irq_lock, flags); > + > + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); > + if (src_buf) > + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); > + > + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); > + if (dst_buf) > + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); > + > + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); > + > + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); > +} > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_dec.h b/drivers/media/platform/sunxi/cedrus/cedrus_dec.h > new file mode 100644 > index 000000000000..b38812136504 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_dec.h > @@ -0,0 +1,27 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#ifndef _CEDRUS_DEC_H_ > +#define _CEDRUS_DEC_H_ > + > +extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; > + > +void cedrus_device_work(struct work_struct *work); > +void cedrus_device_run(void *priv); > +void cedrus_job_abort(void *priv); > + > +int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, > + struct vb2_queue *dst_vq); > + > +#endif > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_hw.c b/drivers/media/platform/sunxi/cedrus/cedrus_hw.c > new file mode 100644 > index 000000000000..9ee1380f4e30 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_hw.c > @@ -0,0 +1,262 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#include <linux/platform_device.h> > +#include <linux/of_reserved_mem.h> > +#include <linux/dma-mapping.h> > +#include <linux/interrupt.h> > +#include <linux/clk.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > +#include <linux/soc/sunxi/sunxi_sram.h> > + > +#include <media/videobuf2-core.h> > +#include <media/v4l2-mem2mem.h> > + > +#include "cedrus.h" > +#include "cedrus_hw.h" > +#include "cedrus_regs.h" > + > +int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_engine engine) > +{ > + u32 reg = 0; > + > + /* > + * FIXME: This is only valid on 32-bits DDR's, we should test > + * it on the A13/A33. > + */ > + reg |= VE_CTRL_REC_WR_MODE_2MB; > + > + reg |= VE_CTRL_CACHE_BUS_BW_128; > + > + switch (engine) { > + case CEDRUS_ENGINE_MPEG: > + reg |= VE_CTRL_DEC_MODE_MPEG; > + break; > + > + default: > + return -EINVAL; > + } > + > + cedrus_write(dev, VE_CTRL, reg); > + > + return 0; > +} > + > +void cedrus_engine_disable(struct cedrus_dev *dev) > +{ > + cedrus_write(dev, VE_CTRL, VE_CTRL_DEC_MODE_DISABLED); > +} > + > +static irqreturn_t cedrus_ve_irq(int irq, void *data) > +{ > + struct cedrus_dev *dev = data; > + struct cedrus_ctx *ctx; > + struct cedrus_buffer *src_buffer, *dst_buffer; > + struct vb2_v4l2_buffer *src_vb, *dst_vb; > + unsigned long flags; > + unsigned int value, status; > + > + spin_lock_irqsave(&dev->irq_lock, flags); > + > + /* Disable MPEG interrupts and stop the MPEG engine. */ > + value = cedrus_read(dev, VE_MPEG_CTRL); > + cedrus_write(dev, VE_MPEG_CTRL, value & (~0xf)); > + > + status = cedrus_read(dev, VE_MPEG_STATUS); > + cedrus_write(dev, VE_MPEG_STATUS, 0x0000c00f); > + cedrus_engine_disable(dev); > + > + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); > + if (!ctx) { > + v4l2_err(&dev->v4l2_dev, > + "Instance released before the end of transaction\n"); > + spin_unlock_irqrestore(&dev->irq_lock, flags); > + > + return IRQ_HANDLED; > + } > + > + src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); > + dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); > + > + if (!src_vb || !dst_vb) { > + v4l2_err(&dev->v4l2_dev, > + "Missing source and/or destination buffers\n"); > + spin_unlock_irqrestore(&dev->irq_lock, flags); > + > + return IRQ_HANDLED; > + } > + > + src_buffer = container_of(src_vb, struct cedrus_buffer, vb); > + dst_buffer = container_of(dst_vb, struct cedrus_buffer, vb); > + > + /* First bit of MPEG_STATUS indicates success. */ > + if (ctx->job_abort || !(status & 0x01)) > + src_buffer->state = dst_buffer->state = VB2_BUF_STATE_ERROR; > + else > + src_buffer->state = dst_buffer->state = VB2_BUF_STATE_DONE; > + > + list_add_tail(&src_buffer->list, &ctx->src_list); > + list_add_tail(&dst_buffer->list, &ctx->dst_list); > + > + spin_unlock_irqrestore(&dev->irq_lock, flags); > + > + schedule_work(&ctx->run_work); > + > + return IRQ_HANDLED; > +} > + > +int cedrus_hw_probe(struct cedrus_dev *dev) > +{ > + struct resource *res; > + int irq_dec; > + int ret; > + > + irq_dec = platform_get_irq(dev->pdev, 0); > + if (irq_dec <= 0) { > + v4l2_err(&dev->v4l2_dev, "Failed to get IRQ\n"); > + return -ENXIO; > + } > + ret = devm_request_irq(dev->dev, irq_dec, cedrus_ve_irq, 0, > + dev_name(dev->dev), dev); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to request IRQ\n"); > + return -ENXIO; > + } > + > + /* > + * The VPU is only able to handle bus addresses so we have to subtract > + * the RAM offset to the physcal addresses. > + */ > + dev->dev->dma_pfn_offset = PHYS_PFN_OFFSET; > + > + ret = of_reserved_mem_device_init(dev->dev); > + if (ret && ret != -ENODEV) { > + v4l2_err(&dev->v4l2_dev, "Failed to reserved memory\n"); > + return -ENODEV; > + } > + > + ret = sunxi_sram_claim(dev->dev); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to claim SRAM\n"); > + goto err_mem; > + } > + > + dev->ahb_clk = devm_clk_get(dev->dev, "ahb"); > + if (IS_ERR(dev->ahb_clk)) { > + v4l2_err(&dev->v4l2_dev, "Failed to get AHB clock\n"); > + > + ret = PTR_ERR(dev->ahb_clk); > + goto err_sram; > + } > + > + dev->mod_clk = devm_clk_get(dev->dev, "mod"); > + if (IS_ERR(dev->mod_clk)) { > + v4l2_err(&dev->v4l2_dev, "Failed to get MOD clock\n"); > + > + ret = PTR_ERR(dev->mod_clk); > + goto err_sram; > + } > + > + dev->ram_clk = devm_clk_get(dev->dev, "ram"); > + if (IS_ERR(dev->ram_clk)) { > + v4l2_err(&dev->v4l2_dev, "Failed to get RAM clock\n"); > + > + ret = PTR_ERR(dev->ram_clk); > + goto err_sram; > + } > + > + dev->rstc = devm_reset_control_get(dev->dev, NULL); > + if (IS_ERR(dev->rstc)) { > + v4l2_err(&dev->v4l2_dev, "Failed to get reset control\n"); > + > + ret = PTR_ERR(dev->rstc); > + goto err_sram; > + } > + > + res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0); > + dev->base = devm_ioremap_resource(dev->dev, res); > + if (!dev->base) { > + v4l2_err(&dev->v4l2_dev, "Failed to map registers\n"); > + > + ret = -EFAULT; > + goto err_sram; > + } > + > + ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to set clock rate\n"); > + goto err_sram; > + } > + > + ret = clk_prepare_enable(dev->ahb_clk); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to enable AHB clock\n"); > + > + ret = -EFAULT; > + goto err_sram; > + } > + > + ret = clk_prepare_enable(dev->mod_clk); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to enable MOD clock\n"); > + > + ret = -EFAULT; > + goto err_ahb_clk; > + } > + > + ret = clk_prepare_enable(dev->ram_clk); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to enable RAM clock\n"); > + > + ret = -EFAULT; > + goto err_mod_clk; > + } > + This might look cleaner with the clock bulk API. Also, have you considered clock enable/disable in open/release, or start/stop? (possibly, via pm runtime). > + ret = reset_control_reset(dev->rstc); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to apply reset\n"); > + > + ret = -EFAULT; > + goto err_ram_clk; > + } > + > + return 0; > + > +err_ram_clk: > + clk_disable_unprepare(dev->ram_clk); > +err_mod_clk: > + clk_disable_unprepare(dev->mod_clk); > +err_ahb_clk: > + clk_disable_unprepare(dev->ahb_clk); > +err_sram: > + sunxi_sram_release(dev->dev); > +err_mem: > + of_reserved_mem_device_release(dev->dev); > + > + return ret; > +} > + > +void cedrus_hw_remove(struct cedrus_dev *dev) > +{ > + reset_control_assert(dev->rstc); > + > + clk_disable_unprepare(dev->ram_clk); > + clk_disable_unprepare(dev->mod_clk); > + clk_disable_unprepare(dev->ahb_clk); > + > + sunxi_sram_release(dev->dev); > + > + of_reserved_mem_device_release(dev->dev); > +} > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_hw.h b/drivers/media/platform/sunxi/cedrus/cedrus_hw.h > new file mode 100644 > index 000000000000..ead4f5089881 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_hw.h > @@ -0,0 +1,30 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#ifndef _CEDRUS_HW_H_ > +#define _CEDRUS_HW_H_ > + > +#define CEDRUS_CLOCK_RATE_DEFAULT 320000000 > + This define is only used in cedrus_hw.c. Perhaps shouldn't belong in a header. > +enum cedrus_engine { > + CEDRUS_ENGINE_MPEG, > +}; > + > +int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_engine engine); > +void cedrus_engine_disable(struct cedrus_dev *dev); > + > +int cedrus_hw_probe(struct cedrus_dev *dev); > +void cedrus_hw_remove(struct cedrus_dev *dev); > + > +#endif > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.c b/drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.c > new file mode 100644 > index 000000000000..0d6dcaec73b3 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.c > @@ -0,0 +1,146 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#include <media/videobuf2-dma-contig.h> > + > +#include "cedrus.h" > +#include "cedrus_hw.h" > +#include "cedrus_regs.h" > + > +static const u8 mpeg_default_intra_quant[64] = { > + 8, 16, 16, 19, 16, 19, 22, 22, > + 22, 22, 22, 22, 26, 24, 26, 27, > + 27, 27, 26, 26, 26, 26, 27, 27, > + 27, 29, 29, 29, 34, 34, 34, 29, > + 29, 29, 27, 27, 29, 29, 32, 32, > + 34, 34, 37, 38, 37, 35, 35, 34, > + 35, 38, 38, 40, 40, 40, 48, 48, > + 46, 46, 56, 56, 58, 69, 69, 83 > +}; > + > +#define m_iq(i) (((64 + i) << 8) | mpeg_default_intra_quant[i]) > + > +static const u8 mpeg_default_non_intra_quant[64] = { > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16, > + 16, 16, 16, 16, 16, 16, 16, 16 > +}; > + > +#define m_niq(i) ((i << 8) | mpeg_default_non_intra_quant[i]) > + > +void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) > +{ > + struct cedrus_dev *dev = ctx->dev; > + const struct v4l2_ctrl_mpeg2_slice_header *frame_hdr = run->mpeg2.hdr; > + > + u16 width = DIV_ROUND_UP(frame_hdr->width, 16); > + u16 height = DIV_ROUND_UP(frame_hdr->height, 16); > + > + u32 pic_header = 0; > + u32 vld_len = frame_hdr->slice_len - frame_hdr->slice_pos; > + int i; > + > + struct vb2_buffer *fwd_vb2_buf, *bwd_vb2_buf; > + dma_addr_t src_buf_addr, dst_luma_addr, dst_chroma_addr; > + dma_addr_t fwd_luma = 0, fwd_chroma = 0, bwd_luma = 0, bwd_chroma = 0; > + > + fwd_vb2_buf = ctx->dst_bufs[frame_hdr->forward_ref_index]; > + if (fwd_vb2_buf) { > + fwd_luma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 0); > + fwd_chroma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 1); > + } > + > + bwd_vb2_buf = ctx->dst_bufs[frame_hdr->backward_ref_index]; > + if (bwd_vb2_buf) { > + bwd_luma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 0); > + bwd_chroma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 1); > + } > + > + /* Activate MPEG engine. */ > + cedrus_engine_enable(dev, CEDRUS_ENGINE_MPEG); > + > + /* Set quantization matrices. */ > + for (i = 0; i < 64; i++) { > + cedrus_write(dev, VE_MPEG_IQ_MIN_INPUT, m_iq(i)); > + cedrus_write(dev, VE_MPEG_IQ_MIN_INPUT, m_niq(i)); > + } > + > + /* Set frame dimensions. */ > + cedrus_write(dev, VE_MPEG_SIZE, width << 8 | height); > + cedrus_write(dev, VE_MPEG_FRAME_SIZE, width << 20 | height << 4); > + > + /* Set MPEG picture header. */ > + pic_header |= (frame_hdr->picture_coding_type & 0xf) << 28; > + pic_header |= (frame_hdr->f_code[0][0] & 0xf) << 24; > + pic_header |= (frame_hdr->f_code[0][1] & 0xf) << 20; > + pic_header |= (frame_hdr->f_code[1][0] & 0xf) << 16; > + pic_header |= (frame_hdr->f_code[1][1] & 0xf) << 12; > + pic_header |= (frame_hdr->intra_dc_precision & 0x3) << 10; > + pic_header |= (frame_hdr->picture_structure & 0x3) << 8; > + pic_header |= (frame_hdr->top_field_first & 0x1) << 7; > + pic_header |= (frame_hdr->frame_pred_frame_dct & 0x1) << 6; > + pic_header |= (frame_hdr->concealment_motion_vectors & 0x1) << 5; > + pic_header |= (frame_hdr->q_scale_type & 0x1) << 4; > + pic_header |= (frame_hdr->intra_vlc_format & 0x1) << 3; > + pic_header |= (frame_hdr->alternate_scan & 0x1) << 2; > + cedrus_write(dev, VE_MPEG_PIC_HDR, pic_header); > + > + /* Enable interrupt and an unknown control flag. */ > + cedrus_write(dev, VE_MPEG_CTRL, VE_MPEG_CTRL_MPEG2); > + > + /* Macroblock address. */ > + cedrus_write(dev, VE_MPEG_MBA, 0); > + > + /* Clear previous errors. */ > + cedrus_write(dev, VE_MPEG_ERROR, 0); > + > + /* Clear correct macroblocks register. */ > + cedrus_write(dev, VE_MPEG_CTR_MB, 0); > + > + /* Forward and backward prediction reference buffers. */ > + cedrus_write(dev, VE_MPEG_FWD_LUMA, fwd_luma); > + cedrus_write(dev, VE_MPEG_FWD_CHROMA, fwd_chroma); > + cedrus_write(dev, VE_MPEG_BACK_LUMA, bwd_luma); > + cedrus_write(dev, VE_MPEG_BACK_CHROMA, bwd_chroma); > + > + /* Destination luma and chroma buffers. */ > + dst_luma_addr = vb2_dma_contig_plane_dma_addr(&run->dst->vb2_buf, 0); > + dst_chroma_addr = vb2_dma_contig_plane_dma_addr(&run->dst->vb2_buf, 1); > + cedrus_write(dev, VE_MPEG_REC_LUMA, dst_luma_addr); > + cedrus_write(dev, VE_MPEG_REC_CHROMA, dst_chroma_addr); > + cedrus_write(dev, VE_MPEG_ROT_LUMA, dst_luma_addr); > + cedrus_write(dev, VE_MPEG_ROT_CHROMA, dst_chroma_addr); > + > + /* Source offset and length in bits. */ > + cedrus_write(dev, VE_MPEG_VLD_OFFSET, frame_hdr->slice_pos); > + cedrus_write(dev, VE_MPEG_VLD_LEN, vld_len); > + > + /* Source beginning and end addresses. */ > + src_buf_addr = vb2_dma_contig_plane_dma_addr(&run->src->vb2_buf, 0); > + cedrus_write(dev, VE_MPEG_VLD_ADDR, VE_MPEG_VLD_ADDR_VAL(src_buf_addr)); > + cedrus_write(dev, VE_MPEG_VLD_END, src_buf_addr + VBV_SIZE - 1); > +} > + > +void cedrus_mpeg2_trigger(struct cedrus_ctx *ctx) > +{ > + struct cedrus_dev *dev = ctx->dev; > + > + /* Trigger MPEG engine. */ > + cedrus_write(dev, VE_MPEG_TRIGGER, VE_TRIG_MPEG2); > +} > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.h b/drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.h > new file mode 100644 > index 000000000000..fd864fab6986 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_mpeg2.h > @@ -0,0 +1,24 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#ifndef _CEDRUS_MPEG2_H_ > +#define _CEDRUS_MPEG2_H_ > + > +struct cedrus_ctx; > +struct cedrus_run; > + > +void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run); > +void cedrus_mpeg2_trigger(struct cedrus_ctx *ctx); > + > +#endif > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_regs.h b/drivers/media/platform/sunxi/cedrus/cedrus_regs.h > new file mode 100644 > index 000000000000..442befcdb6ea > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_regs.h > @@ -0,0 +1,167 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#ifndef _CEDRUS_REGS_H_ > +#define _CEDRUS_REGS_H_ > + > +/* > + * For more information, consult http://linux-sunxi.org/VE_Register_guide > + */ > + > +/* VE_MPEG_CTRL: > + * The bit 3 (0x8) is used to enable IRQs > + * The other bits are unknown but needed > + */ > +#define VE_MPEG_CTRL_MPEG2 0x800001b8 > +#define VE_MPEG_CTRL_MPEG4 (0x80084118 | BIT(7)) > +#define VE_MPEG_CTRL_MPEG4_P (VE_MPEG_CTRL_MPEG4 | BIT(12)) > + > +/* VE_MPEG_VLD_ADDR: > + * The bits 27 to 4 are used for the address > + * The bits 31 to 28 (0x7) are used to select the MPEG or JPEG engine > + */ > +#define VE_MPEG_VLD_ADDR_VAL(x) ((x & 0x0ffffff0) | (x >> 28) | (0x7 << 28)) > + > +/* VE_MPEG_TRIGGER: > + * The first three bits are used to trigger the engine > + * The bits 24 to 26 are used to select the input format (1 for MPEG1, 2 for > + * MPEG2, 4 for MPEG4) > + * The bit 21 (0x8) is used to disable bitstream error handling > + * > + * In MPEG4 the w*h value is somehow used for an offset, unknown but needed > + */ > +#define VE_TRIG_MPEG1 0x8100000f > +#define VE_TRIG_MPEG2 0x8200000f > +#define VE_TRIG_MPEG4(w, h) (0x8400000d | ((w * h) << 8)) > + > +/* VE_MPEG_SDROT_CTRL: > + * The bit 8 at zero is used to disable x downscaling > + * The bit 10 at 0 is used to disable y downscaling > + * The other bits are unknown but needed > + */ > +#define VE_NO_SDROT_CTRL 0x40620000 > + > +/* Decent size fo video buffering verifier */ > +#define VBV_SIZE (1024 * 1024) > + > +/* Registers addresses */ > +#define VE_CTRL 0x000 > +/* > + * The datasheet states that this should be set to 2MB on a 32bits > + * DDR-3. > + */ > +#define VE_CTRL_REC_WR_MODE_2MB (1 << 20) > +#define VE_CTRL_REC_WR_MODE_1MB (0 << 20) > + > +#define VE_CTRL_CACHE_BUS_BW_128 (3 << 16) > +#define VE_CTRL_CACHE_BUS_BW_256 (2 << 16) > + > +#define VE_CTRL_DEC_MODE_DISABLED (7 << 0) > +#define VE_CTRL_DEC_MODE_H265 (4 << 0) > +#define VE_CTRL_DEC_MODE_H264 (1 << 0) > +#define VE_CTRL_DEC_MODE_MPEG (0 << 0) > + > +#define VE_VERSION 0x0f0 > + > +#define VE_MPEG_PIC_HDR 0x100 > +#define VE_MPEG_VOP_HDR 0x104 > +#define VE_MPEG_SIZE 0x108 > +#define VE_MPEG_FRAME_SIZE 0x10c > +#define VE_MPEG_MBA 0x110 > +#define VE_MPEG_CTRL 0x114 > +#define VE_MPEG_TRIGGER 0x118 > +#define VE_MPEG_STATUS 0x11c > +#define VE_MPEG_TRBTRD_FIELD 0x120 > +#define VE_MPEG_TRBTRD_FRAME 0x124 > +#define VE_MPEG_VLD_ADDR 0x128 > +#define VE_MPEG_VLD_OFFSET 0x12c > +#define VE_MPEG_VLD_LEN 0x130 > +#define VE_MPEG_VLD_END 0x134 > +#define VE_MPEG_MBH_ADDR 0x138 > +#define VE_MPEG_DCAC_ADDR 0x13c > +#define VE_MPEG_NCF_ADDR 0x144 > +#define VE_MPEG_REC_LUMA 0x148 > +#define VE_MPEG_REC_CHROMA 0x14c > +#define VE_MPEG_FWD_LUMA 0x150 > +#define VE_MPEG_FWD_CHROMA 0x154 > +#define VE_MPEG_BACK_LUMA 0x158 > +#define VE_MPEG_BACK_CHROMA 0x15c > +#define VE_MPEG_IQ_MIN_INPUT 0x180 > +#define VE_MPEG_QP_INPUT 0x184 > +#define VE_MPEG_JPEG_SIZE 0x1b8 > +#define VE_MPEG_JPEG_RES_INT 0x1c0 > +#define VE_MPEG_ERROR 0x1c4 > +#define VE_MPEG_CTR_MB 0x1c8 > +#define VE_MPEG_ROT_LUMA 0x1cc > +#define VE_MPEG_ROT_CHROMA 0x1d0 > +#define VE_MPEG_SDROT_CTRL 0x1d4 > +#define VE_MPEG_RAM_WRITE_PTR 0x1e0 > +#define VE_MPEG_RAM_WRITE_DATA 0x1e4 > + > +#define VE_H264_FRAME_SIZE 0x200 > +#define VE_H264_PIC_HDR 0x204 > +#define VE_H264_SLICE_HDR 0x208 > +#define VE_H264_SLICE_HDR2 0x20c > +#define VE_H264_PRED_WEIGHT 0x210 > +#define VE_H264_QP_PARAM 0x21c > +#define VE_H264_CTRL 0x220 > +#define VE_H264_TRIGGER 0x224 > +#define VE_H264_STATUS 0x228 > +#define VE_H264_CUR_MB_NUM 0x22c > +#define VE_H264_VLD_ADDR 0x230 > +#define VE_H264_VLD_OFFSET 0x234 > +#define VE_H264_VLD_LEN 0x238 > +#define VE_H264_VLD_END 0x23c > +#define VE_H264_SDROT_CTRL 0x240 > +#define VE_H264_OUTPUT_FRAME_IDX 0x24c > +#define VE_H264_EXTRA_BUFFER1 0x250 > +#define VE_H264_EXTRA_BUFFER2 0x254 > +#define VE_H264_BASIC_BITS 0x2dc > +#define VE_H264_RAM_WRITE_PTR 0x2e0 > +#define VE_H264_RAM_WRITE_DATA 0x2e4 > + > +#define VE_SRAM_H264_PRED_WEIGHT_TABLE 0x000 > +#define VE_SRAM_H264_FRAMEBUFFER_LIST 0x400 > +#define VE_SRAM_H264_REF_LIST0 0x640 > +#define VE_SRAM_H264_REF_LIST1 0x664 > +#define VE_SRAM_H264_SCALING_LISTS 0x800 > + > +#define VE_ISP_INPUT_SIZE 0xa00 > +#define VE_ISP_INPUT_STRIDE 0xa04 > +#define VE_ISP_CTRL 0xa08 > +#define VE_ISP_INPUT_LUMA 0xa78 > +#define VE_ISP_INPUT_CHROMA 0xa7c > + > +#define VE_AVC_PARAM 0xb04 > +#define VE_AVC_QP 0xb08 > +#define VE_AVC_MOTION_EST 0xb10 > +#define VE_AVC_CTRL 0xb14 > +#define VE_AVC_TRIGGER 0xb18 > +#define VE_AVC_STATUS 0xb1c > +#define VE_AVC_BASIC_BITS 0xb20 > +#define VE_AVC_UNK_BUF 0xb60 > +#define VE_AVC_VLE_ADDR 0xb80 > +#define VE_AVC_VLE_END 0xb84 > +#define VE_AVC_VLE_OFFSET 0xb88 > +#define VE_AVC_VLE_MAX 0xb8c > +#define VE_AVC_VLE_LENGTH 0xb90 > +#define VE_AVC_REF_LUMA 0xba0 > +#define VE_AVC_REF_CHROMA 0xba4 > +#define VE_AVC_REC_LUMA 0xbb0 > +#define VE_AVC_REC_CHROMA 0xbb4 > +#define VE_AVC_REF_SLUMA 0xbb8 > +#define VE_AVC_REC_SLUMA 0xbbc > +#define VE_AVC_MB_INFO 0xbc0 > + > +#endif > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_video.c b/drivers/media/platform/sunxi/cedrus/cedrus_video.c > new file mode 100644 > index 000000000000..ad7da8d36966 > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_video.c > @@ -0,0 +1,502 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#include <media/videobuf2-dma-contig.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-event.h> > +#include <media/v4l2-mem2mem.h> > + > +#include "cedrus.h" > +#include "cedrus_mpeg2.h" > +#include "cedrus_dec.h" > +#include "cedrus_hw.h" > + > +/* Flags that indicate a format can be used for capture/output. */ > +#define CEDRUS_CAPTURE BIT(0) > +#define CEDRUS_OUTPUT BIT(1) > + > +#define CEDRUS_MIN_WIDTH 16U > +#define CEDRUS_MIN_HEIGHT 16U > +#define CEDRUS_MAX_WIDTH 3840U > +#define CEDRUS_MAX_HEIGHT 2160U > + > +static struct cedrus_fmt formats[] = { > + { > + .fourcc = V4L2_PIX_FMT_MB32_NV12, > + .types = CEDRUS_CAPTURE, > + .depth = 2, > + .num_planes = 2, > + }, > + { > + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, > + .types = CEDRUS_OUTPUT, > + .num_planes = 1, > + }, > +}; > + > +#define NUM_FORMATS ARRAY_SIZE(formats) > + > +static struct cedrus_fmt *find_format(struct v4l2_format *f) > +{ > + struct cedrus_fmt *fmt; > + unsigned int k; > + > + for (k = 0; k < NUM_FORMATS; k++) { > + fmt = &formats[k]; > + if (fmt->fourcc == f->fmt.pix_mp.pixelformat) > + break; > + } > + > + if (k == NUM_FORMATS) > + return NULL; > + > + return &formats[k]; > +} > + > +static inline struct cedrus_ctx *file2ctx(struct file *file) > +{ > + return container_of(file->private_data, struct cedrus_ctx, fh); > +} > + > +static int vidioc_querycap(struct file *file, void *priv, > + struct v4l2_capability *cap) > +{ > + strncpy(cap->driver, CEDRUS_NAME, sizeof(cap->driver) - 1); > + strncpy(cap->card, CEDRUS_NAME, sizeof(cap->card) - 1); > + snprintf(cap->bus_info, sizeof(cap->bus_info), > + "platform:%s", CEDRUS_NAME); > + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; > + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; > + return 0; > +} > + > +static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) > +{ > + struct cedrus_fmt *fmt; > + int i, num = 0; > + > + for (i = 0; i < NUM_FORMATS; ++i) { > + if (formats[i].types & type) { > + /* index-th format of type type found ? */ > + if (num == f->index) > + break; > + /* > + * Correct type but haven't reached our index yet, > + * just increment per-type index > + */ > + ++num; > + } > + } > + > + if (i < NUM_FORMATS) { > + fmt = &formats[i]; > + f->pixelformat = fmt->fourcc; > + return 0; > + } > + > + return -EINVAL; > +} > + > +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, > + struct v4l2_fmtdesc *f) > +{ > + return enum_fmt(f, CEDRUS_CAPTURE); > +} > + > +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, > + struct v4l2_fmtdesc *f) > +{ > + return enum_fmt(f, CEDRUS_OUTPUT); > +} > + > +static int vidioc_g_fmt(struct cedrus_ctx *ctx, struct v4l2_format *f) > +{ > + struct cedrus_dev *dev = ctx->dev; > + > + switch (f->type) { > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + f->fmt.pix_mp = ctx->dst_fmt; > + break; > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + f->fmt.pix_mp = ctx->src_fmt; > + break; > + default: > + v4l2_err(&dev->v4l2_dev, > + "Invalid buffer type for getting format\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int vidioc_g_fmt_vid_out(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + return vidioc_g_fmt(file2ctx(file), f); > +} > + > +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + return vidioc_g_fmt(file2ctx(file), f); > +} > + > +static int vidioc_try_fmt(struct v4l2_format *f, struct cedrus_fmt *fmt) > +{ > + int i; > + __u32 bpl; > + nit: unsigned int -- or, given it's clamped, just use int. > + f->fmt.pix_mp.field = V4L2_FIELD_NONE; > + f->fmt.pix_mp.num_planes = fmt->num_planes; > + > + switch (f->type) { > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) > + return -EINVAL; > + > + f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; > + break; > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + /* Limit to hardware min/max. */ > + f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width, > + CEDRUS_MIN_WIDTH, CEDRUS_MAX_WIDTH); > + f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height, > + CEDRUS_MIN_HEIGHT, > + CEDRUS_MAX_HEIGHT); > + > + for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) { > + bpl = (f->fmt.pix_mp.width * fmt->depth) >> 3; > + f->fmt.pix_mp.plane_fmt[i].bytesperline = bpl; > + f->fmt.pix_mp.plane_fmt[i].sizeimage = > + f->fmt.pix_mp.height * bpl; > + } > + break; > + } > + return 0; > +} > + > +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + struct cedrus_fmt *fmt; > + struct cedrus_ctx *ctx = file2ctx(file); > + struct cedrus_dev *dev = ctx->dev; > + > + fmt = find_format(f); > + if (!fmt) { > + f->fmt.pix_mp.pixelformat = formats[0].fourcc; > + fmt = find_format(f); > + } > + if (!(fmt->types & CEDRUS_CAPTURE)) { > + v4l2_err(&dev->v4l2_dev, "Invalid destination format: %08x\n", > + f->fmt.pix_mp.pixelformat); > + return -EINVAL; > + } > + > + return vidioc_try_fmt(f, fmt); > +} > + > +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + struct cedrus_fmt *fmt; > + struct cedrus_ctx *ctx = file2ctx(file); > + struct cedrus_dev *dev = ctx->dev; > + > + fmt = find_format(f); > + if (!fmt) { > + f->fmt.pix_mp.pixelformat = formats[0].fourcc; > + fmt = find_format(f); > + } > + if (!(fmt->types & CEDRUS_OUTPUT)) { > + v4l2_err(&dev->v4l2_dev, "Invalid source format: %08x\n", > + f->fmt.pix_mp.pixelformat); > + return -EINVAL; > + } > + > + return vidioc_try_fmt(f, fmt); > +} > + > +static int vidioc_s_fmt(struct cedrus_ctx *ctx, struct v4l2_format *f) > +{ > + struct cedrus_dev *dev = ctx->dev; > + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; > + struct cedrus_fmt *fmt; > + int i, ret = 0; > + > + switch (f->type) { > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + ctx->vpu_src_fmt = find_format(f); > + ctx->src_fmt = *pix_fmt_mp; > + break; > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + fmt = find_format(f); > + ctx->vpu_dst_fmt = fmt; > + > + for (i = 0; i < fmt->num_planes; ++i) { > + pix_fmt_mp->plane_fmt[i].bytesperline = > + pix_fmt_mp->width * fmt->depth; > + pix_fmt_mp->plane_fmt[i].sizeimage = > + pix_fmt_mp->plane_fmt[i].bytesperline > + * pix_fmt_mp->height; > + } > + ctx->dst_fmt = *pix_fmt_mp; > + break; > + default: > + v4l2_err(&dev->v4l2_dev, > + "Invalid buffer type for setting format\n"); > + return -EINVAL; > + } > + > + return ret; > +} > + > +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + struct cedrus_ctx *ctx = file2ctx(file); > + int ret; > + > + ret = vidioc_try_fmt_vid_cap(file, priv, f); > + if (ret) > + return ret; > + > + return vidioc_s_fmt(ctx, f); > +} > + > +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + struct cedrus_ctx *ctx = file2ctx(file); > + int ret; > + > + ret = vidioc_try_fmt_vid_out(file, priv, f); > + if (ret) > + return ret; > + > + return vidioc_s_fmt(ctx, f); > +} > + > +const struct v4l2_ioctl_ops cedrus_ioctl_ops = { > + .vidioc_querycap = vidioc_querycap, > + > + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, > + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, > + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap, > + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, > + > + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, > + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out, > + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out, > + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out, > + > + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, > + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, > + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, > + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, > + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, > + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, > + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, > + > + .vidioc_streamon = v4l2_m2m_ioctl_streamon, > + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, > + > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > +}; > + > +static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, > + unsigned int *nplanes, unsigned int sizes[], > + struct device *alloc_devs[]) > +{ > + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); > + struct cedrus_dev *dev = ctx->dev; > + > + switch (vq->type) { > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + *nplanes = ctx->vpu_src_fmt->num_planes; > + > + sizes[0] = ctx->src_fmt.plane_fmt[0].sizeimage; > + break; > + > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + *nplanes = ctx->vpu_dst_fmt->num_planes; > + > + sizes[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8); > + sizes[1] = sizes[0]; > + break; > + > + default: > + v4l2_err(&dev->v4l2_dev, > + "Invalid buffer type for queue setup\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int cedrus_buf_init(struct vb2_buffer *vb) > +{ > + struct vb2_queue *vq = vb->vb2_queue; > + struct cedrus_ctx *ctx = container_of(vq->drv_priv, > + struct cedrus_ctx, fh); > + > + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) > + ctx->dst_bufs[vb->index] = vb; > + > + return 0; > +} > + > +static void cedrus_buf_cleanup(struct vb2_buffer *vb) > +{ > + struct vb2_queue *vq = vb->vb2_queue; > + struct cedrus_ctx *ctx = container_of(vq->drv_priv, > + struct cedrus_ctx, fh); > + > + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) > + ctx->dst_bufs[vb->index] = NULL; > +} > + > +static int cedrus_buf_prepare(struct vb2_buffer *vb) > +{ > + struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); > + struct cedrus_dev *dev = ctx->dev; > + struct vb2_queue *vq = vb->vb2_queue; > + int i; > + > + switch (vq->type) { > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + if (vb2_plane_size(vb, 0) > + < ctx->src_fmt.plane_fmt[0].sizeimage) { > + v4l2_err(&dev->v4l2_dev, > + "Buffer plane size too small for output\n"); > + return -EINVAL; > + } > + break; > + > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) { > + if (vb2_plane_size(vb, i) > + < ctx->dst_fmt.plane_fmt[i].sizeimage) { > + v4l2_err(&dev->v4l2_dev, > + "Buffer plane %d size too small for capture\n", > + i); > + break; > + } > + } > + > + if (i != ctx->vpu_dst_fmt->num_planes) > + return -EINVAL; > + break; > + > + default: > + v4l2_err(&dev->v4l2_dev, > + "Invalid buffer type for buffer preparation\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static void cedrus_stop_streaming(struct vb2_queue *q) > +{ > + struct cedrus_ctx *ctx = vb2_get_drv_priv(q); > + struct vb2_v4l2_buffer *vbuf; > + unsigned long flags; > + > + flush_scheduled_work(); Just like the other work stuff, flush_scheduled_work seems bogus. > + for (;;) { > + spin_lock_irqsave(&ctx->dev->irq_lock, flags); > + > + if (V4L2_TYPE_IS_OUTPUT(q->type)) > + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); > + else > + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); > + > + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); > + > + if (vbuf == NULL) > + return; > + > + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, > + &ctx->hdl); > + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); > + } > +} > + > +static void cedrus_buf_queue(struct vb2_buffer *vb) > +{ > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); > + struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); > + > + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); > +} > + > +static void cedrus_buf_request_complete(struct vb2_buffer *vb) > +{ > + struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); > + > + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); > +} > + > +static struct vb2_ops cedrus_qops = { > + .queue_setup = cedrus_queue_setup, > + .buf_prepare = cedrus_buf_prepare, > + .buf_init = cedrus_buf_init, > + .buf_cleanup = cedrus_buf_cleanup, > + .buf_queue = cedrus_buf_queue, > + .buf_request_complete = cedrus_buf_request_complete, > + .stop_streaming = cedrus_stop_streaming, > + .wait_prepare = vb2_ops_wait_prepare, > + .wait_finish = vb2_ops_wait_finish, > +}; > + > +int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, > + struct vb2_queue *dst_vq) > +{ > + struct cedrus_ctx *ctx = priv; > + int ret; > + > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; > + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; > + src_vq->drv_priv = ctx; > + src_vq->buf_struct_size = sizeof(struct cedrus_buffer); > + src_vq->allow_zero_bytesused = 1; > + src_vq->min_buffers_needed = 1; > + src_vq->ops = &cedrus_qops; > + src_vq->mem_ops = &vb2_dma_contig_memops; > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + src_vq->lock = &ctx->dev->dev_mutex; > + src_vq->dev = ctx->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->drv_priv = ctx; > + dst_vq->buf_struct_size = sizeof(struct cedrus_buffer); > + dst_vq->allow_zero_bytesused = 1; > + dst_vq->min_buffers_needed = 1; > + dst_vq->ops = &cedrus_qops; > + dst_vq->mem_ops = &vb2_dma_contig_memops; > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + dst_vq->lock = &ctx->dev->dev_mutex; > + dst_vq->dev = ctx->dev->dev; > + > + return vb2_queue_init(dst_vq); > +} > diff --git a/drivers/media/platform/sunxi/cedrus/cedrus_video.h b/drivers/media/platform/sunxi/cedrus/cedrus_video.h > new file mode 100644 > index 000000000000..ed7cea8a6d8f > --- /dev/null > +++ b/drivers/media/platform/sunxi/cedrus/cedrus_video.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Sunxi-Cedrus VPU driver > + * > + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@xxxxxxxxxxx> > + * Copyright (C) 2016 Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> > + * > + * Based on the vim2m driver, that is: > + * > + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. > + * Pawel Osciak, <pawel@xxxxxxxxxx> > + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > + */ > + > +#ifndef _CEDRUS_VIDEO_H_ > +#define _CEDRUS_VIDEO_H_ > + > +extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; > + > +int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, > + struct vb2_queue *dst_vq); > + > +#endif > -- > 2.17.0 > > -- > You received this message because you are subscribed to the Google Groups "linux-sunxi" group. > To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@xxxxxxxxxxxxxxxx. > For more options, visit https://groups.google.com/d/optout. -- Ezequiel García, VanguardiaSur www.vanguardiasur.com.ar -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html