Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 08/27] drm/sun4i: Rename some macros in DE2 driver
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Now that some knowledge of DE2 is gained, rename or add some macros to
make code more readable.

Max channel macro is removed, since it is not used and it is not clear
if it has right value. Structures in BSP driver shows possibility of 5
channels maximum although there is no SoC with such configuration.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c |  5 +++--
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 13 ++++++-------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 23659ce1cf27..c7d4ccd605e0 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -323,8 +323,9 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
 
 	/* Initialize blender */
-	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
-		     SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
+	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL,
+		     SUN8I_MIXER_BLEND_PIPE_CTL_EN(0) |
+		     SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
 
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 9b50733298b8..76f0b2bd91e2 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -16,8 +16,6 @@
 
 #include "sunxi_engine.h"
 
-#define SUN8I_MIXER_MAX_CHAN_COUNT		4
-
 #define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
 #define SUN8I_MIXER_COORD(x, y)			((y) << 16 | (x))
 
@@ -26,14 +24,14 @@
 #define SUN8I_MIXER_GLOBAL_DBUFF		0x8
 #define SUN8I_MIXER_GLOBAL_SIZE			0xc
 
-#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		0x1
+#define SUN8I_MIXER_GLOBAL_CTL_RT_EN		BIT(0)
 
-#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		0x1
+#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE		BIT(0)
 
-#define SUN8I_MIXER_BLEND_FCOLOR_CTL		0x1000
+#define SUN8I_MIXER_BLEND_PIPE_CTL		0x1000
 #define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x)	(0x1004 + 0x10 * (x) + 0x0)
 #define SUN8I_MIXER_BLEND_ATTR_INSIZE(x)	(0x1004 + 0x10 * (x) + 0x4)
-#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x)	(0x1004 + 0x10 * (x) + 0x8)
+#define SUN8I_MIXER_BLEND_ATTR_COORD(x)		(0x1004 + 0x10 * (x) + 0x8)
 #define SUN8I_MIXER_BLEND_ROUTE			0x1080
 #define SUN8I_MIXER_BLEND_PREMULTIPLY		0x1084
 #define SUN8I_MIXER_BLEND_BKCOLOR		0x1088
@@ -45,10 +43,11 @@
 #define SUN8I_MIXER_BLEND_CK_MIN(x)		(0x10e0 + 0x04 * (x))
 #define SUN8I_MIXER_BLEND_OUTCTL		0x10fc
 
+#define SUN8I_MIXER_BLEND_PIPE_CTL_EN(pipe)	BIT(8 + pipe)
+#define SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(pipe)	BIT(pipe)
 /* colors are always in AARRGGBB format */
 #define SUN8I_MIXER_BLEND_COLOR_BLACK		0xff000000
 /* The following numbers are some still unknown magic numbers */
-#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF	0x00000101
 #define SUN8I_MIXER_BLEND_MODE_DEF		0x03010301
 
 #define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 09/27] drm/sun4i: Rework enabling plane in DE2 driver
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

If we want to support multiple planes in the future, code which enables
pipe has to be moved to appropriate place and it must depend on channel
id instead of being hardcoded.

Side effect of that rework is definition of default Z position. For now,
put first channel at the bottom, second above it and so on.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index c7d4ccd605e0..015943c0ed5a 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -55,6 +55,15 @@ void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
 	regmap_update_bits(mixer->engine.regs,
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	if (enable)
+		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(chan);
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_BLEND_PIPE_CTL,
+			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(chan), val);
 }
 
 static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
@@ -98,7 +107,7 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
 					      state->crtc_h));
 		DRM_DEBUG_DRIVER("Updating blender size\n");
 		regmap_write(mixer->engine.regs,
-			     SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
+			     SUN8I_MIXER_BLEND_ATTR_INSIZE(chan),
 			     SUN8I_MIXER_SIZE(state->crtc_w,
 					      state->crtc_h));
 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
@@ -322,20 +331,17 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
 
-	/* Initialize blender */
+	/*
+	 * Set fill color of bottom plane to black. Generally not needed
+	 * except when VI plane is at bottom (zpos = 0) and enabled.
+	 */
 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL,
-		     SUN8I_MIXER_BLEND_PIPE_CTL_EN(0) |
 		     SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
-
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
+	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
 
-	/* Select the first UI channel */
-	DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
-			 mixer->cfg->vi_num);
-	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
-		     mixer->cfg->vi_num);
+	/* Fixed zpos for now */
+	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 0x43210);
 
 	plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
 	for (i = 0; i < plane_cnt; i++)
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 10/27] drm/sun4i: Start using layer id in DE2 driver
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Till now, plane selection was hardcoded to first overlay in first UI
channel and layer parameter is unused.

Rename and add parameters to layer functions so they would represent HW
more accurately and start using then.

It turns out that overlays don't fit well in current DRM design, because
they can't be blended together or scaled independetly when they are set
to same channel. Because of that, always use only first overlay in each
channel.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_layer.c | 18 ++++++++-----
 drivers/gpu/drm/sun4i/sun8i_layer.h |  3 ++-
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 50 ++++++++++++++++---------------------
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 16 ++++++------
 4 files changed, 43 insertions(+), 44 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
index 23810ff72684..5aa6e2c9728e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -32,7 +32,8 @@ static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
 	struct sun8i_mixer *mixer = layer->mixer;
 
-	sun8i_mixer_layer_enable(mixer, layer->id, false);
+	sun8i_mixer_layer_enable(mixer, layer->channel,
+				 layer->overlay, false);
 }
 
 static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
@@ -41,10 +42,14 @@ static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
 	struct sun8i_mixer *mixer = layer->mixer;
 
-	sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
-	sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
-	sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
-	sun8i_mixer_layer_enable(mixer, layer->id, true);
+	sun8i_mixer_update_layer_coord(mixer, layer->channel,
+				       layer->overlay, plane);
+	sun8i_mixer_update_layer_formats(mixer, layer->channel,
+					 layer->overlay, plane);
+	sun8i_mixer_update_layer_buffer(mixer, layer->channel,
+					layer->overlay, plane);
+	sun8i_mixer_layer_enable(mixer, layer->channel,
+				 layer->overlay, true);
 }
 
 static struct drm_plane_helper_funcs sun8i_mixer_layer_helper_funcs = {
@@ -126,7 +131,8 @@ struct drm_plane **sun8i_layers_init(struct drm_device *drm,
 			return ERR_CAST(layer);
 		};
 
-		layer->id = i;
+		layer->channel = mixer->cfg->vi_num + i;
+		layer->overlay = 0;
 		planes[i] = &layer->plane;
 	};
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h
index e5eccd27cff0..d246e0dd3d48 100644
--- a/drivers/gpu/drm/sun4i/sun8i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
@@ -22,7 +22,8 @@ struct sun8i_layer {
 	struct drm_plane	plane;
 	struct sun4i_drv	*drv;
 	struct sun8i_mixer	*mixer;
-	int			id;
+	int			channel;
+	int			overlay;
 };
 
 static inline struct sun8i_layer *
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 015943c0ed5a..2bf7ba1e5ba7 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -37,15 +37,13 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine)
 		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
 }
 
-void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
-				int layer, bool enable)
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, int channel,
+			      int overlay, bool enable)
 {
 	u32 val;
-	/* Currently the first UI channel is used */
-	int chan = mixer->cfg->vi_num;
 
-	DRM_DEBUG_DRIVER("%sabling layer %d in channel %d\n",
-			 enable ? "En" : "Dis", layer, chan);
+	DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
+			 enable ? "En" : "Dis", channel, overlay);
 
 	if (enable)
 		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
@@ -53,17 +51,17 @@ void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
 		val = 0;
 
 	regmap_update_bits(mixer->engine.regs,
-			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
 
 	if (enable)
-		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(chan);
+		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
 	else
 		val = 0;
 
 	regmap_update_bits(mixer->engine.regs,
 			   SUN8I_MIXER_BLEND_PIPE_CTL,
-			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(chan), val);
+			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
 }
 
 static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
@@ -89,15 +87,13 @@ static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
 	return 0;
 }
 
-int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
-				     int layer, struct drm_plane *plane)
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
+				   int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_framebuffer *fb = state->fb;
-	/* Currently the first UI channel is used */
-	int chan = mixer->cfg->vi_num;
 
-	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+	DRM_DEBUG_DRIVER("Updating channel %d overlay %d\n", channel, overlay);
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
 		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
@@ -107,7 +103,7 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
 					      state->crtc_h));
 		DRM_DEBUG_DRIVER("Updating blender size\n");
 		regmap_write(mixer->engine.regs,
-			     SUN8I_MIXER_BLEND_ATTR_INSIZE(chan),
+			     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
 			     SUN8I_MIXER_SIZE(state->crtc_w,
 					      state->crtc_h));
 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
@@ -115,7 +111,7 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
 					      state->crtc_h));
 		DRM_DEBUG_DRIVER("Updating channel size\n");
 		regmap_write(mixer->engine.regs,
-			     SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
+			     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
 			     SUN8I_MIXER_SIZE(state->crtc_w,
 					      state->crtc_h));
 	}
@@ -123,35 +119,33 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
 	/* Set the line width */
 	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
+		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay),
 		     fb->pitches[0]);
 
 	/* Set height and width */
 	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
 			 state->crtc_w, state->crtc_h);
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
+		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
 		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
 
 	/* Set base coordinates */
 	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
 			 state->crtc_x, state->crtc_y);
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
+		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
 		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
 
 	return 0;
 }
 
-int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
-				       int layer, struct drm_plane *plane)
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, int channel,
+				     int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_framebuffer *fb = state->fb;
 	bool interlaced = false;
 	u32 val;
-	/* Currently the first UI channel is used */
-	int chan = mixer->cfg->vi_num;
 	int ret;
 
 	if (plane->state->crtc)
@@ -175,21 +169,19 @@ int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
 
 	val <<= SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
 	regmap_update_bits(mixer->engine.regs,
-			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
 
 	return 0;
 }
 
-int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
-				      int layer, struct drm_plane *plane)
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, int channel,
+				    int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_framebuffer *fb = state->fb;
 	struct drm_gem_cma_object *gem;
 	dma_addr_t paddr;
-	/* Currently the first UI channel is used */
-	int chan = mixer->cfg->vi_num;
 	int bpp;
 
 	/* Get the physical address of the buffer in memory */
@@ -221,7 +213,7 @@ int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
 	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
 
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay),
 		     lower_32_bits(paddr));
 
 	return 0;
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 76f0b2bd91e2..70113c9ea2d1 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -123,12 +123,12 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine)
 	return container_of(engine, struct sun8i_mixer, engine);
 }
 
-void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
-				int layer, bool enable);
-int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
-				     int layer, struct drm_plane *plane);
-int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
-				       int layer, struct drm_plane *plane);
-int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
-				      int layer, struct drm_plane *plane);
+void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, int channel,
+			      int overlay, bool enable);
+int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
+				   int overlay, struct drm_plane *plane);
+int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, int channel,
+				     int overlay, struct drm_plane *plane);
+int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, int channel,
+				    int overlay, struct drm_plane *plane);
 #endif /* _SUN8I_MIXER_H_ */
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 11/27] drm/sun4i: Add constraints checking to DE2 driver
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Since current DE2 driver doesn't know how to scale yet, add atomic check
function which checks that.

Nice side effect of that function is that populates clipped coordinates
and checks visibility of the plane. That data will be used in the
future.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_layer.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
index 5aa6e2c9728e..40e6b83aef3b 100644
--- a/drivers/gpu/drm/sun4i/sun8i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -13,7 +13,9 @@
  * the License, or (at your option) any later version.
  */
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 
@@ -26,6 +28,31 @@ struct sun8i_plane_desc {
 	       uint32_t                nformats;
 };
 
+static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
+					  struct drm_plane_state *state)
+{
+	struct drm_crtc *crtc = state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct drm_rect clip;
+
+	if (!crtc)
+		return 0;
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
+	if (WARN_ON(!crtc_state))
+		return -EINVAL;
+
+	clip.x1 = 0;
+	clip.y1 = 0;
+	clip.x2 = crtc_state->adjusted_mode.hdisplay;
+	clip.y2 = crtc_state->adjusted_mode.vdisplay;
+
+	return drm_plane_helper_check_state(state, &clip,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    true, true);
+}
+
 static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
 					       struct drm_plane_state *old_state)
 {
@@ -53,6 +80,7 @@ static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
 }
 
 static struct drm_plane_helper_funcs sun8i_mixer_layer_helper_funcs = {
+	.atomic_check	= sun8i_mixer_layer_atomic_check,
 	.atomic_disable	= sun8i_mixer_layer_atomic_disable,
 	.atomic_update	= sun8i_mixer_layer_atomic_update,
 };
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 12/27] drm/sun4i: Use values calculated by atomic check
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Now that we have properly clipped coordinates in plane state structure,
use them.

This also fixes bug where source x and y were adjusted for negative
value, but width and height weren't. It wasn't discovered because
primary plane usually doesn't have negative coordinates.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_layer.c |  6 +++++
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 52 ++++++++++++++++---------------------
 2 files changed, 28 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
index 40e6b83aef3b..1eced63f365d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -69,6 +69,12 @@ static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
 	struct sun8i_mixer *mixer = layer->mixer;
 
+	if (!plane->state->visible) {
+		sun8i_mixer_layer_enable(mixer, layer->channel,
+					 layer->overlay, false);
+		return;
+	}
+
 	sun8i_mixer_update_layer_coord(mixer, layer->channel,
 				       layer->overlay, plane);
 	sun8i_mixer_update_layer_formats(mixer, layer->channel,
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 2bf7ba1e5ba7..37fcc5ed18c5 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -92,28 +92,34 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_framebuffer *fb = state->fb;
+	u32 width, height, size;
 
 	DRM_DEBUG_DRIVER("Updating channel %d overlay %d\n", channel, overlay);
 
+	/*
+	 * Same source and destination width and height are guaranteed
+	 * by atomic check function.
+	 */
+	width = drm_rect_width(&state->dst);
+	height = drm_rect_height(&state->dst);
+	size = SUN8I_MIXER_SIZE(width, height);
+
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
 		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
-				 state->crtc_w, state->crtc_h);
-		regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
-			     SUN8I_MIXER_SIZE(state->crtc_w,
-					      state->crtc_h));
+				 width, height);
+		regmap_write(mixer->engine.regs,
+			     SUN8I_MIXER_GLOBAL_SIZE,
+			     size);
 		DRM_DEBUG_DRIVER("Updating blender size\n");
 		regmap_write(mixer->engine.regs,
 			     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
-			     SUN8I_MIXER_SIZE(state->crtc_w,
-					      state->crtc_h));
+			     size);
 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
-			     SUN8I_MIXER_SIZE(state->crtc_w,
-					      state->crtc_h));
+			     size);
 		DRM_DEBUG_DRIVER("Updating channel size\n");
 		regmap_write(mixer->engine.regs,
 			     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
-			     SUN8I_MIXER_SIZE(state->crtc_w,
-					      state->crtc_h));
+			     size);
 	}
 
 	/* Set the line width */
@@ -123,18 +129,17 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 		     fb->pitches[0]);
 
 	/* Set height and width */
-	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
-			 state->crtc_w, state->crtc_h);
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
-		     SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
+		     size);
 
 	/* Set base coordinates */
 	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
-			 state->crtc_x, state->crtc_y);
+			 state->dst.x1, state->dst.y1);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
-		     SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
+		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 
 	return 0;
 }
@@ -194,21 +199,8 @@ int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, int channel,
 	paddr = gem->paddr + fb->offsets[0];
 
 	/* Fixup framebuffer address for src coordinates */
-	paddr += (state->src_x >> 16) * bpp;
-	paddr += (state->src_y >> 16) * fb->pitches[0];
-
-	/*
-	 * The hardware cannot correctly deal with negative crtc
-	 * coordinates, the display is cropped to the requested size,
-	 * but the display content is not moved.
-	 * Manually move the display content by fixup the framebuffer
-	 * address when crtc_x or crtc_y is negative, like what we
-	 * have did for src_x and src_y.
-	 */
-	if (state->crtc_x < 0)
-		paddr += -state->crtc_x * bpp;
-	if (state->crtc_y < 0)
-		paddr += -state->crtc_y * fb->pitches[0];
+	paddr += (state->src.x1 >> 16) * bpp;
+	paddr += (state->src.y1 >> 16) * fb->pitches[0];
 
 	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
 
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 13/27] drm/sun4i: Move line width setting in DE2
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Line width is a property of a framebuffer and it belongs to
sun8i_mixer_update_layer_buffer(). This will became even more obvious
when support for multi-plane formats will be added.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 37fcc5ed18c5..9635c30651d6 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -91,7 +91,6 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 				   int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
-	struct drm_framebuffer *fb = state->fb;
 	u32 width, height, size;
 
 	DRM_DEBUG_DRIVER("Updating channel %d overlay %d\n", channel, overlay);
@@ -122,12 +121,6 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 			     size);
 	}
 
-	/* Set the line width */
-	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay),
-		     fb->pitches[0]);
-
 	/* Set height and width */
 	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
 	regmap_write(mixer->engine.regs,
@@ -202,6 +195,12 @@ int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, int channel,
 	paddr += (state->src.x1 >> 16) * bpp;
 	paddr += (state->src.y1 >> 16) * fb->pitches[0];
 
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay),
+		     fb->pitches[0]);
+
 	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
 
 	regmap_write(mixer->engine.regs,
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 14/27] drm/sun4i: Move channel size related code in DE2
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Channel size should be set every time plane is changed, not only when
primary plane changes. Current code works only because only one
(primary) plane is supported at the moment.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 9635c30651d6..00c0cc6440e2 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -109,16 +109,8 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 		regmap_write(mixer->engine.regs,
 			     SUN8I_MIXER_GLOBAL_SIZE,
 			     size);
-		DRM_DEBUG_DRIVER("Updating blender size\n");
-		regmap_write(mixer->engine.regs,
-			     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
-			     size);
 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
 			     size);
-		DRM_DEBUG_DRIVER("Updating channel size\n");
-		regmap_write(mixer->engine.regs,
-			     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
-			     size);
 	}
 
 	/* Set height and width */
@@ -126,6 +118,9 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
 		     size);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
+		     size);
 
 	/* Set base coordinates */
 	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
@@ -133,6 +128,9 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
+		     size);
 
 	return 0;
 }
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 15/27] drm/sun4i: Move interlace related code in DE2
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

There is no point having code which sets interlace mode of mixer in
channel related function. Interlace mode will only change when CRTC
state will change, so let's move it to the block which is executed only
when primary plane state is changed.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 00c0cc6440e2..f35a08dc260b 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -104,6 +104,9 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 	size = SUN8I_MIXER_SIZE(width, height);
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		bool interlaced = false;
+		u32 val;
+
 		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
 				 width, height);
 		regmap_write(mixer->engine.regs,
@@ -111,6 +114,23 @@ int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 			     size);
 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
 			     size);
+
+		if (state->crtc)
+			interlaced = state->crtc->state->adjusted_mode.flags
+				& DRM_MODE_FLAG_INTERLACE;
+
+		if (interlaced)
+			val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
+		else
+			val = 0;
+
+		regmap_update_bits(mixer->engine.regs,
+				   SUN8I_MIXER_BLEND_OUTCTL,
+				   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+				   val);
+
+		DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+				 interlaced ? "on" : "off");
 	}
 
 	/* Set height and width */
@@ -140,22 +160,9 @@ int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, int channel,
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_framebuffer *fb = state->fb;
-	bool interlaced = false;
 	u32 val;
 	int ret;
 
-	if (plane->state->crtc)
-		interlaced = plane->state->crtc->state->adjusted_mode.flags
-			& DRM_MODE_FLAG_INTERLACE;
-
-	regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTCTL,
-			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
-			   interlaced ?
-			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
-
-	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
-			 interlaced ? "on" : "off");
-
 	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
 						&val);
 	if (ret) {
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 16/27] drm/sun4i: Add multi plane support to DE2 driver
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Support for multiple UI planes can now be easily enabled just by adding
more planes with different index.

For now, add immutable zpos property.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_layer.c | 38 +++++++++++++++++--------------------
 1 file changed, 17 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
index 1eced63f365d..65d6eb7d5cbf 100644
--- a/drivers/gpu/drm/sun4i/sun8i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -22,12 +22,6 @@
 #include "sun8i_layer.h"
 #include "sun8i_mixer.h"
 
-struct sun8i_plane_desc {
-	       enum drm_plane_type     type;
-	       const uint32_t          *formats;
-	       uint32_t                nformats;
-};
-
 static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
 					  struct drm_plane_state *state)
 {
@@ -106,35 +100,38 @@ static const uint32_t sun8i_mixer_layer_formats[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
-static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
-	{
-		.type = DRM_PLANE_TYPE_PRIMARY,
-		.formats = sun8i_mixer_layer_formats,
-		.nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
-	},
-};
-
 static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm,
 						struct sun8i_mixer *mixer,
-						const struct sun8i_plane_desc *plane)
+						int index)
 {
 	struct sun8i_layer *layer;
+	enum drm_plane_type type;
 	int ret;
 
 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
 	if (!layer)
 		return ERR_PTR(-ENOMEM);
 
+	type = index == 0 ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+
 	/* possible crtcs are set later */
 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
 				       &sun8i_mixer_layer_funcs,
-				       plane->formats, plane->nformats,
-				       NULL, plane->type, NULL);
+				       sun8i_mixer_layer_formats,
+				       ARRAY_SIZE(sun8i_mixer_layer_formats),
+				       NULL, type, NULL);
 	if (ret) {
 		dev_err(drm->dev, "Couldn't initialize layer\n");
 		return ERR_PTR(ret);
 	}
 
+	/* fixed zpos for now */
+	ret = drm_plane_create_zpos_immutable_property(&layer->plane, index);
+	if (ret) {
+		dev_err(drm->dev, "Couldn't add zpos property\n");
+		return ERR_PTR(ret);
+	}
+
 	drm_plane_helper_add(&layer->plane,
 			     &sun8i_mixer_layer_helper_funcs);
 	layer->mixer = mixer;
