All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/9] DRM panel drivers for omapdrm
@ 2019-08-13 20:10 Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 1/9] dt-bindings: Add vendor prefix for LG Display Laurent Pinchart
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

Hello everybody,

This patch series adds DT bindings and drivers for 6 panels used by
omapdrm. They are meant to replace the corresponding omapdrm-specific
drivers from drivers/gpu/drm/omapdrm/displays/ that will be removed in a
subsequent patch series, once the omapdrm driver switches fully to the
drm_panel API.

There's nothing very special here. The first three patches add DT vendor
prefixes and DT bindings. The last six patches add new panel drivers.

Please see individual patches for changelogs. Sam, all the patches have
now been acked (resulting in a TODO list in 7/9 and a rework of 8/9).
Would you merge this in drm-misc ?

The patches are based on top of drm-misc-next and can be found at

	git://linuxtv.org/pinchartl/media.git omapdrm/panels

Laurent Pinchart (9):
  dt-bindings: Add vendor prefix for LG Display
  dt-bindings: Add legacy 'toppoly' vendor prefix
  dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel
  drm/panel: Add driver for the LG Philips LB035Q02 panel
  drm/panel: Add driver for the NEC NL8048HL11 panel
  drm/panel: Add driver for the Sharp LS037V7DW01 panel
  drm/panel: Add driver for the Sony ACX565AKM panel
  drm/panel: Add driver for the Toppoly TD028TTEC1 panel
  drm/panel: Add driver for the Toppoly TD043MTEA1 panel

 .../display/panel/nec,nl8048hl11.yaml         |  62 ++
 .../devicetree/bindings/vendor-prefixes.yaml  |   5 +
 drivers/gpu/drm/panel/Kconfig                 |  46 ++
 drivers/gpu/drm/panel/Makefile                |   6 +
 drivers/gpu/drm/panel/panel-lg-lb035q02.c     | 237 ++++++
 drivers/gpu/drm/panel/panel-nec-nl8048hl11.c  | 248 +++++++
 .../gpu/drm/panel/panel-sharp-ls037v7dw01.c   | 226 ++++++
 drivers/gpu/drm/panel/panel-sony-acx565akm.c  | 701 ++++++++++++++++++
 drivers/gpu/drm/panel/panel-tpo-td028ttec1.c  | 399 ++++++++++
 drivers/gpu/drm/panel/panel-tpo-td043mtea1.c  | 509 +++++++++++++
 10 files changed, 2439 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/nec,nl8048hl11.yaml
 create mode 100644 drivers/gpu/drm/panel/panel-lg-lb035q02.c
 create mode 100644 drivers/gpu/drm/panel/panel-nec-nl8048hl11.c
 create mode 100644 drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c
 create mode 100644 drivers/gpu/drm/panel/panel-sony-acx565akm.c
 create mode 100644 drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
 create mode 100644 drivers/gpu/drm/panel/panel-tpo-td043mtea1.c

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 1/9] dt-bindings: Add vendor prefix for LG Display
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 2/9] dt-bindings: Add legacy 'toppoly' vendor prefix Laurent Pinchart
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

LG Display is an LCD display manufacturer. Originally formed as a joint
venture by LG Electronics and Philips Electronics, it was formerly known
as LG.Philips LCD, hence the DT vendor prefix lgphilips (which is
already in active use in the kernel).

More information is available at
https://en.wikipedia.org/wiki/LG_Display.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 6992bbbbffab..5efddbff2430 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -511,6 +511,8 @@ patternProperties:
     description: Lenovo Group Ltd.
   "^lg,.*":
     description: LG Corporation
+  "^lgphilips,.*":
+    description: LG Display
   "^libretech,.*":
     description: Shenzhen Libre Technology Co., Ltd
   "^licheepi,.*":
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 2/9] dt-bindings: Add legacy 'toppoly' vendor prefix
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 1/9] dt-bindings: Add vendor prefix for LG Display Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 3/9] dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel Laurent Pinchart
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

The 'toppoly' vendor prefix is in use and refers to TPO, whose DT vendor
prefix is already defined as 'tpo'. Add 'toppoly' as an alternative and
document it as legacy.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes since v1:

- Mark the prefix as deprecated
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 5efddbff2430..29dcc6f8a64a 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -935,6 +935,9 @@ patternProperties:
     description: Tecon Microprocessor Technologies, LLC.
   "^topeet,.*":
     description: Topeet
+  "^toppoly,.*":
+    description: TPO (deprecated, use tpo)
+    deprecated: true
   "^toradex,.*":
     description: Toradex AG
   "^toshiba,.*":
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 3/9] dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 1/9] dt-bindings: Add vendor prefix for LG Display Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 2/9] dt-bindings: Add legacy 'toppoly' vendor prefix Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 4/9] drm/panel: Add driver for the LG Philips LB035Q02 panel Laurent Pinchart
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

The NEC NL8048HL11 is a 10.4cm WVGA (800x480) panel with a 24-bit RGB
parallel data interface and an SPI control interface.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes since v2:

- Add reg and spi-max-frequency properties
- Make the example pass the checks

Changes since v1:

- Convert to YAML
---
 .../display/panel/nec,nl8048hl11.yaml         | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/nec,nl8048hl11.yaml

diff --git a/Documentation/devicetree/bindings/display/panel/nec,nl8048hl11.yaml b/Documentation/devicetree/bindings/display/panel/nec,nl8048hl11.yaml
new file mode 100644
index 000000000000..aa788eaa2f71
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/nec,nl8048hl11.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/nec,nl8048hl11.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NEC NL8048HL11 4.1" WVGA TFT LCD panel
+
+description:
+  The NEC NL8048HL11 is a 4.1" WVGA TFT LCD panel with a 24-bit RGB parallel
+  data interface and an SPI control interface.
+
+maintainers:
+  - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+allOf:
+  - $ref: panel-common.yaml#
+
+properties:
+  compatible:
+    const: nec,nl8048hl11
+
+  label: true
+  port: true
+  reg: true
+  reset-gpios: true
+
+  spi-max-frequency:
+    maximum: 10000000
+
+required:
+  - compatible
+  - reg
+  - reset-gpios
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    spi0 {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      lcd_panel: panel@0 {
+        compatible = "nec,nl8048hl11";
+        reg = <0>;
+        spi-max-frequency = <10000000>;
+
+        reset-gpios = <&gpio7 7 GPIO_ACTIVE_LOW>;
+
+        port {
+          lcd_in: endpoint {
+            remote-endpoint = <&dpi_out>;
+          };
+        };
+      };
+    };
+
+...
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 4/9] drm/panel: Add driver for the LG Philips LB035Q02 panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (2 preceding siblings ...)
  2019-08-13 20:10 ` [PATCH v4 3/9] dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 5/9] drm/panel: Add driver for the NEC NL8048HL11 panel Laurent Pinchart
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

This panel is used on the Gumstix Overo Palo35.

The code is based on the omapdrm-specific panel-lgphilips-lb035q02
driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
Changes since v1:

- Comments updates
- Store width_mm and height_mm in drm_display_mode
- Use drm_panel_disable() in .remove() handler
---
 drivers/gpu/drm/panel/Kconfig             |   8 +
 drivers/gpu/drm/panel/Makefile            |   1 +
 drivers/gpu/drm/panel/panel-lg-lb035q02.c | 237 ++++++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-lg-lb035q02.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index eaecd40cc32e..87cdc330672b 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -103,6 +103,14 @@ config DRM_PANEL_SAMSUNG_LD9040
 	depends on OF && SPI
 	select VIDEOMODE_HELPERS
 
+config DRM_PANEL_LG_LB035Q02
+	tristate "LG LB035Q024573 RGB panel"
+	depends on GPIOLIB && OF && SPI
+	help
+	  Say Y here if you want to enable support for the LB035Q02 RGB panel
+	  (found on the Gumstix Overo Palo35 board). To compile this driver as
+	  a module, choose M here.
+
 config DRM_PANEL_LG_LG4573
 	tristate "LG4573 RGB/SPI panel"
 	depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 62dae45f8f74..9bc6f3c5bf96 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
 obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
 obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o
+obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
 obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c
new file mode 100644
index 000000000000..694489fb91b2
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-lb035q02.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LG.Philips LB035Q02 LCD Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-lgphilips-lb035q02 driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * Based on a driver by: Steve Sakoman <steve@sakoman.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct lb035q02_device {
+	struct drm_panel panel;
+
+	struct spi_device *spi;
+	struct gpio_desc *enable_gpio;
+};
+
+#define to_lb035q02_device(p) container_of(p, struct lb035q02_device, panel)
+
+static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val)
+{
+	struct spi_message msg;
+	struct spi_transfer index_xfer = {
+		.len		= 3,
+		.cs_change	= 1,
+	};
+	struct spi_transfer value_xfer = {
+		.len		= 3,
+	};
+	u8	buffer[16];
+
+	spi_message_init(&msg);
+
+	/* register index */
+	buffer[0] = 0x70;
+	buffer[1] = 0x00;
+	buffer[2] = reg & 0x7f;
+	index_xfer.tx_buf = buffer;
+	spi_message_add_tail(&index_xfer, &msg);
+
+	/* register value */
+	buffer[4] = 0x72;
+	buffer[5] = val >> 8;
+	buffer[6] = val;
+	value_xfer.tx_buf = buffer + 4;
+	spi_message_add_tail(&value_xfer, &msg);
+
+	return spi_sync(lcd->spi, &msg);
+}
+
+static int lb035q02_init(struct lb035q02_device *lcd)
+{
+	/* Init sequence from page 28 of the lb035q02 spec. */
+	static const struct {
+		u16 index;
+		u16 value;
+	} init_data[] = {
+		{ 0x01, 0x6300 },
+		{ 0x02, 0x0200 },
+		{ 0x03, 0x0177 },
+		{ 0x04, 0x04c7 },
+		{ 0x05, 0xffc0 },
+		{ 0x06, 0xe806 },
+		{ 0x0a, 0x4008 },
+		{ 0x0b, 0x0000 },
+		{ 0x0d, 0x0030 },
+		{ 0x0e, 0x2800 },
+		{ 0x0f, 0x0000 },
+		{ 0x16, 0x9f80 },
+		{ 0x17, 0x0a0f },
+		{ 0x1e, 0x00c1 },
+		{ 0x30, 0x0300 },
+		{ 0x31, 0x0007 },
+		{ 0x32, 0x0000 },
+		{ 0x33, 0x0000 },
+		{ 0x34, 0x0707 },
+		{ 0x35, 0x0004 },
+		{ 0x36, 0x0302 },
+		{ 0x37, 0x0202 },
+		{ 0x3a, 0x0a0d },
+		{ 0x3b, 0x0806 },
+	};
+
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(init_data); ++i) {
+		ret = lb035q02_write(lcd, init_data[i].index,
+				     init_data[i].value);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int lb035q02_disable(struct drm_panel *panel)
+{
+	struct lb035q02_device *lcd = to_lb035q02_device(panel);
+
+	gpiod_set_value_cansleep(lcd->enable_gpio, 0);
+
+	return 0;
+}
+
+static int lb035q02_enable(struct drm_panel *panel)
+{
+	struct lb035q02_device *lcd = to_lb035q02_device(panel);
+
+	gpiod_set_value_cansleep(lcd->enable_gpio, 1);
+
+	return 0;
+}
+
+static const struct drm_display_mode lb035q02_mode = {
+	.clock = 6500,
+	.hdisplay = 320,
+	.hsync_start = 320 + 20,
+	.hsync_end = 320 + 20 + 2,
+	.htotal = 320 + 20 + 2 + 68,
+	.vdisplay = 240,
+	.vsync_start = 240 + 4,
+	.vsync_end = 240 + 4 + 2,
+	.vtotal = 240 + 4 + 2 + 18,
+	.vrefresh = 60,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm = 70,
+	.height_mm = 53,
+};
+
+static int lb035q02_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &lb035q02_mode);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = lb035q02_mode.width_mm;
+	connector->display_info.height_mm = lb035q02_mode.height_mm;
+	/*
+	 * FIXME: According to the datasheet pixel data is sampled on the
+	 * rising edge of the clock, but the code running on the Gumstix Overo
+	 * Palo35 indicates sampling on the negative edge. This should be
+	 * tested on a real device.
+	 */
+	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+					  | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
+					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs lb035q02_funcs = {
+	.disable = lb035q02_disable,
+	.enable = lb035q02_enable,
+	.get_modes = lb035q02_get_modes,
+};
+
+static int lb035q02_probe(struct spi_device *spi)
+{
+	struct lb035q02_device *lcd;
+	int ret;
+
+	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+	if (lcd == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, lcd);
+	lcd->spi = spi;
+
+	lcd->enable_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->enable_gpio)) {
+		dev_err(&spi->dev, "failed to parse enable gpio\n");
+		return PTR_ERR(lcd->enable_gpio);
+	}
+
+	ret = lb035q02_init(lcd);
+	if (ret < 0)
+		return ret;
+
+	drm_panel_init(&lcd->panel);
+	lcd->panel.dev = &lcd->spi->dev;
+	lcd->panel.funcs = &lb035q02_funcs;
+
+	return drm_panel_add(&lcd->panel);
+}
+
+static int lb035q02_remove(struct spi_device *spi)
+{
+	struct lb035q02_device *lcd = spi_get_drvdata(spi);
+
+	drm_panel_remove(&lcd->panel);
+	drm_panel_disable(&lcd->panel);
+
+	return 0;
+}
+
+static const struct of_device_id lb035q02_of_match[] = {
+	{ .compatible = "lgphilips,lb035q02", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, lb035q02_of_match);
+
+static struct spi_driver lb035q02_driver = {
+	.probe		= lb035q02_probe,
+	.remove		= lb035q02_remove,
+	.driver		= {
+		.name	= "panel-lg-lb035q02",
+		.of_match_table = lb035q02_of_match,
+	},
+};
+
+module_spi_driver(lb035q02_driver);
+
+MODULE_ALIAS("spi:lgphilips,lb035q02");
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
+MODULE_LICENSE("GPL");
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 5/9] drm/panel: Add driver for the NEC NL8048HL11 panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (3 preceding siblings ...)
  2019-08-13 20:10 ` [PATCH v4 4/9] drm/panel: Add driver for the LG Philips LB035Q02 panel Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 6/9] drm/panel: Add driver for the Sharp LS037V7DW01 panel Laurent Pinchart
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

