linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/10] splash screen on the stm32f769 disco board
@ 2018-03-02 15:44 yannick fertre
  2018-03-02 15:44 ` [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller yannick fertre
                   ` (10 more replies)
  0 siblings, 11 replies; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Version 2:
- Replace debug log by pr_error, pr_warn or pr_info.
- Rework bridge between ltdc & dsi panel
- Rework backligh management (with or witout gpio)
- Rework panel otm8009a
- Add new panel raydium rm68200

Version 1:
- Initial commit

This serie contains all patchsets needed for displaying a splash screen 
on the stm32f769 disco board.

yannick fertre (10):
  video: stm32: stm32_ltdc: add bridge to display controller
  video: stm32: stm32_ltdc: update debug log
  video: add support of MIPI DSI interface
  video: add support of panel OTM8009A
  video: add MIPI DSI host controller bridge
  video: add support of STM32 MIPI DSI controller driver
  video: add support of panel rm68200
  arm: dts: stm32: add dsi for STM32F746
  arm: dts: stm32: add display for STM32F769 disco board
  board: Add STM32F769 SoC, discovery board support

 arch/arm/dts/stm32f746.dtsi        |  12 +
 arch/arm/dts/stm32f769-disco.dts   |  71 ++++
 configs/stm32f769-disco_defconfig  |  63 +++
 drivers/video/Kconfig              |  32 ++
 drivers/video/Makefile             |   4 +
 drivers/video/dw_mipi_dsi.c        | 822 +++++++++++++++++++++++++++++++++++++
 drivers/video/mipi_display.c       | 807 ++++++++++++++++++++++++++++++++++++
 drivers/video/orisetech_otm8009a.c | 329 +++++++++++++++
 drivers/video/raydium-rm68200.c    | 329 +++++++++++++++
 drivers/video/stm32/Kconfig        |  10 +
 drivers/video/stm32/Makefile       |   1 +
 drivers/video/stm32/stm32_dsi.c    | 427 +++++++++++++++++++
 drivers/video/stm32/stm32_ltdc.c   | 144 ++++---
 include/dw_mipi_dsi.h              |  32 ++
 include/mipi_display.h             | 257 +++++++++++-
 15 files changed, 3285 insertions(+), 55 deletions(-)
 create mode 100644 configs/stm32f769-disco_defconfig
 create mode 100644 drivers/video/dw_mipi_dsi.c
 create mode 100644 drivers/video/mipi_display.c
 create mode 100644 drivers/video/orisetech_otm8009a.c
 create mode 100644 drivers/video/raydium-rm68200.c
 create mode 100644 drivers/video/stm32/stm32_dsi.c
 create mode 100644 include/dw_mipi_dsi.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-13  8:44   ` Patrice CHOTARD
  2018-03-02 15:44 ` [PATCH v2 02/10] video: stm32: stm32_ltdc: update debug log yannick fertre
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Manage a bridge insert between the display controller & a panel.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/stm32/stm32_ltdc.c | 107 ++++++++++++++++++++++++++-------------
 1 file changed, 71 insertions(+), 36 deletions(-)

diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
index e160c77..bd9c0de 100644
--- a/drivers/video/stm32/stm32_ltdc.c
+++ b/drivers/video/stm32/stm32_ltdc.c
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <clk.h>
+#include <display.h>
 #include <dm.h>
 #include <panel.h>
 #include <reset.h>
@@ -15,12 +16,12 @@
 #include <asm/io.h>
 #include <asm/arch/gpio.h>
 #include <dm/device-internal.h>
+#include <video_bridge.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
 struct stm32_ltdc_priv {
 	void __iomem *regs;
-	struct display_timing timing;
 	enum video_log2_bpp l2bpp;
 	u32 bg_col_argb;
 	u32 crop_x, crop_y, crop_w, crop_h;
@@ -210,23 +211,23 @@ static void stm32_ltdc_enable(struct stm32_ltdc_priv *priv)
 	setbits_le32(priv->regs + LTDC_GCR, GCR_LTDCEN);
 }
 
-static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv)
+static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv,
+				struct display_timing *timings)
 {
 	void __iomem *regs = priv->regs;
-	struct display_timing *timing = &priv->timing;
 	u32 hsync, vsync, acc_hbp, acc_vbp, acc_act_w, acc_act_h;
 	u32 total_w, total_h;
 	u32 val;
 
 	/* Convert video timings to ltdc timings */
-	hsync = timing->hsync_len.typ - 1;
-	vsync = timing->vsync_len.typ - 1;
-	acc_hbp = hsync + timing->hback_porch.typ;
-	acc_vbp = vsync + timing->vback_porch.typ;
-	acc_act_w = acc_hbp + timing->hactive.typ;
-	acc_act_h = acc_vbp + timing->vactive.typ;
-	total_w = acc_act_w + timing->hfront_porch.typ;
-	total_h = acc_act_h + timing->vfront_porch.typ;
+	hsync = timings->hsync_len.typ - 1;
+	vsync = timings->vsync_len.typ - 1;
+	acc_hbp = hsync + timings->hback_porch.typ;
+	acc_vbp = vsync + timings->vback_porch.typ;
+	acc_act_w = acc_hbp + timings->hactive.typ;
+	acc_act_h = acc_vbp + timings->vactive.typ;
+	total_w = acc_act_w + timings->hfront_porch.typ;
+	total_h = acc_act_h + timings->vfront_porch.typ;
 
 	/* Synchronization sizes */
 	val = (hsync << 16) | vsync;
@@ -248,14 +249,14 @@ static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv)
 
 	/* Signal polarities */
 	val = 0;
-	debug("%s: timing->flags 0x%08x\n", __func__, timing->flags);
-	if (timing->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+	debug("%s: timing->flags 0x%08x\n", __func__, timings->flags);
+	if (timings->flags & DISPLAY_FLAGS_HSYNC_HIGH)
 		val |= GCR_HSPOL;
-	if (timing->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+	if (timings->flags & DISPLAY_FLAGS_VSYNC_HIGH)
 		val |= GCR_VSPOL;
-	if (timing->flags & DISPLAY_FLAGS_DE_HIGH)
+	if (timings->flags & DISPLAY_FLAGS_DE_HIGH)
 		val |= GCR_DEPOL;
-	if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+	if (timings->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
 		val |= GCR_PCPOL;
 	clrsetbits_le32(regs + LTDC_GCR,
 			GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
@@ -331,7 +332,11 @@ static int stm32_ltdc_probe(struct udevice *dev)
 	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
 	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 	struct stm32_ltdc_priv *priv = dev_get_priv(dev);
-	struct udevice *panel;
+#ifdef CONFIG_VIDEO_BRIDGE
+	struct udevice *bridge = NULL;
+#endif
+	struct udevice *panel = NULL;
+	struct display_timing timings;
 	struct clk pclk;
 	struct reset_ctl rst;
 	int rate, ret;
@@ -364,63 +369,93 @@ static int stm32_ltdc_probe(struct udevice *dev)
 	/* Reset */
 	reset_deassert(&rst);
 
-	ret = uclass_first_device(UCLASS_PANEL, &panel);
+#ifdef CONFIG_VIDEO_BRIDGE
+	ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
 	if (ret) {
-		debug("%s: panel device error %d\n", __func__, ret);
-		return ret;
+		debug("%s: No video bridge, or no backlight on bridge\n",
+		      __func__);
 	}
 
-	ret = panel_enable_backlight(panel);
+	if (bridge) {
+		ret = video_bridge_attach(bridge);
+		if (ret) {
+			debug("%s: fail to attach bridge\n", __func__);
+			return ret;
+		}
+	}
+#endif
+	ret = uclass_first_device(UCLASS_PANEL, &panel);
 	if (ret) {
-		debug("%s: panel %s enable backlight error %d\n",
-		      __func__, panel->name, ret);
+		debug("%s: panel device error %d\n", __func__, ret);
 		return ret;
 	}
 
-	ret = fdtdec_decode_display_timing(gd->fdt_blob,
-					   dev_of_offset(dev), 0,
-					   &priv->timing);
+	ret = fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(panel),
+					   0, &timings);
 	if (ret) {
 		debug("%s: decode display timing error %d\n",
 		      __func__, ret);
-		return -EINVAL;
+		return ret;
 	}
 
-	rate = clk_set_rate(&pclk, priv->timing.pixelclock.typ);
+	rate = clk_set_rate(&pclk, timings.pixelclock.typ);
 	if (rate < 0) {
 		debug("%s: fail to set pixel clock %d hz %d hz\n",
-		      __func__, priv->timing.pixelclock.typ, rate);
+		      __func__, timings.pixelclock.typ, rate);
 		return rate;
 	}
 
 	debug("%s: Set pixel clock req %d hz get %d hz\n", __func__,
-	      priv->timing.pixelclock.typ, rate);
+	      timings.pixelclock.typ, rate);
 
 	/* TODO Below parameters are hard-coded for the moment... */
 	priv->l2bpp = VIDEO_BPP16;
 	priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */
 	priv->crop_x = 0;
 	priv->crop_y = 0;
-	priv->crop_w = priv->timing.hactive.typ;
-	priv->crop_h = priv->timing.vactive.typ;
+	priv->crop_w = timings.hactive.typ;
+	priv->crop_h = timings.vactive.typ;
 	priv->alpha = 0xFF;
 
 	debug("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
-	      priv->timing.hactive.typ, priv->timing.vactive.typ,
+	      timings.hactive.typ, timings.vactive.typ,
 	      VNBITS(priv->l2bpp), uc_plat->base);
 	debug("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
 	      priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
 	      priv->bg_col_argb, priv->alpha);
 
 	/* Configure & start LTDC */
-	stm32_ltdc_set_mode(priv);
+	stm32_ltdc_set_mode(priv, &timings);
 	stm32_ltdc_set_layer1(priv, uc_plat->base);
 	stm32_ltdc_enable(priv);
 
-	uc_priv->xsize = priv->timing.hactive.typ;
-	uc_priv->ysize = priv->timing.vactive.typ;
+	uc_priv->xsize = timings.hactive.typ;
+	uc_priv->ysize = timings.vactive.typ;
 	uc_priv->bpix = priv->l2bpp;
 
+#ifdef CONFIG_VIDEO_BRIDGE
+	if (bridge) {
+		ret = video_bridge_set_backlight(bridge, 80);
+		if (ret) {
+			debug("%s: fail to set backlight\n", __func__);
+			return ret;
+		}
+	} else {
+		ret = panel_enable_backlight(panel);
+		if (ret) {
+			debug("%s: panel %s enable backlight error %d\n",
+			      __func__, panel->name, ret);
+			return ret;
+		}
+	}
+#else
+	ret = panel_enable_backlight(panel);
+	if (ret) {
+		debug("%s: panel %s enable backlight error %d\n",
+		      __func__, panel->name, ret);
+		return ret;
+	}
+#endif
 	video_set_flush_dcache(dev, true);
 
 	return 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 02/10] video: stm32: stm32_ltdc: update debug log
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
  2018-03-02 15:44 ` [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-13  8:48   ` Patrice CHOTARD
  2018-03-02 15:44 ` [PATCH v2 03/10] video: add support of MIPI DSI interface yannick fertre
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Replace  macro debug by pr_error, pr_warn or pr_info.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/stm32/stm32_ltdc.c | 62 ++++++++++++++++++++--------------------
 1 file changed, 31 insertions(+), 31 deletions(-)

diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
index bd9c0de..e95f35c 100644
--- a/drivers/video/stm32/stm32_ltdc.c
+++ b/drivers/video/stm32/stm32_ltdc.c
@@ -176,13 +176,13 @@ static u32 stm32_ltdc_get_pixel_format(enum video_log2_bpp l2bpp)
 	case VIDEO_BPP2:
 	case VIDEO_BPP4:
 	default:
-		debug("%s: warning %dbpp not supported yet, %dbpp instead\n",
-		      __func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
+		pr_warn("%s: warning %dbpp not supported yet, %dbpp instead\n",
+			__func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
 		pf = PF_RGB565;
 		break;
 	}
 
-	debug("%s: %d bpp -> ltdc pf %d\n", __func__, VNBITS(l2bpp), pf);
+	pr_info("%s: %d bpp -> ltdc pf %d\n", __func__, VNBITS(l2bpp), pf);
 
 	return (u32)pf;
 }
@@ -249,7 +249,7 @@ static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv,
 
 	/* Signal polarities */
 	val = 0;
-	debug("%s: timing->flags 0x%08x\n", __func__, timings->flags);
+	pr_info("%s: timing->flags 0x%08x\n", __func__, timings->flags);
 	if (timings->flags & DISPLAY_FLAGS_HSYNC_HIGH)
 		val |= GCR_HSPOL;
 	if (timings->flags & DISPLAY_FLAGS_VSYNC_HIGH)
@@ -343,26 +343,26 @@ static int stm32_ltdc_probe(struct udevice *dev)
 
 	priv->regs = (void *)dev_read_addr(dev);
 	if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) {
-		debug("%s: ltdc dt register address error\n", __func__);
+		pr_err("%s: ltdc dt register address error\n", __func__);
 		return -EINVAL;
 	}
 
 	ret = clk_get_by_index(dev, 0, &pclk);
 	if (ret) {
-		debug("%s: peripheral clock get error %d\n", __func__, ret);
+		pr_err("%s: peripheral clock get error %d\n", __func__, ret);
 		return ret;
 	}
 
 	ret = clk_enable(&pclk);
 	if (ret) {
-		debug("%s: peripheral clock enable error %d\n",
-		      __func__, ret);
+		pr_err("%s: peripheral clock enable error %d\n",
+		       __func__, ret);
 		return ret;
 	}
 
 	ret = reset_get_by_index(dev, 0, &rst);
 	if (ret) {
-		debug("%s: missing ltdc hardware reset\n", __func__);
+		pr_err("%s: missing ltdc hardware reset\n", __func__);
 		return -ENODEV;
 	}
 
@@ -372,41 +372,40 @@ static int stm32_ltdc_probe(struct udevice *dev)
 #ifdef CONFIG_VIDEO_BRIDGE
 	ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
 	if (ret) {
-		debug("%s: No video bridge, or no backlight on bridge\n",
-		      __func__);
+		pr_info("%s: No video bridge, or no backlight on bridge\n",
+			__func__);
 	}
 
 	if (bridge) {
 		ret = video_bridge_attach(bridge);
 		if (ret) {
-			debug("%s: fail to attach bridge\n", __func__);
+			pr_err("%s: fail to attach bridge\n", __func__);
 			return ret;
 		}
 	}
 #endif
 	ret = uclass_first_device(UCLASS_PANEL, &panel);
 	if (ret) {
-		debug("%s: panel device error %d\n", __func__, ret);
+		pr_err("%s: panel device error %d\n", __func__, ret);
 		return ret;
 	}
 
 	ret = fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(panel),
 					   0, &timings);
 	if (ret) {
-		debug("%s: decode display timing error %d\n",
-		      __func__, ret);
+		pr_err("%s: decode display timing error %d\n", __func__, ret);
 		return ret;
 	}
 
 	rate = clk_set_rate(&pclk, timings.pixelclock.typ);
 	if (rate < 0) {
-		debug("%s: fail to set pixel clock %d hz %d hz\n",
-		      __func__, timings.pixelclock.typ, rate);
+		pr_err("%s: fail to set pixel clock %d hz %d hz\n",
+		       __func__, timings.pixelclock.typ, rate);
 		return rate;
 	}
 
-	debug("%s: Set pixel clock req %d hz get %d hz\n", __func__,
-	      timings.pixelclock.typ, rate);
+	pr_info("%s: Set pixel clock req %d hz get %d hz\n", __func__,
+		timings.pixelclock.typ, rate);
 
 	/* TODO Below parameters are hard-coded for the moment... */
 	priv->l2bpp = VIDEO_BPP16;
@@ -417,12 +416,12 @@ static int stm32_ltdc_probe(struct udevice *dev)
 	priv->crop_h = timings.vactive.typ;
 	priv->alpha = 0xFF;
 
-	debug("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
-	      timings.hactive.typ, timings.vactive.typ,
-	      VNBITS(priv->l2bpp), uc_plat->base);
-	debug("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
-	      priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
-	      priv->bg_col_argb, priv->alpha);
+	pr_info("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
+		timings.hactive.typ, timings.vactive.typ,
+		VNBITS(priv->l2bpp), uc_plat->base);
+	pr_info("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
+		priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
+		priv->bg_col_argb, priv->alpha);
 
 	/* Configure & start LTDC */
 	stm32_ltdc_set_mode(priv, &timings);
@@ -437,22 +436,22 @@ static int stm32_ltdc_probe(struct udevice *dev)
 	if (bridge) {
 		ret = video_bridge_set_backlight(bridge, 80);
 		if (ret) {
-			debug("%s: fail to set backlight\n", __func__);
+			pr_err("%s: fail to set backlight\n", __func__);
 			return ret;
 		}
 	} else {
 		ret = panel_enable_backlight(panel);
 		if (ret) {
-			debug("%s: panel %s enable backlight error %d\n",
-			      __func__, panel->name, ret);
+			pr_err("%s: panel %s enable backlight error %d\n",
+			       __func__, panel->name, ret);
 			return ret;
 		}
 	}
 #else
 	ret = panel_enable_backlight(panel);
 	if (ret) {
-		debug("%s: panel %s enable backlight error %d\n",
-		      __func__, panel->name, ret);
+		pr_err("%s: panel %s enable backlight error %d\n",
+		       __func__, panel->name, ret);
 		return ret;
 	}
 #endif
@@ -468,7 +467,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
 	uc_plat->size = CONFIG_VIDEO_STM32_MAX_XRES *
 			CONFIG_VIDEO_STM32_MAX_YRES *
 			(CONFIG_VIDEO_STM32_MAX_BPP >> 3);
-	debug("%s: frame buffer max size %d bytes\n", __func__, uc_plat->size);
+	pr_info("%s: frame buffer max size %d bytes\n", __func__,
+		uc_plat->size);
 
 	return 0;
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 03/10] video: add support of MIPI DSI interface
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
  2018-03-02 15:44 ` [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller yannick fertre
  2018-03-02 15:44 ` [PATCH v2 02/10] video: stm32: stm32_ltdc: update debug log yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-02 15:44 ` [PATCH v2 04/10] video: add support of panel OTM8009A yannick fertre
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Mipi_display.c contains a set of dsi helpers.
This file is a copy of file drm_mipi_dsi.c (linux kernel).

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/Kconfig        |   7 +
 drivers/video/Makefile       |   1 +
 drivers/video/mipi_display.c | 807 +++++++++++++++++++++++++++++++++++++++++++
 include/mipi_display.h       | 257 +++++++++++++-
 4 files changed, 1071 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/mipi_display.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 2fc0def..1981298 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -75,6 +75,13 @@ config VIDEO_ANSI
 	  Enable ANSI escape sequence decoding for a more fully functional
 	  console.
 
+config VIDEO_MIPI_DSI
+	bool "Support MIPI DSI interface"
+	depends on DM_VIDEO
+	default y if DM_VIDEO
+	help
+	  Support MIPI DSI interface for driving a MIPI compatible LCD panel.
+
 config CONSOLE_NORMAL
 	bool "Support a simple text console"
 	depends on DM_VIDEO
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index dfafe08..6f42cca 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_FORMIKE) += formike.o
 obj-$(CONFIG_LG4573) += lg4573.o
 obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
 obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
+obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_display.o
 obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
 obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
 obj-${CONFIG_EXYNOS_FB} += exynos/
