[PATCH 2/2] drm/exynos: decon: Add support for DECON-EXT

Ajay Kumar ajaykumar.rs at samsung.com
Thu Feb 26 07:24:45 PST 2015


* Modify DECON-INT driver to support DECON-EXT.
* Add a table of porch values needed to set timing registers of DECON-EXT.
* DECON-EXT supports only H/w Triggered COMMAND mode.
* DECON-EXT supports only one DMA window(window 1), so modify
  all window management routines to support 2 windows of DECON-INT
  and 1 window of DECON-EXT.

Signed-off-by: Ajay Kumar <ajaykumar.rs at samsung.com>
---
 .../devicetree/bindings/video/exynos7-decon.txt    |    8 +-
 drivers/gpu/drm/exynos/exynos7_drm_decon.c         |  229 ++++++++++++++++++--
 include/video/exynos7_decon.h                      |   13 ++
 3 files changed, 230 insertions(+), 20 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/exynos7-decon.txt b/Documentation/devicetree/bindings/video/exynos7-decon.txt
index f5f9c8d..87350c0 100644
--- a/Documentation/devicetree/bindings/video/exynos7-decon.txt
+++ b/Documentation/devicetree/bindings/video/exynos7-decon.txt
@@ -2,10 +2,14 @@ Device-Tree bindings for Samsung Exynos7 SoC display controller (DECON)
 
 DECON (Display and Enhancement Controller) is the Display Controller for the
 Exynos7 series of SoCs which transfers the image data from a video memory
-buffer to an external LCD interface.
+buffer to an external LCD/HDMI interface.
+
+Exynos7 supports DECON-INT for generating video signals for LCD.
+Exynos7 supports DECON-EXT for generating video signals for HDMI.
 
 Required properties:
-- compatible: value should be "samsung,exynos7-decon";
+- compatible: value should be "samsung,exynos7-decon-int" for DECON-INT;
+	      value should be "samsung,exynos7-decon-ext" for DECON-EXT;
 
 - reg: physical base address and length of the DECON registers set.
 
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 63f02e2..9e2d083 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -41,6 +41,28 @@
 
 #define WINDOWS_NR	2
 