This panel is used on the Zoom2/3/3630 SDP boards.

The code is based on the omapdrm-specific panel-nec-nl8048hl11 driver

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
Changes since v2:

- Call drm_panel_unprepare() in .remove() handler

Changes since v1:

- Mention boards using the panel in Kconfig
- Renamed nl8048_device to nl8048_panel
- Comments updates
- Store width_mm and height_mm in drm_display_mode
- Use drm_panel_disable() in .remove() handler
---
 drivers/gpu/drm/panel/Kconfig                |   8 +
 drivers/gpu/drm/panel/Makefile               |   1 +
 drivers/gpu/drm/panel/panel-nec-nl8048hl11.c | 248 +++++++++++++++++++
 3 files changed, 257 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-nec-nl8048hl11.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 87cdc330672b..d28133c6aa0a 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -119,6 +119,14 @@ config DRM_PANEL_LG_LG4573
 	  Say Y here if you want to enable support for LG4573 RGB panel.
 	  To compile this driver as a module, choose M here.
 
+config DRM_PANEL_NEC_NL8048HL11
+	tristate "NEC NL8048HL11 RGB panel"
+	depends on GPIOLIB && OF && SPI
+	help
+	  Say Y here if you want to enable support for the NEC NL8048HL11 RGB
+	  panel (found on the Zoom2/3/3630 SDP boards). To compile this driver
+	  as a module, choose M here.
+
 config DRM_PANEL_NOVATEK_NT39016
 	tristate "Novatek NT39016 RGB/SPI panel"
 	depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 9bc6f3c5bf96..8052f9a7ad60 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o
 obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
 obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
 obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
diff --git a/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c
new file mode 100644
index 000000000000..21bae2d0a23a
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NEC NL8048HL11 Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-nec-nl8048hl11 driver
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Erik Gilling <konkers@android.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct nl8048_panel {
+	struct drm_panel panel;
+
+	struct spi_device *spi;
+	struct gpio_desc *reset_gpio;
+};
+
+#define to_nl8048_device(p) container_of(p, struct nl8048_panel, panel)
+
+static int nl8048_write(struct nl8048_panel *lcd, unsigned char addr,
+			unsigned char value)
+{
+	u8 data[4] = { value, 0x01, addr, 0x00 };
+	int ret;
+
+	ret = spi_write(lcd->spi, data, sizeof(data));
+	if (ret)
+		dev_err(&lcd->spi->dev, "SPI write to %u failed: %d\n",
+			addr, ret);
+
+	return ret;
+}
+
+static int nl8048_init(struct nl8048_panel *lcd)
+{
+	static const struct {
+		unsigned char addr;
+		unsigned char data;
+	} nl8048_init_seq[] = {
+		{   3, 0x01 }, {   0, 0x00 }, {   1, 0x01 }, {   4, 0x00 },
+		{   5, 0x14 }, {   6, 0x24 }, {  16, 0xd7 }, {  17, 0x00 },
+		{  18, 0x00 }, {  19, 0x55 }, {  20, 0x01 }, {  21, 0x70 },
+		{  22, 0x1e }, {  23, 0x25 }, {  24, 0x25 }, {  25, 0x02 },
+		{  26, 0x02 }, {  27, 0xa0 }, {  32, 0x2f }, {  33, 0x0f },
+		{  34, 0x0f }, {  35, 0x0f }, {  36, 0x0f }, {  37, 0x0f },
+		{  38, 0x0f }, {  39, 0x00 }, {  40, 0x02 }, {  41, 0x02 },
+		{  42, 0x02 }, {  43, 0x0f }, {  44, 0x0f }, {  45, 0x0f },
+		{  46, 0x0f }, {  47, 0x0f }, {  48, 0x0f }, {  49, 0x0f },
+		{  50, 0x00 }, {  51, 0x02 }, {  52, 0x02 }, {  53, 0x02 },
+		{  80, 0x0c }, {  83, 0x42 }, {  84, 0x42 }, {  85, 0x41 },
+		{  86, 0x14 }, {  89, 0x88 }, {  90, 0x01 }, {  91, 0x00 },
+		{  92, 0x02 }, {  93, 0x0c }, {  94, 0x1c }, {  95, 0x27 },
+		{  98, 0x49 }, {  99, 0x27 }, { 102, 0x76 }, { 103, 0x27 },
+		{ 112, 0x01 }, { 113, 0x0e }, { 114, 0x02 }, { 115, 0x0c },
+		{ 118, 0x0c }, { 121, 0x30 }, { 130, 0x00 }, { 131, 0x00 },
+		{ 132, 0xfc }, { 134, 0x00 }, { 136, 0x00 }, { 138, 0x00 },
+		{ 139, 0x00 }, { 140, 0x00 }, { 141, 0xfc }, { 143, 0x00 },
+		{ 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 }, { 149, 0x00 },
+		{ 150, 0xfc }, { 152, 0x00 }, { 154, 0x00 }, { 156, 0x00 },
+		{ 157, 0x00 },
+	};
+
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(nl8048_init_seq); ++i) {
+		ret = nl8048_write(lcd, nl8048_init_seq[i].addr,
+				   nl8048_init_seq[i].data);
+		if (ret < 0)
+			return ret;
+	}
+
+	udelay(20);
+
+	return nl8048_write(lcd, 2, 0x00);
+}
+
+static int nl8048_disable(struct drm_panel *panel)
+{
+	struct nl8048_panel *lcd = to_nl8048_device(panel);
+
+	gpiod_set_value_cansleep(lcd->reset_gpio, 0);
+
+	return 0;
+}
+
+static int nl8048_enable(struct drm_panel *panel)
+{
+	struct nl8048_panel *lcd = to_nl8048_device(panel);
+
+	gpiod_set_value_cansleep(lcd->reset_gpio, 1);
+
+	return 0;
+}
+
+static const struct drm_display_mode nl8048_mode = {
+	/*  NEC PIX Clock Ratings MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz */
+	.clock	= 23800,
+	.hdisplay = 800,
+	.hsync_start = 800 + 6,
+	.hsync_end = 800 + 6 + 1,
+	.htotal = 800 + 6 + 1 + 4,
+	.vdisplay = 480,
+	.vsync_start = 480 + 3,
+	.vsync_end = 480 + 3 + 1,
+	.vtotal = 480 + 3 + 1 + 4,
+	.vrefresh = 60,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm = 89,
+	.height_mm = 53,
+};
+
+static int nl8048_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &nl8048_mode);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = nl8048_mode.width_mm;
+	connector->display_info.height_mm = nl8048_mode.height_mm;
+	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs nl8048_funcs = {
+	.disable = nl8048_disable,
+	.enable = nl8048_enable,
+	.get_modes = nl8048_get_modes,
+};
+
+static int __maybe_unused nl8048_suspend(struct device *dev)
+{
+	struct nl8048_panel *lcd = dev_get_drvdata(dev);
+
+	nl8048_write(lcd, 2, 0x01);
+	msleep(40);
+
+	return 0;
+}
+
+static int __maybe_unused nl8048_resume(struct device *dev)
+{
+	struct nl8048_panel *lcd = dev_get_drvdata(dev);
+
+	/* Reinitialize the panel. */
+	spi_setup(lcd->spi);
+	nl8048_write(lcd, 2, 0x00);
+	nl8048_init(lcd);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(nl8048_pm_ops, nl8048_suspend, nl8048_resume);
+
+static int nl8048_probe(struct spi_device *spi)
+{
+	struct nl8048_panel *lcd;
+	int ret;
+
+	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+	if (lcd == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, lcd);
+	lcd->spi = spi;
+
+	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->reset_gpio)) {
+		dev_err(&spi->dev, "failed to parse reset gpio\n");
+		return PTR_ERR(lcd->reset_gpio);
+	}
+
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 32;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
+		return ret;
+	}
+
+	ret = nl8048_init(lcd);
+	if (ret < 0)
+		return ret;
+
+	drm_panel_init(&lcd->panel);
+	lcd->panel.dev = &lcd->spi->dev;
+	lcd->panel.funcs = &nl8048_funcs;
+
+	return drm_panel_add(&lcd->panel);
+}
+
+static int nl8048_remove(struct spi_device *spi)
+{
+	struct nl8048_panel *lcd = spi_get_drvdata(spi);
+
+	drm_panel_remove(&lcd->panel);
+	drm_panel_disable(&lcd->panel);
+	drm_panel_unprepare(&lcd->panel);
+
+	return 0;
+}
+
+static const struct of_device_id nl8048_of_match[] = {
+	{ .compatible = "nec,nl8048hl11", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, nl8048_of_match);
+
+static struct spi_driver nl8048_driver = {
+	.probe		= nl8048_probe,
+	.remove		= nl8048_remove,
+	.driver		= {
+		.name	= "panel-nec-nl8048hl11",
+		.pm	= &nl8048_pm_ops,
+		.of_match_table = nl8048_of_match,
+	},
+};
+
+module_spi_driver(nl8048_driver);
+
+MODULE_ALIAS("spi:nec,nl8048hl11");
+MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
+MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
+MODULE_LICENSE("GPL");
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 6/9] drm/panel: Add driver for the Sharp LS037V7DW01 panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (4 preceding siblings ...)
  2019-08-13 20:10 ` [PATCH v4 5/9] drm/panel: Add driver for the NEC NL8048HL11 panel Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:10 ` [PATCH v4 7/9] drm/panel: Add driver for the Sony ACX565AKM panel Laurent Pinchart
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

