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	int (*init_sequence)(struct hx8394 *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 int hsd060bhw4_init_sequence(struct hx8394 *ctx)
92{
93	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
94
95	/* 5.19.8 SETEXTC: Set extension command (B9h) */
96	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETEXTC,
97			       0xff, 0x83, 0x94);
98
99	/* 5.19.2 SETPOWER: Set power (B1h) */
100	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER,
101			       0x48, 0x11, 0x71, 0x09, 0x32, 0x24, 0x71, 0x31, 0x55, 0x30);
102
103	/* 5.19.9 SETMIPI: Set MIPI control (BAh) */
104	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETMIPI,
105			       0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
106
107	/* 5.19.3 SETDISP: Set display related register (B2h) */
108	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETDISP,
109			       0x00, 0x80, 0x78, 0x0c, 0x07);
110
111	/* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
112	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETCYC,
113			       0x12, 0x63, 0x12, 0x63, 0x12, 0x63, 0x01, 0x0c, 0x7c, 0x55,
114			       0x00, 0x3f, 0x12, 0x6b, 0x12, 0x6b, 0x12, 0x6b, 0x01, 0x0c,
115			       0x7c);
116
117	/* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
118	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP0,
119			       0x00, 0x00, 0x00, 0x00, 0x3c, 0x1c, 0x00, 0x00, 0x32, 0x10,
120			       0x09, 0x00, 0x09, 0x32, 0x15, 0xad, 0x05, 0xad, 0x32, 0x00,
121			       0x00, 0x00, 0x00, 0x37, 0x03, 0x0b, 0x0b, 0x37, 0x00, 0x00,
122			       0x00, 0x0c, 0x40);
123
124	/* 5.19.20 Set GIP Option1 (D5h) */
125	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP1,
126			       0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b, 0x1a, 0x1a, 0x00, 0x01,
127			       0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x20, 0x21, 0x18, 0x18,
128			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
129			       0x24, 0x25, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
130			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
131
132	/* 5.19.21 Set GIP Option2 (D6h) */
133	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP2,
134			       0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b, 0x1a, 0x1a, 0x07, 0x06,
135			       0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x25, 0x24, 0x18, 0x18,
136			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
137			       0x21, 0x20, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
138			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
139
140	/* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
141	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGAMMA,
142			       0x00, 0x04, 0x0c, 0x12, 0x14, 0x18, 0x1a, 0x18, 0x31, 0x3f,
143			       0x4d, 0x4c, 0x54, 0x65, 0x6b, 0x70, 0x7f, 0x82, 0x7e, 0x8a,
144			       0x99, 0x4a, 0x48, 0x49, 0x4b, 0x4a, 0x4c, 0x4b, 0x7f, 0x00,
145			       0x04, 0x0c, 0x11, 0x13, 0x17, 0x1a, 0x18, 0x31,
146			       0x3f, 0x4d, 0x4c, 0x54, 0x65, 0x6b, 0x70, 0x7f,
147			       0x82, 0x7e, 0x8a, 0x99, 0x4a, 0x48, 0x49, 0x4b,
148			       0x4a, 0x4c, 0x4b, 0x7f);
149
150	/* 5.19.17 SETPANEL (CCh) */
151	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPANEL,
152			       0x0b);
153
154	/* Unknown command, not listed in the HX8394-F datasheet */
155	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN1,
156			       0x1f, 0x31);
157
158	/* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
159	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETVCOM,
160			       0x7d, 0x7d);
161
162	/* Unknown command, not listed in the HX8394-F datasheet */
163	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN3,
164			       0x02);
165
166	/* 5.19.11 Set register bank (BDh) */
167	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
168			       0x01);
169
170	/* 5.19.2 SETPOWER: Set power (B1h) */
171	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER,
172			       0x00);
173
174	/* 5.19.11 Set register bank (BDh) */
175	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
176			       0x00);
177
178	/* Unknown command, not listed in the HX8394-F datasheet */
179	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN3,
180			       0xed);
181
182	return 0;
183}
184
185static const struct drm_display_mode hsd060bhw4_mode = {
186	.hdisplay    = 720,
187	.hsync_start = 720 + 40,
188	.hsync_end   = 720 + 40 + 46,
189	.htotal	     = 720 + 40 + 46 + 40,
190	.vdisplay    = 1440,
191	.vsync_start = 1440 + 9,
192	.vsync_end   = 1440 + 9 + 7,
193	.vtotal	     = 1440 + 9 + 7 + 7,
194	.clock	     = 74250,
195	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
196	.width_mm    = 68,
197	.height_mm   = 136,
198};
199
200static const struct hx8394_panel_desc hsd060bhw4_desc = {
201	.mode = &hsd060bhw4_mode,
202	.lanes = 4,
203	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
204	.format = MIPI_DSI_FMT_RGB888,
205	.init_sequence = hsd060bhw4_init_sequence,
206};
207
208static int powkiddy_x55_init_sequence(struct hx8394 *ctx)
209{
210	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
211
212	/* 5.19.8 SETEXTC: Set extension command (B9h) */
213	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETEXTC,
214			       0xff, 0x83, 0x94);
215
216	/* 5.19.9 SETMIPI: Set MIPI control (BAh) */
217	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETMIPI,
218			       0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
219
220	/* 5.19.2 SETPOWER: Set power (B1h) */
221	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER,
222			       0x48, 0x12, 0x72, 0x09, 0x32, 0x54, 0x71, 0x71, 0x57, 0x47);
223
224	/* 5.19.3 SETDISP: Set display related register (B2h) */
225	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETDISP,
226			       0x00, 0x80, 0x64, 0x2c, 0x16, 0x2f);
227
228	/* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
229	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETCYC,
230			       0x73, 0x74, 0x73, 0x74, 0x73, 0x74, 0x01, 0x0c, 0x86, 0x75,
231			       0x00, 0x3f, 0x73, 0x74, 0x73, 0x74, 0x73, 0x74, 0x01, 0x0c,
232			       0x86);
233
234	/* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
235	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETVCOM,
236			       0x6e, 0x6e);
237
238	/* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
239	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP0,
240			       0x00, 0x00, 0x07, 0x07, 0x40, 0x07, 0x0c, 0x00, 0x08, 0x10,
241			       0x08, 0x00, 0x08, 0x54, 0x15, 0x0a, 0x05, 0x0a, 0x02, 0x15,
242			       0x06, 0x05, 0x06, 0x47, 0x44, 0x0a, 0x0a, 0x4b, 0x10, 0x07,
243			       0x07, 0x0c, 0x40);
244
245	/* 5.19.20 Set GIP Option1 (D5h) */
246	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP1,
247			       0x1c, 0x1c, 0x1d, 0x1d, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
248			       0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x24, 0x25, 0x18, 0x18,
249			       0x26, 0x27, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
250			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x20, 0x21,
251			       0x18, 0x18, 0x18, 0x18);
252
253	/* 5.19.21 Set GIP Option2 (D6h) */
254	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP2,
255			       0x1c, 0x1c, 0x1d, 0x1d, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
256			       0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08, 0x21, 0x20, 0x18, 0x18,
257			       0x27, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
258			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x25, 0x24,
259			       0x18, 0x18, 0x18, 0x18);
260
261	/* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
262	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGAMMA,
263			       0x00, 0x0a, 0x15, 0x1b, 0x1e, 0x21, 0x24, 0x22, 0x47, 0x56,
264			       0x65, 0x66, 0x6e, 0x82, 0x88, 0x8b, 0x9a, 0x9d, 0x98, 0xa8,
265			       0xb9, 0x5d, 0x5c, 0x61, 0x66, 0x6a, 0x6f, 0x7f, 0x7f, 0x00,
266			       0x0a, 0x15, 0x1b, 0x1e, 0x21, 0x24, 0x22, 0x47, 0x56, 0x65,
267			       0x65, 0x6e, 0x81, 0x87, 0x8b, 0x98, 0x9d, 0x99, 0xa8, 0xba,
268			       0x5d, 0x5d, 0x62, 0x67, 0x6b, 0x72, 0x7f, 0x7f);
269
270	/* Unknown command, not listed in the HX8394-F datasheet */
271	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN1,
272			       0x1f, 0x31);
273
274	/* 5.19.17 SETPANEL (CCh) */
275	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPANEL,
276			       0x0b);
277
278	/* Unknown command, not listed in the HX8394-F datasheet */
279	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN3,
280			       0x02);
281
282	/* 5.19.11 Set register bank (BDh) */
283	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
284			       0x02);
285
286	/* Unknown command, not listed in the HX8394-F datasheet */
287	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN4,
288			       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
289			       0xff, 0xff);
290
291	/* 5.19.11 Set register bank (BDh) */
292	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
293			       0x00);
294
295	/* 5.19.11 Set register bank (BDh) */
296	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
297			       0x01);
298
299	/* 5.19.2 SETPOWER: Set power (B1h) */
300	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER,
301			       0x00);
302
303	/* 5.19.11 Set register bank (BDh) */
304	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
305			       0x00);
306
307	/* Unknown command, not listed in the HX8394-F datasheet */
308	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN5,
309			       0x40, 0x81, 0x50, 0x00, 0x1a, 0xfc, 0x01);
310
311	/* Unknown command, not listed in the HX8394-F datasheet */
312	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN2,
313			       0xed);
314
315	return 0;
316}
317
318static const struct drm_display_mode powkiddy_x55_mode = {
319	.hdisplay	= 720,
320	.hsync_start	= 720 + 44,
321	.hsync_end	= 720 + 44 + 20,
322	.htotal		= 720 + 44 + 20 + 20,
323	.vdisplay	= 1280,
324	.vsync_start	= 1280 + 12,
325	.vsync_end	= 1280 + 12 + 10,
326	.vtotal		= 1280 + 12 + 10 + 10,
327	.clock		= 63290,
328	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
329	.width_mm	= 67,
330	.height_mm	= 121,
331};
332
333static const struct hx8394_panel_desc powkiddy_x55_desc = {
334	.mode = &powkiddy_x55_mode,
335	.lanes = 4,
336	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
337		      MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
338	.format = MIPI_DSI_FMT_RGB888,
339	.init_sequence = powkiddy_x55_init_sequence,
340};
341
342static int hx8394_enable(struct drm_panel *panel)
343{
344	struct hx8394 *ctx = panel_to_hx8394(panel);
345	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
346	int ret;
347
348	ret = ctx->desc->init_sequence(ctx);
349	if (ret) {
350		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
351		return ret;
352	}
353
354	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
355	if (ret) {
356		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
357		return ret;
358	}
359
360	/* Panel is operational 120 msec after reset */
361	msleep(120);
362
363	ret = mipi_dsi_dcs_set_display_on(dsi);
364	if (ret) {
365		dev_err(ctx->dev, "Failed to turn on the display: %d\n", ret);
366		goto sleep_in;
367	}
368
369	return 0;
370
371sleep_in:
372	/* This will probably fail, but let's try orderly power off anyway. */
373	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
374	if (!ret)
375		msleep(50);
376
377	return ret;
378}
379
380static int hx8394_disable(struct drm_panel *panel)
381{
382	struct hx8394 *ctx = panel_to_hx8394(panel);
383	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
384	int ret;
385
386	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
387	if (ret) {
388		dev_err(ctx->dev, "Failed to enter sleep mode: %d\n", ret);
389		return ret;
390	}
391
392	msleep(50); /* about 3 frames */
393
394	return 0;
395}
396
397static int hx8394_unprepare(struct drm_panel *panel)
398{
399	struct hx8394 *ctx = panel_to_hx8394(panel);
400
401	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
402
403	regulator_disable(ctx->iovcc);
404	regulator_disable(ctx->vcc);
405
406	return 0;
407}
408
409static int hx8394_prepare(struct drm_panel *panel)
410{
411	struct hx8394 *ctx = panel_to_hx8394(panel);
412	int ret;
413
414	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
415
416	ret = regulator_enable(ctx->vcc);
417	if (ret) {
418		dev_err(ctx->dev, "Failed to enable vcc supply: %d\n", ret);
419		return ret;
420	}
421
422	ret = regulator_enable(ctx->iovcc);
423	if (ret) {
424		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
425		goto disable_vcc;
426	}
427
428	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
429
430	msleep(180);
431
432	return 0;
433
434disable_vcc:
435	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
436	regulator_disable(ctx->vcc);
437	return ret;
438}
439
440static int hx8394_get_modes(struct drm_panel *panel,
441			    struct drm_connector *connector)
442{
443	struct hx8394 *ctx = panel_to_hx8394(panel);
444	struct drm_display_mode *mode;
445
446	mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
447	if (!mode) {
448		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
449			ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
450			drm_mode_vrefresh(ctx->desc->mode));
451		return -ENOMEM;
452	}
453
454	drm_mode_set_name(mode);
455
456	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
457	connector->display_info.width_mm = mode->width_mm;
458	connector->display_info.height_mm = mode->height_mm;
459	drm_mode_probed_add(connector, mode);
460
461	return 1;
462}
463
464static enum drm_panel_orientation hx8394_get_orientation(struct drm_panel *panel)
465{
466	struct hx8394 *ctx = panel_to_hx8394(panel);
467
468	return ctx->orientation;
469}
470
471static const struct drm_panel_funcs hx8394_drm_funcs = {
472	.disable   = hx8394_disable,
473	.unprepare = hx8394_unprepare,
474	.prepare   = hx8394_prepare,
475	.enable	   = hx8394_enable,
476	.get_modes = hx8394_get_modes,
477	.get_orientation = hx8394_get_orientation,
478};
479
480static int hx8394_probe(struct mipi_dsi_device *dsi)
481{
482	struct device *dev = &dsi->dev;
483	struct hx8394 *ctx;
484	int ret;
485
486	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
487	if (!ctx)
488		return -ENOMEM;
489
490	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
491	if (IS_ERR(ctx->reset_gpio))
492		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
493				     "Failed to get reset gpio\n");
494
495	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
496	if (ret < 0) {
497		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
498		return ret;
499	}
500
501	mipi_dsi_set_drvdata(dsi, ctx);
502
503	ctx->dev = dev;
504	ctx->desc = of_device_get_match_data(dev);
505
506	dsi->mode_flags = ctx->desc->mode_flags;
507	dsi->format = ctx->desc->format;
508	dsi->lanes = ctx->desc->lanes;
509
510	ctx->vcc = devm_regulator_get(dev, "vcc");
511	if (IS_ERR(ctx->vcc))
512		return dev_err_probe(dev, PTR_ERR(ctx->vcc),
513				     "Failed to request vcc regulator\n");
514
515	ctx->iovcc = devm_regulator_get(dev, "iovcc");
516	if (IS_ERR(ctx->iovcc))
517		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
518				     "Failed to request iovcc regulator\n");
519
520	drm_panel_init(&ctx->panel, dev, &hx8394_drm_funcs,
521		       DRM_MODE_CONNECTOR_DSI);
522
523	ret = drm_panel_of_backlight(&ctx->panel);
524	if (ret)
525		return ret;
526
527	drm_panel_add(&ctx->panel);
528
529	ret = mipi_dsi_attach(dsi);
530	if (ret < 0) {
531		dev_err_probe(dev, ret, "mipi_dsi_attach failed\n");
532		drm_panel_remove(&ctx->panel);
533		return ret;
534	}
535
536	dev_dbg(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
537		ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
538		drm_mode_vrefresh(ctx->desc->mode),
539		mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
540
541	return 0;
542}
543
544static void hx8394_remove(struct mipi_dsi_device *dsi)
545{
546	struct hx8394 *ctx = mipi_dsi_get_drvdata(dsi);
547	int ret;
548
549	ret = mipi_dsi_detach(dsi);
550	if (ret < 0)
551		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
552
553	drm_panel_remove(&ctx->panel);
554}
555
556static const struct of_device_id hx8394_of_match[] = {
557	{ .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc },
558	{ .compatible = "powkiddy,x55-panel", .data = &powkiddy_x55_desc },
559	{ /* sentinel */ }
560};
561MODULE_DEVICE_TABLE(of, hx8394_of_match);
562
563static struct mipi_dsi_driver hx8394_driver = {
564	.probe	= hx8394_probe,
565	.remove = hx8394_remove,
566	.driver = {
567		.name = DRV_NAME,
568		.of_match_table = hx8394_of_match,
569	},
570};
571module_mipi_dsi_driver(hx8394_driver);
572
573MODULE_AUTHOR("Kamil Trzci��ski <ayufan@ayufan.eu>");
574MODULE_DESCRIPTION("DRM driver for Himax HX8394 based MIPI DSI panels");
575MODULE_LICENSE("GPL");
576