From b6176e313aea415b83d5762db275b2f364f08820 Mon Sep 17 00:00:00 2001
From: Konstantin Kozhevnikov <Konstantin.Kozhevnikov@cogentembedded.com>
Date: Tue, 14 Nov 2017 01:38:51 -0800
Subject: [PATCH] gpu: drm: rcar-du: Extend VSP1-DRM interface

Extend VSP1-DRM interface

Signed-off-by: Konstantin Kozhevnikov <Konstantin.Kozhevnikov@cogentembedded.com>
---
 drivers/gpu/drm/drm_framebuffer.c       |   1 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.h   |   5 +
 drivers/gpu/drm/rcar-du/rcar_du_kms.c   |  42 ++++++-
 drivers/gpu/drm/rcar-du/rcar_du_plane.c |   3 +-
 drivers/gpu/drm/rcar-du/rcar_du_plane.h |   5 +
 drivers/gpu/drm/rcar-du/rcar_du_vsp.c   | 209 ++++++++++++++++++++++++++------
 drivers/gpu/drm/rcar-du/rcar_du_vsp.h   |   7 +-
 7 files changed, 232 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index 398efd6..4a43e15 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -192,6 +192,7 @@ static int format_check(const struct drm_mode_fb_cmd2 *r)
 	case DRM_FORMAT_YVU422:
 	case DRM_FORMAT_YUV444:
 	case DRM_FORMAT_YVU444:
+	case DRM_FORMAT_R8:
 		return 0;
 	default:
 		format_name = drm_get_format_name(r->pixel_format);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index 45d6e7e..bdf2612 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -104,6 +104,11 @@ struct rcar_du_device {
 	struct {
 		struct drm_property *alpha;
 		struct drm_property *colorkey;
+		struct drm_property *alphaplane;
+		struct drm_property *blend;
+		struct drm_property *ckey;
+		struct drm_property *ckey_set0;
+		struct drm_property *ckey_set1;
 	} props;
 
 	unsigned int dpad0_source;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index e955e92..31b48bc 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -10,7 +10,7 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
-
+//#define DEBUG
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
@@ -101,6 +101,12 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
 		.planes = 2,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
 		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_R8,
+		.bpp = 8,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_8BPP,
+		.edf = PnDDCR4_EDF_NONE,
 	},
 };
 
@@ -169,6 +175,10 @@ static const struct rcar_du_format_info rcar_vsp_format_infos[] = {
 		.fourcc = DRM_FORMAT_YVU444,
 		.bpp = 24,
 		.planes = 3,
+	}, {
+		.fourcc = DRM_FORMAT_R8,
+		.bpp = 8,
+		.planes = 1,
 	},
 };
 
@@ -565,6 +575,36 @@ static int rcar_du_properties_init(struct rcar_du_device *rcdu)
 	if (rcdu->props.colorkey == NULL)
 		return -ENOMEM;
 
+	rcdu->props.alphaplane =
+		drm_property_create(rcdu->ddev, DRM_MODE_PROP_OBJECT, "alphaplane", 1);
+	if (rcdu->props.alphaplane == NULL)
+		return -ENOMEM;
+	rcdu->props.alphaplane->values[0] = DRM_MODE_OBJECT_FB;
+
+	rcdu->props.blend =
+		drm_property_create_range(rcdu->ddev, 0, "blend",
+					  0, 0xffffffff);
+	if (rcdu->props.blend == NULL)
+		return -ENOMEM;
+
+	rcdu->props.ckey =
+		drm_property_create_range(rcdu->ddev, 0, "ckey",
+					  0, 0xffffffff);
+	if (rcdu->props.ckey == NULL)
+		return -ENOMEM;
+
+	rcdu->props.ckey_set0 =
+		drm_property_create_range(rcdu->ddev, 0, "ckey_set0",
+					  0, 0xffffffff);
+	if (rcdu->props.ckey_set0 == NULL)
+		return -ENOMEM;
+
+	rcdu->props.ckey_set1 =
+		drm_property_create_range(rcdu->ddev, 0, "ckey_set1",
+					  0, 0xffffffff);
+	if (rcdu->props.ckey_set1 == NULL)
+		return -ENOMEM;
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
index e408aa3..2b57f09 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -10,7 +10,7 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
-
+//#define DEBUG
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
@@ -719,6 +719,7 @@ static const uint32_t formats[] = {
 	DRM_FORMAT_NV12,
 	DRM_FORMAT_NV21,
 	DRM_FORMAT_NV16,
+	DRM_FORMAT_R8,
 };
 
 int rcar_du_planes_init(struct rcar_du_group *rgrp)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
index c1de338..a6065ef 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
@@ -66,6 +66,11 @@ struct rcar_du_plane_state {
 
 	unsigned int alpha;
 	unsigned int colorkey;
+	struct drm_framebuffer *alphaplane;
+	unsigned int blend;
+	unsigned int ckey;
+	unsigned int ckey_set0;
+	unsigned int ckey_set1;
 };
 
 static inline struct rcar_du_plane_state *
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 770238a..910e0f0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -10,7 +10,7 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
-
+//#define DEBUG
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
@@ -91,6 +91,16 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
 
 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
 {
+	struct rcar_du_vsp *vsp = crtc->vsp;
+	struct rcar_du_vsp_plane *primary = &vsp->planes[0];
+	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(primary->plane.state);
+
+	/* ...drop alpha-plane associated with primary plane (why only primary? - tbd) */
+	if (rstate->alphaplane) {
+		drm_framebuffer_unreference(rstate->alphaplane);
+		rstate->alphaplane = NULL;
+	}
+
 	vsp1_du_setup_lif(crtc->vsp->vsp, NULL, crtc->lif_index,
 			  crtc->suspend);
 }
@@ -133,6 +143,7 @@ static const u32 formats_kms[] = {
 	DRM_FORMAT_YVU422,
 	DRM_FORMAT_YUV444,
 	DRM_FORMAT_YVU444,
+	DRM_FORMAT_R8,
 };
 
 static const u32 formats_v4l2[] = {
@@ -162,6 +173,7 @@ static const u32 formats_v4l2[] = {
 	V4L2_PIX_FMT_YVU422M,
 	V4L2_PIX_FMT_YUV444M,
 	V4L2_PIX_FMT_YVU444M,
+	V4L2_PIX_FMT_GREY,
 };
 
 static const u32 formats_xlate[][2] = {
@@ -184,6 +196,7 @@ static const u32 formats_xlate[][2] = {
 	{ DRM_FORMAT_NV21, V4L2_PIX_FMT_NV21M },
 	{ DRM_FORMAT_NV16, V4L2_PIX_FMT_NV16M },
 	{ DRM_FORMAT_NV61, V4L2_PIX_FMT_NV61M },
+	{ DRM_FORMAT_R8, V4L2_PIX_FMT_GREY },
 };
 
 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
@@ -226,6 +239,27 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 		}
 	}
 
+	/* ...add alpha-plane as needed */
+	if (state->alphaplane) {
+		i = state->format->planes;
+		cfg.alpha_mem = sg_dma_address(state->sg_tables[i].sgl);
+		cfg.alpha_pitch = state->alphaplane->pitches[0];
+		pr_debug("alpha-%d: set alpha-mem address: %llx, pitch=%d\n", i, (unsigned long long)cfg.alpha_mem, cfg.alpha_pitch);
+	}
+
+	/* ...add blending formula as  needed */
+	if (state->blend) {
+		cfg.blend = state->blend;
+		pr_debug("set blending formula: %X\n", cfg.blend);
+	}
+
+	/* ...add color key property as needed */
+	if (state->ckey) {
+		cfg.ckey = state->ckey;
+		cfg.ckey_set0 = state->ckey_set0;
+		cfg.ckey_set1 = state->ckey_set1;
+	}
+
 	vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg);
 }
 
@@ -259,6 +293,23 @@ static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
 		}
 	}
 
+	/* ...check if we have alpha-plane attached */
+	if (rstate->alphaplane) {
+		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(rstate->alphaplane, 0);
+		struct sg_table *sgt = &rstate->sg_tables[i++];
+
+		ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr, gem->base.size);
+		if (ret)
+			goto fail;
+
+		ret = vsp1_du_map_sg(vsp->vsp, sgt);
+		if (!ret) {
+			sg_free_table(sgt);
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
 	return 0;
 
 fail:
@@ -288,6 +339,14 @@ static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
 		vsp1_du_unmap_sg(vsp->vsp, sgt);
 		sg_free_table(sgt);
 	}
+
+	if (rstate->alphaplane) {
+		struct sg_table *sgt = &rstate->sg_tables[i];
+
+		vsp1_du_unmap_sg(vsp->vsp, sgt);
+		sg_free_table(sgt);
+		pr_debug("unmap alpha-plane\n");
+	}
 }
 
 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
@@ -369,6 +428,11 @@ rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
 	if (copy == NULL)
 		return NULL;
 
+	if (copy->alphaplane) {
+		drm_framebuffer_reference(copy->alphaplane);
+		pr_debug("duplicate alpha-plane '%p' (refcount=%d)\n", copy->alphaplane, drm_framebuffer_read_refcount(copy->alphaplane));
+	}
+
 	__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
 
 	return &copy->state;