@@ -149,16 +146,15 @@ struct drm_plane **sun8i_layers_init(struct drm_device *drm,
 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
 	int i;
 
-	planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
+	planes = devm_kcalloc(drm->dev, mixer->cfg->ui_num + 1,
 			      sizeof(*planes), GFP_KERNEL);
 	if (!planes)
 		return ERR_PTR(-ENOMEM);
 
-	for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
-		const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
+	for (i = 0; i < mixer->cfg->ui_num; i++) {
 		struct sun8i_layer *layer;
 
-		layer = sun8i_layer_init_one(drm, mixer, plane);
+		layer = sun8i_layer_init_one(drm, mixer, i);
 		if (IS_ERR(layer)) {
 			dev_err(drm->dev, "Couldn't initialize %s plane\n",
 				i ? "overlay" : "primary");
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 17/27] drm/sun4i: Add support for all HW supported DE2 RGB formats
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Currently only a few RGB formats are supported by the DE2 driver. Add
support for all formats supported by the HW.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_layer.c |  19 ++++-
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 134 ++++++++++++++++++++++++++++--------
 drivers/gpu/drm/sun4i/sun8i_mixer.h |  17 +++++
 3 files changed, 139 insertions(+), 31 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
index 65d6eb7d5cbf..cdeda083c60e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -95,8 +95,25 @@ static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
 };
 
 static const uint32_t sun8i_mixer_layer_formats[] = {
-	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ARGB4444,
 	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_XBGR8888,
 	DRM_FORMAT_XRGB8888,
 };
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index f35a08dc260b..d49eed97b452 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -29,6 +29,105 @@
 #include "sun8i_layer.h"
 #include "sunxi_engine.h"
 
+struct de2_fmt_info {
+	u32 drm_fmt;
+	u32 de2_fmt;
+};
+
+static const struct de2_fmt_info de2_formats[] = {
+	{
+		.drm_fmt = DRM_FORMAT_ARGB8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_ABGR8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_RGBA8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_BGRA8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_XRGB8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_XBGR8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_RGBX8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_BGRX8888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_RGB888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_BGR888,
+		.de2_fmt = SUN8I_MIXER_FBFMT_BGR888,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_RGB565,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGB565,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_BGR565,
+		.de2_fmt = SUN8I_MIXER_FBFMT_BGR565,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_ARGB4444,
+		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_ABGR4444,
+		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_RGBA4444,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_BGRA4444,
+		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_ARGB1555,
+		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_ABGR1555,
+		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_RGBA5551,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_BGRA5551,
+		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
+	},
+};
+
+static const struct de2_fmt_info *sun8i_mixer_format_info(u32 format)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(de2_formats); ++i)
+		if (de2_formats[i].drm_fmt == format)
+			return &de2_formats[i];
+
+	return NULL;
+}
+
 static void sun8i_mixer_commit(struct sunxi_engine *engine)
 {
 	DRM_DEBUG_DRIVER("Committing changes\n");
@@ -64,29 +163,6 @@ void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, int channel,
 			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
 }
 
-static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
-					     u32 format, u32 *mode)
-{
-	switch (format) {
-	case DRM_FORMAT_ARGB8888:
-		*mode = SUN8I_MIXER_FBFMT_ARGB8888;
-		break;
-
-	case DRM_FORMAT_XRGB8888:
-		*mode = SUN8I_MIXER_FBFMT_XRGB8888;
-		break;
-
-	case DRM_FORMAT_RGB888:
-		*mode = SUN8I_MIXER_FBFMT_RGB888;
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
 int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
 				   int overlay, struct drm_plane *plane)
 {
@@ -159,18 +235,16 @@ int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, int channel,
 				     int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
-	struct drm_framebuffer *fb = state->fb;
+	const struct de2_fmt_info *fmt_info;
 	u32 val;
-	int ret;
 
-	ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
-						&val);
-	if (ret) {
+	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
+	if (!fmt_info) {
 		DRM_DEBUG_DRIVER("Invalid format\n");
-		return ret;
+		return -EINVAL;
 	}
 
-	val <<= SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
+	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
 	regmap_update_bits(mixer->engine.regs,
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 70113c9ea2d1..e23525b9cf6d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -82,8 +82,25 @@
 #define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
 
 #define SUN8I_MIXER_FBFMT_ARGB8888	0
+#define SUN8I_MIXER_FBFMT_ABGR8888	1
+#define SUN8I_MIXER_FBFMT_RGBA8888	2
+#define SUN8I_MIXER_FBFMT_BGRA8888	3
 #define SUN8I_MIXER_FBFMT_XRGB8888	4
+#define SUN8I_MIXER_FBFMT_XBGR8888	5
+#define SUN8I_MIXER_FBFMT_RGBX8888	6
+#define SUN8I_MIXER_FBFMT_BGRX8888	7
 #define SUN8I_MIXER_FBFMT_RGB888	8
+#define SUN8I_MIXER_FBFMT_BGR888	9
+#define SUN8I_MIXER_FBFMT_RGB565	10
+#define SUN8I_MIXER_FBFMT_BGR565	11
+#define SUN8I_MIXER_FBFMT_ARGB4444	12
+#define SUN8I_MIXER_FBFMT_ABGR4444	13
+#define SUN8I_MIXER_FBFMT_RGBA4444	14
+#define SUN8I_MIXER_FBFMT_BGRA4444	15
+#define SUN8I_MIXER_FBFMT_ARGB1555	16
+#define SUN8I_MIXER_FBFMT_ABGR1555	17
+#define SUN8I_MIXER_FBFMT_RGBA5551	18
+#define SUN8I_MIXER_FBFMT_BGRA5551	19
 
 /*
  * These sub-engines are still unknown now, the EN registers are here only to
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 18/27] drm/sun4i: Reorganize UI layer code in DE2
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Till now, DE2 driver supported only UI planes. Before we add support for
VI planes, lets split out UI layer specific code from common parts. This
commit does the following:
- renames sun8i_layer.c to sun8i_ui_layer.c
- moves UI channel specific code to sun8i_ui_layer.c
- moves common code from sun8i_layer.c to sun8i_mixer.c
- renames function and structure names so it is apparent where they
belong to

No functional change is made.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/Makefile         |   2 +-
 drivers/gpu/drm/sun4i/sun8i_layer.c    | 187 --------------------
 drivers/gpu/drm/sun4i/sun8i_layer.h    |  37 ----
 drivers/gpu/drm/sun4i/sun8i_mixer.c    | 174 +++---------------
 drivers/gpu/drm/sun4i/sun8i_mixer.h    |  43 +----
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 314 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_ui_layer.h |  63 +++++++
 7 files changed, 406 insertions(+), 414 deletions(-)
 delete mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.c
 delete mode 100644 drivers/gpu/drm/sun4i/sun8i_layer.h
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_ui_layer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_ui_layer.h

diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 0c2f8c7facae..0a839674cd1d 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,7 +9,7 @@ sun4i-drm-hdmi-y		+= sun4i_hdmi_enc.o
 sun4i-drm-hdmi-y		+= sun4i_hdmi_i2c.o
 sun4i-drm-hdmi-y		+= sun4i_hdmi_tmds_clk.o
 
-sun8i-mixer-y			+= sun8i_mixer.o sun8i_layer.o
+sun8i-mixer-y			+= sun8i_mixer.o sun8i_ui_layer.o
 
 sun4i-tcon-y			+= sun4i_crtc.o
 sun4i-tcon-y			+= sun4i_dotclock.o
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
deleted file mode 100644
index cdeda083c60e..000000000000
--- a/drivers/gpu/drm/sun4i/sun8i_layer.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
- *
- * Based on sun4i_layer.h, which is:
- *   Copyright (C) 2015 Free Electrons
- *   Copyright (C) 2015 NextThing Co
- *
- *   Maxime Ripard <maxime.ripard@free-electrons.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_plane_helper.h>
-#include <drm/drmP.h>
-
-#include "sun8i_layer.h"
-#include "sun8i_mixer.h"
-
-static int sun8i_mixer_layer_atomic_check(struct drm_plane *plane,
-					  struct drm_plane_state *state)
-{
-	struct drm_crtc *crtc = state->crtc;
-	struct drm_crtc_state *crtc_state;
-	struct drm_rect clip;
-
-	if (!crtc)
-		return 0;
-
-	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
-	if (WARN_ON(!crtc_state))
-		return -EINVAL;
-
-	clip.x1 = 0;
-	clip.y1 = 0;
-	clip.x2 = crtc_state->adjusted_mode.hdisplay;
-	clip.y2 = crtc_state->adjusted_mode.vdisplay;
-
-	return drm_plane_helper_check_state(state, &clip,
-					    DRM_PLANE_HELPER_NO_SCALING,
-					    DRM_PLANE_HELPER_NO_SCALING,
-					    true, true);
-}
-
-static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
-					       struct drm_plane_state *old_state)
-{
-	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
-	struct sun8i_mixer *mixer = layer->mixer;
-
-	sun8i_mixer_layer_enable(mixer, layer->channel,
-				 layer->overlay, false);
-}
-
-static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
-					      struct drm_plane_state *old_state)
-{
-	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
-	struct sun8i_mixer *mixer = layer->mixer;
-
-	if (!plane->state->visible) {
-		sun8i_mixer_layer_enable(mixer, layer->channel,
-					 layer->overlay, false);
-		return;
-	}
-
-	sun8i_mixer_update_layer_coord(mixer, layer->channel,
-				       layer->overlay, plane);
-	sun8i_mixer_update_layer_formats(mixer, layer->channel,
-					 layer->overlay, plane);
-	sun8i_mixer_update_layer_buffer(mixer, layer->channel,
-					layer->overlay, plane);
-	sun8i_mixer_layer_enable(mixer, layer->channel,
-				 layer->overlay, true);
-}
-
-static struct drm_plane_helper_funcs sun8i_mixer_layer_helper_funcs = {
-	.atomic_check	= sun8i_mixer_layer_atomic_check,
-	.atomic_disable	= sun8i_mixer_layer_atomic_disable,
-	.atomic_update	= sun8i_mixer_layer_atomic_update,
-};
-
-static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
-	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
-	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
-	.destroy		= drm_plane_cleanup,
-	.disable_plane		= drm_atomic_helper_disable_plane,
-	.reset			= drm_atomic_helper_plane_reset,
-	.update_plane		= drm_atomic_helper_update_plane,
-};
-
-static const uint32_t sun8i_mixer_layer_formats[] = {
-	DRM_FORMAT_ABGR1555,
-	DRM_FORMAT_ABGR4444,
-	DRM_FORMAT_ABGR8888,
-	DRM_FORMAT_ARGB1555,
-	DRM_FORMAT_ARGB4444,
-	DRM_FORMAT_ARGB8888,
-	DRM_FORMAT_BGR565,
-	DRM_FORMAT_BGR888,
-	DRM_FORMAT_BGRA5551,
-	DRM_FORMAT_BGRA4444,
-	DRM_FORMAT_BGRA8888,
-	DRM_FORMAT_BGRX8888,
-	DRM_FORMAT_RGB565,
-	DRM_FORMAT_RGB888,
-	DRM_FORMAT_RGBA4444,
-	DRM_FORMAT_RGBA5551,
-	DRM_FORMAT_RGBA8888,
-	DRM_FORMAT_RGBX8888,
-	DRM_FORMAT_XBGR8888,
-	DRM_FORMAT_XRGB8888,
-};
-
-static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm,
-						struct sun8i_mixer *mixer,
-						int index)
-{
-	struct sun8i_layer *layer;
-	enum drm_plane_type type;
-	int ret;
-
-	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
-	if (!layer)
-		return ERR_PTR(-ENOMEM);
-
-	type = index == 0 ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
-
-	/* possible crtcs are set later */
-	ret = drm_universal_plane_init(drm, &layer->plane, 0,
-				       &sun8i_mixer_layer_funcs,
-				       sun8i_mixer_layer_formats,
-				       ARRAY_SIZE(sun8i_mixer_layer_formats),
-				       NULL, type, NULL);
-	if (ret) {
-		dev_err(drm->dev, "Couldn't initialize layer\n");
-		return ERR_PTR(ret);
-	}
-
-	/* fixed zpos for now */
-	ret = drm_plane_create_zpos_immutable_property(&layer->plane, index);
-	if (ret) {
-		dev_err(drm->dev, "Couldn't add zpos property\n");
-		return ERR_PTR(ret);
-	}
-
-	drm_plane_helper_add(&layer->plane,
-			     &sun8i_mixer_layer_helper_funcs);
-	layer->mixer = mixer;
-
-	return layer;
-}
-
-struct drm_plane **sun8i_layers_init(struct drm_device *drm,
-				     struct sunxi_engine *engine)
-{
-	struct drm_plane **planes;
-	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
-	int i;
-
-	planes = devm_kcalloc(drm->dev, mixer->cfg->ui_num + 1,
-			      sizeof(*planes), GFP_KERNEL);
-	if (!planes)
-		return ERR_PTR(-ENOMEM);
-
-	for (i = 0; i < mixer->cfg->ui_num; i++) {
-		struct sun8i_layer *layer;
-
-		layer = sun8i_layer_init_one(drm, mixer, i);
-		if (IS_ERR(layer)) {
-			dev_err(drm->dev, "Couldn't initialize %s plane\n",
-				i ? "overlay" : "primary");
-			return ERR_CAST(layer);
-		};
-
-		layer->channel = mixer->cfg->vi_num + i;
-		layer->overlay = 0;
-		planes[i] = &layer->plane;
-	};
-
-	return planes;
-}
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h
deleted file mode 100644
index d246e0dd3d48..000000000000
--- a/drivers/gpu/drm/sun4i/sun8i_layer.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
- *
- * Based on sun4i_layer.h, which is:
- *   Copyright (C) 2015 Free Electrons
- *   Copyright (C) 2015 NextThing Co
- *
- *   Maxime Ripard <maxime.ripard@free-electrons.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-#ifndef _SUN8I_LAYER_H_
-#define _SUN8I_LAYER_H_
-
-struct sunxi_engine;
-
-struct sun8i_layer {
-	struct drm_plane	plane;
-	struct sun4i_drv	*drv;
-	struct sun8i_mixer	*mixer;
-	int			channel;
-	int			overlay;
-};
-
-static inline struct sun8i_layer *
-plane_to_sun8i_layer(struct drm_plane *plane)
-{
-	return container_of(plane, struct sun8i_layer, plane);
-}
-
-struct drm_plane **sun8i_layers_init(struct drm_device *drm,
-				     struct sunxi_engine *engine);
-#endif /* _SUN8I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index d49eed97b452..1de98ad9f6c1 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -26,14 +26,9 @@
 
 #include "sun4i_drv.h"
 #include "sun8i_mixer.h"
-#include "sun8i_layer.h"
+#include "sun8i_ui_layer.h"
 #include "sunxi_engine.h"
 
-struct de2_fmt_info {
-	u32 drm_fmt;
-	u32 de2_fmt;
-};
-
 static const struct de2_fmt_info de2_formats[] = {
 	{
 		.drm_fmt = DRM_FORMAT_ARGB8888,
@@ -117,7 +112,7 @@ static const struct de2_fmt_info de2_formats[] = {
 	},
 };
 
-static const struct de2_fmt_info *sun8i_mixer_format_info(u32 format)
+const struct de2_fmt_info *sun8i_mixer_format_info(u32 format)
 {
 	unsigned int i;
 
@@ -136,157 +131,32 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine)
 		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
 }
 
-void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, int channel,
-			      int overlay, bool enable)
-{
-	u32 val;
-
-	DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
-			 enable ? "En" : "Dis", channel, overlay);
-
-	if (enable)
-		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
-	else
-		val = 0;
-
-	regmap_update_bits(mixer->engine.regs,
-			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
-			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
-
-	if (enable)
-		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
-	else
-		val = 0;
-
-	regmap_update_bits(mixer->engine.regs,
-			   SUN8I_MIXER_BLEND_PIPE_CTL,
-			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
-}
-
-int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
-				   int overlay, struct drm_plane *plane)
-{
-	struct drm_plane_state *state = plane->state;
-	u32 width, height, size;
-
-	DRM_DEBUG_DRIVER("Updating channel %d overlay %d\n", channel, overlay);
-
-	/*
-	 * Same source and destination width and height are guaranteed
-	 * by atomic check function.
-	 */
-	width = drm_rect_width(&state->dst);
-	height = drm_rect_height(&state->dst);
-	size = SUN8I_MIXER_SIZE(width, height);
-
-	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
-		bool interlaced = false;
-		u32 val;
-
-		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
-				 width, height);
-		regmap_write(mixer->engine.regs,
-			     SUN8I_MIXER_GLOBAL_SIZE,
-			     size);
-		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
-			     size);
-
-		if (state->crtc)
-			interlaced = state->crtc->state->adjusted_mode.flags
-				& DRM_MODE_FLAG_INTERLACE;
-
-		if (interlaced)
-			val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
-		else
-			val = 0;
-
-		regmap_update_bits(mixer->engine.regs,
-				   SUN8I_MIXER_BLEND_OUTCTL,
-				   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
-				   val);
-
-		DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
-				 interlaced ? "on" : "off");
-	}
-
-	/* Set height and width */
-	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
-		     size);
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
-		     size);
-
-	/* Set base coordinates */
-	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
-			 state->dst.x1, state->dst.y1);
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
-		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
-		     size);
-
-	return 0;
-}
-
-int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, int channel,
-				     int overlay, struct drm_plane *plane)
+static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
+					    struct sunxi_engine *engine)
 {
-	struct drm_plane_state *state = plane->state;
-	const struct de2_fmt_info *fmt_info;
-	u32 val;
+	struct drm_plane **planes;
+	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+	int i;
 
-	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
-	if (!fmt_info) {
-		DRM_DEBUG_DRIVER("Invalid format\n");
-		return -EINVAL;
-	}
+	planes = devm_kcalloc(drm->dev, mixer->cfg->ui_num + 1,
+			      sizeof(*planes), GFP_KERNEL);
+	if (!planes)
+		return ERR_PTR(-ENOMEM);
 
-	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
-	regmap_update_bits(mixer->engine.regs,
-			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
-			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+	for (i = 0; i < mixer->cfg->ui_num; i++) {
+		struct sun8i_ui_layer *layer;
 
-	return 0;
-}
+		layer = sun8i_ui_layer_init_one(drm, mixer, i);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev, "Couldn't initialize %s plane\n",
+				i ? "overlay" : "primary");
+			return ERR_CAST(layer);
+		};
 
-int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, int channel,
-				    int overlay, struct drm_plane *plane)
-{
-	struct drm_plane_state *state = plane->state;
-	struct drm_framebuffer *fb = state->fb;
-	struct drm_gem_cma_object *gem;
-	dma_addr_t paddr;
-	int bpp;
+		planes[i] = &layer->plane;
+	};
 
-	/* Get the physical address of the buffer in memory */
-	gem = drm_fb_cma_get_gem_obj(fb, 0);
-
-	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
-
-	/* Compute the start of the displayed memory */
-	bpp = fb->format->cpp[0];
-	paddr = gem->paddr + fb->offsets[0];
-
-	/* Fixup framebuffer address for src coordinates */
-	paddr += (state->src.x1 >> 16) * bpp;
-	paddr += (state->src.y1 >> 16) * fb->pitches[0];
-
-	/* Set the line width */
-	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay),
-		     fb->pitches[0]);
-
-	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
-
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay),
-		     lower_32_bits(paddr));
-
-	return 0;
+	return planes;
 }
 
 static const struct sunxi_engine_ops sun8i_engine_ops = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index e23525b9cf6d..bad0969ac105 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -52,35 +52,6 @@
 
 #define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED	BIT(1)
 
-/*
- * VI channels are not used now, but the support of them may be introduced in
- * the future.
- */
-
-#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
-#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
-#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
-#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
-#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
-#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
-#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
-			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
-#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
-#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
-#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
-
-#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
-#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
-#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(12, 8)
-#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET	8
-#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
-
 #define SUN8I_MIXER_FBFMT_ARGB8888	0
 #define SUN8I_MIXER_FBFMT_ABGR8888	1
 #define SUN8I_MIXER_FBFMT_RGBA8888	2
@@ -118,6 +89,11 @@
 #define SUN8I_MIXER_FCC_EN			0xaa000
 #define SUN8I_MIXER_DCSC_EN			0xb0000
 
+struct de2_fmt_info {
+	u32 drm_fmt;
+	u32 de2_fmt;
+};
+
 struct sun8i_mixer_cfg {
 	int		vi_num;
 	int		ui_num;
@@ -140,12 +116,5 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine)
 	return container_of(engine, struct sun8i_mixer, engine);
 }
 
-void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, int channel,
-			      int overlay, bool enable);
-int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, int channel,
-				   int overlay, struct drm_plane *plane);
-int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, int channel,
-				     int overlay, struct drm_plane *plane);
-int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, int channel,
-				    int overlay, struct drm_plane *plane);
+const struct de2_fmt_info *sun8i_mixer_format_info(u32 format);
 #endif /* _SUN8I_MIXER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
new file mode 100644
index 000000000000..3ecf39c50111
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
+ *
+ * Based on sun4i_layer.h, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ *   Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drmP.h>
+
+#include "sun8i_ui_layer.h"
+#include "sun8i_mixer.h"
+
+static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
+				  int overlay, bool enable)
+{
+	u32 val;
+
+	DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
+			 enable ? "En" : "Dis", channel, overlay);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
+
+	if (enable)
+		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_BLEND_PIPE_CTL,
+			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
+}
+
+static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
+				       int overlay, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	u32 width, height, size;
+
+	DRM_DEBUG_DRIVER("Updating channel %d overlay %d\n", channel, overlay);
+
+	/*
+	 * Same source and destination width and height are guaranteed
+	 * by atomic check function.
+	 */
+	width = drm_rect_width(&state->dst);
+	height = drm_rect_height(&state->dst);
+	size = SUN8I_MIXER_SIZE(width, height);
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+		bool interlaced = false;
+		u32 val;
+
+		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+				 width, height);
+		regmap_write(mixer->engine.regs,
+			     SUN8I_MIXER_GLOBAL_SIZE,
+			     size);
+		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
+			     size);
+
+		if (state->crtc)
+			interlaced = state->crtc->state->adjusted_mode.flags
+				& DRM_MODE_FLAG_INTERLACE;
+
+		if (interlaced)
+			val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
+		else
+			val = 0;
+
+		regmap_update_bits(mixer->engine.regs,
+				   SUN8I_MIXER_BLEND_OUTCTL,
+				   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
+				   val);
+
+		DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
+				 interlaced ? "on" : "off");
+	}
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
+		     size);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
+		     size);
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->dst.x1, state->dst.y1);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
+		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
+		     size);
+
+	return 0;
+}
+
+static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
+					 int overlay, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	const struct de2_fmt_info *fmt_info;
+	u32 val;
+
+	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
+	if (!fmt_info) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return -EINVAL;
+	}
+
+	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay),
+			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
+
+	return 0;
+}
+
+static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
+					int overlay, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+
+	/* Fixup framebuffer address for src coordinates */
+	paddr += (state->src.x1 >> 16) * bpp;
+	paddr += (state->src.y1 >> 16) * fb->pitches[0];
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay),
+		     fb->pitches[0]);
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay),
+		     lower_32_bits(paddr));
+
+	return 0;
+}
+
+static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
+				       struct drm_plane_state *state)
+{
+	struct drm_crtc *crtc = state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct drm_rect clip;
+
+	if (!crtc)
+		return 0;
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
+	if (WARN_ON(!crtc_state))
+		return -EINVAL;
+
+	clip.x1 = 0;
+	clip.y1 = 0;
+	clip.x2 = crtc_state->adjusted_mode.hdisplay;
+	clip.y2 = crtc_state->adjusted_mode.vdisplay;
+
+	return drm_plane_helper_check_state(state, &clip,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    true, true);
+}
+
+static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
+					  struct drm_plane_state *old_state)
+{
+	struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
+	struct sun8i_mixer *mixer = layer->mixer;
+
+	sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false);
+}
+
+static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
+					 struct drm_plane_state *old_state)
+{
+	struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
+	struct sun8i_mixer *mixer = layer->mixer;
+
+	if (!plane->state->visible) {
+		sun8i_ui_layer_enable(mixer, layer->channel,
+				      layer->overlay, false);
+		return;
+	}
+
+	sun8i_ui_layer_update_coord(mixer, layer->channel,
+				    layer->overlay, plane);
+	sun8i_ui_layer_update_formats(mixer, layer->channel,
+				      layer->overlay, plane);
+	sun8i_ui_layer_update_buffer(mixer, layer->channel,
+				     layer->overlay, plane);
+	sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, true);
+}
+
+static struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
+	.atomic_check	= sun8i_ui_layer_atomic_check,
+	.atomic_disable	= sun8i_ui_layer_atomic_disable,
+	.atomic_update	= sun8i_ui_layer_atomic_update,
+};
+
+static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
+	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
+	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
+	.destroy		= drm_plane_cleanup,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.reset			= drm_atomic_helper_plane_reset,
+	.update_plane		= drm_atomic_helper_update_plane,
+};
+
+static const u32 sun8i_ui_layer_formats[] = {
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_XRGB8888,
+};
+
+struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
+					       struct sun8i_mixer *mixer,
+					       int index)
+{
+	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
+	int channel = mixer->cfg->vi_num + index;
+	struct sun8i_ui_layer *layer;
+	int ret;
+
+	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
+	if (!layer)
+		return ERR_PTR(-ENOMEM);
+
+	if (index == 0)
+		type = DRM_PLANE_TYPE_PRIMARY;
+
+	/* possible crtcs are set later */
+	ret = drm_universal_plane_init(drm, &layer->plane, 0,
+				       &sun8i_ui_layer_funcs,
+				       sun8i_ui_layer_formats,
+				       ARRAY_SIZE(sun8i_ui_layer_formats),
+				       NULL, type, NULL);
+	if (ret) {
+		dev_err(drm->dev, "Couldn't initialize layer\n");
+		return ERR_PTR(ret);
+	}
+
+	/* fixed zpos for now */
+	ret = drm_plane_create_zpos_immutable_property(&layer->plane, channel);
+	if (ret) {
+		dev_err(drm->dev, "Couldn't add zpos property\n");
+		return ERR_PTR(ret);
+	}
+
+	drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
+	layer->mixer = mixer;
+	layer->channel = channel;
+	layer->overlay = 0;
+
+	return layer;
+}
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h
new file mode 100644
index 000000000000..123b15ea9918
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
+ *
+ * Based on sun4i_layer.h, which is:
+ *   Copyright (C) 2015 Free Electrons
+ *   Copyright (C) 2015 NextThing Co
+ *
+ *   Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_UI_LAYER_H_
+#define _SUN8I_UI_LAYER_H_
+
+#include <drm/drm_plane.h>
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
+#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
+#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
+#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
+			(0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
+#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x80)
+#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch)	(0x2000 + 0x1000 * (ch) + 0x84)
+#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0x88)
+
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN		BIT(0)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK	GENMASK(2, 1)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK	GENMASK(12, 8)
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET	8
+#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK	GENMASK(31, 24)
+
+struct sun8i_mixer;
+
+struct sun8i_ui_layer {
+	struct drm_plane	plane;
+	struct sun8i_mixer	*mixer;
+	int			channel;
+	int			overlay;
+};
+
+static inline struct sun8i_ui_layer *
+plane_to_sun8i_ui_layer(struct drm_plane *plane)
+{
+	return container_of(plane, struct sun8i_ui_layer, plane);
+}
+
+struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
+					       struct sun8i_mixer *mixer,
+					       int index);
+#endif /* _SUN8I_UI_LAYER_H_ */
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 19/27] drm/sun4i: Add support for DE2 VI planes
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

This commit adds basic support for VI planes. They are meant for video
overlay and because of that they support YUV formats too. However, using
YUV format is not straightforward, so only RGB formats are supported for
now.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/Makefile         |   3 +-
 drivers/gpu/drm/sun4i/sun8i_mixer.c    |  19 ++-
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 276 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_vi_layer.h |  51 ++++++
 4 files changed, 346 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_vi_layer.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_vi_layer.h

diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 0a839674cd1d..b0a2eb2c4806 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -9,7 +9,8 @@ sun4i-drm-hdmi-y		+= sun4i_hdmi_enc.o
 sun4i-drm-hdmi-y		+= sun4i_hdmi_i2c.o
 sun4i-drm-hdmi-y		+= sun4i_hdmi_tmds_clk.o
 
-sun8i-mixer-y			+= sun8i_mixer.o sun8i_ui_layer.o
+sun8i-mixer-y			+= sun8i_mixer.o sun8i_ui_layer.o \
+				   sun8i_vi_layer.o
 
 sun4i-tcon-y			+= sun4i_crtc.o
 sun4i-tcon-y			+= sun4i_dotclock.o
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 1de98ad9f6c1..888620b1d3f1 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -27,6 +27,7 @@
 #include "sun4i_drv.h"
 #include "sun8i_mixer.h"
 #include "sun8i_ui_layer.h"
+#include "sun8i_vi_layer.h"
 #include "sunxi_engine.h"
 
 static const struct de2_fmt_info de2_formats[] = {
@@ -138,11 +139,25 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
 	int i;
 
-	planes = devm_kcalloc(drm->dev, mixer->cfg->ui_num + 1,
+	planes = devm_kcalloc(drm->dev,
+			      mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
 			      sizeof(*planes), GFP_KERNEL);
 	if (!planes)
 		return ERR_PTR(-ENOMEM);
 
+	for (i = 0; i < mixer->cfg->vi_num; i++) {
+		struct sun8i_vi_layer *layer;
+
+		layer = sun8i_vi_layer_init_one(drm, mixer, i);
+		if (IS_ERR(layer)) {
+			dev_err(drm->dev,
+				"Couldn't initialize overlay plane\n");
+			return ERR_CAST(layer);
+		};
+
+		planes[i] = &layer->plane;
+	};
+
 	for (i = 0; i < mixer->cfg->ui_num; i++) {
 		struct sun8i_ui_layer *layer;
 
@@ -153,7 +168,7 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
 			return ERR_CAST(layer);
 		};
 
-		planes[i] = &layer->plane;
+		planes[mixer->cfg->vi_num + i] = &layer->plane;
 	};
 
 	return planes;
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
new file mode 100644
index 000000000000..b78d4f3e2b48
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drmP.h>
+
+#include "sun8i_vi_layer.h"
+#include "sun8i_mixer.h"
+
+static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
+				  int overlay, bool enable)
+{
+	u32 val;
+
+	DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
+			 enable ? "En" : "Dis", channel, overlay);
+
+	if (enable)
+		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
+
+	if (enable)
+		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_BLEND_PIPE_CTL,
+			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
+}
+
+static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
+				       int overlay, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	u32 width, height, size;
+
+	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
+			 channel, overlay);
+	/*
+	 * Same source and destination width and height are guaranteed
+	 * by atomic check function.
+	 */
+	width = drm_rect_width(&state->dst);
+	height = drm_rect_height(&state->dst);
+	size = SUN8I_MIXER_SIZE(width, height);
+
+	/* Set height and width */
+	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay),
+		     size);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel),
+		     size);
+
+	/* Set base coordinates */
+	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+			 state->dst.x1, state->dst.y1);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
+		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
+		     size);
+
+	return 0;
+}
+
+static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
+					 int overlay, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	const struct de2_fmt_info *fmt_info;
+	u32 val;
+
+	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
+	if (!fmt_info) {
+		DRM_DEBUG_DRIVER("Invalid format\n");
+		return -EINVAL;
+	}
+
+	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK |
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE,
+			   val | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE);
+
+	return 0;
+}
+
+static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
+					int overlay, struct drm_plane *plane)
+{
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *gem;
+	dma_addr_t paddr;
+	int bpp;
+
+	/* Get the physical address of the buffer in memory */
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+
+	/* Compute the start of the displayed memory */
+	bpp = fb->format->cpp[0];
+	paddr = gem->paddr + fb->offsets[0];
+
+	/* Fixup framebuffer address for src coordinates */
+	paddr += (state->src.x1 >> 16) * bpp;
+	paddr += (state->src.y1 >> 16) * fb->pitches[0];
+
+	/* Set the line width */
+	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, overlay, 0),
+		     fb->pitches[0]);
+
+	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+
+	regmap_write(mixer->engine.regs,
+		     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, overlay, 0),
+		     lower_32_bits(paddr));
+
+	return 0;
+}
+
+static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
+				       struct drm_plane_state *state)
+{
+	struct drm_crtc *crtc = state->crtc;
+	struct drm_crtc_state *crtc_state;
+	struct drm_rect clip;
+
+	if (!crtc)
+		return 0;
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
+	if (WARN_ON(!crtc_state))
+		return -EINVAL;
+
+	clip.x1 = 0;
+	clip.y1 = 0;
+	clip.x2 = crtc_state->adjusted_mode.hdisplay;
+	clip.y2 = crtc_state->adjusted_mode.vdisplay;
+
+	return drm_plane_helper_check_state(state, &clip,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    true, true);
+}
+
+static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
+					  struct drm_plane_state *old_state)
+{
+	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
+	struct sun8i_mixer *mixer = layer->mixer;
+
+	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false);
+}
+
+static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
+					 struct drm_plane_state *old_state)
+{
+	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
+	struct sun8i_mixer *mixer = layer->mixer;
+
+	if (!plane->state->visible) {
+		sun8i_vi_layer_enable(mixer, layer->channel,
+				      layer->overlay, false);
+		return;
+	}
+
+	sun8i_vi_layer_update_coord(mixer, layer->channel,
+				    layer->overlay, plane);
+	sun8i_vi_layer_update_formats(mixer, layer->channel,
+				      layer->overlay, plane);
+	sun8i_vi_layer_update_buffer(mixer, layer->channel,
+				     layer->overlay, plane);
+	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, true);
+}
+
+static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
+	.atomic_check	= sun8i_vi_layer_atomic_check,
+	.atomic_disable	= sun8i_vi_layer_atomic_disable,
+	.atomic_update	= sun8i_vi_layer_atomic_update,
+};
+
+static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
+	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
+	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
+	.destroy		= drm_plane_cleanup,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.reset			= drm_atomic_helper_plane_reset,
+	.update_plane		= drm_atomic_helper_update_plane,
+};
+
+/*
+ * While all RGB formats are supported, VI planes don't support
+ * alpha blending, so there is no point having formats with alpha
+ * channel if their opaque analog exist.
+ */
+static const u32 sun8i_vi_layer_formats[] = {
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_XRGB8888,
+};
+
+struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
+					       struct sun8i_mixer *mixer,
+					       int index)
+{
+	struct sun8i_vi_layer *layer;
+	int ret;
+
+	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
+	if (!layer)
+		return ERR_PTR(-ENOMEM);
+
+	/* possible crtcs are set later */
+	ret = drm_universal_plane_init(drm, &layer->plane, 0,
+				       &sun8i_vi_layer_funcs,
+				       sun8i_vi_layer_formats,
+				       ARRAY_SIZE(sun8i_vi_layer_formats),
+				       NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
+	if (ret) {
+		dev_err(drm->dev, "Couldn't initialize layer\n");
+		return ERR_PTR(ret);
+	}
+
+	/* fixed zpos for now */
+	ret = drm_plane_create_zpos_immutable_property(&layer->plane, index);
+	if (ret) {
+		dev_err(drm->dev, "Couldn't add zpos property\n");
+		return ERR_PTR(ret);
+	}
+
+	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
+	layer->mixer = mixer;
+	layer->channel = index;
+	layer->overlay = 0;
+
+	return layer;
+}
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h
new file mode 100644
index 000000000000..6996627a0a76
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_VI_LAYER_H_
+#define _SUN8I_VI_LAYER_H_
+
+#include <drm/drm_plane.h>
+
+#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch, layer) \
+		(0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x0)
+#define SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch, layer) \
+		(0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x4)
+#define SUN8I_MIXER_CHAN_VI_LAYER_COORD(ch, layer) \
+		(0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x8)
+#define SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch, layer, plane) \
+		(0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0xc + 4 * (plane))
+#define SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch, layer, plane) \
+		(0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x18 + 4 * (plane))
+#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch)	(0x2000 + 0x1000 * (ch) + 0xe8)
+
+#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN		BIT(0)
+/* RGB mode should be set for RGB formats and cleared for YCbCr */
+#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE		BIT(15)
+#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET	8
+#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK	GENMASK(12, 8)
+
+struct sun8i_mixer;
+
+struct sun8i_vi_layer {
+	struct drm_plane	plane;
+	struct sun8i_mixer	*mixer;
+	int			channel;
+	int			overlay;
+};
+
+static inline struct sun8i_vi_layer *
+plane_to_sun8i_vi_layer(struct drm_plane *plane)
+{
+	return container_of(plane, struct sun8i_vi_layer, plane);
+}
+
+struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
+					       struct sun8i_mixer *mixer,
+					       int index);
+#endif /* _SUN8I_VI_LAYER_H_ */
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 20/27] drm/sun4i: Add scaler configuration to DE2 mixers
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

No all SoCs support scaling on all channels. For example, V3s support
scaling only on VI channels. Because of that, add additional
configuration bitmask which tells which channel support scaler.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c |  1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 888620b1d3f1..e3296a060352 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -336,6 +336,7 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
 	.vi_num = 2,
 	.ui_num = 1,
+	.scaler_mask = 0x3,
 };
 
 static const struct of_device_id sun8i_mixer_of_table[] = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index bad0969ac105..a6270b307ae5 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -94,9 +94,19 @@ struct de2_fmt_info {
 	u32 de2_fmt;
 };
 
+/**
+ * struct sun8i_mixer_cfg - mixer HW configuration
+ * @vi_num: number of VI channels
+ * @ui_num: number of UI channels
+ * @scaler_mask: bitmask which tells which channel supports scaling
+ *	First, scaler supports for VI channels is defined and after that, scaler
+ *	support for UI channels. For example, if mixer has 2 VI channels without
+ *	scaler and 2 UI channels with scaler, bitmask would be 0xC.
+ */
 struct sun8i_mixer_cfg {
 	int		vi_num;
 	int		ui_num;
+	int		scaler_mask;
 };
 
 struct sun8i_mixer {
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 21/27] drm/sun4i: Add support for HW scaling to DE2
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Scaling is currently supported only for RGB framebuffers

Coefficients and algorithm which coefficients to select are taken
from BSP driver.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/Makefile          |   3 +-
 drivers/gpu/drm/sun4i/sun8i_mixer.h     |   4 -
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c  |  73 +++--
 drivers/gpu/drm/sun4i/sun8i_ui_scaler.c | 172 ++++++++++
 drivers/gpu/drm/sun4i/sun8i_ui_scaler.h |  49 +++
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c  |  62 +++-
 drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 535 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_vi_scaler.h |  56 ++++
 8 files changed, 915 insertions(+), 39 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_ui_scaler.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_ui_scaler.h
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_vi_scaler.h

diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index b0a2eb2c4806..a458ddbf4a8e 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -10,7 +10,8 @@ sun4i-drm-hdmi-y		+= sun4i_hdmi_i2c.o
 sun4i-drm-hdmi-y		+= sun4i_hdmi_tmds_clk.o
 
 sun8i-mixer-y			+= sun8i_mixer.o sun8i_ui_layer.o \
-				   sun8i_vi_layer.o
+				   sun8i_vi_layer.o sun8i_ui_scaler.o \
+				   sun8i_vi_scaler.o
 
 sun4i-tcon-y			+= sun4i_crtc.o
 sun4i-tcon-y			+= sun4i_dotclock.o
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index a6270b307ae5..9bbd762d26f7 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -77,10 +77,6 @@
  * These sub-engines are still unknown now, the EN registers are here only to
  * be used to disable these sub-engines.
  */
-#define SUN8I_MIXER_VSU_EN			0x20000
-#define SUN8I_MIXER_GSU1_EN			0x30000
-#define SUN8I_MIXER_GSU2_EN			0x40000
-#define SUN8I_MIXER_GSU3_EN			0x50000
 #define SUN8I_MIXER_FCE_EN			0xa0000
 #define SUN8I_MIXER_BWS_EN			0xa2000
 #define SUN8I_MIXER_LTI_EN			0xa4000
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index 3ecf39c50111..3cf0729cc7e9 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -24,6 +24,7 @@
 
 #include "sun8i_ui_layer.h"
 #include "sun8i_mixer.h"
+#include "sun8i_ui_scaler.h"
 
 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
 				  int overlay, bool enable)
@@ -56,29 +57,35 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 				       int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
-	u32 width, height, size;
+	u32 src_w, src_h, dst_w, dst_h;
+	u32 outsize, insize;
+	u32 hphase, vphase;
 
-	DRM_DEBUG_DRIVER("Updating channel %d overlay %d\n", channel, overlay);
+	DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
+			 channel, overlay);
 
-	/*
-	 * Same source and destination width and height are guaranteed
-	 * by atomic check function.
-	 */
-	width = drm_rect_width(&state->dst);
-	height = drm_rect_height(&state->dst);
-	size = SUN8I_MIXER_SIZE(width, height);
+	src_w = drm_rect_width(&state->src) >> 16;
+	src_h = drm_rect_height(&state->src) >> 16;
+	dst_w = drm_rect_width(&state->dst);
+	dst_h = drm_rect_height(&state->dst);
+
+	hphase = state->src.x1 & 0xffff;
+	vphase = state->src.y1 & 0xffff;
+
+	insize = SUN8I_MIXER_SIZE(src_w, src_h);
+	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
 		bool interlaced = false;
 		u32 val;
 
 		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
-				 width, height);
+				 dst_w, dst_h);
 		regmap_write(mixer->engine.regs,
 			     SUN8I_MIXER_GLOBAL_SIZE,
-			     size);
+			     outsize);
 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
-			     size);
+			     outsize);
 
 		if (state->crtc)
 			interlaced = state->crtc->state->adjusted_mode.flags
@@ -99,23 +106,42 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 	}
 
 	/* Set height and width */
-	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
+	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
+			 state->src.x1 >> 16, state->src.y1 >> 16);
+	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay),
-		     size);
+		     insize);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel),
-		     size);
+		     insize);
+
+	if (insize != outsize || hphase || vphase) {
+		u32 hscale, vscale;
+
+		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
+
+		hscale = state->src_w / state->crtc_w;
+		vscale = state->src_h / state->crtc_h;
+
+		sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w,
+				      dst_h, hscale, vscale, hphase, vphase);
+		sun8i_ui_scaler_enable(mixer, channel, true);
+	} else {
+		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
+		sun8i_ui_scaler_enable(mixer, channel, false);
+	}
 
 	/* Set base coordinates */
-	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
 			 state->dst.x1, state->dst.y1);
+	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
-		     size);
+		     outsize);
 
 	return 0;
 }
@@ -181,8 +207,10 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
 				       struct drm_plane_state *state)
 {
+	struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
 	struct drm_crtc *crtc = state->crtc;
 	struct drm_crtc_state *crtc_state;
+	int min_scale, max_scale;
 	struct drm_rect clip;
 
 	if (!crtc)
@@ -197,9 +225,16 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
 	clip.x2 = crtc_state->adjusted_mode.hdisplay;
 	clip.y2 = crtc_state->adjusted_mode.vdisplay;
 
+	min_scale = DRM_PLANE_HELPER_NO_SCALING;
+	max_scale = DRM_PLANE_HELPER_NO_SCALING;
+
+	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
+		min_scale = SUN8I_UI_SCALER_SCALE_MIN;
+		max_scale = SUN8I_UI_SCALER_SCALE_MAX;
+	}
+
 	return drm_plane_helper_check_state(state, &clip,
-					    DRM_PLANE_HELPER_NO_SCALING,
-					    DRM_PLANE_HELPER_NO_SCALING,
+					    min_scale, max_scale,
 					    true, true);
 }
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c
new file mode 100644
index 000000000000..6bb2aa164c8e
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * Coefficients are taken from BSP driver, which is:
+ * Copyright (C) 2014-2015 Allwinner
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include "sun8i_ui_scaler.h"
+
+static const u32 lan2coefftab16[240] = {
+	0x00004000, 0x00033ffe, 0x00063efc, 0x000a3bfb,
+	0xff0f37fb, 0xfe1433fb, 0xfd192ffb, 0xfd1f29fb,
+	0xfc2424fc, 0xfb291ffd, 0xfb2f19fd, 0xfb3314fe,
+	0xfb370fff, 0xfb3b0a00, 0xfc3e0600, 0xfe3f0300,
+
+	0xff053804, 0xff083801, 0xff0a3700, 0xff0e34ff,
+	0xff1232fd, 0xfe162ffd, 0xfd1b2cfc, 0xfd1f28fc,
+	0xfd2323fd, 0xfc281ffd, 0xfc2c1bfd, 0xfd2f16fe,
+	0xfd3212ff, 0xff340eff, 0x00360a00, 0x02370700,
+
+	0xff083207, 0xff0a3205, 0xff0d3103, 0xfe113001,
+	0xfe142e00, 0xfe182bff, 0xfe1b29fe, 0xfe1f25fe,
+	0xfe2222fe, 0xfe251ffe, 0xfe291bfe, 0xff2b18fe,
+	0x002e14fe, 0x013010ff, 0x03310dff, 0x05310a00,
+
+	0xff0a2e09, 0xff0c2e07, 0xff0f2d05, 0xff122c03,
+	0xfe152b02, 0xfe182901, 0xfe1b2700, 0xff1e24ff,
+	0xff2121ff, 0xff241eff, 0x00261bff, 0x012818ff,
+	0x022a15ff, 0x032c12ff, 0x052d0fff, 0x072d0c00,
+
+	0xff0c2a0b, 0xff0e2a09, 0xff102a07, 0xff132905,
+	0xff162803, 0xff182702, 0xff1b2501, 0xff1e2300,
+	0x00202000, 0x01221d00, 0x01251bff, 0x032618ff,
+	0x042815ff, 0x052913ff, 0x072a10ff, 0x092a0d00,
+
+	0xff0d280c, 0xff0f280a, 0xff112808, 0xff142706,
+	0xff162605, 0xff192503, 0x001b2302, 0x001d2201,
+	0x011f1f01, 0x01221d00, 0x02231b00, 0x04241800,
+	0x052616ff, 0x072713ff, 0x08271100, 0x0a280e00,
+
+	0xff0e260d, 0xff10260b, 0xff122609, 0xff142508,
+	0x00152506, 0x00182305, 0x001b2203, 0x011d2002,
+	0x011f1f01, 0x02201d01, 0x03221b00, 0x04231801,
+	0x06241600, 0x08251300, 0x09261100, 0x0b260f00,
+
+	0xff0e250e, 0xff10250c, 0x0011250a, 0x00142408,
+	0x00162307, 0x00182206, 0x011a2104, 0x011c2003,
+	0x021e1e02, 0x03201c01, 0x04211a01, 0x05221801,
+	0x07231600, 0x08241400, 0x0a241200, 0x0c241000,
+
+	0x000e240e, 0x0010240c, 0x0013230a, 0x00142309,
+	0x00162208, 0x01182106, 0x011a2005, 0x021b1f04,
+	0x031d1d03, 0x041e1c02, 0x05201a01, 0x06211801,
+	0x07221601, 0x09231400, 0x0a231300, 0x0c231100,
+
+	0x000f220f, 0x0011220d, 0x0013220b, 0x0015210a,
+	0x01162108, 0x01182007, 0x02191f06, 0x031a1e05,
+	0x041c1c04, 0x051d1b03, 0x061f1902, 0x07201801,
+	0x08211601, 0x0a211500, 0x0b221300, 0x0d221100,
+
+	0x0010210f, 0x0011210e, 0x0013210c, 0x0114200b,
+	0x01161f0a, 0x02171f08, 0x03181e07, 0x031a1d06,
+	0x041c1c04, 0x051d1a04, 0x071d1903, 0x081e1802,
+	0x091f1602, 0x0b1f1501, 0x0c211300, 0x0e201200,
+
+	0x00102010, 0x0012200e, 0x0013200d, 0x01151f0b,
+	0x01161f0a, 0x02171e09, 0x03191d07, 0x041a1c06,
+	0x051b1b05, 0x061c1a04, 0x071d1903, 0x081e1703,
+	0x0a1f1601, 0x0b1f1501, 0x0d201300, 0x0e201200,
+
+	0x00102010, 0x00121f0f, 0x00141f0d, 0x01141f0c,
+	0x02161e0a, 0x03171d09, 0x03181d08, 0x041a1c06,
+	0x051b1b05, 0x061c1a04, 0x081c1903, 0x091d1703,
+	0x0a1e1602, 0x0c1e1501, 0x0d1f1400, 0x0e1f1201,
+
+	0x00111e11, 0x00131e0f, 0x01131e0e, 0x02151d0c,
+	0x02161d0b, 0x03171c0a, 0x04181b09, 0x05191b07,
+	0x061a1a06, 0x071b1905, 0x091b1804, 0x0a1c1703,
+	0x0b1d1602, 0x0c1d1502, 0x0e1d1401, 0x0f1e1300,
+
+	0x00111e11, 0x00131d10, 0x01141d0e, 0x02151c0d,
+	0x03161c0b, 0x04171b0a, 0x05171b09, 0x06181a08,
+	0x07191907, 0x081a1806, 0x091a1805, 0x0a1b1704,
+	0x0b1c1603, 0x0d1c1502, 0x0e1d1401, 0x0f1d1301,
+};
+
+static int sun8i_ui_scaler_coef_index(unsigned int step)
+{
+	unsigned int scale, int_part, float_part;
+
+	scale = step >> (SUN8I_UI_SCALER_SCALE_FRAC - 3);
+	int_part = scale >> 3;
+	float_part = scale & 0x7;
+
+	switch (int_part) {
+	case 0:
+		return 0;
+	case 1:
+		return float_part;
+	case 2:
+		return 8 + (float_part >> 1);
+	case 3:
+		return 12;
+	case 4:
+		return 13;
+	default:
+		return 14;
+	}
+}
+
+void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
+{
+	int vi_cnt = mixer->cfg->vi_num;
+	u32 val;
+
+	if (WARN_ON(layer < vi_cnt))
+		return;
+
+	if (enable)
+		val = SUN8I_SCALER_GSU_CTRL_EN |
+		      SUN8I_SCALER_GSU_CTRL_COEFF_RDY;
+	else
+		val = 0;
+
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_CTRL(vi_cnt, layer - vi_cnt), val);
+}
+
+void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
+			   u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
+			   u32 hscale, u32 vscale, u32 hphase, u32 vphase)
+{
+	int vi_cnt = mixer->cfg->vi_num;
+	u32 insize, outsize;
+	int i, offset;
+
+	if (WARN_ON(layer < vi_cnt))
+		return;
+
+	hphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16;
+	vphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16;
+	hscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16;
+	vscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16;
+
+	insize = SUN8I_UI_SCALER_SIZE(src_w, src_h);
+	outsize = SUN8I_UI_SCALER_SIZE(dst_w, dst_h);
+
+	layer -= vi_cnt;
+
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, layer), outsize);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_INSIZE(vi_cnt, layer), insize);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_HSTEP(vi_cnt, layer), hscale);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_VSTEP(vi_cnt, layer), vscale);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_HPHASE(vi_cnt, layer), hphase);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_GSU_VPHASE(vi_cnt, layer), vphase);
+	offset = sun8i_ui_scaler_coef_index(hscale) *
+			SUN8I_UI_SCALER_COEFF_COUNT;
+	for (i = 0; i < SUN8I_UI_SCALER_COEFF_COUNT; i++)
+		regmap_write(mixer->engine.regs,
+			     SUN8I_SCALER_GSU_HCOEFF(vi_cnt, layer, i),
+			     lan2coefftab16[offset + i]);
+}
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h
new file mode 100644
index 000000000000..86295be8be78
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _SUN8I_UI_SCALER_H_
+#define _SUN8I_UI_SCALER_H_
+
+#include "sun8i_mixer.h"
+
+/* this two macros assumes 16 fractional bits which is standard in DRM */
+#define SUN8I_UI_SCALER_SCALE_MIN		1
+#define SUN8I_UI_SCALER_SCALE_MAX		((1UL << 20) - 1)
+
+#define SUN8I_UI_SCALER_SCALE_FRAC		20
+#define SUN8I_UI_SCALER_PHASE_FRAC		20
+#define SUN8I_UI_SCALER_COEFF_COUNT		16
+#define SUN8I_UI_SCALER_SIZE(w, h)		(((h) - 1) << 16 | ((w) - 1))
+
+#define SUN8I_SCALER_GSU_CTRL(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x0)
+#define SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x40)
+#define SUN8I_SCALER_GSU_INSIZE(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x80)
+#define SUN8I_SCALER_GSU_HSTEP(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x88)
+#define SUN8I_SCALER_GSU_VSTEP(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x8c)
+#define SUN8I_SCALER_GSU_HPHASE(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x90)
+#define SUN8I_SCALER_GSU_VPHASE(vi_cnt, ui_idx) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x98)
+#define SUN8I_SCALER_GSU_HCOEFF(vi_cnt, ui_idx, index) \
+	(0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x200 + \
+	0x4 * (index))
+
+#define SUN8I_SCALER_GSU_CTRL_EN		BIT(0)
+#define SUN8I_SCALER_GSU_CTRL_COEFF_RDY		BIT(4)
+
+void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
+void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
+			   u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
+			   u32 hscale, u32 vscale, u32 hphase, u32 vphase);
+
+#endif
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index b78d4f3e2b48..e47a0c1f640d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -18,6 +18,7 @@
 
 #include "sun8i_vi_layer.h"
 #include "sun8i_mixer.h"
+#include "sun8i_vi_scaler.h"
 
 static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
 				  int overlay, bool enable)
@@ -50,36 +51,61 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 				       int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
-	u32 width, height, size;
+	u32 src_w, src_h, dst_w, dst_h;
+	u32 outsize, insize;
+	u32 hphase, vphase;
 
 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
 			 channel, overlay);
-	/*
-	 * Same source and destination width and height are guaranteed
-	 * by atomic check function.
-	 */
-	width = drm_rect_width(&state->dst);
-	height = drm_rect_height(&state->dst);
-	size = SUN8I_MIXER_SIZE(width, height);
+
+	src_w = drm_rect_width(&state->src) >> 16;
+	src_h = drm_rect_height(&state->src) >> 16;
+	dst_w = drm_rect_width(&state->dst);
+	dst_h = drm_rect_height(&state->dst);
+
+	hphase = state->src.x1 & 0xffff;
+	vphase = state->src.y1 & 0xffff;
+
+	insize = SUN8I_MIXER_SIZE(src_w, src_h);
+	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 
 	/* Set height and width */
-	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", width, height);
+	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
+			 state->src.x1 >> 16, state->src.y1 >> 16);
+	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay),
-		     size);
+		     insize);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel),
-		     size);
+		     insize);
+
+	if (insize != outsize || hphase || vphase) {
+		u32 hscale, vscale;
+
+		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
+
+		hscale = state->src_w / state->crtc_w;
+		vscale = state->src_h / state->crtc_h;
+
+		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
+				      dst_h, hscale, vscale, hphase, vphase);
+		sun8i_vi_scaler_enable(mixer, channel, true);
+	} else {
+		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
+		sun8i_vi_scaler_enable(mixer, channel, false);
+	}
 
 	/* Set base coordinates */
-	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
 			 state->dst.x1, state->dst.y1);
+	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
-		     size);
+		     outsize);
 
 	return 0;
 }
