xref: /linux/drivers/gpu/drm/panel/panel-himax-hx8394.c (revision 22c55fb9eb92395d999b8404d73e58540d11bdd8)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for panels based on Himax HX8394 controller, such as:
4  *
5  * - HannStar HSD060BHW4 5.99" MIPI-DSI panel
6  *
7  * Copyright (C) 2021 Kamil Trzciński
8  *
9  * Based on drivers/gpu/drm/panel/panel-sitronix-st7703.c
10  * Copyright (C) Purism SPC 2019
11  */
12 
13 #include <linux/delay.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/media-bus-format.h>
16 #include <linux/mod_devicetable.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include <linux/regulator/consumer.h>
20 
21 #include <video/mipi_display.h>
22 
23 #include <drm/drm_mipi_dsi.h>
24 #include <drm/drm_modes.h>
25 #include <drm/drm_panel.h>
26 
27 #define DRV_NAME "panel-himax-hx8394"
28 
29 /* Manufacturer specific commands sent via DSI, listed in HX8394-F datasheet */
30 #define HX8394_CMD_SETSEQUENCE	  0xb0
31 #define HX8394_CMD_SETPOWER	  0xb1
32 #define HX8394_CMD_SETDISP	  0xb2
33 #define HX8394_CMD_SETCYC	  0xb4
34 #define HX8394_CMD_SETVCOM	  0xb6
35 #define HX8394_CMD_SETTE	  0xb7
36 #define HX8394_CMD_SETSENSOR	  0xb8
37 #define HX8394_CMD_SETEXTC	  0xb9
38 #define HX8394_CMD_SETMIPI	  0xba
39 #define HX8394_CMD_SETOTP	  0xbb
40 #define HX8394_CMD_SETREGBANK	  0xbd
41 #define HX8394_CMD_UNKNOWN5	  0xbf
42 #define HX8394_CMD_UNKNOWN1	  0xc0
43 #define HX8394_CMD_SETDGCLUT	  0xc1
44 #define HX8394_CMD_SETID	  0xc3
45 #define HX8394_CMD_SETDDB	  0xc4
46 #define HX8394_CMD_UNKNOWN2	  0xc6
47 #define HX8394_CMD_SETCABC	  0xc9
48 #define HX8394_CMD_SETCABCGAIN	  0xca
49 #define HX8394_CMD_SETPANEL	  0xcc
50 #define HX8394_CMD_SETOFFSET	  0xd2
51 #define HX8394_CMD_SETGIP0	  0xd3
52 #define HX8394_CMD_UNKNOWN3	  0xd4
53 #define HX8394_CMD_SETGIP1	  0xd5
54 #define HX8394_CMD_SETGIP2	  0xd6
55 #define HX8394_CMD_SETGPO	  0xd6
56 #define HX8394_CMD_UNKNOWN4	  0xd8
57 #define HX8394_CMD_SETSCALING	  0xdd
58 #define HX8394_CMD_SETIDLE	  0xdf
59 #define HX8394_CMD_SETGAMMA	  0xe0
60 #define HX8394_CMD_SETCHEMODE_DYN 0xe4
61 #define HX8394_CMD_SETCHE	  0xe5
62 #define HX8394_CMD_SETCESEL	  0xe6
63 #define HX8394_CMD_SET_SP_CMD	  0xe9
64 #define HX8394_CMD_SETREADINDEX	  0xfe
65 #define HX8394_CMD_GETSPIREAD	  0xff
66 
67 struct hx8394 {
68 	struct device *dev;
69 	struct drm_panel panel;
70 	struct gpio_desc *reset_gpio;
71 	struct regulator *vcc;
72 	struct regulator *iovcc;
73 	enum drm_panel_orientation orientation;
74 
75 	const struct hx8394_panel_desc *desc;
76 };
77 
78 struct hx8394_panel_desc {
79 	const struct drm_display_mode *mode;
80 	unsigned int lanes;
81 	unsigned long mode_flags;
82 	enum mipi_dsi_pixel_format format;
83 	void (*init_sequence)(struct mipi_dsi_multi_context *dsi_ctx);
84 };
85 
86 static inline struct hx8394 *panel_to_hx8394(struct drm_panel *panel)
87 {
88 	return container_of(panel, struct hx8394, panel);
89 }
90 
91 static void hsd060bhw4_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
92 {
93 	/* 5.19.8 SETEXTC: Set extension command (B9h) */
94 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC,
95 				     0xff, 0x83, 0x94);
96 
97 	/* 5.19.2 SETPOWER: Set power (B1h) */
98 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
99 				     0x48, 0x11, 0x71, 0x09, 0x32, 0x24, 0x71, 0x31, 0x55, 0x30);
100 
101 	/* 5.19.9 SETMIPI: Set MIPI control (BAh) */
102 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETMIPI,
103 				     0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
104 
105 	/* 5.19.3 SETDISP: Set display related register (B2h) */
106 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP,
107 				     0x00, 0x80, 0x78, 0x0c, 0x07);
108 
109 	/* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
110 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC,
111 				     0x12, 0x63, 0x12, 0x63, 0x12, 0x63, 0x01, 0x0c, 0x7c, 0x55,
112 				     0x00, 0x3f, 0x12, 0x6b, 0x12, 0x6b, 0x12, 0x6b, 0x01, 0x0c,
113 				     0x7c);
114 
115 	/* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
116 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0,
117 				     0x00, 0x00, 0x00, 0x00, 0x3c, 0x1c, 0x00, 0x00, 0x32, 0x10,
118 				     0x09, 0x00, 0x09, 0x32, 0x15, 0xad, 0x05, 0xad, 0x32, 0x00,
119 				     0x00, 0x00, 0x00, 0x37, 0x03, 0x0b, 0x0b, 0x37, 0x00, 0x00,
120 				     0x00, 0x0c, 0x40);
121 
122 	/* 5.19.20 Set GIP Option1 (D5h) */
123 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1,
124 				     0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b, 0x1a, 0x1a, 0x00, 0x01,
125 				     0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x20, 0x21, 0x18, 0x18,
126 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
127 				     0x24, 0x25, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
128 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
129 
130 	/* 5.19.21 Set GIP Option2 (D6h) */
131 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2,
132 				     0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b, 0x1a, 0x1a, 0x07, 0x06,
133 				     0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x25, 0x24, 0x18, 0x18,
134 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
135 				     0x21, 0x20, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
136 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
137 
138 	/* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
139 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA,
140 				     0x00, 0x04, 0x0c, 0x12, 0x14, 0x18, 0x1a, 0x18, 0x31, 0x3f,
141 				     0x4d, 0x4c, 0x54, 0x65, 0x6b, 0x70, 0x7f, 0x82, 0x7e, 0x8a,
142 				     0x99, 0x4a, 0x48, 0x49, 0x4b, 0x4a, 0x4c, 0x4b, 0x7f, 0x00,
143 				     0x04, 0x0c, 0x11, 0x13, 0x17, 0x1a, 0x18, 0x31,
144 				     0x3f, 0x4d, 0x4c, 0x54, 0x65, 0x6b, 0x70, 0x7f,
145 				     0x82, 0x7e, 0x8a, 0x99, 0x4a, 0x48, 0x49, 0x4b,
146 				     0x4a, 0x4c, 0x4b, 0x7f);
147 
148 	/* 5.19.17 SETPANEL (CCh) */
149 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL,
150 				     0x0b);
151 
152 	/* Unknown command, not listed in the HX8394-F datasheet */
153 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN1,
154 				     0x1f, 0x31);
155 
156 	/* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
157 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM,
158 				     0x7d, 0x7d);
159 
160 	/* Unknown command, not listed in the HX8394-F datasheet */
161 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN3,
162 				     0x02);
163 
164 	/* 5.19.11 Set register bank (BDh) */
165 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
166 				     0x01);
167 
168 	/* 5.19.2 SETPOWER: Set power (B1h) */
169 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
170 				     0x00);
171 
172 	/* 5.19.11 Set register bank (BDh) */
173 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
174 				     0x00);
175 
176 	/* Unknown command, not listed in the HX8394-F datasheet */
177 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN3,
178 				     0xed);
179 }
180 
181 static const struct drm_display_mode hsd060bhw4_mode = {
182 	.hdisplay    = 720,
183 	.hsync_start = 720 + 40,
184 	.hsync_end   = 720 + 40 + 46,
185 	.htotal	     = 720 + 40 + 46 + 40,
186 	.vdisplay    = 1440,
187 	.vsync_start = 1440 + 9,
188 	.vsync_end   = 1440 + 9 + 7,
189 	.vtotal	     = 1440 + 9 + 7 + 7,
190 	.clock	     = 74250,
191 	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
192 	.width_mm    = 68,
193 	.height_mm   = 136,
194 };
195 
196 static const struct hx8394_panel_desc hsd060bhw4_desc = {
197 	.mode = &hsd060bhw4_mode,
198 	.lanes = 4,
199 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
200 	.format = MIPI_DSI_FMT_RGB888,
201 	.init_sequence = hsd060bhw4_init_sequence,
202 };
203 
204 static void powkiddy_x55_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
205 {
206 	/* 5.19.8 SETEXTC: Set extension command (B9h) */
207 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC,
208 				     0xff, 0x83, 0x94);
209 
210 	/* 5.19.9 SETMIPI: Set MIPI control (BAh) */
211 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETMIPI,
212 				     0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
213 
214 	/* 5.19.2 SETPOWER: Set power (B1h) */
215 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
216 				     0x48, 0x12, 0x72, 0x09, 0x32, 0x54, 0x71, 0x71, 0x57, 0x47);
217 
218 	/* 5.19.3 SETDISP: Set display related register (B2h) */
219 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP,
220 				     0x00, 0x80, 0x64, 0x2c, 0x16, 0x2f);
221 
222 	/* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
223 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC,
224 				     0x73, 0x74, 0x73, 0x74, 0x73, 0x74, 0x01, 0x0c, 0x86, 0x75,
225 				     0x00, 0x3f, 0x73, 0x74, 0x73, 0x74, 0x73, 0x74, 0x01, 0x0c,
226 				     0x86);
227 
228 	/* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
229 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM,
230 				     0x6e, 0x6e);
231 
232 	/* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
233 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0,
234 				     0x00, 0x00, 0x07, 0x07, 0x40, 0x07, 0x0c, 0x00, 0x08, 0x10,
235 				     0x08, 0x00, 0x08, 0x54, 0x15, 0x0a, 0x05, 0x0a, 0x02, 0x15,
236 				     0x06, 0x05, 0x06, 0x47, 0x44, 0x0a, 0x0a, 0x4b, 0x10, 0x07,
237 				     0x07, 0x0c, 0x40);
238 
239 	/* 5.19.20 Set GIP Option1 (D5h) */
240 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1,
241 				     0x1c, 0x1c, 0x1d, 0x1d, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
242 				     0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x24, 0x25, 0x18, 0x18,
243 				     0x26, 0x27, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
244 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x20, 0x21,
245 				     0x18, 0x18, 0x18, 0x18);
246 
247 	/* 5.19.21 Set GIP Option2 (D6h) */
248 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2,
249 				     0x1c, 0x1c, 0x1d, 0x1d, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
250 				     0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08, 0x21, 0x20, 0x18, 0x18,
251 				     0x27, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
252 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x25, 0x24,
253 				     0x18, 0x18, 0x18, 0x18);
254 
255 	/* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
256 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA,
257 				     0x00, 0x0a, 0x15, 0x1b, 0x1e, 0x21, 0x24, 0x22, 0x47, 0x56,
258 				     0x65, 0x66, 0x6e, 0x82, 0x88, 0x8b, 0x9a, 0x9d, 0x98, 0xa8,
259 				     0xb9, 0x5d, 0x5c, 0x61, 0x66, 0x6a, 0x6f, 0x7f, 0x7f, 0x00,
260 				     0x0a, 0x15, 0x1b, 0x1e, 0x21, 0x24, 0x22, 0x47, 0x56, 0x65,
261 				     0x65, 0x6e, 0x81, 0x87, 0x8b, 0x98, 0x9d, 0x99, 0xa8, 0xba,
262 				     0x5d, 0x5d, 0x62, 0x67, 0x6b, 0x72, 0x7f, 0x7f);
263 
264 	/* Unknown command, not listed in the HX8394-F datasheet */
265 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN1,
266 				     0x1f, 0x31);
267 
268 	/* 5.19.17 SETPANEL (CCh) */
269 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL,
270 				     0x0b);
271 
272 	/* Unknown command, not listed in the HX8394-F datasheet */
273 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN3,
274 				     0x02);
275 
276 	/* 5.19.11 Set register bank (BDh) */
277 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
278 				     0x02);
279 
280 	/* Unknown command, not listed in the HX8394-F datasheet */
281 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4,
282 				     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
283 				     0xff, 0xff);
284 
285 	/* 5.19.11 Set register bank (BDh) */
286 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
287 				     0x00);
288 
289 	/* 5.19.11 Set register bank (BDh) */
290 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
291 				     0x01);
292 
293 	/* 5.19.2 SETPOWER: Set power (B1h) */
294 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
295 				     0x00);
296 
297 	/* 5.19.11 Set register bank (BDh) */
298 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
299 				     0x00);
300 
301 	/* Unknown command, not listed in the HX8394-F datasheet */
302 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN5,
303 				     0x40, 0x81, 0x50, 0x00, 0x1a, 0xfc, 0x01);
304 
305 	/* Unknown command, not listed in the HX8394-F datasheet */
306 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN2,
307 				     0xed);
308 }
309 
310 static const struct drm_display_mode powkiddy_x55_mode = {
311 	.hdisplay	= 720,
312 	.hsync_start	= 720 + 44,
313 	.hsync_end	= 720 + 44 + 20,
314 	.htotal		= 720 + 44 + 20 + 20,
315 	.vdisplay	= 1280,
316 	.vsync_start	= 1280 + 12,
317 	.vsync_end	= 1280 + 12 + 10,
318 	.vtotal		= 1280 + 12 + 10 + 10,
319 	.clock		= 63290,
320 	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
321 	.width_mm	= 67,
322 	.height_mm	= 121,
323 };
324 
325 static const struct hx8394_panel_desc powkiddy_x55_desc = {
326 	.mode = &powkiddy_x55_mode,
327 	.lanes = 4,
328 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
329 		      MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
330 	.format = MIPI_DSI_FMT_RGB888,
331 	.init_sequence = powkiddy_x55_init_sequence,
332 };
333 
334 static void mchp_ac40t08a_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
335 {
336 	/* DCS commands do not seem to be sent correclty without this delay */
337 	mipi_dsi_msleep(dsi_ctx, 20);
338 
339 	/* 5.19.8 SETEXTC: Set extension command (B9h) */
340 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC,
341 				     0xff, 0x83, 0x94);
342 
343 	/* 5.19.9 SETMIPI: Set MIPI control (BAh) */
344 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETMIPI,
345 				     0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
346 
347 	/* 5.19.2 SETPOWER: Set power (B1h) */
348 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
349 				     0x48, 0x12, 0x72, 0x09, 0x32, 0x54,
350 				     0x71, 0x71, 0x57, 0x47);
351 
352 	/* 5.19.3 SETDISP: Set display related register (B2h) */
353 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP,
354 				     0x00, 0x80, 0x64, 0x0c, 0x0d, 0x2f);
355 
356 	/* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
357 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC,
358 				     0x73, 0x74, 0x73, 0x74, 0x73, 0x74,
359 				     0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f,
360 				     0x73, 0x74, 0x73, 0x74, 0x73, 0x74,
361 				     0x01, 0x0c, 0x86);
362 
363 	/* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
364 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM,
365 				     0x6e, 0x6e);
366 
367 	/* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
368 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0,
369 				     0x00, 0x00, 0x07, 0x07, 0x40, 0x07,
370 				     0x0c, 0x00, 0x08, 0x10, 0x08, 0x00,
371 				     0x08, 0x54, 0x15, 0x0a, 0x05, 0x0a,
372 				     0x02, 0x15, 0x06, 0x05, 0x06, 0x47,
373 				     0x44, 0x0a, 0x0a, 0x4b, 0x10, 0x07,
374 				     0x07, 0x0c, 0x40);
375 
376 	/* 5.19.20 Set GIP Option1 (D5h) */
377 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1,
378 				     0x1c, 0x1c, 0x1d, 0x1d, 0x00, 0x01,
379 				     0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
380 				     0x08, 0x09, 0x0a, 0x0b, 0x24, 0x25,
381 				     0x18, 0x18, 0x26, 0x27, 0x18, 0x18,
382 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
383 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
384 				     0x18, 0x18, 0x20, 0x21, 0x18, 0x18,
385 				     0x18, 0x18);
386 
387 	/* 5.19.21 Set GIP Option2 (D6h) */
388 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2,
389 				     0x1c, 0x1c, 0x1d, 0x1d, 0x07, 0x06,
390 				     0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
391 				     0x0b, 0x0a, 0x09, 0x08, 0x21, 0x20,
392 				     0x18, 0x18, 0x27, 0x26, 0x18, 0x18,
393 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
394 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
395 				     0x18, 0x18, 0x25, 0x24, 0x18, 0x18,
396 				     0x18, 0x18);
397 
398 	/* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
399 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA,
400 				     0x00, 0x0a, 0x15, 0x1b, 0x1e, 0x21,
401 				     0x24, 0x22, 0x47, 0x56, 0x65, 0x66,
402 				     0x6e, 0x82, 0x88, 0x8b, 0x9a, 0x9d,
403 				     0x98, 0xa8, 0xb9, 0x5d, 0x5c, 0x61,
404 				     0x66, 0x6a, 0x6f, 0x7f, 0x7f, 0x00,
405 				     0x0a, 0x15, 0x1b, 0x1e, 0x21, 0x24,
406 				     0x22, 0x47, 0x56, 0x65, 0x65, 0x6e,
407 				     0x81, 0x87, 0x8b, 0x98, 0x9d, 0x99,
408 				     0xa8, 0xba, 0x5d, 0x5d, 0x62, 0x67,
409 				     0x6b, 0x72, 0x7f, 0x7f);
410 
411 	/* Unknown command, not listed in the HX8394-F datasheet (C0H) */
412 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN1,
413 				     0x1f, 0x73);
414 
415 	/* Set CABC control (C9h)*/
416 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCABC,
417 				     0x76, 0x00, 0x30);
418 
419 	/* 5.19.17 SETPANEL (CCh) */
420 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL,
421 				     0x0b);
422 
423 	/* Unknown command, not listed in the HX8394-F datasheet (D4h) */
424 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN3,
425 				     0x02);
426 
427 	/* 5.19.11 Set register bank (BDh) */
428 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
429 				     0x02);
430 
431 	/* 5.19.11 Set register bank (D8h) */
432 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4,
433 				     0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
434 				     0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
435 
436 	/* 5.19.11 Set register bank (BDh) */
437 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
438 				     0x00);
439 
440 	/* 5.19.11 Set register bank (BDh) */
441 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
442 				     0x01);
443 
444 	/* 5.19.2 SETPOWER: Set power (B1h) */
445 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
446 				     0x00);
447 
448 	/* 5.19.11 Set register bank (BDh) */
449 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
450 				     0x00);
451 
452 	/* Unknown command, not listed in the HX8394-F datasheet (C6h) */
453 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN2,
454 				     0xed);
455 }
456 
457 static const struct drm_display_mode mchp_ac40t08a_mode = {
458 	.hdisplay    = 720,
459 	.hsync_start = 720 + 12,
460 	.hsync_end   = 720 + 12 + 24,
461 	.htotal	     = 720 + 12 + 12 + 24,
462 	.vdisplay    = 1280,
463 	.vsync_start = 1280 + 13,
464 	.vsync_end   = 1280 + 14,
465 	.vtotal	     = 1280 + 14 + 13,
466 	.clock	     = 60226,
467 	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
468 	.width_mm    = 76,
469 	.height_mm   = 132,
470 };
471 
472 static const struct hx8394_panel_desc mchp_ac40t08a_desc = {
473 	.mode = &mchp_ac40t08a_mode,
474 	.lanes = 4,
475 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
476 	.format = MIPI_DSI_FMT_RGB888,
477 	.init_sequence = mchp_ac40t08a_init_sequence,
478 };
479 
480 /*
481  * HL055FHAV028C is based on Himax HX8399, so datasheet pages are
482  * slightly different than HX8394 based panels.
483  */
484 static void hl055fhav028c_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
485 {
486 	/* 6.3.6 SETEXTC: Set extension command (B9h) */
487 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC,
488 				     0xff, 0x83, 0x99);
489 
490 	/* 6.3.17 SETOFFSET: Set offset voltage (D2h) */
491 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETOFFSET,
492 				     0x77);
493 
494 	/* 6.3.1 SETPOWER: Set power (B1h) */
495 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
496 				     0x02, 0x04, 0x74, 0x94, 0x01, 0x32,
497 				     0x33, 0x11, 0x11, 0xab, 0x4d, 0x56,
498 				     0x73, 0x02, 0x02);
499 
500 	/* 6.3.2 SETDISP: Set display related register (B2h) */
501 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP,
502 				     0x00, 0x80, 0x80, 0xae, 0x05, 0x07,
503 				     0x5a, 0x11, 0x00, 0x00, 0x10, 0x1e,
504 				     0x70, 0x03, 0xd4);
505 
506 	/* 6.3.3 SETCYC: Set display waveform cycles (B4h) */
507 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC,
508 				     0x00, 0xff, 0x02, 0xc0, 0x02, 0xc0,
509 				     0x00, 0x00, 0x08, 0x00, 0x04, 0x06,
510 				     0x00, 0x32, 0x04, 0x0a, 0x08, 0x21,
511 				     0x03, 0x01, 0x00, 0x0f, 0xb8, 0x8b,
512 				     0x02, 0xc0, 0x02, 0xc0, 0x00, 0x00,
513 				     0x08, 0x00, 0x04, 0x06, 0x00, 0x32,
514 				     0x04, 0x0a, 0x08, 0x01, 0x00, 0x0f,
515 				     0xb8, 0x01);
516 
517 	/* 6.3.18 SETGIP0: Set GIP Option0 (D3h) */
518 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0,
519 				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
520 				     0x06, 0x00, 0x00, 0x10, 0x04, 0x00,
521 				     0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
522 				     0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
523 				     0x00, 0x05, 0x05, 0x07, 0x00, 0x00,
524 				     0x00, 0x05, 0x40);
525 
526 	/* 6.3.19 Set GIP Option1 (D5h) */
527 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1,
528 				     0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
529 				     0x21, 0x20, 0x01, 0x00, 0x07, 0x06,
530 				     0x05, 0x04, 0x03, 0x02, 0x18, 0x18,
531 				     0x18, 0x18, 0x18, 0x18, 0x2f, 0x2f,
532 				     0x30, 0x30, 0x31, 0x31, 0x18, 0x18,
533 				     0x18, 0x18);
534 
535 	/* 6.3.20 Set GIP Option2 (D6h) */
536 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2,
537 				     0x18, 0x18, 0x19, 0x19, 0x40, 0x40,
538 				     0x20, 0x21, 0x02, 0x03, 0x04, 0x05,
539 				     0x06, 0x07, 0x00, 0x01, 0x40, 0x40,
540 				     0x40, 0x40, 0x40, 0x40, 0x2f, 0x2f,
541 				     0x30, 0x30, 0x31, 0x31, 0x40, 0x40,
542 				     0x40, 0x40);
543 
544 	/* 6.3.21 Set GIP Option3 (D8h) */
545 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4,
546 				     0xa2, 0xaa, 0x02, 0xa0, 0xa2, 0xa8,
547 				     0x02, 0xa0, 0xb0, 0x00, 0x00, 0x00,
548 				     0xb0, 0x00, 0x00, 0x00);
549 
550 	/* 6.3.9 Set register bank (BDh) */
551 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
552 				     0x01);
553 
554 	/* 6.3.21 Set GIP Option3 (D8h) */
555 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4,
556 				     0xb0, 0x00, 0x00, 0x00, 0xb0, 0x00,
557 				     0x00, 0x00, 0xe2, 0xaa, 0x03, 0xf0,
558 				     0xe2, 0xaa, 0x03, 0xf0);
559 
560 	/* 6.3.9 Set register bank (BDh) */
561 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
562 				     0x02);
563 
564 	/* 6.3.21 Set GIP Option3 (D8h) */
565 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4,
566 				     0xe2, 0xaa, 0x03, 0xf0, 0xe2, 0xaa,
567 				     0x03, 0xf0);
568 
569 	/* 6.3.9 Set register bank (BDh) */
570 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
571 				     0x00);
572 
573 	/* 6.3.4 SETVCOM: Set VCOM voltage (B6h) */
574 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM,
575 				     0x7a, 0x7a);
576 
577 	/* 6.3.26 SETGAMMA: Set gamma curve related setting (E0h) */
578 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA,
579 				     0x00, 0x18, 0x27, 0x24, 0x5a, 0x68,
580 				     0x79, 0x78, 0x81, 0x8a, 0x92, 0x99,
581 				     0x9e, 0xa7, 0xaf, 0xb4, 0xb9, 0xc3,
582 				     0xc7, 0xd1, 0xc6, 0xd4, 0xd5, 0x6c,
583 				     0x67, 0x71, 0x77, 0x00, 0x00, 0x18,
584 				     0x27, 0x24, 0x5a, 0x68, 0x79, 0x78,
585 				     0x81, 0x8a, 0x92, 0x99, 0x9e, 0xa7,
586 				     0xaf, 0xb4, 0xb9, 0xc3, 0xc7, 0xd1,
587 				     0xc6, 0xd4, 0xd5, 0x6c, 0x67, 0x77);
588 
589 	/* Unknown command, not listed in the HX8399-C datasheet (C6h) */
590 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN2,
591 				     0xff, 0xf9);
592 
593 	/* 6.3.16 SETPANEL (CCh) */
594 	mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL,
595 				     0x08);
596 }
597 
598 static const struct drm_display_mode hl055fhav028c_mode = {
599 	.hdisplay	= 1080,
600 	.hsync_start	= 1080 + 32,
601 	.hsync_end	= 1080 + 32 + 8,
602 	.htotal		= 1080 + 32 + 8 + 32,
603 	.vdisplay	= 1920,
604 	.vsync_start	= 1920 + 16,
605 	.vsync_end	= 1920 + 16 + 2,
606 	.vtotal		= 1920 + 16 + 2 + 14,
607 	.clock		= 134920,
608 	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
609 	.width_mm	= 70,
610 	.height_mm	= 127,
611 };
612 
613 static const struct hx8394_panel_desc hl055fhav028c_desc = {
614 	.mode = &hl055fhav028c_mode,
615 	.lanes = 4,
616 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
617 	.format = MIPI_DSI_FMT_RGB888,
618 	.init_sequence = hl055fhav028c_init_sequence,
619 };
620 
621 static int hx8394_enable(struct drm_panel *panel)
622 {
623 	struct hx8394 *ctx = panel_to_hx8394(panel);
624 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
625 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
626 	int ret;
627 
628 	ctx->desc->init_sequence(&dsi_ctx);
629 
630 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
631 
632 	if (dsi_ctx.accum_err)
633 		return dsi_ctx.accum_err;
634 	/* Panel is operational 120 msec after reset */
635 	msleep(120);
636 
637 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
638 	if (dsi_ctx.accum_err)
639 		goto sleep_in;
640 
641 	return 0;
642 
643 sleep_in:
644 	ret = dsi_ctx.accum_err;
645 	dsi_ctx.accum_err = 0;
646 
647 	/* This will probably fail, but let's try orderly power off anyway. */
648 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
649 	mipi_dsi_msleep(&dsi_ctx, 50);
650 
651 	return ret;
652 }
653 
654 static int hx8394_disable(struct drm_panel *panel)
655 {
656 	struct hx8394 *ctx = panel_to_hx8394(panel);
657 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
658 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
659 
660 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
661 	mipi_dsi_msleep(&dsi_ctx, 50); /* about 3 frames */
662 
663 	return dsi_ctx.accum_err;
664 }
665 
666 static int hx8394_unprepare(struct drm_panel *panel)
667 {
668 	struct hx8394 *ctx = panel_to_hx8394(panel);
669 
670 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
671 
672 	regulator_disable(ctx->iovcc);
673 	regulator_disable(ctx->vcc);
674 
675 	return 0;
676 }
677 
678 static int hx8394_prepare(struct drm_panel *panel)
679 {
680 	struct hx8394 *ctx = panel_to_hx8394(panel);
681 	int ret;
682 
683 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
684 
685 	ret = regulator_enable(ctx->vcc);
686 	if (ret) {
687 		dev_err(ctx->dev, "Failed to enable vcc supply: %d\n", ret);
688 		return ret;
689 	}
690 
691 	ret = regulator_enable(ctx->iovcc);
692 	if (ret) {
693 		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
694 		goto disable_vcc;
695 	}
696 
697 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
698 
699 	msleep(180);
700 
701 	return 0;
702 
703 disable_vcc:
704 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
705 	regulator_disable(ctx->vcc);
706 	return ret;
707 }
708 
709 static int hx8394_get_modes(struct drm_panel *panel,
710 			    struct drm_connector *connector)
711 {
712 	struct hx8394 *ctx = panel_to_hx8394(panel);
713 	struct drm_display_mode *mode;
714 
715 	mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
716 	if (!mode) {
717 		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
718 			ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
719 			drm_mode_vrefresh(ctx->desc->mode));
720 		return -ENOMEM;
721 	}
722 
723 	drm_mode_set_name(mode);
724 
725 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
726 	connector->display_info.width_mm = mode->width_mm;
727 	connector->display_info.height_mm = mode->height_mm;
728 	drm_mode_probed_add(connector, mode);
729 
730 	return 1;
731 }
732 
733 static enum drm_panel_orientation hx8394_get_orientation(struct drm_panel *panel)
734 {
735 	struct hx8394 *ctx = panel_to_hx8394(panel);
736 
737 	return ctx->orientation;
738 }
739 
740 static const struct drm_panel_funcs hx8394_drm_funcs = {
741 	.disable   = hx8394_disable,
742 	.unprepare = hx8394_unprepare,
743 	.prepare   = hx8394_prepare,
744 	.enable	   = hx8394_enable,
745 	.get_modes = hx8394_get_modes,
746 	.get_orientation = hx8394_get_orientation,
747 };
748 
749 static int hx8394_probe(struct mipi_dsi_device *dsi)
750 {
751 	struct device *dev = &dsi->dev;
752 	struct hx8394 *ctx;
753 	int ret;
754 
755 	ctx = devm_drm_panel_alloc(dev, struct hx8394, panel,
756 				   &hx8394_drm_funcs,
757 				   DRM_MODE_CONNECTOR_DSI);
758 	if (IS_ERR(ctx))
759 		return PTR_ERR(ctx);
760 
761 	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
762 	if (IS_ERR(ctx->reset_gpio))
763 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
764 				     "Failed to get reset gpio\n");
765 
766 	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
767 	if (ret < 0) {
768 		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
769 		return ret;
770 	}
771 
772 	mipi_dsi_set_drvdata(dsi, ctx);
773 
774 	ctx->dev = dev;
775 	ctx->desc = of_device_get_match_data(dev);
776 
777 	dsi->mode_flags = ctx->desc->mode_flags;
778 	dsi->format = ctx->desc->format;
779 	dsi->lanes = ctx->desc->lanes;
780 
781 	ctx->vcc = devm_regulator_get(dev, "vcc");
782 	if (IS_ERR(ctx->vcc))
783 		return dev_err_probe(dev, PTR_ERR(ctx->vcc),
784 				     "Failed to request vcc regulator\n");
785 
786 	ctx->iovcc = devm_regulator_get(dev, "iovcc");
787 	if (IS_ERR(ctx->iovcc))
788 		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
789 				     "Failed to request iovcc regulator\n");
790 
791 	ret = drm_panel_of_backlight(&ctx->panel);
792 	if (ret)
793 		return ret;
794 
795 	drm_panel_add(&ctx->panel);
796 
797 	ret = mipi_dsi_attach(dsi);
798 	if (ret < 0) {
799 		dev_err_probe(dev, ret, "mipi_dsi_attach failed\n");
800 		drm_panel_remove(&ctx->panel);
801 		return ret;
802 	}
803 
804 	dev_dbg(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
805 		ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
806 		drm_mode_vrefresh(ctx->desc->mode),
807 		mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
808 
809 	return 0;
810 }
811 
812 static void hx8394_remove(struct mipi_dsi_device *dsi)
813 {
814 	struct hx8394 *ctx = mipi_dsi_get_drvdata(dsi);
815 	int ret;
816 
817 	ret = mipi_dsi_detach(dsi);
818 	if (ret < 0)
819 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
820 
821 	drm_panel_remove(&ctx->panel);
822 }
823 
824 static const struct of_device_id hx8394_of_match[] = {
825 	{ .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc },
826 	{ .compatible = "huiling,hl055fhav028c", .data = &hl055fhav028c_desc },
827 	{ .compatible = "powkiddy,x55-panel", .data = &powkiddy_x55_desc },
828 	{ .compatible = "microchip,ac40t08a-mipi-panel", .data = &mchp_ac40t08a_desc },
829 	{ /* sentinel */ }
830 };
831 MODULE_DEVICE_TABLE(of, hx8394_of_match);
832 
833 static struct mipi_dsi_driver hx8394_driver = {
834 	.probe	= hx8394_probe,
835 	.remove = hx8394_remove,
836 	.driver = {
837 		.name = DRV_NAME,
838 		.of_match_table = hx8394_of_match,
839 	},
840 };
841 module_mipi_dsi_driver(hx8394_driver);
842 
843 MODULE_AUTHOR("Kamil Trzciński <ayufan@ayufan.eu>");
844 MODULE_DESCRIPTION("DRM driver for Himax HX8394 based MIPI DSI panels");
845 MODULE_LICENSE("GPL");
846