All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/3] Add support for the otm8009a dsi panel
@ 2017-07-04 16:30 ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: Alexandre Torgue, Thierry Reding, David Airlie, Maxime Coquelin,
	Russell King, Mark Rutland, Rob Herring, Arnd Bergmann,
	Benjamin Gaignard, Yannick Fertre, Archit Taneja
  Cc: linux-arm-kernel, devicetree, Philippe Cornu, Fabien Dessenne,
	dri-devel, Mickael Reulier, Vincent Abriou, Gabriel Fernandez,
	Ludovic Barre

Hi all,

The purpose of this patch is to add support for the Orise Tech
otm8009a 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode).
This LCD panel is used in several STM32 boards.

Version 1:
- Initial commit

Best regards,
Philippe

Philippe CORNU (3):
  dt-bindings: Add vendor prefix for Orise Technology
  dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi
    panel
  drm/panel: Add support for otm8009a panel driver

 .../bindings/display/panel/orisetech,otm8009a.txt  |  20 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |   9 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c   | 517 +++++++++++++++++++++
 5 files changed, 548 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
 create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c

-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 0/3] Add support for the otm8009a dsi panel
@ 2017-07-04 16:30 ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

The purpose of this patch is to add support for the Orise Tech
otm8009a 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode).
This LCD panel is used in several STM32 boards.

Version 1:
- Initial commit

Best regards,
Philippe

Philippe CORNU (3):
  dt-bindings: Add vendor prefix for Orise Technology
  dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi
    panel
  drm/panel: Add support for otm8009a panel driver

 .../bindings/display/panel/orisetech,otm8009a.txt  |  20 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |   9 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c   | 517 +++++++++++++++++++++
 5 files changed, 548 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
 create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c

-- 
1.9.1

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

* [PATCH v1 1/3] dt-bindings: Add vendor prefix for Orise Technology
  2017-07-04 16:30 ` Philippe CORNU
@ 2017-07-04 16:30   ` Philippe CORNU
  -1 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: Alexandre Torgue, Thierry Reding, David Airlie, Maxime Coquelin,
	Russell King, Mark Rutland, Rob Herring, Arnd Bergmann,
	Benjamin Gaignard, Yannick Fertre, Archit Taneja
  Cc: linux-arm-kernel, devicetree, Philippe Cornu, Fabien Dessenne,
	dri-devel, Mickael Reulier, Vincent Abriou, Gabriel Fernandez,
	Ludovic Barre

Orise Technology is headquartered in Taiwan and specializes
in manufacture of Flat Panel Display Driver IC and Flat Panel
Display Controller IC.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index f08284e..e6d5f75 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -235,6 +235,7 @@ ontat	On Tat Industrial Company
 opencores	OpenCores.org
 option	Option NV
 ORCL	Oracle Corporation
+orisetech	Orise Technology
 ortustech	Ortus Technology Co., Ltd.
 ovti	OmniVision Technologies
 oxsemi	Oxford Semiconductor, Ltd.
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 1/3] dt-bindings: Add vendor prefix for Orise Technology
@ 2017-07-04 16:30   ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: linux-arm-kernel

Orise Technology is headquartered in Taiwan and specializes
in manufacture of Flat Panel Display Driver IC and Flat Panel
Display Controller IC.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index f08284e..e6d5f75 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -235,6 +235,7 @@ ontat	On Tat Industrial Company
 opencores	OpenCores.org
 option	Option NV
 ORCL	Oracle Corporation
+orisetech	Orise Technology
 ortustech	Ortus Technology Co., Ltd.
 ovti	OmniVision Technologies
 oxsemi	Oxford Semiconductor, Ltd.
-- 
1.9.1

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

* [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
  2017-07-04 16:30 ` Philippe CORNU
@ 2017-07-04 16:30   ` Philippe CORNU
  -1 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: Alexandre Torgue, Thierry Reding, David Airlie, Maxime Coquelin,
	Russell King, Mark Rutland, Rob Herring, Arnd Bergmann,
	Benjamin Gaignard, Yannick Fertre, Archit Taneja
  Cc: linux-arm-kernel, devicetree, Philippe Cornu, Fabien Dessenne,
	dri-devel, Mickael Reulier, Vincent Abriou, Gabriel Fernandez,
	Ludovic Barre

The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
a MIPI-DSI video interface. Its backlight is managed through the DSI link.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
---
 .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt

diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
new file mode 100644
index 0000000..0bb8237
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
@@ -0,0 +1,20 @@
+Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
+
+The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
+a MIPI-DSI video interface. Its backlight is managed through the DSI link.
+
+Required properties:
+  - compatible: "orisetech,otm8009a"
+  - reg: the virtual channel number of a DSI peripheral
+  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)
+
+Example:
+&dsi {
+	...
+
+	panel@0 {
+		compatible = "orisetech,otm8009a";
+		reg = <0>;
+		reset-gpios = <&gpioh 7 0>;
+	};
+};
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
@ 2017-07-04 16:30   ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: linux-arm-kernel

The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
a MIPI-DSI video interface. Its backlight is managed through the DSI link.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
---
 .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt

diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
new file mode 100644
index 0000000..0bb8237
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
@@ -0,0 +1,20 @@
+Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
+
+The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
+a MIPI-DSI video interface. Its backlight is managed through the DSI link.
+
+Required properties:
+  - compatible: "orisetech,otm8009a"
+  - reg: the virtual channel number of a DSI peripheral
+  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)
+
+Example:
+&dsi {
+	...
+
+	panel at 0 {
+		compatible = "orisetech,otm8009a";
+		reg = <0>;
+		reset-gpios = <&gpioh 7 0>;
+	};
+};
-- 
1.9.1

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