@@ -147,8 +173,10 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
 static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
 				       struct drm_plane_state *state)
 {
+	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
 	struct drm_crtc *crtc = state->crtc;
 	struct drm_crtc_state *crtc_state;
+	int min_scale, max_scale;
 	struct drm_rect clip;
 
 	if (!crtc)
@@ -163,9 +191,13 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
 	clip.x2 = crtc_state->adjusted_mode.hdisplay;
 	clip.y2 = crtc_state->adjusted_mode.vdisplay;
 
+	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
+		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
+		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
+	}
+
 	return drm_plane_helper_check_state(state, &clip,
-					    DRM_PLANE_HELPER_NO_SCALING,
-					    DRM_PLANE_HELPER_NO_SCALING,
+					    min_scale, max_scale,
 					    true, true);
 }
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
new file mode 100644
index 000000000000..68b92e46be68
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * Coefficients are taken from BSP driver, which is:
+ * Copyright (C) 2014-2015 Allwinner
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include "sun8i_vi_scaler.h"
+
+static const u32 lan3coefftab32_left[480] = {
+	0x40000000, 0x40fe0000, 0x3ffd0100, 0x3efc0100,
+	0x3efb0100, 0x3dfa0200, 0x3cf90200, 0x3bf80200,
+	0x39f70200, 0x37f70200, 0x35f70200, 0x33f70200,
+	0x31f70200, 0x2ef70200, 0x2cf70200, 0x2af70200,
+	0x27f70200, 0x24f80100, 0x22f80100, 0x1ef90100,
+	0x1cf90100, 0x19fa0100, 0x17fa0100, 0x14fb0100,
+	0x11fc0000, 0x0ffc0000, 0x0cfd0000, 0x0afd0000,
+	0x08fe0000, 0x05ff0000, 0x03ff0000, 0x02000000,
+
+	0x3806fc02, 0x3805fc02, 0x3803fd01, 0x3801fe01,
+	0x3700fe01, 0x35ffff01, 0x35fdff01, 0x34fc0001,
+	0x34fb0000, 0x33fa0000, 0x31fa0100, 0x2ff90100,
+	0x2df80200, 0x2bf80200, 0x2af70200, 0x28f70200,
+	0x27f70200, 0x24f70300, 0x22f70300, 0x1ff70300,
+	0x1ef70300, 0x1cf70300, 0x1af70300, 0x18f70300,
+	0x16f80300, 0x13f80300, 0x11f90300, 0x0ef90300,
+	0x0efa0200, 0x0cfa0200, 0x0afb0200, 0x08fb0200,
+
+	0x320bfa02, 0x3309fa02, 0x3208fb02, 0x3206fb02,
+	0x3205fb02, 0x3104fc02, 0x3102fc01, 0x3001fd01,
+	0x3000fd01, 0x2ffffd01, 0x2efefe01, 0x2dfdfe01,
+	0x2bfcff01, 0x29fcff01, 0x28fbff01, 0x27fa0001,
+	0x26fa0000, 0x24f90000, 0x22f90100, 0x20f90100,
+	0x1ff80100, 0x1ef80100, 0x1cf80100, 0x1af80200,
+	0x18f80200, 0x17f80200, 0x15f80200, 0x12f80200,
+	0x11f90200, 0x0ff90200, 0x0df90200, 0x0cfa0200,
+
+	0x2e0efa01, 0x2f0dfa01, 0x2f0bfa01, 0x2e0afa01,
+	0x2e09fa01, 0x2e07fb01, 0x2d06fb01, 0x2d05fb01,
+	0x2c04fb01, 0x2b03fc01, 0x2a02fc01, 0x2a01fc01,
+	0x2800fd01, 0x28fffd01, 0x26fefd01, 0x25fefe01,
+	0x24fdfe01, 0x23fcfe01, 0x21fcff01, 0x20fbff01,
+	0x1efbff01, 0x1efbff00, 0x1cfa0000, 0x1bfa0000,
+	0x19fa0000, 0x18fa0000, 0x17f90000, 0x15f90100,
+	0x14f90100, 0x12f90100, 0x11f90100, 0x0ff90100,
+
+	0x2b10fa00, 0x2b0ffa00, 0x2b0efa00, 0x2b0cfa00,
+	0x2b0bfa00, 0x2a0afb01, 0x2a09fb01, 0x2908fb01,
+	0x2807fb01, 0x2806fb01, 0x2805fb01, 0x2604fc01,
+	0x2503fc01, 0x2502fc01, 0x2401fc01, 0x2301fc01,
+	0x2100fd01, 0x21fffd01, 0x21fffd01, 0x20fefd01,
+	0x1dfefe01, 0x1cfdfe01, 0x1cfdfe00, 0x1bfcfe00,
+	0x19fcff00, 0x19fbff00, 0x17fbff00, 0x16fbff00,
+	0x15fbff00, 0x14fb0000, 0x13fa0000, 0x11fa0000,
+
+	0x2811fcff, 0x2810fcff, 0x280ffbff, 0x280efbff,
+	0x270dfb00, 0x270cfb00, 0x270bfb00, 0x260afb00,
+	0x2609fb00, 0x2508fb00, 0x2507fb00, 0x2407fb00,
+	0x2406fc00, 0x2305fc00, 0x2204fc00, 0x2203fc00,
+	0x2103fc00, 0x2002fc00, 0x1f01fd00, 0x1e01fd00,
+	0x1d00fd00, 0x1dfffd00, 0x1cfffd00, 0x1bfefd00,
+	0x1afefe00, 0x19fefe00, 0x18fdfe00, 0x17fdfe00,
+	0x16fdfe00, 0x15fcff00, 0x13fcff00, 0x12fcff00,
+
+	0x2512fdfe, 0x2511fdff, 0x2410fdff, 0x240ffdff,
+	0x240efcff, 0x240dfcff, 0x240dfcff, 0x240cfcff,
+	0x230bfcff, 0x230afc00, 0x2209fc00, 0x2108fc00,
+	0x2108fc00, 0x2007fc00, 0x2006fc00, 0x2005fc00,
+	0x1f05fc00, 0x1e04fc00, 0x1e03fc00, 0x1c03fd00,
+	0x1c02fd00, 0x1b02fd00, 0x1b01fd00, 0x1a00fd00,
+	0x1900fd00, 0x1800fd00, 0x17fffe00, 0x16fffe00,
+	0x16fefe00, 0x14fefe00, 0x13fefe00, 0x13fdfe00,
+
+	0x2212fffe, 0x2211fefe, 0x2211fefe, 0x2110fefe,
+	0x210ffeff, 0x220efdff, 0x210dfdff, 0x210dfdff,
+	0x210cfdff, 0x210bfdff, 0x200afdff, 0x200afdff,
+	0x1f09fdff, 0x1f08fdff, 0x1d08fd00, 0x1c07fd00,
+	0x1d06fd00, 0x1b06fd00, 0x1b05fd00, 0x1c04fd00,
+	0x1b04fd00, 0x1a03fd00, 0x1a03fd00, 0x1902fd00,
+	0x1802fd00, 0x1801fd00, 0x1701fd00, 0x1600fd00,
+	0x1400fe00, 0x1400fe00, 0x14fffe00, 0x13fffe00,
+
+	0x201200fe, 0x201100fe, 0x1f11fffe, 0x2010fffe,
+	0x1f0ffffe, 0x1e0ffffe, 0x1f0efeff, 0x1f0dfeff,
+	0x1f0dfeff, 0x1e0cfeff, 0x1e0bfeff, 0x1d0bfeff,
+	0x1d0afeff, 0x1d09fdff, 0x1d09fdff, 0x1c08fdff,
+	0x1c07fdff, 0x1b07fd00, 0x1b06fd00, 0x1a06fd00,
+	0x1a05fd00, 0x1805fd00, 0x1904fd00, 0x1804fd00,
+	0x1703fd00, 0x1703fd00, 0x1602fe00, 0x1502fe00,
+	0x1501fe00, 0x1401fe00, 0x1301fe00, 0x1300fe00,
+
+	0x1c1202fe, 0x1c1102fe, 0x1b1102fe, 0x1c1001fe,
+	0x1b1001fe, 0x1b0f01ff, 0x1b0e00ff, 0x1b0e00ff,
+	0x1b0d00ff, 0x1a0d00ff, 0x1a0c00ff, 0x1a0cffff,
+	0x1a0bffff, 0x1a0bffff, 0x1a0affff, 0x180affff,
+	0x1909ffff, 0x1809ffff, 0x1808ffff, 0x1808feff,
+	0x1807feff, 0x1707fe00, 0x1606fe00, 0x1506fe00,
+	0x1605fe00, 0x1505fe00, 0x1504fe00, 0x1304fe00,
+	0x1304fe00, 0x1303fe00, 0x1203fe00, 0x1203fe00,
+
+	0x181104ff, 0x191103ff, 0x191003ff, 0x181003ff,
+	0x180f03ff, 0x190f02ff, 0x190e02ff, 0x180e02ff,
+	0x180d02ff, 0x180d01ff, 0x180d01ff, 0x180c01ff,
+	0x180c01ff, 0x180b00ff, 0x170b00ff, 0x170a00ff,
+	0x170a00ff, 0x170900ff, 0x160900ff, 0x160900ff,
+	0x1608ffff, 0x1508ffff, 0x1507ff00, 0x1507ff00,
+	0x1407ff00, 0x1306ff00, 0x1306ff00, 0x1305ff00,
+	0x1205ff00, 0x1105ff00, 0x1204ff00, 0x1104ff00,
+
+	0x171005ff, 0x171005ff, 0x171004ff, 0x170f04ff,
+	0x160f04ff, 0x170f03ff, 0x170e03ff, 0x160e03ff,
+	0x160d03ff, 0x160d02ff, 0x160d02ff, 0x160c02ff,
+	0x160c02ff, 0x160c02ff, 0x160b01ff, 0x150b01ff,
+	0x150a01ff, 0x150a01ff, 0x150a01ff, 0x140901ff,
+	0x14090000, 0x14090000, 0x14080000, 0x13080000,
+	0x13070000, 0x12070000, 0x12070000, 0x12060000,
+	0x11060000, 0x11060000, 0x11050000, 0x1105ff00,
+
+	0x14100600, 0x15100500, 0x150f0500, 0x150f0500,
+	0x140f0500, 0x150e0400, 0x140e0400, 0x130e0400,
+	0x140d0400, 0x150d0300, 0x130d0300, 0x140c0300,
+	0x140c0300, 0x140c0200, 0x140b0200, 0x130b0200,
+	0x120b0200, 0x130a0200, 0x130a0200, 0x130a0100,
+	0x13090100, 0x12090100, 0x11090100, 0x12080100,
+	0x11080100, 0x10080100, 0x11070100, 0x11070000,
+	0x10070000, 0x11060000, 0x10060000, 0x10060000,
+
+	0x140f0600, 0x140f0600, 0x130f0600, 0x140f0500,
+	0x140e0500, 0x130e0500, 0x130e0500, 0x140d0400,
+	0x140d0400, 0x130d0400, 0x120d0400, 0x130c0400,
+	0x130c0300, 0x130c0300, 0x130b0300, 0x130b0300,
+	0x110b0300, 0x130a0200, 0x120a0200, 0x120a0200,
+	0x120a0200, 0x12090200, 0x10090200, 0x11090100,
+	0x11080100, 0x11080100, 0x10080100, 0x10080100,
+	0x10070100, 0x10070100, 0x0f070100, 0x10060100,
+
+	0x120f0701, 0x130f0601, 0x130e0601, 0x130e0601,
+	0x120e0601, 0x130e0501, 0x130e0500, 0x130d0500,
+	0x120d0500, 0x120d0500, 0x130c0400, 0x130c0400,
+	0x120c0400, 0x110c0400, 0x120b0400, 0x120b0300,
+	0x120b0300, 0x120b0300, 0x120a0300, 0x110a0300,
+	0x110a0200, 0x11090200, 0x11090200, 0x10090200,
+	0x10090200, 0x10080200, 0x10080200, 0x10080100,
+	0x0f080100, 0x10070100, 0x0f070100, 0x0f070100
+};
+
+static const u32 lan3coefftab32_right[480] = {
+	0x00000000, 0x00000002, 0x0000ff04, 0x0000ff06,
+	0x0000fe08, 0x0000fd0a, 0x0000fd0c, 0x0000fc0f,
+	0x0000fc12, 0x0001fb14, 0x0001fa17, 0x0001fa19,
+	0x0001f91c, 0x0001f91f, 0x0001f822, 0x0001f824,
+	0x0002f727, 0x0002f72a, 0x0002f72c, 0x0002f72f,
+	0x0002f731, 0x0002f733, 0x0002f735, 0x0002f737,
+	0x0002f73a, 0x0002f83b, 0x0002f93c, 0x0002fa3d,
+	0x0001fb3e, 0x0001fc3f, 0x0001fd40, 0x0000fe40,
+
+	0x0002fc06, 0x0002fb08, 0x0002fb0a, 0x0002fa0c,
+	0x0002fa0e, 0x0003f910, 0x0003f912, 0x0003f814,
+	0x0003f816, 0x0003f719, 0x0003f71a, 0x0003f71d,
+	0x0003f71f, 0x0003f721, 0x0003f723, 0x0003f725,
+	0x0002f727, 0x0002f729, 0x0002f72b, 0x0002f82d,
+	0x0002f82e, 0x0001f930, 0x0001fa31, 0x0000fa34,
+	0x0000fb34, 0x0100fc35, 0x01fffd36, 0x01ffff37,
+	0x01fe0037, 0x01fe0138, 0x01fd0338, 0x02fc0538,
+
+	0x0002fa0b, 0x0002fa0c, 0x0002f90e, 0x0002f910,
+	0x0002f911, 0x0002f813, 0x0002f816, 0x0002f817,
+	0x0002f818, 0x0002f81a, 0x0001f81c, 0x0001f81e,
+	0x0001f820, 0x0001f921, 0x0001f923, 0x0000f925,
+	0x0000fa26, 0x0100fa28, 0x01fffb29, 0x01fffc2a,
+	0x01fffc2c, 0x01fefd2d, 0x01fefe2e, 0x01fdff2f,
+	0x01fd0030, 0x01fd0130, 0x01fc0232, 0x02fc0432,
+	0x02fb0532, 0x02fb0633, 0x02fb0833, 0x02fa0933,
+
+	0x0001fa0e, 0x0001f90f, 0x0001f911, 0x0001f913,
+	0x0001f914, 0x0001f915, 0x0000f918, 0x0000fa18,
+	0x0000fa1a, 0x0000fa1b, 0x0000fa1d, 0x00fffb1e,
+	0x01fffb1f, 0x01fffb20, 0x01fffc22, 0x01fefc23,
+	0x01fefd24, 0x01fefe25, 0x01fdfe27, 0x01fdff28,
+	0x01fd0029, 0x01fc012a, 0x01fc022b, 0x01fc032b,
+	0x01fb042d, 0x01fb052d, 0x01fb062e, 0x01fb072e,
+	0x01fa092e, 0x01fa0a2f, 0x01fa0b2f, 0x01fa0d2f,
+
+	0x0000fa11, 0x0000fa12, 0x0000fa13, 0x0000fb14,
+	0x00fffb16, 0x00fffb16, 0x00fffb17, 0x00fffb19,
+	0x00fffc1a, 0x00fefc1c, 0x00fefd1c, 0x01fefd1d,
+	0x01fefe1e, 0x01fdfe20, 0x01fdff21, 0x01fdff22,
+	0x01fd0023, 0x01fc0124, 0x01fc0124, 0x01fc0225,
+	0x01fc0326, 0x01fc0427, 0x01fb0528, 0x01fb0629,
+	0x01fb0729, 0x01fb0829, 0x01fb092a, 0x01fb0a2a,
+	0x00fa0b2c, 0x00fa0c2b, 0x00fa0e2b, 0x00fa0f2c,
+
+	0x00fffc11, 0x00fffc12, 0x00fffc14, 0x00fffc15,
+	0x00fefd16, 0x00fefd17, 0x00fefd18, 0x00fefe19,
+	0x00fefe1a, 0x00fdfe1d, 0x00fdff1d, 0x00fdff1e,
+	0x00fd001d, 0x00fd011e, 0x00fd0120, 0x00fc0221,
+	0x00fc0321, 0x00fc0323, 0x00fc0423, 0x00fc0523,
+	0x00fc0624, 0x00fb0725, 0x00fb0726, 0x00fb0827,
+	0x00fb0926, 0x00fb0a26, 0x00fb0b27, 0x00fb0c27,
+	0x00fb0d27, 0xfffb0e28, 0xfffb0f29, 0xfffc1028,
+
+	0x00fefd13, 0x00fefd13, 0x00fefe14, 0x00fefe15,
+	0x00fefe17, 0x00feff17, 0x00feff17, 0x00fd0018,
+	0x00fd001a, 0x00fd001a, 0x00fd011b, 0x00fd021c,
+	0x00fd021c, 0x00fd031d, 0x00fc031f, 0x00fc041f,
+	0x00fc051f, 0x00fc0521, 0x00fc0621, 0x00fc0721,
+	0x00fc0821, 0x00fc0822, 0x00fc0922, 0x00fc0a23,
+	0xfffc0b24, 0xfffc0c24, 0xfffc0d24, 0xfffc0d25,
+	0xfffc0e25, 0xfffd0f25, 0xfffd1025, 0xfffd1125,
+
+	0x00feff12, 0x00feff14, 0x00feff14, 0x00fe0015,
+	0x00fe0015, 0x00fd0017, 0x00fd0118, 0x00fd0118,
+	0x00fd0218, 0x00fd0219, 0x00fd031a, 0x00fd031a,
+	0x00fd041b, 0x00fd041c, 0x00fd051c, 0x00fd061d,
+	0x00fd061d, 0x00fd071e, 0x00fd081e, 0xfffd081f,
+	0xfffd091f, 0xfffd0a20, 0xfffd0a20, 0xfffd0b21,
+	0xfffd0c21, 0xfffd0d21, 0xfffd0d22, 0xfffd0e23,
+	0xfffe0f22, 0xfefe1022, 0xfefe1122, 0xfefe1123,
+
+	0x00fe0012, 0x00fe0013, 0x00fe0114, 0x00fe0114,
+	0x00fe0116, 0x00fe0216, 0x00fe0216, 0x00fd0317,
+	0x00fd0317, 0x00fd0418, 0x00fd0419, 0x00fd0519,
+	0x00fd051a, 0x00fd061b, 0x00fd061b, 0x00fd071c,
+	0xfffd071e, 0xfffd081d, 0xfffd091d, 0xfffd091e,
+	0xfffe0a1d, 0xfffe0b1e, 0xfffe0b1e, 0xfffe0c1e,
+	0xfffe0d1f, 0xfffe0d1f, 0xfffe0e1f, 0xfeff0f1f,
+	0xfeff0f20, 0xfeff1020, 0xfeff1120, 0xfe001120,
+
+	0x00fe0212, 0x00fe0312, 0x00fe0313, 0x00fe0314,
+	0x00fe0414, 0x00fe0414, 0x00fe0416, 0x00fe0515,
+	0x00fe0516, 0x00fe0616, 0x00fe0617, 0x00fe0717,
+	0xfffe0719, 0xfffe0818, 0xffff0818, 0xffff0919,
+	0xffff0919, 0xffff0a19, 0xffff0a1a, 0xffff0b1a,
+	0xffff0b1b, 0xffff0c1a, 0xff000c1b, 0xff000d1b,
+	0xff000d1b, 0xff000e1b, 0xff000e1c, 0xff010f1c,
+	0xfe01101c, 0xfe01101d, 0xfe02111c, 0xfe02111c,
+
+	0x00ff0411, 0x00ff0411, 0x00ff0412, 0x00ff0512,
+	0x00ff0513, 0x00ff0513, 0x00ff0613, 0x00ff0614,
+	0x00ff0714, 0x00ff0715, 0x00ff0715, 0xffff0816,
+	0xffff0816, 0xff000916, 0xff000917, 0xff000918,
+	0xff000a17, 0xff000a18, 0xff000b18, 0xff000b18,
+	0xff010c18, 0xff010c19, 0xff010d18, 0xff010d18,
+	0xff020d18, 0xff020e19, 0xff020e19, 0xff020f19,
+	0xff030f19, 0xff031019, 0xff031019, 0xff031119,
+
+	0x00ff0511, 0x00ff0511, 0x00000511, 0x00000611,
+	0x00000612, 0x00000612, 0x00000712, 0x00000713,
+	0x00000714, 0x00000814, 0x00000814, 0x00000914,
+	0x00000914, 0xff010914, 0xff010a15, 0xff010a16,
+	0xff010a17, 0xff010b16, 0xff010b16, 0xff020c16,
+	0xff020c16, 0xff020c16, 0xff020d16, 0xff020d17,
+	0xff030d17, 0xff030e17, 0xff030e17, 0xff030f17,
+	0xff040f17, 0xff040f17, 0xff041017, 0xff051017,
+
+	0x00000610, 0x00000610, 0x00000611, 0x00000611,
+	0x00000711, 0x00000712, 0x00010712, 0x00010812,
+	0x00010812, 0x00010812, 0x00010913, 0x00010913,
+	0x00010913, 0x00010a13, 0x00020a13, 0x00020a14,
+	0x00020b14, 0x00020b14, 0x00020b14, 0x00020c14,
+	0x00030c14, 0x00030c15, 0x00030d15, 0x00030d15,
+	0x00040d15, 0x00040e15, 0x00040e15, 0x00040e16,
+	0x00050f15, 0x00050f15, 0x00050f16, 0x00051015,
+
+	0x00000611, 0x00010610, 0x00010710, 0x00010710,
+	0x00010711, 0x00010811, 0x00010811, 0x00010812,
+	0x00010812, 0x00010912, 0x00020912, 0x00020912,
+	0x00020a12, 0x00020a12, 0x00020a13, 0x00020a13,
+	0x00030b13, 0x00030b13, 0x00030b14, 0x00030c13,
+	0x00030c13, 0x00040c13, 0x00040d14, 0x00040d14,
+	0x00040d15, 0x00040d15, 0x00050e14, 0x00050e14,
+	0x00050e15, 0x00050f14, 0x00060f14, 0x00060f14,
+
+	0x0001070f, 0x0001070f, 0x00010710, 0x00010710,
+	0x00010810, 0x00010810, 0x00020810, 0x00020811,
+	0x00020911, 0x00020911, 0x00020912, 0x00020912,
+	0x00020a12, 0x00030a12, 0x00030a12, 0x00030b12,
+	0x00030b12, 0x00030b12, 0x00040b12, 0x00040c12,
+	0x00040c13, 0x00040c14, 0x00040c14, 0x00050d13,
+	0x00050d13, 0x00050d14, 0x00050e13, 0x01050e13,
+	0x01060e13, 0x01060e13, 0x01060e14, 0x01060f13
+};
+
+static const u32 lan2coefftab32[480] = {
+	0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
+	0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
+	0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
+	0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
+	0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
+	0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
+	0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
+	0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
+
+	0xff053804, 0xff063803, 0xff083801, 0xff093701,
+	0xff0a3700, 0xff0c3500, 0xff0e34ff, 0xff1033fe,
+	0xff1232fd, 0xfe1431fd, 0xfe162ffd, 0xfe182dfd,
+	0xfd1b2cfc, 0xfd1d2afc, 0xfd1f28fc, 0xfd2126fc,
+	0xfd2323fd, 0xfc2621fd, 0xfc281ffd, 0xfc2a1dfd,
+	0xfc2c1bfd, 0xfd2d18fe, 0xfd2f16fe, 0xfd3114fe,
+	0xfd3212ff, 0xfe3310ff, 0xff340eff, 0x00350cff,
+	0x00360a00, 0x01360900, 0x02370700, 0x03370600,
+
+	0xff083207, 0xff093206, 0xff0a3205, 0xff0c3203,
+	0xff0d3103, 0xff0e3102, 0xfe113001, 0xfe132f00,
+	0xfe142e00, 0xfe162dff, 0xfe182bff, 0xfe192aff,
+	0xfe1b29fe, 0xfe1d27fe, 0xfe1f25fe, 0xfd2124fe,
+	0xfe2222fe, 0xfe2421fd, 0xfe251ffe, 0xfe271dfe,
+	0xfe291bfe, 0xff2a19fe, 0xff2b18fe, 0xff2d16fe,
+	0x002e14fe, 0x002f12ff, 0x013010ff, 0x02300fff,
+	0x03310dff, 0x04310cff, 0x05310a00, 0x06310900,
+
+	0xff0a2e09, 0xff0b2e08, 0xff0c2e07, 0xff0e2d06,
+	0xff0f2d05, 0xff102d04, 0xff122c03, 0xfe142c02,
+	0xfe152b02, 0xfe172a01, 0xfe182901, 0xfe1a2800,
+	0xfe1b2700, 0xfe1d2500, 0xff1e24ff, 0xfe2023ff,
+	0xff2121ff, 0xff2320fe, 0xff241eff, 0x00251dfe,
+	0x00261bff, 0x00281afe, 0x012818ff, 0x012a16ff,
+	0x022a15ff, 0x032b13ff, 0x032c12ff, 0x052c10ff,
+	0x052d0fff, 0x062d0d00, 0x072d0c00, 0x082d0b00,
+
+	0xff0c2a0b, 0xff0d2a0a, 0xff0e2a09, 0xff0f2a08,
+	0xff102a07, 0xff112a06, 0xff132905, 0xff142904,
+	0xff162803, 0xff172703, 0xff182702, 0xff1a2601,
+	0xff1b2501, 0xff1c2401, 0xff1e2300, 0xff1f2200,
+	0x00202000, 0x00211f00, 0x01221d00, 0x01231c00,
+	0x01251bff, 0x02251aff, 0x032618ff, 0x032717ff,
+	0x042815ff, 0x052814ff, 0x052913ff, 0x06291100,
+	0x072a10ff, 0x082a0e00, 0x092a0d00, 0x0a2a0c00,
+
+	0xff0d280c, 0xff0e280b, 0xff0f280a, 0xff102809,
+	0xff112808, 0xff122708, 0xff142706, 0xff152705,
+	0xff162605, 0xff172604, 0xff192503, 0xff1a2403,
+	0x001b2302, 0x001c2202, 0x001d2201, 0x001e2101,
+	0x011f1f01, 0x01211e00, 0x01221d00, 0x02221c00,
+	0x02231b00, 0x03241900, 0x04241800, 0x04251700,
+	0x052616ff, 0x06261400, 0x072713ff, 0x08271100,
+	0x08271100, 0x09271000, 0x0a280e00, 0x0b280d00,
+
+	0xff0e260d, 0xff0f260c, 0xff10260b, 0xff11260a,
+	0xff122609, 0xff132608, 0xff142508, 0xff152507,
+	0x00152506, 0x00172405, 0x00182305, 0x00192304,
+	0x001b2203, 0x001c2103, 0x011d2002, 0x011d2002,
+	0x011f1f01, 0x021f1e01, 0x02201d01, 0x03211c00,
+	0x03221b00, 0x04221a00, 0x04231801, 0x05241700,
+	0x06241600, 0x07241500, 0x08251300, 0x09251200,
+	0x09261100, 0x0a261000, 0x0b260f00, 0x0c260e00,
+
+	0xff0e250e, 0xff0f250d, 0xff10250c, 0xff11250b,
+	0x0011250a, 0x00132409, 0x00142408, 0x00152407,
+	0x00162307, 0x00172306, 0x00182206, 0x00192205,
+	0x011a2104, 0x011b2004, 0x011c2003, 0x021c1f03,
+	0x021e1e02, 0x031e1d02, 0x03201c01, 0x04201b01,
+	0x04211a01, 0x05221900, 0x05221801, 0x06231700,
+	0x07231600, 0x07241500, 0x08241400, 0x09241300,
+	0x0a241200, 0x0b241100, 0x0c241000, 0x0d240f00,
+
+	0x000e240e, 0x000f240d, 0x0010240c, 0x0011240b,
+	0x0013230a, 0x0013230a, 0x00142309, 0x00152308,
+	0x00162208, 0x00172207, 0x01182106, 0x01192105,
+	0x011a2005, 0x021b1f04, 0x021b1f04, 0x021d1e03,
+	0x031d1d03, 0x031e1d02, 0x041e1c02, 0x041f1b02,
+	0x05201a01, 0x05211901, 0x06211801, 0x07221700,
+	0x07221601, 0x08231500, 0x09231400, 0x0a231300,
+	0x0a231300, 0x0b231200, 0x0c231100, 0x0d231000,
+
+	0x000f220f, 0x0010220e, 0x0011220d, 0x0012220c,
+	0x0013220b, 0x0013220b, 0x0015210a, 0x0015210a,
+	0x01162108, 0x01172008, 0x01182007, 0x02191f06,
+	0x02191f06, 0x021a1e06, 0x031a1e05, 0x031c1d04,
+	0x041c1c04, 0x041d1c03, 0x051d1b03, 0x051e1a03,
+	0x061f1902, 0x061f1902, 0x07201801, 0x08201701,
+	0x08211601, 0x09211501, 0x0a211500, 0x0b211400,
+	0x0b221300, 0x0c221200, 0x0d221100, 0x0e221000,
+
+	0x0010210f, 0x0011210e, 0x0011210e, 0x0012210d,
+	0x0013210c, 0x0014200c, 0x0114200b, 0x0115200a,
+	0x01161f0a, 0x01171f09, 0x02171f08, 0x02181e08,
+	0x03181e07, 0x031a1d06, 0x031a1d06, 0x041b1c05,
+	0x041c1c04, 0x051c1b04, 0x051d1a04, 0x061d1a03,
+	0x071d1903, 0x071e1803, 0x081e1802, 0x081f1702,
+	0x091f1602, 0x0a201501, 0x0b1f1501, 0x0b201401,
+	0x0c211300, 0x0d211200, 0x0e201200, 0x0e211100,
+
+	0x00102010, 0x0011200f, 0x0012200e, 0x0013200d,
+	0x0013200d, 0x01141f0c, 0x01151f0b, 0x01151f0b,
+	0x01161f0a, 0x02171e09, 0x02171e09, 0x03181d08,
+	0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05,
+	0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071d1903,
+	0x071d1903, 0x081d1803, 0x081e1703, 0x091e1702,
+	0x0a1f1601, 0x0a1f1502, 0x0b1f1501, 0x0c1f1401,
+	0x0d201300, 0x0d201300, 0x0e201200, 0x0f201100,
+
+	0x00102010, 0x0011200f, 0x00121f0f, 0x00131f0e,
+	0x00141f0d, 0x01141f0c, 0x01141f0c, 0x01151e0c,
+	0x02161e0a, 0x02171e09, 0x03171d09, 0x03181d08,
+	0x03181d08, 0x04191c07, 0x041a1c06, 0x051a1b06,
+	0x051b1b05, 0x061b1a05, 0x061c1a04, 0x071c1904,
+	0x081c1903, 0x081d1803, 0x091d1703, 0x091e1702,
+	0x0a1e1602, 0x0b1e1502, 0x0c1e1501, 0x0c1f1401,
+	0x0d1f1400, 0x0e1f1300, 0x0e1f1201, 0x0f1f1200,
+
+	0x00111e11, 0x00121e10, 0x00131e0f, 0x00131e0f,
+	0x01131e0e, 0x01141d0e, 0x02151d0c, 0x02151d0c,
+	0x02161d0b, 0x03161c0b, 0x03171c0a, 0x04171c09,
+	0x04181b09, 0x05181b08, 0x05191b07, 0x06191a07,
+	0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805,
+	0x091b1804, 0x091c1704, 0x0a1c1703, 0x0a1c1604,
+	0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1d1402,
+	0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200,
+
+	0x00111e11, 0x00121e10, 0x00131d10, 0x01131d0f,
+	0x01141d0e, 0x01141d0e, 0x02151c0d, 0x02151c0d,
+	0x03161c0b, 0x03161c0b, 0x04171b0a, 0x04171b0a,
+	0x05171b09, 0x05181a09, 0x06181a08, 0x06191a07,
+	0x07191907, 0x071a1906, 0x081a1806, 0x081a1806,
+	0x091a1805, 0x0a1b1704, 0x0a1b1704, 0x0b1c1603,
+	0x0b1c1603, 0x0c1c1503, 0x0d1c1502, 0x0d1d1402,
+	0x0e1d1401, 0x0f1d1301, 0x0f1d1301, 0x101e1200,
+};
+
+static int sun8i_vi_scaler_coef_index(unsigned int step)
+{
+	unsigned int scale, int_part, float_part;
+
+	scale = step >> (SUN8I_VI_SCALER_SCALE_FRAC - 3);
+	int_part = scale >> 3;
+	float_part = scale & 0x7;
+
+	switch (int_part) {
+	case 0:
+		return 0;
+	case 1:
+		return float_part;
+	case 2:
+		return 8 + (float_part >> 1);
+	case 3:
+		return 12;
+	case 4:
+		return 13;
+	default:
+		return 14;
+	}
+}
+
+static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer,
+				      u32 hstep, u32 vstep)
+{
+	int offset, i;
+
+	offset = sun8i_vi_scaler_coef_index(hstep) *
+			SUN8I_VI_SCALER_COEFF_COUNT;
+	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
+		regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF0(layer, i),
+			     lan3coefftab32_left[offset + i]);
+		regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(layer, i),
+			     lan3coefftab32_right[offset + i]);
+		regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(layer, i),
+			     lan3coefftab32_left[offset + i]);
+		regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(layer, i),
+			     lan3coefftab32_right[offset + i]);
+	}
+
+	offset = sun8i_vi_scaler_coef_index(hstep) *
+			SUN8I_VI_SCALER_COEFF_COUNT;
+	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
+		regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(layer, i),
+			     lan2coefftab32[offset + i]);
+		regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(layer, i),
+			     lan2coefftab32[offset + i]);
+	}
+}
+
+void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = SUN8I_SCALER_VSU_CTRL_EN |
+		      SUN8I_SCALER_VSU_CTRL_COEFF_RDY;
+	else
+		val = 0;
+
+	regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(layer), val);
+}
+
+void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
+			   u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
+			   u32 hscale, u32 vscale, u32 hphase, u32 vphase)
+{
+	u32 chphase, cvphase;
+	u32 insize, outsize;
+
+	hphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16;
+	vphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16;
+	hscale <<= SUN8I_VI_SCALER_SCALE_FRAC - 16;
+	vscale <<= SUN8I_VI_SCALER_SCALE_FRAC - 16;
+
+	insize = SUN8I_VI_SCALER_SIZE(src_w, src_h);
+	outsize = SUN8I_VI_SCALER_SIZE(dst_w, dst_h);
+
+	chphase = hphase;
+	cvphase = vphase;
+
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_OUTSIZE(layer), outsize);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_YINSIZE(layer), insize);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_YHSTEP(layer), hscale);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_YVSTEP(layer), vscale);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_YHPHASE(layer), hphase);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_YVPHASE(layer), vphase);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_CINSIZE(layer),
+		     SUN8I_VI_SCALER_SIZE(src_w, src_h));
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_CHSTEP(layer),
+		     hscale);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_CVSTEP(layer),
+		     vscale);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_CHPHASE(layer), chphase);
+	regmap_write(mixer->engine.regs,
+		     SUN8I_SCALER_VSU_CVPHASE(layer), cvphase);
+	sun8i_vi_scaler_set_coeff(mixer->engine.regs, layer,
+				  hscale, vscale);
+}
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
new file mode 100644
index 000000000000..09edb3491dc9
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _SUN8I_VI_SCALER_H_
+#define _SUN8I_VI_SCALER_H_
+
+#include "sun8i_mixer.h"
+
+/* this two macros assumes 16 fractional bits which is standard in DRM */
+#define SUN8I_VI_SCALER_SCALE_MIN		1
+#define SUN8I_VI_SCALER_SCALE_MAX		((1UL << 20) - 1)
+
+#define SUN8I_VI_SCALER_SCALE_FRAC		20
+#define SUN8I_VI_SCALER_PHASE_FRAC		20
+#define SUN8I_VI_SCALER_COEFF_COUNT		32
+#define SUN8I_VI_SCALER_SIZE(w, h)		(((h) - 1) << 16 | ((w) - 1))
+
+#define SUN8I_SCALER_VSU_CTRL(ch)	(0x20000 + 0x20000 * (ch) + 0x0)
+#define SUN8I_SCALER_VSU_OUTSIZE(ch)	(0x20000 + 0x20000 * (ch) + 0x40)
+#define SUN8I_SCALER_VSU_YINSIZE(ch)	(0x20000 + 0x20000 * (ch) + 0x80)
+#define SUN8I_SCALER_VSU_YHSTEP(ch)	(0x20000 + 0x20000 * (ch) + 0x88)
+#define SUN8I_SCALER_VSU_YVSTEP(ch)	(0x20000 + 0x20000 * (ch) + 0x8c)
+#define SUN8I_SCALER_VSU_YHPHASE(ch)	(0x20000 + 0x20000 * (ch) + 0x90)
+#define SUN8I_SCALER_VSU_YVPHASE(ch)	(0x20000 + 0x20000 * (ch) + 0x98)
+#define SUN8I_SCALER_VSU_CINSIZE(ch)	(0x20000 + 0x20000 * (ch) + 0xc0)
+#define SUN8I_SCALER_VSU_CHSTEP(ch)	(0x20000 + 0x20000 * (ch) + 0xc8)
+#define SUN8I_SCALER_VSU_CVSTEP(ch)	(0x20000 + 0x20000 * (ch) + 0xcc)
+#define SUN8I_SCALER_VSU_CHPHASE(ch)	(0x20000 + 0x20000 * (ch) + 0xd0)
+#define SUN8I_SCALER_VSU_CVPHASE(ch)	(0x20000 + 0x20000 * (ch) + 0xd8)
+#define SUN8I_SCALER_VSU_YHCOEFF0(ch, i) \
+	(0x20000 + 0x20000 * (ch) + 0x200 + 0x4 * (i))
+#define SUN8I_SCALER_VSU_YHCOEFF1(ch, i) \
+	(0x20000 + 0x20000 * (ch) + 0x300 + 0x4 * (i))
+#define SUN8I_SCALER_VSU_YVCOEFF(ch, i) \
+	(0x20000 + 0x20000 * (ch) + 0x400 + 0x4 * (i))
+#define SUN8I_SCALER_VSU_CHCOEFF0(ch, i) \
+	(0x20000 + 0x20000 * (ch) + 0x600 + 0x4 * (i))
+#define SUN8I_SCALER_VSU_CHCOEFF1(ch, i) \
+	(0x20000 + 0x20000 * (ch) + 0x700 + 0x4 * (i))
+#define SUN8I_SCALER_VSU_CVCOEFF(ch, i) \
+	(0x20000 + 0x20000 * (ch) + 0x800 + 0x4 * (i))
+
+#define SUN8I_SCALER_VSU_CTRL_EN		BIT(0)
+#define SUN8I_SCALER_VSU_CTRL_COEFF_RDY		BIT(4)
+
+void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
+void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
+			   u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
+			   u32 hscale, u32 vscale, u32 hphase, u32 vphase);
+
+#endif
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 22/27] drm/sun4i: Add CCSC property to DE2 configuration
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Base addresses of channel output CSC (CCSC) depends whether mixer in
question is first or second and if it is second, if supports VEP or not.
This new property will tell which set of base addresses to take.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c | 1 +
 drivers/gpu/drm/sun4i/sun8i_mixer.h | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index e3296a060352..7f88cf5f97eb 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -337,6 +337,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
 	.vi_num = 2,
 	.ui_num = 1,
 	.scaler_mask = 0x3,