diff --git a/drivers/video/mipi_display.c b/drivers/video/mipi_display.c
new file mode 100644
index 0000000..d90ff5d
--- /dev/null
+++ b/drivers/video/mipi_display.c
@@ -0,0 +1,807 @@
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
+ * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <display.h>
+#include <dm.h>
+#include <mipi_display.h>
+
+/**
+ * DOC: dsi helpers
+ *
+ * These functions contain some common logic and helpers to deal with MIPI DSI
+ * peripherals.
+ *
+ * Helpers are provided for a number of standard MIPI DSI command as well as a
+ * subset of the MIPI DCS command set.
+ */
+
+/**
+ * mipi_dsi_attach - attach a DSI device to its DSI host
+ * @dsi: DSI peripheral
+ */
+int mipi_dsi_attach(struct mipi_dsi_device *dsi)
+{
+	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+
+	if (!ops || !ops->attach)
+		return -ENOSYS;
+
+	return ops->attach(dsi->host, dsi);
+}
+EXPORT_SYMBOL(mipi_dsi_attach);
+
+/**
+ * mipi_dsi_detach - detach a DSI device from its DSI host
+ * @dsi: DSI peripheral
+ */
+int mipi_dsi_detach(struct mipi_dsi_device *dsi)
+{
+	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+
+	if (!ops || !ops->detach)
+		return -ENOSYS;
+
+	return ops->detach(dsi->host, dsi);
+}
+EXPORT_SYMBOL(mipi_dsi_detach);
+
+static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
+					struct mipi_dsi_msg *msg)
+{
+	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+
+	if (!ops || !ops->transfer)
+		return -ENOSYS;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+		msg->flags |= MIPI_DSI_MSG_USE_LPM;
+
+	return ops->transfer(dsi->host, msg);
+}
+
+/**
+ * mipi_dsi_packet_format_is_short - check if a packet is of the short format
+ * @type: MIPI DSI data type of the packet
+ *
+ * Return: true if the packet for the given data type is a short packet, false
+ * otherwise.
+ */
+bool mipi_dsi_packet_format_is_short(u8 type)
+{
+	switch (type) {
+	case MIPI_DSI_V_SYNC_START:
+	case MIPI_DSI_V_SYNC_END:
+	case MIPI_DSI_H_SYNC_START:
+	case MIPI_DSI_H_SYNC_END:
+	case MIPI_DSI_END_OF_TRANSMISSION:
+	case MIPI_DSI_COLOR_MODE_OFF:
+	case MIPI_DSI_COLOR_MODE_ON:
+	case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+	case MIPI_DSI_TURN_ON_PERIPHERAL:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_DCS_READ:
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		return true;
+	}
+
+	return false;
+}
+EXPORT_SYMBOL(mipi_dsi_packet_format_is_short);
+
+/**
+ * mipi_dsi_packet_format_is_long - check if a packet is of the long format
+ * @type: MIPI DSI data type of the packet
+ *
+ * Return: true if the packet for the given data type is a long packet, false
+ * otherwise.
+ */
+bool mipi_dsi_packet_format_is_long(u8 type)
+{
+	switch (type) {
+	case MIPI_DSI_NULL_PACKET:
+	case MIPI_DSI_BLANKING_PACKET:
+	case MIPI_DSI_GENERIC_LONG_WRITE:
+	case MIPI_DSI_DCS_LONG_WRITE:
+	case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_30:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_36:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_16:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_18:
+	case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_24:
+		return true;
+	}
+
+	return false;
+}
+EXPORT_SYMBOL(mipi_dsi_packet_format_is_long);
+
+/**
+ * mipi_dsi_create_packet - create a packet from a message according to the
+ *     DSI protocol
+ * @packet: pointer to a DSI packet structure
+ * @msg: message to translate into a packet
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
+			   const struct mipi_dsi_msg *msg)
+{
+	if (!packet || !msg)
+		return -EINVAL;
+
+	/* do some minimum sanity checking */
+	if (!mipi_dsi_packet_format_is_short(msg->type) &&
+	    !mipi_dsi_packet_format_is_long(msg->type))
+		return -EINVAL;
+
+	if (msg->channel > 3)
+		return -EINVAL;
+
+	memset(packet, 0, sizeof(*packet));
+	packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
+
+	/* TODO: compute ECC if hardware support is not available */
+
+	/*
+	 * Long write packets contain the word count in header bytes 1 and 2.
+	 * The payload follows the header and is word count bytes long.
+	 *
+	 * Short write packets encode up to two parameters in header bytes 1
+	 * and 2.
+	 */
+	if (mipi_dsi_packet_format_is_long(msg->type)) {
+		packet->header[1] = (msg->tx_len >> 0) & 0xff;
+		packet->header[2] = (msg->tx_len >> 8) & 0xff;
+
+		packet->payload_length = msg->tx_len;
+		packet->payload = msg->tx_buf;
+	} else {
+		const u8 *tx = msg->tx_buf;
+
+		packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
+		packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
+	}
+
+	packet->size = sizeof(packet->header) + packet->payload_length;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_create_packet);
+
+/*
+ * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
+ *    the payload in a long packet transmitted from the peripheral back to the
+ *    host processor
+ * @dsi: DSI peripheral device
+ * @value: the maximum size of the payload
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
+					    u16 value)
+{
+	u8 tx[2] = { value & 0xff, value >> 8 };
+	struct mipi_dsi_msg msg = {
+		.channel = dsi->channel,
+		.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
+		.tx_len = sizeof(tx),
+		.tx_buf = tx,
+	};
+	int ret = mipi_dsi_device_transfer(dsi, &msg);
+
+	return (ret < 0) ? ret : 0;
+}
+EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
+
+/**
+ * mipi_dsi_generic_write() - transmit data using a generic write packet
+ * @dsi: DSI peripheral device
+ * @payload: buffer containing the payload
+ * @size: size of payload buffer
+ *
+ * This function will automatically choose the right data type depending on
+ * the payload length.
+ *
+ * Return: The number of bytes transmitted on success or a negative error code
+ * on failure.
+ */
+ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
+			       size_t size)
+{
+	struct mipi_dsi_msg msg = {
+		.channel = dsi->channel,
+		.tx_buf = payload,
+		.tx_len = size
+	};
+
+	switch (size) {
+	case 0:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
+		break;
+
+	case 1:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
+		break;
+
+	case 2:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
+		break;
+
+	default:
+		msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
+		break;
+	}
+
+	return mipi_dsi_device_transfer(dsi, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_generic_write);
+
+/**
+ * mipi_dsi_generic_read() - receive data using a generic read packet
+ * @dsi: DSI peripheral device
+ * @params: buffer containing the request parameters
+ * @num_params: number of request parameters
+ * @data: buffer in which to return the received data
+ * @size: size of receive buffer
+ *
+ * This function will automatically choose the right data type depending on
+ * the number of parameters passed in.
+ *
+ * Return: The number of bytes successfully read or a negative error code on
+ * failure.
+ */
+ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
+			      size_t num_params, void *data, size_t size)
+{
+	struct mipi_dsi_msg msg = {
+		.channel = dsi->channel,
+		.tx_len = num_params,
+		.tx_buf = params,
+		.rx_len = size,
+		.rx_buf = data
+	};
+
+	switch (num_params) {
+	case 0:
+		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
+		break;
+
+	case 1:
+		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
+		break;
+
+	case 2:
+		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return mipi_dsi_device_transfer(dsi, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_generic_read);
+
+/**
+ * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
+ * @dsi: DSI peripheral device
+ * @data: buffer containing data to be transmitted
+ * @len: size of transmission buffer
+ *
+ * This function will automatically choose the right data type depending on
+ * the command payload length.
+ *
+ * Return: The number of bytes successfully transmitted or a negative error
+ * code on failure.
+ */
+ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
+				  const void *data, size_t len)
+{
+	struct mipi_dsi_msg msg = {
+		.channel = dsi->channel,
+		.tx_buf = data,
+		.tx_len = len
+	};
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+
+	case 1:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+
+	case 2:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+
+	default:
+		msg.type = MIPI_DSI_DCS_LONG_WRITE;
+		break;
+	}
+
+	return mipi_dsi_device_transfer(dsi, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer);
+
+/**
+ * mipi_dsi_dcs_write() - send DCS write command
+ * @dsi: DSI peripheral device
+ * @cmd: DCS command
+ * @data: buffer containing the command payload
+ * @len: command payload length
+ *
+ * This function will automatically choose the right data type depending on
+ * the command payload length.
+ *
+ * Return: The number of bytes successfully transmitted or a negative error
+ * code on failure.
+ */
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
+			   const void *data, size_t len)
+{
+	ssize_t err;
+	size_t size;
+	u8 *tx;
+
+	if (len > 0) {
+		size = 1 + len;
+
+		tx = kmalloc(size, GFP_KERNEL);
+		if (!tx)
+			return -ENOMEM;
+
+		/* concatenate the DCS command byte and the payload */
+		tx[0] = cmd;
+		memcpy(&tx[1], data, len);
+	} else {
+		tx = &cmd;
+		size = 1;
+	}
+
+	err = mipi_dsi_dcs_write_buffer(dsi, tx, size);
+
+	if (len > 0)
+		kfree(tx);
+
+	return err;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_write);
+
+/**
+ * mipi_dsi_dcs_read() - send DCS read request command
+ * @dsi: DSI peripheral device
+ * @cmd: DCS command
+ * @data: buffer in which to receive data
+ * @len: size of receive buffer
+ *
+ * Return: The number of bytes read or a negative error code on failure.
+ */
+ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
+			  size_t len)
+{
+	struct mipi_dsi_msg msg = {
+		.channel = dsi->channel,
+		.type = MIPI_DSI_DCS_READ,
+		.tx_buf = &cmd,
+		.tx_len = 1,
+		.rx_buf = data,
+		.rx_len = len
+	};
+
+	return mipi_dsi_device_transfer(dsi, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_read);
+
+/**
+ * mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any
+ *                                given pixel format defined by the MIPI DSI
+ *                                specification
+ * @fmt: MIPI DSI pixel format
+ *
+ * Returns: The number of bits per pixel of the given pixel format.
+ */
+int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
+{
+	switch (fmt) {
+	case MIPI_DSI_FMT_RGB888:
+	case MIPI_DSI_FMT_RGB666:
+		return 24;
+
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return 18;
+
+	case MIPI_DSI_FMT_RGB565:
+		return 16;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mipi_dsi_pixel_format_to_bpp);
+
+/**
+ * mipi_dsi_dcs_nop() - send DCS nop packet
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_nop);
+
+/**
+ * mipi_dsi_dcs_soft_reset() - perform a software reset of the display module
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
+
+/**
+ * mipi_dsi_dcs_get_power_mode() - query the display module's current power
+ *    mode
+ * @dsi: DSI peripheral device
+ * @mode: return location for the current power mode
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
+				sizeof(*mode));
+	if (err <= 0) {
+		if (err == 0)
+			err = -ENODATA;
+
+		return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
+
+/**
+ * mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image
+ *    data used by the interface
+ * @dsi: DSI peripheral device
+ * @format: return location for the pixel format
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format,
+				sizeof(*format));
+	if (err <= 0) {
+		if (err == 0)
+			err = -ENODATA;
+
+		return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format);
+
+/**
+ * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
+ *    display module except interface communication
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode);
+
+/**
+ * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
+ *    module
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode);
+
+/**
+ * mipi_dsi_dcs_set_display_off() - stop displaying the image data on the
+ *    display device
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off);
+
+/**
+ * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
+ *    display device
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
+
+/**
+ * mipi_dsi_dcs_set_column_address() - define the column extent of the frame
+ *    memory accessed by the host processor
+ * @dsi: DSI peripheral device
+ * @start: first column of frame memory
+ * @end: last column of frame memory
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
+				    u16 end)
+{
+	u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
+				 sizeof(payload));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_column_address);
+
+/**
+ * mipi_dsi_dcs_set_page_address() - define the page extent of the frame
+ *    memory accessed by the host processor
+ * @dsi: DSI peripheral device
+ * @start: first page of frame memory
+ * @end: last page of frame memory
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
+				  u16 end)
+{
+	u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
+				 sizeof(payload));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address);
+
+/**
+ * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
+ *    output signal on the TE signal line
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off);
+
+/**
+ * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
+ *    output signal on the TE signal line.
+ * @dsi: DSI peripheral device
+ * @mode: the Tearing Effect Output Line mode
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
+			     enum mipi_dsi_dcs_tear_mode mode)
+{
+	u8 value = mode;
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
+				 sizeof(value));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
+
+/**
+ * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
+ *    data used by the interface
+ * @dsi: DSI peripheral device
+ * @format: pixel format
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
+				 sizeof(format));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
+
+/**
+ * mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for
+ *    the Tearing Effect output signal of the display module
+ * @dsi: DSI peripheral device
+ * @scanline: scanline to use as trigger
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline)
+{
+	u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, scanline >> 8,
+			  scanline & 0xff };
+	ssize_t err;
+
+	err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
+
+/**
+ * mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the
+ *    display
+ * @dsi: DSI peripheral device
+ * @brightness: brightness value
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
+					u16 brightness)
+{
+	u8 payload[2] = { brightness & 0xff, brightness >> 8 };
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+				 payload, sizeof(payload));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness);
+
+/**
+ * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value
+ *    of the display
+ * @dsi: DSI peripheral device
+ * @brightness: brightness value
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
+					u16 *brightness)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
+				brightness, sizeof(*brightness));
+	if (err <= 0) {
+		if (err == 0)
+			err = -ENODATA;
+
+		return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness);
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("MIPI DSI Bus");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/include/mipi_display.h b/include/mipi_display.h
index ddcc8ca..8f9d4a4 100644
--- a/include/mipi_display.h
+++ b/include/mipi_display.h
@@ -4,12 +4,16 @@
  *
  * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  * Copyright (C) 2006 Nokia Corporation
- * Author: Imre Deak <imre.deak@nokia.com>
+ * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
+ * Author(s): Imre Deak <imre.deak@nokia.com>
+ *	   Yannick Fertre <yannick.fertre@st.com>
+ *         Philippe Cornu <philippe.cornu@st.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
 #ifndef MIPI_DISPLAY_H
 #define MIPI_DISPLAY_H
 
@@ -115,6 +119,14 @@ enum {
 	MIPI_DCS_READ_MEMORY_CONTINUE	= 0x3E,
 	MIPI_DCS_SET_TEAR_SCANLINE	= 0x44,
 	MIPI_DCS_GET_SCANLINE		= 0x45,
+	MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 0x51,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 0x52,		/* MIPI DCS 1.3 */
+	MIPI_DCS_WRITE_CONTROL_DISPLAY  = 0x53,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_CONTROL_DISPLAY	= 0x54,		/* MIPI DCS 1.3 */
+	MIPI_DCS_WRITE_POWER_SAVE	= 0x55,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_POWER_SAVE		= 0x56,		/* MIPI DCS 1.3 */
+	MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 0x5E,	/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 0x5F,	/* MIPI DCS 1.3 */
 	MIPI_DCS_READ_DDB_START		= 0xA1,
 	MIPI_DCS_READ_DDB_CONTINUE	= 0xA8,
 };
@@ -127,4 +139,247 @@ enum {
 #define MIPI_DCS_PIXEL_FMT_8BIT		2
 #define MIPI_DCS_PIXEL_FMT_3BIT		1
 
+struct mipi_dsi_host;
+struct mipi_dsi_device;
+
+/* request ACK from peripheral */
+#define MIPI_DSI_MSG_REQ_ACK	BIT(0)
+/* use Low Power Mode to transmit message */
+#define MIPI_DSI_MSG_USE_LPM	BIT(1)
+
+/**
+ * struct mipi_dsi_msg - read/write DSI buffer
+ * @channel: virtual channel id
+ * @type: payload data type
+ * @flags: flags controlling this message transmission
+ * @tx_len: length of @tx_buf
+ * @tx_buf: data to be written
+ * @rx_len: length of @rx_buf
+ * @rx_buf: data to be read, or NULL
+ */
+struct mipi_dsi_msg {
+	u8 channel;
+	u8 type;
+	u16 flags;
+
+	size_t tx_len;
+	const void *tx_buf;
+
+	size_t rx_len;
+	void *rx_buf;
+};
+
+bool mipi_dsi_packet_format_is_short(u8 type);
+bool mipi_dsi_packet_format_is_long(u8 type);
+
+/**
+ * struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format
+ * @size: size (in bytes) of the packet
+ * @header: the four bytes that make up the header (Data ID, Word Count or
+ *     Packet Data, and ECC)
+ * @payload_length: number of bytes in the payload
+ * @payload: a pointer to a buffer containing the payload, if any
+ */
+struct mipi_dsi_packet {
+	size_t size;
+	u8 header[4];
+	size_t payload_length;
+	const u8 *payload;
+};
+
+int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
+			   const struct mipi_dsi_msg *msg);
+
+/**
+ * struct mipi_dsi_host_ops - DSI bus operations
+ * @attach: attach DSI device to DSI host
+ * @detach: detach DSI device from DSI host
+ * @transfer: transmit a DSI packet
+ *
+ * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
+ * structures. This structure contains information about the type of packet
+ * being transmitted as well as the transmit and receive buffers. When an
+ * error is encountered during transmission, this function will return a
+ * negative error code. On success it shall return the number of bytes
+ * transmitted for write packets or the number of bytes received for read
+ * packets.
+ *
+ * Note that typically DSI packet transmission is atomic, so the .transfer()
+ * function will seldomly return anything other than the number of bytes
+ * contained in the transmit buffer on success.
+ */
+struct mipi_dsi_host_ops {
+	int (*attach)(struct mipi_dsi_host *host,
+		      struct mipi_dsi_device *dsi);
+	int (*detach)(struct mipi_dsi_host *host,
+		      struct mipi_dsi_device *dsi);
+	ssize_t (*transfer)(struct mipi_dsi_host *host,
+			    const struct mipi_dsi_msg *msg);
+};
+
+/**
+ * struct mipi_dsi_host - DSI host device
+ * @dev: driver model device node for this DSI host
+ * @ops: DSI host operations
+ * @list: list management
+ */
+struct mipi_dsi_host {
+	struct device *dev;
+	const struct mipi_dsi_host_ops *ops;
+	struct list_head list;
+};
+
+/* DSI mode flags */
+
+/* video mode */
+#define MIPI_DSI_MODE_VIDEO		BIT(0)
+/* video burst mode */
+#define MIPI_DSI_MODE_VIDEO_BURST	BIT(1)
+/* video pulse mode */
+#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE	BIT(2)
+/* enable auto vertical count mode */
+#define MIPI_DSI_MODE_VIDEO_AUTO_VERT	BIT(3)
+/* enable hsync-end packets in vsync-pulse and v-porch area */
+#define MIPI_DSI_MODE_VIDEO_HSE		BIT(4)
+/* disable hfront-porch area */
+#define MIPI_DSI_MODE_VIDEO_HFP		BIT(5)
+/* disable hback-porch area */
+#define MIPI_DSI_MODE_VIDEO_HBP		BIT(6)
+/* disable hsync-active area */
+#define MIPI_DSI_MODE_VIDEO_HSA		BIT(7)
+/* flush display FIFO on vsync pulse */
+#define MIPI_DSI_MODE_VSYNC_FLUSH	BIT(8)
+/* disable EoT packets in HS mode */
+#define MIPI_DSI_MODE_EOT_PACKET	BIT(9)
+/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
+#define MIPI_DSI_CLOCK_NON_CONTINUOUS	BIT(10)
+/* transmit data in low power */
+#define MIPI_DSI_MODE_LPM		BIT(11)
+
+enum mipi_dsi_pixel_format {
+	MIPI_DSI_FMT_RGB888,
+	MIPI_DSI_FMT_RGB666,
+	MIPI_DSI_FMT_RGB666_PACKED,
+	MIPI_DSI_FMT_RGB565,
+};
+
+#define DSI_DEV_NAME_SIZE		20
+
+/**
+ * struct mipi_dsi_device_info - template for creating a mipi_dsi_device
+ * @type: DSI peripheral chip type
+ * @channel: DSI virtual channel assigned to peripheral
+ * @node: pointer to OF device node or NULL
+ *
+ * This is populated and passed to mipi_dsi_device_new to create a new
+ * DSI device
+ */
+struct mipi_dsi_device_info {
+	char type[DSI_DEV_NAME_SIZE];
+	u32 channel;
+	struct device_node *node;
+};
+
+/**
+ * struct mipi_dsi_device - DSI peripheral device
+ * @host: DSI host for this peripheral
+ * @dev: driver model device node for this peripheral
+ * @name: DSI peripheral chip type
+ * @channel: virtual channel assigned to the peripheral
+ * @format: pixel format for video mode
+ * @lanes: number of active data lanes
+ * @mode_flags: DSI operation mode related flags
+ */
+struct mipi_dsi_device {
+	struct mipi_dsi_host *host;
+	struct udevice *dev;
+
+	char name[DSI_DEV_NAME_SIZE];
+	unsigned int channel;
+	unsigned int lanes;
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode_flags;
+};
+
+/**
+ * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
+ * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
+ *    information only
+ * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
+ *    V-Blanking and H-Blanking information
+ */
+enum mipi_dsi_dcs_tear_mode {
+	MIPI_DSI_DCS_TEAR_MODE_VBLANK,
+	MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
+};
+
+/**
+ * struct mipi_dsi_panel_plat - DSI panel platform data
+ * @device: DSI peripheral device
+ */
+struct mipi_dsi_panel_plat {
+	struct mipi_dsi_device *device;
+};
+
+int mipi_dsi_attach(struct mipi_dsi_device *dsi);
+int mipi_dsi_detach(struct mipi_dsi_device *dsi);
+int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt);
+
+/* bit compatible with the xrandr RR_ definitions (bits 0-13)
+ *
+ * ABI warning: Existing userspace really expects
+ * the mode flags to match the xrandr definitions. Any
+ * changes that don't match the xrandr definitions will
+ * likely need a new client cap or some other mechanism
+ * to avoid breaking existing userspace. This includes
+ * allocating new flags in the previously unused bits!
+ */
+#define MIPI_DSI_FLAG_PHSYNC			BIT(0)
+#define MIPI_DSI_FLAG_NHSYNC			BIT(1)
+#define MIPI_DSI_FLAG_PVSYNC			BIT(2)
+#define MIPI_DSI_FLAG_NVSYNC			BIT(3)
+#define MIPI_DSI_FLAG_INTERLACE			BIT(4)
+#define MIPI_DSI_FLAG_DBLSCAN			BIT(5)
+#define MIPI_DSI_FLAG_CSYNC			BIT(6)
+#define MIPI_DSI_FLAG_PCSYNC			BIT(7)
+#define MIPI_DSI_FLAG_NCSYNC			BIT(8)
+#define MIPI_DSI_FLAG_HSKEW			BIT(9) /* hskew provided */
+#define MIPI_DSI_FLAG_BCAST			BIT(10)
+#define MIPI_DSI_FLAG_PIXMUX			BIT(11)
+#define MIPI_DSI_FLAG_DBLCLK			BIT(12)
+#define MIPI_DSI_FLAG_CLKDIV2			BIT(13)
+
+/* request ACK from peripheral */
+#define MIPI_DSI_MSG_REQ_ACK	BIT(0)
+/* use Low Power Mode to transmit message */
+#define MIPI_DSI_MSG_USE_LPM	BIT(1)
+
+ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
+				  const void *data, size_t len);
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
+			   const void *data, size_t len);
+ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
+			  size_t len);
+int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode);
+int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format);
+int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
+				    u16 end);
+int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
+				  u16 end);
+int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
+			     enum mipi_dsi_dcs_tear_mode mode);
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
+int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline);
+int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
+					u16 brightness);
+int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
+					u16 *brightness);
+
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 04/10] video: add support of panel OTM8009A
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (2 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 03/10] video: add support of MIPI DSI interface yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-13  8:52   ` Patrice CHOTARD
  2018-03-02 15:44 ` [PATCH v2 05/10] video: add MIPI DSI host controller bridge yannick fertre
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Support for Orise Tech otm8009a 480p dsi 2dl video mode panel.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/Kconfig              |   8 +
 drivers/video/Makefile             |   1 +
 drivers/video/orisetech_otm8009a.c | 329 +++++++++++++++++++++++++++++++++++++
 3 files changed, 338 insertions(+)
 create mode 100644 drivers/video/orisetech_otm8009a.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1981298..b5fc535 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -320,6 +320,14 @@ config VIDEO_LCD_ANX9804
 	from a parallel LCD interface and translate it on the fy into a DP
 	interface for driving eDP TFT displays. It uses I2C for configuration.
 
+config VIDEO_LCD_ORISETECH_OTM8009A
+	bool "OTM8009A DSI LCD panel support"
+	depends on DM_VIDEO
+	select VIDEO_MIPI_DSI
+	default n
+	---help---
+	Support for Orise Tech otm8009a 480p dsi 2dl video mode panel.
+
 config VIDEO_LCD_SSD2828
 	bool "SSD2828 bridge chip"
 	default n
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 6f42cca..65002af 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o
 obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o
 obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
 obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
+obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
 obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
 obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o
 obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
