Hi Hans, On 02/12/2020 13:40, Hans Verkuil wrote: > On 17/11/2020 09:44, Neil Armstrong wrote: >> The GE2D is a 2D accelerator with various features like configurable blitter >> with alpha blending, frame rotation, scaling, format conversion and colorspace >> conversion. >> >> The driver implements a Memory2Memory VB2 V4L2 streaming device permitting: >> - 0, 90, 180, 270deg rotation >> - horizontal/vertical flipping >> - source cropping >> - destination compositing >> - 32bit/24bit/16bit format conversion >> >> This adds the support for the GE2D version found in the AXG SoCs Family. >> >> The missing features are: >> - Source scaling >> - Colorspace conversion >> - Advanced alpha blending & blitting options >> >> Is passes v4l2-compliance SHA: ea16a7ef13a902793a5c2626b0cefc4d956147f3, 64 bits, 64-bit time_t >> >> Signed-off-by: Neil Armstrong <narmstrong@xxxxxxxxxxxx> >> --- >> drivers/media/platform/Kconfig | 13 + >> drivers/media/platform/Makefile | 2 + >> drivers/media/platform/meson/ge2d/Makefile | 3 + >> drivers/media/platform/meson/ge2d/ge2d-regs.h | 360 ++++++ >> drivers/media/platform/meson/ge2d/ge2d.c | 1091 +++++++++++++++++ >> 5 files changed, 1469 insertions(+) >> create mode 100644 drivers/media/platform/meson/ge2d/Makefile >> create mode 100644 drivers/media/platform/meson/ge2d/ge2d-regs.h >> create mode 100644 drivers/media/platform/meson/ge2d/ge2d.c >> [...] >> + >> +#define DEFAULT_WIDTH 100 >> +#define DEFAULT_HEIGHT 100 > > That's a weird default value. I would expect to see some multiple of 8 here. Sure, whatever the HW is quite flexible, I took 100x100 from the other 2D accelerator drivers actually. [...] >> + >> +static void ge2d_stop_streaming(struct vb2_queue *vq) >> +{ >> + struct ge2d_ctx *ctx = vb2_get_drv_priv(vq); >> + struct vb2_v4l2_buffer *vbuf; >> + >> + if (V4L2_TYPE_IS_OUTPUT(vq->type)) >> + ctx->streamon_out = false; >> + else >> + ctx->streamon_cap = false; > > Do you need streamon_out/cap? Can't you use vb2_start_streaming_called() instead? Indeed, I'll switch to vb2_start_streaming_called & co. [...] >> + >> +static int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *f) >> +{ >> + const struct ge2d_fmt *fmt = find_fmt(f); >> + >> + f->fmt.pix.field = V4L2_FIELD_NONE; >> + f->fmt.pix.pixelformat = fmt->fourcc; >> + >> + if (f->fmt.pix.width > MAX_WIDTH) >> + f->fmt.pix.width = MAX_WIDTH; >> + if (f->fmt.pix.height > MAX_HEIGHT) >> + f->fmt.pix.height = MAX_HEIGHT; >> + >> + if (f->fmt.pix.width < 0) >> + f->fmt.pix.width = 0; >> + if (f->fmt.pix.height < 0) >> + f->fmt.pix.height = 0; > > width and height are unsigned, so this check is not needed. Ok [...] >> + >> +static int ge2d_s_ctrl(struct v4l2_ctrl *ctrl) >> +{ >> + struct ge2d_ctx *ctx = container_of(ctrl->handler, struct ge2d_ctx, >> + ctrl_handler); >> + struct v4l2_pix_format fmt; >> + struct vb2_queue *vq; >> + unsigned long flags; >> + >> + spin_lock_irqsave(&ctx->ge2d->ctrl_lock, flags); >> + switch (ctrl->id) { >> + case V4L2_CID_HFLIP: >> + ctx->hflip = ctrl->val; >> + break; >> + case V4L2_CID_VFLIP: >> + ctx->vflip = ctrl->val; >> + break; >> + case V4L2_CID_ROTATE: >> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); >> + if (vb2_is_busy(vq)) >> + return -EBUSY; > > This doesn't call spin_unlock_irqrestore()! > > Do you need the spinlock at all? This function is already called with a mutex > for ctrl->handler held. It's unusual to see a spinlock here. Is device_run also called with ctrl->handler held ? The spinlock is used to protect against concurrent re-config & run. [...] >> + >> + platform_set_drvdata(pdev, ge2d); >> + ge2d->m2m_dev = v4l2_m2m_init(&ge2d_m2m_ops); >> + if (IS_ERR(ge2d->m2m_dev)) { >> + v4l2_err(&ge2d->v4l2_dev, "Failed to init mem2mem device\n"); >> + ret = PTR_ERR(ge2d->m2m_dev); >> + goto unreg_video_dev; > > This should be goto rel_vdev. > >> + } >> + >> + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); >> + if (ret) { >> + v4l2_err(&ge2d->v4l2_dev, "Failed to register video device\n"); >> + goto rel_vdev; >> + } >> + >> + v4l2_info(&ge2d->v4l2_dev, "Registered %s as /dev/%s\n", >> + vfd->name, video_device_node_name(vfd)); >> + >> + return 0; >> + >> +rel_vdev: >> + video_device_release(vfd); >> +unreg_video_dev: >> + video_unregister_device(ge2d->vfd); > > This makes no sense. If video_register_device() fails, then you > call video_device_release(vfd), not video_unregister_device(). > > Just drop these two lines. ok > >> +unreg_v4l2_dev: >> + v4l2_device_unregister(&ge2d->v4l2_dev); >> +disable_clks: >> + clk_disable_unprepare(ge2d->clk); >> + >> + return ret; >> +} >> + >> +static int ge2d_remove(struct platform_device *pdev) >> +{ >> + struct meson_ge2d *ge2d = platform_get_drvdata(pdev); >> + >> + v4l2_m2m_release(ge2d->m2m_dev); >> + video_unregister_device(ge2d->vfd); >> + v4l2_device_unregister(&ge2d->v4l2_dev); >> + >> + clk_disable_unprepare(ge2d->clk); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id meson_ge2d_match[] = { >> + { >> + .compatible = "amlogic,axg-ge2d", >> + }, >> + {}, >> +}; >> + >> +MODULE_DEVICE_TABLE(of, meson_ge2d_match); >> + >> +static struct platform_driver ge2d_drv = { >> + .probe = ge2d_probe, >> + .remove = ge2d_remove, >> + .driver = { >> + .name = "meson-ge2d", >> + .of_match_table = meson_ge2d_match, >> + }, >> +}; >> + >> +module_platform_driver(ge2d_drv); >> + >> +MODULE_AUTHOR("Neil Armstrong <narmstrong@xxxxxxxxxxxx>"); >> +MODULE_DESCRIPTION("Amlogic 2D Graphic Acceleration Unit"); >> +MODULE_LICENSE("GPL"); >> > > Regards, > > Hans > Thanks, Neil