+	.ccsc = 0,
 };
 
 static const struct of_device_id sun8i_mixer_of_table[] = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 9bbd762d26f7..994090858b18 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -98,11 +98,16 @@ struct de2_fmt_info {
  *	First, scaler supports for VI channels is defined and after that, scaler
  *	support for UI channels. For example, if mixer has 2 VI channels without
  *	scaler and 2 UI channels with scaler, bitmask would be 0xC.
+ * @ccsc: select set of CCSC base addresses
+ *	Set value to 0 if this is first mixer or second mixer with VEP support.
+ *	Set value to 1 if this is second mixer without VEP support. Other values
+ *	are invalid.
  */
 struct sun8i_mixer_cfg {
 	int		vi_num;
 	int		ui_num;
 	int		scaler_mask;
+	int		ccsc;
 };
 
 struct sun8i_mixer {
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 23/27] drm/sun4i: Add DE2 CSC library
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

DE2 have many CSC units - channel input CSC, channel output CSC and
mixer output CSC and maybe more.

Fortunately, they have all same register layout, only base offsets
differs.

Add support only for channel output CSC for now.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/Makefile    |  2 +-
 drivers/gpu/drm/sun4i/sun8i_csc.c | 93 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_csc.h | 36 +++++++++++++++
 3 files changed, 130 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_csc.c
 create mode 100644 drivers/gpu/drm/sun4i/sun8i_csc.h

diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index a458ddbf4a8e..82a6ac57fbe3 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -11,7 +11,7 @@ sun4i-drm-hdmi-y		+= sun4i_hdmi_tmds_clk.o
 
 sun8i-mixer-y			+= sun8i_mixer.o sun8i_ui_layer.o \
 				   sun8i_vi_layer.o sun8i_ui_scaler.o \
-				   sun8i_vi_scaler.o
+				   sun8i_vi_scaler.o sun8i_csc.o
 
 sun4i-tcon-y			+= sun4i_crtc.o
 sun4i-tcon-y			+= sun4i_dotclock.o
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
new file mode 100644
index 000000000000..b14925b40ccf
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+
+#include "sun8i_csc.h"
+#include "sun8i_mixer.h"
+
+static const u32 ccsc_base[2][2] = {
+	{CCSC00_OFFSET, CCSC01_OFFSET},
+	{CCSC10_OFFSET, CCSC11_OFFSET},
+};
+
+/*
+ * Factors are in two's complement format, 10 bits for fractinal part.
+ * First tree values in each line are multiplication factor and last
+ * value is constant, which is added at the end.
+ */
+static const u32 yuv2rgb[] = {
+	0x000004A8, 0x00000000, 0x00000662, 0xFFFC845A,
+	0x000004A8, 0xFFFFFE6F, 0xFFFFFCBF, 0x00021DF4,
+	0x000004A8, 0x00000813, 0x00000000, 0xFFFBAC4A,
+};
+
+static const u32 yvu2rgb[] = {
+	0x000004A8, 0x00000662, 0x00000000, 0xFFFC845A,
+	0x000004A8, 0xFFFFFCBF, 0xFFFFFE6F, 0x00021DF4,
+	0x000004A8, 0x00000000, 0x00000813, 0xFFFBAC4A,
+};
+
+static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
+				       enum sun8i_csc_mode mode)
+{
+	const u32 *table;
+	int i, data;
+
+	switch (mode) {
+	case SUN8I_CSC_MODE_YUV2RGB:
+		table = yuv2rgb;
+		break;
+	case SUN8I_CSC_MODE_YVU2RGB:
+		table = yvu2rgb;
+		break;
+	default:
+		DRM_WARN("Wrong CSC mode specified.\n");
+		return;
+	}
+
+	for (i = 0; i < 12; i++) {
+		data = table[i];
+		/* For some reason, 0x200 must be added to constant parts */
+		if (((i + 1) & 3) == 0)
+			data += 0x200;
+		regmap_write(map, SUN8I_CSC_COEFF(base, i), data);
+	}
+}
+
+static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = SUN8I_CSC_CTRL_EN;
+	else
+		val = 0;
+
+	regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val);
+}
+
+void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
+				     enum sun8i_csc_mode mode)
+{
+	u32 base;
+
+	base = ccsc_base[mixer->cfg->ccsc][layer];
+
+	sun8i_csc_set_coefficients(mixer->engine.regs, base, mode);
+}
+
+void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable)
+{
+	u32 base;
+
+	base = ccsc_base[mixer->cfg->ccsc][layer];
+
+	sun8i_csc_enable(mixer->engine.regs, base, enable);
+}
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h
new file mode 100644
index 000000000000..880e8fbb0855
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _SUN8I_CSC_H_
+#define _SUN8I_CSC_H_
+
+struct sun8i_mixer;
+
+/* VI channel CSC units offsets */
+#define CCSC00_OFFSET 0xAA050
+#define CCSC01_OFFSET 0xFA000
+#define CCSC10_OFFSET 0xA0000
+#define CCSC11_OFFSET 0xF0000
+
+#define SUN8I_CSC_CTRL(base)		(base + 0x0)
+#define SUN8I_CSC_COEFF(base, i)	(base + 0x10 + 4 * i)
+
+#define SUN8I_CSC_CTRL_EN		BIT(0)
+
+enum sun8i_csc_mode {
+	SUN8I_CSC_MODE_OFF,
+	SUN8I_CSC_MODE_YUV2RGB,
+	SUN8I_CSC_MODE_YVU2RGB,
+};
+
+void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
+				     enum sun8i_csc_mode mode);
+void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable);
+
+#endif
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 24/27] drm/sun4i: Add DE2 definitions for YUV formats
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

This commit expands translation of DRM YUV format to HW specific
information.

It doesn't do any functional changes.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c    | 136 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h    |  23 +++++-
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c |   2 +-
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c |   2 +-
 4 files changed, 159 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 7f88cf5f97eb..29ceeb016d72 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -34,82 +34,218 @@ static const struct de2_fmt_info de2_formats[] = {
 	{
 		.drm_fmt = DRM_FORMAT_ARGB8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_ABGR8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_RGBA8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_BGRA8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_XRGB8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_XBGR8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_RGBX8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_BGRX8888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_RGB888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_BGR888,
 		.de2_fmt = SUN8I_MIXER_FBFMT_BGR888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_RGB565,
 		.de2_fmt = SUN8I_MIXER_FBFMT_RGB565,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_BGR565,
 		.de2_fmt = SUN8I_MIXER_FBFMT_BGR565,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_ARGB4444,
 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_ABGR4444,
 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_RGBA4444,
 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_BGRA4444,
 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_ARGB1555,
 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_ABGR1555,
 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_RGBA5551,
 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
 	},
 	{
 		.drm_fmt = DRM_FORMAT_BGRA5551,
 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_OFF,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_UYVY,
+		.de2_fmt = SUN8I_MIXER_FBFMT_UYVY,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_VYUY,
+		.de2_fmt = SUN8I_MIXER_FBFMT_VYUY,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YUYV,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUYV,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YVYU,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YVYU,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_NV16,
+		.de2_fmt = SUN8I_MIXER_FBFMT_NV16,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_NV61,
+		.de2_fmt = SUN8I_MIXER_FBFMT_NV61,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_NV12,
+		.de2_fmt = SUN8I_MIXER_FBFMT_NV12,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_NV21,
+		.de2_fmt = SUN8I_MIXER_FBFMT_NV21,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YUV444,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YUV422,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YUV420,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YUV411,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YUV2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YVU444,
+		.de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
+		.rgb = true,
+		.csc = SUN8I_CSC_MODE_YVU2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YVU422,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YVU2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YVU420,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YVU2RGB,
+	},
+	{
+		.drm_fmt = DRM_FORMAT_YVU411,
+		.de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
+		.rgb = false,
+		.csc = SUN8I_CSC_MODE_YVU2RGB,
 	},
 };
 
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 994090858b18..bc58040a88f9 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -14,6 +14,7 @@
 #include <linux/regmap.h>
 #include <linux/reset.h>
 
+#include "sun8i_csc.h"
 #include "sunxi_engine.h"
 
 #define SUN8I_MIXER_SIZE(w, h)			(((h) - 1) << 16 | ((w) - 1))
@@ -73,6 +74,22 @@
 #define SUN8I_MIXER_FBFMT_RGBA5551	18
 #define SUN8I_MIXER_FBFMT_BGRA5551	19
 