diff --git a/drivers/video/orisetech_otm8009a.c b/drivers/video/orisetech_otm8009a.c
new file mode 100644
index 0000000..79f2da8
--- /dev/null
+++ b/drivers/video/orisetech_otm8009a.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
+ * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
+ *	      Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
+ *
+ * This otm8009a panel driver is based on the panel driver from
+ * drivers/gpu/drm/panel/panel-orisetech-otm8009a.c (kernel linux)
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+#include <asm/gpio.h>
+#include <backlight.h>
+#include <common.h>
+#include <dm.h>
+#include <panel.h>
+#include <power/regulator.h>
+#include <mipi_display.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DRV_NAME "orisetech_otm8009a"
+
+#define OTM8009A_BACKLIGHT_DEFAULT	240
+#define OTM8009A_BACKLIGHT_MAX		255
+
+/* Manufacturer Command Set */
+#define MCS_ADRSFT	0x0000	/* Address Shift Function */
+#define MCS_PANSET	0xB3A6	/* Panel Type Setting */
+#define MCS_SD_CTRL	0xC0A2	/* Source Driver Timing Setting */
+#define MCS_P_DRV_M	0xC0B4	/* Panel Driving Mode */
+#define MCS_OSC_ADJ	0xC181	/* Oscillator Adjustment for Idle/Normal mode */
+#define MCS_RGB_VID_SET	0xC1A1	/* RGB Video Mode Setting */
+#define MCS_SD_PCH_CTRL	0xC480	/* Source Driver Precharge Control */
+#define MCS_NO_DOC1	0xC48A	/* Command not documented */
+#define MCS_PWR_CTRL1	0xC580	/* Power Control Setting 1 */
+#define MCS_PWR_CTRL2	0xC590	/* Power Control Setting 2 for Normal Mode */
+#define MCS_PWR_CTRL4	0xC5B0	/* Power Control Setting 4 for DC Voltage */
+#define MCS_PANCTRLSET1	0xCB80	/* Panel Control Setting 1 */
+#define MCS_PANCTRLSET2	0xCB90	/* Panel Control Setting 2 */
+#define MCS_PANCTRLSET3	0xCBA0	/* Panel Control Setting 3 */
+#define MCS_PANCTRLSET4	0xCBB0	/* Panel Control Setting 4 */
+#define MCS_PANCTRLSET5	0xCBC0	/* Panel Control Setting 5 */
+#define MCS_PANCTRLSET6	0xCBD0	/* Panel Control Setting 6 */
+#define MCS_PANCTRLSET7	0xCBE0	/* Panel Control Setting 7 */
+#define MCS_PANCTRLSET8	0xCBF0	/* Panel Control Setting 8 */
+#define MCS_PANU2D1	0xCC80	/* Panel U2D Setting 1 */
+#define MCS_PANU2D2	0xCC90	/* Panel U2D Setting 2 */
+#define MCS_PANU2D3	0xCCA0	/* Panel U2D Setting 3 */
+#define MCS_PAND2U1	0xCCB0	/* Panel D2U Setting 1 */
+#define MCS_PAND2U2	0xCCC0	/* Panel D2U Setting 2 */
+#define MCS_PAND2U3	0xCCD0	/* Panel D2U Setting 3 */
+#define MCS_GOAVST	0xCE80	/* GOA VST Setting */
+#define MCS_GOACLKA1	0xCEA0	/* GOA CLKA1 Setting */
+#define MCS_GOACLKA3	0xCEB0	/* GOA CLKA3 Setting */
+#define MCS_GOAECLK	0xCFC0	/* GOA ECLK Setting */
+#define MCS_NO_DOC2	0xCFD0	/* Command not documented */
+#define MCS_GVDDSET	0xD800	/* GVDD/NGVDD */
+#define MCS_VCOMDC	0xD900	/* VCOM Voltage Setting */
+#define MCS_GMCT2_2P	0xE100	/* Gamma Correction 2.2+ Setting */
+#define MCS_GMCT2_2N	0xE200	/* Gamma Correction 2.2- Setting */
+#define MCS_NO_DOC3	0xF5B6	/* Command not documented */
+#define MCS_CMD2_ENA1	0xFF00	/* Enable Access Command2 "CMD2" */
+#define MCS_CMD2_ENA2	0xFF80	/* Enable Access Orise Command2 */
+
+struct otm8009a_panel_priv {
+	struct udevice *reg;
+	struct gpio_desc reset;
+};
+
+static void otm8009a_dcs_write_buf(struct udevice *dev, const void *data,
+				   size_t len)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = plat->device;
+
+	if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
+		pr_err("mipi dsi dcs write buffer failed\n");
+}
+
+#define dcs_write_seq(dev, seq...)				\
+({								\
+	static const u8 d[] = { seq };				\
+	otm8009a_dcs_write_buf(dev, d, ARRAY_SIZE(d));		\
+})
+
+#define dcs_write_cmd_at(dev, cmd, seq...)		\
+({							\
+	static const u16 c = cmd;			\
+	struct udevice *device = dev;			\
+	dcs_write_seq(device, MCS_ADRSFT, (c) & 0xFF);	\
+	dcs_write_seq(device, (c) >> 8, seq);		\
+})
+
+static int otm8009a_init_sequence(struct udevice *dev)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = plat->device;
+	int ret;
+
+	/* Enter CMD2 */
+	dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
+
+	/* Enter Orise Command2 */
+	dcs_write_cmd_at(dev, MCS_CMD2_ENA2, 0x80, 0x09);
+
+	dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL, 0x30);
+	mdelay(10);
+
+	dcs_write_cmd_at(dev, MCS_NO_DOC1, 0x40);
+	mdelay(10);
+
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL4 + 1, 0xA9);
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 1, 0x34);
+	dcs_write_cmd_at(dev, MCS_P_DRV_M, 0x50);
+	dcs_write_cmd_at(dev, MCS_VCOMDC, 0x4E);
+	dcs_write_cmd_at(dev, MCS_OSC_ADJ, 0x66); /* 65Hz */
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 2, 0x01);
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 5, 0x34);
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 4, 0x33);
+	dcs_write_cmd_at(dev, MCS_GVDDSET, 0x79, 0x79);
+	dcs_write_cmd_at(dev, MCS_SD_CTRL + 1, 0x1B);
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 2, 0x83);
+	dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL + 1, 0x83);
+	dcs_write_cmd_at(dev, MCS_RGB_VID_SET, 0x0E);
+	dcs_write_cmd_at(dev, MCS_PANSET, 0x00, 0x01);
+
+	dcs_write_cmd_at(dev, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
+	dcs_write_cmd_at(dev, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
+			 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
+	dcs_write_cmd_at(dev, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
+			 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
+	dcs_write_cmd_at(dev, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
+			 0x01, 0x02, 0x00, 0x00);
+
+	dcs_write_cmd_at(dev, MCS_NO_DOC2, 0x00);
+
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			 0, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			 0, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
+			 0, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
+			 4, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	dcs_write_cmd_at(dev, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+			 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
+
+	dcs_write_cmd_at(dev, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
+			 0x00, 0x00, 0x00, 0x00);
+	dcs_write_cmd_at(dev, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
+	dcs_write_cmd_at(dev, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
+			 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_cmd_at(dev, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
+			 0x00, 0x00, 0x00, 0x00);
+	dcs_write_cmd_at(dev, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
+	dcs_write_cmd_at(dev, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
+			 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+	dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 1, 0x66);
+
+	dcs_write_cmd_at(dev, MCS_NO_DOC3, 0x06);
+
+	dcs_write_cmd_at(dev, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
+			 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
+			 0x01);
+	dcs_write_cmd_at(dev, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
+			 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
+			 0x01);
+
+	/* Exit CMD2 */
+	dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
+
+	ret =  mipi_dsi_dcs_nop(device);
+	if (ret)
+		return ret;
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(device);
+	if (ret)
+		return ret;
+
+	/* Wait for sleep out exit */
+	mdelay(120);
+
+	/* Default portrait 480x800 rgb24 */
+	dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+
+	ret =  mipi_dsi_dcs_set_column_address(device, 0, 479);
+	if (ret)
+		return ret;
+
+	ret =  mipi_dsi_dcs_set_page_address(device, 0, 799);
+	if (ret)
+		return ret;
+
+	/* See otm8009a driver documentation for pixel format descriptions */
+	ret =  mipi_dsi_dcs_set_pixel_format(device, MIPI_DCS_PIXEL_FMT_24BIT |
+					     MIPI_DCS_PIXEL_FMT_24BIT << 4);
+	if (ret)
+		return ret;
+
+	/* Disable CABC feature */
+	dcs_write_seq(dev, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+	ret = mipi_dsi_dcs_set_display_on(device);
+	if (ret)
+		return ret;
+
+	ret = mipi_dsi_dcs_nop(device);
+	if (ret)
+		return ret;
+
+	/* Send Command GRAM memory write (no parameters) */
+	dcs_write_seq(dev, MIPI_DCS_WRITE_MEMORY_START);
+
+	return 0;
+}
+
+static int otm8009a_panel_enable_backlight(struct udevice *dev)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = plat->device;
+	int ret;
+
+	device->lanes = 2;
+	device->format = MIPI_DSI_FMT_RGB888;
+	device->mode_flags = MIPI_DSI_MODE_VIDEO |
+			     MIPI_DSI_MODE_VIDEO_BURST |
+			     MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_attach(device);
+	if (ret < 0)
+		return ret;
+
+	pr_info("%s: initialize panel\n", __func__);
+	ret = otm8009a_init_sequence(dev);
+	if (ret)
+		return ret;
+
+	/*
+	 * Power on the backlight with the requested brightness
+	 * Note We can not use mipi_dsi_dcs_set_display_brightness()
+	 * as otm8009a driver support only 8-bit brightness (1 param).
+	 */
+	pr_info("%s: start, backlight\n", __func__);
+
+	dcs_write_seq(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+		      OTM8009A_BACKLIGHT_DEFAULT);
+
+	/* Update Brightness Control & Backlight */
+	dcs_write_seq(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
+
+	/*  need to wait a few time before set the DSI bridge in video mode */
+	mdelay(10);
+
+	return 0;
+}
+
+static int otm8009a_panel_ofdata_to_platdata(struct udevice *dev)
+{
+	struct otm8009a_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
+		ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+						   "power-supply", &priv->reg);
+		if (ret) {
+			pr_err("%s: Warning: cannot get power supply\n",
+			       __func__);
+			if (ret != -ENOENT)
+				return ret;
+		}
+	}
+
+	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
+				   GPIOD_IS_OUT);
+	if (ret) {
+		pr_err("%s: Warning: cannot get reset GPIO\n", __func__);
+		if (ret != -ENOENT)
+			return ret;
+	}
+
+	/* reset panel must be done before probe */
+	dm_gpio_set_value(&priv->reset, true);
+
+	return 0;
+}
+
+static int otm8009a_panel_probe(struct udevice *dev)
+{
+	struct otm8009a_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
+		pr_err("%s: Enable regulator '%s'\n", __func__,
+		       priv->reg->name);
+		ret = regulator_set_enable(priv->reg, true);
+		if (ret)
+			return ret;
+	}
+
+	dm_gpio_set_value(&priv->reset, false);
+
+	return 0;
+}
+
+static const struct panel_ops otm8009a_panel_ops = {
+	.enable_backlight = otm8009a_panel_enable_backlight,
+};
+
+static const struct udevice_id otm8009a_panel_ids[] = {
+	{ .compatible = "orisetech,otm8009a" },
+	{ }
+};
+
+U_BOOT_DRIVER(otm8009a_panel) = {
+	.name			  = "otm8009a_panel",
+	.id			  = UCLASS_PANEL,
+	.of_match		  = otm8009a_panel_ids,
+	.ops			  = &otm8009a_panel_ops,
+	.ofdata_to_platdata	  = otm8009a_panel_ofdata_to_platdata,
+	.probe			  = otm8009a_panel_probe,
+	.platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat),
+	.priv_auto_alloc_size	= sizeof(struct otm8009a_panel_priv),
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 05/10] video: add MIPI DSI host controller bridge
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (3 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 04/10] video: add support of panel OTM8009A yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-02 18:58   ` Brian Norris
  2018-03-02 15:44 ` [PATCH v2 06/10] video: add support of STM32 MIPI DSI controller driver yannick fertre
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="a", Size: 26155 bytes --]

Add a Synopsys Designware MIPI DSI host bridge driver, based on the
Rockchip version from rockchip/dw-mipi-dsi.c with phy & bridge APIs.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/Kconfig       |   9 +
 drivers/video/Makefile      |   1 +
 drivers/video/dw_mipi_dsi.c | 822 ++++++++++++++++++++++++++++++++++++++++++++
 include/dw_mipi_dsi.h       |  32 ++
 4 files changed, 864 insertions(+)
 create mode 100644 drivers/video/dw_mipi_dsi.c
 create mode 100644 include/dw_mipi_dsi.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b5fc535..0f641d7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -657,6 +657,15 @@ config VIDEO_DW_HDMI
 	  rather requires a SoC-specific glue driver to call it), it
 	  can not be enabled from the configuration menu.
 
+config VIDEO_DW_MIPI_DSI
+	bool
+	help
+	  Enables the common driver code for the Designware MIPI DSI
+	  block found in SoCs from various vendors.
+	  As this does not provide any functionality by itself (but
+	  rather requires a SoC-specific glue driver to call it), it
+	  can not be enabled from the configuration menu.
+
 config VIDEO_SIMPLE
 	bool "Simple display driver for preconfigured display"
 	help
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 65002af..50be569 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_FORMIKE) += formike.o
 obj-$(CONFIG_LG4573) += lg4573.o
 obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
 obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o
 obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_display.o
 obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
 obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
diff --git a/drivers/video/dw_mipi_dsi.c b/drivers/video/dw_mipi_dsi.c
new file mode 100644
index 0000000..d7bd92d
--- /dev/null
+++ b/drivers/video/dw_mipi_dsi.c
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
+ *	      Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
+ *
+ * Modified by Yannick Fertre <yannick.fertre@st.com>
+ * This generic Synopsys DesignWare MIPI DSI host driver is based on the
+ * bridge synopsys from drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c (kernel
+ * linux).
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <mipi_display.h>
+#include <panel.h>
+#include <video.h>
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+#include <dm/device-internal.h>
+#include <dw_mipi_dsi.h>
+#include <linux/iopoll.h>
+#include <video_bridge.h>
+
+#define HWVER_131			0x31333100	/* IP version 1.31 */
+
+#define DSI_VERSION			0x00
+#define VERSION				GENMASK(31, 8)
+
+#define DSI_PWR_UP			0x04
+#define RESET				0
+#define POWERUP				BIT(0)
+
+#define DSI_CLKMGR_CFG			0x08
+#define TO_CLK_DIVISION(div)		(((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVISION(div)	((div) & 0xff)
+
+#define DSI_DPI_VCID			0x0c
+#define DPI_VCID(vcid)			((vcid) & 0x3)
+
+#define DSI_DPI_COLOR_CODING		0x10
+#define LOOSELY18_EN			BIT(8)
+#define DPI_COLOR_CODING_16BIT_1	0x0
+#define DPI_COLOR_CODING_16BIT_2	0x1
+#define DPI_COLOR_CODING_16BIT_3	0x2
+#define DPI_COLOR_CODING_18BIT_1	0x3
+#define DPI_COLOR_CODING_18BIT_2	0x4
+#define DPI_COLOR_CODING_24BIT		0x5
+
+#define DSI_DPI_CFG_POL			0x14
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM		0x18
+#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
+
+#define DSI_DBI_VCID			0x1c
+#define DSI_DBI_CFG			0x20
+#define DSI_DBI_PARTITIONING_EN		0x24
+#define DSI_DBI_CMDSIZE			0x28
+
+#define DSI_PCKHDL_CFG			0x2c
+#define CRC_RX_EN			BIT(4)
+#define ECC_RX_EN			BIT(3)
+#define BTA_EN				BIT(2)
+#define EOTP_RX_EN			BIT(1)
+#define EOTP_TX_EN			BIT(0)
+
+#define DSI_GEN_VCID			0x30
+
+#define DSI_MODE_CFG			0x34
+#define ENABLE_VIDEO_MODE		0
+#define ENABLE_CMD_MODE			BIT(0)
+
+#define DSI_VID_MODE_CFG		0x38
+#define ENABLE_LOW_POWER		(0x3f << 8)
+#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
+#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES	0x0
+#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS	0x1
+#define VID_MODE_TYPE_BURST			0x2
+#define VID_MODE_TYPE_MASK			0x3
+
+#define DSI_VID_PKT_SIZE		0x3c
+#define VID_PKT_SIZE(p)			((p) & 0x3fff)
+
+#define DSI_VID_NUM_CHUNKS		0x40
+#define VID_NUM_CHUNKS(c)		((c) & 0x1fff)
+
+#define DSI_VID_NULL_SIZE		0x44
+#define VID_NULL_SIZE(b)		((b) & 0x1fff)
+
+#define DSI_VID_HSA_TIME		0x48
+#define DSI_VID_HBP_TIME		0x4c
+#define DSI_VID_HLINE_TIME		0x50
+#define DSI_VID_VSA_LINES		0x54
+#define DSI_VID_VBP_LINES		0x58
+#define DSI_VID_VFP_LINES		0x5c
+#define DSI_VID_VACTIVE_LINES		0x60
+#define DSI_EDPI_CMD_SIZE		0x64
+
+#define DSI_CMD_MODE_CFG		0x68
+#define MAX_RD_PKT_SIZE_LP		BIT(24)
+#define DCS_LW_TX_LP			BIT(19)
+#define DCS_SR_0P_TX_LP			BIT(18)
+#define DCS_SW_1P_TX_LP			BIT(17)
+#define DCS_SW_0P_TX_LP			BIT(16)
+#define GEN_LW_TX_LP			BIT(14)
+#define GEN_SR_2P_TX_LP			BIT(13)
+#define GEN_SR_1P_TX_LP			BIT(12)
+#define GEN_SR_0P_TX_LP			BIT(11)
+#define GEN_SW_2P_TX_LP			BIT(10)
+#define GEN_SW_1P_TX_LP			BIT(9)
+#define GEN_SW_0P_TX_LP			BIT(8)
+#define ACK_RQST_EN			BIT(1)
+#define TEAR_FX_EN			BIT(0)
+
+#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
+					 DCS_LW_TX_LP | \
+					 DCS_SR_0P_TX_LP | \
+					 DCS_SW_1P_TX_LP | \
+					 DCS_SW_0P_TX_LP | \
+					 GEN_LW_TX_LP | \
+					 GEN_SR_2P_TX_LP | \
+					 GEN_SR_1P_TX_LP | \
+					 GEN_SR_0P_TX_LP | \
+					 GEN_SW_2P_TX_LP | \
+					 GEN_SW_1P_TX_LP | \
+					 GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR			0x6c
+#define DSI_GEN_PLD_DATA		0x70
+
+#define DSI_CMD_PKT_STATUS		0x74
+#define GEN_RD_CMD_BUSY			BIT(6)
+#define GEN_PLD_R_FULL			BIT(5)
+#define GEN_PLD_R_EMPTY			BIT(4)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_PLD_W_EMPTY			BIT(2)
+#define GEN_CMD_FULL			BIT(1)
+#define GEN_CMD_EMPTY			BIT(0)
+
+#define DSI_TO_CNT_CFG			0x78
+#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p)			((p) & 0xffff)
+
+#define DSI_HS_RD_TO_CNT		0x7c
+#define DSI_LP_RD_TO_CNT		0x80
+#define DSI_HS_WR_TO_CNT		0x84
+#define DSI_LP_WR_TO_CNT		0x88
+#define DSI_BTA_TO_CNT			0x8c
+
+#define DSI_LPCLK_CTRL			0x94
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG		0x98
+#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG			0x9c
+#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
+#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
+#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
+#define PHY_HS2LP_TIME_V131(lbcc)	(((lbcc) & 0x3ff) << 16)
+#define PHY_LP2HS_TIME_V131(lbcc)	((lbcc) & 0x3ff)
+
+#define DSI_PHY_RSTZ			0xa0
+#define PHY_DISFORCEPLL			0
+#define PHY_ENFORCEPLL			BIT(3)
+#define PHY_DISABLECLK			0
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			0
+#define PHY_UNRSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			0
+#define PHY_UNSHUTDOWNZ			BIT(0)
+
+#define DSI_PHY_IF_CFG			0xa4
+#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
+#define N_LANES(n)			(((n) - 1) & 0x3)
+
+#define DSI_PHY_ULPS_CTRL		0xa8
+#define DSI_PHY_TX_TRIGGERS		0xac
+
+#define DSI_PHY_STATUS			0xb0
+#define PHY_STOP_STATE_CLK_LANE		BIT(2)
+#define PHY_LOCK			BIT(0)
+
+#define DSI_PHY_TST_CTRL0		0xb4
+#define PHY_TESTCLK			BIT(1)
+#define PHY_UNTESTCLK			0
+#define PHY_TESTCLR			BIT(0)
+#define PHY_UNTESTCLR			0
+
+#define DSI_PHY_TST_CTRL1		0xb8
+#define PHY_TESTEN			BIT(16)
+#define PHY_UNTESTEN			0
+#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)			((n) & 0xff)
+
+#define DSI_INT_ST0			0xbc
+#define DSI_INT_ST1			0xc0
+#define DSI_INT_MSK0			0xc4
+#define DSI_INT_MSK1			0xc8
+
+#define DSI_PHY_TMR_RD_CFG		0xf4
+#define MAX_RD_TIME_V131(lbcc)		((lbcc) & 0x7fff)
+
+#define PHY_STATUS_TIMEOUT_US		10000
+#define CMD_PKT_STATUS_TIMEOUT_US	20000
+
+#define MSEC_PER_SEC			1000
+
+struct dw_mipi_dsi {
+	struct mipi_dsi_host dsi_host;
+	struct mipi_dsi_device *device;
+	void __iomem *base;
+	unsigned int lane_mbps; /* per lane */
+	u32 channel;
+	u32 lanes;
+	u32 format;
+	unsigned long mode_flags;
+	unsigned int max_data_lanes;
+	const struct dw_mipi_dsi_phy_ops *phy_ops;
+};
+
+static int dsi_mode_vrefresh(struct display_timing *timings)
+{
+	int refresh = 0;
+	unsigned int calc_val;
+	u32 htotal = timings->hactive.typ + timings->hfront_porch.typ +
+		     timings->hback_porch.typ + timings->hsync_len.typ;
+	u32 vtotal = timings->vactive.typ + timings->vfront_porch.typ +
+		     timings->vback_porch.typ + timings->vsync_len.typ;
+
+	if (htotal > 0 && vtotal > 0) {
+		calc_val = timings->pixelclock.typ;
+		calc_val /= htotal;
+		refresh = (calc_val + vtotal / 2) / vtotal;
+	}
+
+	return refresh;
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void dw_mipi_dsi_wait_for_two_frames(struct display_timing *timings)
+{
+	int refresh, two_frames;
+
+	refresh = dsi_mode_vrefresh(timings);
+	two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+	mdelay(two_frames);
+}
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+	return container_of(host, struct dw_mipi_dsi, dsi_host);
+}
+
+static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+	writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
+{
+	return readl(dsi->base + reg);
+}
+
+static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	if (device->lanes > dsi->max_data_lanes) {
+		pr_err("the number of data lanes(%u) is too many\n",
+		       device->lanes);
+		return -EINVAL;
+	}
+
+	dsi->lanes = device->lanes;
+	dsi->channel = device->channel;
+	dsi->format = device->format;
+	dsi->mode_flags = device->mode_flags;
+
+	return 0;
+}
+
+static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
+				   const struct mipi_dsi_msg *msg)
+{
+	bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
+	u32 val = 0;
+
+	if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
+		val |= ACK_RQST_EN;
+	if (lpm)
+		val |= CMD_MODE_ALL_LP;
+
+	dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS);
+	dsi_write(dsi, DSI_CMD_MODE_CFG, val);
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
+{
+	int ret;
+	u32 val, mask;
+
+	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+				 val, !(val & GEN_CMD_FULL),
+				 CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		pr_err("failed to get available command FIFO\n");
+		return ret;
+	}
+
+	dsi_write(dsi, DSI_GEN_HDR, hdr_val);
+
+	mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
+	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+				 val, (val & mask) == mask,
+				 CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		pr_err("failed to write command FIFO\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dw_mipi_dsi_write(struct dw_mipi_dsi *dsi,
+			     const struct mipi_dsi_packet *packet)
+{
+	const u8 *tx_buf = packet->payload;
+	int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
+	__le32 word;
+	u32 val;
+
+	while (len) {
+		if (len < pld_data_bytes) {
+			word = 0;
+			memcpy(&word, tx_buf, len);
+			dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
+			len = 0;
+		} else {
+			memcpy(&word, tx_buf, pld_data_bytes);
+			dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
+			tx_buf += pld_data_bytes;
+			len -= pld_data_bytes;
+		}
+
+		ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+					 val, !(val & GEN_PLD_W_FULL),
+					 CMD_PKT_STATUS_TIMEOUT_US);
+		if (ret < 0) {
+			dev_err(dsi->dev,
+				"failed to get available write payload FIFO\n");
+			return ret;
+		}
+	}
+
+	word = 0;
+	memcpy(&word, packet->header, sizeof(packet->header));
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, le32_to_cpu(word));
+}
+
+static int dw_mipi_dsi_read(struct dw_mipi_dsi *dsi,
+			    const struct mipi_dsi_msg *msg)
+{
+	int i, j, ret, len = msg->rx_len;
+	u8 *buf = msg->rx_buf;
+	u32 val;
+
+	/* Wait end of the read operation */
+	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+				 val, !(val & GEN_RD_CMD_BUSY),
+				 CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret) {
+		dev_err(dsi->dev, "Timeout during read operation\n");
+		return ret;
+	}
+
+	for (i = 0; i < len; i += 4) {
+		/* Read fifo must not be empty before all bytes are read */
+		ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
+					 val, !(val & GEN_PLD_R_EMPTY),
+					 CMD_PKT_STATUS_TIMEOUT_US);
+		if (ret) {
+			dev_err(dsi->dev, "Read payload FIFO is empty\n");
+			return ret;
+		}
+
+		val = dsi_read(dsi, DSI_GEN_PLD_DATA);
+		for (j = 0; j < 4 && j + i < len; j++)
+			buf[i + j] = val >> (8 * j);
+	}
+
+	return ret;
+}
+
+static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+					 const struct mipi_dsi_msg *msg)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+	struct mipi_dsi_packet packet;
+	int ret, nb_bytes;
+
+	ret = mipi_dsi_create_packet(&packet, msg);
+	if (ret) {
+		dev_err(dsi->dev, "failed to create packet: %d\n", ret);
+		return ret;
+	}
+
+	dw_mipi_message_config(dsi, msg);
+
+	ret = dw_mipi_dsi_write(dsi, &packet);
+	if (ret)
+		return ret;
+
+	if (msg->rx_buf && msg->rx_len) {
+		ret = dw_mipi_dsi_read(dsi, msg);
+		if (ret)
+			return ret;
+		nb_bytes = msg->rx_len;
+	} else {
+		nb_bytes = packet.size;
+	}
+
+	return nb_bytes;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+	.attach = dw_mipi_dsi_host_attach,
+	.transfer = dw_mipi_dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+	u32 val;
+
+	/*
+	 * TODO dw drv improvements
+	 * enabling low power is panel-dependent, we should use the
+	 * panel configuration here...
+	 */
+	val = ENABLE_LOW_POWER;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+		val |= VID_MODE_TYPE_BURST;
+	else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
+	else
+		val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
+
+	dsi_write(dsi, DSI_VID_MODE_CFG, val);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+				 unsigned long mode_flags)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+
+	if (mode_flags & MIPI_DSI_MODE_VIDEO) {
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
+		dw_mipi_dsi_video_mode_config(dsi);
+		dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+	} else {
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+	}
+
+	dsi_write(dsi, DSI_PWR_UP, POWERUP);
+}
+
+static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
+{
+	/*
+	 * The maximum permitted escape clock is 20MHz and it is derived from
+	 * lanebyteclk, which is running at "lane_mbps / 8".  Thus we want:
+	 *
+	 *     (lane_mbps >> 3) / esc_clk_division < 20
+	 * which is:
+	 *     (lane_mbps >> 3) / 20 > esc_clk_division
+	 */
+	u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1;
+
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+
+	/*
+	 * TODO dw drv improvements
+	 * timeout clock division should be computed with the
+	 * high speed transmission counter timeout and byte lane...
+	 */
+	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
+		  TX_ESC_CLK_DIVISION(esc_clk_division));
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+				   struct display_timing *timings)
+{
+	u32 val = 0, color = 0;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		color = DPI_COLOR_CODING_24BIT;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		color = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		color = DPI_COLOR_CODING_18BIT_1;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		color = DPI_COLOR_CODING_16BIT_1;
+		break;
+	}
+
+	if (dsi->mode_flags & DISPLAY_FLAGS_VSYNC_HIGH)
+		val |= VSYNC_ACTIVE_LOW;
+	if (dsi->mode_flags & DISPLAY_FLAGS_HSYNC_HIGH)
+		val |= HSYNC_ACTIVE_LOW;
+
+	dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
+	dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
+	dsi_write(dsi, DSI_DPI_CFG_POL, val);
+	/*
+	 * TODO dw drv improvements
+	 * largest packet sizes during hfp or during vsa/vpb/vfp
+	 * should be computed according to byte lane, lane number and only
+	 * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS)
+	 */
+	dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+		  | INVACT_LPCMD_TIME(4));
+}
+
+static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PCKHDL_CFG, CRC_RX_EN | ECC_RX_EN | BTA_EN);
+}
+
+static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
+					    struct display_timing *timings)
+{
+	/*
+	 * TODO dw drv improvements
+	 * only burst mode is supported here. For non-burst video modes,
+	 * we should compute DSI_VID_PKT_SIZE, DSI_VCCR.NUMC &
+	 * DSI_VNPCR.NPSIZE... especially because this driver supports
+	 * non-burst video modes, see dw_mipi_dsi_video_mode_config()...
+	 */
+	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(timings->hactive.typ));
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+	/*
+	 * TODO dw drv improvements
+	 * compute high speed transmission counter timeout according
+	 * to the timeout clock division (TO_CLK_DIVISION) and byte lane...
+	 */
+	dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	/*
+	 * TODO dw drv improvements
+	 * the Bus-Turn-Around Timeout Counter should be computed
+	 * according to byte lane...
+	 */
+	dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+	dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+					   struct display_timing *timings,
+					   u32 hcomponent)
+{
+	u32 frac, lbcc;
+
+	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
+
+	frac = lbcc % (timings->pixelclock.typ / 1000);
+	lbcc = lbcc / (timings->pixelclock.typ / 1000);
+	if (frac)
+		lbcc++;
+
+	return lbcc;
+}
+
+static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi,
+					  struct display_timing *timings)
+{
+	u32 htotal, hsa, hbp, lbcc;
+
+	htotal = timings->hactive.typ + timings->hfront_porch.typ +
+		 timings->hback_porch.typ + timings->hsync_len.typ;
+
+	hsa = timings->hback_porch.typ;
+	hbp = timings->hsync_len.typ;
+
+	/*
+	 * TODO dw drv improvements
+	 * computations below may be improved...
+	 */
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, timings, htotal);
+	dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, timings, hsa);
+	dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, timings, hbp);
+	dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+}
+
+static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi,
+					       struct display_timing *timings)
+{
+	u32 vactive, vsa, vfp, vbp;
+
+	vactive = timings->vactive.typ;
+	vsa =  timings->vback_porch.typ;
+	vfp =  timings->vfront_porch.typ;
+	vbp = timings->vsync_len.typ;
+
+	dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
+	dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
+	dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
+	dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
+}
+
+static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
+{
+	u32 hw_version;
+
+	/*
+	 * TODO dw drv improvements
+	 * data & clock lane timers should be computed according to panel
+	 * blankings and to the automatic clock lane control mode...
+	 * note: DSI_PHY_TMR_CFG.MAX_RD_TIME should be in line with
+	 * DSI_CMD_MODE_CFG.MAX_RD_PKT_SIZE_LP (see CMD_MODE_ALL_LP)
+	 */
+
+	hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
+
+	if (hw_version >= HWVER_131) {
+		dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME_V131(0x40) |
+			  PHY_LP2HS_TIME_V131(0x40));
+		dsi_write(dsi, DSI_PHY_TMR_RD_CFG, MAX_RD_TIME_V131(10000));
+	} else {
+		dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) |
+			  PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+	}
+
+	dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
+		  | PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+	/*
+	 * TODO dw drv improvements
+	 * stop wait time should be the maximum between host dsi
+	 * and panel stop wait times
+	 */
+	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+		  N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_dphy_init(struct dw_mipi_dsi *dsi)
+{
+	/* Clear PHY state */
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
+		  | PHY_RSTZ | PHY_SHUTDOWNZ);
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLR);
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
+}
+
+static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
+{
+	u32 val;
+	int ret;
+
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+		  PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+
+	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
+				 val & PHY_LOCK, PHY_STATUS_TIMEOUT_US);
+	if (ret < 0)
+		pr_err("failed to wait phy lock state\n");
+
+	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
+				 val, val & PHY_STOP_STATE_CLK_LANE,
+				 PHY_STATUS_TIMEOUT_US);
+	if (ret < 0)
+		pr_err("failed to wait phy clk lane stop state\n");
+}
+
+static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
+{
+	dsi_read(dsi, DSI_INT_ST0);
+	dsi_read(dsi, DSI_INT_ST1);
+	dsi_write(dsi, DSI_INT_MSK0, 0);
+	dsi_write(dsi, DSI_INT_MSK1, 0);
+}
+
+static void dw_mipi_dsi_bridge_set(struct dw_mipi_dsi *dsi,
+				   struct display_timing *timings)
+{
+	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->phy_ops;
+	int ret;
+
+	ret = phy_ops->get_lane_mbps(dsi->device, timings, dsi->lanes,
+				     dsi->format, &dsi->lane_mbps);
+	if (ret)
+		pr_err("Phy get_lane_mbps() failed\n");
+
+	dw_mipi_dsi_init(dsi);
+	dw_mipi_dsi_dpi_config(dsi, timings);
+	dw_mipi_dsi_packet_handler_config(dsi);
+	dw_mipi_dsi_video_mode_config(dsi);
+	dw_mipi_dsi_video_packet_config(dsi, timings);
+	dw_mipi_dsi_command_mode_config(dsi);
+	dw_mipi_dsi_line_timer_config(dsi, timings);
+	dw_mipi_dsi_vertical_timing_config(dsi, timings);
+
+	dw_mipi_dsi_dphy_init(dsi);
+	dw_mipi_dsi_dphy_timing_config(dsi);
+	dw_mipi_dsi_dphy_interface_config(dsi);
+
+	dw_mipi_dsi_clear_err(dsi);
+
+	ret = phy_ops->init(dsi->device);
+	if (ret)
+		pr_err("Phy init() failed\n");
+
+	dw_mipi_dsi_dphy_enable(dsi);
+
+	dw_mipi_dsi_wait_for_two_frames(timings);
+
+	/* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
+	dw_mipi_dsi_set_mode(dsi, 0);
+}
+
+void dw_mipi_dsi_bridge_enable(struct mipi_dsi_device *device)
+{
+	struct mipi_dsi_host *host = device->host;
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	/* Switch to video mode for panel-bridge enable & panel enable */
+	dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
+}
+EXPORT_SYMBOL_GPL(dw_mipi_dsi_bridge_enable);
+
+int dw_mipi_dsi_init_bridge(struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi_plat_data *platdata = dev_get_platdata(device->dev);
+	struct display_timing timings;
+	struct dw_mipi_dsi *dsi;
+	struct clk clk;
+	int ret;
+
+	dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
+
+	dsi->phy_ops = platdata->phy_ops;
+	dsi->max_data_lanes = platdata->max_data_lanes;
+	dsi->device = device;
+	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
+	device->host = &dsi->dsi_host;
+
+	 /* TODO Get these settings from panel */
+	dsi->lanes = 2;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+			  MIPI_DSI_MODE_VIDEO_BURST |
+			  MIPI_DSI_MODE_LPM;
+
+	dsi->base = (void *)dev_read_addr(device->dev);
+	if ((fdt_addr_t)dsi->base == FDT_ADDR_T_NONE) {
+		pr_err("%s: dsi dt register address error\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = fdtdec_decode_display_timing(gd->fdt_blob,
+					   dev_of_offset(platdata->panel),
+					   0, &timings);
+	if (ret) {
+		pr_err("%s: decode display timing error %d\n", __func__, ret);
+		return ret;
+	}
+
+	if (!dsi->phy_ops->init || !dsi->phy_ops->get_lane_mbps) {
+		pr_err("Phy not properly configured\n");
+		return -ENODEV;
+	}
+
+	ret = clk_get_by_name(device->dev, "px_clk", &clk);
+	if (ret) {
+		pr_err("%s: peripheral clock get error %d\n", __func__, ret);
+		return ret;
+	}
+
+	/*  get the pixel clock set by the clock framework */
+	timings.pixelclock.typ = clk_get_rate(&clk);
+
+	dw_mipi_dsi_bridge_set(dsi, &timings);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dw_mipi_dsi_init_bridge);
+
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertré <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("DW MIPI DSI host controller driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dw-mipi-dsi");
diff --git a/include/dw_mipi_dsi.h b/include/dw_mipi_dsi.h
new file mode 100644
index 0000000..8c33ec2
--- /dev/null
+++ b/include/dw_mipi_dsi.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017-2018, STMicroelectronics - All Rights Reserved
+ *
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Philippe Cornu <philippe.cornu@st.com>
+ *
+ * Modified by Yannick Fertre <yannick.fertre@st.com>
+ * This generic Synopsys DesignWare MIPI DSI host driver is based on the
+ * bridge synopsys from include/drm/bridge/dw_mipi_dsi.h (kernel linux).
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef __DW_MIPI_DSI__
+#define __DW_MIPI_DSI__
+
+struct dw_mipi_dsi_phy_ops {
+	int (*init)(void *priv_data);
+	int (*get_lane_mbps)(void *priv_data, struct display_timing *timings,
+			     u32 lanes, u32 format, unsigned int *lane_mbps);
+};
+
+struct dw_mipi_dsi_plat_data {
+	unsigned int max_data_lanes;
+	const struct dw_mipi_dsi_phy_ops *phy_ops;
+	struct udevice *panel;
+};
+
+int dw_mipi_dsi_init_bridge(struct mipi_dsi_device *device);
+void dw_mipi_dsi_bridge_enable(struct mipi_dsi_device *device);
+
+#endif /* __DW_MIPI_DSI__ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 06/10] video: add support of STM32 MIPI DSI controller driver
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (4 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 05/10] video: add MIPI DSI host controller bridge yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-13  8:56   ` Patrice CHOTARD
  2018-03-02 15:44 ` [PATCH v2 07/10] video: add support of panel rm68200 yannick fertre
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Add the STM32 DSI controller driver that uses the Synopsys DesignWare
MIPI DSI host controller bridge.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/stm32/Kconfig     |  10 +
 drivers/video/stm32/Makefile    |   1 +
 drivers/video/stm32/stm32_dsi.c | 427 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 438 insertions(+)
 create mode 100644 drivers/video/stm32/stm32_dsi.c

diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
index 113a2bb..2ea6f18 100644
--- a/drivers/video/stm32/Kconfig
+++ b/drivers/video/stm32/Kconfig
@@ -15,6 +15,16 @@ menuconfig VIDEO_STM32
 	  DSI. This option enables these supports which can be used on
 	  devices which have RGB TFT or DSI display connected.
 
+config VIDEO_STM32_DSI
+	bool "Enable STM32 DSI video support"
+	depends on VIDEO_STM32
+	select VIDEO_MIPI_DSI
+	select VIDEO_BRIDGE
+	select VIDEO_DW_MIPI_DSI
+	help
+	  This option enables support DSI internal bridge which can be used on
+	  devices which have DSI display connected.
+
 config VIDEO_STM32_MAX_XRES
 	int "Maximum horizontal resolution (for memory allocation purposes)"
 	depends on VIDEO_STM32
diff --git a/drivers/video/stm32/Makefile b/drivers/video/stm32/Makefile
index 372a2e1..f8c3ff7 100644
--- a/drivers/video/stm32/Makefile
+++ b/drivers/video/stm32/Makefile
@@ -8,3 +8,4 @@
 #
 
 obj-${CONFIG_VIDEO_STM32} = stm32_ltdc.o
+obj-${CONFIG_VIDEO_STM32_DSI} += stm32_dsi.o
diff --git a/drivers/video/stm32/stm32_dsi.c b/drivers/video/stm32/stm32_dsi.c
new file mode 100644
index 0000000..3e26433
--- /dev/null
+++ b/drivers/video/stm32/stm32_dsi.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
+ * Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
+ *	      Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
+ *
+ * This driver is based on the mipi dsi driver from
+ * drivers/gpu/drm/stm/dw_mipi_dsi-stm.c (kernel linux).
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <mipi_display.h>
+#include <dw_mipi_dsi.h>
+#include <linux/iopoll.h>
+#include <panel.h>
+#include <reset.h>
+#include <video.h>
+#include <video_bridge.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define HWVER_130			0x31333000	/* IP version 1.30 */
+#define HWVER_131			0x31333100	/* IP version 1.31 */
+
+/* DSI digital registers & bit definitions */
+#define DSI_VERSION			0x00
+#define VERSION				GENMASK(31, 8)
+
+/*
+ * DSI wrapper registers & bit definitions
+ * Note: registers are named as in the Reference Manual
+ */
+#define DSI_WCFGR	0x0400		/* Wrapper ConFiGuration Reg */
+#define WCFGR_DSIM	BIT(0)		/* DSI Mode */
+#define WCFGR_COLMUX	GENMASK(3, 1)	/* COLor MUltipleXing */
+
+#define DSI_WCR		0x0404		/* Wrapper Control Reg */
+#define WCR_DSIEN	BIT(3)		/* DSI ENable */
+
+#define DSI_WISR	0x040C		/* Wrapper Interrupt and Status Reg */
+#define WISR_PLLLS	BIT(8)		/* PLL Lock Status */
+#define WISR_RRS	BIT(12)		/* Regulator Ready Status */
+
+#define DSI_WPCR0	0x0418		/* Wrapper Phy Conf Reg 0 */
+#define WPCR0_UIX4	GENMASK(5, 0)	/* Unit Interval X 4 */
+#define WPCR0_TDDL	BIT(16)		/* Turn Disable Data Lanes */
+
+#define DSI_WRPCR	0x0430		/* Wrapper Regulator & Pll Ctrl Reg */
+#define WRPCR_PLLEN	BIT(0)		/* PLL ENable */
+#define WRPCR_NDIV	GENMASK(8, 2)	/* pll loop DIVision Factor */
+#define WRPCR_IDF	GENMASK(14, 11)	/* pll Input Division Factor */
+#define WRPCR_ODF	GENMASK(17, 16)	/* pll Output Division Factor */
+#define WRPCR_REGEN	BIT(24)		/* REGulator ENable */
+#define WRPCR_BGREN	BIT(28)		/* BandGap Reference ENable */
+#define IDF_MIN		1
+#define IDF_MAX		7
+#define NDIV_MIN	10
+#define NDIV_MAX	125
+#define ODF_MIN		1
+#define ODF_MAX		8
+
+/* dsi color format coding according to the datasheet */
+enum dsi_color {
+	DSI_RGB565_CONF1,
+	DSI_RGB565_CONF2,
+	DSI_RGB565_CONF3,
+	DSI_RGB666_CONF1,
+	DSI_RGB666_CONF2,
+	DSI_RGB888,
+};
+
+#define LANE_MIN_KBPS	31250
+#define LANE_MAX_KBPS	500000
+
+/* Timeout for regulator on/off, pll lock/unlock & fifo empty */
+#define TIMEOUT_US	200000
+
+struct stm32_dsi_priv {
+	struct mipi_dsi_device device;
+	void __iomem *base;
+	struct udevice *panel;
+	u32 pllref_clk;
+	u32 hw_version;
+	int lane_min_kbps;
+	int lane_max_kbps;
+};
+
+static inline void dsi_write(struct stm32_dsi_priv *dsi, u32 reg, u32 val)
+{
+	writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct stm32_dsi_priv *dsi, u32 reg)
+{
+	return readl(dsi->base + reg);
+}
+
+static inline void dsi_set(struct stm32_dsi_priv *dsi, u32 reg, u32 mask)
+{
+	dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
+}
+
+static inline void dsi_clear(struct stm32_dsi_priv *dsi, u32 reg, u32 mask)
+{
+	dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask);
+}
+
+static inline void dsi_update_bits(struct stm32_dsi_priv *dsi, u32 reg,
+				   u32 mask, u32 val)
+{
+	dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
+}
+
+static enum dsi_color dsi_color_from_mipi(u32 fmt)
+{
+	switch (fmt) {
+	case MIPI_DSI_FMT_RGB888:
+		return DSI_RGB888;
+	case MIPI_DSI_FMT_RGB666:
+		return DSI_RGB666_CONF2;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return DSI_RGB666_CONF1;
+	case MIPI_DSI_FMT_RGB565:
+		return DSI_RGB565_CONF1;
+	default:
+		pr_err("MIPI color invalid, so we use rgb888\n");
+	}
+	return DSI_RGB888;
+}
+
+static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
+{
+	int divisor = idf * odf;
+
+	/* prevent from division by 0 */
+	if (!divisor)
+		return 0;
+
+	return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
+}
+
+static int dsi_pll_get_params(struct stm32_dsi_priv *dsi,
+			      int clkin_khz, int clkout_khz,
+			      int *idf, int *ndiv, int *odf)
+{
+	int i, o, n, n_min, n_max;
+	int fvco_min, fvco_max, delta, best_delta; /* all in khz */
+
+	/* Early checks preventing division by 0 & odd results */
+	if (clkin_khz <= 0 || clkout_khz <= 0)
+		return -EINVAL;
+
+	fvco_min = dsi->lane_min_kbps * 2 * ODF_MAX;
+	fvco_max = dsi->lane_max_kbps * 2 * ODF_MIN;
+
+	best_delta = 1000000; /* big started value (1000000khz) */
+
+	for (i = IDF_MIN; i <= IDF_MAX; i++) {
+		/* Compute ndiv range according to Fvco */
+		n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1;
+		n_max = (fvco_max * i) / (2 * clkin_khz);
+
+		/* No need to continue idf loop if we reach ndiv max */
+		if (n_min >= NDIV_MAX)
+			break;
+
+		/* Clamp ndiv to valid values */
+		if (n_min < NDIV_MIN)
+			n_min = NDIV_MIN;
+		if (n_max > NDIV_MAX)
+			n_max = NDIV_MAX;
+
+		for (o = ODF_MIN; o <= ODF_MAX; o *= 2) {
+			n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
+			/* Check ndiv according to vco range */
+			if (n < n_min || n > n_max)
+				continue;
+			/* Check if new delta is better & saves parameters */
+			delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) -
+				clkout_khz;
+			if (delta < 0)
+				delta = -delta;
+			if (delta < best_delta) {
+				*idf = i;
+				*ndiv = n;
+				*odf = o;
+				best_delta = delta;
+			}
+			/* fast return in case of "perfect result" */
+			if (!delta)
+				return 0;
+		}
+	}
+
+	return 0;
+}
+
+static int dsi_phy_init(void *priv_data)
+{
+	struct mipi_dsi_device *device = priv_data;
+	struct stm32_dsi_priv *dsi = dev_get_priv(device->dev);
+	u32 val;
+	int ret;
+
+	/* Enable the regulator */
+	dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
+	ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS,
+				 TIMEOUT_US);
+	if (ret) {
+		pr_err("!TIMEOUT! waiting REGU\n");
+		return ret;
+	}
+
+	/* Enable the DSI PLL & wait for its lock */
+	dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN);
+	ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
+				 TIMEOUT_US);
+	if (ret) {
+		pr_err("!TIMEOUT! waiting PLL\n");
+		return ret;
+	}
+
+	/* Enable the DSI wrapper */
+	dsi_set(dsi, DSI_WCR, WCR_DSIEN);
+
+	return 0;
+}
+
+static int dsi_get_lane_mbps(void *priv_data, struct display_timing *timings,
+			     u32 lanes, u32 format, unsigned int *lane_mbps)
+{
+	struct mipi_dsi_device *device = priv_data;
+	struct stm32_dsi_priv *dsi = dev_get_priv(device->dev);
+	int idf, ndiv, odf, pll_in_khz, pll_out_khz;
+	int ret, bpp;
+	u32 val;
+
+	/* Update lane capabilities according to hw version */
+	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
+	dsi->lane_min_kbps = LANE_MIN_KBPS;
+	dsi->lane_max_kbps = LANE_MAX_KBPS;
+	if (dsi->hw_version == HWVER_131) {
+		dsi->lane_min_kbps *= 2;
+		dsi->lane_max_kbps *= 2;
+	}
+
+	pll_in_khz = dsi->pllref_clk / 1000;
+
+	/* Compute requested pll out */
+	bpp = mipi_dsi_pixel_format_to_bpp(format);
+	pll_out_khz = (timings->pixelclock.typ / 1000) * bpp / lanes;
+	/* Add 20% to pll out to be higher than pixel bw (burst mode only) */
+	pll_out_khz = (pll_out_khz * 12) / 10;
+	if (pll_out_khz > dsi->lane_max_kbps) {
+		pll_out_khz = dsi->lane_max_kbps;
+		pr_warn("Warning max phy mbps is used\n");
+	}
+	if (pll_out_khz < dsi->lane_min_kbps) {
+		pll_out_khz = dsi->lane_min_kbps;
+		pr_warn("Warning min phy mbps is used\n");
+	}
+
+	/* Compute best pll parameters */
+	idf = 0;
+	ndiv = 0;
+	odf = 0;
+	ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz,
+				 &idf, &ndiv, &odf);
+	if (ret) {
+		pr_warn("Warning dsi_pll_get_params(): bad params\n");
+		return ret;
+	}
+
+	/* Get the adjusted pll out value */
+	pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
+
+	/* Set the PLL division factors */
+	dsi_update_bits(dsi, DSI_WRPCR,	WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
+			(ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
+
+	/* Compute uix4 & set the bit period in high-speed mode */
+	val = 4000000 / pll_out_khz;
+	dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
+
+	/* Select video mode by resetting DSIM bit */
+	dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
+
+	/* Select the color coding */
+	dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX,
+			dsi_color_from_mipi(format) << 1);
+
+	*lane_mbps = pll_out_khz / 1000;
+
+	pr_info("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n",
+		pll_in_khz, pll_out_khz, *lane_mbps);
+
+	return 0;
+}
+
+static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
+	.init = dsi_phy_init,
+	.get_lane_mbps = dsi_get_lane_mbps,
+};
+
+static int stm32_dsi_attach(struct udevice *dev)
+{
+	struct stm32_dsi_priv *priv = dev_get_priv(dev);
+	struct dw_mipi_dsi_plat_data *platdata = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	int ret;
+
+	platdata->max_data_lanes = 2;
+	platdata->phy_ops = &dw_mipi_dsi_stm_phy_ops;
+
+	ret = uclass_first_device(UCLASS_PANEL, &platdata->panel);
+	if (ret) {
+		pr_err("%s: panel device error %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = dw_mipi_dsi_init_bridge(device);
+	if (ret) {
+		pr_err("Failed to initialize mipi dsi host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int stm32_dsi_set_backlight(struct udevice *dev, int percent)
+{
+	struct dw_mipi_dsi_plat_data *dplat = dev_get_platdata(dev);
+	struct stm32_dsi_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	struct udevice *panel = dplat->panel;
+	struct mipi_dsi_panel_plat *mplat;
+	int ret;
+
+	mplat = dev_get_platdata(panel);
+	mplat->device = device;
+
+	ret = panel_enable_backlight(panel);
+	if (ret) {
+		pr_err("%s: panel %s enable backlight error %d\n",
+		       __func__, panel->name, ret);
+		return ret;
+	}
+
+	dw_mipi_dsi_bridge_enable(device);
+
+	return 0;
+}
+
+static int stm32_dsi_probe(struct udevice *dev)
+{
+	struct stm32_dsi_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	struct reset_ctl rst;
+	struct clk clk;
+	int ret;
+
+	device->dev = dev;
+
+	priv->base = (void *)dev_read_addr(dev);
+	if ((fdt_addr_t)priv->base == FDT_ADDR_T_NONE) {
+		pr_err("%s: dsi dt register address error\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = clk_get_by_name(device->dev, "pclk", &clk);
+	if (ret) {
+		pr_err("%s: peripheral clock get error %d\n", __func__, ret);
+		return -ENODEV;
+	}
+
+	ret = clk_enable(&clk);
+	if (ret) {
+		pr_err("%s: peripheral clock enable error %d\n", __func__, ret);
+		return -ENODEV;
+	}
+
+	ret = clk_get_by_name(dev, "ref", &clk);
+	if (ret) {
+		pr_err("%s: pll reference clock get error %d\n", __func__, ret);
+		return ret;
+	}
+
+	priv->pllref_clk = (unsigned int)clk_get_rate(&clk);
+
+	ret = reset_get_by_index(device->dev, 0, &rst);
+	if (ret) {
+		pr_err("%s: missing dsi hardware reset\n", __func__);
+		return -ENODEV;
+	}
+
+	/* Reset */
+	reset_deassert(&rst);
+
+	return 0;
+}
+
+struct video_bridge_ops stm32_dsi_ops = {
+	.attach = stm32_dsi_attach,
+	.set_backlight = stm32_dsi_set_backlight,
+};
+
+static const struct udevice_id stm32_dsi_ids[] = {
+	{ .compatible = "st,stm32-dsi"},
+	{ }
+};
+
+U_BOOT_DRIVER(stm32_dsi) = {
+	.name				= "stm32-display-dsi",
+	.id				= UCLASS_VIDEO_BRIDGE,
+	.of_match			= stm32_dsi_ids,
+	.bind				= dm_scan_fdt_dev,
+	.probe				= stm32_dsi_probe,
+	.ops				= &stm32_dsi_ops,
+	.priv_auto_alloc_size		= sizeof(struct stm32_dsi_priv),
+	.platdata_auto_alloc_size	= sizeof(struct dw_mipi_dsi_plat_data),
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 07/10] video: add support of panel rm68200
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (5 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 06/10] video: add support of STM32 MIPI DSI controller driver yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-13  8:59   ` Patrice CHOTARD
  2018-03-02 15:44 ` [PATCH v2 08/10] arm: dts: stm32: add dsi for STM32F746 yannick fertre
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Support for Raydium rm68200 720p dsi 2dl video mode panel.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 drivers/video/Kconfig           |   8 +
 drivers/video/Makefile          |   1 +
 drivers/video/raydium-rm68200.c | 329 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 338 insertions(+)
 create mode 100644 drivers/video/raydium-rm68200.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0f641d7..2561c59 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -328,6 +328,14 @@ config VIDEO_LCD_ORISETECH_OTM8009A
 	---help---
 	Support for Orise Tech otm8009a 480p dsi 2dl video mode panel.
 
+config VIDEO_LCD_RAYDIUM_RM68200
+	bool "RM68200 DSI LCD panel support"
+	depends on DM_VIDEO
+	select VIDEO_MIPI_DSI
+	default n
+	---help---
+	Support for Raydium rm68200 720x1280 dsi 2dl video mode panel.
+
 config VIDEO_LCD_SSD2828
 	bool "SSD2828 bridge chip"
 	default n
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 50be569..1a6c8d3 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o
 obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
 obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
 obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
+obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o
 obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
 obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o
 obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
diff --git a/drivers/video/raydium-rm68200.c b/drivers/video/raydium-rm68200.c
new file mode 100644
index 0000000..46afb58
--- /dev/null
+++ b/drivers/video/raydium-rm68200.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
+ * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
+ *	      Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
+ *
+ * This rm68200 panel driver is based on the panel driver from
+ * drivers/gpu/drm/panel/panel-raydium-rm68200.c (kernel linux)
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+#include <asm/gpio.h>
+#include <backlight.h>
+#include <common.h>
+#include <dm.h>
+#include <panel.h>
+#include <power/regulator.h>
+#include <mipi_display.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DRV_NAME "raydium_rm68200"
+
+/*** Manufacturer Command Set ***/
+#define MCS_CMD_MODE_SW	0xFE /* CMD Mode Switch */
+#define MCS_CMD1_UCS	0x00 /* User Command Set (UCS = CMD1) */
+#define MCS_CMD2_P0	0x01 /* Manufacture Command Set Page0 (CMD2 P0) */
+#define MCS_CMD2_P1	0x02 /* Manufacture Command Set Page1 (CMD2 P1) */
+#define MCS_CMD2_P2	0x03 /* Manufacture Command Set Page2 (CMD2 P2) */
+#define MCS_CMD2_P3	0x04 /* Manufacture Command Set Page3 (CMD2 P3) */
+
+/* CMD2 P0 commands (Display Options and Power) */
+#define MCS_STBCTR	0x12 /* TE1 Output Setting Zig-Zag Connection */
+#define MCS_SGOPCTR	0x16 /* Source Bias Current */
+#define MCS_SDCTR	0x1A /* Source Output Delay Time */
+#define MCS_INVCTR	0x1B /* Inversion Type */
+#define MCS_EXT_PWR_IC	0x24 /* External PWR IC Control */
+#define MCS_SETAVDD	0x27 /* PFM Control for AVDD Output */
+#define MCS_SETAVEE	0x29 /* PFM Control for AVEE Output */
+#define MCS_BT2CTR	0x2B /* DDVDL Charge Pump Control */
+#define MCS_BT3CTR	0x2F /* VGH Charge Pump Control */
+#define MCS_BT4CTR	0x34 /* VGL Charge Pump Control */
+#define MCS_VCMCTR	0x46 /* VCOM Output Level Control */
+#define MCS_SETVGN	0x52 /* VG M/S N Control */
+#define MCS_SETVGP	0x54 /* VG M/S P Control */
+#define MCS_SW_CTRL	0x5F /* Interface Control for PFM and MIPI */
+
+/* CMD2 P2 commands (GOA Timing Control) - no description in datasheet */
+#define GOA_VSTV1	0x00
+#define GOA_VSTV2	0x07
+#define GOA_VCLK1	0x0E
+#define GOA_VCLK2	0x17
+#define GOA_VCLK_OPT1	0x20
+#define GOA_BICLK1	0x2A
+#define GOA_BICLK2	0x37
+#define GOA_BICLK3	0x44
+#define GOA_BICLK4	0x4F
+#define GOA_BICLK_OPT1	0x5B
+#define GOA_BICLK_OPT2	0x60
+#define MCS_GOA_GPO1	0x6D
+#define MCS_GOA_GPO2	0x71
+#define MCS_GOA_EQ	0x74
+#define MCS_GOA_CLK_GALLON 0x7C
+#define MCS_GOA_FS_SEL0	0x7E
+#define MCS_GOA_FS_SEL1	0x87
+#define MCS_GOA_FS_SEL2	0x91
+#define MCS_GOA_FS_SEL3	0x9B
+#define MCS_GOA_BS_SEL0	0xAC
+#define MCS_GOA_BS_SEL1	0xB5
+#define MCS_GOA_BS_SEL2	0xBF
+#define MCS_GOA_BS_SEL3	0xC9
+#define MCS_GOA_BS_SEL4	0xD3
+
+/* CMD2 P3 commands (Gamma) */
+#define MCS_GAMMA_VP	0x60 /* Gamma VP1~VP16 */
+#define MCS_GAMMA_VN	0x70 /* Gamma VN1~VN16 */
+
+struct rm68200_panel_priv {
+	struct udevice *reg;
+	struct udevice *backlight;
+	struct gpio_desc reset;
+};
+
+static void rm68200_dcs_write_buf(struct udevice *dev, const void *data,
+				  size_t len)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = plat->device;
+
+	if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
+		pr_err("mipi dsi dcs write buffer failed\n");
+}
+
+static void rm68200_dcs_write_cmd(struct udevice *dev, u8 cmd, u8 value)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = plat->device;
+
+	if (mipi_dsi_dcs_write(device, cmd, &value, 1) < 0)
+		pr_err("mipi dsi dcs write failed\n");
+}
+
+#define dcs_write_seq(ctx, seq...)				\
+({								\
+	static const u8 d[] = { seq };				\
+	rm68200_dcs_write_buf(ctx, d, ARRAY_SIZE(d));		\
+})
+
+/*
+ * This panel is not able to auto-increment all cmd addresses so for some of
+ * them, we need to send them one by one...
+ */
+#define dcs_write_cmd_seq(ctx, cmd, seq...)			\
+({								\
+	static const u8 d[] = { seq };				\
+	static int i;						\
+	for (i = 0; i < ARRAY_SIZE(d) ; i++)			\
+		rm68200_dcs_write_cmd(ctx, cmd + i, d[i]);	\
+})
+
+static int rm68200_init_sequence(struct udevice *dev)
+{
+	/* Enter CMD2 with page 0 */
+	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P0);
+	dcs_write_cmd_seq(dev, MCS_EXT_PWR_IC, 0xC0, 0x53, 0x00);
+	dcs_write_seq(dev, MCS_BT2CTR, 0xE5);
+	dcs_write_seq(dev, MCS_SETAVDD, 0x0A);
+	dcs_write_seq(dev, MCS_SETAVEE, 0x0A);
+	dcs_write_seq(dev, MCS_SGOPCTR, 0x52);
+	dcs_write_seq(dev, MCS_BT3CTR, 0x53);
+	dcs_write_seq(dev, MCS_BT4CTR, 0x5A);
+	dcs_write_seq(dev, MCS_INVCTR, 0x00);
+	dcs_write_seq(dev, MCS_STBCTR, 0x0A);
+	dcs_write_seq(dev, MCS_SDCTR, 0x06);
+	dcs_write_seq(dev, MCS_VCMCTR, 0x56);
+	dcs_write_seq(dev, MCS_SETVGN, 0xA0, 0x00);
+	dcs_write_seq(dev, MCS_SETVGP, 0xA0, 0x00);
+	dcs_write_seq(dev, MCS_SW_CTRL, 0x11); /* 2 data lanes, see doc */
+
+	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P2);
+	dcs_write_seq(dev, GOA_VSTV1, 0x05);
+	dcs_write_seq(dev, 0x02, 0x0B);
+	dcs_write_seq(dev, 0x03, 0x0F);
+	dcs_write_seq(dev, 0x04, 0x7D, 0x00, 0x50);
+	dcs_write_cmd_seq(dev, GOA_VSTV2, 0x05, 0x16, 0x0D, 0x11, 0x7D, 0x00,
+			  0x50);
+	dcs_write_cmd_seq(dev, GOA_VCLK1, 0x07, 0x08, 0x01, 0x02, 0x00, 0x7D,
+			  0x00, 0x85, 0x08);
+	dcs_write_cmd_seq(dev, GOA_VCLK2, 0x03, 0x04, 0x05, 0x06, 0x00, 0x7D,
+			  0x00, 0x85, 0x08);
+	dcs_write_seq(dev, GOA_VCLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00);
+	dcs_write_cmd_seq(dev, GOA_BICLK1, 0x07, 0x08);
+	dcs_write_seq(dev, 0x2D, 0x01);
+	dcs_write_seq(dev, 0x2F, 0x02, 0x00, 0x40, 0x05, 0x08, 0x54, 0x7D,
+		      0x00);
+	dcs_write_cmd_seq(dev, GOA_BICLK2, 0x03, 0x04, 0x05, 0x06, 0x00);
+	dcs_write_seq(dev, 0x3D, 0x40);
+	dcs_write_seq(dev, 0x3F, 0x05, 0x08, 0x54, 0x7D, 0x00);
+	dcs_write_seq(dev, GOA_BICLK3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(dev, GOA_BICLK4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(dev, 0x58, 0x00, 0x00, 0x00);
+	dcs_write_seq(dev, GOA_BICLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(dev, GOA_BICLK_OPT2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(dev, MCS_GOA_GPO1, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(dev, MCS_GOA_GPO2, 0x00, 0x20, 0x00);
+	dcs_write_seq(dev, MCS_GOA_EQ, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+		      0x00, 0x00);
+	dcs_write_seq(dev, MCS_GOA_CLK_GALLON, 0x00, 0x00);
+	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL0, 0xBF, 0x02, 0x06, 0x14, 0x10,
+			  0x16, 0x12, 0x08, 0x3F);
+	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0C,
+			  0x0A, 0x0E, 0x3F, 0x3F, 0x00);
+	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL2, 0x04, 0x3F, 0x3F, 0x3F, 0x3F,
+			  0x05, 0x01, 0x3F, 0x3F, 0x0F);
+	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL3, 0x0B, 0x0D, 0x3F, 0x3F, 0x3F,
+			  0x3F);
+	dcs_write_cmd_seq(dev, 0xA2, 0x3F, 0x09, 0x13, 0x17, 0x11, 0x15);
+	dcs_write_cmd_seq(dev, 0xA9, 0x07, 0x03, 0x3F);
+	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL0, 0x3F, 0x05, 0x01, 0x17, 0x13,
+			  0x15, 0x11, 0x0F, 0x3F);
+	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0B,
+			  0x0D, 0x09, 0x3F, 0x3F, 0x07);
+	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL2, 0x03, 0x3F, 0x3F, 0x3F, 0x3F,
+			  0x02, 0x06, 0x3F, 0x3F, 0x08);
+	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL3, 0x0C, 0x0A, 0x3F, 0x3F, 0x3F,
+			  0x3F, 0x3F, 0x0E, 0x10, 0x14);
+	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL4, 0x12, 0x16, 0x00, 0x04, 0x3F);
+	dcs_write_seq(dev, 0xDC, 0x02);
+	dcs_write_seq(dev, 0xDE, 0x12);
+
+	dcs_write_seq(dev, MCS_CMD_MODE_SW, 0x0E); /* No documentation */
+	dcs_write_seq(dev, 0x01, 0x75);
+
+	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P3);
+	dcs_write_cmd_seq(dev, MCS_GAMMA_VP, 0x00, 0x0C, 0x12, 0x0E, 0x06,
+			  0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,
+			  0x12, 0x0C, 0x00);
+	dcs_write_cmd_seq(dev, MCS_GAMMA_VN, 0x00, 0x0C, 0x12, 0x0E, 0x06,
+			  0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,
+			  0x12, 0x0C, 0x00);
+
+	/* Exit CMD2 */
+	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD1_UCS);
+
+	return 0;
+}
+
+static int rm68200_panel_enable_backlight(struct udevice *dev)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
+	struct mipi_dsi_device *device = plat->device;
+	struct rm68200_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	device->lanes = 2;
+	device->format = MIPI_DSI_FMT_RGB888;
+	device->mode_flags = MIPI_DSI_MODE_VIDEO |
+			     MIPI_DSI_MODE_VIDEO_BURST |
+			     MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_attach(device);
+	if (ret < 0)
+		return ret;
+
+	pr_info("%s: initialize panel\n", __func__);
+
+	ret = rm68200_init_sequence(dev);
+	if (ret)
+		return ret;
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(device);
+	if (ret)
+		return ret;
+
+	mdelay(125);
+
+	ret = mipi_dsi_dcs_set_display_on(device);
+	if (ret)
+		return ret;
+
+	mdelay(20);
+
+	pr_info("%s: start, backlight = '%s'\n", __func__,
+		priv->backlight->name);
+
+	ret = backlight_enable(priv->backlight);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rm68200_panel_ofdata_to_platdata(struct udevice *dev)
+{
+	struct rm68200_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
+		ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+						   "power-supply", &priv->reg);
+		if (ret) {
+			pr_info("%s: Warning: cannot get power supply\n",
+				__func__);
+			if (ret != -ENOENT)
+				return ret;
+		}
+	}
+
+	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
+				   GPIOD_IS_OUT);
+	if (ret) {
+		pr_info("%s: Warning: cannot get reset GPIO\n", __func__);
+		if (ret != -ENOENT)
+			return ret;
+	}
+
+	/* reset panel must be done before probe */
+	dm_gpio_set_value(&priv->reset, true);
+
+	return 0;
+}
+
+static int rm68200_panel_probe(struct udevice *dev)
+{
+	struct rm68200_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
+		pr_info("%s: Enable regulator '%s'\n", __func__,
+			priv->reg->name);
+		ret = regulator_set_enable(priv->reg, true);
+		if (ret)
+			return ret;
+	}
+
+	dm_gpio_set_value(&priv->reset, false);
+
+	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+					   "backlight", &priv->backlight);
+	if (ret) {
+		pr_info("%s: Cannot get backlight: ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct panel_ops rm68200_panel_ops = {
+	.enable_backlight = rm68200_panel_enable_backlight,
+};
+
+static const struct udevice_id rm68200_panel_ids[] = {
+	{ .compatible = "raydium,rm68200" },
+	{ }
+};
+
+U_BOOT_DRIVER(rm68200_panel) = {
+	.name			  = "rm68200_panel",
+	.id			  = UCLASS_PANEL,
+	.of_match		  = rm68200_panel_ids,
+	.ops			  = &rm68200_panel_ops,
+	.ofdata_to_platdata	  = rm68200_panel_ofdata_to_platdata,
+	.probe			  = rm68200_panel_probe,
+	.platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat),
+	.priv_auto_alloc_size	= sizeof(struct rm68200_panel_priv),
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 08/10] arm: dts: stm32: add dsi for STM32F746
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (6 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 07/10] video: add support of panel rm68200 yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-02 15:44 ` [PATCH v2 09/10] arm: dts: stm32: add display for STM32F769 disco board yannick fertre
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Add mipi dsi bridge node in device-tree.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 arch/arm/dts/stm32f746.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/dts/stm32f746.dtsi b/arch/arm/dts/stm32f746.dtsi
index e4d32bf..4ec954d 100644
--- a/arch/arm/dts/stm32f746.dtsi
+++ b/arch/arm/dts/stm32f746.dtsi
@@ -332,6 +332,18 @@
 			u-boot,dm-pre-reloc;
 			status = "disabled";
 		};
+
+		dsi: dsi@40016c00 {
+			compatible = "st,stm32-dsi";
+			reg = <0x40016C00 0x800>;
+			resets = <&rcc STM32F7_APB2_RESET(DSI)>;
+			clocks =  <&rcc 0 STM32F7_APB2_CLOCK(DSI)>,
+				  <&rcc 0 STM32F7_APB2_CLOCK(LTDC)>,
+				  <&clk_hse>;
+			clock-names = "pclk", "px_clk", "ref";
+			u-boot,dm-pre-reloc;
+			status = "disabled";
+		};
 	};
 };
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 09/10] arm: dts: stm32: add display for STM32F769 disco board
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (7 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 08/10] arm: dts: stm32: add dsi for STM32F746 yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-02 15:44 ` [PATCH v2 10/10] board: Add STM32F769 SoC, discovery board support yannick fertre
  2018-03-06 17:51 ` [PATCH v2 00/10] splash screen on the stm32f769 disco board Simon Glass
  10 siblings, 0 replies; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Enable the display controller, mipi dsi bridge & panel.
Set panel display timings.

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 arch/arm/dts/stm32f769-disco.dts | 71 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/arch/arm/dts/stm32f769-disco.dts b/arch/arm/dts/stm32f769-disco.dts
index 59c9d31..82985b9 100644
--- a/arch/arm/dts/stm32f769-disco.dts
+++ b/arch/arm/dts/stm32f769-disco.dts
@@ -42,6 +42,7 @@
 
 /dts-v1/;
 #include "stm32f746.dtsi"
+#include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/memory/stm32-sdram.h>
 
 / {
@@ -264,3 +265,73 @@
 	bus-width = <4>;
 	max-frequency = <25000000>;
 };
+
+&ltdc {
+	status = "okay";
+
+	port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ltdc_out_dsi: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&dsi_in>;
+		};
+	};
+};
+
+&dsi {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+			dsi_in: endpoint {
+				remote-endpoint = <&ltdc_out_dsi>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+			dsi_out: endpoint {
+				remote-endpoint = <&dsi_panel_in>;
+			};
+		};
+	};
+
+	panel-dsi@0 {
+		compatible = "orisetech,otm8009a";
+		reg = <0>; /* dsi virtual channel (0..3) */
+		reset-gpios = <&gpioj 15 GPIO_ACTIVE_LOW>;
+		status = "okay";
+
+		port {
+			dsi_panel_in: endpoint {
+				remote-endpoint = <&dsi_out>;
+			};
+		};
+
+		display-timings {
+			timing@0 {
+				clock-frequency = <32729000>;
+				hactive = <480>;
+				hfront-porch = <120>;
+				hback-porch = <63>;
+				hsync-len = <120>;
+				vactive = <800>;
+				vfront-porch = <12>;
+				vback-porch = <12>;
+				vsync-len = <12>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <1>;
+			};
+		};
+	};
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 10/10] board: Add STM32F769 SoC, discovery board support
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (8 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 09/10] arm: dts: stm32: add display for STM32F769 disco board yannick fertre
@ 2018-03-02 15:44 ` yannick fertre
  2018-03-13  8:41   ` Patrice CHOTARD
  2018-03-06 17:51 ` [PATCH v2 00/10] splash screen on the stm32f769 disco board Simon Glass
  10 siblings, 1 reply; 20+ messages in thread
