162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 BayLibre, SAS
462306a36Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/reset.h>
1462306a36Sopenharmony_ci#include <linux/sched.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/timer.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <media/v4l2-device.h>
2162306a36Sopenharmony_ci#include <media/v4l2-event.h>
2262306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2362306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h>
2462306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
2562306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h>
2662306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "ge2d-regs.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define GE2D_NAME	"meson-ge2d"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define DEFAULT_WIDTH	128
3362306a36Sopenharmony_ci#define DEFAULT_HEIGHT	128
3462306a36Sopenharmony_ci#define DEFAULT_STRIDE	512
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define MAX_WIDTH	8191
3762306a36Sopenharmony_ci#define MAX_HEIGHT	8191
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * Missing features:
4162306a36Sopenharmony_ci * - Scaling
4262306a36Sopenharmony_ci * - Simple 1/2 vertical scaling
4362306a36Sopenharmony_ci * - YUV input support
4462306a36Sopenharmony_ci * - Source global alpha
4562306a36Sopenharmony_ci * - Colorspace conversion
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct ge2d_fmt {
4962306a36Sopenharmony_ci	u32 fourcc;
5062306a36Sopenharmony_ci	bool alpha;
5162306a36Sopenharmony_ci	bool le;
5262306a36Sopenharmony_ci	unsigned int depth;
5362306a36Sopenharmony_ci	unsigned int hw_fmt;
5462306a36Sopenharmony_ci	unsigned int hw_map;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct ge2d_frame {
5862306a36Sopenharmony_ci	struct vb2_v4l2_buffer *buf;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Image Format */
6162306a36Sopenharmony_ci	struct v4l2_pix_format pix_fmt;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Crop */
6462306a36Sopenharmony_ci	struct v4l2_rect crop;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* Image format */
6762306a36Sopenharmony_ci	const struct ge2d_fmt *fmt;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistruct ge2d_ctx {
7162306a36Sopenharmony_ci	struct v4l2_fh fh;
7262306a36Sopenharmony_ci	struct meson_ge2d *ge2d;
7362306a36Sopenharmony_ci	struct ge2d_frame in;
7462306a36Sopenharmony_ci	struct ge2d_frame out;
7562306a36Sopenharmony_ci	struct v4l2_ctrl_handler ctrl_handler;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	unsigned long sequence_out, sequence_cap;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* Control values */
8062306a36Sopenharmony_ci	u32 hflip;
8162306a36Sopenharmony_ci	u32 vflip;
8262306a36Sopenharmony_ci	u32 xy_swap;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct meson_ge2d {
8662306a36Sopenharmony_ci	struct v4l2_device v4l2_dev;
8762306a36Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev;
8862306a36Sopenharmony_ci	struct video_device *vfd;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	struct device *dev;
9162306a36Sopenharmony_ci	struct regmap *map;
9262306a36Sopenharmony_ci	struct clk *clk;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* vb2 queue lock */
9562306a36Sopenharmony_ci	struct mutex mutex;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	struct ge2d_ctx *curr;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define FMT(_fourcc, _alpha, _depth, _map)		\
10162306a36Sopenharmony_ci{							\
10262306a36Sopenharmony_ci	.fourcc = _fourcc,				\
10362306a36Sopenharmony_ci	.alpha = (_alpha),				\
10462306a36Sopenharmony_ci	.depth = (_depth),				\
10562306a36Sopenharmony_ci	.hw_fmt = GE2D_FORMAT_ ## _depth ## BIT,	\
10662306a36Sopenharmony_ci	.hw_map = GE2D_COLOR_MAP_ ## _map,		\
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* TOFIX Handle the YUV input formats */
11062306a36Sopenharmony_cistatic const struct ge2d_fmt formats[] = {
11162306a36Sopenharmony_ci	/*  FOURCC Alpha  HW FMT  HW MAP */
11262306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_XRGB32, false, 32, BGRA8888),
11362306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGB32, true, 32, BGRA8888),
11462306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_ARGB32, true, 32, BGRA8888),
11562306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGBX32, false, 32, ABGR8888),
11662306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGBA32, true, 32, ABGR8888),
11762306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_BGRX32, false, 32, RGBA8888),
11862306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_BGRA32, true, 32, RGBA8888),
11962306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_BGR32, true, 32, ARGB8888),
12062306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_ABGR32, true, 32, ARGB8888),
12162306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_XBGR32, false, 32, ARGB8888),
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGB24, false, 24, BGR888),
12462306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_BGR24, false, 24, RGB888),
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_XRGB555X, false, 16, ARGB1555),
12762306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_ARGB555X, true, 16, ARGB1555),
12862306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGB565, false, 16, RGB565),
12962306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGBX444, false, 16, RGBA4444),
13062306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_RGBA444, true, 16, RGBA4444),
13162306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_XRGB444, false, 16, ARGB4444),
13262306a36Sopenharmony_ci	FMT(V4L2_PIX_FMT_ARGB444, true, 16, ARGB4444),
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define NUM_FORMATS ARRAY_SIZE(formats)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic const struct ge2d_fmt *find_fmt(struct v4l2_format *f)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	unsigned int i;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	for (i = 0; i < NUM_FORMATS; i++) {
14262306a36Sopenharmony_ci		if (formats[i].fourcc == f->fmt.pix.pixelformat)
14362306a36Sopenharmony_ci			return &formats[i];
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/*
14762306a36Sopenharmony_ci	 * TRY_FMT/S_FMT should never return an error when the requested format
14862306a36Sopenharmony_ci	 * is not supported. Drivers should always return a valid format,
14962306a36Sopenharmony_ci	 * preferably a format that is as widely supported by applications as
15062306a36Sopenharmony_ci	 * possible.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	return &formats[0];
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic struct ge2d_frame *get_frame(struct ge2d_ctx *ctx,
15662306a36Sopenharmony_ci				    enum v4l2_buf_type type)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	switch (type) {
15962306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
16062306a36Sopenharmony_ci		return &ctx->in;
16162306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
16262306a36Sopenharmony_ci		return &ctx->out;
16362306a36Sopenharmony_ci	default:
16462306a36Sopenharmony_ci		/* This should never happen, warn and return OUTPUT frame */
16562306a36Sopenharmony_ci		dev_warn(ctx->ge2d->dev, "%s: invalid buffer type\n", __func__);
16662306a36Sopenharmony_ci		return &ctx->in;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void ge2d_hw_start(struct meson_ge2d *ge2d)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct ge2d_ctx *ctx = ge2d->curr;
17362306a36Sopenharmony_ci	u32 reg;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Reset */
17662306a36Sopenharmony_ci	regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1,
17762306a36Sopenharmony_ci			   GE2D_SOFT_RST, GE2D_SOFT_RST);
17862306a36Sopenharmony_ci	regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1,
17962306a36Sopenharmony_ci			   GE2D_SOFT_RST, 0);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	usleep_range(100, 200);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Implement CANVAS for non-AXG */
18462306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC1_BADDR_CTRL,
18562306a36Sopenharmony_ci		     (vb2_dma_contig_plane_dma_addr(&ctx->in.buf->vb2_buf, 0) + 7) >> 3);
18662306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC1_STRIDE_CTRL,
18762306a36Sopenharmony_ci		     (ctx->in.pix_fmt.bytesperline + 7) >> 3);
18862306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC2_BADDR_CTRL,
18962306a36Sopenharmony_ci		     (vb2_dma_contig_plane_dma_addr(&ctx->out.buf->vb2_buf, 0) + 7) >> 3);
19062306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC2_STRIDE_CTRL,
19162306a36Sopenharmony_ci		     (ctx->out.pix_fmt.bytesperline + 7) >> 3);
19262306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_DST1_BADDR_CTRL,
19362306a36Sopenharmony_ci		     (vb2_dma_contig_plane_dma_addr(&ctx->out.buf->vb2_buf, 0) + 7) >> 3);
19462306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_DST1_STRIDE_CTRL,
19562306a36Sopenharmony_ci		     (ctx->out.pix_fmt.bytesperline + 7) >> 3);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_GEN_CTRL0, 0);
19862306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_GEN_CTRL1,
19962306a36Sopenharmony_ci		     FIELD_PREP(GE2D_INTERRUPT_CTRL, 2) |
20062306a36Sopenharmony_ci		     FIELD_PREP(GE2D_SRC2_BURST_SIZE_CTRL, 3) |
20162306a36Sopenharmony_ci		     FIELD_PREP(GE2D_SRC1_BURST_SIZE_CTRL, 0x3f));
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_GEN_CTRL2,
20462306a36Sopenharmony_ci		     GE2D_SRC1_LITTLE_ENDIAN |
20562306a36Sopenharmony_ci		     GE2D_SRC2_LITTLE_ENDIAN |
20662306a36Sopenharmony_ci		     GE2D_DST_LITTLE_ENDIAN |
20762306a36Sopenharmony_ci		     FIELD_PREP(GE2D_DST1_COLOR_MAP, ctx->out.fmt->hw_map) |
20862306a36Sopenharmony_ci		     FIELD_PREP(GE2D_DST1_FORMAT, ctx->out.fmt->hw_fmt) |
20962306a36Sopenharmony_ci		     FIELD_PREP(GE2D_SRC2_COLOR_MAP, ctx->out.fmt->hw_map) |
21062306a36Sopenharmony_ci		     FIELD_PREP(GE2D_SRC2_FORMAT, ctx->out.fmt->hw_fmt) |
21162306a36Sopenharmony_ci		     FIELD_PREP(GE2D_SRC1_COLOR_MAP, ctx->in.fmt->hw_map) |
21262306a36Sopenharmony_ci		     FIELD_PREP(GE2D_SRC1_FORMAT, ctx->in.fmt->hw_fmt));
21362306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_GEN_CTRL3,
21462306a36Sopenharmony_ci		     GE2D_DST1_ENABLE);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC1_CLIPY_START_END,
21762306a36Sopenharmony_ci		     FIELD_PREP(GE2D_START, ctx->in.crop.top) |
21862306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height - 1));
21962306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC1_CLIPX_START_END,
22062306a36Sopenharmony_ci		     FIELD_PREP(GE2D_START, ctx->in.crop.left) |
22162306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width - 1));
22262306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC2_CLIPY_START_END,
22362306a36Sopenharmony_ci		     FIELD_PREP(GE2D_START, ctx->out.crop.top) |
22462306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1));
22562306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC2_CLIPX_START_END,
22662306a36Sopenharmony_ci		     FIELD_PREP(GE2D_START, ctx->out.crop.left) |
22762306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1));
22862306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_DST_CLIPY_START_END,
22962306a36Sopenharmony_ci		     FIELD_PREP(GE2D_START, ctx->out.crop.top) |
23062306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1));
23162306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_DST_CLIPX_START_END,
23262306a36Sopenharmony_ci		     FIELD_PREP(GE2D_START, ctx->out.crop.left) |
23362306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1));
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC1_Y_START_END,
23662306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height - 1));
23762306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC1_X_START_END,
23862306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width - 1));
23962306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC2_Y_START_END,
24062306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1));
24162306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_SRC2_X_START_END,
24262306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1));
24362306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_DST_Y_START_END,
24462306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1));
24562306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_DST_X_START_END,
24662306a36Sopenharmony_ci		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1));
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Color, no blend, use source color */
24962306a36Sopenharmony_ci	reg = GE2D_ALU_DO_COLOR_OPERATION_LOGIC(LOGIC_OPERATION_COPY,
25062306a36Sopenharmony_ci						COLOR_FACTOR_SRC_COLOR);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (ctx->in.fmt->alpha && ctx->out.fmt->alpha)
25362306a36Sopenharmony_ci		/* Take source alpha */
25462306a36Sopenharmony_ci		reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_COPY,
25562306a36Sopenharmony_ci							 COLOR_FACTOR_SRC_ALPHA);
25662306a36Sopenharmony_ci	else if (!ctx->out.fmt->alpha)
25762306a36Sopenharmony_ci		/* Set alpha to 0 */
25862306a36Sopenharmony_ci		reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_SET,
25962306a36Sopenharmony_ci							 COLOR_FACTOR_ZERO);
26062306a36Sopenharmony_ci	else
26162306a36Sopenharmony_ci		/* Keep original alpha */
26262306a36Sopenharmony_ci		reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_COPY,
26362306a36Sopenharmony_ci							 COLOR_FACTOR_DST_ALPHA);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_ALU_OP_CTRL, reg);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* Start */
26862306a36Sopenharmony_ci	regmap_write(ge2d->map, GE2D_CMD_CTRL,
26962306a36Sopenharmony_ci		     (ctx->xy_swap ? GE2D_DST_XY_SWAP : 0) |
27062306a36Sopenharmony_ci		     (ctx->hflip ? GE2D_SRC1_Y_REV : 0) |
27162306a36Sopenharmony_ci		     (ctx->vflip ? GE2D_SRC1_X_REV : 0) |
27262306a36Sopenharmony_ci		     GE2D_CBUS_CMD_WR);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void device_run(void *priv)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
27862306a36Sopenharmony_ci	struct meson_ge2d *ge2d = ctx->ge2d;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	ge2d->curr = ctx;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ctx->in.buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
28362306a36Sopenharmony_ci	ctx->out.buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	ge2d_hw_start(ge2d);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic irqreturn_t ge2d_isr(int irq, void *priv)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct meson_ge2d *ge2d = priv;
29162306a36Sopenharmony_ci	u32 intr;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	regmap_read(ge2d->map, GE2D_STATUS0, &intr);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!(intr & GE2D_GE2D_BUSY)) {
29662306a36Sopenharmony_ci		struct vb2_v4l2_buffer *src, *dst;
29762306a36Sopenharmony_ci		struct ge2d_ctx *ctx = ge2d->curr;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		ge2d->curr = NULL;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
30262306a36Sopenharmony_ci		dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		src->sequence = ctx->sequence_out++;
30562306a36Sopenharmony_ci		dst->sequence = ctx->sequence_cap++;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		dst->timecode = src->timecode;
30862306a36Sopenharmony_ci		dst->vb2_buf.timestamp = src->vb2_buf.timestamp;
30962306a36Sopenharmony_ci		dst->flags = src->flags;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
31262306a36Sopenharmony_ci		v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
31362306a36Sopenharmony_ci		v4l2_m2m_job_finish(ge2d->m2m_dev, ctx->fh.m2m_ctx);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return IRQ_HANDLED;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic const struct v4l2_m2m_ops ge2d_m2m_ops = {
32062306a36Sopenharmony_ci	.device_run = device_run,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int ge2d_queue_setup(struct vb2_queue *vq,
32462306a36Sopenharmony_ci			    unsigned int *nbuffers, unsigned int *nplanes,
32562306a36Sopenharmony_ci			    unsigned int sizes[], struct device *alloc_devs[])
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct ge2d_ctx *ctx = vb2_get_drv_priv(vq);
32862306a36Sopenharmony_ci	struct ge2d_frame *f = get_frame(ctx, vq->type);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (*nplanes)
33162306a36Sopenharmony_ci		return sizes[0] < f->pix_fmt.sizeimage ? -EINVAL : 0;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	sizes[0] = f->pix_fmt.sizeimage;
33462306a36Sopenharmony_ci	*nplanes = 1;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return 0;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int ge2d_buf_prepare(struct vb2_buffer *vb)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct ge2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
34262306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
34362306a36Sopenharmony_ci	struct ge2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	vbuf->field = V4L2_FIELD_NONE;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, f->pix_fmt.sizeimage);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void ge2d_buf_queue(struct vb2_buffer *vb)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
35562306a36Sopenharmony_ci	struct ge2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int ge2d_start_streaming(struct vb2_queue *vq, unsigned int count)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct ge2d_ctx *ctx = vb2_get_drv_priv(vq);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type))
36562306a36Sopenharmony_ci		ctx->sequence_out = 0;
36662306a36Sopenharmony_ci	else
36762306a36Sopenharmony_ci		ctx->sequence_cap = 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return 0;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void ge2d_stop_streaming(struct vb2_queue *vq)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct ge2d_ctx *ctx = vb2_get_drv_priv(vq);
37562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	for (;;) {
37862306a36Sopenharmony_ci		if (V4L2_TYPE_IS_OUTPUT(vq->type))
37962306a36Sopenharmony_ci			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
38062306a36Sopenharmony_ci		else
38162306a36Sopenharmony_ci			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
38262306a36Sopenharmony_ci		if (!vbuf)
38362306a36Sopenharmony_ci			break;
38462306a36Sopenharmony_ci		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic const struct vb2_ops ge2d_qops = {
38962306a36Sopenharmony_ci	.queue_setup = ge2d_queue_setup,
39062306a36Sopenharmony_ci	.buf_prepare = ge2d_buf_prepare,
39162306a36Sopenharmony_ci	.buf_queue = ge2d_buf_queue,
39262306a36Sopenharmony_ci	.start_streaming = ge2d_start_streaming,
39362306a36Sopenharmony_ci	.stop_streaming = ge2d_stop_streaming,
39462306a36Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
39562306a36Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
39662306a36Sopenharmony_ci};
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int
39962306a36Sopenharmony_ciqueue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
40262306a36Sopenharmony_ci	int ret;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
40562306a36Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
40662306a36Sopenharmony_ci	src_vq->drv_priv = ctx;
40762306a36Sopenharmony_ci	src_vq->ops = &ge2d_qops;
40862306a36Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
40962306a36Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
41062306a36Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
41162306a36Sopenharmony_ci	src_vq->lock = &ctx->ge2d->mutex;
41262306a36Sopenharmony_ci	src_vq->dev = ctx->ge2d->v4l2_dev.dev;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = vb2_queue_init(src_vq);
41562306a36Sopenharmony_ci	if (ret)
41662306a36Sopenharmony_ci		return ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
41962306a36Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
42062306a36Sopenharmony_ci	dst_vq->drv_priv = ctx;
42162306a36Sopenharmony_ci	dst_vq->ops = &ge2d_qops;
42262306a36Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
42362306a36Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
42462306a36Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
42562306a36Sopenharmony_ci	dst_vq->lock = &ctx->ge2d->mutex;
42662306a36Sopenharmony_ci	dst_vq->dev = ctx->ge2d->v4l2_dev.dev;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return vb2_queue_init(dst_vq);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int
43262306a36Sopenharmony_cividioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	strscpy(cap->driver, GE2D_NAME, sizeof(cap->driver));
43562306a36Sopenharmony_ci	strscpy(cap->card, GE2D_NAME, sizeof(cap->card));
43662306a36Sopenharmony_ci	strscpy(cap->bus_info, "platform:" GE2D_NAME, sizeof(cap->bus_info));
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	const struct ge2d_fmt *fmt;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (f->index >= NUM_FORMATS)
44662306a36Sopenharmony_ci		return -EINVAL;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	fmt = &formats[f->index];
44962306a36Sopenharmony_ci	f->pixelformat = fmt->fourcc;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return 0;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *priv,
45562306a36Sopenharmony_ci			      struct v4l2_selection *s)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
45862306a36Sopenharmony_ci	struct ge2d_frame *f;
45962306a36Sopenharmony_ci	bool use_frame = false;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
46262306a36Sopenharmony_ci	    s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
46362306a36Sopenharmony_ci		return -EINVAL;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	f = get_frame(ctx, s->type);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	switch (s->target) {
46862306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
46962306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
47062306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
47162306a36Sopenharmony_ci			return -EINVAL;
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
47462306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
47562306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
47662306a36Sopenharmony_ci			return -EINVAL;
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
47962306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
48062306a36Sopenharmony_ci			return -EINVAL;
48162306a36Sopenharmony_ci		use_frame = true;
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
48462306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
48562306a36Sopenharmony_ci			return -EINVAL;
48662306a36Sopenharmony_ci		use_frame = true;
48762306a36Sopenharmony_ci		break;
48862306a36Sopenharmony_ci	default:
48962306a36Sopenharmony_ci		return -EINVAL;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (use_frame) {
49362306a36Sopenharmony_ci		s->r = f->crop;
49462306a36Sopenharmony_ci	} else {
49562306a36Sopenharmony_ci		s->r.left = 0;
49662306a36Sopenharmony_ci		s->r.top = 0;
49762306a36Sopenharmony_ci		s->r.width = f->pix_fmt.width;
49862306a36Sopenharmony_ci		s->r.height = f->pix_fmt.height;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int vidioc_s_selection(struct file *file, void *priv,
50562306a36Sopenharmony_ci			      struct v4l2_selection *s)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
50862306a36Sopenharmony_ci	struct meson_ge2d *ge2d = ctx->ge2d;
50962306a36Sopenharmony_ci	struct ge2d_frame *f;
51062306a36Sopenharmony_ci	int ret = 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
51362306a36Sopenharmony_ci	    s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	f = get_frame(ctx, s->type);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	switch (s->target) {
51962306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
52062306a36Sopenharmony_ci		/*
52162306a36Sopenharmony_ci		 * COMPOSE target is only valid for capture buffer type, return
52262306a36Sopenharmony_ci		 * error for output buffer type
52362306a36Sopenharmony_ci		 */
52462306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
52562306a36Sopenharmony_ci			return -EINVAL;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
52862306a36Sopenharmony_ci		/*
52962306a36Sopenharmony_ci		 * CROP target is only valid for output buffer type, return
53062306a36Sopenharmony_ci		 * error for capture buffer type
53162306a36Sopenharmony_ci		 */
53262306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
53362306a36Sopenharmony_ci			return -EINVAL;
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci	/*
53662306a36Sopenharmony_ci	 * bound and default crop/compose targets are invalid targets to
53762306a36Sopenharmony_ci	 * try/set
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci	default:
54062306a36Sopenharmony_ci		return -EINVAL;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (s->r.top < 0 || s->r.left < 0) {
54462306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev,
54562306a36Sopenharmony_ci			 "doesn't support negative values for top & left.\n");
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (s->r.left + s->r.width > f->pix_fmt.width ||
55062306a36Sopenharmony_ci	    s->r.top + s->r.height > f->pix_fmt.height) {
55162306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "unsupported rectangle value.\n");
55262306a36Sopenharmony_ci		return -EINVAL;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	f->crop = s->r;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return ret;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void vidioc_setup_cap_fmt(struct ge2d_ctx *ctx, struct v4l2_pix_format *f)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct ge2d_frame *frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	*f = frm_out->pix_fmt;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (ctx->xy_swap) {
56762306a36Sopenharmony_ci		f->width = frm_out->pix_fmt.height;
56862306a36Sopenharmony_ci		f->height = frm_out->pix_fmt.width;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	const struct ge2d_fmt *fmt = find_fmt(f);
57562306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
57662306a36Sopenharmony_ci	struct v4l2_pix_format fmt_cap;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	vidioc_setup_cap_fmt(ctx, &fmt_cap);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	fmt_cap.pixelformat = fmt->fourcc;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	fmt_cap.bytesperline = max(f->fmt.pix.bytesperline,
58362306a36Sopenharmony_ci				   ALIGN((fmt_cap.width * fmt->depth) >> 3, 8));
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	fmt_cap.sizeimage = max(f->fmt.pix.sizeimage,
58662306a36Sopenharmony_ci				fmt_cap.height * fmt_cap.bytesperline);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	f->fmt.pix = fmt_cap;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
59662306a36Sopenharmony_ci	struct meson_ge2d *ge2d = ctx->ge2d;
59762306a36Sopenharmony_ci	struct vb2_queue *vq;
59862306a36Sopenharmony_ci	struct ge2d_frame *frm;
59962306a36Sopenharmony_ci	int ret = 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Adjust all values accordingly to the hardware capabilities
60262306a36Sopenharmony_ci	 * and chosen format.
60362306a36Sopenharmony_ci	 */
60462306a36Sopenharmony_ci	ret = vidioc_try_fmt_cap(file, priv, f);
60562306a36Sopenharmony_ci	if (ret)
60662306a36Sopenharmony_ci		return ret;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
60962306a36Sopenharmony_ci	if (vb2_is_busy(vq)) {
61062306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "queue (%d) bust\n", f->type);
61162306a36Sopenharmony_ci		return -EBUSY;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	frm->pix_fmt = f->fmt.pix;
61762306a36Sopenharmony_ci	frm->fmt = find_fmt(f);
61862306a36Sopenharmony_ci	f->fmt.pix.pixelformat = frm->fmt->fourcc;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Reset crop settings */
62162306a36Sopenharmony_ci	frm->crop.left = 0;
62262306a36Sopenharmony_ci	frm->crop.top = 0;
62362306a36Sopenharmony_ci	frm->crop.width = frm->pix_fmt.width;
62462306a36Sopenharmony_ci	frm->crop.height = frm->pix_fmt.height;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	return 0;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
63262306a36Sopenharmony_ci	struct vb2_queue *vq;
63362306a36Sopenharmony_ci	struct ge2d_frame *frm;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
63662306a36Sopenharmony_ci	if (!vq)
63762306a36Sopenharmony_ci		return -EINVAL;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	frm = get_frame(ctx, f->type);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	f->fmt.pix = frm->pix_fmt;
64262306a36Sopenharmony_ci	f->fmt.pix.pixelformat = frm->fmt->fourcc;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *f)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	const struct ge2d_fmt *fmt = find_fmt(f);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	f->fmt.pix.field = V4L2_FIELD_NONE;
65262306a36Sopenharmony_ci	f->fmt.pix.pixelformat = fmt->fourcc;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (f->fmt.pix.width > MAX_WIDTH)
65562306a36Sopenharmony_ci		f->fmt.pix.width = MAX_WIDTH;
65662306a36Sopenharmony_ci	if (f->fmt.pix.height > MAX_HEIGHT)
65762306a36Sopenharmony_ci		f->fmt.pix.height = MAX_HEIGHT;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	f->fmt.pix.bytesperline = max(f->fmt.pix.bytesperline,
66062306a36Sopenharmony_ci				      ALIGN((f->fmt.pix.width * fmt->depth) >> 3, 8));
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	f->fmt.pix.sizeimage = max(f->fmt.pix.sizeimage,
66362306a36Sopenharmony_ci				   f->fmt.pix.height * f->fmt.pix.bytesperline);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return 0;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic int vidioc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *f)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct ge2d_ctx *ctx = priv;
67162306a36Sopenharmony_ci	struct meson_ge2d *ge2d = ctx->ge2d;
67262306a36Sopenharmony_ci	struct vb2_queue *vq;
67362306a36Sopenharmony_ci	struct ge2d_frame *frm, *frm_cap;
67462306a36Sopenharmony_ci	int ret = 0;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Adjust all values accordingly to the hardware capabilities
67762306a36Sopenharmony_ci	 * and chosen format.
67862306a36Sopenharmony_ci	 */
67962306a36Sopenharmony_ci	ret = vidioc_try_fmt_out(file, priv, f);
68062306a36Sopenharmony_ci	if (ret)
68162306a36Sopenharmony_ci		return ret;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
68462306a36Sopenharmony_ci	if (vb2_is_busy(vq)) {
68562306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "queue (%d) bust\n", f->type);
68662306a36Sopenharmony_ci		return -EBUSY;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
69062306a36Sopenharmony_ci	frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	frm->pix_fmt = f->fmt.pix;
69362306a36Sopenharmony_ci	frm->fmt = find_fmt(f);
69462306a36Sopenharmony_ci	f->fmt.pix.pixelformat = frm->fmt->fourcc;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* Reset crop settings */
69762306a36Sopenharmony_ci	frm->crop.left = 0;
69862306a36Sopenharmony_ci	frm->crop.top = 0;
69962306a36Sopenharmony_ci	frm->crop.width = frm->pix_fmt.width;
70062306a36Sopenharmony_ci	frm->crop.height = frm->pix_fmt.height;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* Propagate settings to capture */
70362306a36Sopenharmony_ci	vidioc_setup_cap_fmt(ctx, &frm_cap->pix_fmt);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return 0;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops ge2d_ioctl_ops = {
70962306a36Sopenharmony_ci	.vidioc_querycap = vidioc_querycap,
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
71262306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap = vidioc_g_fmt,
71362306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap,
71462306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap,
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
71762306a36Sopenharmony_ci	.vidioc_g_fmt_vid_out = vidioc_g_fmt,
71862306a36Sopenharmony_ci	.vidioc_try_fmt_vid_out = vidioc_try_fmt_out,
71962306a36Sopenharmony_ci	.vidioc_s_fmt_vid_out = vidioc_s_fmt_out,
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
72262306a36Sopenharmony_ci	.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
72362306a36Sopenharmony_ci	.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
72462306a36Sopenharmony_ci	.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
72562306a36Sopenharmony_ci	.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
72662306a36Sopenharmony_ci	.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
72762306a36Sopenharmony_ci	.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
73062306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	.vidioc_streamon = v4l2_m2m_ioctl_streamon,
73362306a36Sopenharmony_ci	.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	.vidioc_g_selection = vidioc_g_selection,
73662306a36Sopenharmony_ci	.vidioc_s_selection = vidioc_s_selection,
73762306a36Sopenharmony_ci};
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int ge2d_s_ctrl(struct v4l2_ctrl *ctrl)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct ge2d_ctx *ctx = container_of(ctrl->handler, struct ge2d_ctx,
74262306a36Sopenharmony_ci					   ctrl_handler);
74362306a36Sopenharmony_ci	struct v4l2_pix_format fmt;
74462306a36Sopenharmony_ci	struct vb2_queue *vq;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	switch (ctrl->id) {
74762306a36Sopenharmony_ci	case V4L2_CID_HFLIP:
74862306a36Sopenharmony_ci		ctx->hflip = ctrl->val;
74962306a36Sopenharmony_ci		break;
75062306a36Sopenharmony_ci	case V4L2_CID_VFLIP:
75162306a36Sopenharmony_ci		ctx->vflip = ctrl->val;
75262306a36Sopenharmony_ci		break;
75362306a36Sopenharmony_ci	case V4L2_CID_ROTATE:
75462306a36Sopenharmony_ci		vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
75562306a36Sopenharmony_ci		if (vb2_is_busy(vq))
75662306a36Sopenharmony_ci			return -EBUSY;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		if (ctrl->val == 90) {
75962306a36Sopenharmony_ci			ctx->hflip = 0;
76062306a36Sopenharmony_ci			ctx->vflip = 1;
76162306a36Sopenharmony_ci			ctx->xy_swap = 1;
76262306a36Sopenharmony_ci		} else if (ctrl->val == 180) {
76362306a36Sopenharmony_ci			ctx->hflip = 1;
76462306a36Sopenharmony_ci			ctx->vflip = 1;
76562306a36Sopenharmony_ci			ctx->xy_swap = 0;
76662306a36Sopenharmony_ci		} else if (ctrl->val == 270) {
76762306a36Sopenharmony_ci			ctx->hflip = 1;
76862306a36Sopenharmony_ci			ctx->vflip = 0;
76962306a36Sopenharmony_ci			ctx->xy_swap = 1;
77062306a36Sopenharmony_ci		} else {
77162306a36Sopenharmony_ci			ctx->hflip = 0;
77262306a36Sopenharmony_ci			ctx->vflip = 0;
77362306a36Sopenharmony_ci			ctx->xy_swap = 0;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		vidioc_setup_cap_fmt(ctx, &fmt);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		/*
77962306a36Sopenharmony_ci		 * If the rotation parameter changes the OUTPUT frames
78062306a36Sopenharmony_ci		 * parameters, take them in account
78162306a36Sopenharmony_ci		 */
78262306a36Sopenharmony_ci		ctx->out.pix_fmt = fmt;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		break;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops ge2d_ctrl_ops = {
79162306a36Sopenharmony_ci	.s_ctrl = ge2d_s_ctrl,
79262306a36Sopenharmony_ci};
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int ge2d_setup_ctrls(struct ge2d_ctx *ctx)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct meson_ge2d *ge2d = ctx->ge2d;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops,
80162306a36Sopenharmony_ci			  V4L2_CID_HFLIP, 0, 1, 1, 0);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops,
80462306a36Sopenharmony_ci			  V4L2_CID_VFLIP, 0, 1, 1, 0);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops,
80762306a36Sopenharmony_ci			  V4L2_CID_ROTATE, 0, 270, 90, 0);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (ctx->ctrl_handler.error) {
81062306a36Sopenharmony_ci		int err = ctx->ctrl_handler.error;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "%s failed\n", __func__);
81362306a36Sopenharmony_ci		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
81462306a36Sopenharmony_ci		return err;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	return 0;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic const struct ge2d_frame def_frame = {
82162306a36Sopenharmony_ci	.pix_fmt = {
82262306a36Sopenharmony_ci		.width = DEFAULT_WIDTH,
82362306a36Sopenharmony_ci		.height = DEFAULT_HEIGHT,
82462306a36Sopenharmony_ci		.bytesperline = DEFAULT_STRIDE,
82562306a36Sopenharmony_ci		.sizeimage = DEFAULT_STRIDE * DEFAULT_HEIGHT,
82662306a36Sopenharmony_ci		.field = V4L2_FIELD_NONE,
82762306a36Sopenharmony_ci	},
82862306a36Sopenharmony_ci	.crop.width = DEFAULT_WIDTH,
82962306a36Sopenharmony_ci	.crop.height = DEFAULT_HEIGHT,
83062306a36Sopenharmony_ci	.fmt = &formats[0],
83162306a36Sopenharmony_ci};
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic int ge2d_open(struct file *file)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct meson_ge2d *ge2d = video_drvdata(file);
83662306a36Sopenharmony_ci	struct ge2d_ctx *ctx = NULL;
83762306a36Sopenharmony_ci	int ret = 0;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
84062306a36Sopenharmony_ci	if (!ctx)
84162306a36Sopenharmony_ci		return -ENOMEM;
84262306a36Sopenharmony_ci	ctx->ge2d = ge2d;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Set default formats */
84562306a36Sopenharmony_ci	ctx->in = def_frame;
84662306a36Sopenharmony_ci	ctx->out = def_frame;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (mutex_lock_interruptible(&ge2d->mutex)) {
84962306a36Sopenharmony_ci		kfree(ctx);
85062306a36Sopenharmony_ci		return -ERESTARTSYS;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ge2d->m2m_dev, ctx, &queue_init);
85362306a36Sopenharmony_ci	if (IS_ERR(ctx->fh.m2m_ctx)) {
85462306a36Sopenharmony_ci		ret = PTR_ERR(ctx->fh.m2m_ctx);
85562306a36Sopenharmony_ci		mutex_unlock(&ge2d->mutex);
85662306a36Sopenharmony_ci		kfree(ctx);
85762306a36Sopenharmony_ci		return ret;
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci	v4l2_fh_init(&ctx->fh, video_devdata(file));
86062306a36Sopenharmony_ci	file->private_data = &ctx->fh;
86162306a36Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	ge2d_setup_ctrls(ctx);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Write the default values to the ctx struct */
86662306a36Sopenharmony_ci	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
86962306a36Sopenharmony_ci	mutex_unlock(&ge2d->mutex);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int ge2d_release(struct file *file)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	struct ge2d_ctx *ctx =
87762306a36Sopenharmony_ci		container_of(file->private_data, struct ge2d_ctx, fh);
87862306a36Sopenharmony_ci	struct meson_ge2d *ge2d = ctx->ge2d;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	mutex_lock(&ge2d->mutex);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
88562306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
88662306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
88762306a36Sopenharmony_ci	kfree(ctx);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	mutex_unlock(&ge2d->mutex);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	return 0;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic const struct v4l2_file_operations ge2d_fops = {
89562306a36Sopenharmony_ci	.owner = THIS_MODULE,
89662306a36Sopenharmony_ci	.open = ge2d_open,
89762306a36Sopenharmony_ci	.release = ge2d_release,
89862306a36Sopenharmony_ci	.poll = v4l2_m2m_fop_poll,
89962306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
90062306a36Sopenharmony_ci	.mmap = v4l2_m2m_fop_mmap,
90162306a36Sopenharmony_ci};
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic const struct video_device ge2d_videodev = {
90462306a36Sopenharmony_ci	.name = "meson-ge2d",
90562306a36Sopenharmony_ci	.fops = &ge2d_fops,
90662306a36Sopenharmony_ci	.ioctl_ops = &ge2d_ioctl_ops,
90762306a36Sopenharmony_ci	.minor = -1,
90862306a36Sopenharmony_ci	.release = video_device_release,
90962306a36Sopenharmony_ci	.vfl_dir = VFL_DIR_M2M,
91062306a36Sopenharmony_ci	.device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
91162306a36Sopenharmony_ci};
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic const struct regmap_config meson_ge2d_regmap_conf = {
91462306a36Sopenharmony_ci	.reg_bits = 8,
91562306a36Sopenharmony_ci	.val_bits = 32,
91662306a36Sopenharmony_ci	.reg_stride = 4,
91762306a36Sopenharmony_ci	.max_register = GE2D_SRC2_STRIDE_CTRL,
91862306a36Sopenharmony_ci};
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int ge2d_probe(struct platform_device *pdev)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct reset_control *rst;
92362306a36Sopenharmony_ci	struct video_device *vfd;
92462306a36Sopenharmony_ci	struct meson_ge2d *ge2d;
92562306a36Sopenharmony_ci	void __iomem *regs;
92662306a36Sopenharmony_ci	int ret = 0;
92762306a36Sopenharmony_ci	int irq;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (!pdev->dev.of_node)
93062306a36Sopenharmony_ci		return -ENODEV;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	ge2d = devm_kzalloc(&pdev->dev, sizeof(*ge2d), GFP_KERNEL);
93362306a36Sopenharmony_ci	if (!ge2d)
93462306a36Sopenharmony_ci		return -ENOMEM;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	ge2d->dev = &pdev->dev;
93762306a36Sopenharmony_ci	mutex_init(&ge2d->mutex);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	regs = devm_platform_ioremap_resource(pdev, 0);
94062306a36Sopenharmony_ci	if (IS_ERR(regs))
94162306a36Sopenharmony_ci		return PTR_ERR(regs);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	ge2d->map = devm_regmap_init_mmio(ge2d->dev, regs,
94462306a36Sopenharmony_ci					  &meson_ge2d_regmap_conf);
94562306a36Sopenharmony_ci	if (IS_ERR(ge2d->map))
94662306a36Sopenharmony_ci		return PTR_ERR(ge2d->map);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
94962306a36Sopenharmony_ci	ret = devm_request_irq(ge2d->dev, irq, ge2d_isr, 0,
95062306a36Sopenharmony_ci			       dev_name(ge2d->dev), ge2d);
95162306a36Sopenharmony_ci	if (ret < 0) {
95262306a36Sopenharmony_ci		dev_err(ge2d->dev, "failed to request irq\n");
95362306a36Sopenharmony_ci		return ret;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	rst = devm_reset_control_get(ge2d->dev, NULL);
95762306a36Sopenharmony_ci	if (IS_ERR(rst)) {
95862306a36Sopenharmony_ci		dev_err(ge2d->dev, "failed to get core reset controller\n");
95962306a36Sopenharmony_ci		return PTR_ERR(rst);
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	ge2d->clk = devm_clk_get(ge2d->dev, NULL);
96362306a36Sopenharmony_ci	if (IS_ERR(ge2d->clk)) {
96462306a36Sopenharmony_ci		dev_err(ge2d->dev, "failed to get clock\n");
96562306a36Sopenharmony_ci		return PTR_ERR(ge2d->clk);
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	reset_control_assert(rst);
96962306a36Sopenharmony_ci	udelay(1);
97062306a36Sopenharmony_ci	reset_control_deassert(rst);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	ret = clk_prepare_enable(ge2d->clk);
97362306a36Sopenharmony_ci	if (ret) {
97462306a36Sopenharmony_ci		dev_err(ge2d->dev, "Cannot enable ge2d sclk: %d\n", ret);
97562306a36Sopenharmony_ci		return ret;
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &ge2d->v4l2_dev);
97962306a36Sopenharmony_ci	if (ret)
98062306a36Sopenharmony_ci		goto disable_clks;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	vfd = video_device_alloc();
98362306a36Sopenharmony_ci	if (!vfd) {
98462306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "Failed to allocate video device\n");
98562306a36Sopenharmony_ci		ret = -ENOMEM;
98662306a36Sopenharmony_ci		goto unreg_v4l2_dev;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	*vfd = ge2d_videodev;
99062306a36Sopenharmony_ci	vfd->lock = &ge2d->mutex;
99162306a36Sopenharmony_ci	vfd->v4l2_dev = &ge2d->v4l2_dev;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	video_set_drvdata(vfd, ge2d);
99462306a36Sopenharmony_ci	ge2d->vfd = vfd;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	platform_set_drvdata(pdev, ge2d);
99762306a36Sopenharmony_ci	ge2d->m2m_dev = v4l2_m2m_init(&ge2d_m2m_ops);
99862306a36Sopenharmony_ci	if (IS_ERR(ge2d->m2m_dev)) {
99962306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "Failed to init mem2mem device\n");
100062306a36Sopenharmony_ci		ret = PTR_ERR(ge2d->m2m_dev);
100162306a36Sopenharmony_ci		goto rel_vdev;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
100562306a36Sopenharmony_ci	if (ret) {
100662306a36Sopenharmony_ci		v4l2_err(&ge2d->v4l2_dev, "Failed to register video device\n");
100762306a36Sopenharmony_ci		goto rel_m2m;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	v4l2_info(&ge2d->v4l2_dev, "Registered %s as /dev/%s\n",
101162306a36Sopenharmony_ci		  vfd->name, video_device_node_name(vfd));
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	return 0;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cirel_m2m:
101662306a36Sopenharmony_ci	v4l2_m2m_release(ge2d->m2m_dev);
101762306a36Sopenharmony_cirel_vdev:
101862306a36Sopenharmony_ci	video_device_release(ge2d->vfd);
101962306a36Sopenharmony_ciunreg_v4l2_dev:
102062306a36Sopenharmony_ci	v4l2_device_unregister(&ge2d->v4l2_dev);
102162306a36Sopenharmony_cidisable_clks:
102262306a36Sopenharmony_ci	clk_disable_unprepare(ge2d->clk);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return ret;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic void ge2d_remove(struct platform_device *pdev)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	struct meson_ge2d *ge2d = platform_get_drvdata(pdev);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	video_unregister_device(ge2d->vfd);
103262306a36Sopenharmony_ci	v4l2_m2m_release(ge2d->m2m_dev);
103362306a36Sopenharmony_ci	v4l2_device_unregister(&ge2d->v4l2_dev);
103462306a36Sopenharmony_ci	clk_disable_unprepare(ge2d->clk);
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic const struct of_device_id meson_ge2d_match[] = {
103862306a36Sopenharmony_ci	{
103962306a36Sopenharmony_ci		.compatible = "amlogic,axg-ge2d",
104062306a36Sopenharmony_ci	},
104162306a36Sopenharmony_ci	{},
104262306a36Sopenharmony_ci};
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_ge2d_match);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic struct platform_driver ge2d_drv = {
104762306a36Sopenharmony_ci	.probe = ge2d_probe,
104862306a36Sopenharmony_ci	.remove_new = ge2d_remove,
104962306a36Sopenharmony_ci	.driver = {
105062306a36Sopenharmony_ci		.name = "meson-ge2d",
105162306a36Sopenharmony_ci		.of_match_table = meson_ge2d_match,
105262306a36Sopenharmony_ci	},
105362306a36Sopenharmony_ci};
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cimodule_platform_driver(ge2d_drv);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
105862306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic 2D Graphic Acceleration Unit");
105962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1060