Pablo Greco 7b2c62
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
Pablo Greco 7b2c62
From: Icenowy Zheng <icenowy@aosc.io>
Pablo Greco 7b2c62
Date: Mon, 16 Mar 2020 21:35:01 +0800
Pablo Greco 7b2c62
Subject: [PATCH] drm: panel: add Xingbangda XBD599 panel
Pablo Greco 7b2c62
Pablo Greco 7b2c62
Xingbangda XBD599 is a 5.99" 720x1440 MIPI-DSI IPS LCD panel made by
Pablo Greco 7b2c62
Xingbangda, which is used on PinePhone final assembled phones.
Pablo Greco 7b2c62
Pablo Greco 7b2c62
Add support for it.
Pablo Greco 7b2c62
Pablo Greco 7b2c62
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
Pablo Greco 7b2c62
---
Pablo Greco 7b2c62
 drivers/gpu/drm/panel/Kconfig                 |   9 +
Pablo Greco 7b2c62
 drivers/gpu/drm/panel/Makefile                |   1 +
Pablo Greco 7b2c62
 .../gpu/drm/panel/panel-xingbangda-xbd599.c   | 366 ++++++++++++++++++
Pablo Greco 7b2c62
 3 files changed, 376 insertions(+)
Pablo Greco 7b2c62
 create mode 100644 drivers/gpu/drm/panel/panel-xingbangda-xbd599.c
Pablo Greco 7b2c62
Pablo Greco 7b2c62
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
Pablo Greco 7b2c62
index de2f2a452be5..3ce658de416b 100644
Pablo Greco 7b2c62
--- a/drivers/gpu/drm/panel/Kconfig
Pablo Greco 7b2c62
+++ b/drivers/gpu/drm/panel/Kconfig
Pablo Greco 7b2c62
@@ -462,6 +462,15 @@ config DRM_PANEL_VISIONOX_RM69299
Pablo Greco 7b2c62
 	  Say Y here if you want to enable support for Visionox
Pablo Greco 7b2c62
 	  RM69299  DSI Video Mode panel.
Pablo Greco 7b2c62
Pablo Greco 7b2c62
+config DRM_PANEL_XINGBANGDA_XBD599
Pablo Greco 7b2c62
+	tristate "Xingbangda XBD599 panel"
Pablo Greco 7b2c62
+	depends on OF
Pablo Greco 7b2c62
+	depends on DRM_MIPI_DSI
Pablo Greco 7b2c62
+	depends on BACKLIGHT_CLASS_DEVICE
Pablo Greco 7b2c62
+	help
Pablo Greco 7b2c62
+	  Say Y here if you want to enable support for the Xingbangda XBD599
Pablo Greco 7b2c62
+	  MIPI DSI Video Mode panel.
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
 config DRM_PANEL_XINPENG_XPP055C272
Pablo Greco 7b2c62
 	tristate "Xinpeng XPP055C272 panel driver"
Pablo Greco 7b2c62
 	depends on OF
Pablo Greco 7b2c62
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
Pablo Greco 7b2c62
index e45ceac6286f..bd10617430ec 100644
Pablo Greco 7b2c62
--- a/drivers/gpu/drm/panel/Makefile
Pablo Greco 7b2c62
+++ b/drivers/gpu/drm/panel/Makefile
Pablo Greco 7b2c62
@@ -49,4 +49,5 @@ obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
Pablo Greco 7b2c62
 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
Pablo Greco 7b2c62
 obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
Pablo Greco 7b2c62
 obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
Pablo Greco 7b2c62
+obj-$(CONFIG_DRM_PANEL_XINGBANGDA_XBD599) += panel-xingbangda-xbd599.o
Pablo Greco 7b2c62
 obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