@@ -377,8 +441,15 @@ rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
 static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
 						   struct drm_plane_state *state)
 {
+	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
+
+	if (rstate->alphaplane) {
+		pr_debug("unref alpha-plane '%p' (refcount=%d)\n", rstate->alphaplane, drm_framebuffer_read_refcount(rstate->alphaplane));
+		drm_framebuffer_unreference(rstate->alphaplane);
+	}
+
 	__drm_atomic_helper_plane_destroy_state(state);
-	kfree(to_rcar_vsp_plane_state(state));
+	kfree(rstate);
 }
 
 static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
@@ -386,6 +457,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
 	struct rcar_du_vsp_plane_state *state;
 
 	if (plane->state) {
+		pr_debug("reset plane '%p'\n", to_rcar_vsp_plane_state(plane->state)->alphaplane);
 		rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
 		plane->state = NULL;
 	}
@@ -410,7 +482,30 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
 
 	if (property == rcdu->props.alpha)
 		rstate->alpha = val;
-	else
+	else if (property == rcdu->props.blend)
+		rstate->blend = val;
+	else if (property == rcdu->props.ckey)
+		rstate->ckey = val;
+	else if (property == rcdu->props.ckey_set0)
+		rstate->ckey_set0 = val;
+	else if (property == rcdu->props.ckey_set1)
+		rstate->ckey_set1 = val;
+	else if (property == rcdu->props.alphaplane) {
+		if (rstate->alphaplane) {
+			pr_debug("unref alpha-plane '%p' (refcount=%d)\n", rstate->alphaplane, drm_framebuffer_read_refcount(rstate->alphaplane));
+			drm_framebuffer_unreference(rstate->alphaplane);
+		}
+		rstate->alphaplane = drm_framebuffer_lookup(plane->dev, val);
+		if (rstate->alphaplane) {
+			pr_debug("use alpha-plane '%p' (refcount=%d)\n", rstate->alphaplane, drm_framebuffer_read_refcount(rstate->alphaplane));
+			/* ...the way how we handle this leads to a "loss" of plane reference (it is acquired
+			 * within "drm_property_change_valid_get" but not returned in symmetric "drm_property_change_valid_put")
+			 * Whether it is a bug or was done intentionally, I don't know. For a moment just drop that
+			 * extra reference right here
+			 */
+			if (0) drm_framebuffer_unreference(rstate->alphaplane);
+		}
+	} else
 		return -EINVAL;
 
 	return 0;
@@ -426,6 +521,16 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
 
 	if (property == rcdu->props.alpha)
 		*val = rstate->alpha;
+	else if (property == rcdu->props.alphaplane)
+		*val = (rstate->alphaplane ? rstate->alphaplane->base.id : 0);
+	else if (property == rcdu->props.blend)
+		*val = rstate->blend;
+	else if (property == rcdu->props.ckey)
+		*val = rstate->ckey;
+	else if (property == rcdu->props.ckey_set0)
+		*val = rstate->ckey_set0;
+	else if (property == rcdu->props.ckey_set1)
+		*val = rstate->ckey_set1;
 	else
 		return -EINVAL;
 
@@ -442,9 +547,10 @@ int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
 	struct rcar_du_crtc *rcrtc;
 	struct rcar_du_device *rcdu;
 	const struct drm_display_mode *mode;
-	u32 pixelformat, bpp;
-	unsigned int pitch;
+	struct drm_framebuffer *fb;
 	dma_addr_t mem[3];
+	struct sg_table sg_tables[3];
+	int i = 0;
 
 	obj = drm_mode_object_find(dev, sh->crtc_id, DRM_MODE_OBJECT_CRTC);
 	if (!obj)
@@ -455,62 +561,79 @@ int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
 	rcdu = rcrtc->group->dev;
 	mode = &rcrtc->crtc.state->adjusted_mode;
 
