All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Add Truly NT35521 panel driver support
@ 2021-08-04  8:13 Shawn Guo
  2021-08-04  8:13 ` [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support Shawn Guo
  2021-08-04  8:13 ` [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver Shawn Guo
  0 siblings, 2 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-04  8:13 UTC (permalink / raw)
  To: Thierry Reding, Sam Ravnborg
  Cc: Daniel Vetter, Rob Herring, dri-devel, devicetree, linux-arm-msm,
	Shawn Guo

It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
can be found on Sony Xperia M4 Aqua phone.

Shawn Guo (2):
  dt-bindings: display: panel: Add Truly NT35521 panel support
  drm/panel: Add Truly NT35521 panel driver

 .../bindings/display/panel/truly,nt35521.yaml |  62 +++
 drivers/gpu/drm/panel/Kconfig                 |   9 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 drivers/gpu/drm/panel/panel-truly-nt35521.c   | 491 ++++++++++++++++++
 4 files changed, 563 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
 create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c

-- 
2.17.1


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

* [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support
  2021-08-04  8:13 [PATCH 0/2] Add Truly NT35521 panel driver support Shawn Guo
@ 2021-08-04  8:13 ` Shawn Guo
  2021-08-04 16:03   ` Sam Ravnborg
  2021-08-11 18:51   ` Rob Herring
  2021-08-04  8:13 ` [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver Shawn Guo
  1 sibling, 2 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-04  8:13 UTC (permalink / raw)
  To: Thierry Reding, Sam Ravnborg
  Cc: Daniel Vetter, Rob Herring, dri-devel, devicetree, linux-arm-msm,
	Shawn Guo

The Truly NT35521 is a 5.24" 1280x720 DSI panel, and the backlight is
managed through DSI link.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
 .../bindings/display/panel/truly,nt35521.yaml | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml

diff --git a/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml b/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
new file mode 100644
index 000000000000..4727c3df6eb8
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/truly,nt35521.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Truly NT35521 5.24" 1280x720 MIPI-DSI Panel
+
+maintainers:
+  - Shawn Guo <shawn.guo@linaro.org>
+
+description: |
+  The Truly NT35521 is a 5.24" 1280x720 MIPI-DSI panel.  The panel backlight
+  is managed through DSI link.
+
+allOf:
+  - $ref: panel-common.yaml#
+
+properties:
+  compatible:
+    const: truly,nt35521
+
+  reg: true
+
+  reset-gpios: true
+
+  enable-gpios: true
+
+  pwr-positive5-gpios:
+    maxItems: 1
+
+  pwr-negative5-gpios:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - reset-gpios
+  - enable-gpios
+  - pwr-positive5-gpios
+  - pwr-negative5-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    dsi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        panel@0 {
+            compatible = "truly,nt35521";
+            reg = <0>;
+            reset-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>;
+            pwr-positive5-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>;
+            pwr-negative5-gpios = <&msmgpio 17 GPIO_ACTIVE_HIGH>;
+            enable-gpios = <&msmgpio 10 GPIO_ACTIVE_HIGH>;
+        };
+    };
+...
-- 
2.17.1


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

* [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-04  8:13 [PATCH 0/2] Add Truly NT35521 panel driver support Shawn Guo
  2021-08-04  8:13 ` [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support Shawn Guo
@ 2021-08-04  8:13 ` Shawn Guo
  2021-08-04 12:09   ` Stephan Gerhold
  2021-08-04 16:24   ` Sam Ravnborg
  1 sibling, 2 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-04  8:13 UTC (permalink / raw)
  To: Thierry Reding, Sam Ravnborg
  Cc: Daniel Vetter, Rob Herring, dri-devel, devicetree, linux-arm-msm,
	Shawn Guo

It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
managed through DSI link.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
 drivers/gpu/drm/panel/Kconfig               |   9 +
 drivers/gpu/drm/panel/Makefile              |   1 +
 drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 ++++++++++++++++++++
 3 files changed, 501 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index ef87d92cdf49..cdc4abd5c40c 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -537,6 +537,15 @@ config DRM_PANEL_TPO_TPG110
 	  400CH LTPS TFT LCD Single Chip Digital Driver for up to
 	  800x400 LCD panels.
 
+config DRM_PANEL_TRULY_NT35521
+	tristate "Truly NT35521 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 Truly NT35521
+	  1280x720 DSI panel.
+
 config DRM_PANEL_TRULY_NT35597_WQXGA
 	tristate "Truly WQXGA"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index cae4d976c069..3d3c98cb7a7b 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
+obj-$(CONFIG_DRM_PANEL_TRULY_NT35521) += panel-truly-nt35521.o
 obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
 obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
 obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
diff --git a/drivers/gpu/drm/panel/panel-truly-nt35521.c b/drivers/gpu/drm/panel/panel-truly-nt35521.c
new file mode 100644
index 000000000000..ea3cfb46be7e
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-truly-nt35521.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+struct nt35521_panel {
+	struct drm_panel panel;
+	struct device *dev;
+	struct gpio_desc *rst_gpio;
+	struct gpio_desc *pwrp5_gpio;
+	struct gpio_desc *pwrn5_gpio;
+	struct gpio_desc *en_gpio;
+	bool prepared;
+	bool enabled;
+};
+
+static inline struct nt35521_panel *panel_to_nt35521(struct drm_panel *panel)
+{
+	return container_of(panel, struct nt35521_panel, panel);
+}
+
+#define nt_dcs_write(seq...)						\
+({									\
+	const u8 d[] = { seq };						\
+	if (mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)) < 0)	\
+		DRM_DEV_ERROR(dev, "dcs write buffer failed\n");	\
+})
+
+#define nt_gen_write(seq...)						\
+({									\
+	const u8 d[] = { seq };						\
+	if (mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)) < 0)		\
+		DRM_DEV_ERROR(dev, "generic write buffer failed\n");	\
+})
+
+static void nt35521_panel_on(struct nt35521_panel *nt)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+	struct device *dev = nt->dev;
+
+	/* Transmit data in low power mode */
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
+	nt_dcs_write(0xff, 0xaa, 0x55, 0xa5, 0x80);
+	nt_dcs_write(0x6f, 0x11, 0x00);
+	nt_dcs_write(0xf7, 0x20, 0x00);
+	nt_dcs_write(0x6f, 0x01);
+	nt_dcs_write(0xb1, 0x21);
+	nt_dcs_write(0xbd, 0x01, 0xa0, 0x10, 0x08, 0x01);
+	nt_dcs_write(0xb8, 0x01, 0x02, 0x0c, 0x02);
+	nt_dcs_write(0xbb, 0x11, 0x11);
+	nt_dcs_write(0xbc, 0x00, 0x00);
+	nt_dcs_write(0xb6, 0x02);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01);
+	nt_dcs_write(0xb0, 0x09, 0x09);
+	nt_dcs_write(0xb1, 0x09, 0x09);
+	nt_dcs_write(0xbc, 0x8c, 0x00);
+	nt_dcs_write(0xbd, 0x8c, 0x00);
+	nt_dcs_write(0xca, 0x00);
+	nt_dcs_write(0xc0, 0x04);
+	nt_dcs_write(0xbe, 0xb5);
+	nt_dcs_write(0xb3, 0x35, 0x35);
+	nt_dcs_write(0xb4, 0x25, 0x25);
+	nt_dcs_write(0xb9, 0x43, 0x43);
+	nt_dcs_write(0xba, 0x24, 0x24);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x02);
+	nt_dcs_write(0xee, 0x03);
+	nt_dcs_write(0xb0, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00,
+		     0xc3, 0x00, 0xce, 0x00, 0xe1, 0x00, 0xf3, 0x01,
+		     0x11);
+	nt_dcs_write(0xb1, 0x01, 0x2e, 0x01, 0x5c, 0x01, 0x82, 0x01,
+		     0xc3, 0x01, 0xfe, 0x02, 0x00, 0x02, 0x37, 0x02,
+		     0x77);
+	nt_dcs_write(0xb2, 0x02, 0xa1, 0x02, 0xd7, 0x02, 0xfe, 0x03,
+		     0x2c, 0x03, 0x4b, 0x03, 0x63, 0x03, 0x8f, 0x03,
+		     0x90);
+	nt_dcs_write(0xb3, 0x03, 0x96, 0x03, 0x98);
+	nt_dcs_write(0xb4, 0x00, 0x81, 0x00, 0x8b, 0x00, 0x9c, 0x00,
+		     0xa9, 0x00, 0xb5, 0x00, 0xcb, 0x00, 0xdf, 0x01,
+		     0x02);
+	nt_dcs_write(0xb5, 0x01, 0x1f, 0x01, 0x51, 0x01, 0x7a, 0x01,
+		     0xbf, 0x01, 0xfa, 0x01, 0xfc, 0x02, 0x34, 0x02, 0x76);
+	nt_dcs_write(0xb6, 0x02, 0x9f, 0x02, 0xd7, 0x02, 0xfc, 0x03, 0x2c,
+		     0x03, 0x4a, 0x03, 0x63, 0x03, 0x8f, 0x03, 0xa2);
+	nt_dcs_write(0xb7, 0x03, 0xb8, 0x03, 0xba);
+	nt_dcs_write(0xb8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x2a,
+		     0x00, 0x41, 0x00, 0x67, 0x00, 0x87, 0x00, 0xb9);
+	nt_dcs_write(0xb9, 0x00, 0xe2, 0x01, 0x22, 0x01, 0x54, 0x01, 0xa3,
+		     0x01, 0xe6, 0x01, 0xe7, 0x02, 0x24, 0x02, 0x67);
+	nt_dcs_write(0xba, 0x02, 0x93, 0x02, 0xcd, 0x02, 0xf6, 0x03, 0x31,
+		     0x03, 0x6c, 0x03, 0xe9, 0x03, 0xef, 0x03, 0xf4);
+	nt_dcs_write(0xbb, 0x03, 0xf6, 0x03, 0xf7);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x03);
+	nt_dcs_write(0xb0, 0x22, 0x00);
+	nt_dcs_write(0xb1, 0x22, 0x00);
+	nt_dcs_write(0xb2, 0x05, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xb3, 0x05, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xb4, 0x05, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xb5, 0x05, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xba, 0x53, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xbb, 0x53, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xbc, 0x53, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xbd, 0x53, 0x00, 0x60, 0x00, 0x00);
+	nt_dcs_write(0xc0, 0x00, 0x34, 0x00, 0x00);
+	nt_dcs_write(0xc1, 0x00, 0x00, 0x34, 0x00);
+	nt_dcs_write(0xc2, 0x00, 0x00, 0x34, 0x00);
+	nt_dcs_write(0xc3, 0x00, 0x00, 0x34, 0x00);
+	nt_dcs_write(0xc4, 0x60);
+	nt_dcs_write(0xc5, 0xc0);
+	nt_dcs_write(0xc6, 0x00);
+	nt_dcs_write(0xc7, 0x00);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x05);
+	nt_dcs_write(0xb0, 0x17, 0x06);
+	nt_dcs_write(0xb1, 0x17, 0x06);
+	nt_dcs_write(0xb2, 0x17, 0x06);
+	nt_dcs_write(0xb3, 0x17, 0x06);
+	nt_dcs_write(0xb4, 0x17, 0x06);
+	nt_dcs_write(0xb5, 0x17, 0x06);
+	nt_dcs_write(0xb6, 0x17, 0x06);
+	nt_dcs_write(0xb7, 0x17, 0x06);
+	nt_dcs_write(0xb8, 0x00);
+	nt_dcs_write(0xb9, 0x00, 0x03);
+	nt_dcs_write(0xba, 0x00, 0x00);
+	nt_dcs_write(0xbb, 0x02, 0x03);
+	nt_dcs_write(0xbc, 0x02, 0x03);
+	nt_dcs_write(0xbd, 0x03, 0x03, 0x00, 0x03, 0x03);
+	nt_dcs_write(0xc0, 0x0b);
+	nt_dcs_write(0xc1, 0x09);
+	nt_dcs_write(0xc2, 0xa6);
+	nt_dcs_write(0xc3, 0x05);
+	nt_dcs_write(0xc4, 0x00);
+	nt_dcs_write(0xc5, 0x02);
+	nt_dcs_write(0xc6, 0x22);
+	nt_dcs_write(0xc7, 0x03);
+	nt_dcs_write(0xc8, 0x07, 0x20);
+	nt_dcs_write(0xc9, 0x03, 0x20);
+	nt_dcs_write(0xca, 0x01, 0x60);
+	nt_dcs_write(0xcb, 0x01, 0x60);
+	nt_dcs_write(0xcc, 0x00, 0x00, 0x02);
+	nt_dcs_write(0xcd, 0x00, 0x00, 0x02);
+	nt_dcs_write(0xce, 0x00, 0x00, 0x02);
+	nt_dcs_write(0xcf, 0x00, 0x00, 0x02);
+	nt_dcs_write(0xd1, 0x00, 0x05, 0x01, 0x07, 0x10);
+	nt_dcs_write(0xd2, 0x10, 0x05, 0x05, 0x03, 0x10);
+	nt_dcs_write(0xd3, 0x20, 0x00, 0x43, 0x07, 0x10);
+	nt_dcs_write(0xd4, 0x30, 0x00, 0x43, 0x07, 0x10);
+	nt_dcs_write(0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		     0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		     0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		     0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xe5, 0x06);
+	nt_dcs_write(0xe6, 0x06);
+	nt_dcs_write(0xe7, 0x00);
+	nt_dcs_write(0xe8, 0x06);
+	nt_dcs_write(0xe9, 0x06);
+	nt_dcs_write(0xea, 0x06);
+	nt_dcs_write(0xeb, 0x00);
+	nt_dcs_write(0xec, 0x00);
+	nt_dcs_write(0xed, 0x30);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x06);
+	nt_dcs_write(0xb0, 0x31, 0x31);
+	nt_dcs_write(0xb1, 0x31, 0x31);
+	nt_dcs_write(0xb2, 0x2d, 0x2e);
+	nt_dcs_write(0xb3, 0x31, 0x34);
+	nt_dcs_write(0xb4, 0x29, 0x2a);
+	nt_dcs_write(0xb5, 0x12, 0x10);
+	nt_dcs_write(0xb6, 0x18, 0x16);
+	nt_dcs_write(0xb7, 0x00, 0x02);
+	nt_dcs_write(0xb8, 0x08, 0x31);
+	nt_dcs_write(0xb9, 0x31, 0x31);
+	nt_dcs_write(0xba, 0x31, 0x31);
+	nt_dcs_write(0xbb, 0x31, 0x08);
+	nt_dcs_write(0xbc, 0x03, 0x01);
+	nt_dcs_write(0xbd, 0x17, 0x19);
+	nt_dcs_write(0xbe, 0x11, 0x13);
+	nt_dcs_write(0xbf, 0x2a, 0x29);
+	nt_dcs_write(0xc0, 0x34, 0x31);
+	nt_dcs_write(0xc1, 0x2e, 0x2d);
+	nt_dcs_write(0xc2, 0x31, 0x31);
+	nt_dcs_write(0xc3, 0x31, 0x31);
+	nt_dcs_write(0xc4, 0x31, 0x31);
+	nt_dcs_write(0xc5, 0x31, 0x31);
+	nt_dcs_write(0xc6, 0x2e, 0x2d);
+	nt_dcs_write(0xc7, 0x31, 0x34);
+	nt_dcs_write(0xc8, 0x29, 0x2a);
+	nt_dcs_write(0xc9, 0x17, 0x19);
+	nt_dcs_write(0xca, 0x11, 0x13);
+	nt_dcs_write(0xcb, 0x03, 0x01);
+	nt_dcs_write(0xcc, 0x08, 0x31);
+	nt_dcs_write(0xcd, 0x31, 0x31);
+	nt_dcs_write(0xce, 0x31, 0x31);
+	nt_dcs_write(0xcf, 0x31, 0x08);
+	nt_dcs_write(0xd0, 0x00, 0x02);
+	nt_dcs_write(0xd1, 0x12, 0x10);
+	nt_dcs_write(0xd2, 0x18, 0x16);
+	nt_dcs_write(0xd3, 0x2a, 0x29);
+	nt_dcs_write(0xd4, 0x34, 0x31);
+	nt_dcs_write(0xd5, 0x2d, 0x2e);
+	nt_dcs_write(0xd6, 0x31, 0x31);
+	nt_dcs_write(0xd7, 0x31, 0x31);
+	nt_dcs_write(0xe5, 0x31, 0x31);
+	nt_dcs_write(0xe6, 0x31, 0x31);
+	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xd9, 0x00, 0x00, 0x00, 0x00, 0x00);
+	nt_dcs_write(0xe7, 0x00);
+	nt_dcs_write(0x6f, 0x02);
+	nt_dcs_write(0xf7, 0x47);
+	nt_dcs_write(0x6f, 0x0a);
+	nt_dcs_write(0xf7, 0x02);
+	nt_dcs_write(0x6f, 0x17);
+	nt_dcs_write(0xf4, 0x60);
+	nt_dcs_write(0x6f, 0x01);
+	nt_dcs_write(0xf9, 0x46);
+	nt_dcs_write(0x6f, 0x11);
+	nt_dcs_write(0xf3, 0x01);
+	nt_dcs_write(0x35, 0x00);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
+	nt_dcs_write(0xd9, 0x02, 0x03, 0x00);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
+	nt_dcs_write(0xb1, 0x6c, 0x21);
+	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
+	nt_dcs_write(0x35, 0x00);
+	nt_gen_write(0x11, 0x00);
+	msleep(120);
+	nt_gen_write(0x29, 0x00);
+	usleep_range(1000, 1500);
+	nt_dcs_write(0x53, 0x24);
+}
+
+static void nt35521_panel_off(struct nt35521_panel *nt)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+	struct device *dev = nt->dev;
+
+	/* Transmit data in high speed mode */
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	nt_dcs_write(0x28, 0x00);
+	msleep(50);
+	nt_dcs_write(0x10, 0x00);
+	msleep(150);
+}
+
+static int nt35521_prepare(struct drm_panel *panel)
+{
+	struct nt35521_panel *nt = panel_to_nt35521(panel);
+
+	if (nt->prepared)
+		return 0;
+
+	gpiod_set_value_cansleep(nt->pwrp5_gpio, 1);
+	usleep_range(1000, 1500);
+	gpiod_set_value_cansleep(nt->pwrn5_gpio, 1);
+	usleep_range(10000, 15000);
+	gpiod_set_value_cansleep(nt->rst_gpio, 0);
+	msleep(150);
+
+	nt35521_panel_on(nt);
+
+	nt->prepared = true;
+
+	return 0;
+}
+
+static int nt35521_unprepare(struct drm_panel *panel)
+{
+	struct nt35521_panel *nt = panel_to_nt35521(panel);
+
+	if (!nt->prepared)
+		return 0;
+
+	nt35521_panel_off(nt);
+
+	gpiod_set_value_cansleep(nt->rst_gpio, 1);
+
+	nt->prepared = false;
+
+	return 0;
+}
+
+static int nt35521_enable(struct drm_panel *panel)
+{
+	struct nt35521_panel *nt = panel_to_nt35521(panel);
+
+	if (nt->enabled)
+		return 0;
+
+	gpiod_set_value_cansleep(nt->en_gpio, 1);
+
+	nt->enabled = true;
+
+	return 0;
+}
+
+static int nt35521_disable(struct drm_panel *panel)
+{
+	struct nt35521_panel *nt = panel_to_nt35521(panel);
+
+	if (!nt->enabled)
+		return 0;
+
+	gpiod_set_value_cansleep(nt->en_gpio, 0);
+
+	nt->enabled = false;
+
+	return 0;
+}
+
+static const struct drm_display_mode nt35521_modes = {
+	.clock = 133306,
+	.hdisplay = 720,
+	.hsync_start = 720 + 632,
+	.hsync_end = 720 + 632 + 40,
+	.htotal = 720 + 632 + 40 + 295,
+	.vdisplay = 1280,
+	.vsync_start = 1280 + 18,
+	.vsync_end = 1280 + 18 + 1,
+	.vtotal = 1280 + 18 + 1 + 18,
+};
+
+static int nt35521_get_modes(struct drm_panel *panel,
+			      struct drm_connector *connector)
+{
+	struct nt35521_panel *nt = panel_to_nt35521(panel);
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(connector->dev, &nt35521_modes);
+	if (!mode) {
+		DRM_DEV_ERROR(nt->dev, "failed to add display mode\n");
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = 65;
+	connector->display_info.height_mm = 116;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs nt35521_drm_funcs = {
+	.prepare = nt35521_prepare,
+	.unprepare = nt35521_unprepare,
+	.enable = nt35521_enable,
+	.disable = nt35521_disable,
+	.get_modes = nt35521_get_modes,
+};
+
+static int nt35521_backlight_update_status(struct backlight_device *bd)
+{
+	struct nt35521_panel *nt = bl_get_data(bd);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
+	u8 brightness = bd->props.brightness;
+	int ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+				 &brightness,
+				 sizeof(brightness));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static const struct backlight_ops nt35521_backlight_ops = {
+	.update_status = nt35521_backlight_update_status,
+};
+
+static int nt35521_probe(struct mipi_dsi_device *dsi)
+{
+	struct backlight_properties props;
+	struct device *dev = &dsi->dev;
+	struct nt35521_panel *nt;
+	int ret;
+
+	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);
+	if (!nt)
+		return -ENOMEM;
+
+	nt->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(nt->rst_gpio)) {
+		ret = PTR_ERR(nt->rst_gpio);
+		DRM_DEV_ERROR(dev, "failed to get reset GPIO: %d\n", ret);
+		return ret;
+	}
+
+	nt->pwrp5_gpio = devm_gpiod_get(dev, "pwr-positive5", GPIOD_OUT_LOW);
+	if (IS_ERR(nt->pwrp5_gpio)) {
+		ret = PTR_ERR(nt->pwrp5_gpio);
+		DRM_DEV_ERROR(dev, "failed to get positive5 GPIO: %d\n", ret);
+		return ret;
+	}
+
+	nt->pwrn5_gpio = devm_gpiod_get(dev, "pwr-negative5", GPIOD_OUT_LOW);
+	if (IS_ERR(nt->pwrn5_gpio)) {
+		ret = PTR_ERR(nt->pwrn5_gpio);
+		DRM_DEV_ERROR(dev, "failed to get negative5 GPIO: %d\n", ret);
+		return ret;
+	}
+
+	nt->en_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(nt->en_gpio)) {
+		ret = PTR_ERR(nt->en_gpio);
+		DRM_DEV_ERROR(dev, "failed to get enable GPIO: %d\n", ret);
+		return ret;
+	}
+
+	nt->dev = dev;
+
+	drm_panel_init(&nt->panel, dev, &nt35521_drm_funcs,
+		       DRM_MODE_CONNECTOR_DSI);
+
+	props.max_brightness = 255;
+	props.brightness = 240;
+	props.type = BACKLIGHT_RAW;
+
+	nt->panel.backlight = devm_backlight_device_register(dev, dev_name(dev),
+						dev, nt, &nt35521_backlight_ops,
+						&props);
+	if (IS_ERR(nt->panel.backlight)) {
+		ret = PTR_ERR(nt->panel.backlight);
+		DRM_DEV_ERROR(dev, "failed to register backlight: %d\n", ret);
+		return ret;
+	}
+
+	drm_panel_add(&nt->panel);
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "failed to attach DSI device: %d\n", ret);
+		drm_panel_remove(&nt->panel);
+		return ret;
+	}
+
+	mipi_dsi_set_drvdata(dsi, nt);
+
+	return 0;
+}
+
+static int nt35521_remove(struct mipi_dsi_device *dsi)
+{
+	struct nt35521_panel *nt = mipi_dsi_get_drvdata(dsi);
+
+	mipi_dsi_detach(dsi);
+	drm_panel_remove(&nt->panel);
+
+	return 0;
+}
+
+static const struct of_device_id truly_nt35521_of_match[] = {
+	{ .compatible = "truly,nt35521" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, truly_nt35521_of_match);
+
+static struct mipi_dsi_driver truly_nt35521_driver = {
+	.driver = {
+		.name = "panel-truly-nt35521",
+		.of_match_table = truly_nt35521_of_match,
+	},
+	.probe  = nt35521_probe,
+	.remove = nt35521_remove,
+};
+module_mipi_dsi_driver(truly_nt35521_driver);
+
+MODULE_DESCRIPTION("Truly NT35521 DSI panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


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

* Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-04  8:13 ` [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver Shawn Guo
@ 2021-08-04 12:09   ` Stephan Gerhold
  2021-08-08 13:44     ` Shawn Guo
  2021-08-04 16:24   ` Sam Ravnborg
  1 sibling, 1 reply; 13+ messages in thread
From: Stephan Gerhold @ 2021-08-04 12:09 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Thierry Reding, Sam Ravnborg, Daniel Vetter, Rob Herring,
	dri-devel, devicetree, linux-arm-msm

Hi Shawn,

Thanks for the patch!

On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
> can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
> managed through DSI link.
> 
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
>  drivers/gpu/drm/panel/Kconfig               |   9 +
>  drivers/gpu/drm/panel/Makefile              |   1 +
>  drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 ++++++++++++++++++++
>  3 files changed, 501 insertions(+)
>  create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c
> 
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index ef87d92cdf49..cdc4abd5c40c 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -537,6 +537,15 @@ config DRM_PANEL_TPO_TPG110
>  	  400CH LTPS TFT LCD Single Chip Digital Driver for up to
>  	  800x400 LCD panels.
>  
> +config DRM_PANEL_TRULY_NT35521
> +	tristate "Truly NT35521 panel"

I think the name "Truly NT35521" is a bit too generic. AFAIK "Truly" is
a panel vendor and the NovaTek NT35521 is the panel controller. But
there are almost certainly other Truly panels that were also combined
with a NT35521 but need a slightly different configuration.

If you don't know more than "Truly NT35521" based on the Sony sources,
maybe do it similar to "asus,z00t-tm5p5-n35596" and use a compatible
like "sony,<device>-truly-nt35521". Would be good to clarify the Kconfig
option here too.

> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	help
> +	  Say Y here if you want to enable support for Truly NT35521
> +	  1280x720 DSI panel.
> +
>  config DRM_PANEL_TRULY_NT35597_WQXGA
>  	tristate "Truly WQXGA"
>  	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index cae4d976c069..3d3c98cb7a7b 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
>  obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
>  obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
>  obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
> +obj-$(CONFIG_DRM_PANEL_TRULY_NT35521) += panel-truly-nt35521.o
>  obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
>  obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
>  obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
> diff --git a/drivers/gpu/drm/panel/panel-truly-nt35521.c b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> new file mode 100644
> index 000000000000..ea3cfb46be7e
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> @@ -0,0 +1,491 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +
> +#include <video/mipi_display.h>
> +
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_modes.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +
> +struct nt35521_panel {
> +	struct drm_panel panel;
> +	struct device *dev;
> +	struct gpio_desc *rst_gpio;
> +	struct gpio_desc *pwrp5_gpio;
> +	struct gpio_desc *pwrn5_gpio;
> +	struct gpio_desc *en_gpio;
> +	bool prepared;
> +	bool enabled;
> +};
> +
> +static inline struct nt35521_panel *panel_to_nt35521(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct nt35521_panel, panel);
> +}
> +
> +#define nt_dcs_write(seq...)						\
> +({									\
> +	const u8 d[] = { seq };						\
> +	if (mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)) < 0)	\
> +		DRM_DEV_ERROR(dev, "dcs write buffer failed\n");	\
> +})
> +
> +#define nt_gen_write(seq...)						\
> +({									\
> +	const u8 d[] = { seq };						\
> +	if (mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)) < 0)		\
> +		DRM_DEV_ERROR(dev, "generic write buffer failed\n");	\
> +})
> +
> +static void nt35521_panel_on(struct nt35521_panel *nt)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> +	struct device *dev = nt->dev;
> +
> +	/* Transmit data in low power mode */
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> +	nt_dcs_write(0xff, 0xaa, 0x55, 0xa5, 0x80);
> +	nt_dcs_write(0x6f, 0x11, 0x00);
> +	nt_dcs_write(0xf7, 0x20, 0x00);
> +	nt_dcs_write(0x6f, 0x01);
> +	nt_dcs_write(0xb1, 0x21);
> +	nt_dcs_write(0xbd, 0x01, 0xa0, 0x10, 0x08, 0x01);
> +	nt_dcs_write(0xb8, 0x01, 0x02, 0x0c, 0x02);
> +	nt_dcs_write(0xbb, 0x11, 0x11);
> +	nt_dcs_write(0xbc, 0x00, 0x00);
> +	nt_dcs_write(0xb6, 0x02);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01);
> +	nt_dcs_write(0xb0, 0x09, 0x09);
> +	nt_dcs_write(0xb1, 0x09, 0x09);
> +	nt_dcs_write(0xbc, 0x8c, 0x00);
> +	nt_dcs_write(0xbd, 0x8c, 0x00);
> +	nt_dcs_write(0xca, 0x00);
> +	nt_dcs_write(0xc0, 0x04);
> +	nt_dcs_write(0xbe, 0xb5);
> +	nt_dcs_write(0xb3, 0x35, 0x35);
> +	nt_dcs_write(0xb4, 0x25, 0x25);
> +	nt_dcs_write(0xb9, 0x43, 0x43);
> +	nt_dcs_write(0xba, 0x24, 0x24);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x02);
> +	nt_dcs_write(0xee, 0x03);
> +	nt_dcs_write(0xb0, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00,
> +		     0xc3, 0x00, 0xce, 0x00, 0xe1, 0x00, 0xf3, 0x01,
> +		     0x11);
> +	nt_dcs_write(0xb1, 0x01, 0x2e, 0x01, 0x5c, 0x01, 0x82, 0x01,
> +		     0xc3, 0x01, 0xfe, 0x02, 0x00, 0x02, 0x37, 0x02,
> +		     0x77);
> +	nt_dcs_write(0xb2, 0x02, 0xa1, 0x02, 0xd7, 0x02, 0xfe, 0x03,
> +		     0x2c, 0x03, 0x4b, 0x03, 0x63, 0x03, 0x8f, 0x03,
> +		     0x90);
> +	nt_dcs_write(0xb3, 0x03, 0x96, 0x03, 0x98);
> +	nt_dcs_write(0xb4, 0x00, 0x81, 0x00, 0x8b, 0x00, 0x9c, 0x00,
> +		     0xa9, 0x00, 0xb5, 0x00, 0xcb, 0x00, 0xdf, 0x01,
> +		     0x02);
> +	nt_dcs_write(0xb5, 0x01, 0x1f, 0x01, 0x51, 0x01, 0x7a, 0x01,
> +		     0xbf, 0x01, 0xfa, 0x01, 0xfc, 0x02, 0x34, 0x02, 0x76);
> +	nt_dcs_write(0xb6, 0x02, 0x9f, 0x02, 0xd7, 0x02, 0xfc, 0x03, 0x2c,
> +		     0x03, 0x4a, 0x03, 0x63, 0x03, 0x8f, 0x03, 0xa2);
> +	nt_dcs_write(0xb7, 0x03, 0xb8, 0x03, 0xba);
> +	nt_dcs_write(0xb8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x2a,
> +		     0x00, 0x41, 0x00, 0x67, 0x00, 0x87, 0x00, 0xb9);
> +	nt_dcs_write(0xb9, 0x00, 0xe2, 0x01, 0x22, 0x01, 0x54, 0x01, 0xa3,
> +		     0x01, 0xe6, 0x01, 0xe7, 0x02, 0x24, 0x02, 0x67);
> +	nt_dcs_write(0xba, 0x02, 0x93, 0x02, 0xcd, 0x02, 0xf6, 0x03, 0x31,
> +		     0x03, 0x6c, 0x03, 0xe9, 0x03, 0xef, 0x03, 0xf4);
> +	nt_dcs_write(0xbb, 0x03, 0xf6, 0x03, 0xf7);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x03);
> +	nt_dcs_write(0xb0, 0x22, 0x00);
> +	nt_dcs_write(0xb1, 0x22, 0x00);
> +	nt_dcs_write(0xb2, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xb3, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xb4, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xb5, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xba, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xbb, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xbc, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xbd, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xc0, 0x00, 0x34, 0x00, 0x00);
> +	nt_dcs_write(0xc1, 0x00, 0x00, 0x34, 0x00);
> +	nt_dcs_write(0xc2, 0x00, 0x00, 0x34, 0x00);
> +	nt_dcs_write(0xc3, 0x00, 0x00, 0x34, 0x00);
> +	nt_dcs_write(0xc4, 0x60);
> +	nt_dcs_write(0xc5, 0xc0);
> +	nt_dcs_write(0xc6, 0x00);
> +	nt_dcs_write(0xc7, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x05);
> +	nt_dcs_write(0xb0, 0x17, 0x06);
> +	nt_dcs_write(0xb1, 0x17, 0x06);
> +	nt_dcs_write(0xb2, 0x17, 0x06);
> +	nt_dcs_write(0xb3, 0x17, 0x06);
> +	nt_dcs_write(0xb4, 0x17, 0x06);
> +	nt_dcs_write(0xb5, 0x17, 0x06);
> +	nt_dcs_write(0xb6, 0x17, 0x06);
> +	nt_dcs_write(0xb7, 0x17, 0x06);
> +	nt_dcs_write(0xb8, 0x00);
> +	nt_dcs_write(0xb9, 0x00, 0x03);
> +	nt_dcs_write(0xba, 0x00, 0x00);
> +	nt_dcs_write(0xbb, 0x02, 0x03);
> +	nt_dcs_write(0xbc, 0x02, 0x03);
> +	nt_dcs_write(0xbd, 0x03, 0x03, 0x00, 0x03, 0x03);
> +	nt_dcs_write(0xc0, 0x0b);
> +	nt_dcs_write(0xc1, 0x09);
> +	nt_dcs_write(0xc2, 0xa6);
> +	nt_dcs_write(0xc3, 0x05);
> +	nt_dcs_write(0xc4, 0x00);
> +	nt_dcs_write(0xc5, 0x02);
> +	nt_dcs_write(0xc6, 0x22);
> +	nt_dcs_write(0xc7, 0x03);
> +	nt_dcs_write(0xc8, 0x07, 0x20);
> +	nt_dcs_write(0xc9, 0x03, 0x20);
> +	nt_dcs_write(0xca, 0x01, 0x60);
> +	nt_dcs_write(0xcb, 0x01, 0x60);
> +	nt_dcs_write(0xcc, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xcd, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xce, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xcf, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xd1, 0x00, 0x05, 0x01, 0x07, 0x10);
> +	nt_dcs_write(0xd2, 0x10, 0x05, 0x05, 0x03, 0x10);
> +	nt_dcs_write(0xd3, 0x20, 0x00, 0x43, 0x07, 0x10);
> +	nt_dcs_write(0xd4, 0x30, 0x00, 0x43, 0x07, 0x10);
> +	nt_dcs_write(0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		     0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		     0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		     0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xe5, 0x06);
> +	nt_dcs_write(0xe6, 0x06);
> +	nt_dcs_write(0xe7, 0x00);
> +	nt_dcs_write(0xe8, 0x06);
> +	nt_dcs_write(0xe9, 0x06);
> +	nt_dcs_write(0xea, 0x06);
> +	nt_dcs_write(0xeb, 0x00);
> +	nt_dcs_write(0xec, 0x00);
> +	nt_dcs_write(0xed, 0x30);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x06);
> +	nt_dcs_write(0xb0, 0x31, 0x31);
> +	nt_dcs_write(0xb1, 0x31, 0x31);
> +	nt_dcs_write(0xb2, 0x2d, 0x2e);
> +	nt_dcs_write(0xb3, 0x31, 0x34);
> +	nt_dcs_write(0xb4, 0x29, 0x2a);
> +	nt_dcs_write(0xb5, 0x12, 0x10);
> +	nt_dcs_write(0xb6, 0x18, 0x16);
> +	nt_dcs_write(0xb7, 0x00, 0x02);
> +	nt_dcs_write(0xb8, 0x08, 0x31);
> +	nt_dcs_write(0xb9, 0x31, 0x31);
> +	nt_dcs_write(0xba, 0x31, 0x31);
> +	nt_dcs_write(0xbb, 0x31, 0x08);
> +	nt_dcs_write(0xbc, 0x03, 0x01);
> +	nt_dcs_write(0xbd, 0x17, 0x19);
> +	nt_dcs_write(0xbe, 0x11, 0x13);
> +	nt_dcs_write(0xbf, 0x2a, 0x29);
> +	nt_dcs_write(0xc0, 0x34, 0x31);
> +	nt_dcs_write(0xc1, 0x2e, 0x2d);
> +	nt_dcs_write(0xc2, 0x31, 0x31);
> +	nt_dcs_write(0xc3, 0x31, 0x31);
> +	nt_dcs_write(0xc4, 0x31, 0x31);
> +	nt_dcs_write(0xc5, 0x31, 0x31);
> +	nt_dcs_write(0xc6, 0x2e, 0x2d);
> +	nt_dcs_write(0xc7, 0x31, 0x34);
> +	nt_dcs_write(0xc8, 0x29, 0x2a);
> +	nt_dcs_write(0xc9, 0x17, 0x19);
> +	nt_dcs_write(0xca, 0x11, 0x13);
> +	nt_dcs_write(0xcb, 0x03, 0x01);
> +	nt_dcs_write(0xcc, 0x08, 0x31);
> +	nt_dcs_write(0xcd, 0x31, 0x31);
> +	nt_dcs_write(0xce, 0x31, 0x31);
> +	nt_dcs_write(0xcf, 0x31, 0x08);
> +	nt_dcs_write(0xd0, 0x00, 0x02);
> +	nt_dcs_write(0xd1, 0x12, 0x10);
> +	nt_dcs_write(0xd2, 0x18, 0x16);
> +	nt_dcs_write(0xd3, 0x2a, 0x29);
> +	nt_dcs_write(0xd4, 0x34, 0x31);
> +	nt_dcs_write(0xd5, 0x2d, 0x2e);
> +	nt_dcs_write(0xd6, 0x31, 0x31);
> +	nt_dcs_write(0xd7, 0x31, 0x31);
> +	nt_dcs_write(0xe5, 0x31, 0x31);
> +	nt_dcs_write(0xe6, 0x31, 0x31);
> +	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd9, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xe7, 0x00);
> +	nt_dcs_write(0x6f, 0x02);
> +	nt_dcs_write(0xf7, 0x47);
> +	nt_dcs_write(0x6f, 0x0a);
> +	nt_dcs_write(0xf7, 0x02);
> +	nt_dcs_write(0x6f, 0x17);
> +	nt_dcs_write(0xf4, 0x60);
> +	nt_dcs_write(0x6f, 0x01);
> +	nt_dcs_write(0xf9, 0x46);
> +	nt_dcs_write(0x6f, 0x11);
> +	nt_dcs_write(0xf3, 0x01);
> +	nt_dcs_write(0x35, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> +	nt_dcs_write(0xd9, 0x02, 0x03, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> +	nt_dcs_write(0xb1, 0x6c, 0x21);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> +	nt_dcs_write(0x35, 0x00);
> +	nt_gen_write(0x11, 0x00);
> +	msleep(120);
> +	nt_gen_write(0x29, 0x00);
> +	usleep_range(1000, 1500);
> +	nt_dcs_write(0x53, 0x24);

Did you mix up "nt_dcs_write" and "nt_gen_write" here?
The nt_gen_write(0x11, 0x00); looks like MIPI_DCS_EXIT_SLEEP_MODE
and the nt_gen_write(0x29, 0x00); looks like MIPI_DCS_SET_DISPLAY_ON.

For reference you can pull your original reference DTB from Sony through
my panel driver generator: https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator

It produces the following (I compiled "msm8939-kanuti_tulip.dtb"
from https://github.com/sonyxperiadev/kernel/tree/aosp/LA.BR.1.3.3_rb2.14,
not sure if that is right):

	// ...
	dsi_generic_write_seq(dsi, 0x35, 0x00);

	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
		return ret;
	}
	msleep(120);

	ret = mipi_dsi_dcs_set_display_on(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to set display on: %d\n", ret);
		return ret;
	}
	usleep_range(1000, 2000);

	dsi_generic_write_seq(dsi, 0x53, 0x24);

Which also suggests that generic and DCS writes are mixed up here.

Note however that you could not use the generated driver as-is,
because Sony seems to use their own display driver instead of Qualcomm's
and some things seem to be different.

> +}
> +
> +static void nt35521_panel_off(struct nt35521_panel *nt)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> +	struct device *dev = nt->dev;
> +
> +	/* Transmit data in high speed mode */
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	nt_dcs_write(0x28, 0x00);
> +	msleep(50);
> +	nt_dcs_write(0x10, 0x00);
> +	msleep(150);

