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