From a6fd88487f2dda6f8c5ec28230129478711206fb Mon Sep 17 00:00:00 2001
From: Andrey Dolnikov <andrey.dolnikov@cogentembedded.com>
Date: Tue, 6 Feb 2018 13:38:34 +0300
Subject: [PATCH] media: rcar-imr: Add RSE support

This adds RSE support for V3H IMR

Signed-off-by: Andrey Dolnikov <andrey.dolnikov@cogentembedded.com>
---
 drivers/media/platform/rcar_imr.c        | 143 +++++++++++++++++++++++++++++++
 include/uapi/linux/rcar-imr.h            |  22 ++++-
 2 files changed, 165 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/rcar_imr.c b/drivers/media/platform/rcar_imr.c
index 9b601da..7b16765 100644
--- a/drivers/media/platform/rcar_imr.c
+++ b/drivers/media/platform/rcar_imr.c
@@ -17,6 +17,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/delay.h>
 #include <linux/rcar-imr.h>
+#include <linux/of.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fh.h>
@@ -38,6 +39,9 @@ MODULE_PARM_DESC(debug, "Debug level (0-4)");
  * Local types definitions
  ******************************************************************************/
 
+/* Number of RSE planes on V3H (non scaled, 1/2, 1/4, 1/8) */
+#define RSE_PLANES_NUM 4
+
 /* ...configuration data */
 struct imr_cfg {
 	/* ...display-list main program data */
@@ -49,6 +53,21 @@ struct imr_cfg {
 	/* ...pointers to the source/destination planes */
 	u32                    *src_pa_ptr[2];
 	u32                    *dst_pa_ptr[2];
+	/* ...pointers to the RSE destination planes */
+	u32                    *dstn_pa_ptr[RSE_PLANES_NUM];
+	u32                    *dstr_pa_ptr[RSE_PLANES_NUM];
+
+	/* ...offsets to RSE destination planes */
+	u32                     dstnr_offsets[IMR_EXTDST_NUM];
+
+	/* ...RSE logical right shift data */
+	u32                    *rscr_ptr;
+	u8                      rscr_sc8, rscr_sc4, rscr_sc2;
+
+	/* ...RSE destination stride values */
+	u32                     dstnr_strides[IMR_EXTDST_NUM];
+	u32                    *striden_ptr[RSE_PLANES_NUM];
+	u32                    *strider_ptr[RSE_PLANES_NUM];
 
 	/* ...subpixel destination coordinates space */
 	int                     dst_subpixel;
@@ -96,6 +115,8 @@ struct imr_device {
 	struct v4l2_m2m_dev    *m2m_dev;
 	struct device          *alloc_dev;
 
+	bool			rse;
+
 	/* ...do we need that counter really? framework counts fh structures for us - tbd */
 	int                     refcount;
 
@@ -219,6 +240,18 @@ struct imr_ctx {
 
 #define IMR_TPOR                        0xF0
 
+#define IMR_RSCSR			0x204
+#define IMR_RSCCR			0x208
+#define IMR_RSCR_RSE			31
+#define IMR_RSCR_SC8			25
+#define IMR_RSCR_SC4			21
+#define IMR_RSCR_SC2			17
+
+#define IMR_DSANRR0                     0x210
+#define IMR_DSTNRR0                     0x214
+#define IMR_DSARR0                      0x218
+#define IMR_DSTRR0                      0x21C
+
 /*******************************************************************************
  * Auxiliary helpers
  ******************************************************************************/
@@ -398,6 +431,7 @@ static int imr_queue_setup(struct vb2_queue *vq,
 	case V4L2_PIX_FMT_YVYU:
 	case V4L2_PIX_FMT_NV16:
 	case V4L2_PIX_FMT_Y10:
+	case V4L2_PIX_FMT_Y12:
 	case V4L2_PIX_FMT_Y16:
 		sizes[0] = w * h * 2;
 		break;
@@ -750,6 +784,7 @@ static inline void imr_dl_program_setup(struct imr_ctx *ctx, struct imr_cfg *cfg
 	int     W = ctx->queue[1].fmt.width;
 	int     H = ctx->queue[1].fmt.height;
 	u32     tricr = ctx->color & 0xFFFFFF;
+	int     i;
 
 	v4l2_dbg(2, debug, &ctx->imr->v4l2_dev, "setup %u*%u -> %u*%u mapping (type=%x)\n", w, h, W, H, type);
 
@@ -775,6 +810,38 @@ static inline void imr_dl_program_setup(struct imr_ctx *ctx, struct imr_cfg *cfg
 	*dl++ = IMR_OP_WTS(IMR_CMRCCR, 0xFFFF);
 	*dl++ = IMR_OP_WTS(IMR_CMRCCR2, 0xFFFF);
 
+	if (type & IMR_MAP_RSE) {
+		/* ...enable RSE */
+		*dl++ = IMR_OP_WTL(IMR_RSCCR, 1);
+		*dl++ = 0xffffffff;
+		*dl++ = IMR_OP_WTL(IMR_RSCSR, 1);
+		cfg->rscr_ptr = dl++;
+
+		for (i = 0; i < RSE_PLANES_NUM; i++) {
+			/* ...set destination planes base address and strides */
+			*dl++ = IMR_OP_WTL(IMR_DSANRR0 + i * 0x10, 4);
+			cfg->dstn_pa_ptr[i] = dl++;
+			cfg->striden_ptr[i] = dl++;
+			cfg->dstr_pa_ptr[i] = dl++;
+			cfg->strider_ptr[i] = dl++;
+		}
+
+		cfg->rscr_sc8 = cfg->rscr_sc4 = cfg->rscr_sc2 = 0;
+		memset(cfg->dstnr_offsets, 0, sizeof(cfg->dstnr_offsets));
+		memset(cfg->dstnr_strides, 0, sizeof(cfg->dstnr_strides));
+	} else {
+		/* ...disable RSE */
+		*dl++ = IMR_OP_WTL(IMR_RSCCR, 1);
+		*dl++ = 0xffffffff;
+
+		for (i = 0; i < RSE_PLANES_NUM; i++) {
+			cfg->dstn_pa_ptr[i] = NULL;
+			cfg->striden_ptr[i] = NULL;
+			cfg->dstr_pa_ptr[i] = NULL;
+			cfg->strider_ptr[i] = NULL;
+		}
+		cfg->rscr_ptr = NULL;
+	}
 	/* ...set source/destination addresses of Y/UV plane */
 	*dl++ = IMR_OP_WTL(IMR_DSAR, 2);
 	cfg->dst_pa_ptr[0] = dl++;
@@ -907,6 +974,12 @@ static int imr_ioctl_map(struct imr_ctx *ctx, struct imr_map_desc *desc)
 
 	type = desc->type;
 
+	/* ...check for RSE */
+	if ((type & IMR_MAP_RSE) && !imr->rse) {
+		v4l2_err(&imr->v4l2_dev, "Rotator & Scaler extension not supported\n");
+		return -EINVAL;
+	}
+
 	/* ...mesh item size calculation */
 	item_size = (type & IMR_MAP_LUCE ? 4 : 0) + (type & IMR_MAP_CLCE ? 4 : 0);
 
@@ -1055,6 +1128,12 @@ static int imr_ioctl_map_raw(struct imr_ctx *ctx, struct imr_map_desc *desc)
 	u32                     dl_start_offset;
 	dma_addr_t              dl_dma_addr;
 
+	/* ...check RSE */
+	if ((type & IMR_MAP_RSE) && !imr->rse) {
+		v4l2_err(&imr->v4l2_dev, "Rotator & Scaler extension not supported\n");
+		return -EINVAL;
+	}
+
 	/* ...calculate main routine length */
 	dl_size = imr_dl_program_length(ctx);
 	if (!dl_size) {
@@ -1103,6 +1182,46 @@ static int imr_ioctl_color(struct imr_ctx *ctx, u32 color)
 	return 0;
 }
 
+static int imr_extdst_set(struct imr_ctx *ctx, u32 *extdst)
+{
+	struct imr_device      *imr = ctx->imr;
+	struct imr_cfg         *cfg = ctx->cfg;
+
+	if (!cfg) {
+		v4l2_err(&imr->v4l2_dev, "failed to set V3H extension dst buffers: No active confguration.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user((void *) cfg->dstnr_offsets, (void __user *) extdst, sizeof(cfg->dstnr_offsets))) {
+		v4l2_err(&imr->v4l2_dev, "failed to read V3H extension dst buffers\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int imr_extstride_set(struct imr_ctx *ctx, struct imr_rse_param *param)
+{
+	struct imr_device      *imr = ctx->imr;
+	struct imr_cfg         *cfg = ctx->cfg;
+
+	if (!cfg) {
+		v4l2_err(&imr->v4l2_dev, "failed to set V3H extension buffers params: No active confguration.\n");
+		return -EINVAL;
+	}
+
+	cfg->rscr_sc8 = param->sc8;
+	cfg->rscr_sc4 = param->sc4;
+	cfg->rscr_sc2 = param->sc2;
+
+	if (copy_from_user((void *) cfg->dstnr_strides, (void __user *) param->strides, sizeof(cfg->dstnr_strides))) {
+		v4l2_err(&imr->v4l2_dev, "failed to read V3H extension buffers strides\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
 /*******************************************************************************
  * V4L2 I/O controls
  ******************************************************************************/
@@ -1356,6 +1475,14 @@ static long imr_default(struct file *file, void *fh, bool valid_prio, unsigned i
 		/* ...set solid color code */
 		return imr_ioctl_color(ctx, *(u32 *)arg);
 
+	case VIDIOC_IMR_EXTDST:
+		/* ...set V3H extension dst buffers */
+		return imr_extdst_set(ctx, *(u32 **)arg);
+
+	case VIDIOC_IMR_EXTSTRIDE:
+		/* ...set V3H extension dst strides */
+		return imr_extstride_set(ctx, (struct imr_rse_param *)arg);
+
 	default:
 		return -ENOIOCTLCMD;
 	}
@@ -1579,6 +1706,7 @@ static void imr_device_run(void *priv)
 	struct vb2_v4l2_buffer *src_buf, *dst_buf;
 	u32                     src_addr, dst_addr;
 	unsigned long           flags;
+	int			i;
 
 	v4l2_dbg(3, debug, &imr->v4l2_dev, "run next job...\n");
 
@@ -1608,6 +1736,17 @@ static void imr_device_run(void *priv)
 	*cfg->src_pa_ptr[0] = src_addr = (u32)vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
 	*cfg->dst_pa_ptr[0] = dst_addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
 
+	for (i = 0; i < RSE_PLANES_NUM; i++) {
+		if (cfg->rscr_ptr) *cfg->rscr_ptr = (1 << IMR_RSCR_RSE) | (cfg->rscr_sc8 << IMR_RSCR_SC8) |
+						    (cfg->rscr_sc4 << IMR_RSCR_SC4) |(cfg->rscr_sc2 << IMR_RSCR_SC2);
+
+		if (cfg->dstn_pa_ptr[i]) *cfg->dstn_pa_ptr[i] = dst_addr + cfg->dstnr_offsets[i];
+		if (cfg->dstr_pa_ptr[i]) *cfg->dstr_pa_ptr[i] = dst_addr + cfg->dstnr_offsets[i + RSE_PLANES_NUM];
+
+		if (cfg->striden_ptr[i]) *cfg->striden_ptr[i] = cfg->dstnr_strides[i];
+		if (cfg->strider_ptr[i]) *cfg->strider_ptr[i] = cfg->dstnr_strides[i + RSE_PLANES_NUM];
+	}
+
 	/* ...adjust source/destination parameters of the UV-plane as needed */
 	if (cfg->src_pa_ptr[1] && cfg->dst_pa_ptr[1]) {
 		*cfg->src_pa_ptr[1] = src_addr + ctx->queue[0].fmt.width * ctx->queue[0].fmt.height;
@@ -1776,6 +1915,7 @@ static int imr_probe(struct platform_device *pdev)
 {
 	struct imr_device *imr;
 	struct resource *res;
+	struct device_node *np = pdev->dev.of_node;
 	int ret;
 
 	imr = devm_kzalloc(&pdev->dev, sizeof(*imr), GFP_KERNEL);
@@ -1786,6 +1926,9 @@ static int imr_probe(struct platform_device *pdev)
 	spin_lock_init(&imr->lock);
 	imr->dev = &pdev->dev;
 
+	/* Check RSE support */
+	imr->rse = of_property_read_bool(np, "rse");
+
 	/* ...memory-mapped registers */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
diff --git a/include/uapi/linux/rcar-imr.h b/include/uapi/linux/rcar-imr.h
index 7b8ed0c..19fdbae 100644
--- a/include/uapi/linux/rcar-imr.h
+++ b/include/uapi/linux/rcar-imr.h
@@ -25,8 +25,8 @@ struct imr_map_desc {
 	/* ...total size of the mesh structure */
 	u32             size;
 
-    /* ...map-specific user-pointer */
-    void           *data;
+	/* ...map-specific user-pointer */
+	void           *data;
 
 }   __attribute__((packed));
 
@@ -54,6 +54,9 @@ struct imr_map_desc {
 /* ...bilinear filtration enable flag */
 #define IMR_MAP_BFE                     (1 << 7)
 
+/* ...extended functionality (rotation/scaling) enable flag */
+#define IMR_MAP_RSE                     (1 << 21)
+
 /* ...source coordinate decimal point position bit index */
 #define __IMR_MAP_UVDPOR_SHIFT          8
 #define __IMR_MAP_UVDPOR(v)             (((v) >> __IMR_MAP_UVDPOR_SHIFT) & 0x7)
@@ -88,11 +91,26 @@ struct imr_mesh {
 }   __attribute__((packed));
 
 /*******************************************************************************
+ * V3H Extension destination data
+ ******************************************************************************/
+/* ...number of V3H extension destination buffers (rotated/non-rotated, scaled 1/1, 1/2, 1/4, 1/8) */
+#define IMR_EXTDST_NUM                      8
+
+struct imr_rse_param {
+	/* ...logical right shift data */
+	u8		sc8, sc4, sc2;
+	/* ...destination buffers stride */
+	u32            *strides;
+};
+
+/*******************************************************************************
  * Private IOCTL codes
  ******************************************************************************/
 
 #define VIDIOC_IMR_MESH                 _IOW('V', BASE_VIDIOC_PRIVATE + 0, struct imr_map_desc)
 #define VIDIOC_IMR_MESH_RAW             _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct imr_map_desc)
 #define VIDIOC_IMR_COLOR                _IOW('V', BASE_VIDIOC_PRIVATE + 2, u32)
+#define VIDIOC_IMR_EXTDST               _IOW('V', BASE_VIDIOC_PRIVATE + 3, u32 *)
+#define VIDIOC_IMR_EXTSTRIDE            _IOW('V', BASE_VIDIOC_PRIVATE + 4, struct imr_rse_param)
 
 #endif  /* RCAR_IMR_USER_H */
-- 
2.7.4