+#define DECON_EXT_CH_MAPPING	0x432105
+
+enum decon_type {
+	DECON_INT,
+	DECON_EXT,
+} decon_type;
+
+struct decon_driver_data {
+	enum decon_type			decon_type;
+	unsigned int			nr_windows;
+};
+
+static struct decon_driver_data exynos7_decon_int_driver_data = {
+	.decon_type = DECON_INT,
+	.nr_windows = 2,
+};
+
+static struct decon_driver_data exynos7_decon_ext_driver_data = {
+	.decon_type = DECON_EXT,
+	.nr_windows = 1,
+};
+
 struct decon_win_data {
 	unsigned int		ovl_x;
 	unsigned int		ovl_y;
@@ -76,15 +98,28 @@ struct decon_context {
 	atomic_t			wait_vsync_event;
 
 	struct exynos_drm_panel_info panel;
+	struct decon_driver_data *driver_data;
 	struct exynos_drm_display *display;
 };
 
 static const struct of_device_id decon_driver_dt_match[] = {
-	{.compatible = "samsung,exynos7-decon"},
+	{ .compatible = "samsung,exynos7-decon-int",
+	  .data = &exynos7_decon_int_driver_data },
+	{ .compatible = "samsung,exynos7-decon-ext",
+	  .data = &exynos7_decon_ext_driver_data },
 	{},
 };
 MODULE_DEVICE_TABLE(of, decon_driver_dt_match);
 
+static inline struct decon_driver_data *drm_decon_get_driver_data(
+	struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+			of_match_device(decon_driver_dt_match, &pdev->dev);
+
+	return (struct decon_driver_data *)of_id->data;
+}
+
 static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
 {
 	struct decon_context *ctx = crtc->ctx;
@@ -106,13 +141,20 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
 
 static void decon_clear_channel(struct decon_context *ctx)
 {
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	int win, ch_enabled = 0;
 
 	DRM_DEBUG_KMS("%s\n", __FILE__);
 
 	/* Check if any channel is enabled. */
-	for (win = 0; win < WINDOWS_NR; win++) {
-		u32 val = readl(ctx->regs + WINCON(win));
+	for (win = 0; win < drv_data->nr_windows; win++) {
+		u32 val;
+		/* DECON EXT sw-hw window mapping */
+		if (drv_data->decon_type == DECON_EXT) {
+			if (win == 0)
+				win = 1;
+		}
+		val = readl(ctx->regs + WINCON(win));
 
 		if (val & WINCONx_ENWIN) {
 			val &= ~WINCONx_ENWIN;
@@ -187,10 +229,74 @@ static bool decon_mode_fixup(struct exynos_drm_crtc *crtc,
 	return true;
 }
 
+struct decon_ext_videomode {
+	u32 vfront_porch;
+	u32 vback_porch;
+	u32 hfront_porch;
+	u32 hback_porch;
+	u32 vsync_len;
+	u32 hsync_len;
+	u32 hactive;
+	u32 vactive;
+	u32 refresh_rate;
+};
+
+enum DECON_EXT_MODES {
+	MODE_720_480_60,
+	MODE_720_576_50,
+	MODE_1280_720_60,
+	MODE_1280_720_50,
+	MODE_1920_1080_60,
+	MODE_1920_1080_50,
+	MODE_1920_1080_30,
+	MODE_1920_1080_25,
+	MODE_1920_1080_24,
+	MODE_3840_2160_30,
+	MODE_3840_2160_25,
+	MODE_3840_2160_24,
+	MODE_4096_2160_24,
+};
+
+static struct decon_ext_videomode decon_ext_porchs[] = {
+	/*vfp vbp hfp hbp  vsa hsa xres yres fps */
+	{1, 43, 10, 92,   1, 36,  720, 480,   60},
+	{1, 47, 10, 92,   1, 42,  720, 576,   50},
+	{1, 28, 10, 192,  1, 168, 1280, 720,  60},
+	{1, 28, 10, 92,   1, 598, 1280, 720,  50},
+	{1, 43, 10, 92,   1, 178, 1920, 1080, 60},
+	{1, 43, 10, 92,   1, 618, 1920, 1080, 50},
+	{1, 43, 10, 92,   1, 178, 1920, 1080, 30},
+	{1, 43, 10, 92,   1, 618, 1920, 1080, 25},
+	{1, 43, 10, 92,   1, 728, 1920, 1080, 24},
+	{1, 88, 10, 458,  1, 92,  3840, 2160, 30},
+	{1, 88, 10, 1338, 1, 92,  3840, 2160, 25},
+	{1, 88, 10, 1558, 1, 92,  3840, 2160, 24},
+	{1, 88, 10, 1302, 1, 92,  4096, 2160, 24},
+};
+
+static void decon_ext_set_timing(struct drm_display_mode *mode,
+				struct decon_ext_videomode *ext_mode)
+{
+	mode->crtc_hdisplay = ext_mode->hactive;
+	mode->crtc_hsync_start = ext_mode->hactive + ext_mode->hfront_porch;
+	mode->crtc_hsync_end = ext_mode->hactive + ext_mode->hfront_porch
+						+ ext_mode->hsync_len;
+	mode->crtc_htotal = ext_mode->hactive + ext_mode->hfront_porch
+			+ ext_mode->hsync_len + ext_mode->hback_porch;
+	mode->crtc_vdisplay = ext_mode->vactive;
+	mode->crtc_vsync_start = ext_mode->vactive + ext_mode->vfront_porch;
+	mode->crtc_vsync_end = ext_mode->vactive + ext_mode->vfront_porch
+						+ ext_mode->vsync_len;
+	mode->crtc_vtotal = ext_mode->vactive + ext_mode->vfront_porch
+				+ ext_mode->vsync_len + ext_mode->vback_porch;
+}
+
 static void decon_commit(struct exynos_drm_crtc *crtc)
 {
 	struct decon_context *ctx = crtc->ctx;
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	struct drm_display_mode *mode = &crtc->base.mode;
+	struct decon_ext_videomode *my_mode;
 	u32 val, clkdiv;
 
 	if (ctx->suspended)
@@ -200,6 +306,38 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
 	if (mode->htotal == 0 || mode->vtotal == 0)
 		return;
 
+	if (drv_data->decon_type == DECON_EXT) {
+		if ((mode->hdisplay == 720) && (mode->vdisplay == 480) &&
+						(mode->vrefresh == 60))
+			my_mode = &decon_ext_porchs[MODE_720_480_60];
+		if ((mode->hdisplay == 720) && (mode->vdisplay == 576) &&
+						(mode->vrefresh == 50))
+			my_mode = &decon_ext_porchs[MODE_720_576_50];
+		if ((mode->hdisplay == 1280) && (mode->vdisplay == 720) &&
+						(mode->vrefresh == 60))
+			my_mode = &decon_ext_porchs[MODE_1280_720_60];
+		if ((mode->hdisplay == 1280) && (mode->vdisplay == 720) &&
+						(mode->vrefresh == 50))
+			my_mode = &decon_ext_porchs[MODE_1280_720_50];
+		if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080) &&
+						(mode->vrefresh == 60))
+			my_mode = &decon_ext_porchs[MODE_1920_1080_60];
+		if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080) &&
+						(mode->vrefresh == 50))
+			my_mode = &decon_ext_porchs[MODE_1920_1080_50];
+		if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080) &&
+						(mode->vrefresh == 30))
+			my_mode = &decon_ext_porchs[MODE_1920_1080_30];
+		if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080) &&
+						(mode->vrefresh == 25))
+			my_mode = &decon_ext_porchs[MODE_1920_1080_25];
+		if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080) &&
+						(mode->vrefresh == 24))
+			my_mode = &decon_ext_porchs[MODE_1920_1080_24];
+
+		decon_ext_set_timing(mode, my_mode);
+	}
+
 	if (!ctx->i80_if) {
 		int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
 	      /* setup vertical timing values. */
@@ -301,6 +439,7 @@ static void decon_win_mode_set(struct exynos_drm_crtc *crtc,
 {
 	struct decon_context *ctx = crtc->ctx;
 	struct decon_win_data *win_data;
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	int win, padding;
 
 	if (!plane) {
@@ -312,12 +451,18 @@ static void decon_win_mode_set(struct exynos_drm_crtc *crtc,
 	if (win == DEFAULT_ZPOS)
 		win = ctx->default_win;
 
-	if (win < 0 || win >= WINDOWS_NR)
+	if (win < 0 || win >= drv_data->nr_windows)
 		return;
 
 
 	win_data = &ctx->win_data[win];
 
+	/* DECON EXT sw-hw window mapping */
+	if (drv_data->decon_type == DECON_EXT) {
+		if (win == 0)
+			win = 1;
+	}
+
 	padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
 	win_data->offset_x = plane->fb_x;
 	win_data->offset_y = plane->fb_y;
@@ -340,9 +485,9 @@ static void decon_win_mode_set(struct exynos_drm_crtc *crtc,
 			plane->fb_width, plane->crtc_width);
 }
 
-static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
+static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
+					struct decon_win_data *win_data)
 {
-	struct decon_win_data *win_data = &ctx->win_data[win];
 	unsigned long val;
 
 	val = readl(ctx->regs + WINCON(win));
@@ -454,6 +599,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
 	struct decon_context *ctx = crtc->ctx;
 	struct drm_display_mode *mode = &crtc->base.mode;
 	struct decon_win_data *win_data;
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	int win = zpos;
 	unsigned long val, alpha;
 	unsigned int last_x;
@@ -465,11 +611,17 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
 	if (win == DEFAULT_ZPOS)
 		win = ctx->default_win;
 
-	if (win < 0 || win >= WINDOWS_NR)
+	if (win < 0 || win >= drv_data->nr_windows)
 		return;
 
 	win_data = &ctx->win_data[win];
 
+	/* DECON EXT sw-hw window mapping */
+	if (drv_data->decon_type == DECON_EXT) {
+		if (win == 0)
+			win = 1;
+	}
+
 	/* If suspended, enable this on resume */
 	if (ctx->suspended) {
 		win_data->resume = true;
@@ -546,7 +698,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
 
 	writel(alpha, ctx->regs + VIDOSD_D(win));
 
-	decon_win_set_pixfmt(ctx, win);
+	decon_win_set_pixfmt(ctx, win, win_data);
 
 	/* hardware window 0 doesn't support color key. */
 	if (win != 0)
@@ -572,17 +724,24 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, int zpos)
 {
 	struct decon_context *ctx = crtc->ctx;
 	struct decon_win_data *win_data;
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	int win = zpos;
 	u32 val;
 
 	if (win == DEFAULT_ZPOS)
 		win = ctx->default_win;
 
-	if (win < 0 || win >= WINDOWS_NR)
+	if (win < 0 || win >= drv_data->nr_windows)
 		return;
 
 	win_data = &ctx->win_data[win];
 
+	/* DECON EXT sw-hw window mapping */
+	if (drv_data->decon_type == DECON_EXT) {
+		if (win == 0)
+			win = 1;
+	}
+
 	if (ctx->suspended) {
 		/* do not resume this window*/
 		win_data->resume = false;
@@ -609,10 +768,11 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, int zpos)
 
 static void decon_window_suspend(struct decon_context *ctx)
 {
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	struct decon_win_data *win_data;
 	int i;
 
-	for (i = 0; i < WINDOWS_NR; i++) {
+	for (i = 0; i < drv_data->nr_windows; i++) {
 		win_data = &ctx->win_data[i];
 		win_data->resume = win_data->enabled;
 		if (win_data->enabled)
@@ -622,10 +782,11 @@ static void decon_window_suspend(struct decon_context *ctx)
 
 static void decon_window_resume(struct decon_context *ctx)
 {
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	struct decon_win_data *win_data;
 	int i;
 
-	for (i = 0; i < WINDOWS_NR; i++) {
+	for (i = 0; i < drv_data->nr_windows; i++) {
 		win_data = &ctx->win_data[i];
 		win_data->enabled = win_data->resume;
 		win_data->resume = false;
@@ -634,10 +795,11 @@ static void decon_window_resume(struct decon_context *ctx)
 
 static void decon_apply(struct decon_context *ctx)
 {
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	struct decon_win_data *win_data;
 	int i;
 
-	for (i = 0; i < WINDOWS_NR; i++) {
+	for (i = 0; i < drv_data->nr_windows; i++) {
 		win_data = &ctx->win_data[i];
 		if (win_data->enabled)
 			decon_win_commit(ctx->crtc, i);
@@ -650,6 +812,7 @@ static void decon_apply(struct decon_context *ctx)
 
 static void decon_init(struct decon_context *ctx)
 {
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	u32 val;
 
 	writel(VIDCON0_SWRESET, ctx->regs + VIDCON0);
@@ -657,6 +820,14 @@ static void decon_init(struct decon_context *ctx)
 	val = VIDOUTCON0_DISP_IF_0_ON;
 	if (!ctx->i80_if)
 		val |= VIDOUTCON0_RGBIF;
+
+	if (drv_data->decon_type == DECON_EXT) {
+		writel(TRIGCON_HWTRIG_AUTO_MASK |
+			TRIGCON_HWTRIGMASK_DISPIF0 |
+			TRIGCON_HWTRIGEN_I80_RGB, ctx->regs + TRIGCON);
+		val |= VIDOUTCON0_TV_MODE | VIDOUTCON0_I80IF;
+	}
+
 	writel(val, ctx->regs + VIDOUTCON0);
 
 	writel(VCLKCON0_CLKVALUP | VCLKCON0_VCLKFREE, ctx->regs + VCLKCON0);
@@ -667,6 +838,7 @@ static void decon_init(struct decon_context *ctx)
 
 static int decon_poweron(struct decon_context *ctx)
 {
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	int ret;
 
 	if (!ctx->suspended)
@@ -702,6 +874,9 @@ static int decon_poweron(struct decon_context *ctx)
 
 	decon_init(ctx);
 
+	if (drv_data->decon_type == DECON_EXT)
+		writel(DECON_EXT_CH_MAPPING, ctx->regs + WINCHMAP0);
+
 	/* if vblank was enabled status, enable it again. */
 	if (test_and_clear_bit(0, &ctx->irq_flags)) {
 		ret = decon_enable_vblank(ctx->crtc);
@@ -817,6 +992,7 @@ out:
 static int decon_bind(struct device *dev, struct device *master, void *data)
 {
 	struct decon_context *ctx = dev_get_drvdata(dev);
+	struct decon_driver_data *drv_data = ctx->driver_data;
 	struct drm_device *drm_dev = data;
 	int ret;
 
@@ -826,16 +1002,23 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 	}
 
-	ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
+	if (drv_data->decon_type == DECON_INT)
+		ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
 					   EXYNOS_DISPLAY_TYPE_LCD,
 					   &decon_crtc_ops, ctx);
+	else
+		ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
+					   EXYNOS_DISPLAY_TYPE_HDMI,
+					   &decon_crtc_ops, ctx);
+
 	if (IS_ERR(ctx->crtc)) {
 		decon_ctx_remove(ctx);
 		return PTR_ERR(ctx->crtc);
 	}
 
-	if (ctx->display)
-		exynos_drm_create_enc_conn(drm_dev, ctx->display);
+	if (drv_data->decon_type == DECON_INT)
+		if (ctx->display)
+			exynos_drm_create_enc_conn(drm_dev, ctx->display);
 
 	return 0;
 
@@ -848,8 +1031,9 @@ static void decon_unbind(struct device *dev, struct device *master,
 
 	decon_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
 
-	if (ctx->display)
-		exynos_dpi_remove(ctx->display);
+	if (ctx->driver_data->decon_type == DECON_INT)
+		if (ctx->display)
+			exynos_dpi_remove(ctx->display);
 
 	decon_ctx_remove(ctx);
 }
@@ -862,11 +1046,14 @@ static const struct component_ops decon_component_ops = {
 static int decon_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
+	struct decon_driver_data *drv_data;
 	struct decon_context *ctx;
 	struct device_node *i80_if_timings;
 	struct resource *res;
 	int ret;
 
+	drv_data = drm_decon_get_driver_data(pdev);
+
 	if (!dev->of_node)
 		return -ENODEV;
 
@@ -874,11 +1061,17 @@ static int decon_probe(struct platform_device *pdev)
 	if (!ctx)
 		return -ENOMEM;
 
-	ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
+	if (drv_data->decon_type == DECON_INT)
+		ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
 					EXYNOS_DISPLAY_TYPE_LCD);
+	else
+		ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
+					EXYNOS_DISPLAY_TYPE_HDMI);
+
 	if (ret)
 		return ret;
 
+	ctx->driver_data = drv_data;
 	ctx->dev = dev;
 	ctx->suspended = true;
 
diff --git a/include/video/exynos7_decon.h b/include/video/exynos7_decon.h
index a62b11b..d02320b 100644
--- a/include/video/exynos7_decon.h
+++ b/include/video/exynos7_decon.h
@@ -20,6 +20,7 @@
 /* VIDOUTCON0 */
 #define VIDOUTCON0				0x4
 
+#define VIDOUTCON0_TV_MODE			(0x1 << 26)
 #define VIDOUTCON0_DUAL_MASK			(0x3 << 24)
 #define VIDOUTCON0_DUAL_ON			(0x3 << 24)
 #define VIDOUTCON0_DISP_IF_1_ON			(0x2 << 24)
@@ -52,6 +53,9 @@
 
 #define SHADOWCON_WINx_PROTECT(_win)		(1 << (10 + (_win)))
 
+/* WINCHMAP0 */
+#define WINCHMAP0				0x40
+
 /* WINCONx */
 #define WINCON(_win)				(0x50 + ((_win) * 4))
 
@@ -328,6 +332,15 @@
 /* LINECNT OP THRSHOLD*/
 #define LINECNT_OP_THRESHOLD			0x630
 
+/* TRIGCON */
+#define TRIGCON					0x06B0
+#define TRIGCON_HWTRIG_AUTO_MASK		(1 << 6)
+#define TRIGCON_HWTRIGMASK_DISPIF1		(1 << 5)
+#define TRIGCON_HWTRIGMASK_DISPIF0		(1 << 4)
+#define TRIGCON_HWTRIGEN_I80_RGB		(1 << 3)
+#define TRIGCON_SWTRIGCMD_I80_RGB		(1 << 1)
+#define TRIGCON_SWTRIGEN_I80_RGB		(1 << 0)
+
 /* CRCCTRL */
 #define CRCCTRL					0x6C8
 #define CRCCTRL_CRCCLKEN			(0x1 << 2)
-- 
1.7.9.5



More information about the dri-devel mailing list