Pablo Greco 7b2c62
diff --git a/drivers/gpu/drm/panel/panel-xingbangda-xbd599.c b/drivers/gpu/drm/panel/panel-xingbangda-xbd599.c
Pablo Greco 7b2c62
new file mode 100644
Pablo Greco 7b2c62
index 000000000000..b483f96ee1db
Pablo Greco 7b2c62
--- /dev/null
Pablo Greco 7b2c62
+++ b/drivers/gpu/drm/panel/panel-xingbangda-xbd599.c
Pablo Greco 7b2c62
@@ -0,0 +1,366 @@
Pablo Greco 7b2c62
+// SPDX-License-Identifier: GPL-2.0
Pablo Greco 7b2c62
+/*
Pablo Greco 7b2c62
+ * Xingbangda XBD599 MIPI-DSI panel driver
Pablo Greco 7b2c62
+ *
Pablo Greco 7b2c62
+ * Copyright (C) 2019-2020 Icenowy Zheng <icenowy@aosc.io>
Pablo Greco 7b2c62
+ *
Pablo Greco 7b2c62
+ * Based on panel-rocktech-jh057n00900.c, which is:
Pablo Greco 7b2c62
+ *   Copyright (C) Purism SPC 2019
Pablo Greco 7b2c62
+ */
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+#include <linux/delay.h>
Pablo Greco 7b2c62
+#include <linux/gpio/consumer.h>
Pablo Greco 7b2c62
+#include <linux/mod_devicetable.h>
Pablo Greco 7b2c62
+#include <linux/module.h>
Pablo Greco 7b2c62
+#include <linux/of_device.h>
Pablo Greco 7b2c62
+#include <linux/regulator/consumer.h>
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+#include <drm/drm_mipi_dsi.h>
Pablo Greco 7b2c62
+#include <drm/drm_modes.h>
Pablo Greco 7b2c62
+#include <drm/drm_panel.h>
Pablo Greco 7b2c62
+#include <drm/drm_print.h>
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+/* Manufacturer specific Commands send via DSI */
Pablo Greco 7b2c62
+#define ST7703_CMD_ALL_PIXEL_OFF 0x22
Pablo Greco 7b2c62
+#define ST7703_CMD_ALL_PIXEL_ON	 0x23
Pablo Greco 7b2c62
+#define ST7703_CMD_SETDISP	 0xB2
Pablo Greco 7b2c62
+#define ST7703_CMD_SETRGBIF	 0xB3
Pablo Greco 7b2c62
+#define ST7703_CMD_SETCYC	 0xB4
Pablo Greco 7b2c62
+#define ST7703_CMD_SETBGP	 0xB5
Pablo Greco 7b2c62
+#define ST7703_CMD_SETVCOM	 0xB6
Pablo Greco 7b2c62
+#define ST7703_CMD_SETOTP	 0xB7
Pablo Greco 7b2c62
+#define ST7703_CMD_SETPOWER_EXT	 0xB8
Pablo Greco 7b2c62
+#define ST7703_CMD_SETEXTC	 0xB9
Pablo Greco 7b2c62
+#define ST7703_CMD_SETMIPI	 0xBA
Pablo Greco 7b2c62
+#define ST7703_CMD_SETVDC	 0xBC
Pablo Greco 7b2c62
+#define ST7703_CMD_SETSCR	 0xC0
Pablo Greco 7b2c62
+#define ST7703_CMD_SETPOWER	 0xC1
Pablo Greco 7b2c62
+#define ST7703_CMD_UNK_C6	 0xC6
Pablo Greco 7b2c62
+#define ST7703_CMD_SETPANEL	 0xCC
Pablo Greco 7b2c62
+#define ST7703_CMD_SETGAMMA	 0xE0
Pablo Greco 7b2c62
+#define ST7703_CMD_SETEQ	 0xE3
Pablo Greco 7b2c62
+#define ST7703_CMD_SETGIP1	 0xE9
Pablo Greco 7b2c62
+#define ST7703_CMD_SETGIP2	 0xEA
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static const char * const regulator_names[] = {
Pablo Greco 7b2c62
+	"iovcc",
Pablo Greco 7b2c62
+	"vcc",
Pablo Greco 7b2c62
+};
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+struct xbd599 {
Pablo Greco 7b2c62
+	struct device *dev;
Pablo Greco 7b2c62
+	struct drm_panel panel;
Pablo Greco 7b2c62
+	struct gpio_desc *reset_gpio;
Pablo Greco 7b2c62
+	struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
Pablo Greco 7b2c62
+	bool prepared;
Pablo Greco 7b2c62
+};
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static inline struct xbd599 *panel_to_xbd599(struct drm_panel *panel)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	return container_of(panel, struct xbd599, panel);
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+#define dsi_dcs_write_seq(dsi, cmd, seq...) do {			\
Pablo Greco 7b2c62
+		static const u8 d[] = { seq };				\
Pablo Greco 7b2c62
+		int ret;						\
Pablo Greco 7b2c62
+		ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d));	\
Pablo Greco 7b2c62
+		if (ret < 0)						\
Pablo Greco 7b2c62
+			return ret;					\
Pablo Greco 7b2c62
+	} while (0)
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_init_sequence(struct xbd599 *ctx)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
Pablo Greco 7b2c62
+	struct device *dev = ctx->dev;
Pablo Greco 7b2c62
+	int ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	/*
Pablo Greco 7b2c62
+	 * Init sequence was supplied by the panel vendor.
Pablo Greco 7b2c62
+	 */
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC,
Pablo Greco 7b2c62
+			  0xF1, 0x12, 0x83);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI,
Pablo Greco 7b2c62
+			  0x33, 0x81, 0x05, 0xF9, 0x0E, 0x0E, 0x20, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
Pablo Greco 7b2c62
+			  0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4F, 0x11,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x37);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT,
Pablo Greco 7b2c62
+			  0x25, 0x22, 0x20, 0x03);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF,
Pablo Greco 7b2c62
+			  0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR,
Pablo Greco 7b2c62
+			  0x73, 0x73, 0x50, 0x50, 0x00, 0xC0, 0x08, 0x70,
Pablo Greco 7b2c62
+			  0x00);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0xF0);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x0B, 0x0B, 0x10, 0x10, 0x00, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, 0xC6, 0x01, 0x00, 0xFF, 0xFF, 0x00);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER,
Pablo Greco 7b2c62
+			  0x74, 0x00, 0x32, 0x32, 0x77, 0xF1, 0xFF, 0xFF,
Pablo Greco 7b2c62
+			  0xCC, 0xCC, 0x77, 0x77);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, 0x07, 0x07);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, 0x2C, 0x2C);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, 0xBF, 0x02, 0x11, 0x00);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1,
Pablo Greco 7b2c62
+			  0x82, 0x10, 0x06, 0x05, 0xA2, 0x0A, 0xA5, 0x12,
Pablo Greco 7b2c62
+			  0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
Pablo Greco 7b2c62
+			  0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
Pablo Greco 7b2c62
+			  0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
Pablo Greco 7b2c62
+			  0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
Pablo Greco 7b2c62
+			  0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
Pablo Greco 7b2c62
+			  0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2,
Pablo Greco 7b2c62
+			  0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
Pablo Greco 7b2c62
+			  0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
Pablo Greco 7b2c62
+			  0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
Pablo Greco 7b2c62
+			  0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Pablo Greco 7b2c62
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0A,
Pablo Greco 7b2c62
+			  0xA5, 0x00, 0x00, 0x00, 0x00);
Pablo Greco 7b2c62
+	dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA,
Pablo Greco 7b2c62
+			  0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41, 0x35,
Pablo Greco 7b2c62
+			  0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12, 0x12,
Pablo Greco 7b2c62
+			  0x18, 0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41,
Pablo Greco 7b2c62
+			  0x35, 0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12,
Pablo Greco 7b2c62
+			  0x12, 0x18);
Pablo Greco 7b2c62
+	msleep(20);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
Pablo Greco 7b2c62
+	if (ret < 0) {
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+	msleep(250);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = mipi_dsi_dcs_set_display_on(dsi);
Pablo Greco 7b2c62
+	if (ret)
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+	msleep(50);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
Pablo Greco 7b2c62
+	return 0;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_prepare(struct drm_panel *panel)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = panel_to_xbd599(panel);
Pablo Greco 7b2c62
+	int ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	if (ctx->prepared)
Pablo Greco 7b2c62
+		return 0;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
Pablo Greco 7b2c62
+	if (ret)
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
Pablo Greco 7b2c62
+	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
Pablo Greco 7b2c62
+	usleep_range(20, 40);
Pablo Greco 7b2c62
+	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
Pablo Greco 7b2c62
+	msleep(20);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ctx->prepared = true;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return 0;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_enable(struct drm_panel *panel)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = panel_to_xbd599(panel);
Pablo Greco 7b2c62
+	int ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = xbd599_init_sequence(ctx);
Pablo Greco 7b2c62
+	if (ret < 0) {
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
Pablo Greco 7b2c62
+			      ret);
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return 0;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_disable(struct drm_panel *panel)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = panel_to_xbd599(panel);
Pablo Greco 7b2c62
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return mipi_dsi_dcs_set_display_off(dsi);
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_unprepare(struct drm_panel *panel)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = panel_to_xbd599(panel);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	if (!ctx->prepared)
Pablo Greco 7b2c62
+		return 0;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
Pablo Greco 7b2c62
+	regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
Pablo Greco 7b2c62
+	ctx->prepared = false;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return 0;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static const struct drm_display_mode xbd599_default_mode = {
Pablo Greco 7b2c62
+	.hdisplay    = 720,
Pablo Greco 7b2c62
+	.hsync_start = 720 + 40,
Pablo Greco 7b2c62
+	.hsync_end   = 720 + 40 + 40,
Pablo Greco 7b2c62
+	.htotal	     = 720 + 40 + 40 + 40,
Pablo Greco 7b2c62
+	.vdisplay    = 1440,
Pablo Greco 7b2c62
+	.vsync_start = 1440 + 18,
Pablo Greco 7b2c62
+	.vsync_end   = 1440 + 18 + 10,
Pablo Greco 7b2c62
+	.vtotal	     = 1440 + 18 + 10 + 17,
Pablo Greco 7b2c62
+	.vrefresh    = 60,
Pablo Greco 7b2c62
+	.clock	     = 69000,
Pablo Greco 7b2c62
+	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	.width_mm    = 68,
Pablo Greco 7b2c62
+	.height_mm   = 136,
Pablo Greco 7b2c62
+	.type        = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
Pablo Greco 7b2c62
+};
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_get_modes(struct drm_panel *panel,
Pablo Greco 7b2c62
+			    struct drm_connector *connector)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = panel_to_xbd599(panel);
Pablo Greco 7b2c62
+	struct drm_display_mode *mode;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	mode = drm_mode_duplicate(connector->dev, &xbd599_default_mode);
Pablo Greco 7b2c62
+	if (!mode) {
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(ctx->dev, "Failed to add mode\n");
Pablo Greco 7b2c62
+		return -ENOMEM;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	drm_mode_set_name(mode);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
Pablo Greco 7b2c62
+	connector->display_info.width_mm = mode->width_mm;
Pablo Greco 7b2c62
+	connector->display_info.height_mm = mode->height_mm;
Pablo Greco 7b2c62
+	drm_mode_probed_add(connector, mode);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return 1;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static const struct drm_panel_funcs xbd599_drm_funcs = {
Pablo Greco 7b2c62
+	.prepare   = xbd599_prepare,
Pablo Greco 7b2c62
+	.enable    = xbd599_enable,
Pablo Greco 7b2c62
+	.disable   = xbd599_disable,
Pablo Greco 7b2c62
+	.unprepare = xbd599_unprepare,
Pablo Greco 7b2c62
+	.get_modes = xbd599_get_modes,
Pablo Greco 7b2c62
+};
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_probe(struct mipi_dsi_device *dsi)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct device *dev = &dsi->dev;
Pablo Greco 7b2c62
+	struct xbd599 *ctx;
Pablo Greco 7b2c62
+	int i, ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
Pablo Greco 7b2c62
+	if (!ctx)
Pablo Greco 7b2c62
+		return -ENOMEM;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
Pablo Greco 7b2c62
+		ctx->supplies[i].supply = regulator_names[i];
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
Pablo Greco 7b2c62
+				      ctx->supplies);
Pablo Greco 7b2c62
+	if (ret < 0) {
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(&dsi->dev, "cannot get regulators\n");
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
Pablo Greco 7b2c62
+	if (IS_ERR(ctx->reset_gpio)) {
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
Pablo Greco 7b2c62
+		return PTR_ERR(ctx->reset_gpio);
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	mipi_dsi_set_drvdata(dsi, ctx);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ctx->dev = dev;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	dsi->lanes = 4;
Pablo Greco 7b2c62
+	dsi->format = MIPI_DSI_FMT_RGB888;
Pablo Greco 7b2c62
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	drm_panel_init(&ctx->panel, &dsi->dev, &xbd599_drm_funcs,
Pablo Greco 7b2c62
+		       DRM_MODE_CONNECTOR_DSI);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = drm_panel_of_backlight(&ctx->panel);
Pablo Greco 7b2c62
+	if (ret)
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	drm_panel_add(&ctx->panel);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = mipi_dsi_attach(dsi);
Pablo Greco 7b2c62
+	if (ret < 0) {
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
Pablo Greco 7b2c62
+		drm_panel_remove(&ctx->panel);
Pablo Greco 7b2c62
+		return ret;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
Pablo Greco 7b2c62
+		     xbd599_default_mode.hdisplay,
Pablo Greco 7b2c62
+		     xbd599_default_mode.vdisplay,
Pablo Greco 7b2c62
+		     xbd599_default_mode.vrefresh,
Pablo Greco 7b2c62
+		     mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return 0;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static void xbd599_shutdown(struct mipi_dsi_device *dsi)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = mipi_dsi_get_drvdata(dsi);
Pablo Greco 7b2c62
+	int ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = drm_panel_unprepare(&ctx->panel);
Pablo Greco 7b2c62
+	if (ret < 0)
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
Pablo Greco 7b2c62
+			      ret);
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static int xbd599_remove(struct mipi_dsi_device *dsi)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct xbd599 *ctx = mipi_dsi_get_drvdata(dsi);
Pablo Greco 7b2c62
+	int ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	xbd599_shutdown(dsi);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = mipi_dsi_detach(dsi);
Pablo Greco 7b2c62
+	if (ret < 0)
Pablo Greco 7b2c62
+		DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n",
Pablo Greco 7b2c62
+			      ret);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	drm_panel_remove(&ctx->panel);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	return 0;
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static const struct of_device_id xbd599_of_match[] = {
Pablo Greco 7b2c62
+	{ .compatible = "xingbangda,xbd599", },
Pablo Greco 7b2c62
+	{ /* sentinel */ }
Pablo Greco 7b2c62
+};
Pablo Greco 7b2c62
+MODULE_DEVICE_TABLE(of, xbd599_of_match);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static struct mipi_dsi_driver xbd599_driver = {
Pablo Greco 7b2c62
+	.probe	= xbd599_probe,
Pablo Greco 7b2c62
+	.remove = xbd599_remove,
Pablo Greco 7b2c62
+	.shutdown = xbd599_shutdown,
Pablo Greco 7b2c62
+	.driver = {
Pablo Greco 7b2c62
+		.name = "panel-xingbangda-xbd599",
Pablo Greco 7b2c62
+		.of_match_table = xbd599_of_match,
Pablo Greco 7b2c62
+	},
Pablo Greco 7b2c62
+};
Pablo Greco 7b2c62
+module_mipi_dsi_driver(xbd599_driver);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
Pablo Greco 7b2c62
+MODULE_DESCRIPTION("DRM driver for Xingbangda XBD599 MIPI DSI panel");
Pablo Greco 7b2c62
+MODULE_LICENSE("GPL v2");
Pablo Greco 7b2c62
-- 
Pablo Greco 7b2c62
2.28.0
Pablo Greco 7b2c62