* [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver
  2017-07-04 16:30 ` Philippe CORNU
@ 2017-07-04 16:30   ` Philippe CORNU
  -1 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: Alexandre Torgue, Thierry Reding, David Airlie, Maxime Coquelin,
	Russell King, Mark Rutland, Rob Herring, Arnd Bergmann,
	Benjamin Gaignard, Yannick Fertre, Archit Taneja
  Cc: linux-arm-kernel, devicetree, Philippe Cornu, Fabien Dessenne,
	dri-devel, Mickael Reulier, Vincent Abriou, Gabriel Fernandez,
	Ludovic Barre

This patch adds Orise Tech otm8009a 3.97" 480x800 TFT LCD
panel driver (MIPI-DSI video mode). The panel backlight is
managed through the DSI link. This panel driver is used in
several STM32 boards.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
---
 drivers/gpu/drm/panel/Kconfig                    |   9 +
 drivers/gpu/drm/panel/Makefile                   |   1 +
 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 517 +++++++++++++++++++++++
 3 files changed, 527 insertions(+)
 create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d84a031..c1c9291 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -117,4 +117,13 @@ config DRM_PANEL_SITRONIX_ST7789V
 	  Say Y here if you want to enable support for the Sitronix
 	  ST7789V controller for 240x320 LCD panels
 
+config DRM_PANEL_ORISETECH_OTM8009A
+	tristate "Orise Tech otm8009a 480p dsi 2dl video mode panel"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y here if you want to enable support for Orise Tech OTM8009A
+	  480x800 DSI panel
+
 endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 9f6610d..ac798f3 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
+obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
new file mode 100755
index 0000000..7aefc09
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <video/mipi_display.h>
+
+#define DRV_NAME "orisetech_otm8009a"
+
+#define OTM8009A_BACKLIGHT_DEFAULT	240
+#define OTM8009A_BACKLIGHT_MAX		255
+
+struct otm8009a {
+	struct device *dev;
+	struct drm_panel panel;
+	struct backlight_device *bl_dev;
+	struct gpio_desc *reset_gpio;
+	bool prepared;
+	bool enabled;
+};
+
+static const struct drm_display_mode default_mode = {
+	.clock = 32729,
+	.hdisplay = 480,
+	.hsync_start = 480 + 120,
+	.hsync_end = 480 + 120 + 63,
+	.htotal = 480 + 120 + 63 + 120,
+	.vdisplay = 800,
+	.vsync_start = 800 + 12,
+	.vsync_end = 800 + 12 + 12,
+	.vtotal = 800 + 12 + 12 + 12,
+	.vrefresh = 50,
+	.flags = 0,
+	.width_mm = 52,
+	.height_mm = 86,
+};
+
+static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
+{
+	return container_of(panel, struct otm8009a, panel);
+}
+
+static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
+				   size_t len)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+	if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
+		DRM_WARN("mipi dsi dcs write buffer failed");
+}
+
+#define dcs_write_seq(seq...)					\
+	do {							\
+		static const u8 d[] = { seq };			\
+		otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));	\
+	} while (0)
+
+static int otm8009a_init_sequence(struct otm8009a *ctx)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	/*
+	 * CMD2_ENA1: Enter in Command 2 mode, enable write function of
+	 * Command 2 & enable parameter shift function.
+	 * The 3 following sequences allow to enable ORISE command mode.
+	 */
+	dcs_write_seq(0xFF, 0x80, 0x09, 0x01);
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xFF, 0x80, 0x09);
+
+	/*
+	 * Starting from here, address shift needs to be set before sending
+	 * a new command. You can find an example in the next sequence...
+	 * SD_PCH_CTRL (0xC480) Source Driver Precharge Control (SD_PT=GND)
+	 */
+	dcs_write_seq(0x00, 0x80); /* address shift set to 0x80 */
+	dcs_write_seq(0xC4, 0x30); /* 0xC480 parameter 1 is 0x30 */
+	mdelay(10);
+
+	/* Not documented (0xC48A) */
+	dcs_write_seq(0x00, 0x8A);
+	dcs_write_seq(0xC4, 0x40);
+	mdelay(10);
+
+	/* PWR_CTRL4 (0xC5B0) Power Control Setting 4 for DC Voltage */
+	dcs_write_seq(0x00, 0xB1); /* 178th parameter */
+	dcs_write_seq(0xC5, 0xA9);
+
+	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
+	dcs_write_seq(0x00, 0x91); /* 146th parameter */
+	dcs_write_seq(0xC5, 0x34);
+
+	/* P_DRV_M (0xC0B4) Panel Driving Mode */
+	dcs_write_seq(0x00, 0xB4);
+	dcs_write_seq(0xC0, 0x50);
+
+	/* VCOMDC (0xD900) VCOM Voltage Setting */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xD9, 0x4E);
+
+	/* OSC_ADJ (0xC181) Oscillator Adjustment for Idle/Normal mode */
+	dcs_write_seq(0x00, 0x81);
+	dcs_write_seq(0xC1, 0x66); /* 65Hz */
+
+	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
+	dcs_write_seq(0x00, 0xA1);
+	dcs_write_seq(0xC1, 0x08);
+
+	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
+	dcs_write_seq(0x00, 0x92); /* 147th parameter */
+	dcs_write_seq(0xC5, 0x01);
+	dcs_write_seq(0x00, 0x95); /* 150th parameter */
+	dcs_write_seq(0xC5, 0x34);
+	dcs_write_seq(0x00, 0x94); /* 149th parameter */
+	dcs_write_seq(0xC5, 0x33);
+
+	/* GVDD/NGVDD (0xD800) */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xD8, 0x79, 0x79);
+
+	/* SD_CTRL (0xC0A2) Source Driver Timing Setting */
+	dcs_write_seq(0x00, 0xA3); /* 164th parameter */
+	dcs_write_seq(0xC0, 0x1B);
+
+	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
+	dcs_write_seq(0x00, 0x82); /* 131st parameter */
+	dcs_write_seq(0xC5, 0x83);
+
+	/* SD_PCH_CTRL (0xC480) Source Driver Precharge Control */
+	dcs_write_seq(0x00, 0x81); /* 130th parameter */
+	dcs_write_seq(0xC4, 0x83);
+
+	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
+	dcs_write_seq(0x00, 0xA1);
+	dcs_write_seq(0xC1, 0x0E); /* todo previously we wrote 0x08... */
+
+	/* PANSET (0xB3A6) Panel Type Setting */
+	dcs_write_seq(0x00, 0xA6);
+	dcs_write_seq(0xB3, 0x00, 0x01);
+
+	/* GOAVST (0xCE80) GOA VST Setting */
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
+
+	/* GOACLKA1 (0xCEA0) GOA CLKA1 Setting */
+	dcs_write_seq(0x00, 0xA0);
+	dcs_write_seq(0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18,
+		      0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
+
+	/* GOACLKA3 (0xCEB0) GOA CLKA3 Setting */
+	dcs_write_seq(0x00, 0xB0);
+	dcs_write_seq(0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18,
+		      0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
+
+	/* GOAECLK (0xCFC0) GOA ECLK Setting */
+	dcs_write_seq(0x00, 0xC0);
+	dcs_write_seq(0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02,
+		      0x00, 0x00);
+
+	/* Not documented */
+	dcs_write_seq(0x00, 0xD0);
+	dcs_write_seq(0xCF, 0x00);
+
+	/* PANCTRLSET1-8 (0xCB80-0xCBF0) Panel Control Setting 1-8 */
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0x90);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xA0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xB0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0xC0);
+	dcs_write_seq(0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xD0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+		      0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xE0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0xF0);
+	dcs_write_seq(0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		      0xFF, 0xFF);
+
+	/* PANU2D1-3 (0xCC80-0xCCA0) Panel U2D Setting 1-3 */
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0x90);
+	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
+	dcs_write_seq(0x00, 0xA0);
+	dcs_write_seq(0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+	/* PAND2U1-3 (0xCCB0-0xCCD0) Panel D2U Setting 1-3 */
+	dcs_write_seq(0x00, 0xB0);
+	dcs_write_seq(0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0xC0);
+	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
+	dcs_write_seq(0x00, 0xD0);
+	dcs_write_seq(0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
+	dcs_write_seq(0x00, 0x81); /* 130th parameter */
+	dcs_write_seq(0xC5, 0x66);
+
+	/* Not documented */
+	dcs_write_seq(0x00, 0xB6);
+	dcs_write_seq(0xF5, 0x06);
+
+	/* GMCT2.2P (0xE100) Gamma Correction 2.2+ Setting */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
+		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
+
+	/* GMCT2.2N (0xE100) Gamma Correction 2.2- Setting */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
+		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
+
+	/* Exit CMD2 mode */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xFF, 0xFF, 0xFF, 0xFF);
+
+	/* OTM8009a NOP */
+	dcs_write_seq(0x00, 0x00);
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret)
+		return ret;
+
+	/* Wait for sleep out exit */
+	mdelay(120);
+
+	/* Default portrait 480x800 rgb24 */
+	dcs_write_seq(MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+	dcs_write_seq(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xDF);
+	dcs_write_seq(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1F);
+	dcs_write_seq(MIPI_DCS_SET_PIXEL_FORMAT, 0x77);
+
+	/* Disable CABC feature */
+	dcs_write_seq(0x55, 0x00);
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret)
+		return ret;
+
+	/* OTM8009a NOP */
+	dcs_write_seq(0x00, 0x00);
+
+	/* Send Command GRAM memory write (no parameters) */
+	dcs_write_seq(MIPI_DCS_WRITE_MEMORY_START);
+
+	return 0;
+}
+
+static int otm8009a_disable(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	if (!ctx->enabled)
+		return 0; /* This is not an issue so we return 0 here */
+
+	/* Power off the backlight. Note: end-user still controls brightness */
+	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+	ret = backlight_update_status(ctx->bl_dev);
+	if (ret)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(120);
+
+	ctx->enabled = false;
+
+	return 0;
+}
+
+static int otm8009a_unprepare(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+
+	if (!ctx->prepared)
+		return 0;
+
+	if (ctx->reset_gpio) {
+		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+		msleep(20);
+	}
+
+	ctx->prepared = false;
+
+	return 0;
+}
+
+static int otm8009a_prepare(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+	int ret;
+
+	if (ctx->prepared)
+		return 0;
+
+	if (ctx->reset_gpio) {
+		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+		msleep(20);
+		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+		msleep(20);
+	}
+
+	ret = otm8009a_init_sequence(ctx);
+	if (ret)
+		return ret;
+
+	ctx->prepared = true;
+
+	/* Power on the backlight. Note: end-user still controls brightness */
+	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
+	backlight_update_status(ctx->bl_dev);
+
+	return 0;
+}
+
+static int otm8009a_enable(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+
+	ctx->enabled = true;
+
+	return 0;
+}
+
+static int otm8009a_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		DRM_ERROR("failed to add mode %ux%ux@%u\n",
+			  default_mode.hdisplay, default_mode.vdisplay,
+			  default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.width_mm = mode->width_mm;
+	panel->connector->display_info.height_mm = mode->height_mm;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs otm8009a_drm_funcs = {
+	.disable   = otm8009a_disable,
+	.unprepare = otm8009a_unprepare,
+	.prepare   = otm8009a_prepare,
+	.enable    = otm8009a_enable,
+	.get_modes = otm8009a_get_modes,
+};
+
+/*
+ * DSI-BASED BACKLIGHT
+ */
+
+static int otm8009a_backlight_update_status(struct backlight_device *bd)
+{
+	struct otm8009a *ctx = bl_get_data(bd);
+	u8 data[2];
+
+	if (!ctx->prepared) {
+		DRM_WARN("lcd not ready yet for setting its backlight!\n");
+		return -ENXIO;
+	}
+
+	if (bd->props.power <= FB_BLANK_NORMAL) {
+		/* Power on the backlight with the requested brightness */
+		data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
+		data[1] = bd->props.brightness;
+		otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
+		/* set Brightness Control & Backlight on */
+		data[1] = 0x24;
+	} else {
+		/* Power off the backlight: set Brightness Control & Bl off */
+		data[1] = 0;
+	}
+
+	/* Update Brightness Control & Backlight */
+	data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
+	otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
+
+	return 0;
+}
+
+static const struct backlight_ops otm8009a_backlight_ops = {
+	.update_status = otm8009a_backlight_update_status,
+};
+
+static int otm8009a_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct otm8009a *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ctx->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpio\n");
+		return PTR_ERR(ctx->reset_gpio);
+	}
+
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	ctx->dev = dev;
+
+	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;
+
+	drm_panel_init(&ctx->panel);
+	ctx->panel.dev = dev;
+	ctx->panel.funcs = &otm8009a_drm_funcs;
+
+	ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
+						&otm8009a_backlight_ops, NULL);
+	if (IS_ERR(ctx->bl_dev)) {
+		dev_err(dev, "failed to register backlight device\n");
+		return PTR_ERR(ctx->bl_dev);
+	}
+
+	ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
+	ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
+	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+	ctx->bl_dev->props.type = BACKLIGHT_RAW;
+
+	drm_panel_add(&ctx->panel);
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
+		drm_panel_remove(&ctx->panel);
+		backlight_device_unregister(ctx->bl_dev);
+		return ret;
+	}
+
+	DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
+		 default_mode.hdisplay, default_mode.vdisplay,
+		 default_mode.vrefresh,
+		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
+
+	return 0;
+}
+
+static int otm8009a_remove(struct mipi_dsi_device *dsi)
+{
+	struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
+
+	mipi_dsi_detach(dsi);
+	drm_panel_remove(&ctx->panel);
+
+	backlight_device_unregister(ctx->bl_dev);
+
+	return 0;
+}
+
+static const struct of_device_id orisetech_otm8009a_of_match[] = {
+	{ .compatible = "orisetech,otm8009a" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
+
+static struct mipi_dsi_driver orisetech_otm8009a_driver = {
+	.probe  = otm8009a_probe,
+	.remove = otm8009a_remove,
+	.driver = {
+		.name = DRV_NAME "_panel",
+		.of_match_table = orisetech_otm8009a_of_match,
+	},
+};
+module_mipi_dsi_driver(orisetech_otm8009a_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver
@ 2017-07-04 16:30   ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-04 16:30 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds Orise Tech otm8009a 3.97" 480x800 TFT LCD
panel driver (MIPI-DSI video mode). The panel backlight is
managed through the DSI link. This panel driver is used in
several STM32 boards.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
---
 drivers/gpu/drm/panel/Kconfig                    |   9 +
 drivers/gpu/drm/panel/Makefile                   |   1 +
 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 517 +++++++++++++++++++++++
 3 files changed, 527 insertions(+)
 create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d84a031..c1c9291 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -117,4 +117,13 @@ config DRM_PANEL_SITRONIX_ST7789V
 	  Say Y here if you want to enable support for the Sitronix
 	  ST7789V controller for 240x320 LCD panels
 
+config DRM_PANEL_ORISETECH_OTM8009A
+	tristate "Orise Tech otm8009a 480p dsi 2dl video mode panel"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y here if you want to enable support for Orise Tech OTM8009A
+	  480x800 DSI panel
+
 endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 9f6610d..ac798f3 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
+obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
new file mode 100755
index 0000000..7aefc09
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <video/mipi_display.h>
+
+#define DRV_NAME "orisetech_otm8009a"
+
+#define OTM8009A_BACKLIGHT_DEFAULT	240
+#define OTM8009A_BACKLIGHT_MAX		255
+
+struct otm8009a {
+	struct device *dev;
+	struct drm_panel panel;
+	struct backlight_device *bl_dev;
+	struct gpio_desc *reset_gpio;
+	bool prepared;
+	bool enabled;
+};
+
+static const struct drm_display_mode default_mode = {
+	.clock = 32729,
+	.hdisplay = 480,
+	.hsync_start = 480 + 120,
+	.hsync_end = 480 + 120 + 63,
+	.htotal = 480 + 120 + 63 + 120,
+	.vdisplay = 800,
+	.vsync_start = 800 + 12,
+	.vsync_end = 800 + 12 + 12,
+	.vtotal = 800 + 12 + 12 + 12,
+	.vrefresh = 50,
+	.flags = 0,
+	.width_mm = 52,
+	.height_mm = 86,
+};
+
+static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
+{
+	return container_of(panel, struct otm8009a, panel);
+}
+
+static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
+				   size_t len)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+	if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
+		DRM_WARN("mipi dsi dcs write buffer failed");
+}
+
+#define dcs_write_seq(seq...)					\
+	do {							\
+		static const u8 d[] = { seq };			\
+		otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));	\
+	} while (0)
+
+static int otm8009a_init_sequence(struct otm8009a *ctx)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	/*
+	 * CMD2_ENA1: Enter in Command 2 mode, enable write function of
+	 * Command 2 & enable parameter shift function.
+	 * The 3 following sequences allow to enable ORISE command mode.
+	 */
+	dcs_write_seq(0xFF, 0x80, 0x09, 0x01);
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xFF, 0x80, 0x09);
+
+	/*
+	 * Starting from here, address shift needs to be set before sending
+	 * a new command. You can find an example in the next sequence...
+	 * SD_PCH_CTRL (0xC480) Source Driver Precharge Control (SD_PT=GND)
+	 */
+	dcs_write_seq(0x00, 0x80); /* address shift set to 0x80 */
+	dcs_write_seq(0xC4, 0x30); /* 0xC480 parameter 1 is 0x30 */
+	mdelay(10);
+
+	/* Not documented (0xC48A) */
+	dcs_write_seq(0x00, 0x8A);
+	dcs_write_seq(0xC4, 0x40);
+	mdelay(10);
+
+	/* PWR_CTRL4 (0xC5B0) Power Control Setting 4 for DC Voltage */
+	dcs_write_seq(0x00, 0xB1); /* 178th parameter */
+	dcs_write_seq(0xC5, 0xA9);
+
+	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
+	dcs_write_seq(0x00, 0x91); /* 146th parameter */
+	dcs_write_seq(0xC5, 0x34);
+
+	/* P_DRV_M (0xC0B4) Panel Driving Mode */
+	dcs_write_seq(0x00, 0xB4);
+	dcs_write_seq(0xC0, 0x50);
+
+	/* VCOMDC (0xD900) VCOM Voltage Setting */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xD9, 0x4E);
+
+	/* OSC_ADJ (0xC181) Oscillator Adjustment for Idle/Normal mode */
+	dcs_write_seq(0x00, 0x81);
+	dcs_write_seq(0xC1, 0x66); /* 65Hz */
+
+	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
+	dcs_write_seq(0x00, 0xA1);
+	dcs_write_seq(0xC1, 0x08);
+
+	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
+	dcs_write_seq(0x00, 0x92); /* 147th parameter */
+	dcs_write_seq(0xC5, 0x01);
+	dcs_write_seq(0x00, 0x95); /* 150th parameter */
+	dcs_write_seq(0xC5, 0x34);
+	dcs_write_seq(0x00, 0x94); /* 149th parameter */
+	dcs_write_seq(0xC5, 0x33);
+
+	/* GVDD/NGVDD (0xD800) */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xD8, 0x79, 0x79);
+
+	/* SD_CTRL (0xC0A2) Source Driver Timing Setting */
+	dcs_write_seq(0x00, 0xA3); /* 164th parameter */
+	dcs_write_seq(0xC0, 0x1B);
+
+	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
+	dcs_write_seq(0x00, 0x82); /* 131st parameter */
+	dcs_write_seq(0xC5, 0x83);
+
+	/* SD_PCH_CTRL (0xC480) Source Driver Precharge Control */
+	dcs_write_seq(0x00, 0x81); /* 130th parameter */
+	dcs_write_seq(0xC4, 0x83);
+
+	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
+	dcs_write_seq(0x00, 0xA1);
+	dcs_write_seq(0xC1, 0x0E); /* todo previously we wrote 0x08... */
+
+	/* PANSET (0xB3A6) Panel Type Setting */
+	dcs_write_seq(0x00, 0xA6);
+	dcs_write_seq(0xB3, 0x00, 0x01);
+
+	/* GOAVST (0xCE80) GOA VST Setting */
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
+
+	/* GOACLKA1 (0xCEA0) GOA CLKA1 Setting */
+	dcs_write_seq(0x00, 0xA0);
+	dcs_write_seq(0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18,
+		      0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
+
+	/* GOACLKA3 (0xCEB0) GOA CLKA3 Setting */
+	dcs_write_seq(0x00, 0xB0);
+	dcs_write_seq(0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18,
+		      0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
+
+	/* GOAECLK (0xCFC0) GOA ECLK Setting */
+	dcs_write_seq(0x00, 0xC0);
+	dcs_write_seq(0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02,
+		      0x00, 0x00);
+
+	/* Not documented */
+	dcs_write_seq(0x00, 0xD0);
+	dcs_write_seq(0xCF, 0x00);
+
+	/* PANCTRLSET1-8 (0xCB80-0xCBF0) Panel Control Setting 1-8 */
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0x90);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xA0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xB0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0xC0);
+	dcs_write_seq(0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xD0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+		      0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00);
+	dcs_write_seq(0x00, 0xE0);
+	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0xF0);
+	dcs_write_seq(0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		      0xFF, 0xFF);
+
+	/* PANU2D1-3 (0xCC80-0xCCA0) Panel U2D Setting 1-3 */
+	dcs_write_seq(0x00, 0x80);
+	dcs_write_seq(0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0x90);
+	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
+	dcs_write_seq(0x00, 0xA0);
+	dcs_write_seq(0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+	/* PAND2U1-3 (0xCCB0-0xCCD0) Panel D2U Setting 1-3 */
+	dcs_write_seq(0x00, 0xB0);
+	dcs_write_seq(0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00,
+		      0x00, 0x00);
+	dcs_write_seq(0x00, 0xC0);
+	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
+	dcs_write_seq(0x00, 0xD0);
+	dcs_write_seq(0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
+	dcs_write_seq(0x00, 0x81); /* 130th parameter */
+	dcs_write_seq(0xC5, 0x66);
+
+	/* Not documented */
+	dcs_write_seq(0x00, 0xB6);
+	dcs_write_seq(0xF5, 0x06);
+
+	/* GMCT2.2P (0xE100) Gamma Correction 2.2+ Setting */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
+		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
+
+	/* GMCT2.2N (0xE100) Gamma Correction 2.2- Setting */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
+		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
+
+	/* Exit CMD2 mode */
+	dcs_write_seq(0x00, 0x00);
+	dcs_write_seq(0xFF, 0xFF, 0xFF, 0xFF);
+
+	/* OTM8009a NOP */
+	dcs_write_seq(0x00, 0x00);
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret)
+		return ret;
+
+	/* Wait for sleep out exit */
+	mdelay(120);
+
+	/* Default portrait 480x800 rgb24 */
+	dcs_write_seq(MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+	dcs_write_seq(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xDF);
+	dcs_write_seq(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1F);
+	dcs_write_seq(MIPI_DCS_SET_PIXEL_FORMAT, 0x77);
+
+	/* Disable CABC feature */
+	dcs_write_seq(0x55, 0x00);
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret)
+		return ret;
+
+	/* OTM8009a NOP */
+	dcs_write_seq(0x00, 0x00);
+
+	/* Send Command GRAM memory write (no parameters) */
+	dcs_write_seq(MIPI_DCS_WRITE_MEMORY_START);
+
+	return 0;
+}
+
+static int otm8009a_disable(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	int ret;
+
+	if (!ctx->enabled)
+		return 0; /* This is not an issue so we return 0 here */
+
+	/* Power off the backlight. Note: end-user still controls brightness */
+	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+	ret = backlight_update_status(ctx->bl_dev);
+	if (ret)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(120);
+
+	ctx->enabled = false;
+
+	return 0;
+}
+
+static int otm8009a_unprepare(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+
+	if (!ctx->prepared)
+		return 0;
+
+	if (ctx->reset_gpio) {
+		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+		msleep(20);
+	}
+
+	ctx->prepared = false;
+
+	return 0;
+}
+
+static int otm8009a_prepare(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+	int ret;
+
+	if (ctx->prepared)
+		return 0;
+
+	if (ctx->reset_gpio) {
+		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+		msleep(20);
+		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+		msleep(20);
+	}
+
+	ret = otm8009a_init_sequence(ctx);
+	if (ret)
+		return ret;
+
+	ctx->prepared = true;
+
+	/* Power on the backlight. Note: end-user still controls brightness */
+	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
+	backlight_update_status(ctx->bl_dev);
+
+	return 0;
+}
+
+static int otm8009a_enable(struct drm_panel *panel)
+{
+	struct otm8009a *ctx = panel_to_otm8009a(panel);
+
+	ctx->enabled = true;
+
+	return 0;
+}
+
+static int otm8009a_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		DRM_ERROR("failed to add mode %ux%ux@%u\n",
+			  default_mode.hdisplay, default_mode.vdisplay,
+			  default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.width_mm = mode->width_mm;
+	panel->connector->display_info.height_mm = mode->height_mm;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs otm8009a_drm_funcs = {
+	.disable   = otm8009a_disable,
+	.unprepare = otm8009a_unprepare,
+	.prepare   = otm8009a_prepare,
+	.enable    = otm8009a_enable,
+	.get_modes = otm8009a_get_modes,
+};
+
+/*
+ * DSI-BASED BACKLIGHT
+ */
+
+static int otm8009a_backlight_update_status(struct backlight_device *bd)
+{
+	struct otm8009a *ctx = bl_get_data(bd);
+	u8 data[2];
+
+	if (!ctx->prepared) {
+		DRM_WARN("lcd not ready yet for setting its backlight!\n");
+		return -ENXIO;
+	}
+
+	if (bd->props.power <= FB_BLANK_NORMAL) {
+		/* Power on the backlight with the requested brightness */
+		data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
+		data[1] = bd->props.brightness;
+		otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
+		/* set Brightness Control & Backlight on */
+		data[1] = 0x24;
+	} else {
+		/* Power off the backlight: set Brightness Control & Bl off */
+		data[1] = 0;
+	}
+
+	/* Update Brightness Control & Backlight */
+	data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
+	otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
+
+	return 0;
+}
+
+static const struct backlight_ops otm8009a_backlight_ops = {
+	.update_status = otm8009a_backlight_update_status,
+};
+
+static int otm8009a_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct otm8009a *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ctx->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpio\n");
+		return PTR_ERR(ctx->reset_gpio);
+	}
+
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	ctx->dev = dev;
+
+	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;
+
+	drm_panel_init(&ctx->panel);
+	ctx->panel.dev = dev;
+	ctx->panel.funcs = &otm8009a_drm_funcs;
+
+	ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
+						&otm8009a_backlight_ops, NULL);
+	if (IS_ERR(ctx->bl_dev)) {
+		dev_err(dev, "failed to register backlight device\n");
+		return PTR_ERR(ctx->bl_dev);
+	}
+
+	ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
+	ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
+	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+	ctx->bl_dev->props.type = BACKLIGHT_RAW;
+
+	drm_panel_add(&ctx->panel);
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
+		drm_panel_remove(&ctx->panel);
+		backlight_device_unregister(ctx->bl_dev);
+		return ret;
+	}
+
+	DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
+		 default_mode.hdisplay, default_mode.vdisplay,
+		 default_mode.vrefresh,
+		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
+
+	return 0;
+}
+
+static int otm8009a_remove(struct mipi_dsi_device *dsi)
+{
+	struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
+
+	mipi_dsi_detach(dsi);
+	drm_panel_remove(&ctx->panel);
+
+	backlight_device_unregister(ctx->bl_dev);
+
+	return 0;
+}
+
+static const struct of_device_id orisetech_otm8009a_of_match[] = {
+	{ .compatible = "orisetech,otm8009a" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
+
+static struct mipi_dsi_driver orisetech_otm8009a_driver = {
+	.probe  = otm8009a_probe,
+	.remove = otm8009a_remove,
+	.driver = {
+		.name = DRV_NAME "_panel",
+		.of_match_table = orisetech_otm8009a_of_match,
+	},
+};
+module_mipi_dsi_driver(orisetech_otm8009a_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* Re: [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver
  2017-07-04 16:30   ` Philippe CORNU
@ 2017-07-05  9:28     ` Andrzej Hajda
  -1 siblings, 0 replies; 20+ messages in thread
From: Andrzej Hajda @ 2017-07-05  9:28 UTC (permalink / raw)
  To: Philippe CORNU, Alexandre Torgue, Thierry Reding, David Airlie,
	Maxime Coquelin, Russell King, Mark Rutland, Rob Herring,
	Arnd Bergmann, Benjamin Gaignard, Yannick Fertre, Archit Taneja
  Cc: devicetree, dri-devel, Fabien Dessenne, Ludovic Barre,
	Mickael Reulier, Vincent Abriou, Gabriel Fernandez,
	linux-arm-kernel

On 04.07.2017 18:30, Philippe CORNU wrote:
> This patch adds Orise Tech otm8009a 3.97" 480x800 TFT LCD
> panel driver (MIPI-DSI video mode). The panel backlight is
> managed through the DSI link. This panel driver is used in
> several STM32 boards.
>
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>  drivers/gpu/drm/panel/Kconfig                    |   9 +
>  drivers/gpu/drm/panel/Makefile                   |   1 +
>  drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 517 +++++++++++++++++++++++
>  3 files changed, 527 insertions(+)
>  create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index d84a031..c1c9291 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -117,4 +117,13 @@ config DRM_PANEL_SITRONIX_ST7789V
>  	  Say Y here if you want to enable support for the Sitronix
>  	  ST7789V controller for 240x320 LCD panels
>  
> +config DRM_PANEL_ORISETECH_OTM8009A
> +	tristate "Orise Tech otm8009a 480p dsi 2dl video mode panel"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	help
> +	  Say Y here if you want to enable support for Orise Tech OTM8009A
> +	  480x800 DSI panel
> +
>  endmenu
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 9f6610d..ac798f3 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>  obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
> +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
> new file mode 100755
> index 0000000..7aefc09
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
> @@ -0,0 +1,517 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2017
> + *
> + * Authors: Philippe Cornu <philippe.cornu@st.com>
> + *          Yannick Fertre <yannick.fertre@st.com>
> + *
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +#include <drm/drmP.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <video/mipi_display.h>
> +
> +#define DRV_NAME "orisetech_otm8009a"
> +
> +#define OTM8009A_BACKLIGHT_DEFAULT	240
> +#define OTM8009A_BACKLIGHT_MAX		255
> +
> +struct otm8009a {
> +	struct device *dev;
> +	struct drm_panel panel;
> +	struct backlight_device *bl_dev;
> +	struct gpio_desc *reset_gpio;
> +	bool prepared;
> +	bool enabled;
> +};
> +
> +static const struct drm_display_mode default_mode = {
> +	.clock = 32729,
> +	.hdisplay = 480,
> +	.hsync_start = 480 + 120,
> +	.hsync_end = 480 + 120 + 63,
> +	.htotal = 480 + 120 + 63 + 120,
> +	.vdisplay = 800,
> +	.vsync_start = 800 + 12,
> +	.vsync_end = 800 + 12 + 12,
> +	.vtotal = 800 + 12 + 12 + 12,
> +	.vrefresh = 50,
> +	.flags = 0,
> +	.width_mm = 52,
> +	.height_mm = 86,
> +};
> +
> +static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct otm8009a, panel);
> +}
> +
> +static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
> +				   size_t len)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +
> +	if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
> +		DRM_WARN("mipi dsi dcs write buffer failed");

New line at the end of string.

I see another way of fighting with error handling, its drawback is that
failure does not prevent further execution.
Since I am lost what error handling pattern is currently accepted in
panels subsystem, I cannot advice if this should be changed.

> +}
> +
> +#define dcs_write_seq(seq...)					\
> +	do {							\
> +		static const u8 d[] = { seq };			\
> +		otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));	\
> +	} while (0)