A bit more deobfuscated from the panel driver generator:

	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;

	ret = mipi_dsi_dcs_set_display_off(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to set display off: %d\n", ret);
		return ret;
	}
	msleep(50);

	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
	if (ret < 0) {
		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
		return ret;
	}
	msleep(150);

(I'm mainly referring to the mipi_dsi_dcs_*() here instead of the
 magic numbers...)

> +}
> +
> +static int nt35521_prepare(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (nt->prepared)
> +		return 0;
> +
> +	gpiod_set_value_cansleep(nt->pwrp5_gpio, 1);
> +	usleep_range(1000, 1500);
> +	gpiod_set_value_cansleep(nt->pwrn5_gpio, 1);
> +	usleep_range(10000, 15000);
> +	gpiod_set_value_cansleep(nt->rst_gpio, 0);
> +	msleep(150);
> +
> +	nt35521_panel_on(nt);
> +
> +	nt->prepared = true;
> +
> +	return 0;
> +}
> +
> +static int nt35521_unprepare(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (!nt->prepared)
> +		return 0;
> +
> +	nt35521_panel_off(nt);
> +
> +	gpiod_set_value_cansleep(nt->rst_gpio, 1);
> +
> +	nt->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int nt35521_enable(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (nt->enabled)
> +		return 0;
> +
> +	gpiod_set_value_cansleep(nt->en_gpio, 1);
> +
> +	nt->enabled = true;
> +
> +	return 0;
> +}
> +
> +static int nt35521_disable(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (!nt->enabled)
> +		return 0;
> +
> +	gpiod_set_value_cansleep(nt->en_gpio, 0);
> +
> +	nt->enabled = false;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode nt35521_modes = {
> +	.clock = 133306,
> +	.hdisplay = 720,
> +	.hsync_start = 720 + 632,
> +	.hsync_end = 720 + 632 + 40,
> +	.htotal = 720 + 632 + 40 + 295,
> +	.vdisplay = 1280,
> +	.vsync_start = 1280 + 18,
> +	.vsync_end = 1280 + 18 + 1,
> +	.vtotal = 1280 + 18 + 1 + 18,
> +};
> +
> +static int nt35521_get_modes(struct drm_panel *panel,
> +			      struct drm_connector *connector)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(connector->dev, &nt35521_modes);
> +	if (!mode) {
> +		DRM_DEV_ERROR(nt->dev, "failed to add display mode\n");
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +	drm_mode_probed_add(connector, mode);
> +
> +	connector->display_info.width_mm = 65;
> +	connector->display_info.height_mm = 116;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs nt35521_drm_funcs = {
> +	.prepare = nt35521_prepare,
> +	.unprepare = nt35521_unprepare,
> +	.enable = nt35521_enable,
> +	.disable = nt35521_disable,
> +	.get_modes = nt35521_get_modes,
> +};
> +
> +static int nt35521_backlight_update_status(struct backlight_device *bd)
> +{
> +	struct nt35521_panel *nt = bl_get_data(bd);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> +	u8 brightness = bd->props.brightness;

	u8 brightness = backlight_get_brightness(bl);

Is preferred now I think(?).

> +	int ret;
> +
> +	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
> +				 &brightness,
> +				 sizeof(brightness));

Does this work too?

	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);

> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct backlight_ops nt35521_backlight_ops = {
> +	.update_status = nt35521_backlight_update_status,
> +};
> +
> +static int nt35521_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct backlight_properties props;
> +	struct device *dev = &dsi->dev;
> +	struct nt35521_panel *nt;
> +	int ret;
> +
> +	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);
> +	if (!nt)
> +		return -ENOMEM;
> +
> +	nt->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->rst_gpio)) {
> +		ret = PTR_ERR(nt->rst_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get reset GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	nt->pwrp5_gpio = devm_gpiod_get(dev, "pwr-positive5", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->pwrp5_gpio)) {
> +		ret = PTR_ERR(nt->pwrp5_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get positive5 GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	nt->pwrn5_gpio = devm_gpiod_get(dev, "pwr-negative5", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->pwrn5_gpio)) {
> +		ret = PTR_ERR(nt->pwrn5_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get negative5 GPIO: %d\n", ret);
> +		return ret;
> +	}
> +

These two GPIOs look a lot like typical 5V display regulators that would
be better described through the regulator subsystem. You can declare
them as fixed-regulator to toggle the GPIO.

> +	nt->en_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->en_gpio)) {
> +		ret = PTR_ERR(nt->en_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get enable GPIO: %d\n", ret);
> +		return ret;
> +	}

Does this refer to the "platform-bklight-en-gpio" in the Sony sources?
Perhaps calling it "backlight-gpios" or something like that would be
more clear to show that it's related to the backlight.

Thanks!
Stephan

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

* Re: [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support
  2021-08-04  8:13 ` [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support Shawn Guo
@ 2021-08-04 16:03   ` Sam Ravnborg
  2021-08-08 12:04     ` Shawn Guo
  2021-08-11 18:51   ` Rob Herring
  1 sibling, 1 reply; 13+ messages in thread
From: Sam Ravnborg @ 2021-08-04 16:03 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Thierry Reding, Daniel Vetter, Rob Herring, dri-devel,
	devicetree, linux-arm-msm

Hi Shawn,

On Wed, Aug 04, 2021 at 04:13:51PM +0800, Shawn Guo wrote:
> The Truly NT35521 is a 5.24" 1280x720 DSI panel, and the backlight is
> managed through DSI link.
> 
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>

Please consider adding an optional port node, so we can use this panels
in a setup using a graph.

A simple port: true would do the trick.
I am aware that it may not be used today, this is a preparation for
potential future use.

With this fixed,
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

	Sam

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

* Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-04  8:13 ` [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver Shawn Guo
  2021-08-04 12:09   ` Stephan Gerhold
@ 2021-08-04 16:24   ` Sam Ravnborg
  2021-08-08 13:49     ` Shawn Guo
  1 sibling, 1 reply; 13+ messages in thread
From: Sam Ravnborg @ 2021-08-04 16:24 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Thierry Reding, Daniel Vetter, Rob Herring, dri-devel,
	devicetree, linux-arm-msm

Hi Shawn,

see a few comments in the following.

	Sam

On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
> can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
> managed through DSI link.
> 
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
>  drivers/gpu/drm/panel/Kconfig               |   9 +
>  drivers/gpu/drm/panel/Makefile              |   1 +
>  drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 ++++++++++++++++++++
>  3 files changed, 501 insertions(+)
>  create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c
> 
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index ef87d92cdf49..cdc4abd5c40c 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -537,6 +537,15 @@ config DRM_PANEL_TPO_TPG110
>  	  400CH LTPS TFT LCD Single Chip Digital Driver for up to
>  	  800x400 LCD panels.
>  
> +config DRM_PANEL_TRULY_NT35521
> +	tristate "Truly NT35521 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 Truly NT35521
> +	  1280x720 DSI panel.
Here you could mention that the first use is the Sone eXperia M4 Aqua to
help the user.

> +
>  config DRM_PANEL_TRULY_NT35597_WQXGA
>  	tristate "Truly WQXGA"
>  	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index cae4d976c069..3d3c98cb7a7b 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
>  obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
>  obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
>  obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
> +obj-$(CONFIG_DRM_PANEL_TRULY_NT35521) += panel-truly-nt35521.o
>  obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
>  obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
>  obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
> diff --git a/drivers/gpu/drm/panel/panel-truly-nt35521.c b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> new file mode 100644
> index 000000000000..ea3cfb46be7e
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> @@ -0,0 +1,491 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +
> +#include <video/mipi_display.h>
> +
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_modes.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
drm_print is not used by panel drivers - this also imply that you need
to replace all uses of DRM_ERROR and friends.


> +
> +struct nt35521_panel {
> +	struct drm_panel panel;
> +	struct device *dev;
> +	struct gpio_desc *rst_gpio;
> +	struct gpio_desc *pwrp5_gpio;
> +	struct gpio_desc *pwrn5_gpio;
> +	struct gpio_desc *en_gpio;
> +	bool prepared;
> +	bool enabled;
> +};
> +
> +static inline struct nt35521_panel *panel_to_nt35521(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct nt35521_panel, panel);
> +}
> +
> +#define nt_dcs_write(seq...)						\
> +({									\
> +	const u8 d[] = { seq };						\
> +	if (mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)) < 0)	\
> +		DRM_DEV_ERROR(dev, "dcs write buffer failed\n");	\
> +})
So in case of an error the code just continues with the next write, that
likely also errors out.
Please see what is for example implemented in panel-elida-kd35t133.c
That pattern looks much saner.

