Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
67struct 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
78struct 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
86static inline struct hx8394 *panel_to_hx8394(struct drm_panel *panel)
87{
88 return container_of(panel, struct hx8394, panel);
89}
90
91static 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
181static 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
196static 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
204static 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
310static 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
325static 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
334static 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
457static 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
472static 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 */
484static 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
598static 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
613static 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
621static 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
643sleep_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
654static 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
666static 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
678static 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
703disable_vcc:
704 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
705 regulator_disable(ctx->vcc);
706 return ret;
707}
708
709static 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
733static 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
740static 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
749static 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
812static 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
824static 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};
831MODULE_DEVICE_TABLE(of, hx8394_of_match);
832
833static 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};
841module_mipi_dsi_driver(hx8394_driver);
842
843MODULE_AUTHOR("Kamil Trzciński <ayufan@ayufan.eu>");
844MODULE_DESCRIPTION("DRM driver for Himax HX8394 based MIPI DSI panels");
845MODULE_LICENSE("GPL");