It is up to your preferences, but I think "do {} while(0)" construct is
kind of workaround and is obsoleted in favor of ({ ... }) statement
expressions[1].
More serious issue is that you are using ctx implicitly, it should be
added as macro argument.

[1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

> +
> +static int otm8009a_init_sequence(struct otm8009a *ctx)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	/*
> +	 * CMD2_ENA1: Enter in Command 2 mode, enable write function of
> +	 * Command 2 & enable parameter shift function.
> +	 * The 3 following sequences allow to enable ORISE command mode.
> +	 */
> +	dcs_write_seq(0xFF, 0x80, 0x09, 0x01);

I think it would be more readable if you use defines for DCS and MCS
(Manufacturer Command Set), for example:
#define MCS_CMD2 0xff
...
    dcs_write_seq(ctx, MCS_CMD2, 0x80, 0x09, 0x01);

> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xFF, 0x80, 0x09);

Since the patterns with address shifting is repeated multiple times
maybe better would be to define helper for it, for example:
#define MCS_ADRSFT 0x00
#define dcs_write_cmd_at(ctx, cmd, addr, seq...) ({\
    dcs_write_seq(ctx, MCS_ADRSFT, addr);\
    dcs_write_seq(ctx, cmd, seq);\
})

And use it this way:
    dcs_write_cmd_at(MCS_CMD2, 0x80, 0x80, 0x09);