-	switch (sh->fmt) {
-	case DRM_FORMAT_RGB565:
-		bpp = 16;
-		pixelformat = V4L2_PIX_FMT_RGB565;
-		break;
-	case DRM_FORMAT_ARGB1555:
-		bpp = 16;
-		pixelformat = V4L2_PIX_FMT_ARGB555;
-		break;
-	case DRM_FORMAT_ARGB8888:
-		bpp = 32;
-		pixelformat = V4L2_PIX_FMT_ABGR32;
-		break;
-	default:
-		dev_err(rcdu->dev, "specified format is not supported.\n");
+	fb = drm_framebuffer_lookup(dev, sh->buff);
+	if (!fb) {
+		dev_err(dev->dev, "failed to lookup destination framebuffer '%lu'\n", sh->buff);
 		return -EINVAL;
 	}
 
-	pitch = mode->hdisplay * bpp / 8;
+	/* ...check framebuffer is okay */
+	if ((fb->width != (mode->hdisplay)) ||
+		(fb->height != (mode->vdisplay))) {
+        dev_err(dev->dev, "wrong fb mode: %d*%d vs %d*%d\n", fb->width, fb->height, mode->hdisplay, mode->vdisplay);
+		ret = -EINVAL;
+		goto done;
+	}
 
-	mem[0] = sh->buff;
-	mem[1] = 0;
-	mem[2] = 0;
+	/* ...need to verify compatibility of output format, I guess - tbd */
 
-	if ((sh->width != (mode->hdisplay)) ||
-		(sh->height != (mode->vdisplay)))
-		return -EINVAL;
+	/* ...fill memory planes addresses */
+	for (i = 0; i < 3; i++) {
+		struct drm_gem_cma_object  *gem;
+		struct sg_table *sgt = &sg_tables[i];
+		gem = drm_fb_cma_get_gem_obj(fb, i);
+		if (!gem)
+			break;
+		ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
+				      gem->base.size);
+		if (ret)
+			goto done;
 
-	if ((pitch * mode->vdisplay) > sh->buff_len)
-		return -EINVAL;
+		ret = vsp1_du_map_sg(rcrtc->vsp->vsp, sgt);
+		if (!ret) {
+			sg_free_table(sgt);
+			ret = -ENOMEM;
+			goto done;
+		}
+		mem[i] = sg_dma_address(sg_tables[i].sgl) + fb->offsets[i];
+	}
+
+	dev_info(dev->dev, "setup write-back (pixfmt=%X, %u*%u, planes: %d)\n", fb->pixel_format, fb->width, fb->height, i);
+
+	vsp1_du_setup_wb(rcrtc->vsp->vsp, fb->pixel_format, fb->pitches[0], mem, rcrtc->lif_index);
 
-	vsp1_du_setup_wb(rcrtc->vsp->vsp, pixelformat, pitch, mem,
-						rcrtc->lif_index);
 	ret = vsp1_du_wait_wb(rcrtc->vsp->vsp, WB_STAT_CATP_SET,
 			      rcrtc->lif_index);
 	if (ret != 0)
-		return ret;
+		goto done;
 
 	ret = rcar_du_async_commit(dev, crtc);
 	if (ret != 0)
-		return ret;
+		goto done;
 
 	ret = vsp1_du_wait_wb(rcrtc->vsp->vsp, WB_STAT_CATP_START,
 			      rcrtc->lif_index);
 	if (ret != 0)
-		return ret;
+		goto done;
 
 	ret = rcar_du_async_commit(dev, crtc);
 	if (ret != 0)
-		return ret;
+		goto done;
 
 	ret = vsp1_du_wait_wb(rcrtc->vsp->vsp, WB_STAT_CATP_DONE,
 			      rcrtc->lif_index);
 	if (ret != 0)
-		return ret;
+		goto done;
+
+done:
+	/* ...unmap all tables */
+	while (i--) {
+		struct sg_table *sgt = &sg_tables[i];
+		vsp1_du_unmap_sg(rcrtc->vsp->vsp, sgt);
+		sg_free_table(sgt);
+	}
 
+	drm_framebuffer_unreference(fb);
 	return ret;
 }
 
@@ -574,6 +697,7 @@ static const uint32_t formats[] = {
 	DRM_FORMAT_NV21,
 	DRM_FORMAT_NV16,
 	DRM_FORMAT_NV61,
+	DRM_FORMAT_R8,
 };
 
 int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
@@ -653,13 +777,24 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
 		drm_plane_helper_add(&plane->plane,
 				     &rcar_du_vsp_plane_helper_funcs);
 
+#if 0   // ...use same set of properties for all planes
 		if (type == DRM_PLANE_TYPE_PRIMARY)
 			continue;
-
+#endif
 		drm_object_attach_property(&plane->plane.base,
 					   rcdu->props.alpha, 255);
 		drm_plane_create_zpos_property(&plane->plane, 1, 1,
 					       vsp->num_planes - 1);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->props.alphaplane, 0);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->props.blend, 0);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->props.ckey, 0);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->props.ckey_set0, 0);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->props.ckey_set1, 0);
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
index 3fd9cef..2f4aa41 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
@@ -52,10 +52,15 @@ struct rcar_du_vsp_plane_state {
 	struct drm_plane_state state;
 
 	const struct rcar_du_format_info *format;
-	struct sg_table sg_tables[3];
+	struct sg_table sg_tables[4];
 
 	unsigned int alpha;
 	unsigned int zpos;
+	struct drm_framebuffer *alphaplane;
+	unsigned int blend;
+	unsigned int ckey;
+	unsigned int ckey_set0;
+	unsigned int ckey_set1;
 };
 
 static inline struct rcar_du_vsp_plane_state *
-- 
2.7.4