+#define SUN8I_MIXER_FBFMT_YUYV		0
+#define SUN8I_MIXER_FBFMT_UYVY		1
+#define SUN8I_MIXER_FBFMT_YVYU		2
+#define SUN8I_MIXER_FBFMT_VYUY		3
+#define SUN8I_MIXER_FBFMT_NV16		4
+#define SUN8I_MIXER_FBFMT_NV61		5
+#define SUN8I_MIXER_FBFMT_YUV422	6
+/* format 7 doesn't exist */
+#define SUN8I_MIXER_FBFMT_NV12		8
+#define SUN8I_MIXER_FBFMT_NV21		9
+#define SUN8I_MIXER_FBFMT_YUV420	10
+/* format 11 doesn't exist */
+/* format 12 is semi-planar YUV411 UVUV */
+/* format 13 is semi-planar YUV411 VUVU */
+#define SUN8I_MIXER_FBFMT_YUV411	14
+
 /*
  * These sub-engines are still unknown now, the EN registers are here only to
  * be used to disable these sub-engines.
@@ -86,8 +103,10 @@
 #define SUN8I_MIXER_DCSC_EN			0xb0000
 
 struct de2_fmt_info {
-	u32 drm_fmt;
-	u32 de2_fmt;
+	u32			drm_fmt;
+	u32			de2_fmt;
+	bool			rgb;
+	enum sun8i_csc_mode	csc;
 };
 
 /**
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index 3cf0729cc7e9..fd76ffd40580 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -154,7 +154,7 @@ static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
 	u32 val;
 
 	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
-	if (!fmt_info) {
+	if (!fmt_info || !fmt_info->rgb) {
 		DRM_DEBUG_DRIVER("Invalid format\n");
 		return -EINVAL;
 	}
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index e47a0c1f640d..5a09abec189b 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -118,7 +118,7 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
 	u32 val;
 
 	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
-	if (!fmt_info) {
+	if (!fmt_info || !fmt_info->rgb) {
 		DRM_DEBUG_DRIVER("Invalid format\n");
 		return -EINVAL;
 	}
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 25/27] drm/sun4i: Expand DE2 scaler lib with YUV support
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Basic principle of operation when using YUV framebuffer is that chroma
planes have to be upscaled to same size as luma.

Because of that, expand DE2 scaler library to support that.

BSP driver uses another set of FIR filter coefficients for YUV planes.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c  |   4 +-
 drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 458 +++++++++++++++++++++++++++++++-
 drivers/gpu/drm/sun4i/sun8i_vi_scaler.h |   4 +-
 3 files changed, 453 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 5a09abec189b..02aaa7d3fceb 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -51,6 +51,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 				       int overlay, struct drm_plane *plane)
 {
 	struct drm_plane_state *state = plane->state;
+	const struct drm_format_info *format = state->fb->format;
 	u32 src_w, src_h, dst_w, dst_h;
 	u32 outsize, insize;
 	u32 hphase, vphase;
@@ -89,7 +90,8 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 		vscale = state->src_h / state->crtc_h;
 
 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
-				      dst_h, hscale, vscale, hphase, vphase);
+				      dst_h, hscale, vscale, hphase, vphase,
+				      format);
 		sun8i_vi_scaler_enable(mixer, channel, true);
 	} else {
 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
index 68b92e46be68..d3f1acb234b7 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
@@ -422,6 +422,417 @@ static const u32 lan2coefftab32[480] = {
 	0x0e1d1401, 0x0f1d1301, 0x0f1d1301, 0x101e1200,
 };
 
+static const u32 bicubic8coefftab32_left[480] = {
+	0x40000000, 0x40ff0000, 0x3ffe0000, 0x3efe0000,
+	0x3dfd0000, 0x3cfc0000, 0x3bfc0000, 0x39fc0000,
+	0x36fc0000, 0x35fb0000, 0x33fb0000, 0x31fb0000,
+	0x2ffb0000, 0x2cfb0000, 0x29fc0000, 0x27fc0000,
+	0x24fc0000, 0x21fc0000, 0x1efd0000, 0x1cfd0000,
+	0x19fd0000, 0x16fe0000, 0x14fe0000, 0x11fe0000,
+	0x0dff0000, 0x0cff0000, 0x0aff0000, 0x08ff0000,
+	0x05000000, 0x03000000, 0x02000000, 0x01000000,
+
+	0x3904ff00, 0x3903ff00, 0x3902ff00, 0x38010000,
+	0x37000000, 0x36ff0000, 0x35ff0000, 0x34fe0000,
+	0x32fe0000, 0x31fd0000, 0x30fd0000, 0x2efc0000,
+	0x2cfc0000, 0x2afc0000, 0x28fc0000, 0x26fc0000,
+	0x24fc0000, 0x22fc0000, 0x20fc0000, 0x1efc0000,
+	0x1cfc0000, 0x19fc0000, 0x17fc0000, 0x15fd0000,
+	0x12fd0000, 0x11fd0000, 0x0ffd0000, 0x0dfe0000,
+	0x0bfe0000, 0x09fe0000, 0x08fe0000, 0x06ff0000,
+
+	0x3209fe00, 0x3407fe00, 0x3306fe00, 0x3305fe00,
+	0x3204fe00, 0x3102ff00, 0x3102ff00, 0x3001ff00,
+	0x2f00ff00, 0x2effff00, 0x2cff0000, 0x2bfe0000,
+	0x29fe0000, 0x28fe0000, 0x26fd0000, 0x24fd0000,
+	0x23fd0000, 0x21fd0000, 0x20fc0000, 0x1efc0000,
+	0x1dfc0000, 0x1bfc0000, 0x19fc0000, 0x17fc0000,
+	0x16fc0000, 0x14fc0000, 0x12fc0000, 0x10fd0000,
+	0x0ffd0000, 0x0dfd0000, 0x0cfd0000, 0x0afd0000,
+
+	0x2e0cfd00, 0x2e0bfd00, 0x2e09fd00, 0x2e08fd00,
+	0x2e07fd00, 0x2c06fe00, 0x2c05fe00, 0x2b04fe00,
+	0x2b03fe00, 0x2a02fe00, 0x2901fe00, 0x2701ff00,
+	0x2700ff00, 0x26ffff00, 0x24ffff00, 0x23ffff00,
+	0x22feff00, 0x20fe0000, 0x1ffe0000, 0x1efd0000,
+	0x1dfd0000, 0x1bfd0000, 0x1afd0000, 0x19fd0000,
+	0x17fd0000, 0x15fd0000, 0x13fd0000, 0x12fd0000,
+	0x11fd0000, 0x10fd0000, 0x0ffd0000, 0x0cfd0000,
+
+	0x2a0efd00, 0x2a0dfd00, 0x2a0cfd00, 0x290bfd00,
+	0x290afd00, 0x2909fd00, 0x2908fd00, 0x2807fd00,
+	0x2706fd00, 0x2705fd00, 0x2604fe00, 0x2603fe00,
+	0x2502fe00, 0x2402fe00, 0x2401fe00, 0x2200fe00,
+	0x2200fe00, 0x2000ff00, 0x1fffff00, 0x1effff00,
+	0x1dfeff00, 0x1cfeff00, 0x1afeff00, 0x19feff00,
+	0x17fe0000, 0x16fd0000, 0x15fd0000, 0x14fd0000,
+	0x12fd0000, 0x11fd0000, 0x10fd0000, 0x0ffd0000,
+
+	0x2610fd00, 0x260ffd00, 0x260efd00, 0x260dfd00,
+	0x260cfd00, 0x260bfd00, 0x260afd00, 0x2609fd00,
+	0x2508fd00, 0x2507fd00, 0x2406fd00, 0x2406fd00,
+	0x2305fd00, 0x2304fd00, 0x2203fe00, 0x2103fe00,
+	0x2002fe00, 0x1f01fe00, 0x1e01fe00, 0x1e00fe00,
+	0x1c00fe00, 0x1b00fe00, 0x1afffe00, 0x19ffff00,
+	0x18ffff00, 0x17feff00, 0x16feff00, 0x15feff00,
+	0x14feff00, 0x13feff00, 0x11feff00, 0x10fd0000,
+
+	0x2411feff, 0x2410feff, 0x240ffeff, 0x230efeff,
+	0x240dfeff, 0x240cfeff, 0x230cfd00, 0x230bfd00,
+	0x230afd00, 0x2309fd00, 0x2208fd00, 0x2108fd00,
+	0x2007fd00, 0x2106fd00, 0x2005fd00, 0x1f05fd00,
+	0x1f04fd00, 0x1e03fd00, 0x1d03fe00, 0x1c02fe00,
+	0x1b02fe00, 0x1a01fe00, 0x1a01fe00, 0x1900fe00,
+	0x1800fe00, 0x1700fe00, 0x16fffe00, 0x15fffe00,
+	0x13ffff00, 0x12ffff00, 0x12feff00, 0x11feff00,
+
+	0x2212fffe, 0x2211fffe, 0x2210ffff, 0x220ffeff,
+	0x220efeff, 0x210efeff, 0x210dfeff, 0x210cfeff,
+	0x210bfeff, 0x200bfeff, 0x200afeff, 0x1f09feff,
+	0x1f08feff, 0x1d08fe00, 0x1e07fd00, 0x1e06fd00,
+	0x1d06fd00, 0x1c05fd00, 0x1b04fe00, 0x1a04fe00,
+	0x1a03fe00, 0x1903fe00, 0x1802fe00, 0x1802fe00,
+	0x1701fe00, 0x1601fe00, 0x1501fe00, 0x1500fe00,
+	0x1400fe00, 0x1400fe00, 0x13fffe00, 0x12fffe00,
+
+	0x201200fe, 0x201100fe, 0x1f1100fe, 0x2010fffe,
+	0x200ffffe, 0x1f0ffffe, 0x1f0efffe, 0x1e0dffff,
+	0x1f0cfeff, 0x1e0cfeff, 0x1e0bfeff, 0x1e0afeff,
+	0x1d0afeff, 0x1d09feff, 0x1c08feff, 0x1b08feff,
+	0x1b07feff, 0x1a07feff, 0x1a06feff, 0x1a05feff,
+	0x1805fe00, 0x1904fe00, 0x1704fe00, 0x1703fe00,
+	0x1603fe00, 0x1602fe00, 0x1402fe00, 0x1402fe00,
+	0x1401fe00, 0x1301fe00, 0x1201fe00, 0x1200fe00,
+
+	0x1c1202fe, 0x1c1102fe, 0x1b1102fe, 0x1c1001fe,
+	0x1b1001fe, 0x1c0f01fe, 0x1b0f00fe, 0x1b0e00fe,
+	0x1b0e00fe, 0x1b0d00fe, 0x1b0c00fe, 0x1a0cfffe,
+	0x1a0bfffe, 0x1a0bfffe, 0x190afffe, 0x190afffe,
+	0x1909fffe, 0x1709ffff, 0x1808ffff, 0x1708feff,
+	0x1707feff, 0x1707feff, 0x1606feff, 0x1506feff,
+	0x1505feff, 0x1505feff, 0x1404feff, 0x1404feff,
+	0x1404feff, 0x1303feff, 0x1203feff, 0x1202feff,
+
+	0x191104fe, 0x191104fe, 0x191003fe, 0x191003fe,
+	0x171003fe, 0x180f03fe, 0x180f02fe, 0x180e02fe,
+	0x180e02fe, 0x180d01fe, 0x180d01fe, 0x180d01fe,
+	0x170c01fe, 0x160c01fe, 0x170b00fe, 0x170b00fe,
+	0x160a00fe, 0x160a00fe, 0x160a00fe, 0x150900fe,
+	0x1509fffe, 0x1508fffe, 0x1508fffe, 0x1408fffe,
+	0x1407fffe, 0x1307ffff, 0x1306ffff, 0x1206ffff,
+	0x1206ffff, 0x1205ffff, 0x1205ffff, 0x1104feff,
+
+	0x161006ff, 0x161005ff, 0x161005ff, 0x160f05ff,
+	0x160f04ff, 0x150f04ff, 0x150e04ff, 0x150e04ff,
+	0x150e03ff, 0x150d03ff, 0x150d03ff, 0x150d02ff,
+	0x140c02ff, 0x150c02fe, 0x150c02fe, 0x150b02fe,
+	0x140b01fe, 0x140b01fe, 0x140a01fe, 0x140a01fe,
+	0x140a01fe, 0x130900fe, 0x130900fe, 0x130900fe,
+	0x130800fe, 0x120800fe, 0x120800fe, 0x120700fe,
+	0x120700fe, 0x1107fffe, 0x1106fffe, 0x1106fffe,
+
+	0x140f0700, 0x140f0600, 0x140f0600, 0x140f0600,
+	0x140e0600, 0x130e0500, 0x140e05ff, 0x130e05ff,
+	0x140d05ff, 0x130d04ff, 0x130d04ff, 0x120d04ff,
+	0x130c04ff, 0x130c03ff, 0x130c03ff, 0x120c03ff,
+	0x120b03ff, 0x120b02ff, 0x120b02ff, 0x120a02ff,
+	0x120a02ff, 0x110a02ff, 0x110a01ff, 0x120901ff,
+	0x100901ff, 0x100901ff, 0x110801ff, 0x110801ff,
+	0x100800ff, 0x100800ff, 0x100700ff, 0x100700fe,
+
+	0x120f0701, 0x120e0701, 0x120e0701, 0x120e0701,
+	0x120e0600, 0x110e0600, 0x120d0600, 0x120d0600,
+	0x120d0500, 0x120d0500, 0x110d0500, 0x110c0500,
+	0x110c0500, 0x110c0400, 0x110c0400, 0x110b04ff,
+	0x110b04ff, 0x110b04ff, 0x110b03ff, 0x110b03ff,
+	0x110a03ff, 0x110a03ff, 0x100a03ff, 0x110a02ff,
+	0x100902ff, 0x100902ff, 0x100902ff, 0x0f0902ff,
+	0x0e0902ff, 0x100801ff, 0x0f0801ff, 0x0f0801ff,
+
+	0x100e0802, 0x100e0802, 0x110e0702, 0x110d0701,
+	0x110d0701, 0x100d0701, 0x100d0701, 0x110d0601,
+	0x110d0601, 0x110c0601, 0x110c0601, 0x100c0600,
+	0x100c0500, 0x100c0500, 0x100c0500, 0x100b0500,
+	0x100b0500, 0x100b0400, 0x100b0400, 0x0f0b0400,
+	0x100a0400, 0x0f0a0400, 0x0f0a0400, 0x0f0a0300,
+	0x0f0a03ff, 0x0f0903ff, 0x0f0903ff, 0x0f0903ff,
+	0x0f0903ff, 0x0f0902ff, 0x0f0902ff, 0x0f0802ff
+};
+
+static const u32 bicubic8coefftab32_right[480] = {
+	0x00000000, 0x00000001, 0x00000003, 0x00000004,
+	0x00000006, 0x0000ff09, 0x0000ff0a, 0x0000ff0c,
+	0x0000ff0f, 0x0000fe12, 0x0000fe14, 0x0000fe16,
+	0x0000fd19, 0x0000fd1c, 0x0000fd1e, 0x0000fc21,
+	0x0000fc24, 0x0000fc27, 0x0000fc29, 0x0000fb2c,
+	0x0000fb2f, 0x0000fb31, 0x0000fb33, 0x0000fb36,
+	0x0000fc38, 0x0000fc39, 0x0000fc3b, 0x0000fc3d,
+	0x0000fd3e, 0x0000fe3f, 0x0000fe40, 0x0000ff40,
+
+	0x0000ff05, 0x0000ff06, 0x0000fe08, 0x0000fe09,
+	0x0000fe0b, 0x0000fe0d, 0x0000fd0f, 0x0000fd11,
+	0x0000fd13, 0x0000fd15, 0x0000fc17, 0x0000fc1a,
+	0x0000fc1c, 0x0000fc1e, 0x0000fc20, 0x0000fc22,
+	0x0000fc24, 0x0000fc26, 0x0000fc28, 0x0000fc2a,
+	0x0000fc2c, 0x0000fc2f, 0x0000fd30, 0x0000fd31,
+	0x0000fe33, 0x0000fe34, 0x0000ff35, 0x0000ff36,
+	0x00000037, 0x00000138, 0x00ff0239, 0x00ff0339,
+
+	0x0000fe09, 0x0000fd0a, 0x0000fd0c, 0x0000fd0d,
+	0x0000fd0f, 0x0000fd11, 0x0000fc12, 0x0000fc14,
+	0x0000fc16, 0x0000fc18, 0x0000fc19, 0x0000fc1b,
+	0x0000fc1d, 0x0000fc1e, 0x0000fc21, 0x0000fd22,
+	0x0000fd23, 0x0000fd25, 0x0000fd27, 0x0000fe28,
+	0x0000fe29, 0x0000fe2b, 0x0000ff2c, 0x00ffff2f,
+	0x00ff002f, 0x00ff0130, 0x00ff0231, 0x00ff0232,
+	0x00fe0432, 0x00fe0533, 0x00fe0633, 0x00fe0734,
+
+	0x0000fd0c, 0x0000fd0d, 0x0000fd0f, 0x0000fd10,
+	0x0000fd11, 0x0000fd13, 0x0000fd14, 0x0000fd16,
+	0x0000fd17, 0x0000fd19, 0x0000fd1b, 0x0000fd1c,
+	0x0000fd1d, 0x0000fd1f, 0x0000fe20, 0x0000fe21,
+	0x00fffe24, 0x00ffff24, 0x00ffff25, 0x00ffff27,
+	0x00ff0027, 0x00ff0128, 0x00fe012a, 0x00fe022a,
+	0x00fe032b, 0x00fe042c, 0x00fe052d, 0x00fe062d,
+	0x00fd072e, 0x00fd082e, 0x00fd092e, 0x00fd0b2f,
+
+	0x0000fd0e, 0x0000fd0f, 0x0000fd10, 0x0000fd12,
+	0x0000fd13, 0x0000fd14, 0x0000fd15, 0x0000fd17,
+	0x0000fe18, 0x00fffe1a, 0x00fffe1b, 0x00fffe1c,
+	0x00fffe1e, 0x00ffff1e, 0x00ffff1f, 0x00ff0021,
+	0x00fe0022, 0x00fe0023, 0x00fe0124, 0x00fe0224,
+	0x00fe0226, 0x00fe0326, 0x00fe0427, 0x00fd0528,
+	0x00fd0628, 0x00fd0729, 0x00fd0829, 0x00fd0929,
+	0x00fd0a2a, 0x00fd0b2a, 0x00fd0c2a, 0x00fd0d2a,
+
+	0x0000fd10, 0x0000fd11, 0x00fffe12, 0x00fffe13,
+	0x00fffe14, 0x00fffe15, 0x00fffe16, 0x00fffe17,
+	0x00ffff18, 0x00ffff19, 0x00feff1c, 0x00fe001b,
+	0x00fe001d, 0x00fe001e, 0x00fe011e, 0x00fe011f,
+	0x00fe0220, 0x00fe0321, 0x00fe0322, 0x00fd0423,
+	0x00fd0524, 0x00fd0624, 0x00fd0626, 0x00fd0725,
+	0x00fd0825, 0x00fd0926, 0x00fd0a26, 0x00fd0b26,
+	0x00fd0c26, 0x00fd0d26, 0x00fd0e27, 0x00fd0f27,
+
+	0x00fffe11, 0x00fffe12, 0x00fffe13, 0x00ffff14,
+	0x00ffff14, 0x00feff16, 0x00feff17, 0x00fe0017,
+	0x00fe0018, 0x00fe0019, 0x00fe011a, 0x00fe011b,
+	0x00fe021c, 0x00fe021c, 0x00fe031d, 0x00fd031f,
+	0x00fd041f, 0x00fd0520, 0x00fd0520, 0x00fd0621,
+	0x00fd0721, 0x00fd0822, 0x00fd0822, 0x00fd0923,
+	0x00fd0a23, 0x00fd0b23, 0x00fd0b25, 0x00fe0c24,
+	0x00fe0d24, 0x00fe0e24, 0x00fe0f24, 0x00fe1024,
+
+	0x00feff12, 0x00feff13, 0x00feff13, 0x00fe0014,
+	0x00fe0015, 0x00fe0016, 0x00fe0116, 0x00fe0117,
+	0x00fe0118, 0x00fe0218, 0x00fe0219, 0x00fe031a,
+	0x00fe031b, 0x00fe041b, 0x00fd041d, 0x00fd051d,
+	0x00fd061d, 0x00fd061f, 0x00fe071e, 0x00fe081e,
+	0x00fe081f, 0x00fe091f, 0x00fe0a20, 0x00fe0a20,
+	0x00fe0b21, 0x00fe0c21, 0x00fe0d21, 0x00fe0d22,
+	0x00fe0e22, 0x00fe0f21, 0x00ff1021, 0x00ff1022,
+
+	0x00fe0012, 0x00fe0013, 0x00fe0113, 0x00fe0114,
+	0x00fe0115, 0x00fe0215, 0x00fe0216, 0x00fe0217,
+	0x00fe0317, 0x00fe0318, 0x00fe0418, 0x00fe0419,
+	0x00fe0519, 0x00fe051a, 0x00fe061b, 0x00fe071b,
+	0x00fe071c, 0x00fe081c, 0x00fe081d, 0x00fe091d,
+	0x00fe0a1d, 0x00fe0a1d, 0x00fe0b1e, 0x00fe0c1e,
+	0x00ff0c1e, 0x00ff0d1e, 0x00ff0e1f, 0x00ff0e1f,
+	0x00ff0f1f, 0x00ff0f20, 0x0000101f, 0x0000111f,
+
+	0x00fe0212, 0x00fe0312, 0x00fe0313, 0x00fe0314,
+	0x00fe0414, 0x00fe0414, 0x00fe0515, 0x00fe0516,
+	0x00fe0516, 0x00fe0616, 0x00fe0617, 0x00fe0718,
+	0x00fe0719, 0x00fe0818, 0x00ff0819, 0x00ff0918,
+	0x00ff0919, 0x00ff0a19, 0x00ff0a19, 0x00ff0b1a,
+	0x00ff0b1b, 0x00ff0c1a, 0x00000c1b, 0x00000d1b,
+	0x00000d1c, 0x00000e1b, 0x00000e1d, 0x00010f1b,
+	0x00010f1b, 0x0001101c, 0x0001101d, 0x0002111c,
+
+	0x00fe0412, 0x00fe0412, 0x00ff0512, 0x00ff0512,
+	0x00ff0613, 0x00ff0613, 0x00ff0614, 0x00ff0714,
+	0x00ff0714, 0x00ff0815, 0x00ff0815, 0x00ff0815,
+	0x00ff0916, 0x00000916, 0x00000a16, 0x00000a16,
+	0x00000a18, 0x00000b17, 0x00000b17, 0x00010c17,
+	0x00010c18, 0x00010d18, 0x00010d18, 0x00010d19,
+	0x00020e18, 0x00020e18, 0x00020f18, 0x00030f18,
+	0x00030f18, 0x00031018, 0x00031018, 0x00041119,
+
+	0x00ff0610, 0x00ff0611, 0x00ff0611, 0x00ff0711,
+	0x00000711, 0x00000712, 0x00000812, 0x00000812,
+	0x00000813, 0x00000913, 0x00000913, 0x00000914,
+	0x00010a14, 0x00010a14, 0x00010a14, 0x00010b14,
+	0x00010b16, 0x00020b15, 0x00020c15, 0x00020c15,
+	0x00020c15, 0x00020d17, 0x00030d16, 0x00030d16,
+	0x00030e16, 0x00040e16, 0x00040e16, 0x00040f16,
+	0x00040f16, 0x00050f17, 0x00051017, 0x00051017,
+
+	0x0000070f, 0x00000710, 0x00000710, 0x00000710,
+	0x00000810, 0x00010811, 0x00010811, 0x00010911,
+	0x00010911, 0x00010913, 0x00010913, 0x00020a12,
+	0x00020a12, 0x00020a13, 0x00020b12, 0x00020b13,
+	0x00030b13, 0x00030c13, 0x00030c13, 0x00030c14,
+	0x00040c13, 0x00040d13, 0x00040d14, 0x00040d14,
+	0x00050e14, 0x00050e14, 0x00050e14, 0x00050e14,
+	0x00060f14, 0x00060f14, 0x00060f15, 0x00061015,
+
+	0x0001070f, 0x0001080f, 0x0001080f, 0x0001080f,
+	0x00010811, 0x00020910, 0x00020910, 0x00020910,
+	0x00020911, 0x00020a10, 0x00030a10, 0x00030a11,
+	0x00030a11, 0x00030b11, 0x00030b11, 0x00040b12,
+	0x00040b12, 0x00040c11, 0x00040c12, 0x00040c12,
+	0x00050c12, 0x00050c12, 0x00050d12, 0x00050d12,
+	0x00060d13, 0x00060d13, 0x00060e12, 0x00060e13,
+	0x00070e13, 0x00070e13, 0x00070f13, 0x00070f13,
+
+	0x0002080e, 0x0002080e, 0x0002080e, 0x00020810,
+	0x0002090f, 0x0003090f, 0x0003090f, 0x0003090f,
+	0x0003090f, 0x00030a0f, 0x00030a0f, 0x00040a10,
+	0x00040a11, 0x00040b10, 0x00040b10, 0x00040b11,
+	0x00050b10, 0x00050b11, 0x00050c10, 0x00050c11,
+	0x00050c11, 0x00060c11, 0x00060c11, 0x00060d11,
+	0x00060d12, 0x00070d12, 0x00070d12, 0x00070e11,
+	0x00070e11, 0x00070e12, 0x00080e11, 0x00080e12
+};
+
+static const u32 bicubic4coefftab32[480] = {
+	0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
+	0x00063dfd, 0xff083dfc, 0xff0a3bfc, 0xff0c39fc,
+	0xff0e37fc, 0xfe1136fb, 0xfe1433fb, 0xfe1631fb,
+	0xfd192ffb, 0xfd1c2cfb, 0xfd1e29fc, 0xfc2127fc,
+	0xfc2424fc, 0xfc2721fc, 0xfc291efd, 0xfb2c1cfd,
+	0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfb3611fe,
+	0xfc370eff, 0xfc390cff, 0xfc3b0aff, 0xfc3d08ff,
+	0xfd3d0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
+
+	0xfe053904, 0xfe063903, 0xfe083901, 0xfe0a3800,
+	0xfd0b3800, 0xfe0d36ff, 0xfd0f35ff, 0xfd1134fe,
+	0xfd1332fe, 0xfd1531fd, 0xfc1730fd, 0xfc1a2efc,
+	0xfc1c2cfc, 0xfc1e2afc, 0xfc2028fc, 0xfc2226fc,
+	0xfc2424fc, 0xfc2622fc, 0xfc2820fc, 0xfc2a1efc,
+	0xfc2c1cfc, 0xfc2e1afc, 0xfd3017fc, 0xfd3115fd,
+	0xfe3213fd, 0xfe3411fd, 0xff350ffd, 0xff360dfe,
+	0x00370bfe, 0x013809fe, 0x023808fe, 0x033806ff,
+
+	0xfd093208, 0xfd0a3207, 0xfd0c3205, 0xfd0d3204,
+	0xfc0f3203, 0xfc113102, 0xfc123002, 0xfc143000,
+	0xfc152f00, 0xfc172d00, 0xfc192cff, 0xfc1b2bfe,
+	0xfc1d29fe, 0xfc1e28fe, 0xfc2027fd, 0xfd2125fd,
+	0xfd2323fd, 0xfd2521fd, 0xfd2720fc, 0xfe281efc,
+	0xfe291dfc, 0xfe2b1bfc, 0xff2c19fc, 0x002d17fc,
+	0x002e16fc, 0x012f14fc, 0x022f12fd, 0x023110fd,
+	0x03310ffd, 0x05310dfd, 0x06320bfd, 0x07320afd,
+
+	0xfc0c2d0b, 0xfc0d2d0a, 0xfc0e2d09, 0xfc102d07,
+	0xfc112c07, 0xfc132c05, 0xfc142c04, 0xfc162b03,
+	0xfc172a03, 0xfc192a01, 0xfc1a2901, 0xfd1b2800,
+	0xfd1c2700, 0xfd1e2500, 0xfe1f24ff, 0xfe2023ff,
+	0xfe2222fe, 0xff2320fe, 0xff241ffe, 0x00251efd,
+	0x00271cfd, 0x01271bfd, 0x01281afd, 0x022918fd,
+	0x032a16fd, 0x032b15fd, 0x042b14fd, 0x052c12fd,
+	0x072c10fd, 0x082c0ffd, 0x092c0efd, 0x0a2c0dfd,
+
+	0xfd0d290d, 0xfd0e290c, 0xfd0f290b, 0xfd11280a,
+	0xfd122809, 0xfd132808, 0xfd142807, 0xfd162706,
+	0xfd172705, 0xfd192604, 0xfe1a2503, 0xfe1b2502,
+	0xfe1c2402, 0xfe1d2302, 0xff1e2201, 0xff1f2101,
+	0x00202000, 0x00211f00, 0x01221eff, 0x02221dff,
+	0x02241cfe, 0x03241bfe, 0x042519fe, 0x042618fe,
+	0x052617fe, 0x062716fd, 0x072714fe, 0x082713fe,
+	0x092812fd, 0x0a2811fd, 0x0b2810fd, 0x0c280ffd,
+
+	0xfd0f250f, 0xfd10250e, 0xfd11250d, 0xfd12250c,
+	0xfd13250b, 0xfe13250a, 0xfe152409, 0xfe162408,
+	0xfe172308, 0xff182306, 0xff192305, 0xff1a2205,
+	0x001b2104, 0x001c2103, 0x001d2003, 0x011e1f02,
+	0x011f1f01, 0x021f1e01, 0x03201d00, 0x03211c00,
+	0x04211b00, 0x05221aff, 0x062219ff, 0x062318ff,
+	0x082316ff, 0x082316ff, 0x092415fe, 0x0a2414fe,
+	0x0b2413fe, 0x0c2412fe, 0x0d2411fe, 0x0e2410fe,
+
+	0xfe10230f, 0xfe11230e, 0xfe12220e, 0xfe13220d,
+	0xfe14220c, 0xff14220b, 0xff15220a, 0xff16210a,
+	0x00162109, 0x00172108, 0x00182008, 0x01192006,
+	0x011a1f06, 0x021a1f05, 0x021b1e05, 0x031c1d04,
+	0x031d1d03, 0x041d1c03, 0x041e1b03, 0x051e1b02,
+	0x061f1a01, 0x06201901, 0x07201801, 0x08201800,
+	0x09201700, 0x0a211500, 0x0b2115ff, 0x0c2114ff,
+	0x0c2213ff, 0x0d2212ff, 0x0e2211ff, 0x0f2211fe,
+
+	0xff112010, 0xff12200f, 0xff12200f, 0xff13200e,
+	0x0013200d, 0x0014200c, 0x00151f0c, 0x00161f0b,
+	0x01161f0a, 0x01171e0a, 0x02171e09, 0x02181e08,
+	0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05,
+	0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071c1a03,
+	0x071d1903, 0x081e1802, 0x091d1802, 0x091e1702,
+	0x0a1f1601, 0x0b1f1600, 0x0b1f1501, 0x0c201400,
+	0x0d1f1400, 0x0e2013ff, 0x0f1f1200, 0x102011ff,
+
+	0x00111f10, 0x00121e10, 0x00131e0f, 0x00131e0f,
+	0x01131e0e, 0x01141e0d, 0x01151d0d, 0x02151d0c,
+	0x02161d0b, 0x03161d0a, 0x03171c0a, 0x04171c09,
+	0x04181c08, 0x05181b08, 0x05191b07, 0x06191a07,
+	0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805,
+	0x081c1804, 0x091c1704, 0x0a1c1703, 0x0a1d1603,
+	0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1e1401,
+	0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200,
+
+	0x02111c11, 0x02121c10, 0x02131b10, 0x03131b0f,
+	0x03131b0f, 0x03141b0e, 0x04141b0d, 0x04151a0d,
+	0x05151a0c, 0x05151a0c, 0x05161a0b, 0x0616190b,
+	0x0616190b, 0x0716190a, 0x0717180a, 0x08171809,
+	0x08181808, 0x09181708, 0x09181708, 0x0a181707,
+	0x0a191607, 0x0b191606, 0x0b1a1605, 0x0c1a1505,
+	0x0c1a1505, 0x0d1a1504, 0x0d1b1404, 0x0e1b1403,
+	0x0f1b1303, 0x0f1b1303, 0x101b1302, 0x101c1202,
+
+	0x04111a11, 0x04121911, 0x04131910, 0x0513190f,
+	0x0513190f, 0x0513190f, 0x0613190e, 0x0614180e,
+	0x0714180d, 0x0714180d, 0x0715180c, 0x0814180c,
+	0x0815170c, 0x0816170b, 0x0916170a, 0x0916170a,
+	0x0a16160a, 0x0a171609, 0x0a171609, 0x0b171608,
+	0x0b171509, 0x0c171508, 0x0c181507, 0x0d171507,
+	0x0d181407, 0x0e181406, 0x0e181406, 0x0e191306,
+	0x0f191305, 0x0f191305, 0x10191304, 0x10191205,
+
+	0x05121811, 0x06121810, 0x06121810, 0x06131710,
+	0x0713170f, 0x0713170f, 0x0713170f, 0x0813170e,
+	0x0813170e, 0x0814170d, 0x0914160d, 0x0914160d,
+	0x0914160d, 0x0a14160c, 0x0a15160b, 0x0a15150c,
+	0x0b15150b, 0x0b15150b, 0x0b16150a, 0x0c15150a,
+	0x0c16140a, 0x0d161409, 0x0d161409, 0x0d171408,
+	0x0e161408, 0x0e171308, 0x0e171308, 0x0f171307,
+	0x0f171307, 0x10171306, 0x10181206, 0x10181206,
+
+	0x07111711, 0x07121710, 0x07121611, 0x08121610,
+	0x08121610, 0x0813160f, 0x0912160f, 0x0913160e,
+	0x0913160e, 0x0913160e, 0x0a14150d, 0x0a14150d,
+	0x0a14150d, 0x0b14150c, 0x0b14150c, 0x0b14150c,
+	0x0c14140c, 0x0c15140b, 0x0c15140b, 0x0c15140b,
+	0x0d15140a, 0x0d15140a, 0x0d15140a, 0x0e161309,
+	0x0e161309, 0x0e161309, 0x0f151309, 0x0f161308,
+	0x0f161209, 0x10161208, 0x10161208, 0x10171207,
+
+	0x0a111411, 0x0b111410, 0x0b111410, 0x0b111410,
+	0x0b111410, 0x0b12140f, 0x0b12140f, 0x0c12130f,
+	0x0c12130f, 0x0c12130f, 0x0c12130f, 0x0c12130f,
+	0x0d12130e, 0x0d12130e, 0x0d12130e, 0x0d13130d,
+	0x0d13130d, 0x0d13130d, 0x0e12130d, 0x0e13120d,
+	0x0e13120d, 0x0e13120d, 0x0e13120d, 0x0f13120c,
+	0x0f13120c, 0x0f13120c, 0x0f14120b, 0x0f14120b,
+	0x1013120b, 0x1013120b, 0x1013120b, 0x1014110b,
+
+	0x0c111310, 0x0c111310, 0x0c111310, 0x0d101310,
+	0x0d101310, 0x0d111210, 0x0d111210, 0x0d111210,
+	0x0d12120f, 0x0d12120f, 0x0d12120f, 0x0d12120f,
+	0x0e11120f, 0x0e12120e, 0x0e12120e, 0x0e12120e,
+	0x0e12120e, 0x0e12120e, 0x0e12120e, 0x0e12120e,
+	0x0f11120e, 0x0f12120d, 0x0f12120d, 0x0f12120d,
+	0x0f12120d, 0x0f12110e, 0x0f12110e, 0x0f12110e,
+	0x1012110d, 0x1012110d, 0x1013110c, 0x1013110c,
+};
+
 static int sun8i_vi_scaler_coef_index(unsigned int step)
 {
 	unsigned int scale, int_part, float_part;
@@ -447,10 +858,22 @@ static int sun8i_vi_scaler_coef_index(unsigned int step)
 }
 
 static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer,
-				      u32 hstep, u32 vstep)
+				      u32 hstep, u32 vstep,
+				      const struct drm_format_info *format)
 {
+	const u32 *ch_left, *ch_right, *cy;
 	int offset, i;
 
+	if (format->hsub == 1 && format->vsub == 1) {
+		ch_left = lan3coefftab32_left;
+		ch_right = lan3coefftab32_right;
+		cy = lan2coefftab32;
+	} else {
+		ch_left = bicubic8coefftab32_left;
+		ch_right = bicubic8coefftab32_right;
+		cy = bicubic4coefftab32;
+	}
+
 	offset = sun8i_vi_scaler_coef_index(hstep) *
 			SUN8I_VI_SCALER_COEFF_COUNT;
 	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
@@ -459,9 +882,9 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer,
 		regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(layer, i),
 			     lan3coefftab32_right[offset + i]);
 		regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(layer, i),
-			     lan3coefftab32_left[offset + i]);
+			     ch_left[offset + i]);
 		regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(layer, i),
-			     lan3coefftab32_right[offset + i]);
+			     ch_right[offset + i]);
 	}
 
 	offset = sun8i_vi_scaler_coef_index(hstep) *
@@ -470,7 +893,7 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer,
 		regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(layer, i),
 			     lan2coefftab32[offset + i]);
 		regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(layer, i),
-			     lan2coefftab32[offset + i]);
+			     cy[offset + i]);
 	}
 }
 
@@ -489,7 +912,8 @@ void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
 
 void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
 			   u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
-			   u32 hscale, u32 vscale, u32 hphase, u32 vphase)
+			   u32 hscale, u32 vscale, u32 hphase, u32 vphase,
+			   const struct drm_format_info *format)
 {
 	u32 chphase, cvphase;
 	u32 insize, outsize;
@@ -502,8 +926,19 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
 	insize = SUN8I_VI_SCALER_SIZE(src_w, src_h);
 	outsize = SUN8I_VI_SCALER_SIZE(dst_w, dst_h);
 
-	chphase = hphase;
-	cvphase = vphase;
+	/*
+	 * This is chroma V/H phase calculation as it appears in
+	 * BSP driver. There is no detailed explanation. YUV 420
+	 * chroma is threated specialy for some reason.
+	 */
+	if (format->hsub == 2 && format->vsub == 2) {
+		chphase = hphase >> 1;
+		cvphase = (vphase >> 1) -
+			(1UL << (SUN8I_VI_SCALER_SCALE_FRAC - 2));
+	} else {
+		chphase = hphase;
+		cvphase = vphase;
+	}
 
 	regmap_write(mixer->engine.regs,
 		     SUN8I_SCALER_VSU_OUTSIZE(layer), outsize);