> +
> +	/*
> +	 * Starting from here, address shift needs to be set before sending
> +	 * a new command. You can find an example in the next sequence...
> +	 * SD_PCH_CTRL (0xC480) Source Driver Precharge Control (SD_PT=GND)
> +	 */
> +	dcs_write_seq(0x00, 0x80); /* address shift set to 0x80 */
> +	dcs_write_seq(0xC4, 0x30); /* 0xC480 parameter 1 is 0x30 */
> +	mdelay(10);
> +
> +	/* Not documented (0xC48A) */
> +	dcs_write_seq(0x00, 0x8A);
> +	dcs_write_seq(0xC4, 0x40);
> +	mdelay(10);
> +
> +	/* PWR_CTRL4 (0xC5B0) Power Control Setting 4 for DC Voltage */
> +	dcs_write_seq(0x00, 0xB1); /* 178th parameter */
> +	dcs_write_seq(0xC5, 0xA9);
> +
> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
> +	dcs_write_seq(0x00, 0x91); /* 146th parameter */
> +	dcs_write_seq(0xC5, 0x34);
> +
> +	/* P_DRV_M (0xC0B4) Panel Driving Mode */
> +	dcs_write_seq(0x00, 0xB4);
> +	dcs_write_seq(0xC0, 0x50);
> +
> +	/* VCOMDC (0xD900) VCOM Voltage Setting */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xD9, 0x4E);
> +
> +	/* OSC_ADJ (0xC181) Oscillator Adjustment for Idle/Normal mode */
> +	dcs_write_seq(0x00, 0x81);
> +	dcs_write_seq(0xC1, 0x66); /* 65Hz */
> +
> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
> +	dcs_write_seq(0x00, 0xA1);
> +	dcs_write_seq(0xC1, 0x08);
> +
> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
> +	dcs_write_seq(0x00, 0x92); /* 147th parameter */
> +	dcs_write_seq(0xC5, 0x01);
> +	dcs_write_seq(0x00, 0x95); /* 150th parameter */
> +	dcs_write_seq(0xC5, 0x34);
> +	dcs_write_seq(0x00, 0x94); /* 149th parameter */
> +	dcs_write_seq(0xC5, 0x33);
> +
> +	/* GVDD/NGVDD (0xD800) */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xD8, 0x79, 0x79);
> +
> +	/* SD_CTRL (0xC0A2) Source Driver Timing Setting */
> +	dcs_write_seq(0x00, 0xA3); /* 164th parameter */
> +	dcs_write_seq(0xC0, 0x1B);
> +
> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
> +	dcs_write_seq(0x00, 0x82); /* 131st parameter */
> +	dcs_write_seq(0xC5, 0x83);
> +
> +	/* SD_PCH_CTRL (0xC480) Source Driver Precharge Control */
> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
> +	dcs_write_seq(0xC4, 0x83);
> +
> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
> +	dcs_write_seq(0x00, 0xA1);
> +	dcs_write_seq(0xC1, 0x0E); /* todo previously we wrote 0x08... */
> +
> +	/* PANSET (0xB3A6) Panel Type Setting */
> +	dcs_write_seq(0x00, 0xA6);
> +	dcs_write_seq(0xB3, 0x00, 0x01);
> +
> +	/* GOAVST (0xCE80) GOA VST Setting */
> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
> +
> +	/* GOACLKA1 (0xCEA0) GOA CLKA1 Setting */
> +	dcs_write_seq(0x00, 0xA0);
> +	dcs_write_seq(0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18,
> +		      0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
> +
> +	/* GOACLKA3 (0xCEB0) GOA CLKA3 Setting */
> +	dcs_write_seq(0x00, 0xB0);
> +	dcs_write_seq(0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18,
> +		      0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
> +
> +	/* GOAECLK (0xCFC0) GOA ECLK Setting */
> +	dcs_write_seq(0x00, 0xC0);
> +	dcs_write_seq(0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02,
> +		      0x00, 0x00);
> +
> +	/* Not documented */
> +	dcs_write_seq(0x00, 0xD0);
> +	dcs_write_seq(0xCF, 0x00);
> +
> +	/* PANCTRLSET1-8 (0xCB80-0xCBF0) Panel Control Setting 1-8 */
> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0x90);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xA0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xB0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0xC0);
> +	dcs_write_seq(0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xD0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
> +		      0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xE0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0xF0);
> +	dcs_write_seq(0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> +		      0xFF, 0xFF);
> +
> +	/* PANU2D1-3 (0xCC80-0xCCA0) Panel U2D Setting 1-3 */
> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0x90);
> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
> +	dcs_write_seq(0x00, 0xA0);
> +	dcs_write_seq(0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +
> +	/* PAND2U1-3 (0xCCB0-0xCCD0) Panel D2U Setting 1-3 */
> +	dcs_write_seq(0x00, 0xB0);
> +	dcs_write_seq(0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0xC0);
> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
> +	dcs_write_seq(0x00, 0xD0);
> +	dcs_write_seq(0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +
> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
> +	dcs_write_seq(0xC5, 0x66);
> +
> +	/* Not documented */
> +	dcs_write_seq(0x00, 0xB6);
> +	dcs_write_seq(0xF5, 0x06);
> +
> +	/* GMCT2.2P (0xE100) Gamma Correction 2.2+ Setting */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
> +
> +	/* GMCT2.2N (0xE100) Gamma Correction 2.2- Setting */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
> +
> +	/* Exit CMD2 mode */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xFF, 0xFF, 0xFF, 0xFF);
> +
> +	/* OTM8009a NOP */
> +	dcs_write_seq(0x00, 0x00);

It looks more like resetting shift address, not NOP command.

> +
> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for sleep out exit */
> +	mdelay(120);
> +
> +	/* Default portrait 480x800 rgb24 */
> +	dcs_write_seq(MIPI_DCS_SET_ADDRESS_MODE, 0x00);
> +	dcs_write_seq(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xDF);
> +	dcs_write_seq(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1F);
> +	dcs_write_seq(MIPI_DCS_SET_PIXEL_FORMAT, 0x77);

I think some of above DCS commands have corresponding helper functions.

> +
> +	/* Disable CABC feature */
> +	dcs_write_seq(0x55, 0x00);
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret)
> +		return ret;
> +
> +	/* OTM8009a NOP */
> +	dcs_write_seq(0x00, 0x00);
> +
> +	/* Send Command GRAM memory write (no parameters) */
> +	dcs_write_seq(MIPI_DCS_WRITE_MEMORY_START);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_disable(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	if (!ctx->enabled)
> +		return 0; /* This is not an issue so we return 0 here */
> +
> +	/* Power off the backlight. Note: end-user still controls brightness */
> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +	ret = backlight_update_status(ctx->bl_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(120);
> +
> +	ctx->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int otm8009a_unprepare(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +
> +	if (!ctx->prepared)
> +		return 0;
> +
> +	if (ctx->reset_gpio) {
> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> +		msleep(20);
> +	}
> +
> +	ctx->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int otm8009a_prepare(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +	int ret;
> +
> +	if (ctx->prepared)
> +		return 0;
> +
> +	if (ctx->reset_gpio) {
> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> +		msleep(20);
> +		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> +		msleep(20);

Specs says reset gpio is active low, so I guess it should be fixed in
bindings, dts and in driver.

Regards
Andrzej

> +	}
> +
> +	ret = otm8009a_init_sequence(ctx);
> +	if (ret)
> +		return ret;
> +
> +	ctx->prepared = true;
> +
> +	/* Power on the backlight. Note: end-user still controls brightness */
> +	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
> +	backlight_update_status(ctx->bl_dev);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_enable(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +
> +	ctx->enabled = true;
> +
> +	return 0;
> +}
> +
> +static int otm8009a_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		DRM_ERROR("failed to add mode %ux%ux@%u\n",
> +			  default_mode.hdisplay, default_mode.vdisplay,
> +			  default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	panel->connector->display_info.width_mm = mode->width_mm;
> +	panel->connector->display_info.height_mm = mode->height_mm;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs otm8009a_drm_funcs = {
> +	.disable   = otm8009a_disable,
> +	.unprepare = otm8009a_unprepare,
> +	.prepare   = otm8009a_prepare,
> +	.enable    = otm8009a_enable,
> +	.get_modes = otm8009a_get_modes,
> +};
> +
> +/*
> + * DSI-BASED BACKLIGHT
> + */
> +
> +static int otm8009a_backlight_update_status(struct backlight_device *bd)
> +{
> +	struct otm8009a *ctx = bl_get_data(bd);
> +	u8 data[2];
> +
> +	if (!ctx->prepared) {
> +		DRM_WARN("lcd not ready yet for setting its backlight!\n");
> +		return -ENXIO;
> +	}
> +
> +	if (bd->props.power <= FB_BLANK_NORMAL) {
> +		/* Power on the backlight with the requested brightness */
> +		data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
> +		data[1] = bd->props.brightness;
> +		otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
> +		/* set Brightness Control & Backlight on */
> +		data[1] = 0x24;
> +	} else {
> +		/* Power off the backlight: set Brightness Control & Bl off */
> +		data[1] = 0;
> +	}
> +
> +	/* Update Brightness Control & Backlight */
> +	data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
> +	otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
> +
> +	return 0;
> +}
> +
> +static const struct backlight_ops otm8009a_backlight_ops = {
> +	.update_status = otm8009a_backlight_update_status,
> +};
> +
> +static int otm8009a_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct otm8009a *ctx;
> +	int ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpio\n");
> +		return PTR_ERR(ctx->reset_gpio);
> +	}
> +
> +	mipi_dsi_set_drvdata(dsi, ctx);
> +
> +	ctx->dev = dev;
> +
> +	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;
> +
> +	drm_panel_init(&ctx->panel);
> +	ctx->panel.dev = dev;
> +	ctx->panel.funcs = &otm8009a_drm_funcs;
> +
> +	ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
> +						&otm8009a_backlight_ops, NULL);
> +	if (IS_ERR(ctx->bl_dev)) {
> +		dev_err(dev, "failed to register backlight device\n");
> +		return PTR_ERR(ctx->bl_dev);
> +	}
> +
> +	ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
> +	ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +	ctx->bl_dev->props.type = BACKLIGHT_RAW;
> +
> +	drm_panel_add(&ctx->panel);
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0) {
> +		dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
> +		drm_panel_remove(&ctx->panel);
> +		backlight_device_unregister(ctx->bl_dev);
> +		return ret;
> +	}
> +
> +	DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
> +		 default_mode.hdisplay, default_mode.vdisplay,
> +		 default_mode.vrefresh,
> +		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
> +
> +	mipi_dsi_detach(dsi);
> +	drm_panel_remove(&ctx->panel);
> +
> +	backlight_device_unregister(ctx->bl_dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id orisetech_otm8009a_of_match[] = {
> +	{ .compatible = "orisetech,otm8009a" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
> +
> +static struct mipi_dsi_driver orisetech_otm8009a_driver = {
> +	.probe  = otm8009a_probe,
> +	.remove = otm8009a_remove,
> +	.driver = {
> +		.name = DRV_NAME "_panel",
> +		.of_match_table = orisetech_otm8009a_of_match,
> +	},
> +};
> +module_mipi_dsi_driver(orisetech_otm8009a_driver);
> +
> +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
> +MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
> +MODULE_LICENSE("GPL v2");


_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver
@ 2017-07-05  9:28     ` Andrzej Hajda
  0 siblings, 0 replies; 20+ messages in thread
From: Andrzej Hajda @ 2017-07-05  9:28 UTC (permalink / raw)
  To: linux-arm-kernel

On 04.07.2017 18:30, Philippe CORNU wrote:
> This patch adds Orise Tech otm8009a 3.97" 480x800 TFT LCD
> panel driver (MIPI-DSI video mode). The panel backlight is
> managed through the DSI link. This panel driver is used in
> several STM32 boards.
>
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>  drivers/gpu/drm/panel/Kconfig                    |   9 +
>  drivers/gpu/drm/panel/Makefile                   |   1 +
>  drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 517 +++++++++++++++++++++++
>  3 files changed, 527 insertions(+)
>  create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index d84a031..c1c9291 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -117,4 +117,13 @@ config DRM_PANEL_SITRONIX_ST7789V
>  	  Say Y here if you want to enable support for the Sitronix
>  	  ST7789V controller for 240x320 LCD panels
>  
> +config DRM_PANEL_ORISETECH_OTM8009A
> +	tristate "Orise Tech otm8009a 480p dsi 2dl video mode panel"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	help
> +	  Say Y here if you want to enable support for Orise Tech OTM8009A
> +	  480x800 DSI panel
> +
>  endmenu
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 9f6610d..ac798f3 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>  obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
> +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
> new file mode 100755
> index 0000000..7aefc09
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
> @@ -0,0 +1,517 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2017
> + *
> + * Authors: Philippe Cornu <philippe.cornu@st.com>
> + *          Yannick Fertre <yannick.fertre@st.com>
> + *
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +#include <drm/drmP.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <video/mipi_display.h>
> +
> +#define DRV_NAME "orisetech_otm8009a"
> +
> +#define OTM8009A_BACKLIGHT_DEFAULT	240
> +#define OTM8009A_BACKLIGHT_MAX		255
> +
> +struct otm8009a {
> +	struct device *dev;
> +	struct drm_panel panel;
> +	struct backlight_device *bl_dev;
> +	struct gpio_desc *reset_gpio;
> +	bool prepared;
> +	bool enabled;
> +};
> +
> +static const struct drm_display_mode default_mode = {
> +	.clock = 32729,
> +	.hdisplay = 480,
> +	.hsync_start = 480 + 120,
> +	.hsync_end = 480 + 120 + 63,
> +	.htotal = 480 + 120 + 63 + 120,
> +	.vdisplay = 800,
> +	.vsync_start = 800 + 12,
> +	.vsync_end = 800 + 12 + 12,
> +	.vtotal = 800 + 12 + 12 + 12,
> +	.vrefresh = 50,
> +	.flags = 0,
> +	.width_mm = 52,
> +	.height_mm = 86,
> +};
> +
> +static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct otm8009a, panel);
> +}
> +
> +static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
> +				   size_t len)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +
> +	if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
> +		DRM_WARN("mipi dsi dcs write buffer failed");

New line at the end of string.

I see another way of fighting with error handling, its drawback is that
failure does not prevent further execution.
Since I am lost what error handling pattern is currently accepted in
panels subsystem, I cannot advice if this should be changed.

> +}
> +
> +#define dcs_write_seq(seq...)					\
> +	do {							\
> +		static const u8 d[] = { seq };			\
> +		otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));	\
> +	} while (0)

It is up to your preferences, but I think "do {} while(0)" construct is
kind of workaround and is obsoleted in favor of ({ ... }) statement
expressions[1].
More serious issue is that you are using ctx implicitly, it should be
added as macro argument.

[1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

> +
> +static int otm8009a_init_sequence(struct otm8009a *ctx)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	/*
> +	 * CMD2_ENA1: Enter in Command 2 mode, enable write function of
> +	 * Command 2 & enable parameter shift function.
> +	 * The 3 following sequences allow to enable ORISE command mode.
> +	 */
> +	dcs_write_seq(0xFF, 0x80, 0x09, 0x01);

I think it would be more readable if you use defines for DCS and MCS
(Manufacturer Command Set), for example:
#define MCS_CMD2 0xff
...
    dcs_write_seq(ctx, MCS_CMD2, 0x80, 0x09, 0x01);

> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xFF, 0x80, 0x09);

Since the patterns with address shifting is repeated multiple times
maybe better would be to define helper for it, for example:
#define MCS_ADRSFT 0x00
#define dcs_write_cmd_at(ctx, cmd, addr, seq...) ({\
    dcs_write_seq(ctx, MCS_ADRSFT, addr);\
    dcs_write_seq(ctx, cmd, seq);\
})

And use it this way:
    dcs_write_cmd_at(MCS_CMD2, 0x80, 0x80, 0x09);

> +
> +	/*
> +	 * Starting from here, address shift needs to be set before sending
> +	 * a new command. You can find an example in the next sequence...
> +	 * SD_PCH_CTRL (0xC480) Source Driver Precharge Control (SD_PT=GND)
> +	 */
> +	dcs_write_seq(0x00, 0x80); /* address shift set to 0x80 */
> +	dcs_write_seq(0xC4, 0x30); /* 0xC480 parameter 1 is 0x30 */
> +	mdelay(10);
> +
> +	/* Not documented (0xC48A) */
> +	dcs_write_seq(0x00, 0x8A);
> +	dcs_write_seq(0xC4, 0x40);
> +	mdelay(10);
> +
> +	/* PWR_CTRL4 (0xC5B0) Power Control Setting 4 for DC Voltage */
> +	dcs_write_seq(0x00, 0xB1); /* 178th parameter */
> +	dcs_write_seq(0xC5, 0xA9);
> +
> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
> +	dcs_write_seq(0x00, 0x91); /* 146th parameter */
> +	dcs_write_seq(0xC5, 0x34);
> +
> +	/* P_DRV_M (0xC0B4) Panel Driving Mode */
> +	dcs_write_seq(0x00, 0xB4);
> +	dcs_write_seq(0xC0, 0x50);
> +
> +	/* VCOMDC (0xD900) VCOM Voltage Setting */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xD9, 0x4E);
> +
> +	/* OSC_ADJ (0xC181) Oscillator Adjustment for Idle/Normal mode */
> +	dcs_write_seq(0x00, 0x81);
> +	dcs_write_seq(0xC1, 0x66); /* 65Hz */
> +
> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
> +	dcs_write_seq(0x00, 0xA1);
> +	dcs_write_seq(0xC1, 0x08);
> +
> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
> +	dcs_write_seq(0x00, 0x92); /* 147th parameter */
> +	dcs_write_seq(0xC5, 0x01);
> +	dcs_write_seq(0x00, 0x95); /* 150th parameter */
> +	dcs_write_seq(0xC5, 0x34);
> +	dcs_write_seq(0x00, 0x94); /* 149th parameter */
> +	dcs_write_seq(0xC5, 0x33);
> +
> +	/* GVDD/NGVDD (0xD800) */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xD8, 0x79, 0x79);
> +
> +	/* SD_CTRL (0xC0A2) Source Driver Timing Setting */
> +	dcs_write_seq(0x00, 0xA3); /* 164th parameter */
> +	dcs_write_seq(0xC0, 0x1B);
> +
> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
> +	dcs_write_seq(0x00, 0x82); /* 131st parameter */
> +	dcs_write_seq(0xC5, 0x83);
> +
> +	/* SD_PCH_CTRL (0xC480) Source Driver Precharge Control */
> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
> +	dcs_write_seq(0xC4, 0x83);
> +
> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
> +	dcs_write_seq(0x00, 0xA1);
> +	dcs_write_seq(0xC1, 0x0E); /* todo previously we wrote 0x08... */
> +
> +	/* PANSET (0xB3A6) Panel Type Setting */
> +	dcs_write_seq(0x00, 0xA6);
> +	dcs_write_seq(0xB3, 0x00, 0x01);
> +
> +	/* GOAVST (0xCE80) GOA VST Setting */
> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
> +
> +	/* GOACLKA1 (0xCEA0) GOA CLKA1 Setting */
> +	dcs_write_seq(0x00, 0xA0);
> +	dcs_write_seq(0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18,
> +		      0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
> +
> +	/* GOACLKA3 (0xCEB0) GOA CLKA3 Setting */
> +	dcs_write_seq(0x00, 0xB0);
> +	dcs_write_seq(0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18,
> +		      0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
> +
> +	/* GOAECLK (0xCFC0) GOA ECLK Setting */
> +	dcs_write_seq(0x00, 0xC0);
> +	dcs_write_seq(0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02,
> +		      0x00, 0x00);
> +
> +	/* Not documented */
> +	dcs_write_seq(0x00, 0xD0);
> +	dcs_write_seq(0xCF, 0x00);
> +
> +	/* PANCTRLSET1-8 (0xCB80-0xCBF0) Panel Control Setting 1-8 */
> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0x90);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xA0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xB0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0xC0);
> +	dcs_write_seq(0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xD0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
> +		      0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00);
> +	dcs_write_seq(0x00, 0xE0);
> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0xF0);
> +	dcs_write_seq(0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> +		      0xFF, 0xFF);
> +
> +	/* PANU2D1-3 (0xCC80-0xCCA0) Panel U2D Setting 1-3 */
> +	dcs_write_seq(0x00, 0x80);
> +	dcs_write_seq(0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0x90);
> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
> +	dcs_write_seq(0x00, 0xA0);
> +	dcs_write_seq(0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +
> +	/* PAND2U1-3 (0xCCB0-0xCCD0) Panel D2U Setting 1-3 */
> +	dcs_write_seq(0x00, 0xB0);
> +	dcs_write_seq(0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00,
> +		      0x00, 0x00);
> +	dcs_write_seq(0x00, 0xC0);
> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
> +	dcs_write_seq(0x00, 0xD0);
> +	dcs_write_seq(0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +
> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
> +	dcs_write_seq(0xC5, 0x66);
> +
> +	/* Not documented */
> +	dcs_write_seq(0x00, 0xB6);
> +	dcs_write_seq(0xF5, 0x06);
> +
> +	/* GMCT2.2P (0xE100) Gamma Correction 2.2+ Setting */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
> +
> +	/* GMCT2.2N (0xE100) Gamma Correction 2.2- Setting */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
> +
> +	/* Exit CMD2 mode */
> +	dcs_write_seq(0x00, 0x00);
> +	dcs_write_seq(0xFF, 0xFF, 0xFF, 0xFF);
> +
> +	/* OTM8009a NOP */
> +	dcs_write_seq(0x00, 0x00);

It looks more like resetting shift address, not NOP command.

> +
> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for sleep out exit */
> +	mdelay(120);
> +
> +	/* Default portrait 480x800 rgb24 */
> +	dcs_write_seq(MIPI_DCS_SET_ADDRESS_MODE, 0x00);
> +	dcs_write_seq(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xDF);
> +	dcs_write_seq(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1F);
> +	dcs_write_seq(MIPI_DCS_SET_PIXEL_FORMAT, 0x77);

I think some of above DCS commands have corresponding helper functions.

> +
> +	/* Disable CABC feature */
> +	dcs_write_seq(0x55, 0x00);
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret)
> +		return ret;
> +
> +	/* OTM8009a NOP */
> +	dcs_write_seq(0x00, 0x00);
> +
> +	/* Send Command GRAM memory write (no parameters) */
> +	dcs_write_seq(MIPI_DCS_WRITE_MEMORY_START);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_disable(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> +	int ret;
> +
> +	if (!ctx->enabled)
> +		return 0; /* This is not an issue so we return 0 here */
> +
> +	/* Power off the backlight. Note: end-user still controls brightness */
> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +	ret = backlight_update_status(ctx->bl_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(120);
> +
> +	ctx->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int otm8009a_unprepare(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +
> +	if (!ctx->prepared)
> +		return 0;
> +
> +	if (ctx->reset_gpio) {
> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> +		msleep(20);
> +	}
> +
> +	ctx->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int otm8009a_prepare(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +	int ret;
> +
> +	if (ctx->prepared)
> +		return 0;
> +
> +	if (ctx->reset_gpio) {
> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> +		msleep(20);
> +		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> +		msleep(20);

Specs says reset gpio is active low, so I guess it should be fixed in
bindings, dts and in driver.

Regards
Andrzej

> +	}
> +
> +	ret = otm8009a_init_sequence(ctx);
> +	if (ret)
> +		return ret;
> +
> +	ctx->prepared = true;
> +
> +	/* Power on the backlight. Note: end-user still controls brightness */
> +	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
> +	backlight_update_status(ctx->bl_dev);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_enable(struct drm_panel *panel)
> +{
> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
> +
> +	ctx->enabled = true;
> +
> +	return 0;
> +}
> +
> +static int otm8009a_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		DRM_ERROR("failed to add mode %ux%ux@%u\n",
> +			  default_mode.hdisplay, default_mode.vdisplay,
> +			  default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	panel->connector->display_info.width_mm = mode->width_mm;
> +	panel->connector->display_info.height_mm = mode->height_mm;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs otm8009a_drm_funcs = {
> +	.disable   = otm8009a_disable,
> +	.unprepare = otm8009a_unprepare,
> +	.prepare   = otm8009a_prepare,
> +	.enable    = otm8009a_enable,
> +	.get_modes = otm8009a_get_modes,
> +};
> +
> +/*
> + * DSI-BASED BACKLIGHT
> + */
> +
> +static int otm8009a_backlight_update_status(struct backlight_device *bd)
> +{
> +	struct otm8009a *ctx = bl_get_data(bd);
> +	u8 data[2];
> +
> +	if (!ctx->prepared) {
> +		DRM_WARN("lcd not ready yet for setting its backlight!\n");
> +		return -ENXIO;
> +	}
> +
> +	if (bd->props.power <= FB_BLANK_NORMAL) {
> +		/* Power on the backlight with the requested brightness */
> +		data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
> +		data[1] = bd->props.brightness;
> +		otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
> +		/* set Brightness Control & Backlight on */
> +		data[1] = 0x24;
> +	} else {
> +		/* Power off the backlight: set Brightness Control & Bl off */
> +		data[1] = 0;
> +	}
> +
> +	/* Update Brightness Control & Backlight */
> +	data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
> +	otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
> +
> +	return 0;
> +}
> +
> +static const struct backlight_ops otm8009a_backlight_ops = {
> +	.update_status = otm8009a_backlight_update_status,
> +};
> +
> +static int otm8009a_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct otm8009a *ctx;
> +	int ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpio\n");
> +		return PTR_ERR(ctx->reset_gpio);
> +	}
> +
> +	mipi_dsi_set_drvdata(dsi, ctx);
> +
> +	ctx->dev = dev;
> +
> +	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;
> +
> +	drm_panel_init(&ctx->panel);
> +	ctx->panel.dev = dev;
> +	ctx->panel.funcs = &otm8009a_drm_funcs;
> +
> +	ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
> +						&otm8009a_backlight_ops, NULL);
> +	if (IS_ERR(ctx->bl_dev)) {
> +		dev_err(dev, "failed to register backlight device\n");
> +		return PTR_ERR(ctx->bl_dev);
> +	}
> +
> +	ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
> +	ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
> +	ctx->bl_dev->props.type = BACKLIGHT_RAW;
> +
> +	drm_panel_add(&ctx->panel);
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0) {
> +		dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
> +		drm_panel_remove(&ctx->panel);
> +		backlight_device_unregister(ctx->bl_dev);
> +		return ret;
> +	}
> +
> +	DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
> +		 default_mode.hdisplay, default_mode.vdisplay,
> +		 default_mode.vrefresh,
> +		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
> +
> +	return 0;
> +}
> +
> +static int otm8009a_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
> +
> +	mipi_dsi_detach(dsi);
> +	drm_panel_remove(&ctx->panel);
> +
> +	backlight_device_unregister(ctx->bl_dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id orisetech_otm8009a_of_match[] = {
> +	{ .compatible = "orisetech,otm8009a" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
> +
> +static struct mipi_dsi_driver orisetech_otm8009a_driver = {
> +	.probe  = otm8009a_probe,
> +	.remove = otm8009a_remove,
> +	.driver = {
> +		.name = DRV_NAME "_panel",
> +		.of_match_table = orisetech_otm8009a_of_match,
> +	},
> +};
> +module_mipi_dsi_driver(orisetech_otm8009a_driver);
> +
> +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
> +MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
> +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
  2017-07-04 16:30   ` Philippe CORNU
@ 2017-07-06 14:21     ` Philippe CORNU
  -1 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-06 14:21 UTC (permalink / raw)
  To: Alexandre TORGUE, Thierry Reding, David Airlie, Maxime Coquelin,
	Russell King, Mark Rutland, Rob Herring, Arnd Bergmann,
	Benjamin Gaignard, Yannick FERTRE, Archit Taneja
  Cc: linux-arm-kernel, devicetree, dri-devel, Fabien DESSENNE,
	Mickael REULIER, Vincent ABRIOU, Gabriel FERNANDEZ,
	Ludovic BARRE



On 07/04/2017 06:30 PM, Philippe CORNU wrote:
> The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> 
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>   .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
>   1 file changed, 20 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> new file mode 100644
> index 0000000..0bb8237
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> @@ -0,0 +1,20 @@
> +Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
> +
> +The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> +a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> +
> +Required properties:
> +  - compatible: "orisetech,otm8009a"
> +  - reg: the virtual channel number of a DSI peripheral
> +  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)
> +
> +Example:
> +&dsi {
> +	...
> +
> +	panel@0 {
> +		compatible = "orisetech,otm8009a";
> +		reg = <0>;
> +		reset-gpios = <&gpioh 7 0>;

Hi All,
I should have written instead:
  +		reset-gpios = <&gpioh 7 GPIO_ACTIVE_LOW>;

reset-gpios is active low and the define GPIO_ACTIVE_LOW = 1 so the 
example was not good.

I will send soon the v2 with the correction.
Many thanks
Philippe


> +	};
> +};
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
@ 2017-07-06 14:21     ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-06 14:21 UTC (permalink / raw)
  To: linux-arm-kernel



On 07/04/2017 06:30 PM, Philippe CORNU wrote:
> The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> 
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>   .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
>   1 file changed, 20 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> new file mode 100644
> index 0000000..0bb8237
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> @@ -0,0 +1,20 @@
> +Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
> +
> +The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> +a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> +
> +Required properties:
> +  - compatible: "orisetech,otm8009a"
> +  - reg: the virtual channel number of a DSI peripheral
> +  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)
> +
> +Example:
> +&dsi {
> +	...
> +
> +	panel at 0 {
> +		compatible = "orisetech,otm8009a";
> +		reg = <0>;
> +		reset-gpios = <&gpioh 7 0>;

Hi All,
I should have written instead:
  +		reset-gpios = <&gpioh 7 GPIO_ACTIVE_LOW>;

reset-gpios is active low and the define GPIO_ACTIVE_LOW = 1 so the 
example was not good.

I will send soon the v2 with the correction.
Many thanks
Philippe


> +	};
> +};
> 

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

* Re: [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver
  2017-07-05  9:28     ` Andrzej Hajda
@ 2017-07-06 14:44       ` Philippe CORNU
  -1 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-06 14:44 UTC (permalink / raw)
  To: Andrzej Hajda, Alexandre TORGUE, Thierry Reding, David Airlie,
	Maxime Coquelin, Russell King, Mark Rutland, Rob Herring,
	Arnd Bergmann, Benjamin Gaignard, Yannick FERTRE, Archit Taneja
  Cc: devicetree, dri-devel, Fabien DESSENNE, Ludovic BARRE,
	Mickael REULIER, Vincent ABRIOU, Gabriel FERNANDEZ,
	linux-arm-kernel



On 07/05/2017 11:28 AM, Andrzej Hajda wrote:
> On 04.07.2017 18:30, Philippe CORNU wrote:
>> This patch adds Orise Tech otm8009a 3.97" 480x800 TFT LCD
>> panel driver (MIPI-DSI video mode). The panel backlight is
>> managed through the DSI link. This panel driver is used in
>> several STM32 boards.
>>
>> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
>> ---
>>   drivers/gpu/drm/panel/Kconfig                    |   9 +
>>   drivers/gpu/drm/panel/Makefile                   |   1 +
>>   drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 517 +++++++++++++++++++++++
>>   3 files changed, 527 insertions(+)
>>   create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index d84a031..c1c9291 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -117,4 +117,13 @@ config DRM_PANEL_SITRONIX_ST7789V
>>   	  Say Y here if you want to enable support for the Sitronix
>>   	  ST7789V controller for 240x320 LCD panels
>>   
>> +config DRM_PANEL_ORISETECH_OTM8009A
>> +	tristate "Orise Tech otm8009a 480p dsi 2dl video mode panel"
>> +	depends on OF
>> +	depends on DRM_MIPI_DSI
>> +	depends on BACKLIGHT_CLASS_DEVICE
>> +	help
>> +	  Say Y here if you want to enable support for Orise Tech OTM8009A
>> +	  480x800 DSI panel
>> +
>>   endmenu
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index 9f6610d..ac798f3 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>>   obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
>> +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
>> \ No newline at end of file
>> diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>> new file mode 100755
>> index 0000000..7aefc09
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>> @@ -0,0 +1,517 @@
>> +/*
>> + * Copyright (C) STMicroelectronics SA 2017
>> + *
>> + * Authors: Philippe Cornu <philippe.cornu@st.com>
>> + *          Yannick Fertre <yannick.fertre@st.com>
>> + *
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +#include <drm/drmP.h>
>> +#include <drm/drm_mipi_dsi.h>
>> +#include <drm/drm_panel.h>
>> +#include <linux/backlight.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <video/mipi_display.h>
>> +
>> +#define DRV_NAME "orisetech_otm8009a"
>> +
>> +#define OTM8009A_BACKLIGHT_DEFAULT	240
>> +#define OTM8009A_BACKLIGHT_MAX		255
>> +
>> +struct otm8009a {
>> +	struct device *dev;
>> +	struct drm_panel panel;
>> +	struct backlight_device *bl_dev;
>> +	struct gpio_desc *reset_gpio;
>> +	bool prepared;
>> +	bool enabled;
>> +};
>> +
>> +static const struct drm_display_mode default_mode = {
>> +	.clock = 32729,
>> +	.hdisplay = 480,
>> +	.hsync_start = 480 + 120,
>> +	.hsync_end = 480 + 120 + 63,
>> +	.htotal = 480 + 120 + 63 + 120,
>> +	.vdisplay = 800,
>> +	.vsync_start = 800 + 12,
>> +	.vsync_end = 800 + 12 + 12,
>> +	.vtotal = 800 + 12 + 12 + 12,
>> +	.vrefresh = 50,
>> +	.flags = 0,
>> +	.width_mm = 52,
>> +	.height_mm = 86,
>> +};
>> +
>> +static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
>> +{
>> +	return container_of(panel, struct otm8009a, panel);
>> +}
>> +
>> +static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
>> +				   size_t len)
>> +{
>> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +
>> +	if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
>> +		DRM_WARN("mipi dsi dcs write buffer failed");
> 
> New line at the end of string.
> 
> I see another way of fighting with error handling, its drawback is that
> failure does not prevent further execution.
> Since I am lost what error handling pattern is currently accepted in
> panels subsystem, I cannot advice if this should be changed.
> 

Hi Andrzef,

First of all, a big *THANK YOU* for this nice code review. Your comments 
are very relevant :-)

Regarding the error management, there are actually several solutions to 
deal with errors but I confess that for the moment none really convinced 
me... that is why I proposed here the *minimal solution of the trace*...

>> +}
>> +
>> +#define dcs_write_seq(seq...)					\
>> +	do {							\
>> +		static const u8 d[] = { seq };			\
>> +		otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));	\
>> +	} while (0)
> 
> It is up to your preferences, but I think "do {} while(0)" construct is
> kind of workaround and is obsoleted in favor of ({ ... }) statement
> expressions[1].
> More serious issue is that you are using ctx implicitly, it should be
> added as macro argument.
> 
> [1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
> 

I will follow your proposal, thank you so much for these information.

>> +
>> +static int otm8009a_init_sequence(struct otm8009a *ctx)
>> +{
>> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +	int ret;
>> +
>> +	/*
>> +	 * CMD2_ENA1: Enter in Command 2 mode, enable write function of
>> +	 * Command 2 & enable parameter shift function.
>> +	 * The 3 following sequences allow to enable ORISE command mode.
>> +	 */
>> +	dcs_write_seq(0xFF, 0x80, 0x09, 0x01);
> 
> I think it would be more readable if you use defines for DCS and MCS
> (Manufacturer Command Set), for example:
> #define MCS_CMD2 0xff
> ...
>      dcs_write_seq(ctx, MCS_CMD2, 0x80, 0x09, 0x01);
> 
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xFF, 0x80, 0x09);
> 
> Since the patterns with address shifting is repeated multiple times
> maybe better would be to define helper for it, for example:
> #define MCS_ADRSFT 0x00
> #define dcs_write_cmd_at(ctx, cmd, addr, seq...) ({\
>      dcs_write_seq(ctx, MCS_ADRSFT, addr);\
>      dcs_write_seq(ctx, cmd, seq);\
> })
> 
> And use it this way:
>      dcs_write_cmd_at(MCS_CMD2, 0x80, 0x80, 0x09);
> 

Very good & clever proposals, I will implement them in v2. Many thanks

>> +
>> +	/*
>> +	 * Starting from here, address shift needs to be set before sending
>> +	 * a new command. You can find an example in the next sequence...
>> +	 * SD_PCH_CTRL (0xC480) Source Driver Precharge Control (SD_PT=GND)
>> +	 */
>> +	dcs_write_seq(0x00, 0x80); /* address shift set to 0x80 */
>> +	dcs_write_seq(0xC4, 0x30); /* 0xC480 parameter 1 is 0x30 */
>> +	mdelay(10);
>> +
>> +	/* Not documented (0xC48A) */
>> +	dcs_write_seq(0x00, 0x8A);
>> +	dcs_write_seq(0xC4, 0x40);
>> +	mdelay(10);
>> +
>> +	/* PWR_CTRL4 (0xC5B0) Power Control Setting 4 for DC Voltage */
>> +	dcs_write_seq(0x00, 0xB1); /* 178th parameter */
>> +	dcs_write_seq(0xC5, 0xA9);
>> +
>> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
>> +	dcs_write_seq(0x00, 0x91); /* 146th parameter */
>> +	dcs_write_seq(0xC5, 0x34);
>> +
>> +	/* P_DRV_M (0xC0B4) Panel Driving Mode */
>> +	dcs_write_seq(0x00, 0xB4);
>> +	dcs_write_seq(0xC0, 0x50);
>> +
>> +	/* VCOMDC (0xD900) VCOM Voltage Setting */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xD9, 0x4E);
>> +
>> +	/* OSC_ADJ (0xC181) Oscillator Adjustment for Idle/Normal mode */
>> +	dcs_write_seq(0x00, 0x81);
>> +	dcs_write_seq(0xC1, 0x66); /* 65Hz */
>> +
>> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
>> +	dcs_write_seq(0x00, 0xA1);
>> +	dcs_write_seq(0xC1, 0x08);
>> +
>> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
>> +	dcs_write_seq(0x00, 0x92); /* 147th parameter */
>> +	dcs_write_seq(0xC5, 0x01);
>> +	dcs_write_seq(0x00, 0x95); /* 150th parameter */
>> +	dcs_write_seq(0xC5, 0x34);
>> +	dcs_write_seq(0x00, 0x94); /* 149th parameter */
>> +	dcs_write_seq(0xC5, 0x33);
>> +
>> +	/* GVDD/NGVDD (0xD800) */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xD8, 0x79, 0x79);
>> +
>> +	/* SD_CTRL (0xC0A2) Source Driver Timing Setting */
>> +	dcs_write_seq(0x00, 0xA3); /* 164th parameter */
>> +	dcs_write_seq(0xC0, 0x1B);
>> +
>> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
>> +	dcs_write_seq(0x00, 0x82); /* 131st parameter */
>> +	dcs_write_seq(0xC5, 0x83);
>> +
>> +	/* SD_PCH_CTRL (0xC480) Source Driver Precharge Control */
>> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
>> +	dcs_write_seq(0xC4, 0x83);
>> +
>> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
>> +	dcs_write_seq(0x00, 0xA1);
>> +	dcs_write_seq(0xC1, 0x0E); /* todo previously we wrote 0x08... */
>> +
>> +	/* PANSET (0xB3A6) Panel Type Setting */
>> +	dcs_write_seq(0x00, 0xA6);
>> +	dcs_write_seq(0xB3, 0x00, 0x01);
>> +
>> +	/* GOAVST (0xCE80) GOA VST Setting */
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
>> +
>> +	/* GOACLKA1 (0xCEA0) GOA CLKA1 Setting */
>> +	dcs_write_seq(0x00, 0xA0);
>> +	dcs_write_seq(0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18,
>> +		      0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
>> +
>> +	/* GOACLKA3 (0xCEB0) GOA CLKA3 Setting */
>> +	dcs_write_seq(0x00, 0xB0);
>> +	dcs_write_seq(0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18,
>> +		      0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
>> +
>> +	/* GOAECLK (0xCFC0) GOA ECLK Setting */
>> +	dcs_write_seq(0x00, 0xC0);
>> +	dcs_write_seq(0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02,
>> +		      0x00, 0x00);
>> +
>> +	/* Not documented */
>> +	dcs_write_seq(0x00, 0xD0);
>> +	dcs_write_seq(0xCF, 0x00);
>> +
>> +	/* PANCTRLSET1-8 (0xCB80-0xCBF0) Panel Control Setting 1-8 */
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0x90);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xA0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xB0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xC0);
>> +	dcs_write_seq(0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xD0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
>> +		      0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xE0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xF0);
>> +	dcs_write_seq(0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
>> +		      0xFF, 0xFF);
>> +
>> +	/* PANU2D1-3 (0xCC80-0xCCA0) Panel U2D Setting 1-3 */
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0x90);
>> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
>> +	dcs_write_seq(0x00, 0xA0);
>> +	dcs_write_seq(0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +
>> +	/* PAND2U1-3 (0xCCB0-0xCCD0) Panel D2U Setting 1-3 */
>> +	dcs_write_seq(0x00, 0xB0);
>> +	dcs_write_seq(0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xC0);
>> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
>> +	dcs_write_seq(0x00, 0xD0);
>> +	dcs_write_seq(0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +
>> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
>> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
>> +	dcs_write_seq(0xC5, 0x66);
>> +
>> +	/* Not documented */
>> +	dcs_write_seq(0x00, 0xB6);
>> +	dcs_write_seq(0xF5, 0x06);
>> +
>> +	/* GMCT2.2P (0xE100) Gamma Correction 2.2+ Setting */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
>> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
>> +
>> +	/* GMCT2.2N (0xE100) Gamma Correction 2.2- Setting */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
>> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
>> +
>> +	/* Exit CMD2 mode */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xFF, 0xFF, 0xFF, 0xFF);
>> +
>> +	/* OTM8009a NOP */
>> +	dcs_write_seq(0x00, 0x00);
> 
> It looks more like resetting shift address, not NOP command.
> 

Yes, you are right, bad copy/paste, I will update it.

>> +
>> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Wait for sleep out exit */
>> +	mdelay(120);
>> +
>> +	/* Default portrait 480x800 rgb24 */
>> +	dcs_write_seq(MIPI_DCS_SET_ADDRESS_MODE, 0x00);
>> +	dcs_write_seq(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xDF);
>> +	dcs_write_seq(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1F);
>> +	dcs_write_seq(MIPI_DCS_SET_PIXEL_FORMAT, 0x77);
> 
> I think some of above DCS commands have corresponding helper functions.
> 

Yes, you are right, lazy I am :), I will update it. Many thanks.

>> +
>> +	/* Disable CABC feature */
>> +	dcs_write_seq(0x55, 0x00);
>> +
>> +	ret = mipi_dsi_dcs_set_display_on(dsi);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* OTM8009a NOP */
>> +	dcs_write_seq(0x00, 0x00);
>> +
>> +	/* Send Command GRAM memory write (no parameters) */
>> +	dcs_write_seq(MIPI_DCS_WRITE_MEMORY_START);
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_disable(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +	int ret;
>> +
>> +	if (!ctx->enabled)
>> +		return 0; /* This is not an issue so we return 0 here */
>> +
>> +	/* Power off the backlight. Note: end-user still controls brightness */
>> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>> +	ret = backlight_update_status(ctx->bl_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = mipi_dsi_dcs_set_display_off(dsi);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	msleep(120);
>> +
>> +	ctx->enabled = false;
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_unprepare(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +
>> +	if (!ctx->prepared)
>> +		return 0;
>> +
>> +	if (ctx->reset_gpio) {
>> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> +		msleep(20);
>> +	}
>> +
>> +	ctx->prepared = false;
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_prepare(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +	int ret;
>> +
>> +	if (ctx->prepared)
>> +		return 0;
>> +
>> +	if (ctx->reset_gpio) {
>> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> +		msleep(20);
>> +		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> +		msleep(20);
> 
> Specs says reset gpio is active low, so I guess it should be fixed in
> bindings, dts and in driver.
> 
> Regards
> Andrzej
> 

100% correct! I am fixing it, many thanks for your review.
Best regards,
Philippe

>> +	}
>> +
>> +	ret = otm8009a_init_sequence(ctx);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ctx->prepared = true;
>> +
>> +	/* Power on the backlight. Note: end-user still controls brightness */
>> +	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
>> +	backlight_update_status(ctx->bl_dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_enable(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +
>> +	ctx->enabled = true;
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_get_modes(struct drm_panel *panel)
>> +{
>> +	struct drm_display_mode *mode;
>> +
>> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
>> +	if (!mode) {
>> +		DRM_ERROR("failed to add mode %ux%ux@%u\n",
>> +			  default_mode.hdisplay, default_mode.vdisplay,
>> +			  default_mode.vrefresh);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	drm_mode_set_name(mode);
>> +
>> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
>> +	drm_mode_probed_add(panel->connector, mode);
>> +
>> +	panel->connector->display_info.width_mm = mode->width_mm;
>> +	panel->connector->display_info.height_mm = mode->height_mm;
>> +
>> +	return 1;
>> +}
>> +
>> +static const struct drm_panel_funcs otm8009a_drm_funcs = {
>> +	.disable   = otm8009a_disable,
>> +	.unprepare = otm8009a_unprepare,
>> +	.prepare   = otm8009a_prepare,
>> +	.enable    = otm8009a_enable,
>> +	.get_modes = otm8009a_get_modes,
>> +};
>> +
>> +/*
>> + * DSI-BASED BACKLIGHT
>> + */
>> +
>> +static int otm8009a_backlight_update_status(struct backlight_device *bd)
>> +{
>> +	struct otm8009a *ctx = bl_get_data(bd);
>> +	u8 data[2];
>> +
>> +	if (!ctx->prepared) {
>> +		DRM_WARN("lcd not ready yet for setting its backlight!\n");
>> +		return -ENXIO;
>> +	}
>> +
>> +	if (bd->props.power <= FB_BLANK_NORMAL) {
>> +		/* Power on the backlight with the requested brightness */
>> +		data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
>> +		data[1] = bd->props.brightness;
>> +		otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
>> +		/* set Brightness Control & Backlight on */
>> +		data[1] = 0x24;
>> +	} else {
>> +		/* Power off the backlight: set Brightness Control & Bl off */
>> +		data[1] = 0;
>> +	}
>> +
>> +	/* Update Brightness Control & Backlight */
>> +	data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
>> +	otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct backlight_ops otm8009a_backlight_ops = {
>> +	.update_status = otm8009a_backlight_update_status,
>> +};
>> +
>> +static int otm8009a_probe(struct mipi_dsi_device *dsi)
>> +{
>> +	struct device *dev = &dsi->dev;
>> +	struct otm8009a *ctx;
>> +	int ret;
>> +
>> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> +	if (!ctx)
>> +		return -ENOMEM;
>> +
>> +	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(ctx->reset_gpio)) {
>> +		dev_err(dev, "cannot get reset-gpio\n");
>> +		return PTR_ERR(ctx->reset_gpio);
>> +	}
>> +
>> +	mipi_dsi_set_drvdata(dsi, ctx);
>> +
>> +	ctx->dev = dev;
>> +
>> +	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;
>> +
>> +	drm_panel_init(&ctx->panel);
>> +	ctx->panel.dev = dev;
>> +	ctx->panel.funcs = &otm8009a_drm_funcs;
>> +
>> +	ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
>> +						&otm8009a_backlight_ops, NULL);
>> +	if (IS_ERR(ctx->bl_dev)) {
>> +		dev_err(dev, "failed to register backlight device\n");
>> +		return PTR_ERR(ctx->bl_dev);
>> +	}
>> +
>> +	ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
>> +	ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
>> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>> +	ctx->bl_dev->props.type = BACKLIGHT_RAW;
>> +
>> +	drm_panel_add(&ctx->panel);
>> +
>> +	ret = mipi_dsi_attach(dsi);
>> +	if (ret < 0) {
>> +		dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
>> +		drm_panel_remove(&ctx->panel);
>> +		backlight_device_unregister(ctx->bl_dev);
>> +		return ret;
>> +	}
>> +
>> +	DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
>> +		 default_mode.hdisplay, default_mode.vdisplay,
>> +		 default_mode.vrefresh,
>> +		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_remove(struct mipi_dsi_device *dsi)
>> +{
>> +	struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
>> +
>> +	mipi_dsi_detach(dsi);
>> +	drm_panel_remove(&ctx->panel);
>> +
>> +	backlight_device_unregister(ctx->bl_dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id orisetech_otm8009a_of_match[] = {
>> +	{ .compatible = "orisetech,otm8009a" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
>> +
>> +static struct mipi_dsi_driver orisetech_otm8009a_driver = {
>> +	.probe  = otm8009a_probe,
>> +	.remove = otm8009a_remove,
>> +	.driver = {
>> +		.name = DRV_NAME "_panel",
>> +		.of_match_table = orisetech_otm8009a_of_match,
>> +	},
>> +};
>> +module_mipi_dsi_driver(orisetech_otm8009a_driver);
>> +
>> +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
>> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
>> +MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
>> +MODULE_LICENSE("GPL v2");
> 
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver
@ 2017-07-06 14:44       ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-06 14:44 UTC (permalink / raw)
  To: linux-arm-kernel



On 07/05/2017 11:28 AM, Andrzej Hajda wrote:
> On 04.07.2017 18:30, Philippe CORNU wrote:
>> This patch adds Orise Tech otm8009a 3.97" 480x800 TFT LCD
>> panel driver (MIPI-DSI video mode). The panel backlight is
>> managed through the DSI link. This panel driver is used in
>> several STM32 boards.
>>
>> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
>> ---
>>   drivers/gpu/drm/panel/Kconfig                    |   9 +
>>   drivers/gpu/drm/panel/Makefile                   |   1 +
>>   drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 517 +++++++++++++++++++++++
>>   3 files changed, 527 insertions(+)
>>   create mode 100755 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index d84a031..c1c9291 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -117,4 +117,13 @@ config DRM_PANEL_SITRONIX_ST7789V
>>   	  Say Y here if you want to enable support for the Sitronix
>>   	  ST7789V controller for 240x320 LCD panels
>>   
>> +config DRM_PANEL_ORISETECH_OTM8009A
>> +	tristate "Orise Tech otm8009a 480p dsi 2dl video mode panel"
>> +	depends on OF
>> +	depends on DRM_MIPI_DSI
>> +	depends on BACKLIGHT_CLASS_DEVICE
>> +	help
>> +	  Say Y here if you want to enable support for Orise Tech OTM8009A
>> +	  480x800 DSI panel
>> +
>>   endmenu
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index 9f6610d..ac798f3 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>>   obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
>> +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
>> \ No newline at end of file
>> diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>> new file mode 100755
>> index 0000000..7aefc09
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
>> @@ -0,0 +1,517 @@
>> +/*
>> + * Copyright (C) STMicroelectronics SA 2017
>> + *
>> + * Authors: Philippe Cornu <philippe.cornu@st.com>
>> + *          Yannick Fertre <yannick.fertre@st.com>
>> + *
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +#include <drm/drmP.h>
>> +#include <drm/drm_mipi_dsi.h>
>> +#include <drm/drm_panel.h>
>> +#include <linux/backlight.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <video/mipi_display.h>
>> +
>> +#define DRV_NAME "orisetech_otm8009a"
>> +
>> +#define OTM8009A_BACKLIGHT_DEFAULT	240
>> +#define OTM8009A_BACKLIGHT_MAX		255
>> +
>> +struct otm8009a {
>> +	struct device *dev;
>> +	struct drm_panel panel;
>> +	struct backlight_device *bl_dev;
>> +	struct gpio_desc *reset_gpio;
>> +	bool prepared;
>> +	bool enabled;
>> +};
>> +
>> +static const struct drm_display_mode default_mode = {
>> +	.clock = 32729,
>> +	.hdisplay = 480,
>> +	.hsync_start = 480 + 120,
>> +	.hsync_end = 480 + 120 + 63,
>> +	.htotal = 480 + 120 + 63 + 120,
>> +	.vdisplay = 800,
>> +	.vsync_start = 800 + 12,
>> +	.vsync_end = 800 + 12 + 12,
>> +	.vtotal = 800 + 12 + 12 + 12,
>> +	.vrefresh = 50,
>> +	.flags = 0,
>> +	.width_mm = 52,
>> +	.height_mm = 86,
>> +};
>> +
>> +static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
>> +{
>> +	return container_of(panel, struct otm8009a, panel);
>> +}
>> +
>> +static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
>> +				   size_t len)
>> +{
>> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +
>> +	if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
>> +		DRM_WARN("mipi dsi dcs write buffer failed");
> 
> New line at the end of string.
> 
> I see another way of fighting with error handling, its drawback is that
> failure does not prevent further execution.
> Since I am lost what error handling pattern is currently accepted in
> panels subsystem, I cannot advice if this should be changed.
> 

Hi Andrzef,

First of all, a big *THANK YOU* for this nice code review. Your comments 
are very relevant :-)

Regarding the error management, there are actually several solutions to 
deal with errors but I confess that for the moment none really convinced 
me... that is why I proposed here the *minimal solution of the trace*...

>> +}
>> +
>> +#define dcs_write_seq(seq...)					\
>> +	do {							\
>> +		static const u8 d[] = { seq };			\
>> +		otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));	\
>> +	} while (0)
> 
> It is up to your preferences, but I think "do {} while(0)" construct is
> kind of workaround and is obsoleted in favor of ({ ... }) statement
> expressions[1].
> More serious issue is that you are using ctx implicitly, it should be
> added as macro argument.
> 
> [1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
> 

I will follow your proposal, thank you so much for these information.

>> +
>> +static int otm8009a_init_sequence(struct otm8009a *ctx)
>> +{
>> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +	int ret;
>> +
>> +	/*
>> +	 * CMD2_ENA1: Enter in Command 2 mode, enable write function of
>> +	 * Command 2 & enable parameter shift function.
>> +	 * The 3 following sequences allow to enable ORISE command mode.
>> +	 */
>> +	dcs_write_seq(0xFF, 0x80, 0x09, 0x01);
> 
> I think it would be more readable if you use defines for DCS and MCS
> (Manufacturer Command Set), for example:
> #define MCS_CMD2 0xff
> ...
>      dcs_write_seq(ctx, MCS_CMD2, 0x80, 0x09, 0x01);
> 
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xFF, 0x80, 0x09);
> 
> Since the patterns with address shifting is repeated multiple times
> maybe better would be to define helper for it, for example:
> #define MCS_ADRSFT 0x00
> #define dcs_write_cmd_at(ctx, cmd, addr, seq...) ({\
>      dcs_write_seq(ctx, MCS_ADRSFT, addr);\
>      dcs_write_seq(ctx, cmd, seq);\
> })
> 
> And use it this way:
>      dcs_write_cmd_at(MCS_CMD2, 0x80, 0x80, 0x09);
> 

Very good & clever proposals, I will implement them in v2. Many thanks

>> +
>> +	/*
>> +	 * Starting from here, address shift needs to be set before sending
>> +	 * a new command. You can find an example in the next sequence...
>> +	 * SD_PCH_CTRL (0xC480) Source Driver Precharge Control (SD_PT=GND)
>> +	 */
>> +	dcs_write_seq(0x00, 0x80); /* address shift set to 0x80 */
>> +	dcs_write_seq(0xC4, 0x30); /* 0xC480 parameter 1 is 0x30 */
>> +	mdelay(10);
>> +
>> +	/* Not documented (0xC48A) */
>> +	dcs_write_seq(0x00, 0x8A);
>> +	dcs_write_seq(0xC4, 0x40);
>> +	mdelay(10);
>> +
>> +	/* PWR_CTRL4 (0xC5B0) Power Control Setting 4 for DC Voltage */
>> +	dcs_write_seq(0x00, 0xB1); /* 178th parameter */
>> +	dcs_write_seq(0xC5, 0xA9);
>> +
>> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
>> +	dcs_write_seq(0x00, 0x91); /* 146th parameter */
>> +	dcs_write_seq(0xC5, 0x34);
>> +
>> +	/* P_DRV_M (0xC0B4) Panel Driving Mode */
>> +	dcs_write_seq(0x00, 0xB4);
>> +	dcs_write_seq(0xC0, 0x50);
>> +
>> +	/* VCOMDC (0xD900) VCOM Voltage Setting */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xD9, 0x4E);
>> +
>> +	/* OSC_ADJ (0xC181) Oscillator Adjustment for Idle/Normal mode */
>> +	dcs_write_seq(0x00, 0x81);
>> +	dcs_write_seq(0xC1, 0x66); /* 65Hz */
>> +
>> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
>> +	dcs_write_seq(0x00, 0xA1);
>> +	dcs_write_seq(0xC1, 0x08);
>> +
>> +	/* PWR_CTRL2 (0xC590) Power Control Setting 2 for Normal Mode */
>> +	dcs_write_seq(0x00, 0x92); /* 147th parameter */
>> +	dcs_write_seq(0xC5, 0x01);
>> +	dcs_write_seq(0x00, 0x95); /* 150th parameter */
>> +	dcs_write_seq(0xC5, 0x34);
>> +	dcs_write_seq(0x00, 0x94); /* 149th parameter */
>> +	dcs_write_seq(0xC5, 0x33);
>> +
>> +	/* GVDD/NGVDD (0xD800) */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xD8, 0x79, 0x79);
>> +
>> +	/* SD_CTRL (0xC0A2) Source Driver Timing Setting */
>> +	dcs_write_seq(0x00, 0xA3); /* 164th parameter */
>> +	dcs_write_seq(0xC0, 0x1B);
>> +
>> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
>> +	dcs_write_seq(0x00, 0x82); /* 131st parameter */
>> +	dcs_write_seq(0xC5, 0x83);
>> +
>> +	/* SD_PCH_CTRL (0xC480) Source Driver Precharge Control */
>> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
>> +	dcs_write_seq(0xC4, 0x83);
>> +
>> +	/* RGB_VIDEO_SET (0xC1A1) RGB Video Mode Setting */
>> +	dcs_write_seq(0x00, 0xA1);
>> +	dcs_write_seq(0xC1, 0x0E); /* todo previously we wrote 0x08... */
>> +
>> +	/* PANSET (0xB3A6) Panel Type Setting */
>> +	dcs_write_seq(0x00, 0xA6);
>> +	dcs_write_seq(0xB3, 0x00, 0x01);
>> +
>> +	/* GOAVST (0xCE80) GOA VST Setting */
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xCE, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
>> +
>> +	/* GOACLKA1 (0xCEA0) GOA CLKA1 Setting */
>> +	dcs_write_seq(0x00, 0xA0);
>> +	dcs_write_seq(0xCE, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00, 0x18,
>> +		      0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
>> +
>> +	/* GOACLKA3 (0xCEB0) GOA CLKA3 Setting */
>> +	dcs_write_seq(0x00, 0xB0);
>> +	dcs_write_seq(0xCE, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00, 0x18,
>> +		      0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
>> +
>> +	/* GOAECLK (0xCFC0) GOA ECLK Setting */
>> +	dcs_write_seq(0x00, 0xC0);
>> +	dcs_write_seq(0xCF, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02,
>> +		      0x00, 0x00);
>> +
>> +	/* Not documented */
>> +	dcs_write_seq(0x00, 0xD0);
>> +	dcs_write_seq(0xCF, 0x00);
>> +
>> +	/* PANCTRLSET1-8 (0xCB80-0xCBF0) Panel Control Setting 1-8 */
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0x90);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xA0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xB0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xC0);
>> +	dcs_write_seq(0xCB, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xD0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
>> +		      0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xE0);
>> +	dcs_write_seq(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xF0);
>> +	dcs_write_seq(0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
>> +		      0xFF, 0xFF);
>> +
>> +	/* PANU2D1-3 (0xCC80-0xCCA0) Panel U2D Setting 1-3 */
>> +	dcs_write_seq(0x00, 0x80);
>> +	dcs_write_seq(0xCC, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0x90);
>> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
>> +	dcs_write_seq(0x00, 0xA0);
>> +	dcs_write_seq(0xCC, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +
>> +	/* PAND2U1-3 (0xCCB0-0xCCD0) Panel D2U Setting 1-3 */
>> +	dcs_write_seq(0x00, 0xB0);
>> +	dcs_write_seq(0xCC, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00,
>> +		      0x00, 0x00);
>> +	dcs_write_seq(0x00, 0xC0);
>> +	dcs_write_seq(0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
>> +	dcs_write_seq(0x00, 0xD0);
>> +	dcs_write_seq(0xCC, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
>> +
>> +	/* PWR_CTRL1 (0xC580) Power Control Setting 1 */
>> +	dcs_write_seq(0x00, 0x81); /* 130th parameter */
>> +	dcs_write_seq(0xC5, 0x66);
>> +
>> +	/* Not documented */
>> +	dcs_write_seq(0x00, 0xB6);
>> +	dcs_write_seq(0xF5, 0x06);
>> +
>> +	/* GMCT2.2P (0xE100) Gamma Correction 2.2+ Setting */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xE1, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
>> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
>> +
>> +	/* GMCT2.2N (0xE100) Gamma Correction 2.2- Setting */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xE2, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A,
>> +		      0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 0x01);
>> +
>> +	/* Exit CMD2 mode */
>> +	dcs_write_seq(0x00, 0x00);
>> +	dcs_write_seq(0xFF, 0xFF, 0xFF, 0xFF);
>> +
>> +	/* OTM8009a NOP */
>> +	dcs_write_seq(0x00, 0x00);
> 
> It looks more like resetting shift address, not NOP command.
> 

Yes, you are right, bad copy/paste, I will update it.

>> +
>> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Wait for sleep out exit */
>> +	mdelay(120);
>> +
>> +	/* Default portrait 480x800 rgb24 */
>> +	dcs_write_seq(MIPI_DCS_SET_ADDRESS_MODE, 0x00);
>> +	dcs_write_seq(MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xDF);
>> +	dcs_write_seq(MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1F);
>> +	dcs_write_seq(MIPI_DCS_SET_PIXEL_FORMAT, 0x77);
> 
> I think some of above DCS commands have corresponding helper functions.
> 

Yes, you are right, lazy I am :), I will update it. Many thanks.

>> +
>> +	/* Disable CABC feature */
>> +	dcs_write_seq(0x55, 0x00);
>> +
>> +	ret = mipi_dsi_dcs_set_display_on(dsi);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* OTM8009a NOP */
>> +	dcs_write_seq(0x00, 0x00);
>> +
>> +	/* Send Command GRAM memory write (no parameters) */
>> +	dcs_write_seq(MIPI_DCS_WRITE_MEMORY_START);
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_disable(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
>> +	int ret;
>> +
>> +	if (!ctx->enabled)
>> +		return 0; /* This is not an issue so we return 0 here */
>> +
>> +	/* Power off the backlight. Note: end-user still controls brightness */
>> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>> +	ret = backlight_update_status(ctx->bl_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = mipi_dsi_dcs_set_display_off(dsi);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	msleep(120);
>> +
>> +	ctx->enabled = false;
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_unprepare(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +
>> +	if (!ctx->prepared)
>> +		return 0;
>> +
>> +	if (ctx->reset_gpio) {
>> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> +		msleep(20);
>> +	}
>> +
>> +	ctx->prepared = false;
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_prepare(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +	int ret;
>> +
>> +	if (ctx->prepared)
>> +		return 0;
>> +
>> +	if (ctx->reset_gpio) {
>> +		gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> +		msleep(20);
>> +		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> +		msleep(20);
> 
> Specs says reset gpio is active low, so I guess it should be fixed in
> bindings, dts and in driver.
> 
> Regards
> Andrzej
> 

100% correct! I am fixing it, many thanks for your review.
Best regards,
Philippe

>> +	}
>> +
>> +	ret = otm8009a_init_sequence(ctx);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ctx->prepared = true;
>> +
>> +	/* Power on the backlight. Note: end-user still controls brightness */
>> +	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
>> +	backlight_update_status(ctx->bl_dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_enable(struct drm_panel *panel)
>> +{
>> +	struct otm8009a *ctx = panel_to_otm8009a(panel);
>> +
>> +	ctx->enabled = true;
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_get_modes(struct drm_panel *panel)
>> +{
>> +	struct drm_display_mode *mode;
>> +
>> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
>> +	if (!mode) {
>> +		DRM_ERROR("failed to add mode %ux%ux@%u\n",
>> +			  default_mode.hdisplay, default_mode.vdisplay,
>> +			  default_mode.vrefresh);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	drm_mode_set_name(mode);
>> +
>> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
>> +	drm_mode_probed_add(panel->connector, mode);
>> +
>> +	panel->connector->display_info.width_mm = mode->width_mm;
>> +	panel->connector->display_info.height_mm = mode->height_mm;
>> +
>> +	return 1;
>> +}
>> +
>> +static const struct drm_panel_funcs otm8009a_drm_funcs = {
>> +	.disable   = otm8009a_disable,
>> +	.unprepare = otm8009a_unprepare,
>> +	.prepare   = otm8009a_prepare,
>> +	.enable    = otm8009a_enable,
>> +	.get_modes = otm8009a_get_modes,
>> +};
>> +
>> +/*
>> + * DSI-BASED BACKLIGHT
>> + */
>> +
>> +static int otm8009a_backlight_update_status(struct backlight_device *bd)
>> +{
>> +	struct otm8009a *ctx = bl_get_data(bd);
>> +	u8 data[2];
>> +
>> +	if (!ctx->prepared) {
>> +		DRM_WARN("lcd not ready yet for setting its backlight!\n");
>> +		return -ENXIO;
>> +	}
>> +
>> +	if (bd->props.power <= FB_BLANK_NORMAL) {
>> +		/* Power on the backlight with the requested brightness */
>> +		data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
>> +		data[1] = bd->props.brightness;
>> +		otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
>> +		/* set Brightness Control & Backlight on */
>> +		data[1] = 0x24;
>> +	} else {
>> +		/* Power off the backlight: set Brightness Control & Bl off */
>> +		data[1] = 0;
>> +	}
>> +
>> +	/* Update Brightness Control & Backlight */
>> +	data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
>> +	otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct backlight_ops otm8009a_backlight_ops = {
>> +	.update_status = otm8009a_backlight_update_status,
>> +};
>> +
>> +static int otm8009a_probe(struct mipi_dsi_device *dsi)
>> +{
>> +	struct device *dev = &dsi->dev;
>> +	struct otm8009a *ctx;
>> +	int ret;
>> +
>> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> +	if (!ctx)
>> +		return -ENOMEM;
>> +
>> +	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(ctx->reset_gpio)) {
>> +		dev_err(dev, "cannot get reset-gpio\n");
>> +		return PTR_ERR(ctx->reset_gpio);
>> +	}
>> +
>> +	mipi_dsi_set_drvdata(dsi, ctx);
>> +
>> +	ctx->dev = dev;
>> +
>> +	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;
>> +
>> +	drm_panel_init(&ctx->panel);
>> +	ctx->panel.dev = dev;
>> +	ctx->panel.funcs = &otm8009a_drm_funcs;
>> +
>> +	ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
>> +						&otm8009a_backlight_ops, NULL);
>> +	if (IS_ERR(ctx->bl_dev)) {
>> +		dev_err(dev, "failed to register backlight device\n");
>> +		return PTR_ERR(ctx->bl_dev);
>> +	}
>> +
>> +	ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
>> +	ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
>> +	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
>> +	ctx->bl_dev->props.type = BACKLIGHT_RAW;
>> +
>> +	drm_panel_add(&ctx->panel);
>> +
>> +	ret = mipi_dsi_attach(dsi);
>> +	if (ret < 0) {
>> +		dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
>> +		drm_panel_remove(&ctx->panel);
>> +		backlight_device_unregister(ctx->bl_dev);
>> +		return ret;
>> +	}
>> +
>> +	DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
>> +		 default_mode.hdisplay, default_mode.vdisplay,
>> +		 default_mode.vrefresh,
>> +		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
>> +
>> +	return 0;
>> +}
>> +
>> +static int otm8009a_remove(struct mipi_dsi_device *dsi)
>> +{
>> +	struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
>> +
>> +	mipi_dsi_detach(dsi);
>> +	drm_panel_remove(&ctx->panel);
>> +
>> +	backlight_device_unregister(ctx->bl_dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id orisetech_otm8009a_of_match[] = {
>> +	{ .compatible = "orisetech,otm8009a" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
>> +
>> +static struct mipi_dsi_driver orisetech_otm8009a_driver = {
>> +	.probe  = otm8009a_probe,
>> +	.remove = otm8009a_remove,
>> +	.driver = {
>> +		.name = DRV_NAME "_panel",
>> +		.of_match_table = orisetech_otm8009a_of_match,
>> +	},
>> +};
>> +module_mipi_dsi_driver(orisetech_otm8009a_driver);
>> +
>> +MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
>> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
>> +MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
>> +MODULE_LICENSE("GPL v2");
> 
> 

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

* Re: [PATCH v1 1/3] dt-bindings: Add vendor prefix for Orise Technology
  2017-07-04 16:30   ` Philippe CORNU
@ 2017-07-10  1:10     ` Rob Herring
  -1 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-07-10  1:10 UTC (permalink / raw)
  To: Philippe CORNU
  Cc: Mark Rutland, devicetree, Alexandre Torgue, Arnd Bergmann,
	Russell King, Fabien Dessenne, Yannick Fertre, Thierry Reding,
	dri-devel, Maxime Coquelin, Mickael Reulier, Vincent Abriou,
	Gabriel Fernandez, Ludovic Barre, linux-arm-kernel

On Tue, Jul 04, 2017 at 06:30:03PM +0200, Philippe CORNU wrote:
> Orise Technology is headquartered in Taiwan and specializes
> in manufacture of Flat Panel Display Driver IC and Flat Panel
> Display Controller IC.
> 
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh@kernel.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 1/3] dt-bindings: Add vendor prefix for Orise Technology
@ 2017-07-10  1:10     ` Rob Herring
  0 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-07-10  1:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 04, 2017 at 06:30:03PM +0200, Philippe CORNU wrote:
> Orise Technology is headquartered in Taiwan and specializes
> in manufacture of Flat Panel Display Driver IC and Flat Panel
> Display Controller IC.
> 
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
  2017-07-04 16:30   ` Philippe CORNU
@ 2017-07-10  1:12     ` Rob Herring
  -1 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-07-10  1:12 UTC (permalink / raw)
  To: Philippe CORNU
  Cc: Mark Rutland, devicetree, Alexandre Torgue, Arnd Bergmann,
	Russell King, Fabien Dessenne, Yannick Fertre, Thierry Reding,
	dri-devel, Maxime Coquelin, Mickael Reulier, Vincent Abriou,
	Gabriel Fernandez, Ludovic Barre, linux-arm-kernel

On Tue, Jul 04, 2017 at 06:30:04PM +0200, Philippe CORNU wrote:
> The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> 
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>  .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> new file mode 100644
> index 0000000..0bb8237
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> @@ -0,0 +1,20 @@
> +Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
> +
> +The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> +a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> +
> +Required properties:
> +  - compatible: "orisetech,otm8009a"
> +  - reg: the virtual channel number of a DSI peripheral
> +  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)

If this is optional, move it to its own section.

> +
> +Example:
> +&dsi {
> +	...
> +
> +	panel@0 {
> +		compatible = "orisetech,otm8009a";
> +		reg = <0>;
> +		reset-gpios = <&gpioh 7 0>;
> +	};
> +};
> -- 
> 1.9.1
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
@ 2017-07-10  1:12     ` Rob Herring
  0 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-07-10  1:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 04, 2017 at 06:30:04PM +0200, Philippe CORNU wrote:
> The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> 
> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
> ---
>  .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> new file mode 100644
> index 0000000..0bb8237
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
> @@ -0,0 +1,20 @@
> +Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
> +
> +The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
> +a MIPI-DSI video interface. Its backlight is managed through the DSI link.
> +
> +Required properties:
> +  - compatible: "orisetech,otm8009a"
> +  - reg: the virtual channel number of a DSI peripheral
> +  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)

If this is optional, move it to its own section.

> +
> +Example:
> +&dsi {
> +	...
> +
> +	panel at 0 {
> +		compatible = "orisetech,otm8009a";
> +		reg = <0>;
> +		reset-gpios = <&gpioh 7 0>;
> +	};
> +};
> -- 
> 1.9.1
> 

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

* Re: [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
  2017-07-10  1:12     ` Rob Herring
@ 2017-07-10  9:34       ` Philippe CORNU
  -1 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-10  9:34 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree, Alexandre TORGUE, Arnd Bergmann,
	Russell King, Fabien DESSENNE, Yannick FERTRE, Thierry Reding,
	dri-devel, Maxime Coquelin, Mickael REULIER, Vincent ABRIOU,
	Gabriel FERNANDEZ, Ludovic BARRE, linux-arm-kernel



On 07/10/2017 03:12 AM, Rob Herring wrote:
> On Tue, Jul 04, 2017 at 06:30:04PM +0200, Philippe CORNU wrote:
>> The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
>> a MIPI-DSI video interface. Its backlight is managed through the DSI link.
>>
>> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
>> ---
>>   .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
>>   1 file changed, 20 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
>>
>> diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
>> new file mode 100644
>> index 0000000..0bb8237
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
>> @@ -0,0 +1,20 @@
>> +Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
>> +
>> +The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
>> +a MIPI-DSI video interface. Its backlight is managed through the DSI link.
>> +
>> +Required properties:
>> +  - compatible: "orisetech,otm8009a"
>> +  - reg: the virtual channel number of a DSI peripheral
>> +  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)
> 
> If this is optional, move it to its own section.
> 

Dear Rob,
Many thanks for your comment. Fixed in v2, sent few minutes ago.
Best regards,
Philippe

>> +
>> +Example:
>> +&dsi {
>> +	...
>> +
>> +	panel@0 {
>> +		compatible = "orisetech,otm8009a";
>> +		reg = <0>;
>> +		reset-gpios = <&gpioh 7 0>;
>> +	};
>> +};
>> -- 
>> 1.9.1
>>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel
@ 2017-07-10  9:34       ` Philippe CORNU
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe CORNU @ 2017-07-10  9:34 UTC (permalink / raw)
  To: linux-arm-kernel



On 07/10/2017 03:12 AM, Rob Herring wrote:
> On Tue, Jul 04, 2017 at 06:30:04PM +0200, Philippe CORNU wrote:
>> The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
>> a MIPI-DSI video interface. Its backlight is managed through the DSI link.
>>
>> Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
>> ---
>>   .../bindings/display/panel/orisetech,otm8009a.txt    | 20 ++++++++++++++++++++
>>   1 file changed, 20 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
>>
>> diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
>> new file mode 100644
>> index 0000000..0bb8237
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
>> @@ -0,0 +1,20 @@
>> +Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
>> +
>> +The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
>> +a MIPI-DSI video interface. Its backlight is managed through the DSI link.
>> +
>> +Required properties:
>> +  - compatible: "orisetech,otm8009a"
>> +  - reg: the virtual channel number of a DSI peripheral
>> +  - reset-gpios: a GPIO spec for the reset pin (active low). (Optional)
> 
> If this is optional, move it to its own section.
> 

Dear Rob,
Many thanks for your comment. Fixed in v2, sent few minutes ago.
Best regards,
Philippe

>> +
>> +Example:
>> +&dsi {
>> +	...
>> +
>> +	panel at 0 {
>> +		compatible = "orisetech,otm8009a";
>> +		reg = <0>;
>> +		reset-gpios = <&gpioh 7 0>;
>> +	};
>> +};
>> -- 
>> 1.9.1
>>

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

end of thread, other threads:[~2017-07-10  9:34 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-04 16:30 [PATCH v1 0/3] Add support for the otm8009a dsi panel Philippe CORNU
2017-07-04 16:30 ` Philippe CORNU
2017-07-04 16:30 ` [PATCH v1 1/3] dt-bindings: Add vendor prefix for Orise Technology Philippe CORNU
2017-07-04 16:30   ` Philippe CORNU
2017-07-10  1:10   ` Rob Herring
2017-07-10  1:10     ` Rob Herring
2017-07-04 16:30 ` [PATCH v1 2/3] dt-bindings: display: panel: Add support for Orise Tech otm8009a dsi panel Philippe CORNU
2017-07-04 16:30   ` Philippe CORNU
2017-07-06 14:21   ` Philippe CORNU
2017-07-06 14:21     ` Philippe CORNU
2017-07-10  1:12   ` Rob Herring
2017-07-10  1:12     ` Rob Herring
2017-07-10  9:34     ` Philippe CORNU
2017-07-10  9:34       ` Philippe CORNU
2017-07-04 16:30 ` [PATCH v1 3/3] drm/panel: Add support for otm8009a panel driver Philippe CORNU
2017-07-04 16:30   ` Philippe CORNU
2017-07-05  9:28   ` Andrzej Hajda
2017-07-05  9:28     ` Andrzej Hajda
2017-07-06 14:44     ` Philippe CORNU
2017-07-06 14:44       ` Philippe CORNU

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.