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