From: yannick fertre @ 2018-03-02 15:44 UTC (permalink / raw)
  To: Vikas Manocha, Benjamin Gaignard, Yannick Fertre, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Simon Glass, Anatolij Gustschin,
	Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Signed-off-by: yannick fertre <yannick.fertre@st.com>
---
 configs/stm32f769-disco_defconfig | 63 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 configs/stm32f769-disco_defconfig

diff --git a/configs/stm32f769-disco_defconfig b/configs/stm32f769-disco_defconfig
new file mode 100644
index 0000000..ac34076
--- /dev/null
+++ b/configs/stm32f769-disco_defconfig
@@ -0,0 +1,63 @@
+CONFIG_ARM=y
+CONFIG_STM32=y
+CONFIG_SYS_MALLOC_F_LEN=0xC00
+CONFIG_STM32F7=y
+CONFIG_TARGET_STM32F746_DISCO=y
+CONFIG_DEFAULT_DEVICE_TREE="stm32f769-disco"
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
+# CONFIG_DISPLAY_CPUINFO is not set
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_BOARD_EARLY_INIT_F=y
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot > "
+CONFIG_AUTOBOOT_KEYED=y
+CONFIG_AUTOBOOT_PROMPT="Hit SPACE in %d seconds to stop autoboot.\n"
+CONFIG_AUTOBOOT_STOP_STR=" "
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_FPGA is not set
+CONFIG_CMD_GPT=y
+# CONFIG_RANDOM_UUID is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_SNTP=y
+CONFIG_CMD_DNS=y
+CONFIG_CMD_LINK_LOCAL=y
+CONFIG_CMD_BMP=y
+CONFIG_CMD_TIMER=y
+CONFIG_CMD_EXT2=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+# CONFIG_DOS_PARTITION is not set
+CONFIG_OF_CONTROL=y
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_NETCONSOLE=y
+# CONFIG_BLK is not set
+CONFIG_DM_MMC=y
+# CONFIG_SPL_DM_MMC is not set
+CONFIG_ARM_PL180_MMCI=y
+CONFIG_MTD=y
+CONFIG_MTD_NOR_FLASH=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_DM_ETH=y
+CONFIG_ETH_DESIGNWARE=y
+# CONFIG_PINCTRL_FULL is not set
+CONFIG_DM_SPI=y
+CONFIG_STM32_QSPI=y
+CONFIG_DM_VIDEO=y
+CONFIG_BACKLIGHT_GPIO=y
+CONFIG_VIDEO_LCD_ORISETECH_OTM8009A=y
+CONFIG_VIDEO_STM32=y
+CONFIG_VIDEO_STM32_DSI=y
+CONFIG_VIDEO_STM32_MAX_XRES=480
+CONFIG_VIDEO_STM32_MAX_YRES=800
+CONFIG_OF_LIBFDT_OVERLAY=y
+# CONFIG_EFI_LOADER is not set
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 05/10] video: add MIPI DSI host controller bridge
  2018-03-02 15:44 ` [PATCH v2 05/10] video: add MIPI DSI host controller bridge yannick fertre
@ 2018-03-02 18:58   ` Brian Norris
  2018-03-02 19:02     ` Brian Norris
  0 siblings, 1 reply; 20+ messages in thread
From: Brian Norris @ 2018-03-02 18:58 UTC (permalink / raw)
  To: yannick fertre
  Cc: Vikas Manocha, Benjamin Gaignard, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding,
	u-boot, dri-devel, linux-kernel

Hi,

On Fri, Mar 02, 2018 at 04:44:06PM +0100, yannick fertre wrote:
> Add a Synopsys Designware MIPI DSI host bridge driver, based on the
> Rockchip version from rockchip/dw-mipi-dsi.c with phy & bridge APIs.

How many times are we going to allow copy-and-pasting the same driver?
Last time we wanted to modify the Rockchip driver, we were told
"consolidate", because ST had already forked our driver. This nearly
halted all progress. I'm going to be real disappointed if we see another
fork get merged.

(IOW, I would say "over my dead body," but I have no power here.)

And why can't you use DRM?

Brian

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 05/10] video: add MIPI DSI host controller bridge
  2018-03-02 18:58   ` Brian Norris
@ 2018-03-02 19:02     ` Brian Norris
  0 siblings, 0 replies; 20+ messages in thread
From: Brian Norris @ 2018-03-02 19:02 UTC (permalink / raw)
  To: yannick fertre
  Cc: Vikas Manocha, Benjamin Gaignard, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding,
	u-boot, dri-devel, linux-kernel

On Fri, Mar 02, 2018 at 10:57:59AM -0800, Brian Norris wrote:
> Hi,
> 
> On Fri, Mar 02, 2018 at 04:44:06PM +0100, yannick fertre wrote:
> > Add a Synopsys Designware MIPI DSI host bridge driver, based on the
> > Rockchip version from rockchip/dw-mipi-dsi.c with phy & bridge APIs.
> 
> How many times are we going to allow copy-and-pasting the same driver?
> Last time we wanted to modify the Rockchip driver, we were told
> "consolidate", because ST had already forked our driver. This nearly
> halted all progress. I'm going to be real disappointed if we see another
> fork get merged.
> 
> (IOW, I would say "over my dead body," but I have no power here.)
> 
> And why can't you use DRM?

...wait a second...this looks like it's a u-boot driver. There's a
surprising amount of similarity between U-boot and Linux drivers (no
coincidence I'm sure), including <linux/...> headers.

Since when do U-Boot patches go to LKML and dri-devel?

Anyway, I'll try my best to ignore this series.

Brian

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 00/10] splash screen on the stm32f769 disco board
  2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
                   ` (9 preceding siblings ...)
  2018-03-02 15:44 ` [PATCH v2 10/10] board: Add STM32F769 SoC, discovery board support yannick fertre
@ 2018-03-06 17:51 ` Simon Glass
  10 siblings, 0 replies; 20+ messages in thread
From: Simon Glass @ 2018-03-06 17:51 UTC (permalink / raw)
  To: yannick fertre
  Cc: Vikas Manocha, Benjamin Gaignard, Philippe Cornu,
	Patrice Chotard, Patrick DELAUNAY, Christophe KERELLO,
	Archit Taneja, Andrzej Hajda, Laurent Pinchart, David Airlie,
	Brian Norris, Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst,
	Sean Paul, Albert Aribaud, Anatolij Gustschin, Thierry Reding,
	U-Boot Mailing List, dri-devel, lk

On 2 March 2018 at 08:44, yannick fertre <yannick.fertre@st.com> wrote:
>
> Version 2:
> - Replace debug log by pr_error, pr_warn or pr_info.
> - Rework bridge between ltdc & dsi panel
> - Rework backligh management (with or witout gpio)
> - Rework panel otm8009a
> - Add new panel raydium rm68200
>
> Version 1:
> - Initial commit
>
> This serie contains all patchsets needed for displaying a splash screen
> on the stm32f769 disco board.
>
> yannick fertre (10):
>   video: stm32: stm32_ltdc: add bridge to display controller
>   video: stm32: stm32_ltdc: update debug log
>   video: add support of MIPI DSI interface
>   video: add support of panel OTM8009A
>   video: add MIPI DSI host controller bridge
>   video: add support of STM32 MIPI DSI controller driver
>   video: add support of panel rm68200
>   arm: dts: stm32: add dsi for STM32F746
>   arm: dts: stm32: add display for STM32F769 disco board
>   board: Add STM32F769 SoC, discovery board support
>
>  arch/arm/dts/stm32f746.dtsi        |  12 +
>  arch/arm/dts/stm32f769-disco.dts   |  71 ++++
>  configs/stm32f769-disco_defconfig  |  63 +++
>  drivers/video/Kconfig              |  32 ++
>  drivers/video/Makefile             |   4 +
>  drivers/video/dw_mipi_dsi.c        | 822 +++++++++++++++++++++++++++++++++++++
>  drivers/video/mipi_display.c       | 807 ++++++++++++++++++++++++++++++++++++
>  drivers/video/orisetech_otm8009a.c | 329 +++++++++++++++
>  drivers/video/raydium-rm68200.c    | 329 +++++++++++++++
>  drivers/video/stm32/Kconfig        |  10 +
>  drivers/video/stm32/Makefile       |   1 +
>  drivers/video/stm32/stm32_dsi.c    | 427 +++++++++++++++++++
>  drivers/video/stm32/stm32_ltdc.c   | 144 ++++---
>  include/dw_mipi_dsi.h              |  32 ++
>  include/mipi_display.h             | 257 +++++++++++-
>  15 files changed, 3285 insertions(+), 55 deletions(-)
>  create mode 100644 configs/stm32f769-disco_defconfig
>  create mode 100644 drivers/video/dw_mipi_dsi.c
>  create mode 100644 drivers/video/mipi_display.c
>  create mode 100644 drivers/video/orisetech_otm8009a.c
>  create mode 100644 drivers/video/raydium-rm68200.c
>  create mode 100644 drivers/video/stm32/stm32_dsi.c
>  create mode 100644 include/dw_mipi_dsi.h

