From e385a365ce673b9f150f6356a91962c240c105a2 Mon Sep 17 00:00:00 2001
From: Peter Seiderer <ps.report@gmx.net>
Date: Thu, 20 Feb 2014 21:38:16 +0100
Subject: [RFC] imx-drm: enable full hd dual lvds display (side-by-side)
To: dri-devel@lists.freedesktop.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
    Sascha Hauer <kernel@pengutronix.de>

Enable support for a full hd (1920x1080) lcd display ([1]/[2]),
driven by two lvds channels (channel 0 pixels on the left side,
channel 1 pixels on the right side).

First configure two lvds channels with half display width:
        lvds-channel@0 {
                fsl,data-mapping = "spwg";
                fsl,data-width = <24>;
                status = "okay";

                display-timings {
                        native-mode = <&timing0>;
                        timing0: nlt-nl192108ac10-01d {
                                clock-frequency = <74590000>;
                                hactive = <960>;
                                vactive = <1080>;
                        };
                };
        };

        lvds-channel@1 {
                fsl,data-mapping = "spwg";
                fsl,data-width = <24>;
                status = "okay";

                display-timings {
                        native-mode = <&timing1>;
                        timing1: nlt-nl192108ac10-01d {
                                clock-frequency = <74590000>;
                                hactive = <960>;
                                vactive = <1080>;
                        };
                };
        };

Second force the frame buffer to the doubled default size as
calculated from the lvds-channel entries, see changes
in drivers/gpu/drm/drm_fb_helper.c.

Third let the lvds-channel@1 memory start with half pitch offset,
see changes in drivers/staging/imx-drm/ipuv3-plane.c.

And last synchronize the two lvds channels (without the second half
of the display has an vertical offset by some pixels), see
changes in drivers/staging/imx-drm/ipu-v3/ipu-common.c

Tested with TQMa6Q module/starter kit STKa6x ([3]/[4]).

This is similar to the already existing imx-drm/ldb.txt option
'fsl,dual-channel' which is for displays with dual lvds where the
odd pixels are on channel 0 and the even pixels are on channel 1.

This (first implementation) is for requesting comments. Any
hints/suggestion on howto implement this feature the right
way (configurable, etc.)?

[1] http://www1.futureelectronics.com/doc/NEC%20DISPLAYS/NL192108AC10-01D.pdf
[2] http://www.nlt-technologies.co.jp/en/products/nl192108ac10-01d.html
[3] http://www.tq-group.com/en/products/product-details/prod/embedded-modul-tqma6x/extb/Main/productdetail
[4] http://www.tq-group.com/en/products/product-details/prod/starterkit-stka6x/extb/Main/productdetail

Signed-off-by: Peter Seiderer <ps.report@gmx.net>
---
 drivers/gpu/drm/drm_fb_helper.c             | 10 ++++++----
 drivers/staging/imx-drm/ipu-v3/ipu-common.c |  6 ++++++
 drivers/staging/imx-drm/ipuv3-plane.c       |  7 +++++++
 3 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d99df15..50d980d 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -939,12 +939,14 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 		if (desired_mode) {
 			if (gamma_size == 0)
 				gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
-			if (desired_mode->hdisplay < sizes.fb_width)
-				sizes.fb_width = desired_mode->hdisplay;
+			/* double the frame buffer horizontal size */
+			if (2 * desired_mode->hdisplay < sizes.fb_width)
+				sizes.fb_width = 2 * desired_mode->hdisplay;
 			if (desired_mode->vdisplay < sizes.fb_height)
 				sizes.fb_height = desired_mode->vdisplay;
-			if (desired_mode->hdisplay > sizes.surface_width)
-				sizes.surface_width = desired_mode->hdisplay;
+			/* double the frame buffer horizontal size */
+			if (2 * desired_mode->hdisplay > sizes.surface_width)
+				sizes.surface_width =  2 * desired_mode->hdisplay;
 			if (desired_mode->vdisplay > sizes.surface_height)
 				sizes.surface_height = desired_mode->vdisplay;
 			crtc_count++;
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
index ca85d3d..05eb053 100644
--- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
@@ -613,6 +613,7 @@ int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
 {
 	unsigned long lock_flags;
 	u32 val;
+	u32 dval;
 
 	spin_lock_irqsave(&ipu->lock, lock_flags);
 
@@ -627,6 +628,11 @@ int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
 
 	val = ipu_cm_read(ipu, IPU_CONF);
 	val |= mask;
+
+	/* first disable DI0/DI1 to achieve a synchronous start of both lvds channels */
+	dval = val & (~(IPU_CONF_DI0_EN | IPU_CONF_DI1_EN));
+	ipu_cm_write(ipu, dval, IPU_CONF);
+
 	ipu_cm_write(ipu, val, IPU_CONF);
 
 	spin_unlock_irqrestore(&ipu->lock, lock_flags);
diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c
index 34b642a..8a44d5e 100644
--- a/drivers/staging/imx-drm/ipuv3-plane.c
+++ b/drivers/staging/imx-drm/ipuv3-plane.c
@@ -19,6 +19,7 @@
 
 #include "ipu-v3/imx-ipu-v3.h"
 #include "ipuv3-plane.h"
+#include "ipu-v3/ipu-prv.h"
 
 #define to_ipu_plane(x)	container_of(x, struct ipu_plane, base)
 
@@ -80,6 +81,12 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
 
 	eba = cma_obj->paddr + fb->offsets[0] +
 	      fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
+
+	/* start the second lvds output channel with half pitch offset */
+	if (ipu_plane->ipu_ch->num == 28) {
+		eba += fb->pitches[0] / 2;
+	}
+
 	ipu_cpmem_set_buffer(cpmem, 0, eba);
 	ipu_cpmem_set_buffer(cpmem, 1, eba);
 
-- 
1.8.1.4