@@ -519,17 +954,18 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
 		     SUN8I_SCALER_VSU_YVPHASE(layer), vphase);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_SCALER_VSU_CINSIZE(layer),
-		     SUN8I_VI_SCALER_SIZE(src_w, src_h));
+		     SUN8I_VI_SCALER_SIZE(src_w / format->hsub,
+					  src_h / format->vsub));
 	regmap_write(mixer->engine.regs,
 		     SUN8I_SCALER_VSU_CHSTEP(layer),
-		     hscale);
+		     hscale / format->hsub);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_SCALER_VSU_CVSTEP(layer),
-		     vscale);
+		     vscale / format->vsub);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_SCALER_VSU_CHPHASE(layer), chphase);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_SCALER_VSU_CVPHASE(layer), cvphase);
 	sun8i_vi_scaler_set_coeff(mixer->engine.regs, layer,
-				  hscale, vscale);
+				  hscale, vscale, format);
 }
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
index 09edb3491dc9..a595ab643a5a 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
@@ -9,6 +9,7 @@
 #ifndef _SUN8I_VI_SCALER_H_
 #define _SUN8I_VI_SCALER_H_
 
+#include <drm/drm_fourcc.h>
 #include "sun8i_mixer.h"
 
 /* this two macros assumes 16 fractional bits which is standard in DRM */
@@ -51,6 +52,7 @@
 void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
 void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
 			   u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
-			   u32 hscale, u32 vscale, u32 hphase, u32 vphase);
+			   u32 hscale, u32 vscale, u32 hphase, u32 vphase,
+			   const struct drm_format_info *format);
 
 #endif
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 26/27] drm/sun4i: Wire in DE2 YUV support
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Now that we have all required bits, add support for YUV formats.

DRM subsystem doesn't know YUV411 semi-planar format, so leave that out
for now.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 127 ++++++++++++++++++++++++++-------
 1 file changed, 102 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 02aaa7d3fceb..0b4800fd7ba1 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -55,6 +55,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 	u32 src_w, src_h, dst_w, dst_h;
 	u32 outsize, insize;
 	u32 hphase, vphase;
+	bool subsampled;
 
 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
 			 channel, overlay);
@@ -67,12 +68,32 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 	hphase = state->src.x1 & 0xffff;
 	vphase = state->src.y1 & 0xffff;
 
+	/* make coordinates dividable by subsampling factor */
+	if (format->hsub > 1) {
+		int mask, remainder;
+
+		mask = format->hsub - 1;
+		remainder = (state->src.x1 >> 16) & mask;
+		src_w = (src_w + remainder) & ~mask;
+		hphase += remainder << 16;
+	}
+
+	if (format->vsub > 1) {
+		int mask, remainder;
+
+		mask = format->vsub - 1;
+		remainder = (state->src.y1 >> 16) & mask;
+		src_h = (src_h + remainder) & ~mask;
+		vphase += remainder << 16;
+	}
+
 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 
 	/* Set height and width */
 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
-			 state->src.x1 >> 16, state->src.y1 >> 16);
+			 (state->src.x1 >> 16) & ~(format->hsub - 1),
+			 (state->src.y1 >> 16) & ~(format->vsub - 1));
 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
 	regmap_write(mixer->engine.regs,
 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay),
@@ -81,7 +102,13 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel),
 		     insize);
 
-	if (insize != outsize || hphase || vphase) {
+	/*
+	 * Scaler must be enabled for subsampled formats, so it scales
+	 * chroma to same size as luma.
+	 */
+	subsampled = format->hsub > 1 || format->vsub > 1;
+
+	if (insize != outsize || subsampled || hphase || vphase) {
 		u32 hscale, vscale;
 
 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
@@ -120,7 +147,7 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
 	u32 val;
 
 	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
-	if (!fmt_info || !fmt_info->rgb) {
+	if (!fmt_info) {
 		DRM_DEBUG_DRIVER("Invalid format\n");
 		return -EINVAL;
 	}
@@ -128,9 +155,23 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
 	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
 	regmap_update_bits(mixer->engine.regs,
 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
-			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK |
-			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE,
-			   val | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE);
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
+
+	if (fmt_info->csc != SUN8I_CSC_MODE_OFF) {
+		sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc);
+		sun8i_csc_enable_ccsc(mixer, channel, true);
+	} else {
+		sun8i_csc_enable_ccsc(mixer, channel, false);
+	}
+
+	if (fmt_info->rgb)
+		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
+	else
+		val = 0;
+
+	regmap_update_bits(mixer->engine.regs,
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
+			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
 
 	return 0;
 }
@@ -140,34 +181,53 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_framebuffer *fb = state->fb;
+	const struct drm_format_info *format = fb->format;
 	struct drm_gem_cma_object *gem;
+	u32 dx, dy, src_x, src_y;
 	dma_addr_t paddr;
-	int bpp;
+	int i;
 
-	/* Get the physical address of the buffer in memory */
-	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	/* Adjust x and y to be dividable by subsampling factor */
+	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
+	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
 
-	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
+	for (i = 0; i < format->num_planes; i++) {
+		/* Get the physical address of the buffer in memory */
+		gem = drm_fb_cma_get_gem_obj(fb, i);
 
-	/* Compute the start of the displayed memory */
-	bpp = fb->format->cpp[0];
-	paddr = gem->paddr + fb->offsets[0];
+		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
 
-	/* Fixup framebuffer address for src coordinates */
-	paddr += (state->src.x1 >> 16) * bpp;
-	paddr += (state->src.y1 >> 16) * fb->pitches[0];
+		/* Compute the start of the displayed memory */
+		paddr = gem->paddr + fb->offsets[i];
 
-	/* Set the line width */
-	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, overlay, 0),
-		     fb->pitches[0]);
+		dx = src_x;
+		dy = src_y;
 
-	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+		if (i > 0) {
+			dx /= format->hsub;
+			dy /= format->vsub;
+		}
 
-	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, overlay, 0),
-		     lower_32_bits(paddr));
+		/* Fixup framebuffer address for src coordinates */
+		paddr += dx * format->cpp[i];
+		paddr += dy * fb->pitches[i];
+
+		/* Set the line width */
+		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
+				 i + 1, fb->pitches[i]);
+		regmap_write(mixer->engine.regs,
+			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel,
+							     overlay, i),
+	       fb->pitches[i]);
+
+		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
+				 i + 1, &paddr);
+
+		regmap_write(mixer->engine.regs,
+			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel,
+								 overlay, i),
+	       lower_32_bits(paddr));
+	}
 
 	return 0;
 }
@@ -270,6 +330,23 @@ static const u32 sun8i_vi_layer_formats[] = {
 	DRM_FORMAT_RGBX8888,
 	DRM_FORMAT_XBGR8888,
 	DRM_FORMAT_XRGB8888,
+
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_YUV411,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YUV444,
+	DRM_FORMAT_YVU411,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YVU422,
+	DRM_FORMAT_YVU444,
 };
 
 struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
-- 
2.15.1

^ permalink raw reply related

* [PATCH v2 27/27] [DO NOT MERGE]drm/sun4i: Change zpos of bottom VI plane
From: Jernej Skrabec @ 2017-12-01  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201060550.10392-1-jernej.skrabec@siol.net>

Change zpos of VI plane so it is above primary.

Clearly this works only if mixer supports only one VI plane, but it is
good enough for testing and developing.

Proper solution with zpos property should be developed instead.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/gpu/drm/sun4i/sun8i_mixer.c    |  2 +-
 drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 10 ++++++----
 drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 10 ++++++----
 3 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 29ceeb016d72..5a6774d40912 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -424,7 +424,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
 
 	/* Fixed zpos for now */
-	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 0x43210);
+	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 0x43201);
 
 	plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
 	for (i = 0; i < plane_cnt; i++)
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index fd76ffd40580..472a846ac967 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -26,6 +26,8 @@
 #include "sun8i_mixer.h"
 #include "sun8i_ui_scaler.h"
 
+static int lmap[] = {1, 0, 2, 3, 4};
+
 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
 				  int overlay, bool enable)
 {
@@ -44,13 +46,13 @@ static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
 
 	if (enable)
-		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
+		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(lmap[channel]);
 	else
 		val = 0;
 
 	regmap_update_bits(mixer->engine.regs,
 			   SUN8I_MIXER_BLEND_PIPE_CTL,
-			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
+			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(lmap[channel]), val);
 }
 
 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
@@ -137,10 +139,10 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 			 state->dst.x1, state->dst.y1);
 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
+		     SUN8I_MIXER_BLEND_ATTR_COORD(lmap[channel]),
 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
+		     SUN8I_MIXER_BLEND_ATTR_INSIZE(lmap[channel]),
 		     outsize);
 
 	return 0;
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 0b4800fd7ba1..e4ee2b51457f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -20,6 +20,8 @@
 #include "sun8i_mixer.h"
 #include "sun8i_vi_scaler.h"
 
+static int lmap[] = {1, 0, 2, 3, 4};
+
 static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
 				  int overlay, bool enable)
 {
@@ -38,13 +40,13 @@ static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
 
 	if (enable)
-		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
+		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(lmap[channel]);
 	else
 		val = 0;
 
 	regmap_update_bits(mixer->engine.regs,
 			   SUN8I_MIXER_BLEND_PIPE_CTL,
-			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
+			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(lmap[channel]), val);
 }
 
 static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
@@ -130,10 +132,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
 			 state->dst.x1, state->dst.y1);
 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
+		     SUN8I_MIXER_BLEND_ATTR_COORD(lmap[channel]),
 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 	regmap_write(mixer->engine.regs,
-		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
+		     SUN8I_MIXER_BLEND_ATTR_INSIZE(lmap[channel]),
 		     outsize);
 
 	return 0;
-- 
2.15.1

^ permalink raw reply related

* [PATCH 0/4] pci-dra7xx: Fix legacy IRQ handling and errata handling
From: Vignesh R @ 2017-12-01  6:13 UTC (permalink / raw)
  To: linux-arm-kernel

This series contains two fixes:
1. Make workaround for errata i870 applicable in Host mode as
well(previously it was enabled only for EP mode) as per errata
documentation: http://www.ti.com/lit/er/sprz429k/sprz429k.pdf
2. Fix problem with handling of legacy INTD interrupts.

Tested on 66AK2G EVM and ICE boards


Vignesh R (4):
  pci: dwc: pci-dra7xx: Enable errata i870 for both EP and RC mode
  dt-bindings: PCI: dra7xx: Add bindings for unaligned access in host
    mode
  ARM: dts: dra7: Add DT property to allow unaligned mem access to PCIe
    RC
  PCI: dwc: pci-dra7xx: Fix legacy IRQ handling

 Documentation/devicetree/bindings/pci/ti-pci.txt |  5 +++++
 arch/arm/boot/dts/dra7.dtsi                      |  2 ++
 drivers/pci/dwc/pci-dra7xx.c                     | 23 ++++++++++++++---------
 3 files changed, 21 insertions(+), 9 deletions(-)

-- 
2.15.0

^ permalink raw reply

* [PATCH 1/4] pci: dwc: pci-dra7xx: Enable errata i870 for both EP and RC mode
From: Vignesh R @ 2017-12-01  6:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201061311.16691-1-vigneshr@ti.com>

Errata i870 is applicable in both EP and RC mode. Therefore rename
function dra7xx_pcie_ep_unaligned_memaccess(), that implements errata
workaround, to dra7xx_pcie_unaligned_memaccess() and call it from a
common place. So, that errata workaround is applied for both modes of
operation.

Reported-by: Chris Welch <Chris.Welch@viavisolutions.com>
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 drivers/pci/dwc/pci-dra7xx.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index e77a4ceed74c..53f721d1cc40 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -546,7 +546,7 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
 };
 
 /*
- * dra7xx_pcie_ep_unaligned_memaccess: workaround for AM572x/AM571x Errata i870
+ * dra7xx_pcie_unaligned_memaccess: workaround for AM572x/AM571x Errata i870
  * @dra7xx: the dra7xx device where the workaround should be applied
  *
  * Access to the PCIe slave port that are not 32-bit aligned will result
@@ -556,7 +556,7 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
  *
  * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1.
  */
-static int dra7xx_pcie_ep_unaligned_memaccess(struct device *dev)
+static int dra7xx_pcie_unaligned_memaccess(struct device *dev)
 {
 	int ret;
 	struct device_node *np = dev->of_node;
@@ -703,6 +703,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
 	if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2)
 		dra7xx->link_gen = 2;
 
+	ret = dra7xx_pcie_unaligned_memaccess(dev);
+	if (ret)
+		goto err_gpio;
+
 	switch (mode) {
 	case DW_PCIE_RC_TYPE:
 		dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
@@ -715,10 +719,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
 		dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
 				   DEVICE_TYPE_EP);
 
-		ret = dra7xx_pcie_ep_unaligned_memaccess(dev);
-		if (ret)
-			goto err_gpio;
-
 		ret = dra7xx_add_pcie_ep(dra7xx, pdev);
 		if (ret < 0)
 			goto err_gpio;
-- 
2.15.0

^ permalink raw reply related

* [PATCH 2/4] dt-bindings: PCI: dra7xx: Add bindings for unaligned access in host mode
From: Vignesh R @ 2017-12-01  6:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201061311.16691-1-vigneshr@ti.com>

Update device tree binding documentation of TI's dra7xx PCI controller
for enabling unaligned mem access as applicable not just in EP mode but
in host mode as well.

Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 Documentation/devicetree/bindings/pci/ti-pci.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt
index 7f7af3044016..452fe48c4fdd 100644
--- a/Documentation/devicetree/bindings/pci/ti-pci.txt
+++ b/Documentation/devicetree/bindings/pci/ti-pci.txt
@@ -26,6 +26,11 @@ HOST MODE
    ranges,
    interrupt-map-mask,
    interrupt-map : as specified in ../designware-pcie.txt
+ - ti,syscon-unaligned-access: phandle to the syscon DT node. The 1st argument
+			       should contain the register offset within syscon
+			       and the 2nd argument should contain the bit field
+			       for setting the bit to enable unaligned
+			       access.
 
 DEVICE MODE
 ===========
-- 
2.15.0

^ permalink raw reply related

* [PATCH 3/4] ARM: dts: dra7: Add DT property to allow unaligned mem access to PCIe RC
From: Vignesh R @ 2017-12-01  6:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201061311.16691-1-vigneshr@ti.com>

Add ti,syscon-unaligned-access property to PCIe RC nodes in order to
enable workaround for errata i870 in PCIe RC mode as well.

Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 arch/arm/boot/dts/dra7.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index ac9216293b7c..e4182f4cc36d 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -314,6 +314,7 @@
 						<0 0 0 2 &pcie1_intc 2>,
 						<0 0 0 3 &pcie1_intc 3>,
 						<0 0 0 4 &pcie1_intc 4>;
+				ti,syscon-unaligned-access = <&scm_conf1 0x14 2>;
 				status = "disabled";
 				pcie1_intc: interrupt-controller {
 					interrupt-controller;
@@ -367,6 +368,7 @@
 						<0 0 0 2 &pcie2_intc 2>,
 						<0 0 0 3 &pcie2_intc 3>,
 						<0 0 0 4 &pcie2_intc 4>;
+				ti,syscon-unaligned-access = <&scm_conf1 0x14 1>;
 				pcie2_intc: interrupt-controller {
 					interrupt-controller;
 					#address-cells = <0>;
-- 
2.15.0

^ permalink raw reply related

* [PATCH 4/4] PCI: dwc: pci-dra7xx: Fix legacy IRQ handling
From: Vignesh R @ 2017-12-01  6:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171201061311.16691-1-vigneshr@ti.com>

Legacy INTD IRQ handling is broken on dra7xx due to fact that driver
uses hwirq in range of 1-4 for INTA, INTD whereas IRQ domain is of size
4 which is numbered 0-3. Therefore when INTD IRQ line is used with
pci-dra7xx driver following warning is seen:

       WARNING: CPU: 0 PID: 1 at kernel/irq/irqdomain.c:342 irq_domain_associate+0x12c/0x1c4
       error: hwirq 0x4 is too large for dummy

Fix this by using pci_irqd_intx_xlate() helper to translate the INTx 1-4
range into the 0-3 as done in other PCIe drivers.

Also, iterate over all the INTx bits and call their respective IRQ
handlers before clearing the status register.

Suggested-by: Bjorn Helgaas <bhelgaas@google.com>
Reported-by: Chris Welch <Chris.Welch@viavisolutions.com>
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 drivers/pci/dwc/pci-dra7xx.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index 53f721d1cc40..59e8de34cec6 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -226,6 +226,7 @@ static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
 
 static const struct irq_domain_ops intx_domain_ops = {
 	.map = dra7xx_pcie_intx_map,
+	.xlate = pci_irqd_intx_xlate,
 };
 
 static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
@@ -256,7 +257,8 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
 	struct dra7xx_pcie *dra7xx = arg;
 	struct dw_pcie *pci = dra7xx->pci;
 	struct pcie_port *pp = &pci->pp;
-	u32 reg;
+	unsigned long reg;
+	u32 virq, bit;
 
 	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
 
@@ -268,8 +270,11 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
 	case INTB:
 	case INTC:
 	case INTD:
-		generic_handle_irq(irq_find_mapping(dra7xx->irq_domain,
-						    ffs(reg)));
+		for_each_set_bit(bit, &reg, PCI_NUM_INTX) {
+			virq = irq_find_mapping(dra7xx->irq_domain, bit);
+			if (virq)
+				generic_handle_irq(virq);
+		}
 		break;
 	}
 
-- 
2.15.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox