18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Samsung S5P G2D - 2D Graphics Accelerator Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Kamil Debski, <k.debski@samsung.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/fs.h>
118c2ecf20Sopenharmony_ci#include <linux/timer.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/clk.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-mem2mem.h>
208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
228c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h>
238c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "g2d.h"
268c2ecf20Sopenharmony_ci#include "g2d-regs.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct g2d_fmt formats[] = {
318c2ecf20Sopenharmony_ci	{
328c2ecf20Sopenharmony_ci		.fourcc	= V4L2_PIX_FMT_RGB32,
338c2ecf20Sopenharmony_ci		.depth	= 32,
348c2ecf20Sopenharmony_ci		.hw	= COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888),
358c2ecf20Sopenharmony_ci	},
368c2ecf20Sopenharmony_ci	{
378c2ecf20Sopenharmony_ci		.fourcc	= V4L2_PIX_FMT_RGB565X,
388c2ecf20Sopenharmony_ci		.depth	= 16,
398c2ecf20Sopenharmony_ci		.hw	= COLOR_MODE(ORDER_XRGB, MODE_RGB_565),
408c2ecf20Sopenharmony_ci	},
418c2ecf20Sopenharmony_ci	{
428c2ecf20Sopenharmony_ci		.fourcc	= V4L2_PIX_FMT_RGB555X,
438c2ecf20Sopenharmony_ci		.depth	= 16,
448c2ecf20Sopenharmony_ci		.hw	= COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555),
458c2ecf20Sopenharmony_ci	},
468c2ecf20Sopenharmony_ci	{
478c2ecf20Sopenharmony_ci		.fourcc	= V4L2_PIX_FMT_RGB444,
488c2ecf20Sopenharmony_ci		.depth	= 16,
498c2ecf20Sopenharmony_ci		.hw	= COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444),
508c2ecf20Sopenharmony_ci	},
518c2ecf20Sopenharmony_ci	{
528c2ecf20Sopenharmony_ci		.fourcc	= V4L2_PIX_FMT_RGB24,
538c2ecf20Sopenharmony_ci		.depth	= 24,
548c2ecf20Sopenharmony_ci		.hw	= COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888),
558c2ecf20Sopenharmony_ci	},
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci#define NUM_FORMATS ARRAY_SIZE(formats)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct g2d_frame def_frame = {
608c2ecf20Sopenharmony_ci	.width		= DEFAULT_WIDTH,
618c2ecf20Sopenharmony_ci	.height		= DEFAULT_HEIGHT,
628c2ecf20Sopenharmony_ci	.c_width	= DEFAULT_WIDTH,
638c2ecf20Sopenharmony_ci	.c_height	= DEFAULT_HEIGHT,
648c2ecf20Sopenharmony_ci	.o_width	= 0,
658c2ecf20Sopenharmony_ci	.o_height	= 0,
668c2ecf20Sopenharmony_ci	.fmt		= &formats[0],
678c2ecf20Sopenharmony_ci	.right		= DEFAULT_WIDTH,
688c2ecf20Sopenharmony_ci	.bottom		= DEFAULT_HEIGHT,
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic struct g2d_fmt *find_fmt(struct v4l2_format *f)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	unsigned int i;
748c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_FORMATS; i++) {
758c2ecf20Sopenharmony_ci		if (formats[i].fourcc == f->fmt.pix.pixelformat)
768c2ecf20Sopenharmony_ci			return &formats[i];
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	return NULL;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct g2d_frame *get_frame(struct g2d_ctx *ctx,
838c2ecf20Sopenharmony_ci				   enum v4l2_buf_type type)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	switch (type) {
868c2ecf20Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
878c2ecf20Sopenharmony_ci		return &ctx->in;
888c2ecf20Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
898c2ecf20Sopenharmony_ci		return &ctx->out;
908c2ecf20Sopenharmony_ci	default:
918c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int g2d_queue_setup(struct vb2_queue *vq,
968c2ecf20Sopenharmony_ci			   unsigned int *nbuffers, unsigned int *nplanes,
978c2ecf20Sopenharmony_ci			   unsigned int sizes[], struct device *alloc_devs[])
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = vb2_get_drv_priv(vq);
1008c2ecf20Sopenharmony_ci	struct g2d_frame *f = get_frame(ctx, vq->type);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (IS_ERR(f))
1038c2ecf20Sopenharmony_ci		return PTR_ERR(f);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	sizes[0] = f->size;
1068c2ecf20Sopenharmony_ci	*nplanes = 1;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (*nbuffers == 0)
1098c2ecf20Sopenharmony_ci		*nbuffers = 1;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int g2d_buf_prepare(struct vb2_buffer *vb)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
1178c2ecf20Sopenharmony_ci	struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (IS_ERR(f))
1208c2ecf20Sopenharmony_ci		return PTR_ERR(f);
1218c2ecf20Sopenharmony_ci	vb2_set_plane_payload(vb, 0, f->size);
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void g2d_buf_queue(struct vb2_buffer *vb)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
1288c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
1298c2ecf20Sopenharmony_ci	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic const struct vb2_ops g2d_qops = {
1338c2ecf20Sopenharmony_ci	.queue_setup	= g2d_queue_setup,
1348c2ecf20Sopenharmony_ci	.buf_prepare	= g2d_buf_prepare,
1358c2ecf20Sopenharmony_ci	.buf_queue	= g2d_buf_queue,
1368c2ecf20Sopenharmony_ci	.wait_prepare	= vb2_ops_wait_prepare,
1378c2ecf20Sopenharmony_ci	.wait_finish	= vb2_ops_wait_finish,
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq,
1418c2ecf20Sopenharmony_ci						struct vb2_queue *dst_vq)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = priv;
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
1478c2ecf20Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
1488c2ecf20Sopenharmony_ci	src_vq->drv_priv = ctx;
1498c2ecf20Sopenharmony_ci	src_vq->ops = &g2d_qops;
1508c2ecf20Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
1518c2ecf20Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
1528c2ecf20Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
1538c2ecf20Sopenharmony_ci	src_vq->lock = &ctx->dev->mutex;
1548c2ecf20Sopenharmony_ci	src_vq->dev = ctx->dev->v4l2_dev.dev;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = vb2_queue_init(src_vq);
1578c2ecf20Sopenharmony_ci	if (ret)
1588c2ecf20Sopenharmony_ci		return ret;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1618c2ecf20Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
1628c2ecf20Sopenharmony_ci	dst_vq->drv_priv = ctx;
1638c2ecf20Sopenharmony_ci	dst_vq->ops = &g2d_qops;
1648c2ecf20Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
1658c2ecf20Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
1668c2ecf20Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
1678c2ecf20Sopenharmony_ci	dst_vq->lock = &ctx->dev->mutex;
1688c2ecf20Sopenharmony_ci	dst_vq->dev = ctx->dev->v4l2_dev.dev;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return vb2_queue_init(dst_vq);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int g2d_s_ctrl(struct v4l2_ctrl *ctrl)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx,
1768c2ecf20Sopenharmony_ci								ctrl_handler);
1778c2ecf20Sopenharmony_ci	unsigned long flags;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
1808c2ecf20Sopenharmony_ci	switch (ctrl->id) {
1818c2ecf20Sopenharmony_ci	case V4L2_CID_COLORFX:
1828c2ecf20Sopenharmony_ci		if (ctrl->val == V4L2_COLORFX_NEGATIVE)
1838c2ecf20Sopenharmony_ci			ctx->rop = ROP4_INVERT;
1848c2ecf20Sopenharmony_ci		else
1858c2ecf20Sopenharmony_ci			ctx->rop = ROP4_COPY;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	case V4L2_CID_HFLIP:
1898c2ecf20Sopenharmony_ci		ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1);
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops g2d_ctrl_ops = {
1988c2ecf20Sopenharmony_ci	.s_ctrl		= g2d_s_ctrl,
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int g2d_setup_ctrls(struct g2d_ctx *ctx)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct g2d_dev *dev = ctx->dev;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
2088c2ecf20Sopenharmony_ci						V4L2_CID_HFLIP, 0, 1, 1, 0);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
2118c2ecf20Sopenharmony_ci						V4L2_CID_VFLIP, 0, 1, 1, 0);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(
2148c2ecf20Sopenharmony_ci		&ctx->ctrl_handler,
2158c2ecf20Sopenharmony_ci		&g2d_ctrl_ops,
2168c2ecf20Sopenharmony_ci		V4L2_CID_COLORFX,
2178c2ecf20Sopenharmony_ci		V4L2_COLORFX_NEGATIVE,
2188c2ecf20Sopenharmony_ci		~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)),
2198c2ecf20Sopenharmony_ci		V4L2_COLORFX_NONE);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (ctx->ctrl_handler.error) {
2228c2ecf20Sopenharmony_ci		int err = ctx->ctrl_handler.error;
2238c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n");
2248c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
2258c2ecf20Sopenharmony_ci		return err;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	v4l2_ctrl_cluster(2, &ctx->ctrl_hflip);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	return 0;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int g2d_open(struct file *file)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct g2d_dev *dev = video_drvdata(file);
2368c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = NULL;
2378c2ecf20Sopenharmony_ci	int ret = 0;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
2408c2ecf20Sopenharmony_ci	if (!ctx)
2418c2ecf20Sopenharmony_ci		return -ENOMEM;
2428c2ecf20Sopenharmony_ci	ctx->dev = dev;
2438c2ecf20Sopenharmony_ci	/* Set default formats */
2448c2ecf20Sopenharmony_ci	ctx->in		= def_frame;
2458c2ecf20Sopenharmony_ci	ctx->out	= def_frame;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&dev->mutex)) {
2488c2ecf20Sopenharmony_ci		kfree(ctx);
2498c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
2528c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->fh.m2m_ctx)) {
2538c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctx->fh.m2m_ctx);
2548c2ecf20Sopenharmony_ci		mutex_unlock(&dev->mutex);
2558c2ecf20Sopenharmony_ci		kfree(ctx);
2568c2ecf20Sopenharmony_ci		return ret;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci	v4l2_fh_init(&ctx->fh, video_devdata(file));
2598c2ecf20Sopenharmony_ci	file->private_data = &ctx->fh;
2608c2ecf20Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	g2d_setup_ctrls(ctx);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Write the default values to the ctx struct */
2658c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
2688c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mutex);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	v4l2_info(&dev->v4l2_dev, "instance opened\n");
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int g2d_release(struct file *file)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct g2d_dev *dev = video_drvdata(file);
2778c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = fh2ctx(file->private_data);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	mutex_lock(&dev->mutex);
2808c2ecf20Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
2818c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mutex);
2828c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
2838c2ecf20Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
2848c2ecf20Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
2858c2ecf20Sopenharmony_ci	kfree(ctx);
2868c2ecf20Sopenharmony_ci	v4l2_info(&dev->v4l2_dev, "instance closed\n");
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv,
2928c2ecf20Sopenharmony_ci				struct v4l2_capability *cap)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	strscpy(cap->driver, G2D_NAME, sizeof(cap->driver));
2958c2ecf20Sopenharmony_ci	strscpy(cap->card, G2D_NAME, sizeof(cap->card));
2968c2ecf20Sopenharmony_ci	cap->bus_info[0] = 0;
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	if (f->index >= NUM_FORMATS)
3038c2ecf20Sopenharmony_ci		return -EINVAL;
3048c2ecf20Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = prv;
3118c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
3128c2ecf20Sopenharmony_ci	struct g2d_frame *frm;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
3158c2ecf20Sopenharmony_ci	if (!vq)
3168c2ecf20Sopenharmony_ci		return -EINVAL;
3178c2ecf20Sopenharmony_ci	frm = get_frame(ctx, f->type);
3188c2ecf20Sopenharmony_ci	if (IS_ERR(frm))
3198c2ecf20Sopenharmony_ci		return PTR_ERR(frm);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	f->fmt.pix.width		= frm->width;
3228c2ecf20Sopenharmony_ci	f->fmt.pix.height		= frm->height;
3238c2ecf20Sopenharmony_ci	f->fmt.pix.field		= V4L2_FIELD_NONE;
3248c2ecf20Sopenharmony_ci	f->fmt.pix.pixelformat		= frm->fmt->fourcc;
3258c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline		= (frm->width * frm->fmt->depth) >> 3;
3268c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage		= frm->size;
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct g2d_fmt *fmt;
3338c2ecf20Sopenharmony_ci	enum v4l2_field *field;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	fmt = find_fmt(f);
3368c2ecf20Sopenharmony_ci	if (!fmt)
3378c2ecf20Sopenharmony_ci		return -EINVAL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	field = &f->fmt.pix.field;
3408c2ecf20Sopenharmony_ci	if (*field == V4L2_FIELD_ANY)
3418c2ecf20Sopenharmony_ci		*field = V4L2_FIELD_NONE;
3428c2ecf20Sopenharmony_ci	else if (*field != V4L2_FIELD_NONE)
3438c2ecf20Sopenharmony_ci		return -EINVAL;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (f->fmt.pix.width > MAX_WIDTH)
3468c2ecf20Sopenharmony_ci		f->fmt.pix.width = MAX_WIDTH;
3478c2ecf20Sopenharmony_ci	if (f->fmt.pix.height > MAX_HEIGHT)
3488c2ecf20Sopenharmony_ci		f->fmt.pix.height = MAX_HEIGHT;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (f->fmt.pix.width < 1)
3518c2ecf20Sopenharmony_ci		f->fmt.pix.width = 1;
3528c2ecf20Sopenharmony_ci	if (f->fmt.pix.height < 1)
3538c2ecf20Sopenharmony_ci		f->fmt.pix.height = 1;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
3568c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = prv;
3638c2ecf20Sopenharmony_ci	struct g2d_dev *dev = ctx->dev;
3648c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
3658c2ecf20Sopenharmony_ci	struct g2d_frame *frm;
3668c2ecf20Sopenharmony_ci	struct g2d_fmt *fmt;
3678c2ecf20Sopenharmony_ci	int ret = 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Adjust all values accordingly to the hardware capabilities
3708c2ecf20Sopenharmony_ci	 * and chosen format. */
3718c2ecf20Sopenharmony_ci	ret = vidioc_try_fmt(file, prv, f);
3728c2ecf20Sopenharmony_ci	if (ret)
3738c2ecf20Sopenharmony_ci		return ret;
3748c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
3758c2ecf20Sopenharmony_ci	if (vb2_is_busy(vq)) {
3768c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
3778c2ecf20Sopenharmony_ci		return -EBUSY;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci	frm = get_frame(ctx, f->type);
3808c2ecf20Sopenharmony_ci	if (IS_ERR(frm))
3818c2ecf20Sopenharmony_ci		return PTR_ERR(frm);
3828c2ecf20Sopenharmony_ci	fmt = find_fmt(f);
3838c2ecf20Sopenharmony_ci	if (!fmt)
3848c2ecf20Sopenharmony_ci		return -EINVAL;
3858c2ecf20Sopenharmony_ci	frm->width	= f->fmt.pix.width;
3868c2ecf20Sopenharmony_ci	frm->height	= f->fmt.pix.height;
3878c2ecf20Sopenharmony_ci	frm->size	= f->fmt.pix.sizeimage;
3888c2ecf20Sopenharmony_ci	/* Reset crop settings */
3898c2ecf20Sopenharmony_ci	frm->o_width	= 0;
3908c2ecf20Sopenharmony_ci	frm->o_height	= 0;
3918c2ecf20Sopenharmony_ci	frm->c_width	= frm->width;
3928c2ecf20Sopenharmony_ci	frm->c_height	= frm->height;
3938c2ecf20Sopenharmony_ci	frm->right	= frm->width;
3948c2ecf20Sopenharmony_ci	frm->bottom	= frm->height;
3958c2ecf20Sopenharmony_ci	frm->fmt	= fmt;
3968c2ecf20Sopenharmony_ci	frm->stride	= f->fmt.pix.bytesperline;
3978c2ecf20Sopenharmony_ci	return 0;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *prv,
4018c2ecf20Sopenharmony_ci			      struct v4l2_selection *s)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = prv;
4048c2ecf20Sopenharmony_ci	struct g2d_frame *f;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	f = get_frame(ctx, s->type);
4078c2ecf20Sopenharmony_ci	if (IS_ERR(f))
4088c2ecf20Sopenharmony_ci		return PTR_ERR(f);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	switch (s->target) {
4118c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
4128c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
4138c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
4148c2ecf20Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
4158c2ecf20Sopenharmony_ci			return -EINVAL;
4168c2ecf20Sopenharmony_ci		break;
4178c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
4188c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
4198c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
4208c2ecf20Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
4218c2ecf20Sopenharmony_ci			return -EINVAL;
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	default:
4248c2ecf20Sopenharmony_ci		return -EINVAL;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	switch (s->target) {
4288c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
4298c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
4308c2ecf20Sopenharmony_ci		s->r.left = f->o_height;
4318c2ecf20Sopenharmony_ci		s->r.top = f->o_width;
4328c2ecf20Sopenharmony_ci		s->r.width = f->c_width;
4338c2ecf20Sopenharmony_ci		s->r.height = f->c_height;
4348c2ecf20Sopenharmony_ci		break;
4358c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
4368c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
4378c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
4388c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
4398c2ecf20Sopenharmony_ci		s->r.left = 0;
4408c2ecf20Sopenharmony_ci		s->r.top = 0;
4418c2ecf20Sopenharmony_ci		s->r.width = f->width;
4428c2ecf20Sopenharmony_ci		s->r.height = f->height;
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	default:
4458c2ecf20Sopenharmony_ci		return -EINVAL;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci	return 0;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int vidioc_try_selection(struct file *file, void *prv,
4518c2ecf20Sopenharmony_ci				const struct v4l2_selection *s)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = prv;
4548c2ecf20Sopenharmony_ci	struct g2d_dev *dev = ctx->dev;
4558c2ecf20Sopenharmony_ci	struct g2d_frame *f;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	f = get_frame(ctx, s->type);
4588c2ecf20Sopenharmony_ci	if (IS_ERR(f))
4598c2ecf20Sopenharmony_ci		return PTR_ERR(f);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
4628c2ecf20Sopenharmony_ci		if (s->target != V4L2_SEL_TGT_COMPOSE)
4638c2ecf20Sopenharmony_ci			return -EINVAL;
4648c2ecf20Sopenharmony_ci	} else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
4658c2ecf20Sopenharmony_ci		if (s->target != V4L2_SEL_TGT_CROP)
4668c2ecf20Sopenharmony_ci			return -EINVAL;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (s->r.top < 0 || s->r.left < 0) {
4708c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
4718c2ecf20Sopenharmony_ci			"doesn't support negative values for top & left\n");
4728c2ecf20Sopenharmony_ci		return -EINVAL;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic int vidioc_s_selection(struct file *file, void *prv,
4798c2ecf20Sopenharmony_ci			      struct v4l2_selection *s)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = prv;
4828c2ecf20Sopenharmony_ci	struct g2d_frame *f;
4838c2ecf20Sopenharmony_ci	int ret;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	ret = vidioc_try_selection(file, prv, s);
4868c2ecf20Sopenharmony_ci	if (ret)
4878c2ecf20Sopenharmony_ci		return ret;
4888c2ecf20Sopenharmony_ci	f = get_frame(ctx, s->type);
4898c2ecf20Sopenharmony_ci	if (IS_ERR(f))
4908c2ecf20Sopenharmony_ci		return PTR_ERR(f);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	f->c_width	= s->r.width;
4938c2ecf20Sopenharmony_ci	f->c_height	= s->r.height;
4948c2ecf20Sopenharmony_ci	f->o_width	= s->r.left;
4958c2ecf20Sopenharmony_ci	f->o_height	= s->r.top;
4968c2ecf20Sopenharmony_ci	f->bottom	= f->o_height + f->c_height;
4978c2ecf20Sopenharmony_ci	f->right	= f->o_width + f->c_width;
4988c2ecf20Sopenharmony_ci	return 0;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void device_run(void *prv)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = prv;
5048c2ecf20Sopenharmony_ci	struct g2d_dev *dev = ctx->dev;
5058c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
5068c2ecf20Sopenharmony_ci	unsigned long flags;
5078c2ecf20Sopenharmony_ci	u32 cmd = 0;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	dev->curr = ctx;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
5128c2ecf20Sopenharmony_ci	dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	clk_enable(dev->gate);
5158c2ecf20Sopenharmony_ci	g2d_reset(dev);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->ctrl_lock, flags);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	g2d_set_src_size(dev, &ctx->in);
5208c2ecf20Sopenharmony_ci	g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	g2d_set_dst_size(dev, &ctx->out);
5238c2ecf20Sopenharmony_ci	g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0));
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	g2d_set_rop4(dev, ctx->rop);
5268c2ecf20Sopenharmony_ci	g2d_set_flip(dev, ctx->flip);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (ctx->in.c_width != ctx->out.c_width ||
5298c2ecf20Sopenharmony_ci		ctx->in.c_height != ctx->out.c_height) {
5308c2ecf20Sopenharmony_ci		if (dev->variant->hw_rev == TYPE_G2D_3X)
5318c2ecf20Sopenharmony_ci			cmd |= CMD_V3_ENABLE_STRETCH;
5328c2ecf20Sopenharmony_ci		else
5338c2ecf20Sopenharmony_ci			g2d_set_v41_stretch(dev, &ctx->in, &ctx->out);
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	g2d_set_cmd(dev, cmd);
5378c2ecf20Sopenharmony_ci	g2d_start(dev);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->ctrl_lock, flags);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic irqreturn_t g2d_isr(int irq, void *prv)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct g2d_dev *dev = prv;
5458c2ecf20Sopenharmony_ci	struct g2d_ctx *ctx = dev->curr;
5468c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	g2d_clear_int(dev);
5498c2ecf20Sopenharmony_ci	clk_disable(dev->gate);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	BUG_ON(ctx == NULL);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
5548c2ecf20Sopenharmony_ci	dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	BUG_ON(src == NULL);
5578c2ecf20Sopenharmony_ci	BUG_ON(dst == NULL);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	dst->timecode = src->timecode;
5608c2ecf20Sopenharmony_ci	dst->vb2_buf.timestamp = src->vb2_buf.timestamp;
5618c2ecf20Sopenharmony_ci	dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
5628c2ecf20Sopenharmony_ci	dst->flags |=
5638c2ecf20Sopenharmony_ci		src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
5668c2ecf20Sopenharmony_ci	v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
5678c2ecf20Sopenharmony_ci	v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	dev->curr = NULL;
5708c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations g2d_fops = {
5748c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5758c2ecf20Sopenharmony_ci	.open		= g2d_open,
5768c2ecf20Sopenharmony_ci	.release	= g2d_release,
5778c2ecf20Sopenharmony_ci	.poll		= v4l2_m2m_fop_poll,
5788c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
5798c2ecf20Sopenharmony_ci	.mmap		= v4l2_m2m_fop_mmap,
5808c2ecf20Sopenharmony_ci};
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops g2d_ioctl_ops = {
5838c2ecf20Sopenharmony_ci	.vidioc_querycap	= vidioc_querycap,
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= vidioc_enum_fmt,
5868c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= vidioc_g_fmt,
5878c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= vidioc_try_fmt,
5888c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= vidioc_s_fmt,
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_out	= vidioc_enum_fmt,
5918c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_out		= vidioc_g_fmt,
5928c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_out		= vidioc_try_fmt,
5938c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_out		= vidioc_s_fmt,
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
5968c2ecf20Sopenharmony_ci	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
5978c2ecf20Sopenharmony_ci	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
5988c2ecf20Sopenharmony_ci	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
6018c2ecf20Sopenharmony_ci	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	.vidioc_g_selection		= vidioc_g_selection,
6048c2ecf20Sopenharmony_ci	.vidioc_s_selection		= vidioc_s_selection,
6058c2ecf20Sopenharmony_ci};
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic const struct video_device g2d_videodev = {
6088c2ecf20Sopenharmony_ci	.name		= G2D_NAME,
6098c2ecf20Sopenharmony_ci	.fops		= &g2d_fops,
6108c2ecf20Sopenharmony_ci	.ioctl_ops	= &g2d_ioctl_ops,
6118c2ecf20Sopenharmony_ci	.minor		= -1,
6128c2ecf20Sopenharmony_ci	.release	= video_device_release,
6138c2ecf20Sopenharmony_ci	.vfl_dir	= VFL_DIR_M2M,
6148c2ecf20Sopenharmony_ci};
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic const struct v4l2_m2m_ops g2d_m2m_ops = {
6178c2ecf20Sopenharmony_ci	.device_run	= device_run,
6188c2ecf20Sopenharmony_ci};
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_g2d_match[];
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic int g2d_probe(struct platform_device *pdev)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct g2d_dev *dev;
6258c2ecf20Sopenharmony_ci	struct video_device *vfd;
6268c2ecf20Sopenharmony_ci	struct resource *res;
6278c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
6288c2ecf20Sopenharmony_ci	int ret = 0;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
6318c2ecf20Sopenharmony_ci	if (!dev)
6328c2ecf20Sopenharmony_ci		return -ENOMEM;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	spin_lock_init(&dev->ctrl_lock);
6358c2ecf20Sopenharmony_ci	mutex_init(&dev->mutex);
6368c2ecf20Sopenharmony_ci	atomic_set(&dev->num_inst, 0);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	dev->regs = devm_ioremap_resource(&pdev->dev, res);
6418c2ecf20Sopenharmony_ci	if (IS_ERR(dev->regs))
6428c2ecf20Sopenharmony_ci		return PTR_ERR(dev->regs);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	dev->clk = clk_get(&pdev->dev, "sclk_fimg2d");
6458c2ecf20Sopenharmony_ci	if (IS_ERR(dev->clk)) {
6468c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get g2d clock\n");
6478c2ecf20Sopenharmony_ci		return -ENXIO;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	ret = clk_prepare(dev->clk);
6518c2ecf20Sopenharmony_ci	if (ret) {
6528c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to prepare g2d clock\n");
6538c2ecf20Sopenharmony_ci		goto put_clk;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	dev->gate = clk_get(&pdev->dev, "fimg2d");
6578c2ecf20Sopenharmony_ci	if (IS_ERR(dev->gate)) {
6588c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get g2d clock gate\n");
6598c2ecf20Sopenharmony_ci		ret = -ENXIO;
6608c2ecf20Sopenharmony_ci		goto unprep_clk;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	ret = clk_prepare(dev->gate);
6648c2ecf20Sopenharmony_ci	if (ret) {
6658c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to prepare g2d clock gate\n");
6668c2ecf20Sopenharmony_ci		goto put_clk_gate;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
6708c2ecf20Sopenharmony_ci	if (!res) {
6718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to find IRQ\n");
6728c2ecf20Sopenharmony_ci		ret = -ENXIO;
6738c2ecf20Sopenharmony_ci		goto unprep_clk_gate;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	dev->irq = res->start;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr,
6798c2ecf20Sopenharmony_ci						0, pdev->name, dev);
6808c2ecf20Sopenharmony_ci	if (ret) {
6818c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to install IRQ\n");
6828c2ecf20Sopenharmony_ci		goto unprep_clk_gate;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
6888c2ecf20Sopenharmony_ci	if (ret)
6898c2ecf20Sopenharmony_ci		goto unprep_clk_gate;
6908c2ecf20Sopenharmony_ci	vfd = video_device_alloc();
6918c2ecf20Sopenharmony_ci	if (!vfd) {
6928c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
6938c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6948c2ecf20Sopenharmony_ci		goto unreg_v4l2_dev;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci	*vfd = g2d_videodev;
6978c2ecf20Sopenharmony_ci	set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
6988c2ecf20Sopenharmony_ci	vfd->lock = &dev->mutex;
6998c2ecf20Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
7008c2ecf20Sopenharmony_ci	vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dev);
7038c2ecf20Sopenharmony_ci	dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops);
7048c2ecf20Sopenharmony_ci	if (IS_ERR(dev->m2m_dev)) {
7058c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
7068c2ecf20Sopenharmony_ci		ret = PTR_ERR(dev->m2m_dev);
7078c2ecf20Sopenharmony_ci		goto rel_vdev;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node);
7138c2ecf20Sopenharmony_ci	if (!of_id) {
7148c2ecf20Sopenharmony_ci		ret = -ENODEV;
7158c2ecf20Sopenharmony_ci		goto free_m2m;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	dev->variant = (struct g2d_variant *)of_id->data;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
7208c2ecf20Sopenharmony_ci	if (ret) {
7218c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
7228c2ecf20Sopenharmony_ci		goto free_m2m;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci	video_set_drvdata(vfd, dev);
7258c2ecf20Sopenharmony_ci	dev->vfd = vfd;
7268c2ecf20Sopenharmony_ci	v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
7278c2ecf20Sopenharmony_ci		  vfd->num);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	return 0;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cifree_m2m:
7328c2ecf20Sopenharmony_ci	v4l2_m2m_release(dev->m2m_dev);
7338c2ecf20Sopenharmony_cirel_vdev:
7348c2ecf20Sopenharmony_ci	video_device_release(vfd);
7358c2ecf20Sopenharmony_ciunreg_v4l2_dev:
7368c2ecf20Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
7378c2ecf20Sopenharmony_ciunprep_clk_gate:
7388c2ecf20Sopenharmony_ci	clk_unprepare(dev->gate);
7398c2ecf20Sopenharmony_ciput_clk_gate:
7408c2ecf20Sopenharmony_ci	clk_put(dev->gate);
7418c2ecf20Sopenharmony_ciunprep_clk:
7428c2ecf20Sopenharmony_ci	clk_unprepare(dev->clk);
7438c2ecf20Sopenharmony_ciput_clk:
7448c2ecf20Sopenharmony_ci	clk_put(dev->clk);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return ret;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int g2d_remove(struct platform_device *pdev)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	struct g2d_dev *dev = platform_get_drvdata(pdev);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);
7548c2ecf20Sopenharmony_ci	v4l2_m2m_release(dev->m2m_dev);
7558c2ecf20Sopenharmony_ci	video_unregister_device(dev->vfd);
7568c2ecf20Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
7578c2ecf20Sopenharmony_ci	vb2_dma_contig_clear_max_seg_size(&pdev->dev);
7588c2ecf20Sopenharmony_ci	clk_unprepare(dev->gate);
7598c2ecf20Sopenharmony_ci	clk_put(dev->gate);
7608c2ecf20Sopenharmony_ci	clk_unprepare(dev->clk);
7618c2ecf20Sopenharmony_ci	clk_put(dev->clk);
7628c2ecf20Sopenharmony_ci	return 0;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic struct g2d_variant g2d_drvdata_v3x = {
7668c2ecf20Sopenharmony_ci	.hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */
7678c2ecf20Sopenharmony_ci};
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic struct g2d_variant g2d_drvdata_v4x = {
7708c2ecf20Sopenharmony_ci	.hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */
7718c2ecf20Sopenharmony_ci};
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_g2d_match[] = {
7748c2ecf20Sopenharmony_ci	{
7758c2ecf20Sopenharmony_ci		.compatible = "samsung,s5pv210-g2d",
7768c2ecf20Sopenharmony_ci		.data = &g2d_drvdata_v3x,
7778c2ecf20Sopenharmony_ci	}, {
7788c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos4212-g2d",
7798c2ecf20Sopenharmony_ci		.data = &g2d_drvdata_v4x,
7808c2ecf20Sopenharmony_ci	},
7818c2ecf20Sopenharmony_ci	{},
7828c2ecf20Sopenharmony_ci};
7838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_g2d_match);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic struct platform_driver g2d_pdrv = {
7868c2ecf20Sopenharmony_ci	.probe		= g2d_probe,
7878c2ecf20Sopenharmony_ci	.remove		= g2d_remove,
7888c2ecf20Sopenharmony_ci	.driver		= {
7898c2ecf20Sopenharmony_ci		.name = G2D_NAME,
7908c2ecf20Sopenharmony_ci		.of_match_table = exynos_g2d_match,
7918c2ecf20Sopenharmony_ci	},
7928c2ecf20Sopenharmony_ci};
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cimodule_platform_driver(g2d_pdrv);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
7978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver");
7988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
799