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 | |
55 | struct 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 | |
68 | struct 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 | |
76 | static inline struct st7703 *panel_to_st7703(struct drm_panel *panel) |
77 | { |
78 | return container_of(panel, struct st7703, panel); |
79 | } |
80 | |
81 | static 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(msecs: 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(msecs: 20); |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | static 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 | |
154 | static 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 | |
163 | static 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 | |
322 | static 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 | |
337 | static 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 | |
345 | static 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 | |
413 | static 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 | |
428 | static 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 | |
437 | static 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(msecs: 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 | |
501 | static 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 | |
516 | static 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 | |
525 | static 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 | |
591 | static 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 | |
606 | static 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 | |
615 | static 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(msecs: 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 | |
645 | static 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(msecs: 120); |
661 | |
662 | return 0; |
663 | } |
664 | |
665 | static 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(desc: ctx->reset_gpio, value: 1); |
673 | regulator_disable(regulator: ctx->iovcc); |
674 | regulator_disable(regulator: ctx->vcc); |
675 | ctx->prepared = false; |
676 | |
677 | return 0; |
678 | } |
679 | |
680 | static 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(desc: ctx->reset_gpio, value: 1); |
690 | |
691 | ret = regulator_enable(regulator: 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(regulator: ctx->vcc); |
698 | if (ret < 0) { |
699 | dev_err(ctx->dev, "Failed to enable vcc supply: %d\n" , ret); |
700 | regulator_disable(regulator: ctx->iovcc); |
701 | return ret; |
702 | } |
703 | |
704 | /* Give power supplies time to stabilize before deasserting reset. */ |
705 | usleep_range(min: 10000, max: 20000); |
706 | |
707 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0); |
708 | usleep_range(min: 15000, max: 20000); |
709 | |
710 | ctx->prepared = true; |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static const u32 mantix_bus_formats[] = { |
716 | MEDIA_BUS_FMT_RGB888_1X24, |
717 | }; |
718 | |
719 | static 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(dev: connector->dev, mode: 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(info: &connector->display_info, |
741 | formats: mantix_bus_formats, |
742 | ARRAY_SIZE(mantix_bus_formats)); |
743 | |
744 | return 1; |
745 | } |
746 | |
747 | static 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 | |
754 | static 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 | |
763 | static 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(msecs: val * 1000); |
771 | /* Reset the panel to get video back */ |
772 | drm_panel_disable(panel: &ctx->panel); |
773 | drm_panel_unprepare(panel: &ctx->panel); |
774 | drm_panel_prepare(panel: &ctx->panel); |
775 | drm_panel_enable(panel: &ctx->panel); |
776 | |
777 | return 0; |
778 | } |
779 | |
780 | DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL, |
781 | allpixelson_set, "%llu\n" ); |
782 | |
783 | static void st7703_debugfs_init(struct st7703 *ctx) |
784 | { |
785 | ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL); |
786 | |
787 | debugfs_create_file(name: "allpixelson" , mode: 0600, parent: ctx->debugfs, data: ctx, |
788 | fops: &allpixelson_fops); |
789 | } |
790 | |
791 | static void st7703_debugfs_remove(struct st7703 *ctx) |
792 | { |
793 | debugfs_remove_recursive(dentry: ctx->debugfs); |
794 | ctx->debugfs = NULL; |
795 | } |
796 | |
797 | static 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, size: sizeof(*ctx), GFP_KERNEL); |
804 | if (!ctx) |
805 | return -ENOMEM; |
806 | |
807 | ctx->reset_gpio = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
808 | if (IS_ERR(ptr: ctx->reset_gpio)) |
809 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->reset_gpio), fmt: "Failed to get reset gpio\n" ); |
810 | |
811 | mipi_dsi_set_drvdata(dsi, data: 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, id: "vcc" ); |
821 | if (IS_ERR(ptr: ctx->vcc)) |
822 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->vcc), fmt: "Failed to request vcc regulator\n" ); |
823 | |
824 | ctx->iovcc = devm_regulator_get(dev, id: "iovcc" ); |
825 | if (IS_ERR(ptr: ctx->iovcc)) |
826 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->iovcc), |
827 | fmt: "Failed to request iovcc regulator\n" ); |
828 | |
829 | ret = of_drm_get_panel_orientation(np: dsi->dev.of_node, orientation: &ctx->orientation); |
830 | if (ret < 0) |
831 | return dev_err_probe(dev: &dsi->dev, err: ret, fmt: "Failed to get orientation\n" ); |
832 | |
833 | drm_panel_init(panel: &ctx->panel, dev, funcs: &st7703_drm_funcs, |
834 | DRM_MODE_CONNECTOR_DSI); |
835 | |
836 | ret = drm_panel_of_backlight(panel: &ctx->panel); |
837 | if (ret) |
838 | return ret; |
839 | |
840 | drm_panel_add(panel: &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(panel: &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 | |
858 | static 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(panel: &ctx->panel); |
864 | if (ret < 0) |
865 | dev_err(&dsi->dev, "Failed to unprepare panel: %d\n" , ret); |
866 | |
867 | ret = drm_panel_disable(panel: &ctx->panel); |
868 | if (ret < 0) |
869 | dev_err(&dsi->dev, "Failed to disable panel: %d\n" , ret); |
870 | } |
871 | |
872 | static 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(panel: &ctx->panel); |
884 | |
885 | st7703_debugfs_remove(ctx); |
886 | } |
887 | |
888 | static 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 | }; |
896 | MODULE_DEVICE_TABLE(of, st7703_of_match); |
897 | |
898 | static 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 | }; |
907 | module_mipi_dsi_driver(st7703_driver); |
908 | |
909 | MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>" ); |
910 | MODULE_DESCRIPTION("DRM driver for Sitronix ST7703 based MIPI DSI panels" ); |
911 | MODULE_LICENSE("GPL v2" ); |
912 | |