brintos

brintos / linux-shallow public Read only

0
0
Text · 26.1 KiB · 0940990 Raw
1060 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2020 BayLibre, SAS4 * Author: Neil Armstrong <narmstrong@baylibre.com>5 */6 7#include <linux/clk.h>8#include <linux/delay.h>9#include <linux/bitfield.h>10#include <linux/interrupt.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/reset.h>14#include <linux/sched.h>15#include <linux/slab.h>16#include <linux/timer.h>17#include <linux/regmap.h>18 19#include <linux/platform_device.h>20#include <media/v4l2-device.h>21#include <media/v4l2-event.h>22#include <media/v4l2-ioctl.h>23#include <media/v4l2-mem2mem.h>24#include <media/v4l2-ctrls.h>25#include <media/videobuf2-v4l2.h>26#include <media/videobuf2-dma-contig.h>27 28#include "ge2d-regs.h"29 30#define GE2D_NAME	"meson-ge2d"31 32#define DEFAULT_WIDTH	12833#define DEFAULT_HEIGHT	12834#define DEFAULT_STRIDE	51235 36#define MAX_WIDTH	819137#define MAX_HEIGHT	819138 39/*40 * Missing features:41 * - Scaling42 * - Simple 1/2 vertical scaling43 * - YUV input support44 * - Source global alpha45 * - Colorspace conversion46 */47 48struct ge2d_fmt {49	u32 fourcc;50	bool alpha;51	bool le;52	unsigned int depth;53	unsigned int hw_fmt;54	unsigned int hw_map;55};56 57struct ge2d_frame {58	struct vb2_v4l2_buffer *buf;59 60	/* Image Format */61	struct v4l2_pix_format pix_fmt;62 63	/* Crop */64	struct v4l2_rect crop;65 66	/* Image format */67	const struct ge2d_fmt *fmt;68};69 70struct ge2d_ctx {71	struct v4l2_fh fh;72	struct meson_ge2d *ge2d;73	struct ge2d_frame in;74	struct ge2d_frame out;75	struct v4l2_ctrl_handler ctrl_handler;76 77	unsigned long sequence_out, sequence_cap;78 79	/* Control values */80	u32 hflip;81	u32 vflip;82	u32 xy_swap;83};84 85struct meson_ge2d {86	struct v4l2_device v4l2_dev;87	struct v4l2_m2m_dev *m2m_dev;88	struct video_device *vfd;89 90	struct device *dev;91	struct regmap *map;92	struct clk *clk;93 94	/* vb2 queue lock */95	struct mutex mutex;96 97	struct ge2d_ctx *curr;98};99 100#define FMT(_fourcc, _alpha, _depth, _map)		\101{							\102	.fourcc = _fourcc,				\103	.alpha = (_alpha),				\104	.depth = (_depth),				\105	.hw_fmt = GE2D_FORMAT_ ## _depth ## BIT,	\106	.hw_map = GE2D_COLOR_MAP_ ## _map,		\107}108 109/* TOFIX Handle the YUV input formats */110static const struct ge2d_fmt formats[] = {111	/*  FOURCC Alpha  HW FMT  HW MAP */112	FMT(V4L2_PIX_FMT_XRGB32, false, 32, BGRA8888),113	FMT(V4L2_PIX_FMT_RGB32, true, 32, BGRA8888),114	FMT(V4L2_PIX_FMT_ARGB32, true, 32, BGRA8888),115	FMT(V4L2_PIX_FMT_RGBX32, false, 32, ABGR8888),116	FMT(V4L2_PIX_FMT_RGBA32, true, 32, ABGR8888),117	FMT(V4L2_PIX_FMT_BGRX32, false, 32, RGBA8888),118	FMT(V4L2_PIX_FMT_BGRA32, true, 32, RGBA8888),119	FMT(V4L2_PIX_FMT_BGR32, true, 32, ARGB8888),120	FMT(V4L2_PIX_FMT_ABGR32, true, 32, ARGB8888),121	FMT(V4L2_PIX_FMT_XBGR32, false, 32, ARGB8888),122 123	FMT(V4L2_PIX_FMT_RGB24, false, 24, BGR888),124	FMT(V4L2_PIX_FMT_BGR24, false, 24, RGB888),125 126	FMT(V4L2_PIX_FMT_XRGB555X, false, 16, ARGB1555),127	FMT(V4L2_PIX_FMT_ARGB555X, true, 16, ARGB1555),128	FMT(V4L2_PIX_FMT_RGB565, false, 16, RGB565),129	FMT(V4L2_PIX_FMT_RGBX444, false, 16, RGBA4444),130	FMT(V4L2_PIX_FMT_RGBA444, true, 16, RGBA4444),131	FMT(V4L2_PIX_FMT_XRGB444, false, 16, ARGB4444),132	FMT(V4L2_PIX_FMT_ARGB444, true, 16, ARGB4444),133};134 135#define NUM_FORMATS ARRAY_SIZE(formats)136 137static const struct ge2d_fmt *find_fmt(struct v4l2_format *f)138{139	unsigned int i;140 141	for (i = 0; i < NUM_FORMATS; i++) {142		if (formats[i].fourcc == f->fmt.pix.pixelformat)143			return &formats[i];144	}145 146	/*147	 * TRY_FMT/S_FMT should never return an error when the requested format148	 * is not supported. Drivers should always return a valid format,149	 * preferably a format that is as widely supported by applications as150	 * possible.151	 */152	return &formats[0];153}154 155static struct ge2d_frame *get_frame(struct ge2d_ctx *ctx,156				    enum v4l2_buf_type type)157{158	switch (type) {159	case V4L2_BUF_TYPE_VIDEO_OUTPUT:160		return &ctx->in;161	case V4L2_BUF_TYPE_VIDEO_CAPTURE:162		return &ctx->out;163	default:164		/* This should never happen, warn and return OUTPUT frame */165		dev_warn(ctx->ge2d->dev, "%s: invalid buffer type\n", __func__);166		return &ctx->in;167	}168}169 170static void ge2d_hw_start(struct meson_ge2d *ge2d)171{172	struct ge2d_ctx *ctx = ge2d->curr;173	u32 reg;174 175	/* Reset */176	regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1,177			   GE2D_SOFT_RST, GE2D_SOFT_RST);178	regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1,179			   GE2D_SOFT_RST, 0);180 181	usleep_range(100, 200);182 183	/* Implement CANVAS for non-AXG */184	regmap_write(ge2d->map, GE2D_SRC1_BADDR_CTRL,185		     (vb2_dma_contig_plane_dma_addr(&ctx->in.buf->vb2_buf, 0) + 7) >> 3);186	regmap_write(ge2d->map, GE2D_SRC1_STRIDE_CTRL,187		     (ctx->in.pix_fmt.bytesperline + 7) >> 3);188	regmap_write(ge2d->map, GE2D_SRC2_BADDR_CTRL,189		     (vb2_dma_contig_plane_dma_addr(&ctx->out.buf->vb2_buf, 0) + 7) >> 3);190	regmap_write(ge2d->map, GE2D_SRC2_STRIDE_CTRL,191		     (ctx->out.pix_fmt.bytesperline + 7) >> 3);192	regmap_write(ge2d->map, GE2D_DST1_BADDR_CTRL,193		     (vb2_dma_contig_plane_dma_addr(&ctx->out.buf->vb2_buf, 0) + 7) >> 3);194	regmap_write(ge2d->map, GE2D_DST1_STRIDE_CTRL,195		     (ctx->out.pix_fmt.bytesperline + 7) >> 3);196 197	regmap_write(ge2d->map, GE2D_GEN_CTRL0, 0);198	regmap_write(ge2d->map, GE2D_GEN_CTRL1,199		     FIELD_PREP(GE2D_INTERRUPT_CTRL, 2) |200		     FIELD_PREP(GE2D_SRC2_BURST_SIZE_CTRL, 3) |201		     FIELD_PREP(GE2D_SRC1_BURST_SIZE_CTRL, 0x3f));202 203	regmap_write(ge2d->map, GE2D_GEN_CTRL2,204		     GE2D_SRC1_LITTLE_ENDIAN |205		     GE2D_SRC2_LITTLE_ENDIAN |206		     GE2D_DST_LITTLE_ENDIAN |207		     FIELD_PREP(GE2D_DST1_COLOR_MAP, ctx->out.fmt->hw_map) |208		     FIELD_PREP(GE2D_DST1_FORMAT, ctx->out.fmt->hw_fmt) |209		     FIELD_PREP(GE2D_SRC2_COLOR_MAP, ctx->out.fmt->hw_map) |210		     FIELD_PREP(GE2D_SRC2_FORMAT, ctx->out.fmt->hw_fmt) |211		     FIELD_PREP(GE2D_SRC1_COLOR_MAP, ctx->in.fmt->hw_map) |212		     FIELD_PREP(GE2D_SRC1_FORMAT, ctx->in.fmt->hw_fmt));213	regmap_write(ge2d->map, GE2D_GEN_CTRL3,214		     GE2D_DST1_ENABLE);215 216	regmap_write(ge2d->map, GE2D_SRC1_CLIPY_START_END,217		     FIELD_PREP(GE2D_START, ctx->in.crop.top) |218		     FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height - 1));219	regmap_write(ge2d->map, GE2D_SRC1_CLIPX_START_END,220		     FIELD_PREP(GE2D_START, ctx->in.crop.left) |221		     FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width - 1));222	regmap_write(ge2d->map, GE2D_SRC2_CLIPY_START_END,223		     FIELD_PREP(GE2D_START, ctx->out.crop.top) |224		     FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1));225	regmap_write(ge2d->map, GE2D_SRC2_CLIPX_START_END,226		     FIELD_PREP(GE2D_START, ctx->out.crop.left) |227		     FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1));228	regmap_write(ge2d->map, GE2D_DST_CLIPY_START_END,229		     FIELD_PREP(GE2D_START, ctx->out.crop.top) |230		     FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1));231	regmap_write(ge2d->map, GE2D_DST_CLIPX_START_END,232		     FIELD_PREP(GE2D_START, ctx->out.crop.left) |233		     FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1));234 235	regmap_write(ge2d->map, GE2D_SRC1_Y_START_END,236		     FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height - 1));237	regmap_write(ge2d->map, GE2D_SRC1_X_START_END,238		     FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width - 1));239	regmap_write(ge2d->map, GE2D_SRC2_Y_START_END,240		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1));241	regmap_write(ge2d->map, GE2D_SRC2_X_START_END,242		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1));243	regmap_write(ge2d->map, GE2D_DST_Y_START_END,244		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1));245	regmap_write(ge2d->map, GE2D_DST_X_START_END,246		     FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1));247 248	/* Color, no blend, use source color */249	reg = GE2D_ALU_DO_COLOR_OPERATION_LOGIC(LOGIC_OPERATION_COPY,250						COLOR_FACTOR_SRC_COLOR);251 252	if (ctx->in.fmt->alpha && ctx->out.fmt->alpha)253		/* Take source alpha */254		reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_COPY,255							 COLOR_FACTOR_SRC_ALPHA);256	else if (!ctx->out.fmt->alpha)257		/* Set alpha to 0 */258		reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_SET,259							 COLOR_FACTOR_ZERO);260	else261		/* Keep original alpha */262		reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_COPY,263							 COLOR_FACTOR_DST_ALPHA);264 265	regmap_write(ge2d->map, GE2D_ALU_OP_CTRL, reg);266 267	/* Start */268	regmap_write(ge2d->map, GE2D_CMD_CTRL,269		     (ctx->xy_swap ? GE2D_DST_XY_SWAP : 0) |270		     (ctx->hflip ? GE2D_SRC1_Y_REV : 0) |271		     (ctx->vflip ? GE2D_SRC1_X_REV : 0) |272		     GE2D_CBUS_CMD_WR);273}274 275static void device_run(void *priv)276{277	struct ge2d_ctx *ctx = priv;278	struct meson_ge2d *ge2d = ctx->ge2d;279 280	ge2d->curr = ctx;281 282	ctx->in.buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);283	ctx->out.buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);284 285	ge2d_hw_start(ge2d);286}287 288static irqreturn_t ge2d_isr(int irq, void *priv)289{290	struct meson_ge2d *ge2d = priv;291	u32 intr;292 293	regmap_read(ge2d->map, GE2D_STATUS0, &intr);294 295	if (!(intr & GE2D_GE2D_BUSY)) {296		struct vb2_v4l2_buffer *src, *dst;297		struct ge2d_ctx *ctx = ge2d->curr;298 299		ge2d->curr = NULL;300 301		src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);302		dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);303 304		src->sequence = ctx->sequence_out++;305		dst->sequence = ctx->sequence_cap++;306 307		dst->timecode = src->timecode;308		dst->vb2_buf.timestamp = src->vb2_buf.timestamp;309		dst->flags = src->flags;310 311		v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);312		v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);313		v4l2_m2m_job_finish(ge2d->m2m_dev, ctx->fh.m2m_ctx);314	}315 316	return IRQ_HANDLED;317}318 319static const struct v4l2_m2m_ops ge2d_m2m_ops = {320	.device_run = device_run,321};322 323static int ge2d_queue_setup(struct vb2_queue *vq,324			    unsigned int *nbuffers, unsigned int *nplanes,325			    unsigned int sizes[], struct device *alloc_devs[])326{327	struct ge2d_ctx *ctx = vb2_get_drv_priv(vq);328	struct ge2d_frame *f = get_frame(ctx, vq->type);329 330	if (*nplanes)331		return sizes[0] < f->pix_fmt.sizeimage ? -EINVAL : 0;332 333	sizes[0] = f->pix_fmt.sizeimage;334	*nplanes = 1;335 336	return 0;337}338 339static int ge2d_buf_prepare(struct vb2_buffer *vb)340{341	struct ge2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);342	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);343	struct ge2d_frame *f = get_frame(ctx, vb->vb2_queue->type);344 345	vbuf->field = V4L2_FIELD_NONE;346 347	vb2_set_plane_payload(vb, 0, f->pix_fmt.sizeimage);348 349	return 0;350}351 352static void ge2d_buf_queue(struct vb2_buffer *vb)353{354	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);355	struct ge2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);356 357	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);358}359 360static int ge2d_start_streaming(struct vb2_queue *vq, unsigned int count)361{362	struct ge2d_ctx *ctx = vb2_get_drv_priv(vq);363 364	if (V4L2_TYPE_IS_OUTPUT(vq->type))365		ctx->sequence_out = 0;366	else367		ctx->sequence_cap = 0;368 369	return 0;370}371 372static void ge2d_stop_streaming(struct vb2_queue *vq)373{374	struct ge2d_ctx *ctx = vb2_get_drv_priv(vq);375	struct vb2_v4l2_buffer *vbuf;376 377	for (;;) {378		if (V4L2_TYPE_IS_OUTPUT(vq->type))379			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);380		else381			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);382		if (!vbuf)383			break;384		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);385	}386}387 388static const struct vb2_ops ge2d_qops = {389	.queue_setup = ge2d_queue_setup,390	.buf_prepare = ge2d_buf_prepare,391	.buf_queue = ge2d_buf_queue,392	.start_streaming = ge2d_start_streaming,393	.stop_streaming = ge2d_stop_streaming,394	.wait_prepare = vb2_ops_wait_prepare,395	.wait_finish = vb2_ops_wait_finish,396};397 398static int399queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)400{401	struct ge2d_ctx *ctx = priv;402	int ret;403 404	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;405	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;406	src_vq->drv_priv = ctx;407	src_vq->ops = &ge2d_qops;408	src_vq->mem_ops = &vb2_dma_contig_memops;409	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);410	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;411	src_vq->lock = &ctx->ge2d->mutex;412	src_vq->dev = ctx->ge2d->v4l2_dev.dev;413 414	ret = vb2_queue_init(src_vq);415	if (ret)416		return ret;417 418	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;419	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;420	dst_vq->drv_priv = ctx;421	dst_vq->ops = &ge2d_qops;422	dst_vq->mem_ops = &vb2_dma_contig_memops;423	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);424	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;425	dst_vq->lock = &ctx->ge2d->mutex;426	dst_vq->dev = ctx->ge2d->v4l2_dev.dev;427 428	return vb2_queue_init(dst_vq);429}430 431static int432vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)433{434	strscpy(cap->driver, GE2D_NAME, sizeof(cap->driver));435	strscpy(cap->card, GE2D_NAME, sizeof(cap->card));436	strscpy(cap->bus_info, "platform:" GE2D_NAME, sizeof(cap->bus_info));437 438	return 0;439}440 441static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)442{443	const struct ge2d_fmt *fmt;444 445	if (f->index >= NUM_FORMATS)446		return -EINVAL;447 448	fmt = &formats[f->index];449	f->pixelformat = fmt->fourcc;450 451	return 0;452}453 454static int vidioc_g_selection(struct file *file, void *priv,455			      struct v4l2_selection *s)456{457	struct ge2d_ctx *ctx = priv;458	struct ge2d_frame *f;459	bool use_frame = false;460 461	if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&462	    s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)463		return -EINVAL;464 465	f = get_frame(ctx, s->type);466 467	switch (s->target) {468	case V4L2_SEL_TGT_COMPOSE_DEFAULT:469	case V4L2_SEL_TGT_COMPOSE_BOUNDS:470		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)471			return -EINVAL;472		break;473	case V4L2_SEL_TGT_CROP_DEFAULT:474	case V4L2_SEL_TGT_CROP_BOUNDS:475		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)476			return -EINVAL;477		break;478	case V4L2_SEL_TGT_COMPOSE:479		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)480			return -EINVAL;481		use_frame = true;482		break;483	case V4L2_SEL_TGT_CROP:484		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)485			return -EINVAL;486		use_frame = true;487		break;488	default:489		return -EINVAL;490	}491 492	if (use_frame) {493		s->r = f->crop;494	} else {495		s->r.left = 0;496		s->r.top = 0;497		s->r.width = f->pix_fmt.width;498		s->r.height = f->pix_fmt.height;499	}500 501	return 0;502}503 504static int vidioc_s_selection(struct file *file, void *priv,505			      struct v4l2_selection *s)506{507	struct ge2d_ctx *ctx = priv;508	struct meson_ge2d *ge2d = ctx->ge2d;509	struct ge2d_frame *f;510	int ret = 0;511 512	if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&513	    s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)514		return -EINVAL;515 516	f = get_frame(ctx, s->type);517 518	switch (s->target) {519	case V4L2_SEL_TGT_COMPOSE:520		/*521		 * COMPOSE target is only valid for capture buffer type, return522		 * error for output buffer type523		 */524		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)525			return -EINVAL;526		break;527	case V4L2_SEL_TGT_CROP:528		/*529		 * CROP target is only valid for output buffer type, return530		 * error for capture buffer type531		 */532		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)533			return -EINVAL;534		break;535	/*536	 * bound and default crop/compose targets are invalid targets to537	 * try/set538	 */539	default:540		return -EINVAL;541	}542 543	if (s->r.top < 0 || s->r.left < 0) {544		v4l2_err(&ge2d->v4l2_dev,545			 "doesn't support negative values for top & left.\n");546		return -EINVAL;547	}548 549	if (s->r.left + s->r.width > f->pix_fmt.width ||550	    s->r.top + s->r.height > f->pix_fmt.height) {551		v4l2_err(&ge2d->v4l2_dev, "unsupported rectangle value.\n");552		return -EINVAL;553	}554 555	f->crop = s->r;556 557	return ret;558}559 560static void vidioc_setup_cap_fmt(struct ge2d_ctx *ctx, struct v4l2_pix_format *f)561{562	struct ge2d_frame *frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);563 564	*f = frm_out->pix_fmt;565 566	if (ctx->xy_swap) {567		f->width = frm_out->pix_fmt.height;568		f->height = frm_out->pix_fmt.width;569	}570}571 572static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f)573{574	const struct ge2d_fmt *fmt = find_fmt(f);575	struct ge2d_ctx *ctx = priv;576	struct v4l2_pix_format fmt_cap;577 578	vidioc_setup_cap_fmt(ctx, &fmt_cap);579 580	fmt_cap.pixelformat = fmt->fourcc;581 582	fmt_cap.bytesperline = max(f->fmt.pix.bytesperline,583				   ALIGN((fmt_cap.width * fmt->depth) >> 3, 8));584 585	fmt_cap.sizeimage = max(f->fmt.pix.sizeimage,586				fmt_cap.height * fmt_cap.bytesperline);587 588	f->fmt.pix = fmt_cap;589 590	return 0;591}592 593static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f)594{595	struct ge2d_ctx *ctx = priv;596	struct meson_ge2d *ge2d = ctx->ge2d;597	struct vb2_queue *vq;598	struct ge2d_frame *frm;599	int ret = 0;600 601	/* Adjust all values accordingly to the hardware capabilities602	 * and chosen format.603	 */604	ret = vidioc_try_fmt_cap(file, priv, f);605	if (ret)606		return ret;607 608	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);609	if (vb2_is_busy(vq)) {610		v4l2_err(&ge2d->v4l2_dev, "queue (%d) bust\n", f->type);611		return -EBUSY;612	}613 614	frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);615 616	frm->pix_fmt = f->fmt.pix;617	frm->fmt = find_fmt(f);618	f->fmt.pix.pixelformat = frm->fmt->fourcc;619 620	/* Reset crop settings */621	frm->crop.left = 0;622	frm->crop.top = 0;623	frm->crop.width = frm->pix_fmt.width;624	frm->crop.height = frm->pix_fmt.height;625 626	return 0;627}628 629static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)630{631	struct ge2d_ctx *ctx = priv;632	struct vb2_queue *vq;633	struct ge2d_frame *frm;634 635	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);636	if (!vq)637		return -EINVAL;638 639	frm = get_frame(ctx, f->type);640 641	f->fmt.pix = frm->pix_fmt;642	f->fmt.pix.pixelformat = frm->fmt->fourcc;643 644	return 0;645}646 647static int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *f)648{649	const struct ge2d_fmt *fmt = find_fmt(f);650 651	f->fmt.pix.field = V4L2_FIELD_NONE;652	f->fmt.pix.pixelformat = fmt->fourcc;653 654	if (f->fmt.pix.width > MAX_WIDTH)655		f->fmt.pix.width = MAX_WIDTH;656	if (f->fmt.pix.height > MAX_HEIGHT)657		f->fmt.pix.height = MAX_HEIGHT;658 659	f->fmt.pix.bytesperline = max(f->fmt.pix.bytesperline,660				      ALIGN((f->fmt.pix.width * fmt->depth) >> 3, 8));661 662	f->fmt.pix.sizeimage = max(f->fmt.pix.sizeimage,663				   f->fmt.pix.height * f->fmt.pix.bytesperline);664 665	return 0;666}667 668static int vidioc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *f)669{670	struct ge2d_ctx *ctx = priv;671	struct meson_ge2d *ge2d = ctx->ge2d;672	struct vb2_queue *vq;673	struct ge2d_frame *frm, *frm_cap;674	int ret = 0;675 676	/* Adjust all values accordingly to the hardware capabilities677	 * and chosen format.678	 */679	ret = vidioc_try_fmt_out(file, priv, f);680	if (ret)681		return ret;682 683	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);684	if (vb2_is_busy(vq)) {685		v4l2_err(&ge2d->v4l2_dev, "queue (%d) bust\n", f->type);686		return -EBUSY;687	}688 689	frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);690	frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);691 692	frm->pix_fmt = f->fmt.pix;693	frm->fmt = find_fmt(f);694	f->fmt.pix.pixelformat = frm->fmt->fourcc;695 696	/* Reset crop settings */697	frm->crop.left = 0;698	frm->crop.top = 0;699	frm->crop.width = frm->pix_fmt.width;700	frm->crop.height = frm->pix_fmt.height;701 702	/* Propagate settings to capture */703	vidioc_setup_cap_fmt(ctx, &frm_cap->pix_fmt);704 705	return 0;706}707 708static const struct v4l2_ioctl_ops ge2d_ioctl_ops = {709	.vidioc_querycap = vidioc_querycap,710 711	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,712	.vidioc_g_fmt_vid_cap = vidioc_g_fmt,713	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap,714	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap,715 716	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt,717	.vidioc_g_fmt_vid_out = vidioc_g_fmt,718	.vidioc_try_fmt_vid_out = vidioc_try_fmt_out,719	.vidioc_s_fmt_vid_out = vidioc_s_fmt_out,720 721	.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,722	.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,723	.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,724	.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,725	.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,726	.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,727	.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,728 729	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,730	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,731 732	.vidioc_streamon = v4l2_m2m_ioctl_streamon,733	.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,734 735	.vidioc_g_selection = vidioc_g_selection,736	.vidioc_s_selection = vidioc_s_selection,737};738 739static int ge2d_s_ctrl(struct v4l2_ctrl *ctrl)740{741	struct ge2d_ctx *ctx = container_of(ctrl->handler, struct ge2d_ctx,742					   ctrl_handler);743	struct v4l2_pix_format fmt;744	struct vb2_queue *vq;745 746	switch (ctrl->id) {747	case V4L2_CID_HFLIP:748		ctx->hflip = ctrl->val;749		break;750	case V4L2_CID_VFLIP:751		ctx->vflip = ctrl->val;752		break;753	case V4L2_CID_ROTATE:754		vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);755		if (vb2_is_busy(vq))756			return -EBUSY;757 758		if (ctrl->val == 90) {759			ctx->hflip = 0;760			ctx->vflip = 1;761			ctx->xy_swap = 1;762		} else if (ctrl->val == 180) {763			ctx->hflip = 1;764			ctx->vflip = 1;765			ctx->xy_swap = 0;766		} else if (ctrl->val == 270) {767			ctx->hflip = 1;768			ctx->vflip = 0;769			ctx->xy_swap = 1;770		} else {771			ctx->hflip = 0;772			ctx->vflip = 0;773			ctx->xy_swap = 0;774		}775 776		vidioc_setup_cap_fmt(ctx, &fmt);777 778		/*779		 * If the rotation parameter changes the OUTPUT frames780		 * parameters, take them in account781		 */782		ctx->out.pix_fmt = fmt;783 784		break;785	}786 787	return 0;788}789 790static const struct v4l2_ctrl_ops ge2d_ctrl_ops = {791	.s_ctrl = ge2d_s_ctrl,792};793 794static int ge2d_setup_ctrls(struct ge2d_ctx *ctx)795{796	struct meson_ge2d *ge2d = ctx->ge2d;797 798	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);799 800	v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops,801			  V4L2_CID_HFLIP, 0, 1, 1, 0);802 803	v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops,804			  V4L2_CID_VFLIP, 0, 1, 1, 0);805 806	v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops,807			  V4L2_CID_ROTATE, 0, 270, 90, 0);808 809	if (ctx->ctrl_handler.error) {810		int err = ctx->ctrl_handler.error;811 812		v4l2_err(&ge2d->v4l2_dev, "%s failed\n", __func__);813		v4l2_ctrl_handler_free(&ctx->ctrl_handler);814		return err;815	}816 817	return 0;818}819 820static const struct ge2d_frame def_frame = {821	.pix_fmt = {822		.width = DEFAULT_WIDTH,823		.height = DEFAULT_HEIGHT,824		.bytesperline = DEFAULT_STRIDE,825		.sizeimage = DEFAULT_STRIDE * DEFAULT_HEIGHT,826		.field = V4L2_FIELD_NONE,827	},828	.crop.width = DEFAULT_WIDTH,829	.crop.height = DEFAULT_HEIGHT,830	.fmt = &formats[0],831};832 833static int ge2d_open(struct file *file)834{835	struct meson_ge2d *ge2d = video_drvdata(file);836	struct ge2d_ctx *ctx = NULL;837	int ret = 0;838 839	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);840	if (!ctx)841		return -ENOMEM;842	ctx->ge2d = ge2d;843 844	/* Set default formats */845	ctx->in = def_frame;846	ctx->out = def_frame;847 848	if (mutex_lock_interruptible(&ge2d->mutex)) {849		kfree(ctx);850		return -ERESTARTSYS;851	}852	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ge2d->m2m_dev, ctx, &queue_init);853	if (IS_ERR(ctx->fh.m2m_ctx)) {854		ret = PTR_ERR(ctx->fh.m2m_ctx);855		mutex_unlock(&ge2d->mutex);856		kfree(ctx);857		return ret;858	}859	v4l2_fh_init(&ctx->fh, video_devdata(file));860	file->private_data = &ctx->fh;861	v4l2_fh_add(&ctx->fh);862 863	ge2d_setup_ctrls(ctx);864 865	/* Write the default values to the ctx struct */866	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);867 868	ctx->fh.ctrl_handler = &ctx->ctrl_handler;869	mutex_unlock(&ge2d->mutex);870 871	return 0;872}873 874static int ge2d_release(struct file *file)875{876	struct ge2d_ctx *ctx =877		container_of(file->private_data, struct ge2d_ctx, fh);878	struct meson_ge2d *ge2d = ctx->ge2d;879 880	mutex_lock(&ge2d->mutex);881 882	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);883 884	v4l2_ctrl_handler_free(&ctx->ctrl_handler);885	v4l2_fh_del(&ctx->fh);886	v4l2_fh_exit(&ctx->fh);887	kfree(ctx);888 889	mutex_unlock(&ge2d->mutex);890 891	return 0;892}893 894static const struct v4l2_file_operations ge2d_fops = {895	.owner = THIS_MODULE,896	.open = ge2d_open,897	.release = ge2d_release,898	.poll = v4l2_m2m_fop_poll,899	.unlocked_ioctl = video_ioctl2,900	.mmap = v4l2_m2m_fop_mmap,901};902 903static const struct video_device ge2d_videodev = {904	.name = "meson-ge2d",905	.fops = &ge2d_fops,906	.ioctl_ops = &ge2d_ioctl_ops,907	.minor = -1,908	.release = video_device_release,909	.vfl_dir = VFL_DIR_M2M,910	.device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,911};912 913static const struct regmap_config meson_ge2d_regmap_conf = {914	.reg_bits = 8,915	.val_bits = 32,916	.reg_stride = 4,917	.max_register = GE2D_SRC2_STRIDE_CTRL,918};919 920static int ge2d_probe(struct platform_device *pdev)921{922	struct reset_control *rst;923	struct video_device *vfd;924	struct meson_ge2d *ge2d;925	void __iomem *regs;926	int ret = 0;927	int irq;928 929	if (!pdev->dev.of_node)930		return -ENODEV;931 932	ge2d = devm_kzalloc(&pdev->dev, sizeof(*ge2d), GFP_KERNEL);933	if (!ge2d)934		return -ENOMEM;935 936	ge2d->dev = &pdev->dev;937	mutex_init(&ge2d->mutex);938 939	regs = devm_platform_ioremap_resource(pdev, 0);940	if (IS_ERR(regs))941		return PTR_ERR(regs);942 943	ge2d->map = devm_regmap_init_mmio(ge2d->dev, regs,944					  &meson_ge2d_regmap_conf);945	if (IS_ERR(ge2d->map))946		return PTR_ERR(ge2d->map);947 948	irq = platform_get_irq(pdev, 0);949	ret = devm_request_irq(ge2d->dev, irq, ge2d_isr, 0,950			       dev_name(ge2d->dev), ge2d);951	if (ret < 0) {952		dev_err(ge2d->dev, "failed to request irq\n");953		return ret;954	}955 956	rst = devm_reset_control_get(ge2d->dev, NULL);957	if (IS_ERR(rst)) {958		dev_err(ge2d->dev, "failed to get core reset controller\n");959		return PTR_ERR(rst);960	}961 962	ge2d->clk = devm_clk_get(ge2d->dev, NULL);963	if (IS_ERR(ge2d->clk)) {964		dev_err(ge2d->dev, "failed to get clock\n");965		return PTR_ERR(ge2d->clk);966	}967 968	reset_control_assert(rst);969	udelay(1);970	reset_control_deassert(rst);971 972	ret = clk_prepare_enable(ge2d->clk);973	if (ret) {974		dev_err(ge2d->dev, "Cannot enable ge2d sclk: %d\n", ret);975		return ret;976	}977 978	ret = v4l2_device_register(&pdev->dev, &ge2d->v4l2_dev);979	if (ret)980		goto disable_clks;981 982	vfd = video_device_alloc();983	if (!vfd) {984		v4l2_err(&ge2d->v4l2_dev, "Failed to allocate video device\n");985		ret = -ENOMEM;986		goto unreg_v4l2_dev;987	}988 989	*vfd = ge2d_videodev;990	vfd->lock = &ge2d->mutex;991	vfd->v4l2_dev = &ge2d->v4l2_dev;992 993	video_set_drvdata(vfd, ge2d);994	ge2d->vfd = vfd;995 996	platform_set_drvdata(pdev, ge2d);997	ge2d->m2m_dev = v4l2_m2m_init(&ge2d_m2m_ops);998	if (IS_ERR(ge2d->m2m_dev)) {999		v4l2_err(&ge2d->v4l2_dev, "Failed to init mem2mem device\n");1000		ret = PTR_ERR(ge2d->m2m_dev);1001		goto rel_vdev;1002	}1003 1004	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);1005	if (ret) {1006		v4l2_err(&ge2d->v4l2_dev, "Failed to register video device\n");1007		goto rel_m2m;1008	}1009 1010	v4l2_info(&ge2d->v4l2_dev, "Registered %s as /dev/%s\n",1011		  vfd->name, video_device_node_name(vfd));1012 1013	return 0;1014 1015rel_m2m:1016	v4l2_m2m_release(ge2d->m2m_dev);1017rel_vdev:1018	video_device_release(ge2d->vfd);1019unreg_v4l2_dev:1020	v4l2_device_unregister(&ge2d->v4l2_dev);1021disable_clks:1022	clk_disable_unprepare(ge2d->clk);1023 1024	return ret;1025}1026 1027static void ge2d_remove(struct platform_device *pdev)1028{1029	struct meson_ge2d *ge2d = platform_get_drvdata(pdev);1030 1031	video_unregister_device(ge2d->vfd);1032	v4l2_m2m_release(ge2d->m2m_dev);1033	v4l2_device_unregister(&ge2d->v4l2_dev);1034	clk_disable_unprepare(ge2d->clk);1035}1036 1037static const struct of_device_id meson_ge2d_match[] = {1038	{1039		.compatible = "amlogic,axg-ge2d",1040	},1041	{},1042};1043 1044MODULE_DEVICE_TABLE(of, meson_ge2d_match);1045 1046static struct platform_driver ge2d_drv = {1047	.probe = ge2d_probe,1048	.remove_new = ge2d_remove,1049	.driver = {1050		.name = "meson-ge2d",1051		.of_match_table = meson_ge2d_match,1052	},1053};1054 1055module_platform_driver(ge2d_drv);1056 1057MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");1058MODULE_DESCRIPTION("Amlogic 2D Graphic Acceleration Unit");1059MODULE_LICENSE("GPL");1060