This panel is used on the TI SDP3430 board.

The code is based on the omapdrm-specific panel-sharp-ls037v7dw01
driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
Changes since v1:

- Mention boards using the panel in Kconfig
- Renamed ls037v7dw01_device to ls037v7dw01_panel
- Renamed vcc to vdd
- Comments updates
- Store width_mm and height_mm in drm_display_mode
- Use drm_panel_disable() in .remove() handler
- Use devm_gpiod_get() where applicable
- Remove NULL-check on vdd
- Order Kconfig entries alphabetically
---
 drivers/gpu/drm/panel/Kconfig                 |   7 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 .../gpu/drm/panel/panel-sharp-ls037v7dw01.c   | 226 ++++++++++++++++++
 3 files changed, 234 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d28133c6aa0a..8d9a8cdb704e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -282,6 +282,13 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_SHARP_LS037V7DW01
+	tristate "Sharp LS037V7DW01 VGA LCD panel"
+	depends on GPIOLIB && OF && REGULATOR
+	help
+	  Say Y here if you want to enable support for Sharp LS037V7DW01 VGA
+	  (480x640) LCD panel (found on the TI SDP3430 board).
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 8052f9a7ad60..14d1c49ef3ab 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
+obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c
new file mode 100644
index 000000000000..2aaea507cc1f
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sharp LS037V7DW01 LCD Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-sharp-ls037v7dw01 driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct ls037v7dw01_panel {
+	struct drm_panel panel;
+	struct platform_device *pdev;
+
+	struct regulator *vdd;
+	struct gpio_desc *resb_gpio;	/* low = reset active min 20 us */
+	struct gpio_desc *ini_gpio;	/* high = power on */
+	struct gpio_desc *mo_gpio;	/* low = 480x640, high = 240x320 */
+	struct gpio_desc *lr_gpio;	/* high = conventional horizontal scanning */
+	struct gpio_desc *ud_gpio;	/* high = conventional vertical scanning */
+};
+
+#define to_ls037v7dw01_device(p) \
+	container_of(p, struct ls037v7dw01_panel, panel)
+
+static int ls037v7dw01_disable(struct drm_panel *panel)
+{
+	struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+
+	gpiod_set_value_cansleep(lcd->ini_gpio, 0);
+	gpiod_set_value_cansleep(lcd->resb_gpio, 0);
+
+	/* Wait at least 5 vsyncs after disabling the LCD. */
+	msleep(100);
+
+	return 0;
+}
+
+static int ls037v7dw01_unprepare(struct drm_panel *panel)
+{
+	struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+
+	regulator_disable(lcd->vdd);
+	return 0;
+}
+
+static int ls037v7dw01_prepare(struct drm_panel *panel)
+{
+	struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+	int ret;
+
+	ret = regulator_enable(lcd->vdd);
+	if (ret < 0)
+		dev_err(&lcd->pdev->dev, "%s: failed to enable regulator\n",
+			__func__);
+
+	return ret;
+}
+
+static int ls037v7dw01_enable(struct drm_panel *panel)
+{
+	struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+
+	/* Wait couple of vsyncs before enabling the LCD. */
+	msleep(50);
+
+	gpiod_set_value_cansleep(lcd->resb_gpio, 1);
+	gpiod_set_value_cansleep(lcd->ini_gpio, 1);
+
+	return 0;
+}
+
+static const struct drm_display_mode ls037v7dw01_mode = {
+	.clock = 19200,
+	.hdisplay = 480,
+	.hsync_start = 480 + 1,
+	.hsync_end = 480 + 1 + 2,
+	.htotal = 480 + 1 + 2 + 28,
+	.vdisplay = 640,
+	.vsync_start = 640 + 1,
+	.vsync_end = 640 + 1 + 1,
+	.vtotal = 640 + 1 + 1 + 1,
+	.vrefresh = 58,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm = 56,
+	.height_mm = 75,
+};
+
+static int ls037v7dw01_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &ls037v7dw01_mode);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = ls037v7dw01_mode.width_mm;
+	connector->display_info.height_mm = ls037v7dw01_mode.height_mm;
+	/*
+	 * FIXME: According to the datasheet pixel data is sampled on the
+	 * rising edge of the clock, but the code running on the SDP3430
+	 * indicates sampling on the negative edge. This should be tested on a
+	 * real device.
+	 */
+	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+					  | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
+					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs ls037v7dw01_funcs = {
+	.disable = ls037v7dw01_disable,
+	.unprepare = ls037v7dw01_unprepare,
+	.prepare = ls037v7dw01_prepare,
+	.enable = ls037v7dw01_enable,
+	.get_modes = ls037v7dw01_get_modes,
+};
+
+static int ls037v7dw01_probe(struct platform_device *pdev)
+{
+	struct ls037v7dw01_panel *lcd;
+
+	lcd = devm_kzalloc(&pdev->dev, sizeof(*lcd), GFP_KERNEL);
+	if (lcd == NULL)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, lcd);
+	lcd->pdev = pdev;
+
+	lcd->vdd = devm_regulator_get(&pdev->dev, "envdd");
+	if (IS_ERR(lcd->vdd)) {
+		dev_err(&pdev->dev, "failed to get regulator\n");
+		return PTR_ERR(lcd->vdd);
+	}
+
+	lcd->ini_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->ini_gpio)) {
+		dev_err(&pdev->dev, "failed to get enable gpio\n");
+		return PTR_ERR(lcd->ini_gpio);
+	}
+
+	lcd->resb_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->resb_gpio)) {
+		dev_err(&pdev->dev, "failed to get reset gpio\n");
+		return PTR_ERR(lcd->resb_gpio);
+	}
+
+	lcd->mo_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 0,
+					    GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->mo_gpio)) {
+		dev_err(&pdev->dev, "failed to get mode[0] gpio\n");
+		return PTR_ERR(lcd->mo_gpio);
+	}
+
+	lcd->lr_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 1,
+					    GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->lr_gpio)) {
+		dev_err(&pdev->dev, "failed to get mode[1] gpio\n");
+		return PTR_ERR(lcd->lr_gpio);
+	}
+
+	lcd->ud_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 2,
+					    GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->ud_gpio)) {
+		dev_err(&pdev->dev, "failed to get mode[2] gpio\n");
+		return PTR_ERR(lcd->ud_gpio);
+	}
+
+	drm_panel_init(&lcd->panel);
+	lcd->panel.dev = &pdev->dev;
+	lcd->panel.funcs = &ls037v7dw01_funcs;
+
+	return drm_panel_add(&lcd->panel);
+}
+
+static int ls037v7dw01_remove(struct platform_device *pdev)
+{
+	struct ls037v7dw01_panel *lcd = platform_get_drvdata(pdev);
+
+	drm_panel_remove(&lcd->panel);
+	drm_panel_disable(&lcd->panel);
+	drm_panel_unprepare(&lcd->panel);
+
+	return 0;
+}
+
+static const struct of_device_id ls037v7dw01_of_match[] = {
+	{ .compatible = "sharp,ls037v7dw01", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, ls037v7dw01_of_match);
+
+static struct platform_driver ls037v7dw01_driver = {
+	.probe		= ls037v7dw01_probe,
+	.remove		= __exit_p(ls037v7dw01_remove),
+	.driver		= {
+		.name = "panel-sharp-ls037v7dw01",
+		.of_match_table = ls037v7dw01_of_match,
+	},
+};
+
+module_platform_driver(ls037v7dw01_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
+MODULE_LICENSE("GPL");
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 7/9] drm/panel: Add driver for the Sony ACX565AKM panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (5 preceding siblings ...)
  2019-08-13 20:10 ` [PATCH v4 6/9] drm/panel: Add driver for the Sharp LS037V7DW01 panel Laurent Pinchart
@ 2019-08-13 20:10 ` Laurent Pinchart
  2019-08-13 20:11 ` [PATCH v4 8/9] drm/panel: Add driver for the Toppoly TD028TTEC1 panel Laurent Pinchart
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

This panel is used on the Nokia N900.

The code is based on the omapdrm-specific panel-sony-acx565akm driver.
The hardware-related logic has been changed as little as possible to
avoid regressions as hardware availability is lacking to test the
changes. Follow-up patches should address the items listed in the TODO
list.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
Changes since v3:

- Add TODO

Changes since v2:

- Call drm_panel_unprepare() in .remove() handler

Changes since v1:

- Mention boards using the panel in Kconfig
- Renamed acx565akm_device to acx565akm_panel
- Comments updates
- Store width_mm and height_mm in drm_display_mode
- Use drm_panel_disable() in .remove() handler
---
 drivers/gpu/drm/panel/Kconfig                |   8 +
 drivers/gpu/drm/panel/Makefile               |   1 +
 drivers/gpu/drm/panel/panel-sony-acx565akm.c | 701 +++++++++++++++++++
 3 files changed, 710 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-sony-acx565akm.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 8d9a8cdb704e..b05649b3118a 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -316,6 +316,14 @@ config DRM_PANEL_SITRONIX_ST7789V
 	  Say Y here if you want to enable support for the Sitronix
 	  ST7789V controller for 240x320 LCD panels
 
+config DRM_PANEL_SONY_ACX565AKM
+	tristate "Sony ACX565AKM panel"
+	depends on GPIOLIB && OF && SPI
+	depends on BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y here if you want to enable support for the Sony ACX565AKM
+	  800x600 3.5" panel (found on the Nokia N900).
+
 config DRM_PANEL_TPO_TPG110
 	tristate "TPO TPG 800x400 panel"
 	depends on OF && SPI && GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 14d1c49ef3ab..28cf2332fd06 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -33,5 +33,6 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
+obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
 obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
diff --git a/drivers/gpu/drm/panel/panel-sony-acx565akm.c b/drivers/gpu/drm/panel/panel-sony-acx565akm.c
new file mode 100644
index 000000000000..28d900d60f2d
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sony-acx565akm.c
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sony ACX565AKM LCD Panel driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-sony-acx565akm driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ */
+
+/*
+ * TODO (to be addressed with hardware access to test the changes):
+ *
+ * - Update backlight support to use backlight_update_status() etc.
+ * - Use prepare/unprepare for the basic power on/off of the backligt
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define CTRL_DISP_BRIGHTNESS_CTRL_ON		(1 << 5)
+#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON		(1 << 4)
+#define CTRL_DISP_BACKLIGHT_ON			(1 << 2)
+#define CTRL_DISP_AUTO_BRIGHTNESS_ON		(1 << 1)
+
+#define MIPID_CMD_WRITE_CABC		0x55
+#define MIPID_CMD_READ_CABC		0x56
+
+#define MIPID_VER_LPH8923		3
+#define MIPID_VER_LS041Y3		4
+#define MIPID_VER_L4F00311		8
+#define MIPID_VER_ACX565AKM		9
+
+struct acx565akm_panel {
+	struct drm_panel panel;
+
+	struct spi_device *spi;
+	struct gpio_desc *reset_gpio;
+	struct backlight_device *backlight;
+
+	struct mutex mutex;
+
+	const char *name;
+	u8 display_id[3];
+	int model;
+	int revision;
+	bool has_bc;
+	bool has_cabc;
+
+	bool enabled;
+	unsigned int cabc_mode;
+	/*
+	 * Next value of jiffies when we can issue the next sleep in/out
+	 * command.
+	 */
+	unsigned long hw_guard_end;
+	unsigned long hw_guard_wait;		/* max guard time in jiffies */
+};
+
+#define to_acx565akm_device(p) container_of(p, struct acx565akm_panel, panel)
+
+static void acx565akm_transfer(struct acx565akm_panel *lcd, int cmd,
+			      const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+	struct spi_message	m;
+	struct spi_transfer	*x, xfer[5];
+	int			ret;
+
+	spi_message_init(&m);
+
+	memset(xfer, 0, sizeof(xfer));
+	x = &xfer[0];
+
+	cmd &=  0xff;
+	x->tx_buf = &cmd;
+	x->bits_per_word = 9;
+	x->len = 2;
+
+	if (rlen > 1 && wlen == 0) {
+		/*
+		 * Between the command and the response data there is a
+		 * dummy clock cycle. Add an extra bit after the command
+		 * word to account for this.
+		 */
+		x->bits_per_word = 10;
+		cmd <<= 1;
+	}
+	spi_message_add_tail(x, &m);
+
+	if (wlen) {
+		x++;
+		x->tx_buf = wbuf;
+		x->len = wlen;
+		x->bits_per_word = 9;
+		spi_message_add_tail(x, &m);
+	}
+
+	if (rlen) {
+		x++;
+		x->rx_buf	= rbuf;
+		x->len		= rlen;
+		spi_message_add_tail(x, &m);
+	}
+
+	ret = spi_sync(lcd->spi, &m);
+	if (ret < 0)
+		dev_dbg(&lcd->spi->dev, "spi_sync %d\n", ret);
+}
+
+static inline void acx565akm_cmd(struct acx565akm_panel *lcd, int cmd)
+{
+	acx565akm_transfer(lcd, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void acx565akm_write(struct acx565akm_panel *lcd,
+			       int reg, const u8 *buf, int len)
+{
+	acx565akm_transfer(lcd, reg, buf, len, NULL, 0);
+}
+
+static inline void acx565akm_read(struct acx565akm_panel *lcd,
+			      int reg, u8 *buf, int len)
+{
+	acx565akm_transfer(lcd, reg, NULL, 0, buf, len);
+}
+
+/* -----------------------------------------------------------------------------
+ * Auto Brightness Control Via sysfs
+ */
+
+static unsigned int acx565akm_get_cabc_mode(struct acx565akm_panel *lcd)
+{
+	return lcd->cabc_mode;
+}
+
+static void acx565akm_set_cabc_mode(struct acx565akm_panel *lcd,
+				    unsigned int mode)
+{
+	u16 cabc_ctrl;
+
+	lcd->cabc_mode = mode;
+	if (!lcd->enabled)
+		return;
+	cabc_ctrl = 0;
+	acx565akm_read(lcd, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
+	cabc_ctrl &= ~3;
+	cabc_ctrl |= (1 << 8) | (mode & 3);
+	acx565akm_write(lcd, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
+}
+
+static unsigned int acx565akm_get_hw_cabc_mode(struct acx565akm_panel *lcd)
+{
+	u8 cabc_ctrl;
+
+	acx565akm_read(lcd, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
+	return cabc_ctrl & 3;
+}
+
+static const char * const acx565akm_cabc_modes[] = {
+	"off",		/* always used when CABC is not supported */
+	"ui",
+	"still-image",
+	"moving-image",
+};
+
+static ssize_t cabc_mode_show(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	struct acx565akm_panel *lcd = dev_get_drvdata(dev);
+	const char *mode_str;
+	int mode;
+
+	if (!lcd->has_cabc)
+		mode = 0;
+	else
+		mode = acx565akm_get_cabc_mode(lcd);
+
+	mode_str = "unknown";
+	if (mode >= 0 && mode < ARRAY_SIZE(acx565akm_cabc_modes))
+		mode_str = acx565akm_cabc_modes[mode];
+
+	return sprintf(buf, "%s\n", mode_str);
+}
+
+static ssize_t cabc_mode_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct acx565akm_panel *lcd = dev_get_drvdata(dev);
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++) {
+		const char *mode_str = acx565akm_cabc_modes[i];
+		int cmp_len = strlen(mode_str);
+
+		if (count > 0 && buf[count - 1] == '\n')
+			count--;
+		if (count != cmp_len)
+			continue;
+
+		if (strncmp(buf, mode_str, cmp_len) == 0)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(acx565akm_cabc_modes))
+		return -EINVAL;
+
+	if (!lcd->has_cabc && i != 0)
+		return -EINVAL;
+
+	mutex_lock(&lcd->mutex);
+	acx565akm_set_cabc_mode(lcd, i);
+	mutex_unlock(&lcd->mutex);
+
+	return count;
+}
+
+static ssize_t cabc_available_modes_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct acx565akm_panel *lcd = dev_get_drvdata(dev);
+	unsigned int i;
+	size_t len = 0;
+
+	if (!lcd->has_cabc)
+		return sprintf(buf, "%s\n", acx565akm_cabc_modes[0]);
+
+	for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++)
+		len += sprintf(&buf[len], "%s%s", i ? " " : "",
+			       acx565akm_cabc_modes[i]);
+
+	buf[len++] = '\n';
+
+	return len;
+}
+
+static DEVICE_ATTR_RW(cabc_mode);
+static DEVICE_ATTR_RO(cabc_available_modes);
+
+static struct attribute *acx565akm_cabc_attrs[] = {
+	&dev_attr_cabc_mode.attr,
+	&dev_attr_cabc_available_modes.attr,
+	NULL,
+};
+
+static const struct attribute_group acx565akm_cabc_attr_group = {
+	.attrs = acx565akm_cabc_attrs,
+};
+
+/* -----------------------------------------------------------------------------
+ * Backlight Device
+ */
+
+static int acx565akm_get_actual_brightness(struct acx565akm_panel *lcd)
+{
+	u8 bv;
+
+	acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, &bv, 1);
+
+	return bv;
+}
+
+static void acx565akm_set_brightness(struct acx565akm_panel *lcd, int level)
+{
+	u16 ctrl;
+	int bv;
+
+	bv = level | (1 << 8);
+	acx565akm_write(lcd, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, (u8 *)&bv, 2);
+
+	acx565akm_read(lcd, MIPI_DCS_GET_CONTROL_DISPLAY, (u8 *)&ctrl, 1);
+	if (level)
+		ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
+			CTRL_DISP_BACKLIGHT_ON;
+	else
+		ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
+			  CTRL_DISP_BACKLIGHT_ON);
+
+	ctrl |= 1 << 8;
+	acx565akm_write(lcd, MIPI_DCS_WRITE_CONTROL_DISPLAY, (u8 *)&ctrl, 2);
+}
+
+static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
+{
+	struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
+	int level;
+
+	if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+	    dev->props.power == FB_BLANK_UNBLANK)
+		level = dev->props.brightness;
+	else
+		level = 0;
+
+	acx565akm_set_brightness(lcd, level);
+
+	return 0;
+}
+
+static int acx565akm_bl_update_status(struct backlight_device *dev)
+{
+	struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
+	int ret;
+
+	mutex_lock(&lcd->mutex);
+	ret = acx565akm_bl_update_status_locked(dev);
+	mutex_unlock(&lcd->mutex);
+
+	return ret;
+}
+
+static int acx565akm_bl_get_intensity(struct backlight_device *dev)
+{
+	struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
+	unsigned int intensity;
+
+	mutex_lock(&lcd->mutex);
+
+	if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+	    dev->props.power == FB_BLANK_UNBLANK)
+		intensity = acx565akm_get_actual_brightness(lcd);
+	else
+		intensity = 0;
+
+	mutex_unlock(&lcd->mutex);
+
+	return intensity;
+}
+
+static const struct backlight_ops acx565akm_bl_ops = {
+	.get_brightness = acx565akm_bl_get_intensity,
+	.update_status  = acx565akm_bl_update_status,
+};
+
+static int acx565akm_backlight_init(struct acx565akm_panel *lcd)
+{
+	struct backlight_properties props = {
+		.fb_blank = FB_BLANK_UNBLANK,
+		.power = FB_BLANK_UNBLANK,
+		.type = BACKLIGHT_RAW,
+	};
+	int ret;
+
+	lcd->backlight = backlight_device_register(lcd->name, &lcd->spi->dev,
+						   lcd, &acx565akm_bl_ops,
+						   &props);
+	if (IS_ERR(lcd->backlight)) {
+		ret = PTR_ERR(lcd->backlight);
+		lcd->backlight = NULL;
+		return ret;
+	}
+
+	if (lcd->has_cabc) {
+		ret = sysfs_create_group(&lcd->backlight->dev.kobj,
+					 &acx565akm_cabc_attr_group);
+		if (ret < 0) {
+			dev_err(&lcd->spi->dev,
+				"%s failed to create sysfs files\n", __func__);
+			backlight_device_unregister(lcd->backlight);
+			return ret;
+		}
+
+		lcd->cabc_mode = acx565akm_get_hw_cabc_mode(lcd);
+	}
+
+	lcd->backlight->props.max_brightness = 255;
+	lcd->backlight->props.brightness = acx565akm_get_actual_brightness(lcd);
+
+	acx565akm_bl_update_status_locked(lcd->backlight);
+
+	return 0;
+}
+
+static void acx565akm_backlight_cleanup(struct acx565akm_panel *lcd)
+{
+	if (lcd->has_cabc)
+		sysfs_remove_group(&lcd->backlight->dev.kobj,
+				   &acx565akm_cabc_attr_group);
+
+	backlight_device_unregister(lcd->backlight);
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
+static void acx565akm_set_sleep_mode(struct acx565akm_panel *lcd, int on)
+{
+	int cmd = on ? MIPI_DCS_ENTER_SLEEP_MODE : MIPI_DCS_EXIT_SLEEP_MODE;
+	unsigned long wait;
+
+	/*
+	 * We have to keep 120msec between sleep in/out commands.
+	 * (8.2.15, 8.2.16).
+	 */
+	wait = lcd->hw_guard_end - jiffies;
+	if ((long)wait > 0 && wait <= lcd->hw_guard_wait) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(wait);
+	}
+
+	acx565akm_cmd(lcd, cmd);
+
+	lcd->hw_guard_wait = msecs_to_jiffies(120);
+	lcd->hw_guard_end = jiffies + lcd->hw_guard_wait;
+}
+
+static void acx565akm_set_display_state(struct acx565akm_panel *lcd,
+					int enabled)
+{
+	int cmd = enabled ? MIPI_DCS_SET_DISPLAY_ON : MIPI_DCS_SET_DISPLAY_OFF;
+
+	acx565akm_cmd(lcd, cmd);
+}
+
+static int acx565akm_power_on(struct acx565akm_panel *lcd)
+{
+	/*FIXME tweak me */
+	msleep(50);
+
+	gpiod_set_value(lcd->reset_gpio, 1);
+
+	if (lcd->enabled) {
+		dev_dbg(&lcd->spi->dev, "panel already enabled\n");
+		return 0;
+	}
+
+	/*
+	 * We have to meet all the following delay requirements:
+	 * 1. tRW: reset pulse width 10usec (7.12.1)
+	 * 2. tRT: reset cancel time 5msec (7.12.1)
+	 * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
+	 *    case (7.6.2)
+	 * 4. 120msec before the sleep out command (7.12.1)
+	 */
+	msleep(120);
+
+	acx565akm_set_sleep_mode(lcd, 0);
+	lcd->enabled = true;
+
+	/* 5msec between sleep out and the next command. (8.2.16) */
+	usleep_range(5000, 10000);
+	acx565akm_set_display_state(lcd, 1);
+	acx565akm_set_cabc_mode(lcd, lcd->cabc_mode);
+
+	return acx565akm_bl_update_status_locked(lcd->backlight);
+}
+
+static void acx565akm_power_off(struct acx565akm_panel *lcd)
+{
+	if (!lcd->enabled)
+		return;
+
+	acx565akm_set_display_state(lcd, 0);
+	acx565akm_set_sleep_mode(lcd, 1);
+	lcd->enabled = false;
+	/*
+	 * We have to provide PCLK,HS,VS signals for 2 frames (worst case
+	 * ~50msec) after sending the sleep in command and asserting the
+	 * reset signal. We probably could assert the reset w/o the delay
+	 * but we still delay to avoid possible artifacts. (7.6.1)
+	 */
+	msleep(50);
+
+	gpiod_set_value(lcd->reset_gpio, 0);
+
+	/* FIXME need to tweak this delay */
+	msleep(100);
+}
+
+static int acx565akm_disable(struct drm_panel *panel)
+{
+	struct acx565akm_panel *lcd = to_acx565akm_device(panel);
+
+	mutex_lock(&lcd->mutex);
+	acx565akm_power_off(lcd);
+	mutex_unlock(&lcd->mutex);
+
+	return 0;
+}
+
+static int acx565akm_enable(struct drm_panel *panel)
+{
+	struct acx565akm_panel *lcd = to_acx565akm_device(panel);
+
+	mutex_lock(&lcd->mutex);
+	acx565akm_power_on(lcd);
+	mutex_unlock(&lcd->mutex);
+
+	return 0;
+}
+
+static const struct drm_display_mode acx565akm_mode = {
+	.clock = 24000,
+	.hdisplay = 800,
+	.hsync_start = 800 + 28,
+	.hsync_end = 800 + 28 + 4,
+	.htotal = 800 + 28 + 4 + 24,
+	.vdisplay = 480,
+	.vsync_start = 480 + 3,
+	.vsync_end = 480 + 3 + 3,
+	.vtotal = 480 + 3 + 3 + 4,
+	.vrefresh = 57,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm = 77,
+	.height_mm = 46,
+};
+
+static int acx565akm_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &acx565akm_mode);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = acx565akm_mode.width_mm;
+	connector->display_info.height_mm = acx565akm_mode.height_mm;
+	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+					  | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
+					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs acx565akm_funcs = {
+	.disable = acx565akm_disable,
+	.enable = acx565akm_enable,
+	.get_modes = acx565akm_get_modes,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe, Detect and Remove
+ */
+
+static int acx565akm_detect(struct acx565akm_panel *lcd)
+{
+	__be32 value;
+	u32 status;
+	int ret = 0;
+
+	/*
+	 * After being taken out of reset the panel needs 5ms before the first
+	 * command can be sent.
+	 */
+	gpiod_set_value(lcd->reset_gpio, 1);
+	usleep_range(5000, 10000);
+
+	acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_STATUS, (u8 *)&value, 4);
+	status = __be32_to_cpu(value);
+	lcd->enabled = (status & (1 << 17)) && (status & (1 << 10));
+
+	dev_dbg(&lcd->spi->dev,
+		"LCD panel %s by bootloader (status 0x%04x)\n",
+		lcd->enabled ? "enabled" : "disabled ", status);
+
+	acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_ID, lcd->display_id, 3);
+	dev_dbg(&lcd->spi->dev, "MIPI display ID: %02x%02x%02x\n",
+		lcd->display_id[0], lcd->display_id[1], lcd->display_id[2]);
+
+	switch (lcd->display_id[0]) {
+	case 0x10:
+		lcd->model = MIPID_VER_ACX565AKM;
+		lcd->name = "acx565akm";
+		lcd->has_bc = 1;
+		lcd->has_cabc = 1;
+		break;
+	case 0x29:
+		lcd->model = MIPID_VER_L4F00311;
+		lcd->name = "l4f00311";
+		break;
+	case 0x45:
+		lcd->model = MIPID_VER_LPH8923;
+		lcd->name = "lph8923";
+		break;
+	case 0x83:
+		lcd->model = MIPID_VER_LS041Y3;
+		lcd->name = "ls041y3";
+		break;
+	default:
+		lcd->name = "unknown";
+		dev_err(&lcd->spi->dev, "unknown display ID\n");
+		ret = -ENODEV;
+		goto done;
+	}
+
+	lcd->revision = lcd->display_id[1];
+
+	dev_info(&lcd->spi->dev, "%s rev %02x panel detected\n",
+		 lcd->name, lcd->revision);
+
+done:
+	if (!lcd->enabled)
+		gpiod_set_value(lcd->reset_gpio, 0);
+
+	return ret;
+}
+
+static int acx565akm_probe(struct spi_device *spi)
+{
+	struct acx565akm_panel *lcd;
+	int ret;
+
+	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+	if (lcd == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, lcd);
+	spi->mode = SPI_MODE_3;
+
+	lcd->spi = spi;
+	mutex_init(&lcd->mutex);
+
+	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(lcd->reset_gpio)) {
+		dev_err(&spi->dev, "failed to get reset GPIO\n");
+		return PTR_ERR(lcd->reset_gpio);
+	}
+
+	ret = acx565akm_detect(lcd);
+	if (ret < 0) {
+		dev_err(&spi->dev, "panel detection failed\n");
+		return ret;
+	}
+
+	if (lcd->has_bc) {
+		ret = acx565akm_backlight_init(lcd);
+		if (ret < 0)
+			return ret;
+	}
+
+	drm_panel_init(&lcd->panel);
+	lcd->panel.dev = &lcd->spi->dev;
+	lcd->panel.funcs = &acx565akm_funcs;
+
+	ret = drm_panel_add(&lcd->panel);
+	if (ret < 0) {
+		if (lcd->has_bc)
+			acx565akm_backlight_cleanup(lcd);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int acx565akm_remove(struct spi_device *spi)
+{
+	struct acx565akm_panel *lcd = spi_get_drvdata(spi);
+
+	drm_panel_remove(&lcd->panel);
+
+	if (lcd->has_bc)
+		acx565akm_backlight_cleanup(lcd);
+
+	drm_panel_disable(&lcd->panel);
+	drm_panel_unprepare(&lcd->panel);
+
+	return 0;
+}
+
+static const struct of_device_id acx565akm_of_match[] = {
+	{ .compatible = "sony,acx565akm", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, acx565akm_of_match);
+
+static struct spi_driver acx565akm_driver = {
+	.probe		= acx565akm_probe,
+	.remove		= acx565akm_remove,
+	.driver		= {
+		.name	= "panel-sony-acx565akm",
+		.of_match_table = acx565akm_of_match,
+	},
+};
+
+module_spi_driver(acx565akm_driver);
+
+MODULE_ALIAS("spi:sony,acx565akm");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("Sony ACX565AKM LCD Panel Driver");
+MODULE_LICENSE("GPL");
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 8/9] drm/panel: Add driver for the Toppoly TD028TTEC1 panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (6 preceding siblings ...)
  2019-08-13 20:10 ` [PATCH v4 7/9] drm/panel: Add driver for the Sony ACX565AKM panel Laurent Pinchart
@ 2019-08-13 20:11 ` Laurent Pinchart
  2019-08-13 20:11 ` [PATCH v4 9/9] drm/panel: Add driver for the Toppoly TD043MTEA1 panel Laurent Pinchart
  2019-08-14 16:44 ` [PATCH v4 0/9] DRM panel drivers for omapdrm Sam Ravnborg
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:11 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

This panel is used on the OpenMoko Neo FreeRunner and Neo 1973.

The code is based on the omapdrm-specific panel-tpo-td028ttec1 driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
Changes since v3:

- Split enable/disable in prepare/enable/disable/unprepare

Changes since v2:

- Call drm_panel_unprepare() in .remove() handler

Changes since v1:

- Mention boards using the panel in Kconfig
- Renamed td028ttec1_device to td028ttec1_panel
- Comments updates
- Removed unneeded ret check
- Store width_mm and height_mm in drm_display_mode
- Use drm_panel_disable() in .remove() handler
---
 drivers/gpu/drm/panel/Kconfig                |   8 +
 drivers/gpu/drm/panel/Makefile               |   1 +
 drivers/gpu/drm/panel/panel-tpo-td028ttec1.c | 399 +++++++++++++++++++
 3 files changed, 408 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-tpo-td028ttec1.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index b05649b3118a..c2689420d2dd 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -324,6 +324,14 @@ config DRM_PANEL_SONY_ACX565AKM
 	  Say Y here if you want to enable support for the Sony ACX565AKM
 	  800x600 3.5" panel (found on the Nokia N900).
 
+config DRM_PANEL_TPO_TD028TTEC1
+	tristate "Toppoly (TPO) TD028TTEC1 panel driver"
+	depends on OF && SPI
+	depends on BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y here if you want to enable support for TPO TD028TTEC1 480x640
+	  2.8" panel (found on the OpenMoko Neo FreeRunner and Neo 1973).
+
 config DRM_PANEL_TPO_TPG110
 	tristate "TPO TPG 800x400 panel"
 	depends on OF && SPI && GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 28cf2332fd06..27b776737ad3 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -34,5 +34,6 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
+obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
 obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
new file mode 100644
index 000000000000..a400c83486b4
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Toppoly TD028TTEC1 Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-tpo-td028ttec1 driver
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * Neo 1973 code (jbt6k74.c):
+ * Copyright (C) 2006-2007 OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * Ported and adapted from Neo 1973 U-Boot by:
+ * H. Nikolaus Schaller <hns@goldelico.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define JBT_COMMAND			0x000
+#define JBT_DATA			0x100
+
+#define JBT_REG_SLEEP_IN		0x10
+#define JBT_REG_SLEEP_OUT		0x11
+
+#define JBT_REG_DISPLAY_OFF		0x28
+#define JBT_REG_DISPLAY_ON		0x29
+
+#define JBT_REG_RGB_FORMAT		0x3a
+#define JBT_REG_QUAD_RATE		0x3b
+
+#define JBT_REG_POWER_ON_OFF		0xb0
+#define JBT_REG_BOOSTER_OP		0xb1
+#define JBT_REG_BOOSTER_MODE		0xb2
+#define JBT_REG_BOOSTER_FREQ		0xb3
+#define JBT_REG_OPAMP_SYSCLK		0xb4
+#define JBT_REG_VSC_VOLTAGE		0xb5
+#define JBT_REG_VCOM_VOLTAGE		0xb6
+#define JBT_REG_EXT_DISPL		0xb7
+#define JBT_REG_OUTPUT_CONTROL		0xb8
+#define JBT_REG_DCCLK_DCEV		0xb9
+#define JBT_REG_DISPLAY_MODE1		0xba
+#define JBT_REG_DISPLAY_MODE2		0xbb
+#define JBT_REG_DISPLAY_MODE		0xbc
+#define JBT_REG_ASW_SLEW		0xbd
+#define JBT_REG_DUMMY_DISPLAY		0xbe
+#define JBT_REG_DRIVE_SYSTEM		0xbf
+
+#define JBT_REG_SLEEP_OUT_FR_A		0xc0
+#define JBT_REG_SLEEP_OUT_FR_B		0xc1
+#define JBT_REG_SLEEP_OUT_FR_C		0xc2
+#define JBT_REG_SLEEP_IN_LCCNT_D	0xc3
+#define JBT_REG_SLEEP_IN_LCCNT_E	0xc4
+#define JBT_REG_SLEEP_IN_LCCNT_F	0xc5
+#define JBT_REG_SLEEP_IN_LCCNT_G	0xc6
+
+#define JBT_REG_GAMMA1_FINE_1		0xc7
+#define JBT_REG_GAMMA1_FINE_2		0xc8
+#define JBT_REG_GAMMA1_INCLINATION	0xc9
+#define JBT_REG_GAMMA1_BLUE_OFFSET	0xca
+
+#define JBT_REG_BLANK_CONTROL		0xcf
+#define JBT_REG_BLANK_TH_TV		0xd0
+#define JBT_REG_CKV_ON_OFF		0xd1
+#define JBT_REG_CKV_1_2			0xd2
+#define JBT_REG_OEV_TIMING		0xd3
+#define JBT_REG_ASW_TIMING_1		0xd4
+#define JBT_REG_ASW_TIMING_2		0xd5
+
+#define JBT_REG_HCLOCK_VGA		0xec
+#define JBT_REG_HCLOCK_QVGA		0xed
+
+struct td028ttec1_panel {
+	struct drm_panel panel;
+
+	struct spi_device *spi;
+	struct backlight_device *backlight;
+};
+
+#define to_td028ttec1_device(p) container_of(p, struct td028ttec1_panel, panel)
+
+static int jbt_ret_write_0(struct td028ttec1_panel *lcd, u8 reg, int *err)
+{
+	struct spi_device *spi = lcd->spi;
+	u16 tx_buf = JBT_COMMAND | reg;
+	int ret;
+
+	if (err && *err)
+		return *err;
+
+	ret = spi_write(spi, (u8 *)&tx_buf, sizeof(tx_buf));
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
+		if (err)
+			*err = ret;
+	}
+
+	return ret;
+}
+
+static int jbt_reg_write_1(struct td028ttec1_panel *lcd,
+			   u8 reg, u8 data, int *err)
+{
+	struct spi_device *spi = lcd->spi;
+	u16 tx_buf[2];
+	int ret;
+
+	if (err && *err)
+		return *err;
+
+	tx_buf[0] = JBT_COMMAND | reg;
+	tx_buf[1] = JBT_DATA | data;
+
+	ret = spi_write(spi, (u8 *)tx_buf, sizeof(tx_buf));
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
+		if (err)
+			*err = ret;
+	}
+
+	return ret;
+}
+
+static int jbt_reg_write_2(struct td028ttec1_panel *lcd,
+			   u8 reg, u16 data, int *err)
+{
+	struct spi_device *spi = lcd->spi;
+	u16 tx_buf[3];
+	int ret;
+
+	if (err && *err)
+		return *err;
+
+	tx_buf[0] = JBT_COMMAND | reg;
+	tx_buf[1] = JBT_DATA | (data >> 8);
+	tx_buf[2] = JBT_DATA | (data & 0xff);
+
+	ret = spi_write(spi, (u8 *)tx_buf, sizeof(tx_buf));
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
+		if (err)
+			*err = ret;
+	}
+
+	return ret;
+}
+
+static int td028ttec1_prepare(struct drm_panel *panel)
+{
+	struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+	unsigned int i;
+	int ret = 0;
+
+	/* Three times command zero */
+	for (i = 0; i < 3; ++i) {
+		jbt_ret_write_0(lcd, 0x00, &ret);
+		usleep_range(1000, 2000);
+	}
+
+	/* deep standby out */
+	jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x17, &ret);
+
+	/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
+	jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE, 0x80, &ret);
+
+	/* Quad mode off */
+	jbt_reg_write_1(lcd, JBT_REG_QUAD_RATE, 0x00, &ret);
+
+	/* AVDD on, XVDD on */
+	jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x16, &ret);
+
+	/* Output control */
+	jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0xfff9, &ret);
+
+	/* Sleep mode off */
+	jbt_ret_write_0(lcd, JBT_REG_SLEEP_OUT, &ret);
+
+	/* at this point we have like 50% grey */
+
+	/* initialize register set */
+	jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE1, 0x01, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE2, 0x00, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_RGB_FORMAT, 0x60, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_DRIVE_SYSTEM, 0x10, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_OP, 0x56, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_MODE, 0x33, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_OPAMP_SYSCLK, 0x02, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_VSC_VOLTAGE, 0x2b, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_VCOM_VOLTAGE, 0x40, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_EXT_DISPL, 0x03, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_DCCLK_DCEV, 0x04, &ret);
+	/*
+	 * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
+	 * to avoid red / blue flicker
+	 */
+	jbt_reg_write_1(lcd, JBT_REG_ASW_SLEW, 0x04, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_DUMMY_DISPLAY, 0x00, &ret);
+
+	jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_A, 0x11, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_B, 0x11, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_C, 0x11, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0, &ret);
+
+	jbt_reg_write_2(lcd, JBT_REG_GAMMA1_FINE_1, 0x5533, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_GAMMA1_FINE_2, 0x00, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_GAMMA1_INCLINATION, 0x00, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00, &ret);
+
+	jbt_reg_write_2(lcd, JBT_REG_HCLOCK_VGA, 0x1f0, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_BLANK_CONTROL, 0x02, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_BLANK_TH_TV, 0x0804, &ret);
+
+	jbt_reg_write_1(lcd, JBT_REG_CKV_ON_OFF, 0x01, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_CKV_1_2, 0x0000, &ret);
+
+	jbt_reg_write_2(lcd, JBT_REG_OEV_TIMING, 0x0d0e, &ret);
+	jbt_reg_write_2(lcd, JBT_REG_ASW_TIMING_1, 0x11a4, &ret);
+	jbt_reg_write_1(lcd, JBT_REG_ASW_TIMING_2, 0x0e, &ret);
+
+	return ret;
+}
+
+static int td028ttec1_enable(struct drm_panel *panel)
+{
+	struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+	int ret;
+
+	ret = jbt_ret_write_0(lcd, JBT_REG_DISPLAY_ON, NULL);
+	if (ret)
+		return ret;
+
+	backlight_enable(lcd->backlight);
+
+	return 0;
+}
+
+static int td028ttec1_disable(struct drm_panel *panel)
+{
+	struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+
+	backlight_disable(lcd->backlight);
+
+	jbt_ret_write_0(lcd, JBT_REG_DISPLAY_OFF, NULL);
+
+	return 0;
+}
+
+static int td028ttec1_unprepare(struct drm_panel *panel)
+{
+	struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+
+	jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0x8002, NULL);
+	jbt_ret_write_0(lcd, JBT_REG_SLEEP_IN, NULL);
+	jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x00, NULL);
+
+	return 0;
+}
+
+static const struct drm_display_mode td028ttec1_mode = {
+	.clock = 22153,
+	.hdisplay = 480,
+	.hsync_start = 480 + 24,
+	.hsync_end = 480 + 24 + 8,
+	.htotal = 480 + 24 + 8 + 8,
+	.vdisplay = 640,
+	.vsync_start = 640 + 4,
+	.vsync_end = 640 + 4 + 2,
+	.vtotal = 640 + 4 + 2 + 2,
+	.vrefresh = 66,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm = 43,
+	.height_mm = 58,
+};
+
+static int td028ttec1_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &td028ttec1_mode);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = td028ttec1_mode.width_mm;
+	connector->display_info.height_mm = td028ttec1_mode.height_mm;
+	/*
+	 * FIXME: According to the datasheet sync signals are sampled on the
+	 * rising edge of the clock, but the code running on the OpenMoko Neo
+	 * FreeRunner and Neo 1973 indicates sampling on the falling edge. This
+	 * should be tested on a real device.
+	 */
+	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs td028ttec1_funcs = {
+	.prepare = td028ttec1_prepare,
+	.enable = td028ttec1_enable,
+	.disable = td028ttec1_disable,
+	.unprepare = td028ttec1_unprepare,
+	.get_modes = td028ttec1_get_modes,
+};
+
+static int td028ttec1_probe(struct spi_device *spi)
+{
+	struct td028ttec1_panel *lcd;
+	int ret;
+
+	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+	if (lcd == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, lcd);
+	lcd->spi = spi;
+
+	lcd->backlight = devm_of_find_backlight(&spi->dev);
+	if (IS_ERR(lcd->backlight))
+		return PTR_ERR(lcd->backlight);
+
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = 9;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
+		return ret;
+	}
+
+	drm_panel_init(&lcd->panel);
+	lcd->panel.dev = &lcd->spi->dev;
+	lcd->panel.funcs = &td028ttec1_funcs;
+
+	return drm_panel_add(&lcd->panel);
+}
+
+static int td028ttec1_remove(struct spi_device *spi)
+{
+	struct td028ttec1_panel *lcd = spi_get_drvdata(spi);
+
+	drm_panel_remove(&lcd->panel);
+	drm_panel_disable(&lcd->panel);
+	drm_panel_unprepare(&lcd->panel);
+
+	return 0;
+}
+
+static const struct of_device_id td028ttec1_of_match[] = {
+	{ .compatible = "tpo,td028ttec1", },
+	/* DT backward compatibility. */
+	{ .compatible = "toppoly,td028ttec1", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
+
+static const struct spi_device_id td028ttec1_ids[] = {
+	{ "tpo,td028ttec1", 0},
+	{ "toppoly,td028ttec1", 0 },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, td028ttec1_ids);
+
+static struct spi_driver td028ttec1_driver = {
+	.probe		= td028ttec1_probe,
+	.remove		= td028ttec1_remove,
+	.id_table	= td028ttec1_ids,
+	.driver		= {
+		.name   = "panel-tpo-td028ttec1",
+		.of_match_table = td028ttec1_of_match,
+	},
+};
+
+module_spi_driver(td028ttec1_driver);
+
+MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
+MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
+MODULE_LICENSE("GPL");
-- 
Regards,

Laurent Pinchart

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

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

* [PATCH v4 9/9] drm/panel: Add driver for the Toppoly TD043MTEA1 panel
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (7 preceding siblings ...)
  2019-08-13 20:11 ` [PATCH v4 8/9] drm/panel: Add driver for the Toppoly TD028TTEC1 panel Laurent Pinchart
@ 2019-08-13 20:11 ` Laurent Pinchart
  2019-08-14 16:44 ` [PATCH v4 0/9] DRM panel drivers for omapdrm Sam Ravnborg
  9 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-13 20:11 UTC (permalink / raw)
  To: dri-devel; +Cc: Thierry Reding, Sam Ravnborg

This panel is used on the OMAP3 Pandora.

The code is based on the omapdrm-specific panel-tpo-td043mtea1 driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
Changes since v2:

- Call drm_panel_disable() in .remove() handler

Changes since v1:

- Mention boards using the panel in Kconfig
- Renamed td043mtea1_device to td043mtea1_panel
- Fixed typo in Toppoly name
- Comments updates
- Store width_mm and height_mm in drm_display_mode
- Use drm_panel_unprepare() in .remove() handler
- Replace enable/disable with prepare/unprepare
---
 drivers/gpu/drm/panel/Kconfig                |   7 +
 drivers/gpu/drm/panel/Makefile               |   1 +
 drivers/gpu/drm/panel/panel-tpo-td043mtea1.c | 509 +++++++++++++++++++
 3 files changed, 517 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-tpo-td043mtea1.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index c2689420d2dd..f152bc4eeb53 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -332,6 +332,13 @@ config DRM_PANEL_TPO_TD028TTEC1
 	  Say Y here if you want to enable support for TPO TD028TTEC1 480x640
 	  2.8" panel (found on the OpenMoko Neo FreeRunner and Neo 1973).
 
+config DRM_PANEL_TPO_TD043MTEA1
+	tristate "Toppoly (TPO) TD043MTEA1 panel driver"
+	depends on GPIOLIB && OF && REGULATOR && SPI
+	help
+	  Say Y here if you want to enable support for TPO TD043MTEA1 800x480
+	  4.3" panel (found on the OMAP3 Pandora board).
+
 config DRM_PANEL_TPO_TPG110
 	tristate "TPO TPG 800x400 panel"
 	depends on OF && SPI && GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 27b776737ad3..b6cd39fe0f20 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -35,5 +35,6 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.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_NT35597_WQXGA) += panel-truly-nt35597.o
