Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Driver for panels based on Sitronix ST7703 controller, souch as:
  4 *
  5 * - Rocktech jh057n00900 5.5" MIPI-DSI panel
  6 *
  7 * Copyright (C) Purism SPC 2019
  8 */
  9
 10#include <linux/debugfs.h>
 11#include <linux/delay.h>
 12#include <linux/gpio/consumer.h>
 13#include <linux/media-bus-format.h>
 14#include <linux/mod_devicetable.h>
 15#include <linux/module.h>
 16#include <linux/of.h>
 17#include <linux/regulator/consumer.h>
 18
 19#include <video/display_timing.h>
 20#include <video/mipi_display.h>
 21
 22#include <drm/drm_mipi_dsi.h>
 23#include <drm/drm_modes.h>
 24#include <drm/drm_panel.h>
 25
 26#define DRV_NAME "panel-sitronix-st7703"
 27
 28/* Manufacturer specific Commands send via DSI */
 29#define ST7703_CMD_ALL_PIXEL_OFF 0x22
 30#define ST7703_CMD_ALL_PIXEL_ON	 0x23
 31#define ST7703_CMD_SETAPID	 0xB1
 32#define ST7703_CMD_SETDISP	 0xB2
 33#define ST7703_CMD_SETRGBIF	 0xB3
 34#define ST7703_CMD_SETCYC	 0xB4
 35#define ST7703_CMD_SETBGP	 0xB5
 36#define ST7703_CMD_SETVCOM	 0xB6
 37#define ST7703_CMD_SETOTP	 0xB7
 38#define ST7703_CMD_SETPOWER_EXT	 0xB8
 39#define ST7703_CMD_SETEXTC	 0xB9
 40#define ST7703_CMD_SETMIPI	 0xBA
 41#define ST7703_CMD_SETVDC	 0xBC
 42#define ST7703_CMD_UNKNOWN_BF	 0xBF
 43#define ST7703_CMD_SETSCR	 0xC0
 44#define ST7703_CMD_SETPOWER	 0xC1
 45#define ST7703_CMD_SETECO	 0xC6
 46#define ST7703_CMD_SETIO	 0xC7
 47#define ST7703_CMD_SETCABC	 0xC8
 48#define ST7703_CMD_SETPANEL	 0xCC
 49#define ST7703_CMD_SETGAMMA	 0xE0
 50#define ST7703_CMD_SETEQ	 0xE3
 51#define ST7703_CMD_SETGIP1	 0xE9
 52#define ST7703_CMD_SETGIP2	 0xEA
 53#define ST7703_CMD_UNKNOWN_EF	 0xEF
 54
 55struct st7703 {
 56	struct device *dev;
 57	struct drm_panel panel;
 58	struct gpio_desc *reset_gpio;
 59	struct regulator *vcc;
 60	struct regulator *iovcc;
 61	bool prepared;
 62
 63	struct dentry *debugfs;
 64	const struct st7703_panel_desc *desc;
 65	enum drm_panel_orientation orientation;
 66};
 67
 68struct st7703_panel_desc {
 69	const struct drm_display_mode *mode;
 70	unsigned int lanes;
 71	unsigned long mode_flags;
 72	enum mipi_dsi_pixel_format format;
 73	int (*init_sequence)(struct st7703 *ctx);
 74};
 75
 76static inline struct st7703 *panel_to_st7703(struct drm_panel *panel)
 77{
 78	return container_of(panel, struct st7703, panel);
 79}
 80
 81static int jh057n_init_sequence(struct st7703 *ctx)
 82{
 83	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 84
 85	/*
 86	 * Init sequence was supplied by the panel vendor. Most of the commands
 87	 * resemble the ST7703 but the number of parameters often don't match
 88	 * so it's likely a clone.
 89	 */
 90	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC,
 91				   0xF1, 0x12, 0x83);
 92	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF,
 93				   0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00,
 94				   0x00, 0x00);
 95	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR,
 96				   0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
 97				   0x00);
 98	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
 99	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