> +
> +#define nt_gen_write(seq...)						\
> +({									\
> +	const u8 d[] = { seq };						\
> +	if (mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)) < 0)		\
> +		DRM_DEV_ERROR(dev, "generic write buffer failed\n");	\
> +})
For the two uses, please consider open coding this.

> +
> +static void nt35521_panel_on(struct nt35521_panel *nt)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> +	struct device *dev = nt->dev;
> +
> +	/* Transmit data in low power mode */
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> +	nt_dcs_write(0xff, 0xaa, 0x55, 0xa5, 0x80);
> +	nt_dcs_write(0x6f, 0x11, 0x00);
> +	nt_dcs_write(0xf7, 0x20, 0x00);
> +	nt_dcs_write(0x6f, 0x01);
> +	nt_dcs_write(0xb1, 0x21);
> +	nt_dcs_write(0xbd, 0x01, 0xa0, 0x10, 0x08, 0x01);
> +	nt_dcs_write(0xb8, 0x01, 0x02, 0x0c, 0x02);
> +	nt_dcs_write(0xbb, 0x11, 0x11);
> +	nt_dcs_write(0xbc, 0x00, 0x00);
> +	nt_dcs_write(0xb6, 0x02);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01);
> +	nt_dcs_write(0xb0, 0x09, 0x09);
> +	nt_dcs_write(0xb1, 0x09, 0x09);
> +	nt_dcs_write(0xbc, 0x8c, 0x00);
> +	nt_dcs_write(0xbd, 0x8c, 0x00);
> +	nt_dcs_write(0xca, 0x00);
> +	nt_dcs_write(0xc0, 0x04);
> +	nt_dcs_write(0xbe, 0xb5);
> +	nt_dcs_write(0xb3, 0x35, 0x35);
> +	nt_dcs_write(0xb4, 0x25, 0x25);
> +	nt_dcs_write(0xb9, 0x43, 0x43);
> +	nt_dcs_write(0xba, 0x24, 0x24);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x02);
> +	nt_dcs_write(0xee, 0x03);
> +	nt_dcs_write(0xb0, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00,
> +		     0xc3, 0x00, 0xce, 0x00, 0xe1, 0x00, 0xf3, 0x01,
> +		     0x11);
> +	nt_dcs_write(0xb1, 0x01, 0x2e, 0x01, 0x5c, 0x01, 0x82, 0x01,
> +		     0xc3, 0x01, 0xfe, 0x02, 0x00, 0x02, 0x37, 0x02,
> +		     0x77);
> +	nt_dcs_write(0xb2, 0x02, 0xa1, 0x02, 0xd7, 0x02, 0xfe, 0x03,
> +		     0x2c, 0x03, 0x4b, 0x03, 0x63, 0x03, 0x8f, 0x03,
> +		     0x90);
> +	nt_dcs_write(0xb3, 0x03, 0x96, 0x03, 0x98);
> +	nt_dcs_write(0xb4, 0x00, 0x81, 0x00, 0x8b, 0x00, 0x9c, 0x00,
> +		     0xa9, 0x00, 0xb5, 0x00, 0xcb, 0x00, 0xdf, 0x01,
> +		     0x02);
> +	nt_dcs_write(0xb5, 0x01, 0x1f, 0x01, 0x51, 0x01, 0x7a, 0x01,
> +		     0xbf, 0x01, 0xfa, 0x01, 0xfc, 0x02, 0x34, 0x02, 0x76);
> +	nt_dcs_write(0xb6, 0x02, 0x9f, 0x02, 0xd7, 0x02, 0xfc, 0x03, 0x2c,
> +		     0x03, 0x4a, 0x03, 0x63, 0x03, 0x8f, 0x03, 0xa2);
> +	nt_dcs_write(0xb7, 0x03, 0xb8, 0x03, 0xba);
> +	nt_dcs_write(0xb8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x2a,
> +		     0x00, 0x41, 0x00, 0x67, 0x00, 0x87, 0x00, 0xb9);
> +	nt_dcs_write(0xb9, 0x00, 0xe2, 0x01, 0x22, 0x01, 0x54, 0x01, 0xa3,
> +		     0x01, 0xe6, 0x01, 0xe7, 0x02, 0x24, 0x02, 0x67);
> +	nt_dcs_write(0xba, 0x02, 0x93, 0x02, 0xcd, 0x02, 0xf6, 0x03, 0x31,
> +		     0x03, 0x6c, 0x03, 0xe9, 0x03, 0xef, 0x03, 0xf4);
> +	nt_dcs_write(0xbb, 0x03, 0xf6, 0x03, 0xf7);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x03);
> +	nt_dcs_write(0xb0, 0x22, 0x00);
> +	nt_dcs_write(0xb1, 0x22, 0x00);
> +	nt_dcs_write(0xb2, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xb3, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xb4, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xb5, 0x05, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xba, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xbb, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xbc, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xbd, 0x53, 0x00, 0x60, 0x00, 0x00);
> +	nt_dcs_write(0xc0, 0x00, 0x34, 0x00, 0x00);
> +	nt_dcs_write(0xc1, 0x00, 0x00, 0x34, 0x00);
> +	nt_dcs_write(0xc2, 0x00, 0x00, 0x34, 0x00);
> +	nt_dcs_write(0xc3, 0x00, 0x00, 0x34, 0x00);
> +	nt_dcs_write(0xc4, 0x60);
> +	nt_dcs_write(0xc5, 0xc0);
> +	nt_dcs_write(0xc6, 0x00);
> +	nt_dcs_write(0xc7, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x05);
> +	nt_dcs_write(0xb0, 0x17, 0x06);
> +	nt_dcs_write(0xb1, 0x17, 0x06);
> +	nt_dcs_write(0xb2, 0x17, 0x06);
> +	nt_dcs_write(0xb3, 0x17, 0x06);
> +	nt_dcs_write(0xb4, 0x17, 0x06);
> +	nt_dcs_write(0xb5, 0x17, 0x06);
> +	nt_dcs_write(0xb6, 0x17, 0x06);
> +	nt_dcs_write(0xb7, 0x17, 0x06);
> +	nt_dcs_write(0xb8, 0x00);
> +	nt_dcs_write(0xb9, 0x00, 0x03);
> +	nt_dcs_write(0xba, 0x00, 0x00);
> +	nt_dcs_write(0xbb, 0x02, 0x03);
> +	nt_dcs_write(0xbc, 0x02, 0x03);
> +	nt_dcs_write(0xbd, 0x03, 0x03, 0x00, 0x03, 0x03);
> +	nt_dcs_write(0xc0, 0x0b);
> +	nt_dcs_write(0xc1, 0x09);
> +	nt_dcs_write(0xc2, 0xa6);
> +	nt_dcs_write(0xc3, 0x05);
> +	nt_dcs_write(0xc4, 0x00);
> +	nt_dcs_write(0xc5, 0x02);
> +	nt_dcs_write(0xc6, 0x22);
> +	nt_dcs_write(0xc7, 0x03);
> +	nt_dcs_write(0xc8, 0x07, 0x20);
> +	nt_dcs_write(0xc9, 0x03, 0x20);
> +	nt_dcs_write(0xca, 0x01, 0x60);
> +	nt_dcs_write(0xcb, 0x01, 0x60);
> +	nt_dcs_write(0xcc, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xcd, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xce, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xcf, 0x00, 0x00, 0x02);
> +	nt_dcs_write(0xd1, 0x00, 0x05, 0x01, 0x07, 0x10);
> +	nt_dcs_write(0xd2, 0x10, 0x05, 0x05, 0x03, 0x10);
> +	nt_dcs_write(0xd3, 0x20, 0x00, 0x43, 0x07, 0x10);
> +	nt_dcs_write(0xd4, 0x30, 0x00, 0x43, 0x07, 0x10);
> +	nt_dcs_write(0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		     0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		     0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +		     0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xe5, 0x06);
> +	nt_dcs_write(0xe6, 0x06);
> +	nt_dcs_write(0xe7, 0x00);
> +	nt_dcs_write(0xe8, 0x06);
> +	nt_dcs_write(0xe9, 0x06);
> +	nt_dcs_write(0xea, 0x06);
> +	nt_dcs_write(0xeb, 0x00);
> +	nt_dcs_write(0xec, 0x00);
> +	nt_dcs_write(0xed, 0x30);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x06);
> +	nt_dcs_write(0xb0, 0x31, 0x31);
> +	nt_dcs_write(0xb1, 0x31, 0x31);
> +	nt_dcs_write(0xb2, 0x2d, 0x2e);
> +	nt_dcs_write(0xb3, 0x31, 0x34);
> +	nt_dcs_write(0xb4, 0x29, 0x2a);
> +	nt_dcs_write(0xb5, 0x12, 0x10);
> +	nt_dcs_write(0xb6, 0x18, 0x16);
> +	nt_dcs_write(0xb7, 0x00, 0x02);
> +	nt_dcs_write(0xb8, 0x08, 0x31);
> +	nt_dcs_write(0xb9, 0x31, 0x31);
> +	nt_dcs_write(0xba, 0x31, 0x31);
> +	nt_dcs_write(0xbb, 0x31, 0x08);
> +	nt_dcs_write(0xbc, 0x03, 0x01);
> +	nt_dcs_write(0xbd, 0x17, 0x19);
> +	nt_dcs_write(0xbe, 0x11, 0x13);
> +	nt_dcs_write(0xbf, 0x2a, 0x29);
> +	nt_dcs_write(0xc0, 0x34, 0x31);
> +	nt_dcs_write(0xc1, 0x2e, 0x2d);
> +	nt_dcs_write(0xc2, 0x31, 0x31);
> +	nt_dcs_write(0xc3, 0x31, 0x31);
> +	nt_dcs_write(0xc4, 0x31, 0x31);
> +	nt_dcs_write(0xc5, 0x31, 0x31);
> +	nt_dcs_write(0xc6, 0x2e, 0x2d);
> +	nt_dcs_write(0xc7, 0x31, 0x34);
> +	nt_dcs_write(0xc8, 0x29, 0x2a);
> +	nt_dcs_write(0xc9, 0x17, 0x19);
> +	nt_dcs_write(0xca, 0x11, 0x13);
> +	nt_dcs_write(0xcb, 0x03, 0x01);
> +	nt_dcs_write(0xcc, 0x08, 0x31);
> +	nt_dcs_write(0xcd, 0x31, 0x31);
> +	nt_dcs_write(0xce, 0x31, 0x31);
> +	nt_dcs_write(0xcf, 0x31, 0x08);
> +	nt_dcs_write(0xd0, 0x00, 0x02);
> +	nt_dcs_write(0xd1, 0x12, 0x10);
> +	nt_dcs_write(0xd2, 0x18, 0x16);
> +	nt_dcs_write(0xd3, 0x2a, 0x29);
> +	nt_dcs_write(0xd4, 0x34, 0x31);
> +	nt_dcs_write(0xd5, 0x2d, 0x2e);
> +	nt_dcs_write(0xd6, 0x31, 0x31);
> +	nt_dcs_write(0xd7, 0x31, 0x31);
> +	nt_dcs_write(0xe5, 0x31, 0x31);
> +	nt_dcs_write(0xe6, 0x31, 0x31);
> +	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xd9, 0x00, 0x00, 0x00, 0x00, 0x00);
> +	nt_dcs_write(0xe7, 0x00);
> +	nt_dcs_write(0x6f, 0x02);
> +	nt_dcs_write(0xf7, 0x47);
> +	nt_dcs_write(0x6f, 0x0a);
> +	nt_dcs_write(0xf7, 0x02);
> +	nt_dcs_write(0x6f, 0x17);
> +	nt_dcs_write(0xf4, 0x60);
> +	nt_dcs_write(0x6f, 0x01);
> +	nt_dcs_write(0xf9, 0x46);
> +	nt_dcs_write(0x6f, 0x11);
> +	nt_dcs_write(0xf3, 0x01);
> +	nt_dcs_write(0x35, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> +	nt_dcs_write(0xd9, 0x02, 0x03, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> +	nt_dcs_write(0xb1, 0x6c, 0x21);
> +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> +	nt_dcs_write(0x35, 0x00);
> +	nt_gen_write(0x11, 0x00);
> +	msleep(120);
> +	nt_gen_write(0x29, 0x00);
> +	usleep_range(1000, 1500);
> +	nt_dcs_write(0x53, 0x24);
> +}
> +
> +static void nt35521_panel_off(struct nt35521_panel *nt)
> +{
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> +	struct device *dev = nt->dev;
> +
> +	/* Transmit data in high speed mode */
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	nt_dcs_write(0x28, 0x00);
This is MIPI_DCS_SET_DISPLAY_OFF
So you can use:
	mipi_dsi_dcs_set_display_off()

> +	msleep(50);
> +	nt_dcs_write(0x10, 0x00);
This is MIPI_DCS_ENTER_SLEEP_MODE
So you can use:
	mipi_dsi_dcs_enter_sleep_mode()

> +	msleep(150);
Please verify if there are other cases where thestandard helpers can be
used.

> +}
> +
> +static int nt35521_prepare(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (nt->prepared)
> +		return 0;
> +
> +	gpiod_set_value_cansleep(nt->pwrp5_gpio, 1);
> +	usleep_range(1000, 1500);
> +	gpiod_set_value_cansleep(nt->pwrn5_gpio, 1);
> +	usleep_range(10000, 15000);
> +	gpiod_set_value_cansleep(nt->rst_gpio, 0);
> +	msleep(150);
> +
> +	nt35521_panel_on(nt);
> +
> +	nt->prepared = true;
> +
> +	return 0;
> +}
> +
> +static int nt35521_unprepare(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (!nt->prepared)
> +		return 0;
> +
> +	nt35521_panel_off(nt);
> +
> +	gpiod_set_value_cansleep(nt->rst_gpio, 1);
> +
> +	nt->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int nt35521_enable(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (nt->enabled)
> +		return 0;
> +
> +	gpiod_set_value_cansleep(nt->en_gpio, 1);
> +
> +	nt->enabled = true;
> +
> +	return 0;
> +}
> +
> +static int nt35521_disable(struct drm_panel *panel)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +
> +	if (!nt->enabled)
> +		return 0;
> +
> +	gpiod_set_value_cansleep(nt->en_gpio, 0);
> +
> +	nt->enabled = false;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode nt35521_modes = {
> +	.clock = 133306,
> +	.hdisplay = 720,
> +	.hsync_start = 720 + 632,
> +	.hsync_end = 720 + 632 + 40,
> +	.htotal = 720 + 632 + 40 + 295,
> +	.vdisplay = 1280,
> +	.vsync_start = 1280 + 18,
> +	.vsync_end = 1280 + 18 + 1,
> +	.vtotal = 1280 + 18 + 1 + 18,
> +};
> +
> +static int nt35521_get_modes(struct drm_panel *panel,
> +			      struct drm_connector *connector)
> +{
> +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(connector->dev, &nt35521_modes);
> +	if (!mode) {
> +		DRM_DEV_ERROR(nt->dev, "failed to add display mode\n");
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +	drm_mode_probed_add(connector, mode);
> +
> +	connector->display_info.width_mm = 65;
> +	connector->display_info.height_mm = 116;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs nt35521_drm_funcs = {
> +	.prepare = nt35521_prepare,
> +	.unprepare = nt35521_unprepare,
> +	.enable = nt35521_enable,
> +	.disable = nt35521_disable,
> +	.get_modes = nt35521_get_modes,
> +};
> +
> +static int nt35521_backlight_update_status(struct backlight_device *bd)
> +{
> +	struct nt35521_panel *nt = bl_get_data(bd);
> +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> +	u8 brightness = bd->props.brightness;
Use backlight_get_brightness(bd);


> +	int ret;
> +
> +	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
> +				 &brightness,
> +				 sizeof(brightness));
Use mipi_dsi_dcs_set_display_brightness()

hich btw uses u16 for brightness.

> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct backlight_ops nt35521_backlight_ops = {
> +	.update_status = nt35521_backlight_update_status,
> +};
> +
> +static int nt35521_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct backlight_properties props;
> +	struct device *dev = &dsi->dev;
> +	struct nt35521_panel *nt;
> +	int ret;
> +
> +	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);
> +	if (!nt)
> +		return -ENOMEM;
> +
> +	nt->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->rst_gpio)) {
> +		ret = PTR_ERR(nt->rst_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get reset GPIO: %d\n", ret);
> +		return ret;
> +	}
In general - use dev_err_probe() all over the probe function.
See for example panel-samsung-sofef00.c

> +
> +	nt->pwrp5_gpio = devm_gpiod_get(dev, "pwr-positive5", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->pwrp5_gpio)) {
> +		ret = PTR_ERR(nt->pwrp5_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get positive5 GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	nt->pwrn5_gpio = devm_gpiod_get(dev, "pwr-negative5", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->pwrn5_gpio)) {
> +		ret = PTR_ERR(nt->pwrn5_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get negative5 GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	nt->en_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(nt->en_gpio)) {
> +		ret = PTR_ERR(nt->en_gpio);
> +		DRM_DEV_ERROR(dev, "failed to get enable GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	nt->dev = dev;
> +
> +	drm_panel_init(&nt->panel, dev, &nt35521_drm_funcs,
> +		       DRM_MODE_CONNECTOR_DSI);
> +
> +	props.max_brightness = 255;
> +	props.brightness = 240;
> +	props.type = BACKLIGHT_RAW;
Use:
       const struct backlight_properties props = {
                .type = BACKLIGHT_RAW,
                .brightness = 240,
                .max_brightness = 255,
        };
in the variable declaration.

> +
> +	nt->panel.backlight = devm_backlight_device_register(dev, dev_name(dev),
> +						dev, nt, &nt35521_backlight_ops,
> +						&props);
> +	if (IS_ERR(nt->panel.backlight)) {
> +		ret = PTR_ERR(nt->panel.backlight);
> +		DRM_DEV_ERROR(dev, "failed to register backlight: %d\n", ret);
> +		return ret;
> +	}
> +
> +	drm_panel_add(&nt->panel);
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
> +			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "failed to attach DSI device: %d\n", ret);
> +		drm_panel_remove(&nt->panel);
> +		return ret;
> +	}
> +
> +	mipi_dsi_set_drvdata(dsi, nt);
I think it is wise to set drvdata before mipi_dsi_attach.

> +
> +	return 0;
> +}
> +
> +static int nt35521_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct nt35521_panel *nt = mipi_dsi_get_drvdata(dsi);
> +
> +	mipi_dsi_detach(dsi);
> +	drm_panel_remove(&nt->panel);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id truly_nt35521_of_match[] = {
> +	{ .compatible = "truly,nt35521" },
> +	{ }
Write this as:
	{ /* sentinel */ },

just to be consistent with most panels.

> +};
> +MODULE_DEVICE_TABLE(of, truly_nt35521_of_match);
> +
> +static struct mipi_dsi_driver truly_nt35521_driver = {
> +	.driver = {
> +		.name = "panel-truly-nt35521",
> +		.of_match_table = truly_nt35521_of_match,
> +	},
> +	.probe  = nt35521_probe,
> +	.remove = nt35521_remove,
> +};
> +module_mipi_dsi_driver(truly_nt35521_driver);
> +
MODULE_AUTHOR()??

> +MODULE_DESCRIPTION("Truly NT35521 DSI panel driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.17.1

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

* Re: [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support
  2021-08-04 16:03   ` Sam Ravnborg
@ 2021-08-08 12:04     ` Shawn Guo
  0 siblings, 0 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-08 12:04 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Thierry Reding, Daniel Vetter, Rob Herring, dri-devel,
	devicetree, linux-arm-msm

On Wed, Aug 04, 2021 at 06:03:54PM +0200, Sam Ravnborg wrote:
> Hi Shawn,
> 
> On Wed, Aug 04, 2021 at 04:13:51PM +0800, Shawn Guo wrote:
> > The Truly NT35521 is a 5.24" 1280x720 DSI panel, and the backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> 
> Please consider adding an optional port node, so we can use this panels
> in a setup using a graph.

Sure, will do in v2.

> A simple port: true would do the trick.
> I am aware that it may not be used today, this is a preparation for
> potential future use.
> 
> With this fixed,
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

Thanks, Sam!

Shawn

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

* Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-04 12:09   ` Stephan Gerhold
@ 2021-08-08 13:44     ` Shawn Guo
  2021-08-08 15:29       ` Stephan Gerhold
  0 siblings, 1 reply; 13+ messages in thread
From: Shawn Guo @ 2021-08-08 13:44 UTC (permalink / raw)
  To: Stephan Gerhold, Konrad Dybcio
  Cc: Thierry Reding, Sam Ravnborg, Daniel Vetter, Rob Herring,
	dri-devel, devicetree, linux-arm-msm

Hi Stephan,

Thanks for looking at the patch!

On Wed, Aug 04, 2021 at 02:09:19PM +0200, Stephan Gerhold wrote:
> Hi Shawn,
> 
> Thanks for the patch!
> 
> On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> > It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
> > can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> > ---
> >  drivers/gpu/drm/panel/Kconfig               |   9 +
> >  drivers/gpu/drm/panel/Makefile              |   1 +
> >  drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 ++++++++++++++++++++
> >  3 files changed, 501 insertions(+)
> >  create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c
> > 
> > diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > index ef87d92cdf49..cdc4abd5c40c 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -537,6 +537,15 @@ config DRM_PANEL_TPO_TPG110
> >  	  400CH LTPS TFT LCD Single Chip Digital Driver for up to
> >  	  800x400 LCD panels.
> >  
> > +config DRM_PANEL_TRULY_NT35521
> > +	tristate "Truly NT35521 panel"
> 
> I think the name "Truly NT35521" is a bit too generic. AFAIK "Truly" is
> a panel vendor and the NovaTek NT35521 is the panel controller. But
> there are almost certainly other Truly panels that were also combined
> with a NT35521 but need a slightly different configuration.
> 
> If you don't know more than "Truly NT35521" based on the Sony sources,
> maybe do it similar to "asus,z00t-tm5p5-n35596" and use a compatible
> like "sony,<device>-truly-nt35521". Would be good to clarify the Kconfig
> option here too.

Sounds good!

> 
> > +	depends on OF
> > +	depends on DRM_MIPI_DSI
> > +	depends on BACKLIGHT_CLASS_DEVICE
> > +	help
> > +	  Say Y here if you want to enable support for Truly NT35521
> > +	  1280x720 DSI panel.
> > +
> >  config DRM_PANEL_TRULY_NT35597_WQXGA
> >  	tristate "Truly WQXGA"
> >  	depends on OF
> > diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> > index cae4d976c069..3d3c98cb7a7b 100644
> > --- a/drivers/gpu/drm/panel/Makefile
> > +++ b/drivers/gpu/drm/panel/Makefile
> > @@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
> >  obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
> >  obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
> >  obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
> > +obj-$(CONFIG_DRM_PANEL_TRULY_NT35521) += panel-truly-nt35521.o
> >  obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
> >  obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
> >  obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
> > diff --git a/drivers/gpu/drm/panel/panel-truly-nt35521.c b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> > new file mode 100644
> > index 000000000000..ea3cfb46be7e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> > @@ -0,0 +1,491 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/module.h>
> > +
> > +#include <video/mipi_display.h>
> > +
> > +#include <drm/drm_mipi_dsi.h>
> > +#include <drm/drm_modes.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +
> > +struct nt35521_panel {
> > +	struct drm_panel panel;
> > +	struct device *dev;
> > +	struct gpio_desc *rst_gpio;
> > +	struct gpio_desc *pwrp5_gpio;
> > +	struct gpio_desc *pwrn5_gpio;
> > +	struct gpio_desc *en_gpio;
> > +	bool prepared;
> > +	bool enabled;
> > +};
> > +
> > +static inline struct nt35521_panel *panel_to_nt35521(struct drm_panel *panel)
> > +{
> > +	return container_of(panel, struct nt35521_panel, panel);
> > +}
> > +
> > +#define nt_dcs_write(seq...)						\
> > +({									\
> > +	const u8 d[] = { seq };						\
> > +	if (mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)) < 0)	\
> > +		DRM_DEV_ERROR(dev, "dcs write buffer failed\n");	\
> > +})
> > +
> > +#define nt_gen_write(seq...)						\
> > +({									\
> > +	const u8 d[] = { seq };						\
> > +	if (mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)) < 0)		\
> > +		DRM_DEV_ERROR(dev, "generic write buffer failed\n");	\
> > +})
> > +
> > +static void nt35521_panel_on(struct nt35521_panel *nt)
> > +{
> > +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> > +	struct device *dev = nt->dev;
> > +
> > +	/* Transmit data in low power mode */
> > +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> > +	nt_dcs_write(0xff, 0xaa, 0x55, 0xa5, 0x80);
> > +	nt_dcs_write(0x6f, 0x11, 0x00);
> > +	nt_dcs_write(0xf7, 0x20, 0x00);
> > +	nt_dcs_write(0x6f, 0x01);
> > +	nt_dcs_write(0xb1, 0x21);
> > +	nt_dcs_write(0xbd, 0x01, 0xa0, 0x10, 0x08, 0x01);
> > +	nt_dcs_write(0xb8, 0x01, 0x02, 0x0c, 0x02);
> > +	nt_dcs_write(0xbb, 0x11, 0x11);
> > +	nt_dcs_write(0xbc, 0x00, 0x00);
> > +	nt_dcs_write(0xb6, 0x02);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01);
> > +	nt_dcs_write(0xb0, 0x09, 0x09);
> > +	nt_dcs_write(0xb1, 0x09, 0x09);
> > +	nt_dcs_write(0xbc, 0x8c, 0x00);
> > +	nt_dcs_write(0xbd, 0x8c, 0x00);
> > +	nt_dcs_write(0xca, 0x00);
> > +	nt_dcs_write(0xc0, 0x04);
> > +	nt_dcs_write(0xbe, 0xb5);
> > +	nt_dcs_write(0xb3, 0x35, 0x35);
> > +	nt_dcs_write(0xb4, 0x25, 0x25);
> > +	nt_dcs_write(0xb9, 0x43, 0x43);
> > +	nt_dcs_write(0xba, 0x24, 0x24);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x02);
> > +	nt_dcs_write(0xee, 0x03);
> > +	nt_dcs_write(0xb0, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00,
> > +		     0xc3, 0x00, 0xce, 0x00, 0xe1, 0x00, 0xf3, 0x01,
> > +		     0x11);
> > +	nt_dcs_write(0xb1, 0x01, 0x2e, 0x01, 0x5c, 0x01, 0x82, 0x01,
> > +		     0xc3, 0x01, 0xfe, 0x02, 0x00, 0x02, 0x37, 0x02,
> > +		     0x77);
> > +	nt_dcs_write(0xb2, 0x02, 0xa1, 0x02, 0xd7, 0x02, 0xfe, 0x03,
> > +		     0x2c, 0x03, 0x4b, 0x03, 0x63, 0x03, 0x8f, 0x03,
> > +		     0x90);
> > +	nt_dcs_write(0xb3, 0x03, 0x96, 0x03, 0x98);
> > +	nt_dcs_write(0xb4, 0x00, 0x81, 0x00, 0x8b, 0x00, 0x9c, 0x00,
> > +		     0xa9, 0x00, 0xb5, 0x00, 0xcb, 0x00, 0xdf, 0x01,
> > +		     0x02);
> > +	nt_dcs_write(0xb5, 0x01, 0x1f, 0x01, 0x51, 0x01, 0x7a, 0x01,
> > +		     0xbf, 0x01, 0xfa, 0x01, 0xfc, 0x02, 0x34, 0x02, 0x76);
> > +	nt_dcs_write(0xb6, 0x02, 0x9f, 0x02, 0xd7, 0x02, 0xfc, 0x03, 0x2c,
> > +		     0x03, 0x4a, 0x03, 0x63, 0x03, 0x8f, 0x03, 0xa2);
> > +	nt_dcs_write(0xb7, 0x03, 0xb8, 0x03, 0xba);
> > +	nt_dcs_write(0xb8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x2a,
> > +		     0x00, 0x41, 0x00, 0x67, 0x00, 0x87, 0x00, 0xb9);
> > +	nt_dcs_write(0xb9, 0x00, 0xe2, 0x01, 0x22, 0x01, 0x54, 0x01, 0xa3,
> > +		     0x01, 0xe6, 0x01, 0xe7, 0x02, 0x24, 0x02, 0x67);
> > +	nt_dcs_write(0xba, 0x02, 0x93, 0x02, 0xcd, 0x02, 0xf6, 0x03, 0x31,
> > +		     0x03, 0x6c, 0x03, 0xe9, 0x03, 0xef, 0x03, 0xf4);
> > +	nt_dcs_write(0xbb, 0x03, 0xf6, 0x03, 0xf7);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x03);
> > +	nt_dcs_write(0xb0, 0x22, 0x00);
> > +	nt_dcs_write(0xb1, 0x22, 0x00);
> > +	nt_dcs_write(0xb2, 0x05, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xb3, 0x05, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xb4, 0x05, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xb5, 0x05, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xba, 0x53, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xbb, 0x53, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xbc, 0x53, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xbd, 0x53, 0x00, 0x60, 0x00, 0x00);
> > +	nt_dcs_write(0xc0, 0x00, 0x34, 0x00, 0x00);
> > +	nt_dcs_write(0xc1, 0x00, 0x00, 0x34, 0x00);
> > +	nt_dcs_write(0xc2, 0x00, 0x00, 0x34, 0x00);
> > +	nt_dcs_write(0xc3, 0x00, 0x00, 0x34, 0x00);
> > +	nt_dcs_write(0xc4, 0x60);
> > +	nt_dcs_write(0xc5, 0xc0);
> > +	nt_dcs_write(0xc6, 0x00);
> > +	nt_dcs_write(0xc7, 0x00);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x05);
> > +	nt_dcs_write(0xb0, 0x17, 0x06);
> > +	nt_dcs_write(0xb1, 0x17, 0x06);
> > +	nt_dcs_write(0xb2, 0x17, 0x06);
> > +	nt_dcs_write(0xb3, 0x17, 0x06);
> > +	nt_dcs_write(0xb4, 0x17, 0x06);
> > +	nt_dcs_write(0xb5, 0x17, 0x06);
> > +	nt_dcs_write(0xb6, 0x17, 0x06);
> > +	nt_dcs_write(0xb7, 0x17, 0x06);
> > +	nt_dcs_write(0xb8, 0x00);
> > +	nt_dcs_write(0xb9, 0x00, 0x03);
> > +	nt_dcs_write(0xba, 0x00, 0x00);
> > +	nt_dcs_write(0xbb, 0x02, 0x03);
> > +	nt_dcs_write(0xbc, 0x02, 0x03);
> > +	nt_dcs_write(0xbd, 0x03, 0x03, 0x00, 0x03, 0x03);
> > +	nt_dcs_write(0xc0, 0x0b);
> > +	nt_dcs_write(0xc1, 0x09);
> > +	nt_dcs_write(0xc2, 0xa6);
> > +	nt_dcs_write(0xc3, 0x05);
> > +	nt_dcs_write(0xc4, 0x00);
> > +	nt_dcs_write(0xc5, 0x02);
> > +	nt_dcs_write(0xc6, 0x22);
> > +	nt_dcs_write(0xc7, 0x03);
> > +	nt_dcs_write(0xc8, 0x07, 0x20);
> > +	nt_dcs_write(0xc9, 0x03, 0x20);
> > +	nt_dcs_write(0xca, 0x01, 0x60);
> > +	nt_dcs_write(0xcb, 0x01, 0x60);
> > +	nt_dcs_write(0xcc, 0x00, 0x00, 0x02);
> > +	nt_dcs_write(0xcd, 0x00, 0x00, 0x02);
> > +	nt_dcs_write(0xce, 0x00, 0x00, 0x02);
> > +	nt_dcs_write(0xcf, 0x00, 0x00, 0x02);
> > +	nt_dcs_write(0xd1, 0x00, 0x05, 0x01, 0x07, 0x10);
> > +	nt_dcs_write(0xd2, 0x10, 0x05, 0x05, 0x03, 0x10);
> > +	nt_dcs_write(0xd3, 0x20, 0x00, 0x43, 0x07, 0x10);
> > +	nt_dcs_write(0xd4, 0x30, 0x00, 0x43, 0x07, 0x10);
> > +	nt_dcs_write(0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > +		     0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > +		     0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > +		     0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xe5, 0x06);
> > +	nt_dcs_write(0xe6, 0x06);
> > +	nt_dcs_write(0xe7, 0x00);
> > +	nt_dcs_write(0xe8, 0x06);
> > +	nt_dcs_write(0xe9, 0x06);
> > +	nt_dcs_write(0xea, 0x06);
> > +	nt_dcs_write(0xeb, 0x00);
> > +	nt_dcs_write(0xec, 0x00);
> > +	nt_dcs_write(0xed, 0x30);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x06);
> > +	nt_dcs_write(0xb0, 0x31, 0x31);
> > +	nt_dcs_write(0xb1, 0x31, 0x31);
> > +	nt_dcs_write(0xb2, 0x2d, 0x2e);
> > +	nt_dcs_write(0xb3, 0x31, 0x34);
> > +	nt_dcs_write(0xb4, 0x29, 0x2a);
> > +	nt_dcs_write(0xb5, 0x12, 0x10);
> > +	nt_dcs_write(0xb6, 0x18, 0x16);
> > +	nt_dcs_write(0xb7, 0x00, 0x02);
> > +	nt_dcs_write(0xb8, 0x08, 0x31);
> > +	nt_dcs_write(0xb9, 0x31, 0x31);
> > +	nt_dcs_write(0xba, 0x31, 0x31);
> > +	nt_dcs_write(0xbb, 0x31, 0x08);
> > +	nt_dcs_write(0xbc, 0x03, 0x01);
> > +	nt_dcs_write(0xbd, 0x17, 0x19);
> > +	nt_dcs_write(0xbe, 0x11, 0x13);
> > +	nt_dcs_write(0xbf, 0x2a, 0x29);
> > +	nt_dcs_write(0xc0, 0x34, 0x31);
> > +	nt_dcs_write(0xc1, 0x2e, 0x2d);
> > +	nt_dcs_write(0xc2, 0x31, 0x31);
> > +	nt_dcs_write(0xc3, 0x31, 0x31);
> > +	nt_dcs_write(0xc4, 0x31, 0x31);
> > +	nt_dcs_write(0xc5, 0x31, 0x31);
> > +	nt_dcs_write(0xc6, 0x2e, 0x2d);
> > +	nt_dcs_write(0xc7, 0x31, 0x34);
> > +	nt_dcs_write(0xc8, 0x29, 0x2a);
> > +	nt_dcs_write(0xc9, 0x17, 0x19);
> > +	nt_dcs_write(0xca, 0x11, 0x13);
> > +	nt_dcs_write(0xcb, 0x03, 0x01);
> > +	nt_dcs_write(0xcc, 0x08, 0x31);
> > +	nt_dcs_write(0xcd, 0x31, 0x31);
> > +	nt_dcs_write(0xce, 0x31, 0x31);
> > +	nt_dcs_write(0xcf, 0x31, 0x08);
> > +	nt_dcs_write(0xd0, 0x00, 0x02);
> > +	nt_dcs_write(0xd1, 0x12, 0x10);
> > +	nt_dcs_write(0xd2, 0x18, 0x16);
> > +	nt_dcs_write(0xd3, 0x2a, 0x29);
> > +	nt_dcs_write(0xd4, 0x34, 0x31);
> > +	nt_dcs_write(0xd5, 0x2d, 0x2e);
> > +	nt_dcs_write(0xd6, 0x31, 0x31);
> > +	nt_dcs_write(0xd7, 0x31, 0x31);
> > +	nt_dcs_write(0xe5, 0x31, 0x31);
> > +	nt_dcs_write(0xe6, 0x31, 0x31);
> > +	nt_dcs_write(0xd8, 0x00, 0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xd9, 0x00, 0x00, 0x00, 0x00, 0x00);
> > +	nt_dcs_write(0xe7, 0x00);
> > +	nt_dcs_write(0x6f, 0x02);
> > +	nt_dcs_write(0xf7, 0x47);
> > +	nt_dcs_write(0x6f, 0x0a);
> > +	nt_dcs_write(0xf7, 0x02);
> > +	nt_dcs_write(0x6f, 0x17);
> > +	nt_dcs_write(0xf4, 0x60);
> > +	nt_dcs_write(0x6f, 0x01);
> > +	nt_dcs_write(0xf9, 0x46);
> > +	nt_dcs_write(0x6f, 0x11);
> > +	nt_dcs_write(0xf3, 0x01);
> > +	nt_dcs_write(0x35, 0x00);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> > +	nt_dcs_write(0xd9, 0x02, 0x03, 0x00);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> > +	nt_dcs_write(0xb1, 0x6c, 0x21);
> > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> > +	nt_dcs_write(0x35, 0x00);
> > +	nt_gen_write(0x11, 0x00);
> > +	msleep(120);
> > +	nt_gen_write(0x29, 0x00);
> > +	usleep_range(1000, 1500);
> > +	nt_dcs_write(0x53, 0x24);
> 
> Did you mix up "nt_dcs_write" and "nt_gen_write" here?
> The nt_gen_write(0x11, 0x00); looks like MIPI_DCS_EXIT_SLEEP_MODE
> and the nt_gen_write(0x29, 0x00); looks like MIPI_DCS_SET_DISPLAY_ON.
> 
> For reference you can pull your original reference DTB from Sony through
> my panel driver generator: https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator

Wow, very nice!  It really deserves wider spread!

> 
> It produces the following (I compiled "msm8939-kanuti_tulip.dtb"
> from https://github.com/sonyxperiadev/kernel/tree/aosp/LA.BR.1.3.3_rb2.14,
> not sure if that is right):
> 
> 	// ...
> 	dsi_generic_write_seq(dsi, 0x35, 0x00);
> 
> 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> 	if (ret < 0) {
> 		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> 		return ret;
> 	}
> 	msleep(120);
> 
> 	ret = mipi_dsi_dcs_set_display_on(dsi);
> 	if (ret < 0) {
> 		dev_err(dev, "Failed to set display on: %d\n", ret);
> 		return ret;
> 	}
> 	usleep_range(1000, 2000);
> 
> 	dsi_generic_write_seq(dsi, 0x53, 0x24);
> 
> Which also suggests that generic and DCS writes are mixed up here.
> 
> Note however that you could not use the generated driver as-is,
> because Sony seems to use their own display driver instead of Qualcomm's
> and some things seem to be different.

I re-created the driver using your generator.  With modeling the 5v
control GPIOs with regulators and adding backlight-gpios support, the
driver works quite nicely, except the following two problems:

1) I have to drop the MIPI_DSI_MODE_LPM configuration from .update_status
   hook. Otherwise brightness did not get updated to panel.

---8<------
diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
index 31e5f942a039..eba926c6f722 100644
--- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
+++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
@@ -420,33 +420,23 @@ static int truly_nt35521_bl_update_status(struct backlight_device *bl)
        u16 brightness = backlight_get_brightness(bl);
        int ret;
 
-       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
-
        ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
        if (ret < 0)
                return ret;
 
-       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
-
        return 0;
 }
------>8---

2) The driver works good, if the kernel is launched via "fastboot boot".
   But if the kernel is flashed to eMMC and launched by bootloader with
   splash screen, kernel will fail to bring up the panel.  After kernel
   boots up, a blank & unblank cycle can get panel work though.

The problem 2) is not driver generator related.  @Konrad, did you see
it on asus-z00t-tm5p5-n35596 driver?

Shawn

> 
> > +}
> > +
> > +static void nt35521_panel_off(struct nt35521_panel *nt)
> > +{
> > +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> > +	struct device *dev = nt->dev;
> > +
> > +	/* Transmit data in high speed mode */
> > +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > +	nt_dcs_write(0x28, 0x00);
> > +	msleep(50);
> > +	nt_dcs_write(0x10, 0x00);
> > +	msleep(150);
> 
> A bit more deobfuscated from the panel driver generator:
> 
> 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> 
> 	ret = mipi_dsi_dcs_set_display_off(dsi);
> 	if (ret < 0) {
> 		dev_err(dev, "Failed to set display off: %d\n", ret);
> 		return ret;
> 	}
> 	msleep(50);
> 
> 	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> 	if (ret < 0) {
> 		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> 		return ret;
> 	}
> 	msleep(150);
> 
> (I'm mainly referring to the mipi_dsi_dcs_*() here instead of the
>  magic numbers...)
> 
> > +}
> > +
> > +static int nt35521_prepare(struct drm_panel *panel)
> > +{
> > +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> > +
> > +	if (nt->prepared)
> > +		return 0;
> > +
> > +	gpiod_set_value_cansleep(nt->pwrp5_gpio, 1);
> > +	usleep_range(1000, 1500);
> > +	gpiod_set_value_cansleep(nt->pwrn5_gpio, 1);
> > +	usleep_range(10000, 15000);
> > +	gpiod_set_value_cansleep(nt->rst_gpio, 0);
> > +	msleep(150);
> > +
> > +	nt35521_panel_on(nt);
> > +
> > +	nt->prepared = true;
> > +
> > +	return 0;
> > +}
> > +
> > +static int nt35521_unprepare(struct drm_panel *panel)
> > +{
> > +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> > +
> > +	if (!nt->prepared)
> > +		return 0;
> > +
> > +	nt35521_panel_off(nt);
> > +
> > +	gpiod_set_value_cansleep(nt->rst_gpio, 1);
> > +
> > +	nt->prepared = false;
> > +
> > +	return 0;
> > +}
> > +
> > +static int nt35521_enable(struct drm_panel *panel)
> > +{
> > +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> > +
> > +	if (nt->enabled)
> > +		return 0;
> > +
> > +	gpiod_set_value_cansleep(nt->en_gpio, 1);
> > +
> > +	nt->enabled = true;
> > +
> > +	return 0;
> > +}
> > +
> > +static int nt35521_disable(struct drm_panel *panel)
> > +{
> > +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> > +
> > +	if (!nt->enabled)
> > +		return 0;
> > +
> > +	gpiod_set_value_cansleep(nt->en_gpio, 0);
> > +
> > +	nt->enabled = false;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct drm_display_mode nt35521_modes = {
> > +	.clock = 133306,
> > +	.hdisplay = 720,
> > +	.hsync_start = 720 + 632,
> > +	.hsync_end = 720 + 632 + 40,
> > +	.htotal = 720 + 632 + 40 + 295,
> > +	.vdisplay = 1280,
> > +	.vsync_start = 1280 + 18,
> > +	.vsync_end = 1280 + 18 + 1,
> > +	.vtotal = 1280 + 18 + 1 + 18,
> > +};
> > +
> > +static int nt35521_get_modes(struct drm_panel *panel,
> > +			      struct drm_connector *connector)
> > +{
> > +	struct nt35521_panel *nt = panel_to_nt35521(panel);
> > +	struct drm_display_mode *mode;
> > +
> > +	mode = drm_mode_duplicate(connector->dev, &nt35521_modes);
> > +	if (!mode) {
> > +		DRM_DEV_ERROR(nt->dev, "failed to add display mode\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	drm_mode_set_name(mode);
> > +	drm_mode_probed_add(connector, mode);
> > +
> > +	connector->display_info.width_mm = 65;
> > +	connector->display_info.height_mm = 116;
> > +
> > +	return 1;
> > +}
> > +
> > +static const struct drm_panel_funcs nt35521_drm_funcs = {
> > +	.prepare = nt35521_prepare,
> > +	.unprepare = nt35521_unprepare,
> > +	.enable = nt35521_enable,
> > +	.disable = nt35521_disable,
> > +	.get_modes = nt35521_get_modes,
> > +};
> > +
> > +static int nt35521_backlight_update_status(struct backlight_device *bd)
> > +{
> > +	struct nt35521_panel *nt = bl_get_data(bd);
> > +	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> > +	u8 brightness = bd->props.brightness;
> 
> 	u8 brightness = backlight_get_brightness(bl);
> 
> Is preferred now I think(?).
> 
> > +	int ret;
> > +
> > +	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
> > +				 &brightness,
> > +				 sizeof(brightness));
> 
> Does this work too?
> 
> 	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
> 
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct backlight_ops nt35521_backlight_ops = {
> > +	.update_status = nt35521_backlight_update_status,
> > +};
> > +
> > +static int nt35521_probe(struct mipi_dsi_device *dsi)
> > +{
> > +	struct backlight_properties props;
> > +	struct device *dev = &dsi->dev;
> > +	struct nt35521_panel *nt;
> > +	int ret;
> > +
> > +	nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);
> > +	if (!nt)
> > +		return -ENOMEM;
> > +
> > +	nt->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> > +	if (IS_ERR(nt->rst_gpio)) {
> > +		ret = PTR_ERR(nt->rst_gpio);
> > +		DRM_DEV_ERROR(dev, "failed to get reset GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	nt->pwrp5_gpio = devm_gpiod_get(dev, "pwr-positive5", GPIOD_OUT_LOW);
> > +	if (IS_ERR(nt->pwrp5_gpio)) {
> > +		ret = PTR_ERR(nt->pwrp5_gpio);
> > +		DRM_DEV_ERROR(dev, "failed to get positive5 GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	nt->pwrn5_gpio = devm_gpiod_get(dev, "pwr-negative5", GPIOD_OUT_LOW);
> > +	if (IS_ERR(nt->pwrn5_gpio)) {
> > +		ret = PTR_ERR(nt->pwrn5_gpio);
> > +		DRM_DEV_ERROR(dev, "failed to get negative5 GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> 
> These two GPIOs look a lot like typical 5V display regulators that would
> be better described through the regulator subsystem. You can declare
> them as fixed-regulator to toggle the GPIO.
> 
> > +	nt->en_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> > +	if (IS_ERR(nt->en_gpio)) {
> > +		ret = PTR_ERR(nt->en_gpio);
> > +		DRM_DEV_ERROR(dev, "failed to get enable GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> 
> Does this refer to the "platform-bklight-en-gpio" in the Sony sources?
> Perhaps calling it "backlight-gpios" or something like that would be
> more clear to show that it's related to the backlight.
> 
> Thanks!
> Stephan

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

* Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-04 16:24   ` Sam Ravnborg
@ 2021-08-08 13:49     ` Shawn Guo
  0 siblings, 0 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-08 13:49 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Thierry Reding, Daniel Vetter, Rob Herring, dri-devel,
	devicetree, linux-arm-msm

Hi Sam,

On Wed, Aug 04, 2021 at 06:24:12PM +0200, Sam Ravnborg wrote:
> Hi Shawn,
> 
> see a few comments in the following.

Thanks for the review comments!  All of them will be addressed in v2.

Shawn

> On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> > It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
> > can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> > ---
> >  drivers/gpu/drm/panel/Kconfig               |   9 +
> >  drivers/gpu/drm/panel/Makefile              |   1 +
> >  drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 ++++++++++++++++++++
> >  3 files changed, 501 insertions(+)
> >  create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c

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

* Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-08 13:44     ` Shawn Guo
@ 2021-08-08 15:29       ` Stephan Gerhold
  2021-08-09  4:20         ` Shawn Guo
  0 siblings, 1 reply; 13+ messages in thread
From: Stephan Gerhold @ 2021-08-08 15:29 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Konrad Dybcio, Thierry Reding, Sam Ravnborg, Daniel Vetter,
	Rob Herring, dri-devel, devicetree, linux-arm-msm

On Sun, Aug 08, 2021 at 09:44:57PM +0800, Shawn Guo wrote:
> On Wed, Aug 04, 2021 at 02:09:19PM +0200, Stephan Gerhold wrote:
> > On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> > > + ...
> > > +	nt_dcs_write(0xb1, 0x6c, 0x21);
> > > +	nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> > > +	nt_dcs_write(0x35, 0x00);
> > > +	nt_gen_write(0x11, 0x00);
> > > +	msleep(120);
> > > +	nt_gen_write(0x29, 0x00);
> > > +	usleep_range(1000, 1500);
> > > +	nt_dcs_write(0x53, 0x24);
> > 
> > Did you mix up "nt_dcs_write" and "nt_gen_write" here?
> > The nt_gen_write(0x11, 0x00); looks like MIPI_DCS_EXIT_SLEEP_MODE
> > and the nt_gen_write(0x29, 0x00); looks like MIPI_DCS_SET_DISPLAY_ON.
> > 
> > For reference you can pull your original reference DTB from Sony through
> > my panel driver generator: https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator
> 
> Wow, very nice!  It really deserves wider spread!
> 
> > 
> > It produces the following (I compiled "msm8939-kanuti_tulip.dtb"
> > from https://github.com/sonyxperiadev/kernel/tree/aosp/LA.BR.1.3.3_rb2.14,
> > not sure if that is right):
> > 
> > 	// ...
> > 	dsi_generic_write_seq(dsi, 0x35, 0x00);
> > 
> > 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> > 	if (ret < 0) {
> > 		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> > 		return ret;
> > 	}
> > 	msleep(120);
> > 
> > 	ret = mipi_dsi_dcs_set_display_on(dsi);
> > 	if (ret < 0) {
> > 		dev_err(dev, "Failed to set display on: %d\n", ret);
> > 		return ret;
> > 	}
> > 	usleep_range(1000, 2000);
> > 
> > 	dsi_generic_write_seq(dsi, 0x53, 0x24);
> > 
> > Which also suggests that generic and DCS writes are mixed up here.
> > 
> > Note however that you could not use the generated driver as-is,
> > because Sony seems to use their own display driver instead of Qualcomm's
> > and some things seem to be different.
> 
> I re-created the driver using your generator.  With modeling the 5v
> control GPIOs with regulators and adding backlight-gpios support, the
> driver works quite nicely, except the following two problems:
> 
> 1) I have to drop the MIPI_DSI_MODE_LPM configuration from .update_status
>    hook. Otherwise brightness did not get updated to panel.
> 
> ---8<------
> diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
> index 31e5f942a039..eba926c6f722 100644
> --- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
> +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
> @@ -420,33 +420,23 @@ static int truly_nt35521_bl_update_status(struct backlight_device *bl)
>         u16 brightness = backlight_get_brightness(bl);
>         int ret;
>  
> -       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> -
>         ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
>         if (ret < 0)
>                 return ret;
>  
> -       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> -
>         return 0;
>  }
> ------>8---
> 

I have to admit I don't know much about Low Power Mode vs High Speed
Mode. As long it works it is good I guess :-)

> 2) The driver works good, if the kernel is launched via "fastboot boot".
>    But if the kernel is flashed to eMMC and launched by bootloader with
>    splash screen, kernel will fail to bring up the panel.  After kernel
>    boots up, a blank & unblank cycle can get panel work though.
> 
> The problem 2) is not driver generator related.  @Konrad, did you see
> it on asus-z00t-tm5p5-n35596 driver?
> 