Does this use driver model? I cannot see it.

Regards,
Simon

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 10/10] board: Add STM32F769 SoC, discovery board support
  2018-03-02 15:44 ` [PATCH v2 10/10] board: Add STM32F769 SoC, discovery board support yannick fertre
@ 2018-03-13  8:41   ` Patrice CHOTARD
  0 siblings, 0 replies; 20+ messages in thread
From: Patrice CHOTARD @ 2018-03-13  8:41 UTC (permalink / raw)
  To: Yannick FERTRE, Vikas MANOCHA, Benjamin Gaignard, Philippe CORNU,
	Patrick DELAUNAY, Christophe KERELLO, Archit Taneja,
	Andrzej Hajda, Laurent Pinchart, David Airlie, Brian Norris,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Hi Yannick

On 03/02/2018 04:44 PM, yannick fertre wrote:
> Signed-off-by: yannick fertre <yannick.fertre@st.com>

Can you add a commit message explaining why you add a specific defconfig 
for this board. FYI, previously, the same defconfig was used for all 
STM32F7 boards (ie /stm32f746-disco_defconfig).

You will also need to resync with the last master branch regarding 
defconfig content.

Thanks

Patrice

> ---
>   configs/stm32f769-disco_defconfig | 63 +++++++++++++++++++++++++++++++++++++++
>   1 file changed, 63 insertions(+)
>   create mode 100644 configs/stm32f769-disco_defconfig
> 
> diff --git a/configs/stm32f769-disco_defconfig b/configs/stm32f769-disco_defconfig
> new file mode 100644
> index 0000000..ac34076
> --- /dev/null
> +++ b/configs/stm32f769-disco_defconfig
> @@ -0,0 +1,63 @@
> +CONFIG_ARM=y
> +CONFIG_STM32=y
> +CONFIG_SYS_MALLOC_F_LEN=0xC00
> +CONFIG_STM32F7=y
> +CONFIG_TARGET_STM32F746_DISCO=y
> +CONFIG_DEFAULT_DEVICE_TREE="stm32f769-disco"
> +CONFIG_BOOTDELAY=3
> +CONFIG_USE_BOOTARGS=y
> +CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
> +# CONFIG_DISPLAY_CPUINFO is not set
> +# CONFIG_DISPLAY_BOARDINFO is not set
> +CONFIG_BOARD_EARLY_INIT_F=y
> +CONFIG_HUSH_PARSER=y
> +CONFIG_SYS_PROMPT="U-Boot > "
> +CONFIG_AUTOBOOT_KEYED=y
> +CONFIG_AUTOBOOT_PROMPT="Hit SPACE in %d seconds to stop autoboot.\n"
> +CONFIG_AUTOBOOT_STOP_STR=" "
> +CONFIG_CMD_BOOTZ=y
> +# CONFIG_CMD_FPGA is not set
> +CONFIG_CMD_GPT=y
> +# CONFIG_RANDOM_UUID is not set
> +CONFIG_CMD_MMC=y
> +CONFIG_CMD_SF=y
> +# CONFIG_CMD_SETEXPR is not set
> +CONFIG_CMD_DHCP=y
> +CONFIG_CMD_MII=y
> +CONFIG_CMD_PING=y
> +CONFIG_CMD_SNTP=y
> +CONFIG_CMD_DNS=y
> +CONFIG_CMD_LINK_LOCAL=y
> +CONFIG_CMD_BMP=y
> +CONFIG_CMD_TIMER=y
> +CONFIG_CMD_EXT2=y
> +CONFIG_CMD_EXT4=y
> +CONFIG_CMD_FAT=y
> +CONFIG_CMD_FS_GENERIC=y
> +# CONFIG_DOS_PARTITION is not set
> +CONFIG_OF_CONTROL=y
> +CONFIG_NET_RANDOM_ETHADDR=y
> +CONFIG_NETCONSOLE=y
> +# CONFIG_BLK is not set
> +CONFIG_DM_MMC=y
> +# CONFIG_SPL_DM_MMC is not set
> +CONFIG_ARM_PL180_MMCI=y
> +CONFIG_MTD=y
> +CONFIG_MTD_NOR_FLASH=y
> +CONFIG_DM_SPI_FLASH=y
> +CONFIG_SPI_FLASH=y
> +CONFIG_SPI_FLASH_STMICRO=y
> +CONFIG_DM_ETH=y
> +CONFIG_ETH_DESIGNWARE=y
> +# CONFIG_PINCTRL_FULL is not set
> +CONFIG_DM_SPI=y
> +CONFIG_STM32_QSPI=y
> +CONFIG_DM_VIDEO=y
> +CONFIG_BACKLIGHT_GPIO=y
> +CONFIG_VIDEO_LCD_ORISETECH_OTM8009A=y
> +CONFIG_VIDEO_STM32=y
> +CONFIG_VIDEO_STM32_DSI=y
> +CONFIG_VIDEO_STM32_MAX_XRES=480
> +CONFIG_VIDEO_STM32_MAX_YRES=800
> +CONFIG_OF_LIBFDT_OVERLAY=y
> +# CONFIG_EFI_LOADER is not set
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller
  2018-03-02 15:44 ` [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller yannick fertre
@ 2018-03-13  8:44   ` Patrice CHOTARD
  0 siblings, 0 replies; 20+ messages in thread
From: Patrice CHOTARD @ 2018-03-13  8:44 UTC (permalink / raw)
  To: Yannick FERTRE, Vikas MANOCHA, Benjamin Gaignard, Philippe CORNU,
	Patrick DELAUNAY, Christophe KERELLO, Archit Taneja,
	Andrzej Hajda, Laurent Pinchart, David Airlie, Brian Norris,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Hi Yannick

On 03/02/2018 04:44 PM, yannick fertre wrote:
> Manage a bridge insert between the display controller & a panel.
> 
> Signed-off-by: yannick fertre <yannick.fertre@st.com>
> ---
>   drivers/video/stm32/stm32_ltdc.c | 107 ++++++++++++++++++++++++++-------------
>   1 file changed, 71 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
> index e160c77..bd9c0de 100644
> --- a/drivers/video/stm32/stm32_ltdc.c
> +++ b/drivers/video/stm32/stm32_ltdc.c
> @@ -8,6 +8,7 @@
>   
>   #include <common.h>
>   #include <clk.h>
> +#include <display.h>
>   #include <dm.h>
>   #include <panel.h>
>   #include <reset.h>
> @@ -15,12 +16,12 @@
>   #include <asm/io.h>
>   #include <asm/arch/gpio.h>
>   #include <dm/device-internal.h>
> +#include <video_bridge.h>
>   
>   DECLARE_GLOBAL_DATA_PTR;
>   
>   struct stm32_ltdc_priv {
>   	void __iomem *regs;
> -	struct display_timing timing;
>   	enum video_log2_bpp l2bpp;
>   	u32 bg_col_argb;
>   	u32 crop_x, crop_y, crop_w, crop_h;
> @@ -210,23 +211,23 @@ static void stm32_ltdc_enable(struct stm32_ltdc_priv *priv)
>   	setbits_le32(priv->regs + LTDC_GCR, GCR_LTDCEN);
>   }
>   
> -static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv)
> +static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv,
> +				struct display_timing *timings)
>   {
>   	void __iomem *regs = priv->regs;
> -	struct display_timing *timing = &priv->timing;
>   	u32 hsync, vsync, acc_hbp, acc_vbp, acc_act_w, acc_act_h;
>   	u32 total_w, total_h;
>   	u32 val;
>   
>   	/* Convert video timings to ltdc timings */
> -	hsync = timing->hsync_len.typ - 1;
> -	vsync = timing->vsync_len.typ - 1;
> -	acc_hbp = hsync + timing->hback_porch.typ;
> -	acc_vbp = vsync + timing->vback_porch.typ;
> -	acc_act_w = acc_hbp + timing->hactive.typ;
> -	acc_act_h = acc_vbp + timing->vactive.typ;
> -	total_w = acc_act_w + timing->hfront_porch.typ;
> -	total_h = acc_act_h + timing->vfront_porch.typ;
> +	hsync = timings->hsync_len.typ - 1;
> +	vsync = timings->vsync_len.typ - 1;
> +	acc_hbp = hsync + timings->hback_porch.typ;
> +	acc_vbp = vsync + timings->vback_porch.typ;
> +	acc_act_w = acc_hbp + timings->hactive.typ;
> +	acc_act_h = acc_vbp + timings->vactive.typ;
> +	total_w = acc_act_w + timings->hfront_porch.typ;
> +	total_h = acc_act_h + timings->vfront_porch.typ;
>   
>   	/* Synchronization sizes */
>   	val = (hsync << 16) | vsync;
> @@ -248,14 +249,14 @@ static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv)
>   
>   	/* Signal polarities */
>   	val = 0;
> -	debug("%s: timing->flags 0x%08x\n", __func__, timing->flags);
> -	if (timing->flags & DISPLAY_FLAGS_HSYNC_HIGH)
> +	debug("%s: timing->flags 0x%08x\n", __func__, timings->flags);
> +	if (timings->flags & DISPLAY_FLAGS_HSYNC_HIGH)
>   		val |= GCR_HSPOL;
> -	if (timing->flags & DISPLAY_FLAGS_VSYNC_HIGH)
> +	if (timings->flags & DISPLAY_FLAGS_VSYNC_HIGH)
>   		val |= GCR_VSPOL;
> -	if (timing->flags & DISPLAY_FLAGS_DE_HIGH)
> +	if (timings->flags & DISPLAY_FLAGS_DE_HIGH)
>   		val |= GCR_DEPOL;
> -	if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
> +	if (timings->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
>   		val |= GCR_PCPOL;
>   	clrsetbits_le32(regs + LTDC_GCR,
>   			GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
> @@ -331,7 +332,11 @@ static int stm32_ltdc_probe(struct udevice *dev)
>   	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
>   	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>   	struct stm32_ltdc_priv *priv = dev_get_priv(dev);
> -	struct udevice *panel;
> +#ifdef CONFIG_VIDEO_BRIDGE
> +	struct udevice *bridge = NULL;
> +#endif
> +	struct udevice *panel = NULL;
> +	struct display_timing timings;
>   	struct clk pclk;
>   	struct reset_ctl rst;
>   	int rate, ret;
> @@ -364,63 +369,93 @@ static int stm32_ltdc_probe(struct udevice *dev)
>   	/* Reset */
>   	reset_deassert(&rst);
>   
> -	ret = uclass_first_device(UCLASS_PANEL, &panel);
> +#ifdef CONFIG_VIDEO_BRIDGE
> +	ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
>   	if (ret) {
> -		debug("%s: panel device error %d\n", __func__, ret);
> -		return ret;
> +		debug("%s: No video bridge, or no backlight on bridge\n",
> +		      __func__);
>   	}
>   
> -	ret = panel_enable_backlight(panel);
> +	if (bridge) {
> +		ret = video_bridge_attach(bridge);
> +		if (ret) {
> +			debug("%s: fail to attach bridge\n", __func__);

I would replace debug by dev_err() here.

> +			return ret;
> +		}
> +	}
> +#endif
> +	ret = uclass_first_device(UCLASS_PANEL, &panel);
>   	if (ret) {
> -		debug("%s: panel %s enable backlight error %d\n",
> -		      __func__, panel->name, ret);
> +		debug("%s: panel device error %d\n", __func__, ret);

Ditto

>   		return ret;
>   	}
>   
> -	ret = fdtdec_decode_display_timing(gd->fdt_blob,
> -					   dev_of_offset(dev), 0,
> -					   &priv->timing);
> +	ret = fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(panel),
> +					   0, &timings);
>   	if (ret) {
>   		debug("%s: decode display timing error %d\n",
>   		      __func__, ret);
> -		return -EINVAL;

Ditto

> +		return ret;
>   	}
>   
> -	rate = clk_set_rate(&pclk, priv->timing.pixelclock.typ);
> +	rate = clk_set_rate(&pclk, timings.pixelclock.typ);
>   	if (rate < 0) {
>   		debug("%s: fail to set pixel clock %d hz %d hz\n",
> -		      __func__, priv->timing.pixelclock.typ, rate);
> +		      __func__, timings.pixelclock.typ, rate);

Ditto

>   		return rate;
>   	}
>   
>   	debug("%s: Set pixel clock req %d hz get %d hz\n", __func__,
> -	      priv->timing.pixelclock.typ, rate);
> +	      timings.pixelclock.typ, rate);
>   
>   	/* TODO Below parameters are hard-coded for the moment... */
>   	priv->l2bpp = VIDEO_BPP16;
>   	priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */
>   	priv->crop_x = 0;
>   	priv->crop_y = 0;
> -	priv->crop_w = priv->timing.hactive.typ;
> -	priv->crop_h = priv->timing.vactive.typ;
> +	priv->crop_w = timings.hactive.typ;
> +	priv->crop_h = timings.vactive.typ;
>   	priv->alpha = 0xFF;
>   
>   	debug("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
> -	      priv->timing.hactive.typ, priv->timing.vactive.typ,
> +	      timings.hactive.typ, timings.vactive.typ,
>   	      VNBITS(priv->l2bpp), uc_plat->base);
>   	debug("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
>   	      priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
>   	      priv->bg_col_argb, priv->alpha);
>   
>   	/* Configure & start LTDC */
> -	stm32_ltdc_set_mode(priv);
> +	stm32_ltdc_set_mode(priv, &timings);
>   	stm32_ltdc_set_layer1(priv, uc_plat->base);
>   	stm32_ltdc_enable(priv);
>   
> -	uc_priv->xsize = priv->timing.hactive.typ;
> -	uc_priv->ysize = priv->timing.vactive.typ;
> +	uc_priv->xsize = timings.hactive.typ;
> +	uc_priv->ysize = timings.vactive.typ;
>   	uc_priv->bpix = priv->l2bpp;
>   
> +#ifdef CONFIG_VIDEO_BRIDGE
> +	if (bridge) {
> +		ret = video_bridge_set_backlight(bridge, 80);
> +		if (ret) {
> +			debug("%s: fail to set backlight\n", __func__);

Ditto

> +			return ret;
> +		}
> +	} else {
> +		ret = panel_enable_backlight(panel);
> +		if (ret) {
> +			debug("%s: panel %s enable backlight error %d\n",
> +			      __func__, panel->name, ret);

Ditto

> +			return ret;
> +		}
> +	}
> +#else
> +	ret = panel_enable_backlight(panel);
> +	if (ret) {
> +		debug("%s: panel %s enable backlight error %d\n",
> +		      __func__, panel->name, ret);

Ditto

> +		return ret;
> +	}
> +#endif
>   	video_set_flush_dcache(dev, true);
>   
>   	return 0;
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 02/10] video: stm32: stm32_ltdc: update debug log
  2018-03-02 15:44 ` [PATCH v2 02/10] video: stm32: stm32_ltdc: update debug log yannick fertre
@ 2018-03-13  8:48   ` Patrice CHOTARD
  0 siblings, 0 replies; 20+ messages in thread
From: Patrice CHOTARD @ 2018-03-13  8:48 UTC (permalink / raw)
  To: Yannick FERTRE, Vikas MANOCHA, Benjamin Gaignard, Philippe CORNU,
	Patrick DELAUNAY, Christophe KERELLO, Archit Taneja,
	Andrzej Hajda, Laurent Pinchart, David Airlie, Brian Norris,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Hi Yannick

On 03/02/2018 04:44 PM, yannick fertre wrote:
> Replace  macro debug by pr_error, pr_warn or pr_info.
> 
> Signed-off-by: yannick fertre <yannick.fertre@st.com>
> ---
>   drivers/video/stm32/stm32_ltdc.c | 62 ++++++++++++++++++++--------------------
>   1 file changed, 31 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
> index bd9c0de..e95f35c 100644
> --- a/drivers/video/stm32/stm32_ltdc.c
> +++ b/drivers/video/stm32/stm32_ltdc.c
> @@ -176,13 +176,13 @@ static u32 stm32_ltdc_get_pixel_format(enum video_log2_bpp l2bpp)
>   	case VIDEO_BPP2:
>   	case VIDEO_BPP4:
>   	default:
> -		debug("%s: warning %dbpp not supported yet, %dbpp instead\n",
> -		      __func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
> +		pr_warn("%s: warning %dbpp not supported yet, %dbpp instead\n",
> +			__func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
>   		pf = PF_RGB565;
>   		break;
>   	}
>   
> -	debug("%s: %d bpp -> ltdc pf %d\n", __func__, VNBITS(l2bpp), pf);
> +	pr_info("%s: %d bpp -> ltdc pf %d\n", __func__, VNBITS(l2bpp), pf);
>   
>   	return (u32)pf;
>   }
> @@ -249,7 +249,7 @@ static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv,
>   
>   	/* Signal polarities */
>   	val = 0;
> -	debug("%s: timing->flags 0x%08x\n", __func__, timings->flags);
> +	pr_info("%s: timing->flags 0x%08x\n", __func__, timings->flags);
>   	if (timings->flags & DISPLAY_FLAGS_HSYNC_HIGH)
>   		val |= GCR_HSPOL;
>   	if (timings->flags & DISPLAY_FLAGS_VSYNC_HIGH)
> @@ -343,26 +343,26 @@ static int stm32_ltdc_probe(struct udevice *dev)
>   
>   	priv->regs = (void *)dev_read_addr(dev);
>   	if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) {
> -		debug("%s: ltdc dt register address error\n", __func__);
> +		pr_err("%s: ltdc dt register address error\n", __func__);

As you get access to the struct udevice, prefer dev_err() here.

>   		return -EINVAL;
>   	}
>   
>   	ret = clk_get_by_index(dev, 0, &pclk);
>   	if (ret) {
> -		debug("%s: peripheral clock get error %d\n", __func__, ret);
> +		pr_err("%s: peripheral clock get error %d\n", __func__, ret);

ditto

>   		return ret;
>   	}
>   
>   	ret = clk_enable(&pclk);
>   	if (ret) {
> -		debug("%s: peripheral clock enable error %d\n",
> -		      __func__, ret);
> +		pr_err("%s: peripheral clock enable error %d\n",
> +		       __func__, ret);
ditto
>   		return ret;
>   	}
>   
>   	ret = reset_get_by_index(dev, 0, &rst);
>   	if (ret) {
> -		debug("%s: missing ltdc hardware reset\n", __func__);
> +		pr_err("%s: missing ltdc hardware reset\n", __func__);
ditto
>   		return -ENODEV;
>   	}
>   
> @@ -372,41 +372,40 @@ static int stm32_ltdc_probe(struct udevice *dev)
>   #ifdef CONFIG_VIDEO_BRIDGE
>   	ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
>   	if (ret) {
> -		debug("%s: No video bridge, or no backlight on bridge\n",
> -		      __func__);
> +		pr_info("%s: No video bridge, or no backlight on bridge\n",
> +			__func__);

dev_info()

>   	}
>   
>   	if (bridge) {
>   		ret = video_bridge_attach(bridge);
>   		if (ret) {
> -			debug("%s: fail to attach bridge\n", __func__);
> +			pr_err("%s: fail to attach bridge\n", __func__);

dev_err()

>   			return ret;
>   		}
>   	}
>   #endif
>   	ret = uclass_first_device(UCLASS_PANEL, &panel);
>   	if (ret) {
> -		debug("%s: panel device error %d\n", __func__, ret);
> +		pr_err("%s: panel device error %d\n", __func__, ret);

ditto

>   		return ret;
>   	}
>   
>   	ret = fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(panel),
>   					   0, &timings);
>   	if (ret) {
> -		debug("%s: decode display timing error %d\n",
> -		      __func__, ret);
> +		pr_err("%s: decode display timing error %d\n", __func__, ret);

ditto

>   		return ret;
>   	}
>   
>   	rate = clk_set_rate(&pclk, timings.pixelclock.typ);
>   	if (rate < 0) {
> -		debug("%s: fail to set pixel clock %d hz %d hz\n",
> -		      __func__, timings.pixelclock.typ, rate);
> +		pr_err("%s: fail to set pixel clock %d hz %d hz\n",
> +		       __func__, timings.pixelclock.typ, rate);

ditto

>   		return rate;
>   	}
>   
> -	debug("%s: Set pixel clock req %d hz get %d hz\n", __func__,
> -	      timings.pixelclock.typ, rate);
> +	pr_info("%s: Set pixel clock req %d hz get %d hz\n", __func__,
> +		timings.pixelclock.typ, rate);
>   
>   	/* TODO Below parameters are hard-coded for the moment... */
>   	priv->l2bpp = VIDEO_BPP16;
> @@ -417,12 +416,12 @@ static int stm32_ltdc_probe(struct udevice *dev)
>   	priv->crop_h = timings.vactive.typ;
>   	priv->alpha = 0xFF;
>   
> -	debug("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
> -	      timings.hactive.typ, timings.vactive.typ,
> -	      VNBITS(priv->l2bpp), uc_plat->base);
> -	debug("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
> -	      priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
> -	      priv->bg_col_argb, priv->alpha);
> +	pr_info("%s: %dx%d %dbpp frame buffer at 0x%lx\n", __func__,
> +		timings.hactive.typ, timings.vactive.typ,
> +		VNBITS(priv->l2bpp), uc_plat->base);
> +	pr_info("%s: crop %d,%d %dx%d bg 0x%08x alpha %d\n", __func__,
> +		priv->crop_x, priv->crop_y, priv->crop_w, priv->crop_h,
> +		priv->bg_col_argb, priv->alpha);


dev_info()

>   
>   	/* Configure & start LTDC */
>   	stm32_ltdc_set_mode(priv, &timings);
> @@ -437,22 +436,22 @@ static int stm32_ltdc_probe(struct udevice *dev)
>   	if (bridge) {
>   		ret = video_bridge_set_backlight(bridge, 80);
>   		if (ret) {
> -			debug("%s: fail to set backlight\n", __func__);
> +			pr_err("%s: fail to set backlight\n", __func__);

dev_err()

>   			return ret;
>   		}
>   	} else {
>   		ret = panel_enable_backlight(panel);
>   		if (ret) {
> -			debug("%s: panel %s enable backlight error %d\n",
> -			      __func__, panel->name, ret);
> +			pr_err("%s: panel %s enable backlight error %d\n",
> +			       __func__, panel->name, ret);

ditto

>   			return ret;
>   		}
>   	}
>   #else
>   	ret = panel_enable_backlight(panel);
>   	if (ret) {
> -		debug("%s: panel %s enable backlight error %d\n",
> -		      __func__, panel->name, ret);
> +		pr_err("%s: panel %s enable backlight error %d\n",
> +		       __func__, panel->name, ret);

ditto

>   		return ret;
>   	}
>   #endif
> @@ -468,7 +467,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
>   	uc_plat->size = CONFIG_VIDEO_STM32_MAX_XRES *
>   			CONFIG_VIDEO_STM32_MAX_YRES *
>   			(CONFIG_VIDEO_STM32_MAX_BPP >> 3);
> -	debug("%s: frame buffer max size %d bytes\n", __func__, uc_plat->size);
> +	pr_info("%s: frame buffer max size %d bytes\n", __func__,

dev_info()

> +		uc_plat->size);
>   
>   	return 0;
>   }
> 

Thanks

Patrice

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 04/10] video: add support of panel OTM8009A
  2018-03-02 15:44 ` [PATCH v2 04/10] video: add support of panel OTM8009A yannick fertre
@ 2018-03-13  8:52   ` Patrice CHOTARD
  0 siblings, 0 replies; 20+ messages in thread
From: Patrice CHOTARD @ 2018-03-13  8:52 UTC (permalink / raw)
  To: Yannick FERTRE, Vikas MANOCHA, Benjamin Gaignard, Philippe CORNU,
	Patrick DELAUNAY, Christophe KERELLO, Archit Taneja,
	Andrzej Hajda, Laurent Pinchart, David Airlie, Brian Norris,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Hi Yannick