100	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
101	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30);
102	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ,
103				   0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
104				   0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
105	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08);
106	msleep(20);
107
108	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
109	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
110	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1,
111				   0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12,
112				   0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
113				   0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
114				   0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
115				   0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
116				   0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
117				   0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
119	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2,
120				   0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121				   0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
122				   0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
123				   0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
124				   0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
125				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A,
127				   0xA5, 0x00, 0x00, 0x00, 0x00);
128	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA,
129				   0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37,
130				   0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11,
131				   0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41,
132				   0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10,
133				   0x11, 0x18);
134	msleep(20);
135
136	return 0;
137}
138
139static const struct drm_display_mode jh057n00900_mode = {
140	.hdisplay    = 720,
141	.hsync_start = 720 + 90,
142	.hsync_end   = 720 + 90 + 20,
143	.htotal	     = 720 + 90 + 20 + 20,
144	.vdisplay    = 1440,
145	.vsync_start = 1440 + 20,
146	.vsync_end   = 1440 + 20 + 4,
147	.vtotal	     = 1440 + 20 + 4 + 12,
148	.clock	     = 75276,
149	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
150	.width_mm    = 65,
151	.height_mm   = 130,
152};
153
154static const struct st7703_panel_desc jh057n00900_panel_desc = {
155	.mode = &jh057n00900_mode,
156	.lanes = 4,
157	.mode_flags = MIPI_DSI_MODE_VIDEO |
158		MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
159	.format = MIPI_DSI_FMT_RGB888,
160	.init_sequence = jh057n_init_sequence,
161};
162
163static int xbd599_init_sequence(struct st7703 *ctx)
164{
165	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
166
167	/*
168	 * Init sequence was supplied by the panel vendor.
169	 */
170
171	/* Magic sequence to unlock user commands below. */
172	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xF1, 0x12, 0x83);
173
174	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI,
175			       0x33, /* VC_main = 0, Lane_Number = 3 (4 lanes) */
176			       0x81, /* DSI_LDO_SEL = 1.7V, RTERM = 90 Ohm */
177			       0x05, /* IHSRX = x6 (Low High Speed driving ability) */
178			       0xF9, /* TX_CLK_SEL = fDSICLK/16 */
179			       0x0E, /* HFP_OSC (min. HFP number in DSI mode) */
180			       0x0E, /* HBP_OSC (min. HBP number in DSI mode) */
181			       /* The rest is undocumented in ST7703 datasheet */
182			       0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183			       0x44, 0x25, 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02,
184			       0x4F, 0x11, 0x00, 0x00, 0x37);
185
186	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT,
187			       0x25, /* PCCS = 2, ECP_DC_DIV = 1/4 HSYNC */
188			       0x22, /* DT = 15ms XDK_ECP = x2 */
189			       0x20, /* PFM_DC_DIV = /1 */
190			       0x03  /* ECP_SYNC_EN = 1, VGX_SYNC_EN = 1 */);
191
192	/* RGB I/F porch timing */
193	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF,
194			       0x10, /* VBP_RGB_GEN */
195			       0x10, /* VFP_RGB_GEN */
196			       0x05, /* DE_BP_RGB_GEN */
197			       0x05, /* DE_FP_RGB_GEN */
198			       /* The rest is undocumented in ST7703 datasheet */
199			       0x03, 0xFF,
200			       0x00, 0x00,
201			       0x00, 0x00);
202
203	/* Source driving settings. */
204	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR,
205			       0x73, /* N_POPON */
206			       0x73, /* N_NOPON */
207			       0x50, /* I_POPON */
208			       0x50, /* I_NOPON */
209			       0x00, /* SCR[31,24] */
210			       0xC0, /* SCR[23,16] */
211			       0x08, /* SCR[15,8] */
212			       0x70, /* SCR[7,0] */
213			       0x00  /* Undocumented */);
214
215	/* NVDDD_SEL = -1.8V, VDDD_SEL = out of range (possibly 1.9V?) */
216	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E);
217
218	/*
219	 * SS_PANEL = 1 (reverse scan), GS_PANEL = 0 (normal scan)
220	 * REV_PANEL = 1 (normally black panel), BGR_PANEL = 1 (BGR)
221	 */
222	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);
223
224	/* Zig-Zag Type C column inversion. */
225	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
226
227	/* Set display resolution. */
228	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP,
229			       0xF0, /* NL = 240 */
230			       0x12, /* RES_V_LSB = 0, BLK_CON = VSSD,
231				      * RESO_SEL = 720RGB
232				      */
233			       0xF0  /* WHITE_GND_EN = 1 (GND),
234				      * WHITE_FRAME_SEL = 7 frames,
235				      * ISC = 0 frames
236				      */);
237
238	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ,
239			       0x00, /* PNOEQ */
240			       0x00, /* NNOEQ */
241			       0x0B, /* PEQGND */
242			       0x0B, /* NEQGND */
243			       0x10, /* PEQVCI */
244			       0x10, /* NEQVCI */
245			       0x00, /* PEQVCI1 */
246			       0x00, /* NEQVCI1 */
247			       0x00, /* reserved */
248			       0x00, /* reserved */
249			       0xFF, /* reserved */
250			       0x00, /* reserved */
251			       0xC0, /* ESD_DET_DATA_WHITE = 1, ESD_WHITE_EN = 1 */
252			       0x10  /* SLPIN_OPTION = 1 (no need vsync after sleep-in)
253				      * VEDIO_NO_CHECK_EN = 0
254				      * ESD_WHITE_GND_EN = 0
255				      * ESD_DET_TIME_SEL = 0 frames
256				      */);
257
258	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETECO, 0x01, 0x00, 0xFF, 0xFF, 0x00);
259
260	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER,
261			       0x74, /* VBTHS, VBTLS: VGH = 17V, VBL = -11V */
262			       0x00, /* FBOFF_VGH = 0, FBOFF_VGL = 0 */
263			       0x32, /* VRP  */
264			       0x32, /* VRN */
265			       0x77, /* reserved */
266			       0xF1, /* APS = 1 (small),
267				      * VGL_DET_EN = 1, VGH_DET_EN = 1,
268				      * VGL_TURBO = 1, VGH_TURBO = 1
269				      */
270			       0xFF, /* VGH1_L_DIV, VGL1_L_DIV (1.5MHz) */
271			       0xFF, /* VGH1_R_DIV, VGL1_R_DIV (1.5MHz) */
272			       0xCC, /* VGH2_L_DIV, VGL2_L_DIV (2.6MHz) */
273			       0xCC, /* VGH2_R_DIV, VGL2_R_DIV (2.6MHz) */
274			       0x77, /* VGH3_L_DIV, VGL3_L_DIV (4.5MHz) */
275			       0x77  /* VGH3_R_DIV, VGL3_R_DIV (4.5MHz) */);
276
277	/* Reference voltage. */
278	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP,
279			       0x07, /* VREF_SEL = 4.2V */
280			       0x07  /* NVREF_SEL = 4.2V */);
281
282	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM,
283			       0x2C, /* VCOMDC_F = -0.67V */
284			       0x2C  /* VCOMDC_B = -0.67V */);
285
286	/* Undocumented command. */
287	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
288
289	/* This command is to set forward GIP timing. */
290	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1,
291			       0x82, 0x10, 0x06, 0x05, 0xA2, 0x0A, 0xA5, 0x12,
292			       0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38,
293			       0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00,
294			       0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88,
295			       0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64,
296			       0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
297			       0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
299
300	/* This command is to set backward GIP timing. */
301	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2,
302			       0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303			       0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88,
304			       0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13,
305			       0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
306			       0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00,
307			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0A,
309			       0xA5, 0x00, 0x00, 0x00, 0x00);
310
311	/* Adjust the gamma characteristics of the panel. */
312	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA,
313			       0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41, 0x35,
314			       0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12, 0x12,
315			       0x18, 0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41,
316			       0x35, 0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12,
317			       0x12, 0x18);
318
319	return 0;
320}
321
322static const struct drm_display_mode xbd599_mode = {
323	.hdisplay    = 720,
324	.hsync_start = 720 + 40,
325	.hsync_end   = 720 + 40 + 40,
326	.htotal	     = 720 + 40 + 40 + 40,
327	.vdisplay    = 1440,
328	.vsync_start = 1440 + 18,
329	.vsync_end   = 1440 + 18 + 10,
330	.vtotal	     = 1440 + 18 + 10 + 17,
331	.clock	     = 69000,
332	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
333	.width_mm    = 68,
334	.height_mm   = 136,
335};
336
337static const struct st7703_panel_desc xbd599_desc = {
338	.mode = &xbd599_mode,
339	.lanes = 4,
340	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
341	.format = MIPI_DSI_FMT_RGB888,
342	.init_sequence = xbd599_init_sequence,
343};
344
345static int rg353v2_init_sequence(struct st7703 *ctx)
346{
347	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
348
349	/*
350	 * Init sequence was supplied by the panel vendor.
351	 */
352
353	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
354	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETAPID, 0x00, 0x00, 0x00,
355			       0xda, 0x80);
356	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, 0x00, 0x13, 0x70);
357	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
358			       0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
359	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
360	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, 0x0a, 0x0a);
361	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, 0x92, 0x92);
362	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22,
363			       0xf0, 0x63);
364	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05,
365			       0xf9, 0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00,
366			       0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a,
367			       0x00, 0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
368	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x47);
369	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
370	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
371			       0x00, 0x00, 0x12, 0x50, 0x00);
372	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER, 0x53, 0xc0, 0x32,
373			       0x32, 0x77, 0xe1, 0xdd, 0xdd, 0x77, 0x77, 0x33,
374			       0x33);
375	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETECO, 0x82, 0x00, 0xbf, 0xff,
376			       0x00, 0xff);
377	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETIO, 0xb8, 0x00, 0x0a, 0x00,
378			       0x00, 0x00);
379	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCABC, 0x10, 0x40, 0x1e,
380			       0x02);
381	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0b);
382	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA, 0x00, 0x07, 0x0d,
383			       0x37, 0x35, 0x3f, 0x41, 0x44, 0x06, 0x0c, 0x0d,
384			       0x0f, 0x11, 0x10, 0x12, 0x14, 0x1a, 0x00, 0x07,
385			       0x0d, 0x37, 0x35, 0x3f, 0x41, 0x44, 0x06, 0x0c,
386			       0x0d, 0x0f, 0x11, 0x10, 0x12, 0x14, 0x1a);
387	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0b, 0x0b,
388			       0x0b, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
389			       0xc0, 0x10);
390	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x02, 0x00,
391			       0x00, 0xb0, 0xb1, 0x11, 0x31, 0x23, 0x28, 0x80,
392			       0xb0, 0xb1, 0x27, 0x08, 0x00, 0x04, 0x02, 0x00,
393			       0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00,
394			       0x88, 0x88, 0xba, 0x60, 0x24, 0x08, 0x88, 0x88,
395			       0x88, 0x88, 0x88, 0x88, 0x88, 0xba, 0x71, 0x35,
396			       0x18, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
397			       0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
398			       0x00, 0x00, 0x00);
399	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2, 0x97, 0x0a, 0x82, 0x02,
400			       0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
401			       0x81, 0x88, 0xba, 0x17, 0x53, 0x88, 0x88, 0x88,
402			       0x88, 0x88, 0x88, 0x80, 0x88, 0xba, 0x06, 0x42,
403			       0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00,
404			       0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
405			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
406			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
407			       0x00);
408	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff, 0x01);
409
410	return 0;
411}
412
413static const struct drm_display_mode rg353v2_mode = {
414	.hdisplay	= 640,
415	.hsync_start	= 640 + 40,
416	.hsync_end	= 640 + 40 + 2,
417	.htotal		= 640 + 40 + 2 + 80,
418	.vdisplay	= 480,
419	.vsync_start	= 480 + 18,
420	.vsync_end	= 480 + 18 + 2,
421	.vtotal		= 480 + 18 + 2 + 28,
422	.clock		= 24150,
423	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
424	.width_mm	= 70,
425	.height_mm	= 57,
426};
427
428static const struct st7703_panel_desc rg353v2_desc = {
429	.mode = &rg353v2_mode,
430	.lanes = 4,
431	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
432		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
433	.format = MIPI_DSI_FMT_RGB888,
434	.init_sequence = rg353v2_init_sequence,
435};
436
437static int rgb30panel_init_sequence(struct st7703 *ctx)
438{
439	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
440
441	/* Init sequence extracted from Powkiddy RGB30 BSP kernel. */
442
443	/*
444	 * For some reason this specific panel must be taken out of sleep
445	 * before the full init sequence, or else it will not display.
446	 */
447	mipi_dsi_dcs_exit_sleep_mode(dsi);
448	msleep(250);
449
450	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
451	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05, 0xf9,
452			       0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
453			       0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a, 0x00,
454			       0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
455	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22, 0xf0,
456			       0x63);
457	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
458	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
459			       0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
460	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
461			       0x00, 0x00, 0x12, 0x70, 0x00);
462	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x46);
463	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0b);
464	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
465	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, 0x3c, 0x12, 0x30);
466	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0b, 0x0b,
467			       0x03, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
468			       0xc0, 0x10);
469	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER, 0x36, 0x00, 0x32,
470			       0x32, 0x77, 0xf1, 0xcc, 0xcc, 0x77, 0x77, 0x33,
471			       0x33);
472	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, 0x0a, 0x0a);
473	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, 0x88, 0x88);
474	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x0a, 0x10,
475			       0x0f, 0xa1, 0x80, 0x12, 0x31, 0x23, 0x47, 0x86,
476			       0xa1, 0x80, 0x47, 0x08, 0x00, 0x00, 0x0d, 0x00,
477			       0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
478			       0x48, 0x02, 0x8b, 0xaf, 0x46, 0x02, 0x88, 0x88,
479			       0x88, 0x88, 0x88, 0x48, 0x13, 0x8b, 0xaf, 0x57,
480			       0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
481			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482			       0x00, 0x00, 0x00);
483	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2, 0x96, 0x12, 0x01, 0x01,
484			       0x01, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
485			       0x4f, 0x31, 0x8b, 0xa8, 0x31, 0x75, 0x88, 0x88,
486			       0x88, 0x88, 0x88, 0x4f, 0x20, 0x8b, 0xa8, 0x20,
487			       0x64, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00,
488			       0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
489			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490			       0x00, 0x00, 0x40, 0xa1, 0x80, 0x00, 0x00, 0x00,
491			       0x00);
492	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA, 0x00, 0x0a, 0x0f,
493			       0x29, 0x3b, 0x3f, 0x42, 0x39, 0x06, 0x0d, 0x10,
494			       0x13, 0x15, 0x14, 0x15, 0x10, 0x17, 0x00, 0x0a,
495			       0x0f, 0x29, 0x3b, 0x3f, 0x42, 0x39, 0x06, 0x0d,
496			       0x10, 0x13, 0x15, 0x14, 0x15, 0x10, 0x17);
497
498	return 0;
499}
500
501static const struct drm_display_mode rgb30panel_mode = {
502	.hdisplay	= 720,
503	.hsync_start	= 720 + 45,
504	.hsync_end	= 720 + 45 + 4,
505	.htotal		= 720 + 45 + 4 + 45,
506	.vdisplay	= 720,
507	.vsync_start	= 720 + 15,
508	.vsync_end	= 720 + 15 + 3,
509	.vtotal		= 720 + 15 + 3 + 11,
510	.clock		= 36570,
511	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
512	.width_mm	= 76,
513	.height_mm	= 76,
514};
515
516static const struct st7703_panel_desc rgb30panel_desc = {
517	.mode = &rgb30panel_mode,
518	.lanes = 4,
519	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
520		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
521	.format = MIPI_DSI_FMT_RGB888,
522	.init_sequence = rgb30panel_init_sequence,
523};
524
525static int rgb10max3_panel_init_sequence(struct st7703 *ctx)
526{
527	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
528
529	/* Init sequence extracted from Powkiddy RGB10MAX3 BSP kernel. */
530
531	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xf1, 0x12, 0x83);
532	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETAPID, 0x00, 0x00, 0x00, 0xda,
533			       0x80);
534	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, 0xc8, 0x02, 0x30);
535	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,
536			       0x28, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00);
537	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);
538	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, 0x04, 0x04);
539	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, 0x78, 0x78);
540	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT, 0x25, 0x22, 0xf0,
541			       0x63);
542	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05, 0xf9,
543			       0x0e, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
544			       0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0a, 0x00,
545			       0x00, 0x01, 0x4f, 0x01, 0x00, 0x00, 0x37);
546	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x47);
547	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);
548	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50,
549			       0x00, 0x00, 0x12, 0x70, 0x00);
550	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER, 0x25, 0x00, 0x32,
551			       0x32, 0x77, 0xe1, 0xff, 0xff, 0xcc, 0xcc, 0x77,
552			       0x77);
553	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETECO, 0x82, 0x00, 0xbf, 0xff,
554			       0x00, 0xff);
555	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETIO, 0xb8, 0x00, 0x0a, 0x00,
556			       0x00, 0x00);
557	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCABC, 0x10, 0x40, 0x1e,
558			       0x02);
559	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0b);
560	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA, 0x00, 0x04, 0x07,
561			       0x2a, 0x39, 0x3f, 0x36, 0x31, 0x06, 0x0b, 0x0e,
562			       0x12, 0x14, 0x12, 0x13, 0x0f, 0x17, 0x00, 0x04,
563			       0x07, 0x2a, 0x39, 0x3f, 0x36, 0x31, 0x06, 0x0b,
564			       0x0e, 0x12, 0x14, 0x12, 0x13, 0x0f, 0x17);
565	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ, 0x03, 0x03, 0x03, 0x03,
566			       0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80,
567			       0xc0, 0x10);
568	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1, 0xc8, 0x10, 0x08, 0x00,
569			       0x00, 0x41, 0xf8, 0x12, 0x31, 0x23, 0x37, 0x86,
570			       0x11, 0xc8, 0x37, 0x2a, 0x00, 0x00, 0x0c, 0x00,
571			       0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
572			       0x88, 0x20, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88,
573			       0x88, 0x88, 0xff, 0x88, 0x31, 0x57, 0x13, 0x88,
574			       0x88, 0x88, 0x88, 0x88, 0x88, 0xff, 0x00, 0x00,
575			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
576			       0x00, 0x00, 0x00);
577	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2, 0x00, 0x1a, 0x00, 0x00,
578			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
579			       0x8f, 0x13, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88,
580			       0x88, 0x88, 0xf8, 0x8f, 0x02, 0x20, 0x64, 0x88,
581			       0x88, 0x88, 0x88, 0x88, 0x88, 0xf8, 0x00, 0x00,
582			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
583			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585			       0x00);
586	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff, 0x01);
587
588	return 0;
589}
590
591static const struct drm_display_mode rgb10max3_panel_mode = {
592	.hdisplay	= 720,
593	.hsync_start	= 720 + 40,
594	.hsync_end	= 720 + 40 + 10,
595	.htotal		= 720 + 40 + 10 + 40,
596	.vdisplay	= 1280,
597	.vsync_start	= 1280 + 16,
598	.vsync_end	= 1280 + 16 + 4,
599	.vtotal		= 1280 + 16 + 4 + 14,
600	.clock		= 63800,
601	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
602	.width_mm	= 62,
603	.height_mm	= 109,
604};
605
606static const struct st7703_panel_desc rgb10max3_panel_desc = {
607	.mode = &rgb10max3_panel_mode,
608	.lanes = 4,
609	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
610		      MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,
611	.format = MIPI_DSI_FMT_RGB888,
612	.init_sequence = rgb10max3_panel_init_sequence,
613};
614
615static int st7703_enable(struct drm_panel *panel)
616{
617	struct st7703 *ctx = panel_to_st7703(panel);
618	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
619	int ret;
620
621	ret = ctx->desc->init_sequence(ctx);
622	if (ret < 0) {
623		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
624		return ret;
625	}
626
627	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
628	if (ret < 0) {
629		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
630		return ret;
631	}
632
633	/* It takes the controller 120 msec to wake up after sleep. */
634	msleep(120);
635
636	ret = mipi_dsi_dcs_set_display_on(dsi);
637	if (ret)
638		return ret;
639
640	dev_dbg(ctx->dev, "Panel init sequence done\n");
641
642	return 0;
643}
644
645static int st7703_disable(struct drm_panel *panel)
646{
647	struct st7703 *ctx = panel_to_st7703(panel);
648	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
649	int ret;
650
651	ret = mipi_dsi_dcs_set_display_off(dsi);
652	if (ret < 0)
653		dev_err(ctx->dev, "Failed to turn off the display: %d\n", ret);
654
655	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
656	if (ret < 0)
657		dev_err(ctx->dev, "Failed to enter sleep mode: %d\n", ret);
658
659	/* It takes the controller 120 msec to enter sleep mode. */
660	msleep(120);
661
662	return 0;
663}
664
665static int st7703_unprepare(struct drm_panel *panel)
666{
667	struct st7703 *ctx = panel_to_st7703(panel);
668
669	if (!ctx->prepared)
670		return 0;
671
672	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
673	regulator_disable(ctx->iovcc);
674	regulator_disable(ctx->vcc);
675	ctx->prepared = false;
676
677	return 0;
678}
679
680static int st7703_prepare(struct drm_panel *panel)
681{
682	struct st7703 *ctx = panel_to_st7703(panel);
683	int ret;
684
685	if (ctx->prepared)
686		return 0;
687
688	dev_dbg(ctx->dev, "Resetting the panel\n");
689	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
690
691	ret = regulator_enable(ctx->iovcc);
692	if (ret < 0) {
693		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
694		return ret;
695	}
696
697	ret = regulator_enable(ctx->vcc);
698	if (ret < 0) {
699		dev_err(ctx->dev, "Failed to enable vcc supply: %d\n", ret);
700		regulator_disable(ctx->iovcc);
701		return ret;
702	}
703
704	/* Give power supplies time to stabilize before deasserting reset. */
705	usleep_range(10000, 20000);
706
707	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
708	usleep_range(15000, 20000);
709
710	ctx->prepared = true;
711
712	return 0;
713}
714
715static const u32 mantix_bus_formats[] = {
716	MEDIA_BUS_FMT_RGB888_1X24,
717};
718
719static int st7703_get_modes(struct drm_panel *panel,
720			    struct drm_connector *connector)
721{
722	struct st7703 *ctx = panel_to_st7703(panel);
723	struct drm_display_mode *mode;
724
725	mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
726	if (!mode) {
727		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
728			ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
729			drm_mode_vrefresh(ctx->desc->mode));
730		return -ENOMEM;
731	}
732
733	drm_mode_set_name(mode);
734
735	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
736	connector->display_info.width_mm = mode->width_mm;
737	connector->display_info.height_mm = mode->height_mm;
738	drm_mode_probed_add(connector, mode);
739
740	drm_display_info_set_bus_formats(&connector->display_info,
741					 mantix_bus_formats,
742					 ARRAY_SIZE(mantix_bus_formats));
743
744	return 1;
745}
746
747static enum drm_panel_orientation st7703_get_orientation(struct drm_panel *panel)
748{
749	struct st7703 *st7703 = panel_to_st7703(panel);
750
751	return st7703->orientation;
752}
753
754static const struct drm_panel_funcs st7703_drm_funcs = {
755	.disable   = st7703_disable,
756	.unprepare = st7703_unprepare,
757	.prepare   = st7703_prepare,
758	.enable	   = st7703_enable,
759	.get_modes = st7703_get_modes,
760	.get_orientation = st7703_get_orientation,
761};
762
763static int allpixelson_set(void *data, u64 val)
764{
765	struct st7703 *ctx = data;
766	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
767
768	dev_dbg(ctx->dev, "Setting all pixels on\n");
769	mipi_dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
770	msleep(val * 1000);
771	/* Reset the panel to get video back */
772	drm_panel_disable(&ctx->panel);
773	drm_panel_unprepare(&ctx->panel);
774	drm_panel_prepare(&ctx->panel);
775	drm_panel_enable(&ctx->panel);
776
777	return 0;
778}
779
780DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL,
781			allpixelson_set, "%llu\n");
782
783static void st7703_debugfs_init(struct st7703 *ctx)
784{
785	ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL);
786
787	debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx,
788			    &allpixelson_fops);
789}
790
791static void st7703_debugfs_remove(struct st7703 *ctx)
792{
793	debugfs_remove_recursive(ctx->debugfs);
794	ctx->debugfs = NULL;
795}
796
797static int st7703_probe(struct mipi_dsi_device *dsi)
798{
799	struct device *dev = &dsi->dev;
800	struct st7703 *ctx;
801	int ret;
802
803	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
804	if (!ctx)
805		return -ENOMEM;
806
807	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
808	if (IS_ERR(ctx->reset_gpio))
809		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset gpio\n");
810
811	mipi_dsi_set_drvdata(dsi, ctx);
812
813	ctx->dev = dev;
814	ctx->desc = of_device_get_match_data(dev);
815
816	dsi->mode_flags = ctx->desc->mode_flags;
817	dsi->format = ctx->desc->format;
818	dsi->lanes = ctx->desc->lanes;
819
820	ctx->vcc = devm_regulator_get(dev, "vcc");
821	if (IS_ERR(ctx->vcc))
822		return dev_err_probe(dev, PTR_ERR(ctx->vcc), "Failed to request vcc regulator\n");
823
824	ctx->iovcc = devm_regulator_get(dev, "iovcc");
825	if (IS_ERR(ctx->iovcc))
826		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
827				     "Failed to request iovcc regulator\n");
828
829	ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
830	if (ret < 0)
831		return dev_err_probe(&dsi->dev, ret, "Failed to get orientation\n");
832
833	drm_panel_init(&ctx->panel, dev, &st7703_drm_funcs,
834		       DRM_MODE_CONNECTOR_DSI);
835
836	ret = drm_panel_of_backlight(&ctx->panel);
837	if (ret)
838		return ret;
839
840	drm_panel_add(&ctx->panel);
841
842	ret = mipi_dsi_attach(dsi);
843	if (ret < 0) {
844		dev_err(dev, "mipi_dsi_attach failed (%d). Is host ready?\n", ret);
845		drm_panel_remove(&ctx->panel);
846		return ret;
847	}
848
849	dev_info(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
850		 ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
851		 drm_mode_vrefresh(ctx->desc->mode),
852		 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
853
854	st7703_debugfs_init(ctx);
855	return 0;
856}
857
858static void st7703_shutdown(struct mipi_dsi_device *dsi)
859{
860	struct st7703 *ctx = mipi_dsi_get_drvdata(dsi);
861	int ret;
862
863	ret = drm_panel_unprepare(&ctx->panel);
864	if (ret < 0)
865		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
866
867	ret = drm_panel_disable(&ctx->panel);
868	if (ret < 0)
869		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
870}
871
872static void st7703_remove(struct mipi_dsi_device *dsi)
873{
874	struct st7703 *ctx = mipi_dsi_get_drvdata(dsi);
875	int ret;
876
877	st7703_shutdown(dsi);
878
879	ret = mipi_dsi_detach(dsi);
880	if (ret < 0)
881		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
882
883	drm_panel_remove(&ctx->panel);
884
885	st7703_debugfs_remove(ctx);
886}
887
888static const struct of_device_id st7703_of_match[] = {
889	{ .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc },
890	{ .compatible = "powkiddy,rgb10max3-panel", .data = &rgb10max3_panel_desc },
891	{ .compatible = "powkiddy,rgb30-panel", .data = &rgb30panel_desc },
892	{ .compatible = "rocktech,jh057n00900", .data = &jh057n00900_panel_desc },
893	{ .compatible = "xingbangda,xbd599", .data = &xbd599_desc },
894	{ /* sentinel */ }
895};
896MODULE_DEVICE_TABLE(of, st7703_of_match);
897
898static struct mipi_dsi_driver st7703_driver = {
899	.probe	= st7703_probe,
900	.remove = st7703_remove,
901	.shutdown = st7703_shutdown,
902	.driver = {
903		.name = DRV_NAME,
904		.of_match_table = st7703_of_match,
905	},
906};
907module_mipi_dsi_driver(st7703_driver);
908
909MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>");
910MODULE_DESCRIPTION("DRM driver for Sitronix ST7703 based MIPI DSI panels");
911MODULE_LICENSE("GPL v2");