Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

Need help to get security updates for your Linux BSP?
Loading...
Note: File does not exist in v5.4.
  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 mchp_ac40t08a_init_sequence(struct hx8394 *ctx)
343{
344	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
345
346	/* DCS commands do not seem to be sent correclty without this delay */
347	msleep(20);
348
349	/* 5.19.8 SETEXTC: Set extension command (B9h) */
350	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETEXTC,
351			       0xff, 0x83, 0x94);
352
353	/* 5.19.9 SETMIPI: Set MIPI control (BAh) */
354	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETMIPI,
355			       0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
356
357	/* 5.19.2 SETPOWER: Set power (B1h) */
358	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER,
359			       0x48, 0x12, 0x72, 0x09, 0x32, 0x54,
360			       0x71, 0x71, 0x57, 0x47);
361
362	/* 5.19.3 SETDISP: Set display related register (B2h) */
363	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETDISP,
364			       0x00, 0x80, 0x64, 0x0c, 0x0d, 0x2f);
365
366	/* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
367	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETCYC,
368			       0x73, 0x74, 0x73, 0x74, 0x73, 0x74,
369			       0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f,
370			       0x73, 0x74, 0x73, 0x74, 0x73, 0x74,
371			       0x01, 0x0c, 0x86);
372
373	/* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
374	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETVCOM,
375			       0x6e, 0x6e);
376
377	/* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
378	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP0,
379			       0x00, 0x00, 0x07, 0x07, 0x40, 0x07,
380			       0x0c, 0x00, 0x08, 0x10, 0x08, 0x00,
381			       0x08, 0x54, 0x15, 0x0a, 0x05, 0x0a,
382			       0x02, 0x15, 0x06, 0x05, 0x06, 0x47,
383			       0x44, 0x0a, 0x0a, 0x4b, 0x10, 0x07,
384			       0x07, 0x0c, 0x40);
385
386	/* 5.19.20 Set GIP Option1 (D5h) */
387	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP1,
388			       0x1c, 0x1c, 0x1d, 0x1d, 0x00, 0x01,
389			       0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
390			       0x08, 0x09, 0x0a, 0x0b, 0x24, 0x25,
391			       0x18, 0x18, 0x26, 0x27, 0x18, 0x18,
392			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
393			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
394			       0x18, 0x18, 0x20, 0x21, 0x18, 0x18,
395			       0x18, 0x18);
396
397	/* 5.19.21 Set GIP Option2 (D6h) */
398	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP2,
399			       0x1c, 0x1c, 0x1d, 0x1d, 0x07, 0x06,
400			       0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
401			       0x0b, 0x0a, 0x09, 0x08, 0x21, 0x20,
402			       0x18, 0x18, 0x27, 0x26, 0x18, 0x18,
403			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
404			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
405			       0x18, 0x18, 0x25, 0x24, 0x18, 0x18,
406			       0x18, 0x18);
407
408	/* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
409	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGAMMA,
410			       0x00, 0x0a, 0x15, 0x1b, 0x1e, 0x21,
411			       0x24, 0x22, 0x47, 0x56, 0x65, 0x66,
412			       0x6e, 0x82, 0x88, 0x8b, 0x9a, 0x9d,
413			       0x98, 0xa8, 0xb9, 0x5d, 0x5c, 0x61,
414			       0x66, 0x6a, 0x6f, 0x7f, 0x7f, 0x00,
415			       0x0a, 0x15, 0x1b, 0x1e, 0x21, 0x24,
416			       0x22, 0x47, 0x56, 0x65, 0x65, 0x6e,
417			       0x81, 0x87, 0x8b, 0x98, 0x9d, 0x99,
418			       0xa8, 0xba, 0x5d, 0x5d, 0x62, 0x67,
419			       0x6b, 0x72, 0x7f, 0x7f);
420
421	/* Unknown command, not listed in the HX8394-F datasheet (C0H) */
422	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN1,
423			       0x1f, 0x73);
424
425	/* Set CABC control (C9h)*/
426	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETCABC,
427			       0x76, 0x00, 0x30);
428
429	/* 5.19.17 SETPANEL (CCh) */
430	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPANEL,
431			       0x0b);
432
433	/* Unknown command, not listed in the HX8394-F datasheet (D4h) */
434	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN3,
435			       0x02);
436
437	/* 5.19.11 Set register bank (BDh) */
438	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
439			       0x02);
440
441	/* 5.19.11 Set register bank (D8h) */
442	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN4,
443			       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
444			       0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
445
446	/* 5.19.11 Set register bank (BDh) */
447	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
448			       0x00);
449
450	/* 5.19.11 Set register bank (BDh) */
451	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
452			       0x01);
453
454	/* 5.19.2 SETPOWER: Set power (B1h) */
455	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER,
456			       0x00);
457
458	/* 5.19.11 Set register bank (BDh) */
459	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK,
460			       0x00);
461
462	/* Unknown command, not listed in the HX8394-F datasheet (C6h) */
463	mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN2,
464			       0xed);
465
466	return 0;
467}
468
469static const struct drm_display_mode mchp_ac40t08a_mode = {
470	.hdisplay    = 720,
471	.hsync_start = 720 + 12,
472	.hsync_end   = 720 + 12 + 24,
473	.htotal	     = 720 + 12 + 12 + 24,
474	.vdisplay    = 1280,
475	.vsync_start = 1280 + 13,
476	.vsync_end   = 1280 + 14,
477	.vtotal	     = 1280 + 14 + 13,
478	.clock	     = 60226,
479	.flags	     = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
480	.width_mm    = 76,
481	.height_mm   = 132,
482};
483
484static const struct hx8394_panel_desc mchp_ac40t08a_desc = {
485	.mode = &mchp_ac40t08a_mode,
486	.lanes = 4,
487	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
488	.format = MIPI_DSI_FMT_RGB888,
489	.init_sequence = mchp_ac40t08a_init_sequence,
490};
491
492static int hx8394_enable(struct drm_panel *panel)
493{
494	struct hx8394 *ctx = panel_to_hx8394(panel);
495	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
496	int ret;
497
498	ret = ctx->desc->init_sequence(ctx);
499	if (ret) {
500		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
501		return ret;
502	}
503
504	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
505	if (ret) {
506		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
507		return ret;
508	}
509
510	/* Panel is operational 120 msec after reset */
511	msleep(120);
512
513	ret = mipi_dsi_dcs_set_display_on(dsi);
514	if (ret) {
515		dev_err(ctx->dev, "Failed to turn on the display: %d\n", ret);
516		goto sleep_in;
517	}
518
519	return 0;
520
521sleep_in:
522	/* This will probably fail, but let's try orderly power off anyway. */
523	if (!mipi_dsi_dcs_enter_sleep_mode(dsi))
524		msleep(50);
525
526	return ret;
527}
528
529static int hx8394_disable(struct drm_panel *panel)
530{
531	struct hx8394 *ctx = panel_to_hx8394(panel);
532	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
533	int ret;
534
535	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
536	if (ret) {
537		dev_err(ctx->dev, "Failed to enter sleep mode: %d\n", ret);
538		return ret;
539	}
540
541	msleep(50); /* about 3 frames */
542
543	return 0;
544}
545
546static int hx8394_unprepare(struct drm_panel *panel)
547{
548	struct hx8394 *ctx = panel_to_hx8394(panel);
549
550	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
551
552	regulator_disable(ctx->iovcc);
553	regulator_disable(ctx->vcc);
554
555	return 0;
556}
557
558static int hx8394_prepare(struct drm_panel *panel)
559{
560	struct hx8394 *ctx = panel_to_hx8394(panel);
561	int ret;
562
563	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
564
565	ret = regulator_enable(ctx->vcc);
566	if (ret) {
567		dev_err(ctx->dev, "Failed to enable vcc supply: %d\n", ret);
568		return ret;
569	}
570
571	ret = regulator_enable(ctx->iovcc);
572	if (ret) {
573		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
574		goto disable_vcc;
575	}
576
577	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
578
579	msleep(180);
580
581	return 0;
582
583disable_vcc:
584	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
585	regulator_disable(ctx->vcc);
586	return ret;
587}
588
589static int hx8394_get_modes(struct drm_panel *panel,
590			    struct drm_connector *connector)
591{
592	struct hx8394 *ctx = panel_to_hx8394(panel);
593	struct drm_display_mode *mode;
594
595	mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
596	if (!mode) {
597		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
598			ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
599			drm_mode_vrefresh(ctx->desc->mode));
600		return -ENOMEM;
601	}
602
603	drm_mode_set_name(mode);
604
605	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
606	connector->display_info.width_mm = mode->width_mm;
607	connector->display_info.height_mm = mode->height_mm;
608	drm_mode_probed_add(connector, mode);
609
610	return 1;
611}
612
613static enum drm_panel_orientation hx8394_get_orientation(struct drm_panel *panel)
614{
615	struct hx8394 *ctx = panel_to_hx8394(panel);
616
617	return ctx->orientation;
618}
619
620static const struct drm_panel_funcs hx8394_drm_funcs = {
621	.disable   = hx8394_disable,
622	.unprepare = hx8394_unprepare,
623	.prepare   = hx8394_prepare,
624	.enable	   = hx8394_enable,
625	.get_modes = hx8394_get_modes,
626	.get_orientation = hx8394_get_orientation,
627};
628
629static int hx8394_probe(struct mipi_dsi_device *dsi)
630{
631	struct device *dev = &dsi->dev;
632	struct hx8394 *ctx;
633	int ret;
634
635	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
636	if (!ctx)
637		return -ENOMEM;
638
639	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
640	if (IS_ERR(ctx->reset_gpio))
641		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
642				     "Failed to get reset gpio\n");
643
644	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
645	if (ret < 0) {
646		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
647		return ret;
648	}
649
650	mipi_dsi_set_drvdata(dsi, ctx);
651
652	ctx->dev = dev;
653	ctx->desc = of_device_get_match_data(dev);
654
655	dsi->mode_flags = ctx->desc->mode_flags;
656	dsi->format = ctx->desc->format;
657	dsi->lanes = ctx->desc->lanes;
658
659	ctx->vcc = devm_regulator_get(dev, "vcc");
660	if (IS_ERR(ctx->vcc))
661		return dev_err_probe(dev, PTR_ERR(ctx->vcc),
662				     "Failed to request vcc regulator\n");
663
664	ctx->iovcc = devm_regulator_get(dev, "iovcc");
665	if (IS_ERR(ctx->iovcc))
666		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
667				     "Failed to request iovcc regulator\n");
668
669	drm_panel_init(&ctx->panel, dev, &hx8394_drm_funcs,
670		       DRM_MODE_CONNECTOR_DSI);
671
672	ret = drm_panel_of_backlight(&ctx->panel);
673	if (ret)
674		return ret;
675
676	drm_panel_add(&ctx->panel);
677
678	ret = mipi_dsi_attach(dsi);
679	if (ret < 0) {
680		dev_err_probe(dev, ret, "mipi_dsi_attach failed\n");
681		drm_panel_remove(&ctx->panel);
682		return ret;
683	}
684
685	dev_dbg(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
686		ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
687		drm_mode_vrefresh(ctx->desc->mode),
688		mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
689
690	return 0;
691}
692
693static void hx8394_remove(struct mipi_dsi_device *dsi)
694{
695	struct hx8394 *ctx = mipi_dsi_get_drvdata(dsi);
696	int ret;
697
698	ret = mipi_dsi_detach(dsi);
699	if (ret < 0)
700		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
701
702	drm_panel_remove(&ctx->panel);
703}
704
705static const struct of_device_id hx8394_of_match[] = {
706	{ .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc },
707	{ .compatible = "powkiddy,x55-panel", .data = &powkiddy_x55_desc },
708	{ .compatible = "microchip,ac40t08a-mipi-panel", .data = &mchp_ac40t08a_desc },
709	{ /* sentinel */ }
710};
711MODULE_DEVICE_TABLE(of, hx8394_of_match);
712
713static struct mipi_dsi_driver hx8394_driver = {
714	.probe	= hx8394_probe,
715	.remove = hx8394_remove,
716	.driver = {
717		.name = DRV_NAME,
718		.of_match_table = hx8394_of_match,
719	},
720};
721module_mipi_dsi_driver(hx8394_driver);
722
723MODULE_AUTHOR("Kamil Trzciński <ayufan@ayufan.eu>");
724MODULE_DESCRIPTION("DRM driver for Himax HX8394 based MIPI DSI panels");
725MODULE_LICENSE("GPL");