Do you have CONFIG_DRM_MSM=y (built-in) instead of =m (module) maybe?
I think a similar issue exists on MSM8916 but it does not happen
for some reason if CONFIG_DRM_MSM=m instead of =y. Somehow having it
load later during the boot process fixes some things there.

Thanks,
Stephan

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

* Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver
  2021-08-08 15:29       ` Stephan Gerhold
@ 2021-08-09  4:20         ` Shawn Guo
  0 siblings, 0 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-09  4:20 UTC (permalink / raw)
  To: Stephan Gerhold
  Cc: Konrad Dybcio, Thierry Reding, Sam Ravnborg, Daniel Vetter,
	Rob Herring, dri-devel, devicetree, linux-arm-msm

On Sun, Aug 08, 2021 at 05:29:30PM +0200, Stephan Gerhold wrote:
> > 2) The driver works good, if the kernel is launched via "fastboot boot".
> >    But if the kernel is flashed to eMMC and launched by bootloader with
> >    splash screen, kernel will fail to bring up the panel.  After kernel
> >    boots up, a blank & unblank cycle can get panel work though.
> > 
> > The problem 2) is not driver generator related.  @Konrad, did you see
> > it on asus-z00t-tm5p5-n35596 driver?
> > 
> 
> Do you have CONFIG_DRM_MSM=y (built-in) instead of =m (module) maybe?
> I think a similar issue exists on MSM8916 but it does not happen
> for some reason if CONFIG_DRM_MSM=m instead of =y. Somehow having it
> load later during the boot process fixes some things there.

