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