xref: /openbmc/linux/drivers/gpu/drm/panel/panel-sitronix-st7703.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1a74e81a5SOndrej Jirman // SPDX-License-Identifier: GPL-2.0
2a74e81a5SOndrej Jirman /*
324489ba0SOndrej Jirman  * Driver for panels based on Sitronix ST7703 controller, souch as:
424489ba0SOndrej Jirman  *
524489ba0SOndrej Jirman  * - Rocktech jh057n00900 5.5" MIPI-DSI panel
6a74e81a5SOndrej Jirman  *
7a74e81a5SOndrej Jirman  * Copyright (C) Purism SPC 2019
8a74e81a5SOndrej Jirman  */
9a74e81a5SOndrej Jirman 
10a74e81a5SOndrej Jirman #include <linux/debugfs.h>
11a74e81a5SOndrej Jirman #include <linux/delay.h>
12a74e81a5SOndrej Jirman #include <linux/gpio/consumer.h>
13a74e81a5SOndrej Jirman #include <linux/media-bus-format.h>
14a74e81a5SOndrej Jirman #include <linux/mod_devicetable.h>
15a74e81a5SOndrej Jirman #include <linux/module.h>
16722d4f06SRob Herring #include <linux/of.h>
17a74e81a5SOndrej Jirman #include <linux/regulator/consumer.h>
18a74e81a5SOndrej Jirman 
19a74e81a5SOndrej Jirman #include <video/display_timing.h>
20a74e81a5SOndrej Jirman #include <video/mipi_display.h>
21a74e81a5SOndrej Jirman 
22a74e81a5SOndrej Jirman #include <drm/drm_mipi_dsi.h>
23a74e81a5SOndrej Jirman #include <drm/drm_modes.h>
24a74e81a5SOndrej Jirman #include <drm/drm_panel.h>
25a74e81a5SOndrej Jirman 
2624489ba0SOndrej Jirman #define DRV_NAME "panel-sitronix-st7703"
27a74e81a5SOndrej Jirman 
28a74e81a5SOndrej Jirman /* Manufacturer specific Commands send via DSI */
29a74e81a5SOndrej Jirman #define ST7703_CMD_ALL_PIXEL_OFF 0x22
30a74e81a5SOndrej Jirman #define ST7703_CMD_ALL_PIXEL_ON	 0x23
310ed9208cSChris Morgan #define ST7703_CMD_SETAPID	 0xB1
32a74e81a5SOndrej Jirman #define ST7703_CMD_SETDISP	 0xB2
33a74e81a5SOndrej Jirman #define ST7703_CMD_SETRGBIF	 0xB3
34a74e81a5SOndrej Jirman #define ST7703_CMD_SETCYC	 0xB4
35a74e81a5SOndrej Jirman #define ST7703_CMD_SETBGP	 0xB5
36a74e81a5SOndrej Jirman #define ST7703_CMD_SETVCOM	 0xB6
37a74e81a5SOndrej Jirman #define ST7703_CMD_SETOTP	 0xB7
38a74e81a5SOndrej Jirman #define ST7703_CMD_SETPOWER_EXT	 0xB8
39a74e81a5SOndrej Jirman #define ST7703_CMD_SETEXTC	 0xB9
40a74e81a5SOndrej Jirman #define ST7703_CMD_SETMIPI	 0xBA
41a74e81a5SOndrej Jirman #define ST7703_CMD_SETVDC	 0xBC
4267680f87SOndrej Jirman #define ST7703_CMD_UNKNOWN_BF	 0xBF
43a74e81a5SOndrej Jirman #define ST7703_CMD_SETSCR	 0xC0
44a74e81a5SOndrej Jirman #define ST7703_CMD_SETPOWER	 0xC1
45e0310564SChris Morgan #define ST7703_CMD_SETECO	 0xC6
460ed9208cSChris Morgan #define ST7703_CMD_SETIO	 0xC7
470ed9208cSChris Morgan #define ST7703_CMD_SETCABC	 0xC8
48a74e81a5SOndrej Jirman #define ST7703_CMD_SETPANEL	 0xCC
49a74e81a5SOndrej Jirman #define ST7703_CMD_SETGAMMA	 0xE0
50a74e81a5SOndrej Jirman #define ST7703_CMD_SETEQ	 0xE3
51a74e81a5SOndrej Jirman #define ST7703_CMD_SETGIP1	 0xE9
52a74e81a5SOndrej Jirman #define ST7703_CMD_SETGIP2	 0xEA
530ed9208cSChris Morgan #define ST7703_CMD_UNKNOWN_EF	 0xEF
54a74e81a5SOndrej Jirman 
5524489ba0SOndrej Jirman struct st7703 {
56a74e81a5SOndrej Jirman 	struct device *dev;
57a74e81a5SOndrej Jirman 	struct drm_panel panel;
58a74e81a5SOndrej Jirman 	struct gpio_desc *reset_gpio;
59a74e81a5SOndrej Jirman 	struct regulator *vcc;
60a74e81a5SOndrej Jirman 	struct regulator *iovcc;
61a74e81a5SOndrej Jirman 	bool prepared;
62a74e81a5SOndrej Jirman 
63a74e81a5SOndrej Jirman 	struct dentry *debugfs;
64be6ca339SOndrej Jirman 	const struct st7703_panel_desc *desc;
65be6ca339SOndrej Jirman };
66be6ca339SOndrej Jirman 
67be6ca339SOndrej Jirman struct st7703_panel_desc {
68be6ca339SOndrej Jirman 	const struct drm_display_mode *mode;
69be6ca339SOndrej Jirman 	unsigned int lanes;
70be6ca339SOndrej Jirman 	unsigned long mode_flags;
71be6ca339SOndrej Jirman 	enum mipi_dsi_pixel_format format;
72be6ca339SOndrej Jirman 	int (*init_sequence)(struct st7703 *ctx);
73a74e81a5SOndrej Jirman };
74a74e81a5SOndrej Jirman 
panel_to_st7703(struct drm_panel * panel)7524489ba0SOndrej Jirman static inline struct st7703 *panel_to_st7703(struct drm_panel *panel)
76a74e81a5SOndrej Jirman {
7724489ba0SOndrej Jirman 	return container_of(panel, struct st7703, panel);
78a74e81a5SOndrej Jirman }
79a74e81a5SOndrej Jirman 
jh057n_init_sequence(struct st7703 * ctx)8024489ba0SOndrej Jirman static int jh057n_init_sequence(struct st7703 *ctx)
81a74e81a5SOndrej Jirman {
82a74e81a5SOndrej Jirman 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
83a74e81a5SOndrej Jirman 
84a74e81a5SOndrej Jirman 	/*
85a74e81a5SOndrej Jirman 	 * Init sequence was supplied by the panel vendor. Most of the commands
86a74e81a5SOndrej Jirman 	 * resemble the ST7703 but the number of parameters often don't match
87a74e81a5SOndrej Jirman 	 * so it's likely a clone.
88a74e81a5SOndrej Jirman 	 */
898f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
90a74e81a5SOndrej Jirman 				   0xF1, 0x12, 0x83);
918f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
92a74e81a5SOndrej Jirman 				   0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
93a74e81a5SOndrej Jirman 				   0x00, 0x00);
948f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
95a74e81a5SOndrej Jirman 				   0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
96a74e81a5SOndrej Jirman 				   0x00);
978f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
988f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
998f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
1008f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
1018f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ,
102a74e81a5SOndrej Jirman 				   0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
103a74e81a5SOndrej Jirman 				   0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
1048f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08);
105a74e81a5SOndrej Jirman 	msleep(20);
106a74e81a5SOndrej Jirman 
1078f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
1088f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
1098f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1,
110a74e81a5SOndrej Jirman 				   0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
111a74e81a5SOndrej Jirman 				   0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
112a74e81a5SOndrej Jirman 				   0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
113a74e81a5SOndrej Jirman 				   0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
114a74e81a5SOndrej Jirman 				   0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
115a74e81a5SOndrej Jirman 				   0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
116a74e81a5SOndrej Jirman 				   0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117a74e81a5SOndrej Jirman 				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
1188f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2,
119a74e81a5SOndrej Jirman 				   0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120a74e81a5SOndrej Jirman 				   0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
121a74e81a5SOndrej Jirman 				   0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
122a74e81a5SOndrej Jirman 				   0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
123a74e81a5SOndrej Jirman 				   0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
124a74e81a5SOndrej Jirman 				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125a74e81a5SOndrej Jirman 				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
126a74e81a5SOndrej Jirman 				   0xA5, 0x00, 0x00, 0x00, 0x00);
1278f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA,
128a74e81a5SOndrej Jirman 				   0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
129a74e81a5SOndrej Jirman 				   0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
130a74e81a5SOndrej Jirman 				   0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
131a74e81a5SOndrej Jirman 				   0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
132a74e81a5SOndrej Jirman 				   0x11, 0x18);
133a74e81a5SOndrej Jirman 
134a74e81a5SOndrej Jirman 	return 0;
135a74e81a5SOndrej Jirman }
136a74e81a5SOndrej Jirman 
137a14268a6SOndrej Jirman static const struct drm_display_mode jh057n00900_mode = {
138a14268a6SOndrej Jirman 	.hdisplay    = 720,
139a14268a6SOndrej Jirman 	.hsync_start = 720 + 90,
140a14268a6SOndrej Jirman 	.hsync_end   = 720 + 90 + 20,
141a14268a6SOndrej Jirman 	.htotal	     = 720 + 90 + 20 + 20,
142a14268a6SOndrej Jirman 	.vdisplay    = 1440,
143a14268a6SOndrej Jirman 	.vsync_start = 1440 + 20,
144a14268a6SOndrej Jirman 	.vsync_end   = 1440 + 20 + 4,
145a14268a6SOndrej Jirman 	.vtotal	     = 1440 + 20 + 4 + 12,
146a14268a6SOndrej Jirman 	.clock	     = 75276,
147a14268a6SOndrej Jirman 	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
148a14268a6SOndrej Jirman 	.width_mm    = 65,
149a14268a6SOndrej Jirman 	.height_mm   = 130,
150a14268a6SOndrej Jirman };
151a14268a6SOndrej Jirman 
1524caca3d8SJason Yan static const struct st7703_panel_desc jh057n00900_panel_desc = {
153a14268a6SOndrej Jirman 	.mode = &jh057n00900_mode,
154a14268a6SOndrej Jirman 	.lanes = 4,
155a14268a6SOndrej Jirman 	.mode_flags = MIPI_DSI_MODE_VIDEO |
156a14268a6SOndrej Jirman 		MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
157a14268a6SOndrej Jirman 	.format = MIPI_DSI_FMT_RGB888,
158a14268a6SOndrej Jirman 	.init_sequence = jh057n_init_sequence,
159a14268a6SOndrej Jirman };
160a14268a6SOndrej Jirman 
xbd599_init_sequence(struct st7703 * ctx)16167680f87SOndrej Jirman static int xbd599_init_sequence(struct st7703 *ctx)
16267680f87SOndrej Jirman {
16367680f87SOndrej Jirman 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
16467680f87SOndrej Jirman 
16567680f87SOndrej Jirman 	/*
16667680f87SOndrej Jirman 	 * Init sequence was supplied by the panel vendor.
16767680f87SOndrej Jirman 	 */
16867680f87SOndrej Jirman 
16967680f87SOndrej Jirman 	/* Magic sequence to unlock user commands below. */
1708f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xF1, 0x12, 0x83);
17167680f87SOndrej Jirman 
1728f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI,
17367680f87SOndrej Jirman 			       0x33, /* VC_main = 0, Lane_Number = 3 (4 lanes) */
17467680f87SOndrej Jirman 			       0x81, /* DSI_LDO_SEL = 1.7V, RTERM = 90 Ohm */
17567680f87SOndrej Jirman 			       0x05, /* IHSRX = x6 (Low High Speed driving ability) */
17667680f87SOndrej Jirman 			       0xF9, /* TX_CLK_SEL = fDSICLK/16 */
17767680f87SOndrej Jirman 			       0x0E, /* HFP_OSC (min. HFP number in DSI mode) */
17867680f87SOndrej Jirman 			       0x0E, /* HBP_OSC (min. HBP number in DSI mode) */
17967680f87SOndrej Jirman 			       /* The rest is undocumented in ST7703 datasheet */
18067680f87SOndrej Jirman 			       0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
18167680f87SOndrej Jirman 			       0x44, 0x25, 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02,
18267680f87SOndrej Jirman 			       0x4F, 0x11, 0x00, 0x00, 0x37);
18367680f87SOndrej Jirman 
1848f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT,
18567680f87SOndrej Jirman 			       0x25, /* PCCS = 2, ECP_DC_DIV = 1/4 HSYNC */
18667680f87SOndrej Jirman 			       0x22, /* DT = 15ms XDK_ECP = x2 */
18767680f87SOndrej Jirman 			       0x20, /* PFM_DC_DIV = /1 */
18867680f87SOndrej Jirman 			       0x03  /* ECP_SYNC_EN = 1, VGX_SYNC_EN = 1 */);
18967680f87SOndrej Jirman 
19067680f87SOndrej Jirman 	/* RGB I/F porch timing */
1918f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF,
19267680f87SOndrej Jirman 			       0x10, /* VBP_RGB_GEN */
19367680f87SOndrej Jirman 			       0x10, /* VFP_RGB_GEN */
19467680f87SOndrej Jirman 			       0x05, /* DE_BP_RGB_GEN */
19567680f87SOndrej Jirman 			       0x05, /* DE_FP_RGB_GEN */
19667680f87SOndrej Jirman 			       /* The rest is undocumented in ST7703 datasheet */
19767680f87SOndrej Jirman 			       0x03, 0xFF,
19867680f87SOndrej Jirman 			       0x00, 0x00,
19967680f87SOndrej Jirman 			       0x00, 0x00);
20067680f87SOndrej Jirman 
20167680f87SOndrej Jirman 	/* Source driving settings. */
2028f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR,
20367680f87SOndrej Jirman 			       0x73, /* N_POPON */
20467680f87SOndrej Jirman 			       0x73, /* N_NOPON */
20567680f87SOndrej Jirman 			       0x50, /* I_POPON */
20667680f87SOndrej Jirman 			       0x50, /* I_NOPON */
20767680f87SOndrej Jirman 			       0x00, /* SCR[31,24] */
20867680f87SOndrej Jirman 			       0xC0, /* SCR[23,16] */
20967680f87SOndrej Jirman 			       0x08, /* SCR[15,8] */
21067680f87SOndrej Jirman 			       0x70, /* SCR[7,0] */
21167680f87SOndrej Jirman 			       0x00  /* Undocumented */);
21267680f87SOndrej Jirman 
21367680f87SOndrej Jirman 	/* NVDDD_SEL = -1.8V, VDDD_SEL = out of range (possibly 1.9V?) */
2148f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
21567680f87SOndrej Jirman 
21667680f87SOndrej Jirman 	/*
21767680f87SOndrej Jirman 	 * SS_PANEL = 1 (reverse scan), GS_PANEL = 0 (normal scan)
21867680f87SOndrej Jirman 	 * REV_PANEL = 1 (normally black panel), BGR_PANEL = 1 (BGR)
21967680f87SOndrej Jirman 	 */
2208f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
22167680f87SOndrej Jirman 
22267680f87SOndrej Jirman 	/* Zig-Zag Type C column inversion. */
2238f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
22467680f87SOndrej Jirman 
22567680f87SOndrej Jirman 	/* Set display resolution. */
2268f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP,
22767680f87SOndrej Jirman 			       0xF0, /* NL = 240 */
22867680f87SOndrej Jirman 			       0x12, /* RES_V_LSB = 0, BLK_CON = VSSD,
22967680f87SOndrej Jirman 				      * RESO_SEL = 720RGB
23067680f87SOndrej Jirman 				      */
23167680f87SOndrej Jirman 			       0xF0  /* WHITE_GND_EN = 1 (GND),
23267680f87SOndrej Jirman 				      * WHITE_FRAME_SEL = 7 frames,
23367680f87SOndrej Jirman 				      * ISC = 0 frames
23467680f87SOndrej Jirman 				      */);
23567680f87SOndrej Jirman 
2368f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ,
23767680f87SOndrej Jirman 			       0x00, /* PNOEQ */
23867680f87SOndrej Jirman 			       0x00, /* NNOEQ */
23967680f87SOndrej Jirman 			       0x0B, /* PEQGND */
24067680f87SOndrej Jirman 			       0x0B, /* NEQGND */
24167680f87SOndrej Jirman 			       0x10, /* PEQVCI */
24267680f87SOndrej Jirman 			       0x10, /* NEQVCI */
24367680f87SOndrej Jirman 			       0x00, /* PEQVCI1 */
24467680f87SOndrej Jirman 			       0x00, /* NEQVCI1 */
24567680f87SOndrej Jirman 			       0x00, /* reserved */
24667680f87SOndrej Jirman 			       0x00, /* reserved */
24767680f87SOndrej Jirman 			       0xFF, /* reserved */
24867680f87SOndrej Jirman 			       0x00, /* reserved */
24967680f87SOndrej Jirman 			       0xC0, /* ESD_DET_DATA_WHITE = 1, ESD_WHITE_EN = 1 */
25067680f87SOndrej Jirman 			       0x10  /* SLPIN_OPTION = 1 (no need vsync after sleep-in)
25167680f87SOndrej Jirman 				      * VEDIO_NO_CHECK_EN = 0
25267680f87SOndrej Jirman 				      * ESD_WHITE_GND_EN = 0
25367680f87SOndrej Jirman 				      * ESD_DET_TIME_SEL = 0 frames
25467680f87SOndrej Jirman 				      */);
25567680f87SOndrej Jirman 
256e0310564SChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETECO, 0x01, 0x00, 0xFF, 0xFF, 0x00);
25767680f87SOndrej Jirman 
2588f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER,
25967680f87SOndrej Jirman 			       0x74, /* VBTHS, VBTLS: VGH = 17V, VBL = -11V */
26067680f87SOndrej Jirman 			       0x00, /* FBOFF_VGH = 0, FBOFF_VGL = 0 */
26167680f87SOndrej Jirman 			       0x32, /* VRP  */
26267680f87SOndrej Jirman 			       0x32, /* VRN */
26367680f87SOndrej Jirman 			       0x77, /* reserved */
26467680f87SOndrej Jirman 			       0xF1, /* APS = 1 (small),
26567680f87SOndrej Jirman 				      * VGL_DET_EN = 1, VGH_DET_EN = 1,
26667680f87SOndrej Jirman 				      * VGL_TURBO = 1, VGH_TURBO = 1
26767680f87SOndrej Jirman 				      */
26867680f87SOndrej Jirman 			       0xFF, /* VGH1_L_DIV, VGL1_L_DIV (1.5MHz) */
26967680f87SOndrej Jirman 			       0xFF, /* VGH1_R_DIV, VGL1_R_DIV (1.5MHz) */
27067680f87SOndrej Jirman 			       0xCC, /* VGH2_L_DIV, VGL2_L_DIV (2.6MHz) */
27167680f87SOndrej Jirman 			       0xCC, /* VGH2_R_DIV, VGL2_R_DIV (2.6MHz) */
27267680f87SOndrej Jirman 			       0x77, /* VGH3_L_DIV, VGL3_L_DIV (4.5MHz) */
27367680f87SOndrej Jirman 			       0x77  /* VGH3_R_DIV, VGL3_R_DIV (4.5MHz) */);
27467680f87SOndrej Jirman 
27567680f87SOndrej Jirman 	/* Reference voltage. */
2768f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP,
27767680f87SOndrej Jirman 			       0x07, /* VREF_SEL = 4.2V */
27867680f87SOndrej Jirman 			       0x07  /* NVREF_SEL = 4.2V */);
27967680f87SOndrej Jirman 	msleep(20);
28067680f87SOndrej Jirman 
2818f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM,
28267680f87SOndrej Jirman 			       0x2C, /* VCOMDC_F = -0.67V */
28367680f87SOndrej Jirman 			       0x2C  /* VCOMDC_B = -0.67V */);
28467680f87SOndrej Jirman 
28567680f87SOndrej Jirman 	/* Undocumented command. */
2868f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
28767680f87SOndrej Jirman 
28867680f87SOndrej Jirman 	/* This command is to set forward GIP timing. */
2898f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1,
29067680f87SOndrej Jirman 			       0x82, 0x10, 0x06, 0x05, 0xA2, 0x0A, 0xA5, 0x12,
29167680f87SOndrej Jirman 			       0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
29267680f87SOndrej Jirman 			       0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
29367680f87SOndrej Jirman 			       0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
29467680f87SOndrej Jirman 			       0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
29567680f87SOndrej Jirman 			       0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
29667680f87SOndrej Jirman 			       0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29767680f87SOndrej Jirman 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
29867680f87SOndrej Jirman 
29967680f87SOndrej Jirman 	/* This command is to set backward GIP timing. */
3008f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2,
30167680f87SOndrej Jirman 			       0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30267680f87SOndrej Jirman 			       0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
30367680f87SOndrej Jirman 			       0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
30467680f87SOndrej Jirman 			       0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
30567680f87SOndrej Jirman 			       0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
30667680f87SOndrej Jirman 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30767680f87SOndrej Jirman 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0A,
30867680f87SOndrej Jirman 			       0xA5, 0x00, 0x00, 0x00, 0x00);
30967680f87SOndrej Jirman 
31067680f87SOndrej Jirman 	/* Adjust the gamma characteristics of the panel. */
3118f821edcSJavier Martinez Canillas 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA,
31267680f87SOndrej Jirman 			       0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41, 0x35,
31367680f87SOndrej Jirman 			       0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12, 0x12,
31467680f87SOndrej Jirman 			       0x18, 0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41,
31567680f87SOndrej Jirman 			       0x35, 0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12,
31667680f87SOndrej Jirman 			       0x12, 0x18);
31767680f87SOndrej Jirman 
31867680f87SOndrej Jirman 	return 0;
31967680f87SOndrej Jirman }
32067680f87SOndrej Jirman 
32167680f87SOndrej Jirman static const struct drm_display_mode xbd599_mode = {
32267680f87SOndrej Jirman 	.hdisplay    = 720,
32367680f87SOndrej Jirman 	.hsync_start = 720 + 40,
32467680f87SOndrej Jirman 	.hsync_end   = 720 + 40 + 40,
32567680f87SOndrej Jirman 	.htotal	     = 720 + 40 + 40 + 40,
32667680f87SOndrej Jirman 	.vdisplay    = 1440,
32767680f87SOndrej Jirman 	.vsync_start = 1440 + 18,
32867680f87SOndrej Jirman 	.vsync_end   = 1440 + 18 + 10,
32967680f87SOndrej Jirman 	.vtotal	     = 1440 + 18 + 10 + 17,
33067680f87SOndrej Jirman 	.clock	     = 69000,
33167680f87SOndrej Jirman 	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
33267680f87SOndrej Jirman 	.width_mm    = 68,
33367680f87SOndrej Jirman 	.height_mm   = 136,
33467680f87SOndrej Jirman };
33567680f87SOndrej Jirman 
33667680f87SOndrej Jirman static const struct st7703_panel_desc xbd599_desc = {
33767680f87SOndrej Jirman 	.mode = &xbd599_mode,
33867680f87SOndrej Jirman 	.lanes = 4,
33967680f87SOndrej Jirman 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
34067680f87SOndrej Jirman 	.format = MIPI_DSI_FMT_RGB888,
34167680f87SOndrej Jirman 	.init_sequence = xbd599_init_sequence,
34267680f87SOndrej Jirman };
34367680f87SOndrej Jirman 
rg353v2_init_sequence(struct st7703 * ctx)3440ed9208cSChris Morgan static int rg353v2_init_sequence(struct st7703 *ctx)
3450ed9208cSChris Morgan {
3460ed9208cSChris Morgan 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
3470ed9208cSChris Morgan 
3480ed9208cSChris Morgan 	/*
3490ed9208cSChris Morgan 	 * Init sequence was supplied by the panel vendor.
3500ed9208cSChris Morgan 	 */
3510ed9208cSChris Morgan 
3520ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
3530ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETAPID, 0x00, 0x00, 0x00,
3540ed9208cSChris Morgan 			       0xda, 0x80);
3550ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, 0x00, 0x13, 0x70);
3560ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
3570ed9208cSChris Morgan 			       0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
3580ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
3590ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, 0x0a, 0x0a);
3600ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, 0x92, 0x92);
3610ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22,
3620ed9208cSChris Morgan 			       0xf0, 0x63);
3630ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05,
3640ed9208cSChris Morgan 			       0xf9, 0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00,
3650ed9208cSChris Morgan 			       0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a,
3660ed9208cSChris Morgan 			       0x00, 0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
3670ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x47);
3680ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
3690ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
3700ed9208cSChris Morgan 			       0x00, 0x00, 0x12, 0x50, 0x00);
3710ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER, 0x53, 0xc0, 0x32,
3720ed9208cSChris Morgan 			       0x32, 0x77, 0xe1, 0xdd, 0xdd, 0x77, 0x77, 0x33,
3730ed9208cSChris Morgan 			       0x33);
3740ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETECO, 0x82, 0x00, 0xbf, 0xff,
3750ed9208cSChris Morgan 			       0x00, 0xff);
3760ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETIO, 0xb8, 0x00, 0x0a, 0x00,
3770ed9208cSChris Morgan 			       0x00, 0x00);
3780ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCABC, 0x10, 0x40, 0x1e,
3790ed9208cSChris Morgan 			       0x02);
3800ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0b);
3810ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA, 0x00, 0x07, 0x0d,
3820ed9208cSChris Morgan 			       0x37, 0x35, 0x3f, 0x41, 0x44, 0x06, 0x0c, 0x0d,
3830ed9208cSChris Morgan 			       0x0f, 0x11, 0x10, 0x12, 0x14, 0x1a, 0x00, 0x07,
3840ed9208cSChris Morgan 			       0x0d, 0x37, 0x35, 0x3f, 0x41, 0x44, 0x06, 0x0c,
3850ed9208cSChris Morgan 			       0x0d, 0x0f, 0x11, 0x10, 0x12, 0x14, 0x1a);
3860ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0b, 0x0b,
3870ed9208cSChris Morgan 			       0x0b, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
3880ed9208cSChris Morgan 			       0xc0, 0x10);
3890ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x02, 0x00,
3900ed9208cSChris Morgan 			       0x00, 0xb0, 0xb1, 0x11, 0x31, 0x23, 0x28, 0x80,
3910ed9208cSChris Morgan 			       0xb0, 0xb1, 0x27, 0x08, 0x00, 0x04, 0x02, 0x00,
3920ed9208cSChris Morgan 			       0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00,
3930ed9208cSChris Morgan 			       0x88, 0x88, 0xba, 0x60, 0x24, 0x08, 0x88, 0x88,
3940ed9208cSChris Morgan 			       0x88, 0x88, 0x88, 0x88, 0x88, 0xba, 0x71, 0x35,
3950ed9208cSChris Morgan 			       0x18, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
3960ed9208cSChris Morgan 			       0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3970ed9208cSChris Morgan 			       0x00, 0x00, 0x00);
3980ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2, 0x97, 0x0a, 0x82, 0x02,
3990ed9208cSChris Morgan 			       0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4000ed9208cSChris Morgan 			       0x81, 0x88, 0xba, 0x17, 0x53, 0x88, 0x88, 0x88,
4010ed9208cSChris Morgan 			       0x88, 0x88, 0x88, 0x80, 0x88, 0xba, 0x06, 0x42,
4020ed9208cSChris Morgan 			       0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00,
4030ed9208cSChris Morgan 			       0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
4040ed9208cSChris Morgan 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4050ed9208cSChris Morgan 			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4060ed9208cSChris Morgan 			       0x00);
4070ed9208cSChris Morgan 	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff, 0x01);
4080ed9208cSChris Morgan 
4090ed9208cSChris Morgan 	return 0;
4100ed9208cSChris Morgan }
4110ed9208cSChris Morgan 
4120ed9208cSChris Morgan static const struct drm_display_mode rg353v2_mode = {
4130ed9208cSChris Morgan 	.hdisplay	= 640,
4140ed9208cSChris Morgan 	.hsync_start	= 640 + 40,
4150ed9208cSChris Morgan 	.hsync_end	= 640 + 40 + 2,
4160ed9208cSChris Morgan 	.htotal		= 640 + 40 + 2 + 80,
4170ed9208cSChris Morgan 	.vdisplay	= 480,
4180ed9208cSChris Morgan 	.vsync_start	= 480 + 18,
4190ed9208cSChris Morgan 	.vsync_end	= 480 + 18 + 2,
4200ed9208cSChris Morgan 	.vtotal		= 480 + 18 + 2 + 28,
4210ed9208cSChris Morgan 	.clock		= 24150,
4220ed9208cSChris Morgan 	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
4230ed9208cSChris Morgan 	.width_mm	= 70,
4240ed9208cSChris Morgan 	.height_mm	= 57,
4250ed9208cSChris Morgan };
4260ed9208cSChris Morgan 
4270ed9208cSChris Morgan static const struct st7703_panel_desc rg353v2_desc = {
4280ed9208cSChris Morgan 	.mode = &rg353v2_mode,
4290ed9208cSChris Morgan 	.lanes = 4,
4300ed9208cSChris Morgan 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
4310ed9208cSChris Morgan 		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
4320ed9208cSChris Morgan 	.format = MIPI_DSI_FMT_RGB888,
4330ed9208cSChris Morgan 	.init_sequence = rg353v2_init_sequence,
4340ed9208cSChris Morgan };
4350ed9208cSChris Morgan 
st7703_enable(struct drm_panel * panel)43624489ba0SOndrej Jirman static int st7703_enable(struct drm_panel *panel)
437a74e81a5SOndrej Jirman {
43824489ba0SOndrej Jirman 	struct st7703 *ctx = panel_to_st7703(panel);
439e609fb1eSOndrej Jirman 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
440a74e81a5SOndrej Jirman 	int ret;
441a74e81a5SOndrej Jirman 
442be6ca339SOndrej Jirman 	ret = ctx->desc->init_sequence(ctx);
443a74e81a5SOndrej Jirman 	if (ret < 0) {
44465d5c86fSSam Ravnborg 		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
445a74e81a5SOndrej Jirman 		return ret;
446a74e81a5SOndrej Jirman 	}
447a74e81a5SOndrej Jirman 
448e609fb1eSOndrej Jirman 	msleep(20);
449e609fb1eSOndrej Jirman 
450e609fb1eSOndrej Jirman 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
451e609fb1eSOndrej Jirman 	if (ret < 0) {
45265d5c86fSSam Ravnborg 		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
453e609fb1eSOndrej Jirman 		return ret;
454e609fb1eSOndrej Jirman 	}
455e609fb1eSOndrej Jirman 
456e609fb1eSOndrej Jirman 	/* Panel is operational 120 msec after reset */
457e609fb1eSOndrej Jirman 	msleep(60);
458e609fb1eSOndrej Jirman 
459e609fb1eSOndrej Jirman 	ret = mipi_dsi_dcs_set_display_on(dsi);
460e609fb1eSOndrej Jirman 	if (ret)
461e609fb1eSOndrej Jirman 		return ret;
462e609fb1eSOndrej Jirman 
46365d5c86fSSam Ravnborg 	dev_dbg(ctx->dev, "Panel init sequence done\n");
464e609fb1eSOndrej Jirman 
465a74e81a5SOndrej Jirman 	return 0;
466a74e81a5SOndrej Jirman }
467a74e81a5SOndrej Jirman 
st7703_disable(struct drm_panel * panel)46824489ba0SOndrej Jirman static int st7703_disable(struct drm_panel *panel)
469a74e81a5SOndrej Jirman {
47024489ba0SOndrej Jirman 	struct st7703 *ctx = panel_to_st7703(panel);
471a74e81a5SOndrej Jirman 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
47243fc6db0SOndrej Jirman 	int ret;
473a74e81a5SOndrej Jirman 
47443fc6db0SOndrej Jirman 	ret = mipi_dsi_dcs_set_display_off(dsi);
47543fc6db0SOndrej Jirman 	if (ret < 0)
47665d5c86fSSam Ravnborg 		dev_err(ctx->dev, "Failed to turn off the display: %d\n", ret);
47743fc6db0SOndrej Jirman 
47843fc6db0SOndrej Jirman 	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
47943fc6db0SOndrej Jirman 	if (ret < 0)
48065d5c86fSSam Ravnborg 		dev_err(ctx->dev, "Failed to enter sleep mode: %d\n", ret);
48143fc6db0SOndrej Jirman 
48243fc6db0SOndrej Jirman 	return 0;
483a74e81a5SOndrej Jirman }
484a74e81a5SOndrej Jirman 
st7703_unprepare(struct drm_panel * panel)48524489ba0SOndrej Jirman static int st7703_unprepare(struct drm_panel *panel)
486a74e81a5SOndrej Jirman {
48724489ba0SOndrej Jirman 	struct st7703 *ctx = panel_to_st7703(panel);
488a74e81a5SOndrej Jirman 
489a74e81a5SOndrej Jirman 	if (!ctx->prepared)
490a74e81a5SOndrej Jirman 		return 0;
491a74e81a5SOndrej Jirman 
492c8a75348SOndrej Jirman 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
493a74e81a5SOndrej Jirman 	regulator_disable(ctx->iovcc);
494a74e81a5SOndrej Jirman 	regulator_disable(ctx->vcc);
495a74e81a5SOndrej Jirman 	ctx->prepared = false;
496a74e81a5SOndrej Jirman 
497a74e81a5SOndrej Jirman 	return 0;
498a74e81a5SOndrej Jirman }
499a74e81a5SOndrej Jirman 
st7703_prepare(struct drm_panel * panel)50024489ba0SOndrej Jirman static int st7703_prepare(struct drm_panel *panel)
501a74e81a5SOndrej Jirman {
50224489ba0SOndrej Jirman 	struct st7703 *ctx = panel_to_st7703(panel);
503a74e81a5SOndrej Jirman 	int ret;
504a74e81a5SOndrej Jirman 
505a74e81a5SOndrej Jirman 	if (ctx->prepared)
506a74e81a5SOndrej Jirman 		return 0;
507a74e81a5SOndrej Jirman 
50865d5c86fSSam Ravnborg 	dev_dbg(ctx->dev, "Resetting the panel\n");
509*1afe3973SOndrej Jirman 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
510*1afe3973SOndrej Jirman 
511a74e81a5SOndrej Jirman 	ret = regulator_enable(ctx->iovcc);
512a74e81a5SOndrej Jirman 	if (ret < 0) {
51365d5c86fSSam Ravnborg 		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
514*1afe3973SOndrej Jirman 		return ret;
515a74e81a5SOndrej Jirman 	}
516a74e81a5SOndrej Jirman 
517*1afe3973SOndrej Jirman 	ret = regulator_enable(ctx->vcc);
518*1afe3973SOndrej Jirman 	if (ret < 0) {
519*1afe3973SOndrej Jirman 		dev_err(ctx->dev, "Failed to enable vcc supply: %d\n", ret);
520*1afe3973SOndrej Jirman 		regulator_disable(ctx->iovcc);
521*1afe3973SOndrej Jirman 		return ret;
522*1afe3973SOndrej Jirman 	}
523*1afe3973SOndrej Jirman 
524*1afe3973SOndrej Jirman 	/* Give power supplies time to stabilize before deasserting reset. */
525*1afe3973SOndrej Jirman 	usleep_range(10000, 20000);
526*1afe3973SOndrej Jirman 
527a74e81a5SOndrej Jirman 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
528*1afe3973SOndrej Jirman 	usleep_range(15000, 20000);
529a74e81a5SOndrej Jirman 
530a74e81a5SOndrej Jirman 	ctx->prepared = true;
531a74e81a5SOndrej Jirman 
532a74e81a5SOndrej Jirman 	return 0;
533a74e81a5SOndrej Jirman }
534a74e81a5SOndrej Jirman 
5350c464eeeSGuido Günther static const u32 mantix_bus_formats[] = {
5360c464eeeSGuido Günther 	MEDIA_BUS_FMT_RGB888_1X24,
5370c464eeeSGuido Günther };
5380c464eeeSGuido Günther 
st7703_get_modes(struct drm_panel * panel,struct drm_connector * connector)53924489ba0SOndrej Jirman static int st7703_get_modes(struct drm_panel *panel,
540a74e81a5SOndrej Jirman 			    struct drm_connector *connector)
541a74e81a5SOndrej Jirman {
54224489ba0SOndrej Jirman 	struct st7703 *ctx = panel_to_st7703(panel);
543a74e81a5SOndrej Jirman 	struct drm_display_mode *mode;
544a74e81a5SOndrej Jirman 
545be6ca339SOndrej Jirman 	mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
546a74e81a5SOndrej Jirman 	if (!mode) {
54765d5c86fSSam Ravnborg 		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
548be6ca339SOndrej Jirman 			ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
549be6ca339SOndrej Jirman 			drm_mode_vrefresh(ctx->desc->mode));
550a74e81a5SOndrej Jirman 		return -ENOMEM;
551a74e81a5SOndrej Jirman 	}
552a74e81a5SOndrej Jirman 
553a74e81a5SOndrej Jirman 	drm_mode_set_name(mode);
554a74e81a5SOndrej Jirman 
555a74e81a5SOndrej Jirman 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
556a74e81a5SOndrej Jirman 	connector->display_info.width_mm = mode->width_mm;
557a74e81a5SOndrej Jirman 	connector->display_info.height_mm = mode->height_mm;
558a74e81a5SOndrej Jirman 	drm_mode_probed_add(connector, mode);
559a74e81a5SOndrej Jirman 
5600c464eeeSGuido Günther 	drm_display_info_set_bus_formats(&connector->display_info,
5610c464eeeSGuido Günther 					 mantix_bus_formats,
5620c464eeeSGuido Günther 					 ARRAY_SIZE(mantix_bus_formats));
5630c464eeeSGuido Günther 
564a74e81a5SOndrej Jirman 	return 1;
565a74e81a5SOndrej Jirman }
566a74e81a5SOndrej Jirman 
56724489ba0SOndrej Jirman static const struct drm_panel_funcs st7703_drm_funcs = {
56824489ba0SOndrej Jirman 	.disable   = st7703_disable,
56924489ba0SOndrej Jirman 	.unprepare = st7703_unprepare,
57024489ba0SOndrej Jirman 	.prepare   = st7703_prepare,
57124489ba0SOndrej Jirman 	.enable	   = st7703_enable,
57224489ba0SOndrej Jirman 	.get_modes = st7703_get_modes,
573a74e81a5SOndrej Jirman };
574a74e81a5SOndrej Jirman 
allpixelson_set(void * data,u64 val)575a74e81a5SOndrej Jirman static int allpixelson_set(void *data, u64 val)
576a74e81a5SOndrej Jirman {
57724489ba0SOndrej Jirman 	struct st7703 *ctx = data;
578a74e81a5SOndrej Jirman 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
579a74e81a5SOndrej Jirman 
58065d5c86fSSam Ravnborg 	dev_dbg(ctx->dev, "Setting all pixels on\n");
5818f821edcSJavier Martinez Canillas 	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
582a74e81a5SOndrej Jirman 	msleep(val * 1000);
583a74e81a5SOndrej Jirman 	/* Reset the panel to get video back */
584a74e81a5SOndrej Jirman 	drm_panel_disable(&ctx->panel);
585a74e81a5SOndrej Jirman 	drm_panel_unprepare(&ctx->panel);
586a74e81a5SOndrej Jirman 	drm_panel_prepare(&ctx->panel);
587a74e81a5SOndrej Jirman 	drm_panel_enable(&ctx->panel);
588a74e81a5SOndrej Jirman 
589a74e81a5SOndrej Jirman 	return 0;
590a74e81a5SOndrej Jirman }
591a74e81a5SOndrej Jirman 
592a74e81a5SOndrej Jirman DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
593a74e81a5SOndrej Jirman 			allpixelson_set, "%llu\n");
594a74e81a5SOndrej Jirman 
st7703_debugfs_init(struct st7703 * ctx)59524489ba0SOndrej Jirman static void st7703_debugfs_init(struct st7703 *ctx)
596a74e81a5SOndrej Jirman {
597a74e81a5SOndrej Jirman 	ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
598a74e81a5SOndrej Jirman 
599a74e81a5SOndrej Jirman 	debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
600a74e81a5SOndrej Jirman 			    &allpixelson_fops);
601a74e81a5SOndrej Jirman }
602a74e81a5SOndrej Jirman 
st7703_debugfs_remove(struct st7703 * ctx)60324489ba0SOndrej Jirman static void st7703_debugfs_remove(struct st7703 *ctx)
604a74e81a5SOndrej Jirman {
605a74e81a5SOndrej Jirman 	debugfs_remove_recursive(ctx->debugfs);
606a74e81a5SOndrej Jirman 	ctx->debugfs = NULL;
607a74e81a5SOndrej Jirman }
608a74e81a5SOndrej Jirman 
st7703_probe(struct mipi_dsi_device * dsi)60924489ba0SOndrej Jirman static int st7703_probe(struct mipi_dsi_device *dsi)
610a74e81a5SOndrej Jirman {
611a74e81a5SOndrej Jirman 	struct device *dev = &dsi->dev;
61224489ba0SOndrej Jirman 	struct st7703 *ctx;
613a74e81a5SOndrej Jirman 	int ret;
614a74e81a5SOndrej Jirman 
615a74e81a5SOndrej Jirman 	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
616a74e81a5SOndrej Jirman 	if (!ctx)
617a74e81a5SOndrej Jirman 		return -ENOMEM;
618a74e81a5SOndrej Jirman 
619a74e81a5SOndrej Jirman 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
620dfb64090SGuido Günther 	if (IS_ERR(ctx->reset_gpio))
621dfb64090SGuido Günther 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset gpio\n");
622a74e81a5SOndrej Jirman 
623a74e81a5SOndrej Jirman 	mipi_dsi_set_drvdata(dsi, ctx);
624a74e81a5SOndrej Jirman 
625a74e81a5SOndrej Jirman 	ctx->dev = dev;
626be6ca339SOndrej Jirman 	ctx->desc = of_device_get_match_data(dev);
627a74e81a5SOndrej Jirman 
628be6ca339SOndrej Jirman 	dsi->mode_flags = ctx->desc->mode_flags;
629be6ca339SOndrej Jirman 	dsi->format = ctx->desc->format;
630be6ca339SOndrej Jirman 	dsi->lanes = ctx->desc->lanes;
631a74e81a5SOndrej Jirman 
632a74e81a5SOndrej Jirman 	ctx->vcc = devm_regulator_get(dev, "vcc");
633dfb64090SGuido Günther 	if (IS_ERR(ctx->vcc))
634dfb64090SGuido Günther 		return dev_err_probe(dev, PTR_ERR(ctx->vcc), "Failed to request vcc regulator\n");
635dfb64090SGuido Günther 
636a74e81a5SOndrej Jirman 	ctx->iovcc = devm_regulator_get(dev, "iovcc");
637dfb64090SGuido Günther 	if (IS_ERR(ctx->iovcc))
638dfb64090SGuido Günther 		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
639dfb64090SGuido Günther 				     "Failed to request iovcc regulator\n");
640a74e81a5SOndrej Jirman 
64124489ba0SOndrej Jirman 	drm_panel_init(&ctx->panel, dev, &st7703_drm_funcs,
642a74e81a5SOndrej Jirman 		       DRM_MODE_CONNECTOR_DSI);
643a74e81a5SOndrej Jirman 
644a74e81a5SOndrej Jirman 	ret = drm_panel_of_backlight(&ctx->panel);
645a74e81a5SOndrej Jirman 	if (ret)
646a74e81a5SOndrej Jirman 		return ret;
647a74e81a5SOndrej Jirman 
648a74e81a5SOndrej Jirman 	drm_panel_add(&ctx->panel);
649a74e81a5SOndrej Jirman 
650a74e81a5SOndrej Jirman 	ret = mipi_dsi_attach(dsi);
651a74e81a5SOndrej Jirman 	if (ret < 0) {
65265d5c86fSSam Ravnborg 		dev_err(dev, "mipi_dsi_attach failed (%d). Is host ready?\n", ret);
653a74e81a5SOndrej Jirman 		drm_panel_remove(&ctx->panel);
654a74e81a5SOndrej Jirman 		return ret;
655a74e81a5SOndrej Jirman 	}
656a74e81a5SOndrej Jirman 
65765d5c86fSSam Ravnborg 	dev_info(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
658be6ca339SOndrej Jirman 		 ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
659be6ca339SOndrej Jirman 		 drm_mode_vrefresh(ctx->desc->mode),
660a74e81a5SOndrej Jirman 		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
661a74e81a5SOndrej Jirman 
66224489ba0SOndrej Jirman 	st7703_debugfs_init(ctx);
663a74e81a5SOndrej Jirman 	return 0;
664a74e81a5SOndrej Jirman }
665a74e81a5SOndrej Jirman 
st7703_shutdown(struct mipi_dsi_device * dsi)66624489ba0SOndrej Jirman static void st7703_shutdown(struct mipi_dsi_device *dsi)
667a74e81a5SOndrej Jirman {
66824489ba0SOndrej Jirman 	struct st7703 *ctx = mipi_dsi_get_drvdata(dsi);
669a74e81a5SOndrej Jirman 	int ret;
670a74e81a5SOndrej Jirman 
671a74e81a5SOndrej Jirman 	ret = drm_panel_unprepare(&ctx->panel);
672a74e81a5SOndrej Jirman 	if (ret < 0)
67365d5c86fSSam Ravnborg 		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
674a74e81a5SOndrej Jirman 
675a74e81a5SOndrej Jirman 	ret = drm_panel_disable(&ctx->panel);
676a74e81a5SOndrej Jirman 	if (ret < 0)
67765d5c86fSSam Ravnborg 		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
678a74e81a5SOndrej Jirman }
679a74e81a5SOndrej Jirman 
st7703_remove(struct mipi_dsi_device * dsi)68079abca2bSUwe Kleine-König static void st7703_remove(struct mipi_dsi_device *dsi)
681a74e81a5SOndrej Jirman {
68224489ba0SOndrej Jirman 	struct st7703 *ctx = mipi_dsi_get_drvdata(dsi);
683a74e81a5SOndrej Jirman 	int ret;
684a74e81a5SOndrej Jirman 
68524489ba0SOndrej Jirman 	st7703_shutdown(dsi);
686a74e81a5SOndrej Jirman 
687a74e81a5SOndrej Jirman 	ret = mipi_dsi_detach(dsi);
688a74e81a5SOndrej Jirman 	if (ret < 0)
68965d5c86fSSam Ravnborg 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
690a74e81a5SOndrej Jirman 
691a74e81a5SOndrej Jirman 	drm_panel_remove(&ctx->panel);
692a74e81a5SOndrej Jirman 
69324489ba0SOndrej Jirman 	st7703_debugfs_remove(ctx);
694a74e81a5SOndrej Jirman }
695a74e81a5SOndrej Jirman 
69624489ba0SOndrej Jirman static const struct of_device_id st7703_of_match[] = {
6970ed9208cSChris Morgan 	{ .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc },
698be6ca339SOndrej Jirman 	{ .compatible = "rocktech,jh057n00900", .data = &jh057n00900_panel_desc },
69967680f87SOndrej Jirman 	{ .compatible = "xingbangda,xbd599", .data = &xbd599_desc },
700a74e81a5SOndrej Jirman 	{ /* sentinel */ }
701a74e81a5SOndrej Jirman };
70224489ba0SOndrej Jirman MODULE_DEVICE_TABLE(of, st7703_of_match);
703a74e81a5SOndrej Jirman 
70424489ba0SOndrej Jirman static struct mipi_dsi_driver st7703_driver = {
70524489ba0SOndrej Jirman 	.probe	= st7703_probe,
70624489ba0SOndrej Jirman 	.remove = st7703_remove,
70724489ba0SOndrej Jirman 	.shutdown = st7703_shutdown,
708a74e81a5SOndrej Jirman 	.driver = {
709a74e81a5SOndrej Jirman 		.name = DRV_NAME,
71024489ba0SOndrej Jirman 		.of_match_table = st7703_of_match,
711a74e81a5SOndrej Jirman 	},
712a74e81a5SOndrej Jirman };
71324489ba0SOndrej Jirman module_mipi_dsi_driver(st7703_driver);
714a74e81a5SOndrej Jirman 
715a74e81a5SOndrej Jirman MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
71624489ba0SOndrej Jirman MODULE_DESCRIPTION("DRM driver for Sitronix ST7703 based MIPI DSI panels");
717a74e81a5SOndrej Jirman MODULE_LICENSE("GPL v2");
718