diff --git a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c
new file mode 100644
index 000000000000..71824a334af7
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Toppoly TD043MTEA1 Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-tpo-td043mtea1 driver
+ *
+ * Author: Gražvydas Ignotas <notasas@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define TPO_R02_MODE(x)			((x) & 7)
+#define TPO_R02_MODE_800x480		7
+#define TPO_R02_NCLK_RISING		BIT(3)
+#define TPO_R02_HSYNC_HIGH		BIT(4)
+#define TPO_R02_VSYNC_HIGH		BIT(5)
+
+#define TPO_R03_NSTANDBY		BIT(0)
+#define TPO_R03_EN_CP_CLK		BIT(1)
+#define TPO_R03_EN_VGL_PUMP		BIT(2)
+#define TPO_R03_EN_PWM			BIT(3)
+#define TPO_R03_DRIVING_CAP_100		BIT(4)
+#define TPO_R03_EN_PRE_CHARGE		BIT(6)
+#define TPO_R03_SOFTWARE_CTL		BIT(7)
+
+#define TPO_R04_NFLIP_H			BIT(0)
+#define TPO_R04_NFLIP_V			BIT(1)
+#define TPO_R04_CP_CLK_FREQ_1H		BIT(2)
+#define TPO_R04_VGL_FREQ_1H		BIT(4)
+
+#define TPO_R03_VAL_NORMAL \
+	(TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | TPO_R03_EN_VGL_PUMP | \
+	 TPO_R03_EN_PWM | TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
+	 TPO_R03_SOFTWARE_CTL)
+
+#define TPO_R03_VAL_STANDBY \
+	(TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
+	 TPO_R03_SOFTWARE_CTL)
+
+static const u16 td043mtea1_def_gamma[12] = {
+	105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
+};
+
+struct td043mtea1_panel {
+	struct drm_panel panel;
+
+	struct spi_device *spi;
+	struct regulator *vcc_reg;
+	struct gpio_desc *reset_gpio;
+
+	unsigned int mode;
+	u16 gamma[12];
+	bool vmirror;
+	bool powered_on;
+	bool spi_suspended;
+	bool power_on_resume;
+};
+
+#define to_td043mtea1_device(p) container_of(p, struct td043mtea1_panel, panel)
+
+/* -----------------------------------------------------------------------------
+ * Hardware Access
+ */
+
+static int td043mtea1_write(struct td043mtea1_panel *lcd, u8 addr, u8 value)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	u16 data;
+	int ret;
+
+	spi_message_init(&msg);
+
+	memset(&xfer, 0, sizeof(xfer));
+
+	data = ((u16)addr << 10) | (1 << 8) | value;
+	xfer.tx_buf = &data;
+	xfer.bits_per_word = 16;
+	xfer.len = 2;
+	spi_message_add_tail(&xfer, &msg);
+
+	ret = spi_sync(lcd->spi, &msg);
+	if (ret < 0)
+		dev_warn(&lcd->spi->dev, "failed to write to LCD reg (%d)\n",
+			 ret);
+
+	return ret;
+}
+
+static void td043mtea1_write_gamma(struct td043mtea1_panel *lcd)
+{
+	const u16 *gamma = lcd->gamma;
+	unsigned int i;
+	u8 val;
+
+	/* gamma bits [9:8] */
+	for (val = i = 0; i < 4; i++)
+		val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
+	td043mtea1_write(lcd, 0x11, val);
+
+	for (val = i = 0; i < 4; i++)
+		val |= (gamma[i + 4] & 0x300) >> ((i + 1) * 2);
+	td043mtea1_write(lcd, 0x12, val);
+
+	for (val = i = 0; i < 4; i++)
+		val |= (gamma[i + 8] & 0x300) >> ((i + 1) * 2);
+	td043mtea1_write(lcd, 0x13, val);
+
+	/* gamma bits [7:0] */
+	for (val = i = 0; i < 12; i++)
+		td043mtea1_write(lcd, 0x14 + i, gamma[i] & 0xff);
+}
+
+static int td043mtea1_write_mirror(struct td043mtea1_panel *lcd)
+{
+	u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
+		TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
+	if (lcd->vmirror)
+		reg4 &= ~TPO_R04_NFLIP_V;
+
+	return td043mtea1_write(lcd, 4, reg4);
+}
+
+static int td043mtea1_power_on(struct td043mtea1_panel *lcd)
+{
+	int ret;
+
+	if (lcd->powered_on)
+		return 0;
+
+	ret = regulator_enable(lcd->vcc_reg);
+	if (ret < 0)
+		return ret;
+
+	/* Wait for the panel to stabilize. */
+	msleep(160);
+
+	gpiod_set_value(lcd->reset_gpio, 0);
+
+	td043mtea1_write(lcd, 2, TPO_R02_MODE(lcd->mode) | TPO_R02_NCLK_RISING);
+	td043mtea1_write(lcd, 3, TPO_R03_VAL_NORMAL);
+	td043mtea1_write(lcd, 0x20, 0xf0);
+	td043mtea1_write(lcd, 0x21, 0xf0);
+	td043mtea1_write_mirror(lcd);
+	td043mtea1_write_gamma(lcd);
+
+	lcd->powered_on = true;
+
+	return 0;
+}
+
+static void td043mtea1_power_off(struct td043mtea1_panel *lcd)
+{
+	if (!lcd->powered_on)
+		return;
+
+	td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
+
+	gpiod_set_value(lcd->reset_gpio, 1);
+
+	/* wait for at least 2 vsyncs before cutting off power */
+	msleep(50);
+
+	td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY);
+
+	regulator_disable(lcd->vcc_reg);
+
+	lcd->powered_on = false;
+}
+
+/* -----------------------------------------------------------------------------
+ * sysfs
+ */
+
+static ssize_t vmirror_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", lcd->vmirror);
+}
+
+static ssize_t vmirror_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+	int val;
+	int ret;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret < 0)
+		return ret;
+
+	lcd->vmirror = !!val;
+
+	ret = td043mtea1_write_mirror(lcd);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", lcd->mode);
+}
+
+static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+	long val;
+	int ret;
+
+	ret = kstrtol(buf, 0, &val);
+	if (ret != 0 || val & ~7)
+		return -EINVAL;
+
+	lcd->mode = val;
+
+	val |= TPO_R02_NCLK_RISING;
+	td043mtea1_write(lcd, 2, val);
+
+	return count;
+}
+
+static ssize_t gamma_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+	ssize_t len = 0;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(lcd->gamma); i++) {
+		ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
+				lcd->gamma[i]);
+		if (ret < 0)
+			return ret;
+		len += ret;
+	}
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t gamma_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+	unsigned int g[12];
+	unsigned int i;
+	int ret;
+
+	ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
+			&g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
+			&g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
+	if (ret != 12)
+		return -EINVAL;
+
+	for (i = 0; i < 12; i++)
+		lcd->gamma[i] = g[i];
+
+	td043mtea1_write_gamma(lcd);
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(vmirror);
+static DEVICE_ATTR_RW(mode);
+static DEVICE_ATTR_RW(gamma);
+
+static struct attribute *td043mtea1_attrs[] = {
+	&dev_attr_vmirror.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_gamma.attr,
+	NULL,
+};
+
+static const struct attribute_group td043mtea1_attr_group = {
+	.attrs = td043mtea1_attrs,
+};
+
+/* -----------------------------------------------------------------------------
+ * Panel Operations
+ */
+
+static int td043mtea1_unprepare(struct drm_panel *panel)
+{
+	struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
+
+	if (!lcd->spi_suspended)
+		td043mtea1_power_off(lcd);
+
+	return 0;
+}
+
+static int td043mtea1_prepare(struct drm_panel *panel)
+{
+	struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
+	int ret;
+
+	/*
+	 * If we are resuming from system suspend, SPI might not be enabled
+	 * yet, so we'll program the LCD from SPI PM resume callback.
+	 */
+	if (lcd->spi_suspended)
+		return 0;
+
+	ret = td043mtea1_power_on(lcd);
+	if (ret) {
+		dev_err(&lcd->spi->dev, "%s: power on failed (%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct drm_display_mode td043mtea1_mode = {
+	.clock = 36000,
+	.hdisplay = 800,
+	.hsync_start = 800 + 68,
+	.hsync_end = 800 + 68 + 1,
+	.htotal = 800 + 68 + 1 + 214,
+	.vdisplay = 480,
+	.vsync_start = 480 + 39,
+	.vsync_end = 480 + 39 + 1,
+	.vtotal = 480 + 39 + 1 + 34,
+	.vrefresh = 60,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm = 94,
+	.height_mm = 56,
+};
+
+static int td043mtea1_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &td043mtea1_mode);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = td043mtea1_mode.width_mm;
+	connector->display_info.height_mm = td043mtea1_mode.height_mm;
+	/*
+	 * FIXME: According to the datasheet sync signals are sampled on the
+	 * rising edge of the clock, but the code running on the OMAP3 Pandora
+	 * indicates sampling on the falling edge. This should be tested on a
+	 * real device.
+	 */
+	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs td043mtea1_funcs = {
+	.unprepare = td043mtea1_unprepare,
+	.prepare = td043mtea1_prepare,
+	.get_modes = td043mtea1_get_modes,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management, Probe and Remove
+ */
+
+static int __maybe_unused td043mtea1_suspend(struct device *dev)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+
+	if (lcd->powered_on) {
+		td043mtea1_power_off(lcd);
+		lcd->powered_on = true;
+	}
+
+	lcd->spi_suspended = true;
+
+	return 0;
+}
+
+static int __maybe_unused td043mtea1_resume(struct device *dev)
+{
+	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+	int ret;
+
+	lcd->spi_suspended = false;
+
+	if (lcd->powered_on) {
+		lcd->powered_on = false;
+		ret = td043mtea1_power_on(lcd);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(td043mtea1_pm_ops, td043mtea1_suspend,
+			 td043mtea1_resume);
+
+static int td043mtea1_probe(struct spi_device *spi)
+{
+	struct td043mtea1_panel *lcd;
+	int ret;
+
+	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+	if (lcd == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, lcd);
+	lcd->spi = spi;
+	lcd->mode = TPO_R02_MODE_800x480;
+	memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma));
+
+	lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
+	if (IS_ERR(lcd->vcc_reg)) {
+		dev_err(&spi->dev, "failed to get VCC regulator\n");
+		return PTR_ERR(lcd->vcc_reg);
+	}
+
+	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(lcd->reset_gpio)) {
+		dev_err(&spi->dev, "failed to get reset GPIO\n");
+		return PTR_ERR(lcd->reset_gpio);
+	}
+
+	spi->bits_per_word = 16;
+	spi->mode = SPI_MODE_0;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
+		return ret;
+	}
+
+	ret = sysfs_create_group(&spi->dev.kobj, &td043mtea1_attr_group);
+	if (ret < 0) {
+		dev_err(&spi->dev, "failed to create sysfs files\n");
+		return ret;
+	}
+
+	drm_panel_init(&lcd->panel);
+	lcd->panel.dev = &lcd->spi->dev;
+	lcd->panel.funcs = &td043mtea1_funcs;
+
+	ret = drm_panel_add(&lcd->panel);
+	if (ret < 0) {
+		sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int td043mtea1_remove(struct spi_device *spi)
+{
+	struct td043mtea1_panel *lcd = spi_get_drvdata(spi);
+
+	drm_panel_remove(&lcd->panel);
+	drm_panel_disable(&lcd->panel);
+	drm_panel_unprepare(&lcd->panel);
+
+	sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
+
+	return 0;
+}
+
+static const struct of_device_id td043mtea1_of_match[] = {
+	{ .compatible = "tpo,td043mtea1", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, td043mtea1_of_match);
+
+static struct spi_driver td043mtea1_driver = {
+	.probe		= td043mtea1_probe,
+	.remove		= td043mtea1_remove,
+	.driver		= {
+		.name	= "panel-tpo-td043mtea1",
+		.pm	= &td043mtea1_pm_ops,
+		.of_match_table = td043mtea1_of_match,
+	},
+};
+
+module_spi_driver(td043mtea1_driver);
+
+MODULE_ALIAS("spi:tpo,td043mtea1");
+MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver");
+MODULE_LICENSE("GPL");
-- 
Regards,

Laurent Pinchart

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

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

* Re: [PATCH v4 0/9] DRM panel drivers for omapdrm
  2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
                   ` (8 preceding siblings ...)
  2019-08-13 20:11 ` [PATCH v4 9/9] drm/panel: Add driver for the Toppoly TD043MTEA1 panel Laurent Pinchart
@ 2019-08-14 16:44 ` Sam Ravnborg
  2019-08-14 20:28   ` Sam Ravnborg
  9 siblings, 1 reply; 13+ messages in thread
From: Sam Ravnborg @ 2019-08-14 16:44 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Thierry Reding, dri-devel

Hi Laurent.

On Tue, Aug 13, 2019 at 11:10:52PM +0300, Laurent Pinchart wrote:
> Hello everybody,
> 
> This patch series adds DT bindings and drivers for 6 panels used by
> omapdrm. They are meant to replace the corresponding omapdrm-specific
> drivers from drivers/gpu/drm/omapdrm/displays/ that will be removed in a
> subsequent patch series, once the omapdrm driver switches fully to the
> drm_panel API.
> 
> There's nothing very special here. The first three patches add DT vendor
> prefixes and DT bindings. The last six patches add new panel drivers.
> 
> Please see individual patches for changelogs. Sam, all the patches have
> now been acked (resulting in a TODO list in 7/9 and a rework of 8/9).
> Would you merge this in drm-misc ?
> 
> The patches are based on top of drm-misc-next and can be found at
> 
> 	git://linuxtv.org/pinchartl/media.git omapdrm/panels
> 
> Laurent Pinchart (9):
>   dt-bindings: Add vendor prefix for LG Display
>   dt-bindings: Add legacy 'toppoly' vendor prefix
>   dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel
>   drm/panel: Add driver for the LG Philips LB035Q02 panel
>   drm/panel: Add driver for the NEC NL8048HL11 panel
>   drm/panel: Add driver for the Sharp LS037V7DW01 panel
>   drm/panel: Add driver for the Sony ACX565AKM panel
>   drm/panel: Add driver for the Toppoly TD028TTEC1 panel
>   drm/panel: Add driver for the Toppoly TD043MTEA1 panel

dim was not too happy with the patches.
checkpatch --strict triggers too much:

drm/panel: Add driver for the LG Philips LB035Q02 panel
-:24: WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
#24: FILE: drivers/gpu/drm/panel/Kconfig:106:
+config DRM_PANEL_LG_LB035Q02

-:235: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!lcd"
#235: FILE: drivers/gpu/drm/panel/panel-lg-lb035q02.c:183:
+	if (lcd == NULL)

drm/panel: Add driver for the NEC NL8048HL11 panel
-:23: WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
#23: FILE: drivers/gpu/drm/panel/Kconfig:122:
+config DRM_PANEL_NEC_NL8048HL11

-:47: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#47:
new file mode 100644

-:136: CHECK:USLEEP_RANGE: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
#136: FILE: drivers/gpu/drm/panel/panel-nec-nl8048hl11.c:85:
+	udelay(20);

-:234: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!lcd"
#234: FILE: drivers/gpu/drm/panel/panel-nec-nl8048hl11.c:183:
+	if (lcd == NULL)

etc..

Nothing serious but please give them an extra round so we do
not have extra patches flowing in to fix these trivial things.
There is a few "line too long" that I personally would ignore.
But warnings lige (lcd == NULL) => (!lcd) I would fix.

And there was too much to fix while applying.

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

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

* Re: [PATCH v4 0/9] DRM panel drivers for omapdrm
  2019-08-14 16:44 ` [PATCH v4 0/9] DRM panel drivers for omapdrm Sam Ravnborg
@ 2019-08-14 20:28   ` Sam Ravnborg
  2019-08-14 21:40     ` Laurent Pinchart
  0 siblings, 1 reply; 13+ messages in thread
From: Sam Ravnborg @ 2019-08-14 20:28 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Thierry Reding, dri-devel

Hi Laurent.

On Wed, Aug 14, 2019 at 06:44:26PM +0200, Sam Ravnborg wrote:
> Hi Laurent.
> 
> On Tue, Aug 13, 2019 at 11:10:52PM +0300, Laurent Pinchart wrote:
> > Hello everybody,
> > 
> > This patch series adds DT bindings and drivers for 6 panels used by
> > omapdrm. They are meant to replace the corresponding omapdrm-specific
> > drivers from drivers/gpu/drm/omapdrm/displays/ that will be removed in a
> > subsequent patch series, once the omapdrm driver switches fully to the
> > drm_panel API.
> > 
> > There's nothing very special here. The first three patches add DT vendor
> > prefixes and DT bindings. The last six patches add new panel drivers.
> > 
> > Please see individual patches for changelogs. Sam, all the patches have
> > now been acked (resulting in a TODO list in 7/9 and a rework of 8/9).
> > Would you merge this in drm-misc ?
> > 
> > The patches are based on top of drm-misc-next and can be found at
> > 
> > 	git://linuxtv.org/pinchartl/media.git omapdrm/panels
> > 
> > Laurent Pinchart (9):
> >   dt-bindings: Add vendor prefix for LG Display
> >   dt-bindings: Add legacy 'toppoly' vendor prefix
> >   dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel
> >   drm/panel: Add driver for the LG Philips LB035Q02 panel
> >   drm/panel: Add driver for the NEC NL8048HL11 panel
> >   drm/panel: Add driver for the Sharp LS037V7DW01 panel
> >   drm/panel: Add driver for the Sony ACX565AKM panel
> >   drm/panel: Add driver for the Toppoly TD028TTEC1 panel
> >   drm/panel: Add driver for the Toppoly TD043MTEA1 panel
> 
> dim was not too happy with the patches.
> checkpatch --strict triggers too much:
> 
> drm/panel: Add driver for the LG Philips LB035Q02 panel
> -:24: WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
> #24: FILE: drivers/gpu/drm/panel/Kconfig:106:
> +config DRM_PANEL_LG_LB035Q02
> 
> -:235: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!lcd"
> #235: FILE: drivers/gpu/drm/panel/panel-lg-lb035q02.c:183:
> +	if (lcd == NULL)
> 
> drm/panel: Add driver for the NEC NL8048HL11 panel
> -:23: WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
> #23: FILE: drivers/gpu/drm/panel/Kconfig:122:
> +config DRM_PANEL_NEC_NL8048HL11
> 
> -:47: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
> #47:
> new file mode 100644
> 
> -:136: CHECK:USLEEP_RANGE: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
> #136: FILE: drivers/gpu/drm/panel/panel-nec-nl8048hl11.c:85:
> +	udelay(20);
> 
> -:234: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!lcd"
> #234: FILE: drivers/gpu/drm/panel/panel-nec-nl8048hl11.c:183:
> +	if (lcd == NULL)
> 
> etc..
> 
> Nothing serious but please give them an extra round so we do
> not have extra patches flowing in to fix these trivial things.
> There is a few "line too long" that I personally would ignore.
> But warnings lige (lcd == NULL) => (!lcd) I would fix.
> 
> And there was too much to fix while applying.

Forget this. I could use this existing little task to avoid some boring
work stuff.
Updated all checkpatch issues I considered relevant:
o (lcd == NULL) => (!lcd)
o <1 << X) => BIT(X)

And removed the __exit_p() annotation for a remove function to fix a
build warning.

Build tested and applied to drm-misc-next.

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

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

* Re: [PATCH v4 0/9] DRM panel drivers for omapdrm
  2019-08-14 20:28   ` Sam Ravnborg
@ 2019-08-14 21:40     ` Laurent Pinchart
  0 siblings, 0 replies; 13+ messages in thread
From: Laurent Pinchart @ 2019-08-14 21:40 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: Thierry Reding, dri-devel

Hi Sam,

On Wed, Aug 14, 2019 at 10:28:01PM +0200, Sam Ravnborg wrote:
> On Wed, Aug 14, 2019 at 06:44:26PM +0200, Sam Ravnborg wrote:
> > On Tue, Aug 13, 2019 at 11:10:52PM +0300, Laurent Pinchart wrote:
> > > Hello everybody,
> > > 
> > > This patch series adds DT bindings and drivers for 6 panels used by
> > > omapdrm. They are meant to replace the corresponding omapdrm-specific
> > > drivers from drivers/gpu/drm/omapdrm/displays/ that will be removed in a
> > > subsequent patch series, once the omapdrm driver switches fully to the
> > > drm_panel API.
> > > 
> > > There's nothing very special here. The first three patches add DT vendor
> > > prefixes and DT bindings. The last six patches add new panel drivers.
> > > 
> > > Please see individual patches for changelogs. Sam, all the patches have
> > > now been acked (resulting in a TODO list in 7/9 and a rework of 8/9).
> > > Would you merge this in drm-misc ?
> > > 
> > > The patches are based on top of drm-misc-next and can be found at
> > > 
> > > 	git://linuxtv.org/pinchartl/media.git omapdrm/panels
> > > 
> > > Laurent Pinchart (9):
> > >   dt-bindings: Add vendor prefix for LG Display
> > >   dt-bindings: Add legacy 'toppoly' vendor prefix
> > >   dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel
> > >   drm/panel: Add driver for the LG Philips LB035Q02 panel
> > >   drm/panel: Add driver for the NEC NL8048HL11 panel
> > >   drm/panel: Add driver for the Sharp LS037V7DW01 panel
> > >   drm/panel: Add driver for the Sony ACX565AKM panel
> > >   drm/panel: Add driver for the Toppoly TD028TTEC1 panel
> > >   drm/panel: Add driver for the Toppoly TD043MTEA1 panel
> > 
> > dim was not too happy with the patches.
> > checkpatch --strict triggers too much:
> > 
> > drm/panel: Add driver for the LG Philips LB035Q02 panel
> > -:24: WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
> > #24: FILE: drivers/gpu/drm/panel/Kconfig:106:
> > +config DRM_PANEL_LG_LB035Q02
> > 
> > -:235: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!lcd"
> > #235: FILE: drivers/gpu/drm/panel/panel-lg-lb035q02.c:183:
> > +	if (lcd == NULL)
> > 
> > drm/panel: Add driver for the NEC NL8048HL11 panel
> > -:23: WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
> > #23: FILE: drivers/gpu/drm/panel/Kconfig:122:
> > +config DRM_PANEL_NEC_NL8048HL11
> > 
> > -:47: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
> > #47:
> > new file mode 100644
> > 
> > -:136: CHECK:USLEEP_RANGE: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
> > #136: FILE: drivers/gpu/drm/panel/panel-nec-nl8048hl11.c:85:
> > +	udelay(20);
> > 
> > -:234: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!lcd"
> > #234: FILE: drivers/gpu/drm/panel/panel-nec-nl8048hl11.c:183:
> > +	if (lcd == NULL)
> > 
> > etc..
> > 
> > Nothing serious but please give them an extra round so we do
> > not have extra patches flowing in to fix these trivial things.
> > There is a few "line too long" that I personally would ignore.
> > But warnings lige (lcd == NULL) => (!lcd) I would fix.
> > 
> > And there was too much to fix while applying.
> 
> Forget this. I could use this existing little task to avoid some boring
> work stuff.
> Updated all checkpatch issues I considered relevant:
> o (lcd == NULL) => (!lcd)
> o <1 << X) => BIT(X)
> 
> And removed the __exit_p() annotation for a remove function to fix a
> build warning.
> 
> Build tested and applied to drm-misc-next.

Thank you !

-- 
Regards,

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

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

end of thread, other threads:[~2019-08-14 21:40 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-13 20:10 [PATCH v4 0/9] DRM panel drivers for omapdrm Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 1/9] dt-bindings: Add vendor prefix for LG Display Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 2/9] dt-bindings: Add legacy 'toppoly' vendor prefix Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 3/9] dt-bindings: display: panel: Add bindings for NEC NL8048HL11 panel Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 4/9] drm/panel: Add driver for the LG Philips LB035Q02 panel Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 5/9] drm/panel: Add driver for the NEC NL8048HL11 panel Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 6/9] drm/panel: Add driver for the Sharp LS037V7DW01 panel Laurent Pinchart
2019-08-13 20:10 ` [PATCH v4 7/9] drm/panel: Add driver for the Sony ACX565AKM panel Laurent Pinchart
2019-08-13 20:11 ` [PATCH v4 8/9] drm/panel: Add driver for the Toppoly TD028TTEC1 panel Laurent Pinchart
2019-08-13 20:11 ` [PATCH v4 9/9] drm/panel: Add driver for the Toppoly TD043MTEA1 panel Laurent Pinchart
2019-08-14 16:44 ` [PATCH v4 0/9] DRM panel drivers for omapdrm Sam Ravnborg
2019-08-14 20:28   ` Sam Ravnborg
2019-08-14 21:40     ` Laurent Pinchart

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.