mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] video: panel: add support for Sitronix ST7703 MIPI-DSI panel
@ 2025-10-27  6:57 Ahmad Fatoum
  2025-10-28  7:30 ` Sascha Hauer
  0 siblings, 1 reply; 2+ messages in thread
From: Ahmad Fatoum @ 2025-10-27  6:57 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

This panel has been tested with the RK3566 VOP2 MIPI-DSI on the
Powkiddy RGB30. Support for MIPI-DSI on Rockchip is not upstream yet,
but the panel can be used with other MIPI-DSI controllers as well.

Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
 drivers/video/Kconfig                 |  13 +
 drivers/video/Makefile                |   1 +
 drivers/video/panel-sitronix-st7703.c | 903 ++++++++++++++++++++++++++
 3 files changed, 917 insertions(+)
 create mode 100644 drivers/video/panel-sitronix-st7703.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b2eccd5db7fe..4605634bb581 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -202,6 +202,19 @@ config DRIVER_VIDEO_PANEL_SITRONIX_ST7789V
 	  Say Y here if you want to enable support for the Sitronix
 	  ST7789V controller for 240x320 LCD panels
 
+config DRIVER_VIDEO_PANEL_SITRONIX_ST7703
+	tristate "Sitronix ST7703 based MIPI touchscreen panels"
+	depends on OF
+	depends on DRIVER_VIDEO_MIPI_DSI
+	depends on DRIVER_VIDEO_BACKLIGHT
+	help
+	  Say Y here if you want to enable support for Sitronix ST7703 based
+	  panels, souch as Rocktech JH057N00900 MIPI DSI panel as e.g. used in
+	  the Librem 5 devkit. It has a resolution of 720x1440 pixels, a built
+	  in backlight and touch controller.
+	  Touch input support is provided by the goodix driver and needs to be
+	  selected separately.
+
 config DRIVER_VIDEO_PANEL_ORISETECH_OTM8009A
 	tristate "Orise Technology otm8009a 480x800 dsi 2dl panel"
 	depends on OF
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 470a5abaa450..8dfebe2be6a2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_DRIVER_VIDEO_MIPI_DSI) += mipi_dsi.o
 obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
 obj-$(CONFIG_DRIVER_VIDEO_PANEL_MIPI_DBI) += panel-mipi-dbi.o
 obj-$(CONFIG_DRIVER_VIDEO_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
+obj-$(CONFIG_DRIVER_VIDEO_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
 obj-$(CONFIG_DRIVER_VIDEO_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
 
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
diff --git a/drivers/video/panel-sitronix-st7703.c b/drivers/video/panel-sitronix-st7703.c
new file mode 100644
index 000000000000..a1c87c74bb6d
--- /dev/null
+++ b/drivers/video/panel-sitronix-st7703.c
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for panels based on Sitronix ST7703 controller, souch as:
+ *
+ * - Rocktech jh057n00900 5.5" MIPI-DSI panel
+ *
+ * Copyright (C) Purism SPC 2019
+ */
+
+#include <clock.h>
+#include <linux/gpio/consumer.h>
+#include <video/media-bus-format.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <of.h>
+#include <of_device.h>
+#include <regulator.h>
+
+#include <video/vpl.h>
+#include <video/mipi_display.h>
+#include <video/mipi_dsi.h>
+#include <video/drm/drm_modes.h>
+#include <video/drm/drm_connector.h>
+#include <video/videomode.h>
+#include <video/backlight.h>
+
+#define DRV_NAME "panel-sitronix-st7703"
+
+/* Manufacturer specific Commands send via DSI */
+#define ST7703_CMD_ALL_PIXEL_OFF 0x22
+#define ST7703_CMD_ALL_PIXEL_ON	 0x23
+#define ST7703_CMD_SETAPID	 0xB1
+#define ST7703_CMD_SETDISP	 0xB2
+#define ST7703_CMD_SETRGBIF	 0xB3
+#define ST7703_CMD_SETCYC	 0xB4
+#define ST7703_CMD_SETBGP	 0xB5
+#define ST7703_CMD_SETVCOM	 0xB6
+#define ST7703_CMD_SETOTP	 0xB7
+#define ST7703_CMD_SETPOWER_EXT	 0xB8
+#define ST7703_CMD_SETEXTC	 0xB9
+#define ST7703_CMD_SETMIPI	 0xBA
+#define ST7703_CMD_SETVDC	 0xBC
+#define ST7703_CMD_UNKNOWN_BF	 0xBF
+#define ST7703_CMD_SETSCR	 0xC0
+#define ST7703_CMD_SETPOWER	 0xC1
+#define ST7703_CMD_SETECO	 0xC6
+#define ST7703_CMD_SETIO	 0xC7
+#define ST7703_CMD_SETCABC	 0xC8
+#define ST7703_CMD_SETPANEL	 0xCC
+#define ST7703_CMD_SETGAMMA	 0xE0
+#define ST7703_CMD_SETEQ	 0xE3
+#define ST7703_CMD_SETGIP1	 0xE9
+#define ST7703_CMD_SETGIP2	 0xEA
+#define ST7703_CMD_UNKNOWN_EF	 0xEF
+
+struct st7703 {
+	struct device *dev;
+	struct vpl vpl;
+	struct gpio_desc *reset_gpio;
+	struct regulator *vcc;
+	struct regulator *iovcc;
+	struct backlight_device *backlight;
+
+	const struct st7703_panel_desc *desc;
+	int allpixelson;
+};
+
+struct st7703_panel_desc {
+	const struct drm_display_mode *mode;
+	unsigned int lanes;
+	unsigned long mode_flags;
+	enum mipi_dsi_pixel_format format;
+	void (*init_sequence)(struct mipi_dsi_multi_context *dsi_ctx);
+};
+
+static void jh057n_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/*
+	 * Init sequence was supplied by the panel vendor. Most of the commands
+	 * resemble the ST7703 but the number of parameters often don't match
+	 * so it's likely a clone.
+	 */
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC,
+					 0xF1, 0x12, 0x83);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF,
+					 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
+					 0x00, 0x00);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR,
+					 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
+					 0x00);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x4E);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0B);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ,
+					 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
+					 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x08, 0x08);
+	mipi_dsi_mdelay(dsi_ctx, 20);
+
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1,
+					 0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
+					 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
+					 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
+					 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
+					 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
+					 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+					 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2,
+					 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
+					 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
+					 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+					 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
+					 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
+					 0xA5, 0x00, 0x00, 0x00, 0x00);
+	mipi_dsi_generic_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA,
+					 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
+					 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
+					 0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
+					 0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
+					 0x11, 0x18);
+	mipi_dsi_mdelay(dsi_ctx, 20);
+}
+
+static const struct drm_display_mode jh057n00900_mode = {
+	.hdisplay    = 720,
+	.hsync_start = 720 + 90,
+	.hsync_end   = 720 + 90 + 20,
+	.htotal	     = 720 + 90 + 20 + 20,
+	.vdisplay    = 1440,
+	.vsync_start = 1440 + 20,
+	.vsync_end   = 1440 + 20 + 4,
+	.vtotal	     = 1440 + 20 + 4 + 12,
+	.clock	     = 75276,
+	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm    = 65,
+	.height_mm   = 130,
+};
+
+static const struct st7703_panel_desc jh057n00900_panel_desc = {
+	.mode = &jh057n00900_mode,
+	.lanes = 4,
+	.mode_flags = MIPI_DSI_MODE_VIDEO |
+		MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+	.format = MIPI_DSI_FMT_RGB888,
+	.init_sequence = jh057n_init_sequence,
+};
+
+static void xbd599_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/*
+	 * Init sequence was supplied by the panel vendor.
+	 */
+
+	/* Magic sequence to unlock user commands below. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xF1, 0x12, 0x83);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI,
+				     0x33, /* VC_main = 0, Lane_Number = 3 (4 lanes) */
+				     0x81, /* DSI_LDO_SEL = 1.7V, RTERM = 90 Ohm */
+				     0x05, /* IHSRX = x6 (Low High Speed driving ability) */
+				     0xF9, /* TX_CLK_SEL = fDSICLK/16 */
+				     0x0E, /* HFP_OSC (min. HFP number in DSI mode) */
+				     0x0E, /* HBP_OSC (min. HBP number in DSI mode) */
+				     /* The rest is undocumented in ST7703 datasheet */
+				     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x44, 0x25, 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02,
+				     0x4F, 0x11, 0x00, 0x00, 0x37);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT,
+				     0x25, /* PCCS = 2, ECP_DC_DIV = 1/4 HSYNC */
+				     0x22, /* DT = 15ms XDK_ECP = x2 */
+				     0x20, /* PFM_DC_DIV = /1 */
+				     0x03  /* ECP_SYNC_EN = 1, VGX_SYNC_EN = 1 */);
+
+	/* RGB I/F porch timing */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF,
+				     0x10, /* VBP_RGB_GEN */
+				     0x10, /* VFP_RGB_GEN */
+				     0x05, /* DE_BP_RGB_GEN */
+				     0x05, /* DE_FP_RGB_GEN */
+				     /* The rest is undocumented in ST7703 datasheet */
+				     0x03, 0xFF,
+				     0x00, 0x00,
+				     0x00, 0x00);
+
+	/* Source driving settings. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR,
+				     0x73, /* N_POPON */
+				     0x73, /* N_NOPON */
+				     0x50, /* I_POPON */
+				     0x50, /* I_NOPON */
+				     0x00, /* SCR[31,24] */
+				     0xC0, /* SCR[23,16] */
+				     0x08, /* SCR[15,8] */
+				     0x70, /* SCR[7,0] */
+				     0x00  /* Undocumented */);
+
+	/* NVDDD_SEL = -1.8V, VDDD_SEL = out of range (possibly 1.9V?) */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x4E);
+
+	/*
+	 * SS_PANEL = 1 (reverse scan), GS_PANEL = 0 (normal scan)
+	 * REV_PANEL = 1 (normally black panel), BGR_PANEL = 1 (BGR)
+	 */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0B);
+
+	/* Zig-Zag Type C column inversion. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+
+	/* Set display resolution. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP,
+				     0xF0, /* NL = 240 */
+				     0x12, /* RES_V_LSB = 0, BLK_CON = VSSD,
+					    * RESO_SEL = 720RGB
+					    */
+				     0xF0  /* WHITE_GND_EN = 1 (GND),
+					    * WHITE_FRAME_SEL = 7 frames,
+					    * ISC = 0 frames
+					    */);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ,
+				     0x00, /* PNOEQ */
+				     0x00, /* NNOEQ */
+				     0x0B, /* PEQGND */
+				     0x0B, /* NEQGND */
+				     0x10, /* PEQVCI */
+				     0x10, /* NEQVCI */
+				     0x00, /* PEQVCI1 */
+				     0x00, /* NEQVCI1 */
+				     0x00, /* reserved */
+				     0x00, /* reserved */
+				     0xFF, /* reserved */
+				     0x00, /* reserved */
+				     0xC0, /* ESD_DET_DATA_WHITE = 1, ESD_WHITE_EN = 1 */
+				     0x10  /* SLPIN_OPTION = 1 (no need vsync after sleep-in)
+					    * VEDIO_NO_CHECK_EN = 0
+					    * ESD_WHITE_GND_EN = 0
+					    * ESD_DET_TIME_SEL = 0 frames
+					    */);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETECO, 0x01, 0x00, 0xFF, 0xFF, 0x00);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER,
+				     0x74, /* VBTHS, VBTLS: VGH = 17V, VBL = -11V */
+				     0x00, /* FBOFF_VGH = 0, FBOFF_VGL = 0 */
+				     0x32, /* VRP  */
+				     0x32, /* VRN */
+				     0x77, /* reserved */
+				     0xF1, /* APS = 1 (small),
+					    * VGL_DET_EN = 1, VGH_DET_EN = 1,
+					    * VGL_TURBO = 1, VGH_TURBO = 1
+					    */
+				     0xFF, /* VGH1_L_DIV, VGL1_L_DIV (1.5MHz) */
+				     0xFF, /* VGH1_R_DIV, VGL1_R_DIV (1.5MHz) */
+				     0xCC, /* VGH2_L_DIV, VGL2_L_DIV (2.6MHz) */
+				     0xCC, /* VGH2_R_DIV, VGL2_R_DIV (2.6MHz) */
+				     0x77, /* VGH3_L_DIV, VGL3_L_DIV (4.5MHz) */
+				     0x77  /* VGH3_R_DIV, VGL3_R_DIV (4.5MHz) */);
+
+	/* Reference voltage. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP,
+				     0x07, /* VREF_SEL = 4.2V */
+				     0x07  /* NVREF_SEL = 4.2V */);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM,
+				     0x2C, /* VCOMDC_F = -0.67V */
+				     0x2C  /* VCOMDC_B = -0.67V */);
+
+	/* Undocumented command. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
+
+	/* This command is to set forward GIP timing. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1,
+				     0x82, 0x10, 0x06, 0x05, 0xA2, 0x0A, 0xA5, 0x12,
+				     0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
+				     0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
+				     0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
+				     0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+				     0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+	/* This command is to set backward GIP timing. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2,
+				     0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
+				     0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+				     0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0A,
+				     0xA5, 0x00, 0x00, 0x00, 0x00);
+
+	/* Adjust the gamma characteristics of the panel. */
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA,
+				     0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41, 0x35,
+				     0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12, 0x12,
+				     0x18, 0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41,
+				     0x35, 0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12,
+				     0x12, 0x18);
+}
+
+static const struct drm_display_mode xbd599_mode = {
+	.hdisplay    = 720,
+	.hsync_start = 720 + 40,
+	.hsync_end   = 720 + 40 + 40,
+	.htotal	     = 720 + 40 + 40 + 40,
+	.vdisplay    = 1440,
+	.vsync_start = 1440 + 18,
+	.vsync_end   = 1440 + 18 + 10,
+	.vtotal	     = 1440 + 18 + 10 + 17,
+	.clock	     = 69000,
+	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm    = 68,
+	.height_mm   = 136,
+};
+
+static const struct st7703_panel_desc xbd599_desc = {
+	.mode = &xbd599_mode,
+	.lanes = 4,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+	.format = MIPI_DSI_FMT_RGB888,
+	.init_sequence = xbd599_init_sequence,
+};
+
+static void rg353v2_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/*
+	 * Init sequence was supplied by the panel vendor.
+	 */
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETAPID, 0x00, 0x00, 0x00,
+				     0xda, 0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0x00, 0x13, 0x70);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
+				     0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x0a, 0x0a);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x92, 0x92);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22,
+				     0xf0, 0x63);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05,
+				     0xf9, 0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a,
+				     0x00, 0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x47);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
+				     0x00, 0x00, 0x12, 0x50, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x53, 0xc0, 0x32,
+				     0x32, 0x77, 0xe1, 0xdd, 0xdd, 0x77, 0x77, 0x33,
+				     0x33);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETECO, 0x82, 0x00, 0xbf, 0xff,
+				     0x00, 0xff);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETIO, 0xb8, 0x00, 0x0a, 0x00,
+				     0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCABC, 0x10, 0x40, 0x1e,
+				     0x02);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x07, 0x0d,
+				     0x37, 0x35, 0x3f, 0x41, 0x44, 0x06, 0x0c, 0x0d,
+				     0x0f, 0x11, 0x10, 0x12, 0x14, 0x1a, 0x00, 0x07,
+				     0x0d, 0x37, 0x35, 0x3f, 0x41, 0x44, 0x06, 0x0c,
+				     0x0d, 0x0f, 0x11, 0x10, 0x12, 0x14, 0x1a);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0b, 0x0b,
+				     0x0b, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+				     0xc0, 0x10);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x02, 0x00,
+				     0x00, 0xb0, 0xb1, 0x11, 0x31, 0x23, 0x28, 0x80,
+				     0xb0, 0xb1, 0x27, 0x08, 0x00, 0x04, 0x02, 0x00,
+				     0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00,
+				     0x88, 0x88, 0xba, 0x60, 0x24, 0x08, 0x88, 0x88,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0xba, 0x71, 0x35,
+				     0x18, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
+				     0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x97, 0x0a, 0x82, 0x02,
+				     0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x81, 0x88, 0xba, 0x17, 0x53, 0x88, 0x88, 0x88,
+				     0x88, 0x88, 0x88, 0x80, 0x88, 0xba, 0x06, 0x42,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00,
+				     0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff, 0x01);
+}
+
+static const struct drm_display_mode rg353v2_mode = {
+	.hdisplay	= 640,
+	.hsync_start	= 640 + 40,
+	.hsync_end	= 640 + 40 + 2,
+	.htotal		= 640 + 40 + 2 + 80,
+	.vdisplay	= 480,
+	.vsync_start	= 480 + 18,
+	.vsync_end	= 480 + 18 + 2,
+	.vtotal		= 480 + 18 + 2 + 28,
+	.clock		= 24150,
+	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm	= 70,
+	.height_mm	= 57,
+};
+
+static const struct st7703_panel_desc rg353v2_desc = {
+	.mode = &rg353v2_mode,
+	.lanes = 4,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
+	.format = MIPI_DSI_FMT_RGB888,
+	.init_sequence = rg353v2_init_sequence,
+};
+
+static void rgb30panel_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/* Init sequence extracted from Powkiddy RGB30 BSP kernel. */
+
+	/*
+	 * For some reason this specific panel must be taken out of sleep
+	 * before the full init sequence, or else it will not display.
+	 */
+	mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
+	mipi_dsi_mdelay(dsi_ctx, 250);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05, 0xf9,
+				     0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a, 0x00,
+				     0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22, 0xf0,
+				     0x63);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
+				     0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
+				     0x00, 0x00, 0x12, 0x70, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x46);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0x3c, 0x12, 0x30);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0b, 0x0b,
+				     0x03, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+				     0xc0, 0x10);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x36, 0x00, 0x32,
+				     0x32, 0x77, 0xf1, 0xcc, 0xcc, 0x77, 0x77, 0x33,
+				     0x33);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x0a, 0x0a);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x88, 0x88);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x0a, 0x10,
+				     0x0f, 0xa1, 0x80, 0x12, 0x31, 0x23, 0x47, 0x86,
+				     0xa1, 0x80, 0x47, 0x08, 0x00, 0x00, 0x0d, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+				     0x48, 0x02, 0x8b, 0xaf, 0x46, 0x02, 0x88, 0x88,
+				     0x88, 0x88, 0x88, 0x48, 0x13, 0x8b, 0xaf, 0x57,
+				     0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x96, 0x12, 0x01, 0x01,
+				     0x01, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x4f, 0x31, 0x8b, 0xa8, 0x31, 0x75, 0x88, 0x88,
+				     0x88, 0x88, 0x88, 0x4f, 0x20, 0x8b, 0xa8, 0x20,
+				     0x64, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00,
+				     0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x40, 0xa1, 0x80, 0x00, 0x00, 0x00,
+				     0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x0a, 0x0f,
+				     0x29, 0x3b, 0x3f, 0x42, 0x39, 0x06, 0x0d, 0x10,
+				     0x13, 0x15, 0x14, 0x15, 0x10, 0x17, 0x00, 0x0a,
+				     0x0f, 0x29, 0x3b, 0x3f, 0x42, 0x39, 0x06, 0x0d,
+				     0x10, 0x13, 0x15, 0x14, 0x15, 0x10, 0x17);
+}
+
+static const struct drm_display_mode rgb30panel_mode = {
+	.hdisplay	= 720,
+	.hsync_start	= 720 + 45,
+	.hsync_end	= 720 + 45 + 4,
+	.htotal		= 720 + 45 + 4 + 45,
+	.vdisplay	= 720,
+	.vsync_start	= 720 + 15,
+	.vsync_end	= 720 + 15 + 3,
+	.vtotal		= 720 + 15 + 3 + 11,
+	.clock		= 36570,
+	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm	= 76,
+	.height_mm	= 76,
+};
+
+static const struct st7703_panel_desc rgb30panel_desc = {
+	.mode = &rgb30panel_mode,
+	.lanes = 4,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
+	.format = MIPI_DSI_FMT_RGB888,
+	.init_sequence = rgb30panel_init_sequence,
+};
+
+static void rgb10max3_panel_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/* Init sequence extracted from Powkiddy RGB10MAX3 BSP kernel. */
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETAPID, 0x00, 0x00, 0x00, 0xda,
+				     0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0xc8, 0x02, 0x30);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
+				     0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x04, 0x04);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x78, 0x78);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22, 0xf0,
+				     0x63);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05, 0xf9,
+				     0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a, 0x00,
+				     0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x47);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
+				     0x00, 0x00, 0x12, 0x70, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x25, 0x00, 0x32,
+				     0x32, 0x77, 0xe1, 0xff, 0xff, 0xcc, 0xcc, 0x77,
+				     0x77);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETECO, 0x82, 0x00, 0xbf, 0xff,
+				     0x00, 0xff);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETIO, 0xb8, 0x00, 0x0a, 0x00,
+				     0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCABC, 0x10, 0x40, 0x1e,
+				     0x02);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x04, 0x07,
+				     0x2a, 0x39, 0x3f, 0x36, 0x31, 0x06, 0x0b, 0x0e,
+				     0x12, 0x14, 0x12, 0x13, 0x0f, 0x17, 0x00, 0x04,
+				     0x07, 0x2a, 0x39, 0x3f, 0x36, 0x31, 0x06, 0x0b,
+				     0x0e, 0x12, 0x14, 0x12, 0x13, 0x0f, 0x17);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x03, 0x03, 0x03, 0x03,
+				     0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80,
+				     0xc0, 0x10);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x08, 0x00,
+				     0x00, 0x41, 0xf8, 0x12, 0x31, 0x23, 0x37, 0x86,
+				     0x11, 0xc8, 0x37, 0x2a, 0x00, 0x00, 0x0c, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+				     0x88, 0x20, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88,
+				     0x88, 0x88, 0xff, 0x88, 0x31, 0x57, 0x13, 0x88,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0xff, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x00, 0x1a, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x8f, 0x13, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88,
+				     0x88, 0x88, 0xf8, 0x8f, 0x02, 0x20, 0x64, 0x88,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0xf8, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff, 0x01);
+}
+
+static const struct drm_display_mode rgb10max3_panel_mode = {
+	.hdisplay	= 720,
+	.hsync_start	= 720 + 40,
+	.hsync_end	= 720 + 40 + 10,
+	.htotal		= 720 + 40 + 10 + 40,
+	.vdisplay	= 1280,
+	.vsync_start	= 1280 + 16,
+	.vsync_end	= 1280 + 16 + 4,
+	.vtotal		= 1280 + 16 + 4 + 14,
+	.clock		= 63800,
+	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm	= 62,
+	.height_mm	= 109,
+};
+
+static const struct st7703_panel_desc rgb10max3_panel_desc = {
+	.mode = &rgb10max3_panel_mode,
+	.lanes = 4,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
+	.format = MIPI_DSI_FMT_RGB888,
+	.init_sequence = rgb10max3_panel_init_sequence,
+};
+
+static void gameforcechi_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+	/*
+	 * Init sequence was supplied by the panel vendor. Panel will not
+	 * respond to commands until it is brought out of sleep mode first.
+	 */
+
+	mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
+	mipi_dsi_mdelay(dsi_ctx, 250);
+
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x31, 0x81, 0x05, 0xf9,
+				     0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x44, 0x25, 0x00, 0x91, 0x0a, 0x00,
+				     0x00, 0x02, 0x4f, 0xd1, 0x00, 0x00, 0x37);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x25);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x0c, 0x10, 0x0a,
+				     0x50, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
+				     0x00, 0x00, 0x08, 0x70, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x46);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0x00, 0x13, 0xf0);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0b, 0x0b,
+				     0x03, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+				     0xc0, 0x10);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x53, 0x00, 0x1e,
+				     0x1e, 0x77, 0xe1, 0xcc, 0xdd, 0x67, 0x77, 0x33,
+				     0x33);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x10, 0x10);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x6c, 0x7c);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0x08, 0x00, 0x0e, 0x00,
+				     0x00, 0xb0, 0xb1, 0x11, 0x31, 0x23, 0x28, 0x10,
+				     0xb0, 0xb1, 0x27, 0x08, 0x00, 0x04, 0x02, 0x00,
+				     0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00,
+				     0x88, 0x88, 0xba, 0x60, 0x24, 0x08, 0x88, 0x88,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0xba, 0x71, 0x35,
+				     0x18, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
+				     0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x97, 0x0a, 0x82, 0x02,
+				     0x13, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x80, 0x88, 0xba, 0x17, 0x53, 0x88, 0x88, 0x88,
+				     0x88, 0x88, 0x88, 0x81, 0x88, 0xba, 0x06, 0x42,
+				     0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x10,
+				     0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00);
+	mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x07, 0x0b,
+				     0x27, 0x2d, 0x3f, 0x3b, 0x37, 0x05, 0x0a, 0x0b,
+				     0x0f, 0x11, 0x0f, 0x12, 0x12, 0x18, 0x00, 0x07,
+				     0x0b, 0x27, 0x2d, 0x3f, 0x3b, 0x37, 0x05, 0xa0,
+				     0x0b, 0x0f, 0x11, 0x0f, 0x12, 0x12, 0x18);
+}
+
+static const struct drm_display_mode gameforcechi_mode = {
+	.hdisplay	= 640,
+	.hsync_start	= 640 + 40,
+	.hsync_end	= 640 + 40 + 2,
+	.htotal		= 640 + 40 + 2 + 80,
+	.vdisplay	= 480,
+	.vsync_start	= 480 + 17,
+	.vsync_end	= 480 + 17 + 5,
+	.vtotal		= 480 + 17 + 5 + 13,
+	.clock		= 23546,
+	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+	.width_mm	= 71,
+	.height_mm	= 53,
+};
+
+static const struct st7703_panel_desc gameforcechi_desc = {
+	.mode = &gameforcechi_mode,
+	.lanes = 2,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
+	.format = MIPI_DSI_FMT_RGB888,
+	.init_sequence = gameforcechi_init_sequence,
+};
+
+static int st7703_enable(struct st7703 *ctx)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
+
+	ctx->desc->init_sequence(&dsi_ctx);
+
+	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+
+	/* It takes the controller 120 msec to wake up after sleep. */
+	mipi_dsi_mdelay(&dsi_ctx, 120);
+
+	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+	if  (dsi_ctx.accum_err)
+		return dsi_ctx.accum_err;
+
+	dev_dbg(ctx->dev, "Panel init sequence done\n");
+	return backlight_enable(ctx->backlight);
+}
+
+static int st7703_disable(struct st7703 *ctx)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
+
+	backlight_disable(ctx->backlight);
+
+	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+
+	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+
+	/* It takes the controller 120 msec to enter sleep mode. */
+	mipi_dsi_mdelay(&dsi_ctx, 120);
+
+	return dsi_ctx.accum_err;
+}
+
+static int st7703_unprepare(struct st7703 *ctx)
+{
+
+	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+	regulator_disable(ctx->iovcc);
+	regulator_disable(ctx->vcc);
+
+	return 0;
+}
+
+static int st7703_prepare(struct st7703 *ctx)
+{
+	int ret;
+
+	dev_dbg(ctx->dev, "Resetting the panel\n");
+	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+	ret = regulator_enable(ctx->iovcc);
+	if (ret < 0) {
+		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_enable(ctx->vcc);
+	if (ret < 0) {
+		dev_err(ctx->dev, "Failed to enable vcc supply: %d\n", ret);
+		regulator_disable(ctx->iovcc);
+		return ret;
+	}
+
+	/* Give power supplies time to stabilize before deasserting reset. */
+	udelay(10000);
+
+	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+	udelay(15000);
+
+	return 0;
+}
+
+static int st7703_get_modes(struct st7703 *ctx,
+			    struct display_timings *timings)
+{
+	struct fb_videomode *mode;
+
+	mode = xzalloc(sizeof(*mode));
+
+	drm_display_mode_to_fb_videomode(ctx->desc->mode, mode);
+
+	timings->modes = mode;
+	timings->num_modes = 1;
+
+	return 0;
+}
+
+static int st7703_allpixelson_set(struct param_d *param, void *priv)
+{
+	struct st7703 *ctx = priv;
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
+
+	dev_dbg(ctx->dev, "Setting all pixels %s\n",
+		ctx->allpixelson ? "on" : "off");
+
+	if (ctx->allpixelson)
+		mipi_dsi_generic_write_seq_multi(&dsi_ctx, ST7703_CMD_ALL_PIXEL_ON);
+	else
+		mipi_dsi_generic_write_seq_multi(&dsi_ctx, ST7703_CMD_ALL_PIXEL_OFF);
+
+	/*
+	 * Reset the panel to get video back. NOTE: This isn't a
+	 * particularly safe thing to do in general because it assumes
+	 * that the screen was on to begin with, but this is just a
+	 * debug parameter so it's not a huge deal.
+	 */
+	st7703_disable(ctx);
+	st7703_unprepare(ctx);
+	st7703_prepare(ctx);
+	st7703_enable(ctx);
+
+	return dsi_ctx.accum_err;
+}
+
+static int st7703_ioctl(struct vpl *vpl, unsigned int port,
+			unsigned int cmd, void *ptr)
+{
+	struct st7703 *ctx = container_of(vpl, struct st7703, vpl);
+
+	switch (cmd) {
+	case VPL_PREPARE:
+		return st7703_prepare(ctx);
+	case VPL_ENABLE:
+		return st7703_enable(ctx);
+	case VPL_DISABLE:
+		return st7703_disable(ctx);
+	case VPL_UNPREPARE:
+		return st7703_unprepare(ctx);
+	case VPL_GET_VIDEOMODES:
+		return st7703_get_modes(ctx, ptr);
+	case VPL_GET_BUS_FORMAT:
+		*(u32 *)ptr = MEDIA_BUS_FMT_RGB888_1X24;
+		return 0;
+	default:
+		return 0;
+	}
+}
+
+static int st7703_probe(struct mipi_dsi_device *dsi)
+{
+	struct device_node *backlight_node;
+	struct device *dev = &dsi->dev;
+	struct st7703 *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ctx->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset gpio\n");
+
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	ctx->dev = dev;
+	ctx->desc = of_device_get_match_data(dev);
+
+	dsi->mode_flags = ctx->desc->mode_flags;
+	dsi->format = ctx->desc->format;
+	dsi->lanes = ctx->desc->lanes;
+
+	ctx->vcc = regulator_get(dev, "vcc");
+	if (IS_ERR(ctx->vcc))
+		return dev_err_probe(dev, PTR_ERR(ctx->vcc), "Failed to request vcc regulator\n");
+
+	ctx->iovcc = regulator_get(dev, "iovcc");
+	if (IS_ERR(ctx->iovcc))
+		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
+				     "Failed to request iovcc regulator\n");
+
+	backlight_node = of_parse_phandle(dev->device_node, "backlight", 0);
+	if (backlight_node) {
+		ctx->backlight = of_backlight_find(backlight_node);
+		if (!ctx->backlight) {
+			dev_err(dev, "Cannot find backlight\n");
+			return -ENODEV;
+		}
+	}
+
+	ctx->vpl.node = dev->of_node;
+	ctx->vpl.ioctl = st7703_ioctl;
+
+	ret = vpl_register(&ctx->vpl);
+	if (ret)
+		return ret;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(dev, "mipi_dsi_attach failed (%d). Is host ready?\n", ret);
+		return ret;
+	}
+
+	dev_info(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
+		 ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
+		 drm_mode_vrefresh(ctx->desc->mode),
+		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
+
+	dev_add_param_bool(dev, "allpixelson", st7703_allpixelson_set, NULL,
+			&ctx->allpixelson, ctx);
+	return 0;
+}
+
+static const struct of_device_id st7703_of_match[] = {
+	{ .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc },
+	{ .compatible = "gameforce,chi-panel", .data = &gameforcechi_desc },
+	{ .compatible = "powkiddy,rgb10max3-panel", .data = &rgb10max3_panel_desc },
+	{ .compatible = "powkiddy,rgb30-panel", .data = &rgb30panel_desc },
+	{ .compatible = "rocktech,jh057n00900", .data = &jh057n00900_panel_desc },
+	{ .compatible = "xingbangda,xbd599", .data = &xbd599_desc },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, st7703_of_match);
+
+static struct mipi_dsi_driver st7703_driver = {
+	.probe	= st7703_probe,
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = st7703_of_match,
+	},
+};
+module_mipi_dsi_driver(st7703_driver);
+
+MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
+MODULE_DESCRIPTION("DRM driver for Sitronix ST7703 based MIPI DSI panels");
+MODULE_LICENSE("GPL v2");
-- 
2.47.3




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

end of thread, other threads:[~2025-10-28  7:31 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-10-27  6:57 [PATCH] video: panel: add support for Sitronix ST7703 MIPI-DSI panel Ahmad Fatoum
2025-10-28  7:30 ` Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox