// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-ctrls.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include "ge2d-regs.h"
#define GE2D_NAME "meson-ge2d"
#define DEFAULT_WIDTH 128
#define DEFAULT_HEIGHT 128
#define DEFAULT_STRIDE 512
#define MAX_WIDTH 8191
#define MAX_HEIGHT 8191
/*
* Missing features:
* - Scaling
* - Simple 1/2 vertical scaling
* - YUV input support
* - Source global alpha
* - Colorspace conversion
*/
struct ge2d_fmt {
u32 fourcc;
bool alpha;
bool le;
unsigned int depth;
unsigned int hw_fmt;
unsigned int hw_map;
};
struct ge2d_frame {
struct vb2_v4l2_buffer *buf;
/* Image Format */
struct v4l2_pix_format pix_fmt;
/* Crop */
struct v4l2_rect crop;
/* Image format */
const struct ge2d_fmt *fmt;
};
struct ge2d_ctx {
struct v4l2_fh fh;
struct meson_ge2d *ge2d;
struct ge2d_frame in;
struct ge2d_frame out;
struct v4l2_ctrl_handler ctrl_handler;
unsigned long sequence_out, sequence_cap;
/* Control values */
u32 hflip;
u32 vflip;
u32 xy_swap;
};
struct meson_ge2d {
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
struct video_device *vfd;
struct device *dev;
struct regmap *map;
struct clk *clk;
/* vb2 queue lock */
struct mutex mutex;
struct ge2d_ctx *curr;
};
#define FMT(_fourcc, _alpha, _depth, _map) \
{ \
.fourcc = _fourcc, \
.alpha = (_alpha), \
.depth = (_depth), \
.hw_fmt = GE2D_FORMAT_ ## _depth ## BIT, \
.hw_map = GE2D_COLOR_MAP_ ## _map, \
}
/* TOFIX Handle the YUV input formats */
static const struct ge2d_fmt formats[] = {
/* FOURCC Alpha HW FMT HW MAP */
FMT(V4L2_PIX_FMT_XRGB32, false, 32, BGRA8888),
FMT(V4L2_PIX_FMT_RGB32, true, 32, BGRA8888),
FMT(V4L2_PIX_FMT_ARGB32, true, 32, BGRA8888),
FMT(V4L2_PIX_FMT_RGBX32, false, 32, ABGR8888),
FMT(V4L2_PIX_FMT_RGBA32, true, 32, ABGR8888),
FMT(V4L2_PIX_FMT_BGRX32, false, 32, RGBA8888),
FMT(V4L2_PIX_FMT_BGRA32, true, 32, RGBA8888),
FMT(V4L2_PIX_FMT_BGR32, true, 32, ARGB8888),
FMT(V4L2_PIX_FMT_ABGR32, true, 32, ARGB8888),
FMT(V4L2_PIX_FMT_XBGR32, false, 32, ARGB8888),
FMT(V4L2_PIX_FMT_RGB24, false, 24, BGR888),
FMT(V4L2_PIX_FMT_BGR24, false, 24, RGB888),
FMT(V4L2_PIX_FMT_XRGB555X, false, 16, ARGB1555),
FMT(V4L2_PIX_FMT_ARGB555X, true, 16, ARGB1555),
FMT(V4L2_PIX_FMT_RGB565, false, 16, RGB565),
FMT(V4L2_PIX_FMT_RGBX444, false, 16, RGBA4444),
FMT(V4L2_PIX_FMT_RGBA444, true, 16, RGBA4444),
FMT(V4L2_PIX_FMT_XRGB444, false, 16, ARGB4444),
FMT(V4L2_PIX_FMT_ARGB444, true, 16, ARGB4444),
};
#define NUM_FORMATS ARRAY_SIZE(formats)
static const struct ge2d_fmt *find_fmt(struct v4l2_format *f)
{
unsigned int i;
for (i = 0; i < NUM_FORMATS; i++) {
if (formats[i].fourcc == f->fmt.pix.pixelformat)
return &formats[i];
}
/*
* TRY_FMT/S_FMT should never return an error when the requested format
* is not supported. Drivers should always return a valid format,
* preferably a format that is as widely supported by applications as
* possible.
*/
return &formats[0];
}
static struct ge2d_frame *get_frame(struct ge2d_ctx *ctx,
enum v4l2_buf_type type)
{
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return &ctx->in;
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &ctx->out;
default:
/* This should never happen, warn and return OUTPUT frame */
dev_warn(ctx->ge2d->dev, "%s: invalid buffer type\n", __func__);
return &ctx->in;
}
}
static void ge2d_hw_start(struct meson_ge2d *ge2d)
{
struct ge2d_ctx *ctx = ge2d->curr;
u32 reg;
/* Reset */
regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1,
GE2D_SOFT_RST, GE2D_SOFT_RST);
regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1,
GE2D_SOFT_RST, 0);
usleep_range(100, 200);
/* Implement CANVAS for non-AXG */
regmap_write(ge2d->map, GE2D_SRC1_BADDR_CTRL,
(vb2_dma_contig_plane_dma_addr(&ctx->in.buf->vb2_buf, 0) + 7) >> 3);
regmap_write(ge2d->map, GE2D_SRC1_STRIDE_CTRL,
(ctx->in.pix_fmt.bytesperline + 7) >> 3);
reg