On 03/02/2018 04:44 PM, yannick fertre wrote:
> Support for Orise Tech otm8009a 480p dsi 2dl video mode panel.
> 
> Signed-off-by: yannick fertre <yannick.fertre@st.com>
> ---
>   drivers/video/Kconfig              |   8 +
>   drivers/video/Makefile             |   1 +
>   drivers/video/orisetech_otm8009a.c | 329 +++++++++++++++++++++++++++++++++++++
>   3 files changed, 338 insertions(+)
>   create mode 100644 drivers/video/orisetech_otm8009a.c
> 
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 1981298..b5fc535 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -320,6 +320,14 @@ config VIDEO_LCD_ANX9804
>   	from a parallel LCD interface and translate it on the fy into a DP
>   	interface for driving eDP TFT displays. It uses I2C for configuration.
>   
> +config VIDEO_LCD_ORISETECH_OTM8009A
> +	bool "OTM8009A DSI LCD panel support"
> +	depends on DM_VIDEO
> +	select VIDEO_MIPI_DSI
> +	default n
> +	---help---
> +	Support for Orise Tech otm8009a 480p dsi 2dl video mode panel.
> +
>   config VIDEO_LCD_SSD2828
>   	bool "SSD2828 bridge chip"
>   	default n
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 6f42cca..65002af 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o
>   obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o
>   obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
>   obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
> +obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
>   obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
>   obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o
>   obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
> diff --git a/drivers/video/orisetech_otm8009a.c b/drivers/video/orisetech_otm8009a.c
> new file mode 100644
> index 0000000..79f2da8
> --- /dev/null
> +++ b/drivers/video/orisetech_otm8009a.c
> @@ -0,0 +1,329 @@
> +/*
> + * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
> + * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
> + *	      Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
> + *
> + * This otm8009a panel driver is based on the panel driver from
> + * drivers/gpu/drm/panel/panel-orisetech-otm8009a.c (kernel linux)
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +#include <asm/gpio.h>
> +#include <backlight.h>
> +#include <common.h>
> +#include <dm.h>
> +#include <panel.h>
> +#include <power/regulator.h>
> +#include <mipi_display.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define DRV_NAME "orisetech_otm8009a"
> +
> +#define OTM8009A_BACKLIGHT_DEFAULT	240
> +#define OTM8009A_BACKLIGHT_MAX		255
> +
> +/* Manufacturer Command Set */
> +#define MCS_ADRSFT	0x0000	/* Address Shift Function */
> +#define MCS_PANSET	0xB3A6	/* Panel Type Setting */
> +#define MCS_SD_CTRL	0xC0A2	/* Source Driver Timing Setting */
> +#define MCS_P_DRV_M	0xC0B4	/* Panel Driving Mode */
> +#define MCS_OSC_ADJ	0xC181	/* Oscillator Adjustment for Idle/Normal mode */
> +#define MCS_RGB_VID_SET	0xC1A1	/* RGB Video Mode Setting */
> +#define MCS_SD_PCH_CTRL	0xC480	/* Source Driver Precharge Control */
> +#define MCS_NO_DOC1	0xC48A	/* Command not documented */
> +#define MCS_PWR_CTRL1	0xC580	/* Power Control Setting 1 */
> +#define MCS_PWR_CTRL2	0xC590	/* Power Control Setting 2 for Normal Mode */
> +#define MCS_PWR_CTRL4	0xC5B0	/* Power Control Setting 4 for DC Voltage */
> +#define MCS_PANCTRLSET1	0xCB80	/* Panel Control Setting 1 */
> +#define MCS_PANCTRLSET2	0xCB90	/* Panel Control Setting 2 */
> +#define MCS_PANCTRLSET3	0xCBA0	/* Panel Control Setting 3 */
> +#define MCS_PANCTRLSET4	0xCBB0	/* Panel Control Setting 4 */
> +#define MCS_PANCTRLSET5	0xCBC0	/* Panel Control Setting 5 */
> +#define MCS_PANCTRLSET6	0xCBD0	/* Panel Control Setting 6 */
> +#define MCS_PANCTRLSET7	0xCBE0	/* Panel Control Setting 7 */
> +#define MCS_PANCTRLSET8	0xCBF0	/* Panel Control Setting 8 */
> +#define MCS_PANU2D1	0xCC80	/* Panel U2D Setting 1 */
> +#define MCS_PANU2D2	0xCC90	/* Panel U2D Setting 2 */
> +#define MCS_PANU2D3	0xCCA0	/* Panel U2D Setting 3 */
> +#define MCS_PAND2U1	0xCCB0	/* Panel D2U Setting 1 */
> +#define MCS_PAND2U2	0xCCC0	/* Panel D2U Setting 2 */
> +#define MCS_PAND2U3	0xCCD0	/* Panel D2U Setting 3 */
> +#define MCS_GOAVST	0xCE80	/* GOA VST Setting */
> +#define MCS_GOACLKA1	0xCEA0	/* GOA CLKA1 Setting */
> +#define MCS_GOACLKA3	0xCEB0	/* GOA CLKA3 Setting */
> +#define MCS_GOAECLK	0xCFC0	/* GOA ECLK Setting */
> +#define MCS_NO_DOC2	0xCFD0	/* Command not documented */
> +#define MCS_GVDDSET	0xD800	/* GVDD/NGVDD */
> +#define MCS_VCOMDC	0xD900	/* VCOM Voltage Setting */
> +#define MCS_GMCT2_2P	0xE100	/* Gamma Correction 2.2+ Setting */
> +#define MCS_GMCT2_2N	0xE200	/* Gamma Correction 2.2- Setting */
> +#define MCS_NO_DOC3	0xF5B6	/* Command not documented */
> +#define MCS_CMD2_ENA1	0xFF00	/* Enable Access Command2 "CMD2" */
> +#define MCS_CMD2_ENA2	0xFF80	/* Enable Access Orise Command2 */
> +
> +struct otm8009a_panel_priv {
> +	struct udevice *reg;
> +	struct gpio_desc reset;
> +};
> +
> +static void otm8009a_dcs_write_buf(struct udevice *dev, const void *data,
> +				   size_t len)
> +{
> +	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = plat->device;
> +
> +	if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
> +		pr_err("mipi dsi dcs write buffer failed\n");


dev_err()

> +}
> +
> +#define dcs_write_seq(dev, seq...)				\
> +({								\
> +	static const u8 d[] = { seq };				\
> +	otm8009a_dcs_write_buf(dev, d, ARRAY_SIZE(d));		\
> +})
> +
> +#define dcs_write_cmd_at(dev, cmd, seq...)		\
> +({							\
> +	static const u16 c = cmd;			\
> +	struct udevice *device = dev;			\
> +	dcs_write_seq(device, MCS_ADRSFT, (c) & 0xFF);	\
> +	dcs_write_seq(device, (c) >> 8, seq);		\
> +})
> +
> +static int otm8009a_init_sequence(struct udevice *dev)
> +{
> +	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = plat->device;
> +	int ret;
> +
> +	/* Enter CMD2 */
> +	dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
> +
> +	/* Enter Orise Command2 */
> +	dcs_write_cmd_at(dev, MCS_CMD2_ENA2, 0x80, 0x09);
> +
> +	dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL, 0x30);
> +	mdelay(10);
> +
> +	dcs_write_cmd_at(dev, MCS_NO_DOC1, 0x40);
> +	mdelay(10);
> +
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL4 + 1, 0xA9);
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 1, 0x34);
> +	dcs_write_cmd_at(dev, MCS_P_DRV_M, 0x50);
> +	dcs_write_cmd_at(dev, MCS_VCOMDC, 0x4E);
> +	dcs_write_cmd_at(dev, MCS_OSC_ADJ, 0x66); /* 65Hz */
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 2, 0x01);
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 5, 0x34);
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL2 + 4, 0x33);
> +	dcs_write_cmd_at(dev, MCS_GVDDSET, 0x79, 0x79);
> +	dcs_write_cmd_at(dev, MCS_SD_CTRL + 1, 0x1B);
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 2, 0x83);
> +	dcs_write_cmd_at(dev, MCS_SD_PCH_CTRL + 1, 0x83);
> +	dcs_write_cmd_at(dev, MCS_RGB_VID_SET, 0x0E);
> +	dcs_write_cmd_at(dev, MCS_PANSET, 0x00, 0x01);
> +
> +	dcs_write_cmd_at(dev, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
> +	dcs_write_cmd_at(dev, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
> +			 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
> +	dcs_write_cmd_at(dev, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
> +			 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
> +	dcs_write_cmd_at(dev, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
> +			 0x01, 0x02, 0x00, 0x00);
> +
> +	dcs_write_cmd_at(dev, MCS_NO_DOC2, 0x00);
> +
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +			 0, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +			 0, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
> +			 0, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
> +			 4, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
> +	dcs_write_cmd_at(dev, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> +			 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
> +
> +	dcs_write_cmd_at(dev, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
> +			 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_cmd_at(dev, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +			 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
> +	dcs_write_cmd_at(dev, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
> +			 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_cmd_at(dev, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
> +			 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_cmd_at(dev, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +			 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
> +	dcs_write_cmd_at(dev, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
> +			 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +
> +	dcs_write_cmd_at(dev, MCS_PWR_CTRL1 + 1, 0x66);
> +
> +	dcs_write_cmd_at(dev, MCS_NO_DOC3, 0x06);
> +
> +	dcs_write_cmd_at(dev, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
> +			 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
> +			 0x01);
> +	dcs_write_cmd_at(dev, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
> +			 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
> +			 0x01);
> +
> +	/* Exit CMD2 */
> +	dcs_write_cmd_at(dev, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
> +
> +	ret =  mipi_dsi_dcs_nop(device);
> +	if (ret)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_exit_sleep_mode(device);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for sleep out exit */
> +	mdelay(120);
> +
> +	/* Default portrait 480x800 rgb24 */
> +	dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
> +
> +	ret =  mipi_dsi_dcs_set_column_address(device, 0, 479);
> +	if (ret)
> +		return ret;
> +
> +	ret =  mipi_dsi_dcs_set_page_address(device, 0, 799);
> +	if (ret)
> +		return ret;
> +
> +	/* See otm8009a driver documentation for pixel format descriptions */
> +	ret =  mipi_dsi_dcs_set_pixel_format(device, MIPI_DCS_PIXEL_FMT_24BIT |
> +					     MIPI_DCS_PIXEL_FMT_24BIT << 4);
> +	if (ret)
> +		return ret;
> +
> +	/* Disable CABC feature */
> +	dcs_write_seq(dev, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
> +
> +	ret = mipi_dsi_dcs_set_display_on(device);
> +	if (ret)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_nop(device);
> +	if (ret)
> +		return ret;
> +
> +	/* Send Command GRAM memory write (no parameters) */
> +	dcs_write_seq(dev, MIPI_DCS_WRITE_MEMORY_START);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_panel_enable_backlight(struct udevice *dev)
> +{
> +	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = plat->device;
> +	int ret;
> +
> +	device->lanes = 2;
> +	device->format = MIPI_DSI_FMT_RGB888;
> +	device->mode_flags = MIPI_DSI_MODE_VIDEO |
> +			     MIPI_DSI_MODE_VIDEO_BURST |
> +			     MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_attach(device);
> +	if (ret < 0)
> +		return ret;
> +
> +	pr_info("%s: initialize panel\n", __func__);

Is it useful to print this each time the backligth is enabled ?

> +	ret = otm8009a_init_sequence(dev);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Power on the backlight with the requested brightness
> +	 * Note We can not use mipi_dsi_dcs_set_display_brightness()
> +	 * as otm8009a driver support only 8-bit brightness (1 param).
> +	 */
> +	pr_info("%s: start, backlight\n", __func__);

ditto

> +
> +	dcs_write_seq(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
> +		      OTM8009A_BACKLIGHT_DEFAULT);
> +
> +	/* Update Brightness Control & Backlight */
> +	dcs_write_seq(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
> +
> +	/*  need to wait a few time before set the DSI bridge in video mode */
> +	mdelay(10);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_panel_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct otm8009a_panel_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
> +		ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
> +						   "power-supply", &priv->reg);
> +		if (ret) {
> +			pr_err("%s: Warning: cannot get power supply\n",
> +			       __func__);

dev_err()

> +			if (ret != -ENOENT)
> +				return ret;
> +		}
> +	}
> +
> +	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
> +				   GPIOD_IS_OUT);
> +	if (ret) {
> +		pr_err("%s: Warning: cannot get reset GPIO\n", __func__);

ditto

> +		if (ret != -ENOENT)
> +			return ret;
> +	}
> +
> +	/* reset panel must be done before probe */
> +	dm_gpio_set_value(&priv->reset, true);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_panel_probe(struct udevice *dev)
> +{
> +	struct otm8009a_panel_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
> +		pr_err("%s: Enable regulator '%s'\n", __func__,
> +		       priv->reg->name);

debug() ?

> +		ret = regulator_set_enable(priv->reg, true);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	dm_gpio_set_value(&priv->reset, false);
> +
> +	return 0;
> +}
> +
> +static const struct panel_ops otm8009a_panel_ops = {
> +	.enable_backlight = otm8009a_panel_enable_backlight,
> +};
> +
> +static const struct udevice_id otm8009a_panel_ids[] = {
> +	{ .compatible = "orisetech,otm8009a" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(otm8009a_panel) = {
> +	.name			  = "otm8009a_panel",
> +	.id			  = UCLASS_PANEL,
> +	.of_match		  = otm8009a_panel_ids,
> +	.ops			  = &otm8009a_panel_ops,
> +	.ofdata_to_platdata	  = otm8009a_panel_ofdata_to_platdata,
> +	.probe			  = otm8009a_panel_probe,
> +	.platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat),
> +	.priv_auto_alloc_size	= sizeof(struct otm8009a_panel_priv),
> +};
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 06/10] video: add support of STM32 MIPI DSI controller driver
  2018-03-02 15:44 ` [PATCH v2 06/10] video: add support of STM32 MIPI DSI controller driver yannick fertre
@ 2018-03-13  8:56   ` Patrice CHOTARD
  0 siblings, 0 replies; 20+ messages in thread
From: Patrice CHOTARD @ 2018-03-13  8:56 UTC (permalink / raw)
  To: Yannick FERTRE, Vikas MANOCHA, Benjamin Gaignard, Philippe CORNU,
	Patrick DELAUNAY, Christophe KERELLO, Archit Taneja,
	Andrzej Hajda, Laurent Pinchart, David Airlie, Brian Norris,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Hi yannick

On 03/02/2018 04:44 PM, yannick fertre wrote:
> Add the STM32 DSI controller driver that uses the Synopsys DesignWare
> MIPI DSI host controller bridge.
> 
> Signed-off-by: yannick fertre <yannick.fertre@st.com>
> ---
>   drivers/video/stm32/Kconfig     |  10 +
>   drivers/video/stm32/Makefile    |   1 +
>   drivers/video/stm32/stm32_dsi.c | 427 ++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 438 insertions(+)
>   create mode 100644 drivers/video/stm32/stm32_dsi.c
> 
> diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
> index 113a2bb..2ea6f18 100644
> --- a/drivers/video/stm32/Kconfig
> +++ b/drivers/video/stm32/Kconfig
> @@ -15,6 +15,16 @@ menuconfig VIDEO_STM32
>   	  DSI. This option enables these supports which can be used on
>   	  devices which have RGB TFT or DSI display connected.
>   
> +config VIDEO_STM32_DSI
> +	bool "Enable STM32 DSI video support"
> +	depends on VIDEO_STM32
> +	select VIDEO_MIPI_DSI
> +	select VIDEO_BRIDGE
> +	select VIDEO_DW_MIPI_DSI
> +	help
> +	  This option enables support DSI internal bridge which can be used on
> +	  devices which have DSI display connected.
> +
>   config VIDEO_STM32_MAX_XRES
>   	int "Maximum horizontal resolution (for memory allocation purposes)"
>   	depends on VIDEO_STM32
> diff --git a/drivers/video/stm32/Makefile b/drivers/video/stm32/Makefile
> index 372a2e1..f8c3ff7 100644
> --- a/drivers/video/stm32/Makefile
> +++ b/drivers/video/stm32/Makefile
> @@ -8,3 +8,4 @@
>   #
>   
>   obj-${CONFIG_VIDEO_STM32} = stm32_ltdc.o
> +obj-${CONFIG_VIDEO_STM32_DSI} += stm32_dsi.o
> diff --git a/drivers/video/stm32/stm32_dsi.c b/drivers/video/stm32/stm32_dsi.c
> new file mode 100644
> index 0000000..3e26433
> --- /dev/null
> +++ b/drivers/video/stm32/stm32_dsi.c
> @@ -0,0 +1,427 @@
> +/*
> + * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
> + * Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
> + *	      Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
> + *
> + * This driver is based on the mipi dsi driver from
> + * drivers/gpu/drm/stm/dw_mipi_dsi-stm.c (kernel linux).
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#include <asm/io.h>
> +#include <asm/arch/gpio.h>
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dm/device-internal.h>
> +#include <mipi_display.h>
> +#include <dw_mipi_dsi.h>
> +#include <linux/iopoll.h>
> +#include <panel.h>
> +#include <reset.h>
> +#include <video.h>
> +#include <video_bridge.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define HWVER_130			0x31333000	/* IP version 1.30 */
> +#define HWVER_131			0x31333100	/* IP version 1.31 */
> +
> +/* DSI digital registers & bit definitions */
> +#define DSI_VERSION			0x00
> +#define VERSION				GENMASK(31, 8)
> +
> +/*
> + * DSI wrapper registers & bit definitions
> + * Note: registers are named as in the Reference Manual
> + */
> +#define DSI_WCFGR	0x0400		/* Wrapper ConFiGuration Reg */
> +#define WCFGR_DSIM	BIT(0)		/* DSI Mode */
> +#define WCFGR_COLMUX	GENMASK(3, 1)	/* COLor MUltipleXing */
> +
> +#define DSI_WCR		0x0404		/* Wrapper Control Reg */
> +#define WCR_DSIEN	BIT(3)		/* DSI ENable */
> +
> +#define DSI_WISR	0x040C		/* Wrapper Interrupt and Status Reg */
> +#define WISR_PLLLS	BIT(8)		/* PLL Lock Status */
> +#define WISR_RRS	BIT(12)		/* Regulator Ready Status */
> +
> +#define DSI_WPCR0	0x0418		/* Wrapper Phy Conf Reg 0 */
> +#define WPCR0_UIX4	GENMASK(5, 0)	/* Unit Interval X 4 */
> +#define WPCR0_TDDL	BIT(16)		/* Turn Disable Data Lanes */
> +
> +#define DSI_WRPCR	0x0430		/* Wrapper Regulator & Pll Ctrl Reg */
> +#define WRPCR_PLLEN	BIT(0)		/* PLL ENable */
> +#define WRPCR_NDIV	GENMASK(8, 2)	/* pll loop DIVision Factor */
> +#define WRPCR_IDF	GENMASK(14, 11)	/* pll Input Division Factor */
> +#define WRPCR_ODF	GENMASK(17, 16)	/* pll Output Division Factor */
> +#define WRPCR_REGEN	BIT(24)		/* REGulator ENable */
> +#define WRPCR_BGREN	BIT(28)		/* BandGap Reference ENable */
> +#define IDF_MIN		1
> +#define IDF_MAX		7
> +#define NDIV_MIN	10
> +#define NDIV_MAX	125
> +#define ODF_MIN		1
> +#define ODF_MAX		8
> +
> +/* dsi color format coding according to the datasheet */
> +enum dsi_color {
> +	DSI_RGB565_CONF1,
> +	DSI_RGB565_CONF2,
> +	DSI_RGB565_CONF3,
> +	DSI_RGB666_CONF1,
> +	DSI_RGB666_CONF2,
> +	DSI_RGB888,
> +};
> +
> +#define LANE_MIN_KBPS	31250
> +#define LANE_MAX_KBPS	500000
> +
> +/* Timeout for regulator on/off, pll lock/unlock & fifo empty */
> +#define TIMEOUT_US	200000
> +
> +struct stm32_dsi_priv {
> +	struct mipi_dsi_device device;
> +	void __iomem *base;
> +	struct udevice *panel;
> +	u32 pllref_clk;
> +	u32 hw_version;
> +	int lane_min_kbps;
> +	int lane_max_kbps;
> +};
> +
> +static inline void dsi_write(struct stm32_dsi_priv *dsi, u32 reg, u32 val)
> +{
> +	writel(val, dsi->base + reg);
> +}
> +
> +static inline u32 dsi_read(struct stm32_dsi_priv *dsi, u32 reg)
> +{
> +	return readl(dsi->base + reg);
> +}
> +
> +static inline void dsi_set(struct stm32_dsi_priv *dsi, u32 reg, u32 mask)
> +{
> +	dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
> +}
> +
> +static inline void dsi_clear(struct stm32_dsi_priv *dsi, u32 reg, u32 mask)
> +{
> +	dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask);
> +}
> +
> +static inline void dsi_update_bits(struct stm32_dsi_priv *dsi, u32 reg,
> +				   u32 mask, u32 val)
> +{
> +	dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
> +}
> +
> +static enum dsi_color dsi_color_from_mipi(u32 fmt)
> +{
> +	switch (fmt) {
> +	case MIPI_DSI_FMT_RGB888:
> +		return DSI_RGB888;
> +	case MIPI_DSI_FMT_RGB666:
> +		return DSI_RGB666_CONF2;
> +	case MIPI_DSI_FMT_RGB666_PACKED:
> +		return DSI_RGB666_CONF1;
> +	case MIPI_DSI_FMT_RGB565:
> +		return DSI_RGB565_CONF1;
> +	default:
> +		pr_err("MIPI color invalid, so we use rgb888\n");
> +	}
> +	return DSI_RGB888;
> +}
> +
> +static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
> +{
> +	int divisor = idf * odf;
> +
> +	/* prevent from division by 0 */
> +	if (!divisor)
> +		return 0;
> +
> +	return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
> +}
> +
> +static int dsi_pll_get_params(struct stm32_dsi_priv *dsi,
> +			      int clkin_khz, int clkout_khz,
> +			      int *idf, int *ndiv, int *odf)
> +{
> +	int i, o, n, n_min, n_max;
> +	int fvco_min, fvco_max, delta, best_delta; /* all in khz */
> +
> +	/* Early checks preventing division by 0 & odd results */
> +	if (clkin_khz <= 0 || clkout_khz <= 0)
> +		return -EINVAL;
> +
> +	fvco_min = dsi->lane_min_kbps * 2 * ODF_MAX;
> +	fvco_max = dsi->lane_max_kbps * 2 * ODF_MIN;
> +
> +	best_delta = 1000000; /* big started value (1000000khz) */
> +
> +	for (i = IDF_MIN; i <= IDF_MAX; i++) {
> +		/* Compute ndiv range according to Fvco */
> +		n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1;
> +		n_max = (fvco_max * i) / (2 * clkin_khz);
> +
> +		/* No need to continue idf loop if we reach ndiv max */
> +		if (n_min >= NDIV_MAX)
> +			break;
> +
> +		/* Clamp ndiv to valid values */
> +		if (n_min < NDIV_MIN)
> +			n_min = NDIV_MIN;
> +		if (n_max > NDIV_MAX)
> +			n_max = NDIV_MAX;
> +
> +		for (o = ODF_MIN; o <= ODF_MAX; o *= 2) {
> +			n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
> +			/* Check ndiv according to vco range */
> +			if (n < n_min || n > n_max)
> +				continue;
> +			/* Check if new delta is better & saves parameters */
> +			delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) -
> +				clkout_khz;
> +			if (delta < 0)
> +				delta = -delta;
> +			if (delta < best_delta) {
> +				*idf = i;
> +				*ndiv = n;
> +				*odf = o;
> +				best_delta = delta;
> +			}
> +			/* fast return in case of "perfect result" */
> +			if (!delta)
> +				return 0;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int dsi_phy_init(void *priv_data)
> +{
> +	struct mipi_dsi_device *device = priv_data;
> +	struct stm32_dsi_priv *dsi = dev_get_priv(device->dev);
> +	u32 val;
> +	int ret;
> +
> +	/* Enable the regulator */
> +	dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
> +	ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS,
> +				 TIMEOUT_US);
> +	if (ret) {
> +		pr_err("!TIMEOUT! waiting REGU\n");

dev_err()

> +		return ret;
> +	}
> +
> +	/* Enable the DSI PLL & wait for its lock */
> +	dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN);
> +	ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
> +				 TIMEOUT_US);
> +	if (ret) {
> +		pr_err("!TIMEOUT! waiting PLL\n");

dev_err()

> +		return ret;
> +	}
> +
> +	/* Enable the DSI wrapper */
> +	dsi_set(dsi, DSI_WCR, WCR_DSIEN);
> +
> +	return 0;
> +}
> +
> +static int dsi_get_lane_mbps(void *priv_data, struct display_timing *timings,
> +			     u32 lanes, u32 format, unsigned int *lane_mbps)
> +{
> +	struct mipi_dsi_device *device = priv_data;
> +	struct stm32_dsi_priv *dsi = dev_get_priv(device->dev);
> +	int idf, ndiv, odf, pll_in_khz, pll_out_khz;
> +	int ret, bpp;
> +	u32 val;
> +
> +	/* Update lane capabilities according to hw version */
> +	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
> +	dsi->lane_min_kbps = LANE_MIN_KBPS;
> +	dsi->lane_max_kbps = LANE_MAX_KBPS;
> +	if (dsi->hw_version == HWVER_131) {
> +		dsi->lane_min_kbps *= 2;
> +		dsi->lane_max_kbps *= 2;
> +	}
> +
> +	pll_in_khz = dsi->pllref_clk / 1000;
> +
> +	/* Compute requested pll out */
> +	bpp = mipi_dsi_pixel_format_to_bpp(format);
> +	pll_out_khz = (timings->pixelclock.typ / 1000) * bpp / lanes;
> +	/* Add 20% to pll out to be higher than pixel bw (burst mode only) */
> +	pll_out_khz = (pll_out_khz * 12) / 10;
> +	if (pll_out_khz > dsi->lane_max_kbps) {
> +		pll_out_khz = dsi->lane_max_kbps;
> +		pr_warn("Warning max phy mbps is used\n");
> +	}
> +	if (pll_out_khz < dsi->lane_min_kbps) {
> +		pll_out_khz = dsi->lane_min_kbps;
> +		pr_warn("Warning min phy mbps is used\n");
> +	}
> +
> +	/* Compute best pll parameters */
> +	idf = 0;
> +	ndiv = 0;
> +	odf = 0;
> +	ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz,
> +				 &idf, &ndiv, &odf);
> +	if (ret) {
> +		pr_warn("Warning dsi_pll_get_params(): bad params\n");

dev_warn() or dev_err() ?

> +		return ret;
> +	}
> +
> +	/* Get the adjusted pll out value */
> +	pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
> +
> +	/* Set the PLL division factors */
> +	dsi_update_bits(dsi, DSI_WRPCR,	WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
> +			(ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
> +
> +	/* Compute uix4 & set the bit period in high-speed mode */
> +	val = 4000000 / pll_out_khz;
> +	dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
> +
> +	/* Select video mode by resetting DSIM bit */
> +	dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
> +
> +	/* Select the color coding */
> +	dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX,
> +			dsi_color_from_mipi(format) << 1);
> +
> +	*lane_mbps = pll_out_khz / 1000;
> +
> +	pr_info("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n",
> +		pll_in_khz, pll_out_khz, *lane_mbps);

is it useful to print this each time ? or is it for debug purpose ? in 
this case use debug()

> +
> +	return 0;
> +}
> +
> +static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
> +	.init = dsi_phy_init,
> +	.get_lane_mbps = dsi_get_lane_mbps,
> +};
> +
> +static int stm32_dsi_attach(struct udevice *dev)
> +{
> +	struct stm32_dsi_priv *priv = dev_get_priv(dev);
> +	struct dw_mipi_dsi_plat_data *platdata = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = &priv->device;
> +	int ret;
> +
> +	platdata->max_data_lanes = 2;
> +	platdata->phy_ops = &dw_mipi_dsi_stm_phy_ops;
> +
> +	ret = uclass_first_device(UCLASS_PANEL, &platdata->panel);
> +	if (ret) {
> +		pr_err("%s: panel device error %d\n", __func__, ret);

dev_err()

> +		return ret;
> +	}
> +
> +	ret = dw_mipi_dsi_init_bridge(device);
> +	if (ret) {
> +		pr_err("Failed to initialize mipi dsi host\n");

ditto

> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_dsi_set_backlight(struct udevice *dev, int percent)
> +{
> +	struct dw_mipi_dsi_plat_data *dplat = dev_get_platdata(dev);
> +	struct stm32_dsi_priv *priv = dev_get_priv(dev);
> +	struct mipi_dsi_device *device = &priv->device;
> +	struct udevice *panel = dplat->panel;
> +	struct mipi_dsi_panel_plat *mplat;
> +	int ret;
> +
> +	mplat = dev_get_platdata(panel);
> +	mplat->device = device;
> +
> +	ret = panel_enable_backlight(panel);
> +	if (ret) {
> +		pr_err("%s: panel %s enable backlight error %d\n",
> +		       __func__, panel->name, ret);

ditto

> +		return ret;
> +	}
> +
> +	dw_mipi_dsi_bridge_enable(device);
> +
> +	return 0;
> +}
> +
> +static int stm32_dsi_probe(struct udevice *dev)
> +{
> +	struct stm32_dsi_priv *priv = dev_get_priv(dev);
> +	struct mipi_dsi_device *device = &priv->device;
> +	struct reset_ctl rst;
> +	struct clk clk;
> +	int ret;
> +
> +	device->dev = dev;
> +
> +	priv->base = (void *)dev_read_addr(dev);
> +	if ((fdt_addr_t)priv->base == FDT_ADDR_T_NONE) {
> +		pr_err("%s: dsi dt register address error\n", __func__);

ditto

> +		return -EINVAL;
> +	}
> +
> +	ret = clk_get_by_name(device->dev, "pclk", &clk);
> +	if (ret) {
> +		pr_err("%s: peripheral clock get error %d\n", __func__, ret);

ditto

> +		return -ENODEV;
> +	}
> +
> +	ret = clk_enable(&clk);
> +	if (ret) {
> +		pr_err("%s: peripheral clock enable error %d\n", __func__, ret);

ditto

> +		return -ENODEV;
> +	}
> +
> +	ret = clk_get_by_name(dev, "ref", &clk);
> +	if (ret) {
> +		pr_err("%s: pll reference clock get error %d\n", __func__, ret);

ditto

> +		return ret;
> +	}
> +
> +	priv->pllref_clk = (unsigned int)clk_get_rate(&clk);
> +
> +	ret = reset_get_by_index(device->dev, 0, &rst);
> +	if (ret) {
> +		pr_err("%s: missing dsi hardware reset\n", __func__);

ditto

> +		return -ENODEV;
> +	}
> +
> +	/* Reset */
> +	reset_deassert(&rst);
> +
> +	return 0;
> +}
> +
> +struct video_bridge_ops stm32_dsi_ops = {
> +	.attach = stm32_dsi_attach,
> +	.set_backlight = stm32_dsi_set_backlight,
> +};
> +
> +static const struct udevice_id stm32_dsi_ids[] = {
> +	{ .compatible = "st,stm32-dsi"},
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(stm32_dsi) = {
> +	.name				= "stm32-display-dsi",
> +	.id				= UCLASS_VIDEO_BRIDGE,
> +	.of_match			= stm32_dsi_ids,
> +	.bind				= dm_scan_fdt_dev,
> +	.probe				= stm32_dsi_probe,
> +	.ops				= &stm32_dsi_ops,
> +	.priv_auto_alloc_size		= sizeof(struct stm32_dsi_priv),
> +	.platdata_auto_alloc_size	= sizeof(struct dw_mipi_dsi_plat_data),
> +};
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 07/10] video: add support of panel rm68200
  2018-03-02 15:44 ` [PATCH v2 07/10] video: add support of panel rm68200 yannick fertre
@ 2018-03-13  8:59   ` Patrice CHOTARD
  0 siblings, 0 replies; 20+ messages in thread
From: Patrice CHOTARD @ 2018-03-13  8:59 UTC (permalink / raw)
  To: Yannick FERTRE, Vikas MANOCHA, Benjamin Gaignard, Philippe CORNU,
	Patrick DELAUNAY, Christophe KERELLO, Archit Taneja,
	Andrzej Hajda, Laurent Pinchart, David Airlie, Brian Norris,
	Bhumika Goyal, Gustavo Padovan, Maarten Lankhorst, Sean Paul,
	Albert Aribaud, Simon Glass, Anatolij Gustschin, Thierry Reding
  Cc: u-boot, dri-devel, linux-kernel

Hi yannick

On 03/02/2018 04:44 PM, yannick fertre wrote:
> Support for Raydium rm68200 720p dsi 2dl video mode panel.
> 
> Signed-off-by: yannick fertre <yannick.fertre@st.com>
> ---
>   drivers/video/Kconfig           |   8 +
>   drivers/video/Makefile          |   1 +
>   drivers/video/raydium-rm68200.c | 329 ++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 338 insertions(+)
>   create mode 100644 drivers/video/raydium-rm68200.c
> 
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 0f641d7..2561c59 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -328,6 +328,14 @@ config VIDEO_LCD_ORISETECH_OTM8009A
>   	---help---
>   	Support for Orise Tech otm8009a 480p dsi 2dl video mode panel.
>   
> +config VIDEO_LCD_RAYDIUM_RM68200
> +	bool "RM68200 DSI LCD panel support"
> +	depends on DM_VIDEO
> +	select VIDEO_MIPI_DSI
> +	default n
> +	---help---
> +	Support for Raydium rm68200 720x1280 dsi 2dl video mode panel.
> +
>   config VIDEO_LCD_SSD2828
>   	bool "SSD2828 bridge chip"
>   	default n
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 50be569..1a6c8d3 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o
>   obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
>   obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
>   obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
> +obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o
>   obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
>   obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o
>   obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
> diff --git a/drivers/video/raydium-rm68200.c b/drivers/video/raydium-rm68200.c
> new file mode 100644
> index 0000000..46afb58
> --- /dev/null
> +++ b/drivers/video/raydium-rm68200.c
> @@ -0,0 +1,329 @@
> +/*
> + * Copyright (C) 2018 STMicroelectronics - All Rights Reserved
> + * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
> + *	      Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
> + *
> + * This rm68200 panel driver is based on the panel driver from
> + * drivers/gpu/drm/panel/panel-raydium-rm68200.c (kernel linux)
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +#include <asm/gpio.h>
> +#include <backlight.h>
> +#include <common.h>
> +#include <dm.h>
> +#include <panel.h>
> +#include <power/regulator.h>
> +#include <mipi_display.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define DRV_NAME "raydium_rm68200"
> +
> +/*** Manufacturer Command Set ***/
> +#define MCS_CMD_MODE_SW	0xFE /* CMD Mode Switch */
> +#define MCS_CMD1_UCS	0x00 /* User Command Set (UCS = CMD1) */
> +#define MCS_CMD2_P0	0x01 /* Manufacture Command Set Page0 (CMD2 P0) */
> +#define MCS_CMD2_P1	0x02 /* Manufacture Command Set Page1 (CMD2 P1) */
> +#define MCS_CMD2_P2	0x03 /* Manufacture Command Set Page2 (CMD2 P2) */
> +#define MCS_CMD2_P3	0x04 /* Manufacture Command Set Page3 (CMD2 P3) */
> +
> +/* CMD2 P0 commands (Display Options and Power) */
> +#define MCS_STBCTR	0x12 /* TE1 Output Setting Zig-Zag Connection */
> +#define MCS_SGOPCTR	0x16 /* Source Bias Current */
> +#define MCS_SDCTR	0x1A /* Source Output Delay Time */
> +#define MCS_INVCTR	0x1B /* Inversion Type */
> +#define MCS_EXT_PWR_IC	0x24 /* External PWR IC Control */
> +#define MCS_SETAVDD	0x27 /* PFM Control for AVDD Output */
> +#define MCS_SETAVEE	0x29 /* PFM Control for AVEE Output */
> +#define MCS_BT2CTR	0x2B /* DDVDL Charge Pump Control */
> +#define MCS_BT3CTR	0x2F /* VGH Charge Pump Control */
> +#define MCS_BT4CTR	0x34 /* VGL Charge Pump Control */
> +#define MCS_VCMCTR	0x46 /* VCOM Output Level Control */
> +#define MCS_SETVGN	0x52 /* VG M/S N Control */
> +#define MCS_SETVGP	0x54 /* VG M/S P Control */
> +#define MCS_SW_CTRL	0x5F /* Interface Control for PFM and MIPI */
> +
> +/* CMD2 P2 commands (GOA Timing Control) - no description in datasheet */
> +#define GOA_VSTV1	0x00
> +#define GOA_VSTV2	0x07
> +#define GOA_VCLK1	0x0E
> +#define GOA_VCLK2	0x17
> +#define GOA_VCLK_OPT1	0x20
> +#define GOA_BICLK1	0x2A
> +#define GOA_BICLK2	0x37
> +#define GOA_BICLK3	0x44
> +#define GOA_BICLK4	0x4F
> +#define GOA_BICLK_OPT1	0x5B
> +#define GOA_BICLK_OPT2	0x60
> +#define MCS_GOA_GPO1	0x6D
> +#define MCS_GOA_GPO2	0x71
> +#define MCS_GOA_EQ	0x74
> +#define MCS_GOA_CLK_GALLON 0x7C
> +#define MCS_GOA_FS_SEL0	0x7E
> +#define MCS_GOA_FS_SEL1	0x87
> +#define MCS_GOA_FS_SEL2	0x91
> +#define MCS_GOA_FS_SEL3	0x9B
> +#define MCS_GOA_BS_SEL0	0xAC
> +#define MCS_GOA_BS_SEL1	0xB5
> +#define MCS_GOA_BS_SEL2	0xBF
> +#define MCS_GOA_BS_SEL3	0xC9
> +#define MCS_GOA_BS_SEL4	0xD3
> +
> +/* CMD2 P3 commands (Gamma) */
> +#define MCS_GAMMA_VP	0x60 /* Gamma VP1~VP16 */
> +#define MCS_GAMMA_VN	0x70 /* Gamma VN1~VN16 */
> +
> +struct rm68200_panel_priv {
> +	struct udevice *reg;
> +	struct udevice *backlight;
> +	struct gpio_desc reset;
> +};
> +
> +static void rm68200_dcs_write_buf(struct udevice *dev, const void *data,
> +				  size_t len)
> +{
> +	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = plat->device;
> +
> +	if (mipi_dsi_dcs_write_buffer(device, data, len) < 0)
> +		pr_err("mipi dsi dcs write buffer failed\n");

pr_err()

> +}
> +
> +static void rm68200_dcs_write_cmd(struct udevice *dev, u8 cmd, u8 value)
> +{
> +	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = plat->device;
> +
> +	if (mipi_dsi_dcs_write(device, cmd, &value, 1) < 0)
> +		pr_err("mipi dsi dcs write failed\n");

ditto

> +}
> +
> +#define dcs_write_seq(ctx, seq...)				\
> +({								\
> +	static const u8 d[] = { seq };				\
> +	rm68200_dcs_write_buf(ctx, d, ARRAY_SIZE(d));		\
> +})
> +
> +/*
> + * This panel is not able to auto-increment all cmd addresses so for some of
> + * them, we need to send them one by one...
> + */
> +#define dcs_write_cmd_seq(ctx, cmd, seq...)			\
> +({								\
> +	static const u8 d[] = { seq };				\
> +	static int i;						\
> +	for (i = 0; i < ARRAY_SIZE(d) ; i++)			\
> +		rm68200_dcs_write_cmd(ctx, cmd + i, d[i]);	\
> +})
> +
> +static int rm68200_init_sequence(struct udevice *dev)
> +{
> +	/* Enter CMD2 with page 0 */
> +	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P0);
> +	dcs_write_cmd_seq(dev, MCS_EXT_PWR_IC, 0xC0, 0x53, 0x00);
> +	dcs_write_seq(dev, MCS_BT2CTR, 0xE5);
> +	dcs_write_seq(dev, MCS_SETAVDD, 0x0A);
> +	dcs_write_seq(dev, MCS_SETAVEE, 0x0A);
> +	dcs_write_seq(dev, MCS_SGOPCTR, 0x52);
> +	dcs_write_seq(dev, MCS_BT3CTR, 0x53);
> +	dcs_write_seq(dev, MCS_BT4CTR, 0x5A);
> +	dcs_write_seq(dev, MCS_INVCTR, 0x00);
> +	dcs_write_seq(dev, MCS_STBCTR, 0x0A);
> +	dcs_write_seq(dev, MCS_SDCTR, 0x06);
> +	dcs_write_seq(dev, MCS_VCMCTR, 0x56);
> +	dcs_write_seq(dev, MCS_SETVGN, 0xA0, 0x00);
> +	dcs_write_seq(dev, MCS_SETVGP, 0xA0, 0x00);
> +	dcs_write_seq(dev, MCS_SW_CTRL, 0x11); /* 2 data lanes, see doc */
> +
> +	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P2);
> +	dcs_write_seq(dev, GOA_VSTV1, 0x05);
> +	dcs_write_seq(dev, 0x02, 0x0B);
> +	dcs_write_seq(dev, 0x03, 0x0F);
> +	dcs_write_seq(dev, 0x04, 0x7D, 0x00, 0x50);
> +	dcs_write_cmd_seq(dev, GOA_VSTV2, 0x05, 0x16, 0x0D, 0x11, 0x7D, 0x00,
> +			  0x50);
> +	dcs_write_cmd_seq(dev, GOA_VCLK1, 0x07, 0x08, 0x01, 0x02, 0x00, 0x7D,
> +			  0x00, 0x85, 0x08);
> +	dcs_write_cmd_seq(dev, GOA_VCLK2, 0x03, 0x04, 0x05, 0x06, 0x00, 0x7D,
> +			  0x00, 0x85, 0x08);
> +	dcs_write_seq(dev, GOA_VCLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00);
> +	dcs_write_cmd_seq(dev, GOA_BICLK1, 0x07, 0x08);
> +	dcs_write_seq(dev, 0x2D, 0x01);
> +	dcs_write_seq(dev, 0x2F, 0x02, 0x00, 0x40, 0x05, 0x08, 0x54, 0x7D,
> +		      0x00);
> +	dcs_write_cmd_seq(dev, GOA_BICLK2, 0x03, 0x04, 0x05, 0x06, 0x00);
> +	dcs_write_seq(dev, 0x3D, 0x40);
> +	dcs_write_seq(dev, 0x3F, 0x05, 0x08, 0x54, 0x7D, 0x00);
> +	dcs_write_seq(dev, GOA_BICLK3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(dev, GOA_BICLK4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(dev, 0x58, 0x00, 0x00, 0x00);
> +	dcs_write_seq(dev, GOA_BICLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(dev, GOA_BICLK_OPT2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(dev, MCS_GOA_GPO1, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(dev, MCS_GOA_GPO2, 0x00, 0x20, 0x00);
> +	dcs_write_seq(dev, MCS_GOA_EQ, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
> +		      0x00, 0x00);
> +	dcs_write_seq(dev, MCS_GOA_CLK_GALLON, 0x00, 0x00);
> +	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL0, 0xBF, 0x02, 0x06, 0x14, 0x10,
> +			  0x16, 0x12, 0x08, 0x3F);
> +	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0C,
> +			  0x0A, 0x0E, 0x3F, 0x3F, 0x00);
> +	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL2, 0x04, 0x3F, 0x3F, 0x3F, 0x3F,
> +			  0x05, 0x01, 0x3F, 0x3F, 0x0F);
> +	dcs_write_cmd_seq(dev, MCS_GOA_FS_SEL3, 0x0B, 0x0D, 0x3F, 0x3F, 0x3F,
> +			  0x3F);
> +	dcs_write_cmd_seq(dev, 0xA2, 0x3F, 0x09, 0x13, 0x17, 0x11, 0x15);
> +	dcs_write_cmd_seq(dev, 0xA9, 0x07, 0x03, 0x3F);
> +	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL0, 0x3F, 0x05, 0x01, 0x17, 0x13,
> +			  0x15, 0x11, 0x0F, 0x3F);
> +	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0B,
> +			  0x0D, 0x09, 0x3F, 0x3F, 0x07);
> +	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL2, 0x03, 0x3F, 0x3F, 0x3F, 0x3F,
> +			  0x02, 0x06, 0x3F, 0x3F, 0x08);
> +	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL3, 0x0C, 0x0A, 0x3F, 0x3F, 0x3F,
> +			  0x3F, 0x3F, 0x0E, 0x10, 0x14);
> +	dcs_write_cmd_seq(dev, MCS_GOA_BS_SEL4, 0x12, 0x16, 0x00, 0x04, 0x3F);
> +	dcs_write_seq(dev, 0xDC, 0x02);
> +	dcs_write_seq(dev, 0xDE, 0x12);
> +
> +	dcs_write_seq(dev, MCS_CMD_MODE_SW, 0x0E); /* No documentation */
> +	dcs_write_seq(dev, 0x01, 0x75);
> +
> +	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD2_P3);
> +	dcs_write_cmd_seq(dev, MCS_GAMMA_VP, 0x00, 0x0C, 0x12, 0x0E, 0x06,
> +			  0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,
> +			  0x12, 0x0C, 0x00);
> +	dcs_write_cmd_seq(dev, MCS_GAMMA_VN, 0x00, 0x0C, 0x12, 0x0E, 0x06,
> +			  0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,
> +			  0x12, 0x0C, 0x00);
> +
> +	/* Exit CMD2 */
> +	dcs_write_seq(dev, MCS_CMD_MODE_SW, MCS_CMD1_UCS);
> +
> +	return 0;
> +}
> +
> +static int rm68200_panel_enable_backlight(struct udevice *dev)
> +{
> +	struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev);
> +	struct mipi_dsi_device *device = plat->device;
> +	struct rm68200_panel_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	device->lanes = 2;
> +	device->format = MIPI_DSI_FMT_RGB888;
> +	device->mode_flags = MIPI_DSI_MODE_VIDEO |
> +			     MIPI_DSI_MODE_VIDEO_BURST |
> +			     MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_attach(device);
> +	if (ret < 0)
> +		return ret;
> +
> +	pr_info("%s: initialize panel\n", __func__);

debug() ?

> +
> +	ret = rm68200_init_sequence(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_exit_sleep_mode(device);
> +	if (ret)
> +		return ret;
> +
> +	mdelay(125);
> +
> +	ret = mipi_dsi_dcs_set_display_on(device);
> +	if (ret)
> +		return ret;
> +
> +	mdelay(20);
> +
> +	pr_info("%s: start, backlight = '%s'\n", __func__,
> +		priv->backlight->name);
> +

debug() ?

> +	ret = backlight_enable(priv->backlight);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int rm68200_panel_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct rm68200_panel_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	if (IS_ENABLED(CONFIG_DM_REGULATOR)) {
> +		ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
> +						   "power-supply", &priv->reg);
> +		if (ret) {
> +			pr_info("%s: Warning: cannot get power supply\n",
> +				__func__);

dev_info()

> +			if (ret != -ENOENT)
> +				return ret;
> +		}
> +	}
> +
> +	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
> +				   GPIOD_IS_OUT);
> +	if (ret) {
> +		pr_info("%s: Warning: cannot get reset GPIO\n", __func__);

ditto

> +		if (ret != -ENOENT)
> +			return ret;
> +	}
> +
> +	/* reset panel must be done before probe */
> +	dm_gpio_set_value(&priv->reset, true);
> +
> +	return 0;
> +}
> +
> +static int rm68200_panel_probe(struct udevice *dev)
> +{
> +	struct rm68200_panel_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
> +		pr_info("%s: Enable regulator '%s'\n", __func__,
> +			priv->reg->name);

debug() ?

> +		ret = regulator_set_enable(priv->reg, true);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	dm_gpio_set_value(&priv->reset, false);
> +
> +	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
> +					   "backlight", &priv->backlight);
> +	if (ret) {
> +		pr_info("%s: Cannot get backlight: ret=%d\n", __func__, ret);

dev_err()

> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct panel_ops rm68200_panel_ops = {
> +	.enable_backlight = rm68200_panel_enable_backlight,
> +};
> +
> +static const struct udevice_id rm68200_panel_ids[] = {
> +	{ .compatible = "raydium,rm68200" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(rm68200_panel) = {
> +	.name			  = "rm68200_panel",
> +	.id			  = UCLASS_PANEL,
> +	.of_match		  = rm68200_panel_ids,
> +	.ops			  = &rm68200_panel_ops,
> +	.ofdata_to_platdata	  = rm68200_panel_ofdata_to_platdata,
> +	.probe			  = rm68200_panel_probe,
> +	.platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat),
> +	.priv_auto_alloc_size	= sizeof(struct rm68200_panel_priv),
> +};
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2018-03-13  8:59 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-02 15:44 [PATCH v2 00/10] splash screen on the stm32f769 disco board yannick fertre
2018-03-02 15:44 ` [PATCH v2 01/10] video: stm32: stm32_ltdc: add bridge to display controller yannick fertre
2018-03-13  8:44   ` Patrice CHOTARD
2018-03-02 15:44 ` [PATCH v2 02/10] video: stm32: stm32_ltdc: update debug log yannick fertre
2018-03-13  8:48   ` Patrice CHOTARD
2018-03-02 15:44 ` [PATCH v2 03/10] video: add support of MIPI DSI interface yannick fertre
2018-03-02 15:44 ` [PATCH v2 04/10] video: add support of panel OTM8009A yannick fertre
2018-03-13  8:52   ` Patrice CHOTARD
2018-03-02 15:44 ` [PATCH v2 05/10] video: add MIPI DSI host controller bridge yannick fertre
2018-03-02 18:58   ` Brian Norris
2018-03-02 19:02     ` Brian Norris
2018-03-02 15:44 ` [PATCH v2 06/10] video: add support of STM32 MIPI DSI controller driver yannick fertre
2018-03-13  8:56   ` Patrice CHOTARD
2018-03-02 15:44 ` [PATCH v2 07/10] video: add support of panel rm68200 yannick fertre
2018-03-13  8:59   ` Patrice CHOTARD
2018-03-02 15:44 ` [PATCH v2 08/10] arm: dts: stm32: add dsi for STM32F746 yannick fertre
2018-03-02 15:44 ` [PATCH v2 09/10] arm: dts: stm32: add display for STM32F769 disco board yannick fertre
2018-03-02 15:44 ` [PATCH v2 10/10] board: Add STM32F769 SoC, discovery board support yannick fertre
2018-03-13  8:41   ` Patrice CHOTARD
2018-03-06 17:51 ` [PATCH v2 00/10] splash screen on the stm32f769 disco board Simon Glass

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).