Indeed!  I have CONFIG_DRM_MSM=y in my build, and changing it to module
removes the problem.  Thanks much for the hint, Stephan!

Shawn

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

* Re: [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support
  2021-08-04  8:13 ` [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support Shawn Guo
  2021-08-04 16:03   ` Sam Ravnborg
@ 2021-08-11 18:51   ` Rob Herring
  2021-08-13 13:59     ` Shawn Guo
  1 sibling, 1 reply; 13+ messages in thread
From: Rob Herring @ 2021-08-11 18:51 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Thierry Reding, Sam Ravnborg, Daniel Vetter, dri-devel,
	devicetree, linux-arm-msm

On Wed, Aug 04, 2021 at 04:13:51PM +0800, Shawn Guo wrote:
> The Truly NT35521 is a 5.24" 1280x720 DSI panel, and the backlight is
> managed through DSI link.
> 
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
>  .../bindings/display/panel/truly,nt35521.yaml | 62 +++++++++++++++++++
>  1 file changed, 62 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml b/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
> new file mode 100644
> index 000000000000..4727c3df6eb8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
> @@ -0,0 +1,62 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/panel/truly,nt35521.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Truly NT35521 5.24" 1280x720 MIPI-DSI Panel
> +
> +maintainers:
> +  - Shawn Guo <shawn.guo@linaro.org>
> +
> +description: |
> +  The Truly NT35521 is a 5.24" 1280x720 MIPI-DSI panel.  The panel backlight
> +  is managed through DSI link.
> +
> +allOf:
> +  - $ref: panel-common.yaml#
> +
> +properties:
> +  compatible:
> +    const: truly,nt35521
> +
> +  reg: true
> +
> +  reset-gpios: true
> +
> +  enable-gpios: true
> +
> +  pwr-positive5-gpios:
> +    maxItems: 1
> +
> +  pwr-negative5-gpios:
> +    maxItems: 1

Are these +/-5V supplies? If so, they should be modeled with 
gpio-regulator perhaps unless the panel connection could only ever be 
GPIOs.

> +
> +required:
> +  - compatible
> +  - reg
> +  - reset-gpios
> +  - enable-gpios
> +  - pwr-positive5-gpios
> +  - pwr-negative5-gpios
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    dsi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        panel@0 {
> +            compatible = "truly,nt35521";
> +            reg = <0>;
> +            reset-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>;
> +            pwr-positive5-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>;
> +            pwr-negative5-gpios = <&msmgpio 17 GPIO_ACTIVE_HIGH>;
> +            enable-gpios = <&msmgpio 10 GPIO_ACTIVE_HIGH>;
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 
> 

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

* Re: [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support
  2021-08-11 18:51   ` Rob Herring
@ 2021-08-13 13:59     ` Shawn Guo
  0 siblings, 0 replies; 13+ messages in thread
From: Shawn Guo @ 2021-08-13 13:59 UTC (permalink / raw)
  To: Rob Herring
  Cc: Thierry Reding, Sam Ravnborg, Daniel Vetter, dri-devel,
	devicetree, linux-arm-msm

On Wed, Aug 11, 2021 at 12:51:56PM -0600, Rob Herring wrote:
> On Wed, Aug 04, 2021 at 04:13:51PM +0800, Shawn Guo wrote:
> > The Truly NT35521 is a 5.24" 1280x720 DSI panel, and the backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> > ---
> >  .../bindings/display/panel/truly,nt35521.yaml | 62 +++++++++++++++++++
> >  1 file changed, 62 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
> > 
> > diff --git a/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml b/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
> > new file mode 100644
> > index 000000000000..4727c3df6eb8
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/panel/truly,nt35521.yaml
> > @@ -0,0 +1,62 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/display/panel/truly,nt35521.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Truly NT35521 5.24" 1280x720 MIPI-DSI Panel
> > +
> > +maintainers:
> > +  - Shawn Guo <shawn.guo@linaro.org>
> > +
> > +description: |
> > +  The Truly NT35521 is a 5.24" 1280x720 MIPI-DSI panel.  The panel backlight
> > +  is managed through DSI link.
> > +
> > +allOf:
> > +  - $ref: panel-common.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    const: truly,nt35521
> > +
> > +  reg: true
> > +
> > +  reset-gpios: true
> > +
> > +  enable-gpios: true
> > +
> > +  pwr-positive5-gpios:
> > +    maxItems: 1
> > +
> > +  pwr-negative5-gpios:
> > +    maxItems: 1
> 
> Are these +/-5V supplies? If so, they should be modeled with 
> gpio-regulator perhaps unless the panel connection could only ever be 
> GPIOs.

Hi Rob,

The binding has been updated in v2 [1].  Please help review that.
Thanks!

Shawn

[1] https://lore.kernel.org/linux-arm-msm/20210809051008.6172-2-shawn.guo@linaro.org/T/#m587035a602b1be6c5326dcf24af01b3e8a5d2cc9

> 
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - reset-gpios
> > +  - enable-gpios
> > +  - pwr-positive5-gpios
> > +  - pwr-negative5-gpios
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/gpio/gpio.h>
> > +
> > +    dsi {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        panel@0 {
> > +            compatible = "truly,nt35521";
> > +            reg = <0>;
> > +            reset-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>;
> > +            pwr-positive5-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>;
> > +            pwr-negative5-gpios = <&msmgpio 17 GPIO_ACTIVE_HIGH>;
> > +            enable-gpios = <&msmgpio 10 GPIO_ACTIVE_HIGH>;
> > +        };
> > +    };
> > +...
> > -- 
> > 2.17.1
> > 
> > 

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

end of thread, other threads:[~2021-08-13 13:59 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-04  8:13 [PATCH 0/2] Add Truly NT35521 panel driver support Shawn Guo
2021-08-04  8:13 ` [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support Shawn Guo
2021-08-04 16:03   ` Sam Ravnborg
2021-08-08 12:04     ` Shawn Guo
2021-08-11 18:51   ` Rob Herring
2021-08-13 13:59     ` Shawn Guo
2021-08-04  8:13 ` [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver Shawn Guo
2021-08-04 12:09   ` Stephan Gerhold
2021-08-08 13:44     ` Shawn Guo
2021-08-08 15:29       ` Stephan Gerhold
2021-08-09  4:20         ` Shawn Guo
2021-08-04 16:24   ` Sam Ravnborg
2021-08-08 13:49     ` Shawn Guo

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.