xref: /linux/drivers/gpu/drm/panel/panel-himax-hx83112a.c (revision 260f6f4fda93c8485c8037865c941b42b9cba5d2)
1654f26a0SLuca Weiss // SPDX-License-Identifier: GPL-2.0-only
2654f26a0SLuca Weiss /*
3654f26a0SLuca Weiss  * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
4654f26a0SLuca Weiss  * Copyright (c) 2024 Luca Weiss <luca.weiss@fairphone.com>
5654f26a0SLuca Weiss  */
6654f26a0SLuca Weiss 
7654f26a0SLuca Weiss #include <linux/delay.h>
8654f26a0SLuca Weiss #include <linux/gpio/consumer.h>
9654f26a0SLuca Weiss #include <linux/module.h>
10654f26a0SLuca Weiss #include <linux/of.h>
11654f26a0SLuca Weiss #include <linux/regulator/consumer.h>
12654f26a0SLuca Weiss 
13654f26a0SLuca Weiss #include <drm/drm_mipi_dsi.h>
14654f26a0SLuca Weiss #include <drm/drm_modes.h>
15654f26a0SLuca Weiss #include <drm/drm_panel.h>
16654f26a0SLuca Weiss #include <drm/drm_probe_helper.h>
17654f26a0SLuca Weiss 
18654f26a0SLuca Weiss /* Manufacturer specific DSI commands */
19654f26a0SLuca Weiss #define HX83112A_SETPOWER1	0xb1
20654f26a0SLuca Weiss #define HX83112A_SETDISP	0xb2
21654f26a0SLuca Weiss #define HX83112A_SETDRV		0xb4
22654f26a0SLuca Weiss #define HX83112A_SETEXTC	0xb9
23654f26a0SLuca Weiss #define HX83112A_SETBANK	0xbd
24654f26a0SLuca Weiss #define HX83112A_SETPTBA	0xbf
25654f26a0SLuca Weiss #define HX83112A_SETDGCLUT	0xc1
26654f26a0SLuca Weiss #define HX83112A_SETTCON	0xc7
27654f26a0SLuca Weiss #define HX83112A_SETCLOCK	0xcb
28654f26a0SLuca Weiss #define HX83112A_SETPANEL	0xcc
29654f26a0SLuca Weiss #define HX83112A_SETPOWER2	0xd2
30654f26a0SLuca Weiss #define HX83112A_SETGIP0	0xd3
31654f26a0SLuca Weiss #define HX83112A_SETGIP1	0xd5
32654f26a0SLuca Weiss #define HX83112A_SETGIP2	0xd6
33654f26a0SLuca Weiss #define HX83112A_SETGIP3	0xd8
34654f26a0SLuca Weiss #define HX83112A_SETTP1		0xe7
35654f26a0SLuca Weiss #define HX83112A_UNKNOWN1	0xe9
36654f26a0SLuca Weiss 
37654f26a0SLuca Weiss struct hx83112a_panel {
38654f26a0SLuca Weiss 	struct drm_panel panel;
39654f26a0SLuca Weiss 	struct mipi_dsi_device *dsi;
40654f26a0SLuca Weiss 	struct regulator_bulk_data supplies[3];
41654f26a0SLuca Weiss 	struct gpio_desc *reset_gpio;
42654f26a0SLuca Weiss };
43654f26a0SLuca Weiss 
to_hx83112a_panel(struct drm_panel * panel)44654f26a0SLuca Weiss static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel)
45654f26a0SLuca Weiss {
46654f26a0SLuca Weiss 	return container_of(panel, struct hx83112a_panel, panel);
47654f26a0SLuca Weiss }
48654f26a0SLuca Weiss 
hx83112a_reset(struct hx83112a_panel * ctx)49654f26a0SLuca Weiss static void hx83112a_reset(struct hx83112a_panel *ctx)
50654f26a0SLuca Weiss {
51654f26a0SLuca Weiss 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
52654f26a0SLuca Weiss 	msleep(20);
53654f26a0SLuca Weiss 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
54654f26a0SLuca Weiss 	msleep(20);
55654f26a0SLuca Weiss 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
56654f26a0SLuca Weiss 	msleep(50);
57654f26a0SLuca Weiss }
58654f26a0SLuca Weiss 
hx83112a_on(struct mipi_dsi_device * dsi)5932e5666bSTejas Vipin static int hx83112a_on(struct mipi_dsi_device *dsi)
60654f26a0SLuca Weiss {
6132e5666bSTejas Vipin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
62654f26a0SLuca Weiss 
63654f26a0SLuca Weiss 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
64654f26a0SLuca Weiss 
6532e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETEXTC, 0x83, 0x11, 0x2a);
6632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPOWER1,
67654f26a0SLuca Weiss 				     0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
6832e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDISP,
69654f26a0SLuca Weiss 				     0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
70654f26a0SLuca Weiss 				     0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
7132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDRV,
72654f26a0SLuca Weiss 				     0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
73654f26a0SLuca Weiss 				     0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
74654f26a0SLuca Weiss 				     0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
75654f26a0SLuca Weiss 				     0x12, 0x00, 0x29);
7632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
7732e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDRV,
78654f26a0SLuca Weiss 				     0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
79654f26a0SLuca Weiss 				     0x53);
8032e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
8132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x03);
8232e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT,
83654f26a0SLuca Weiss 				     0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
84654f26a0SLuca Weiss 				     0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
85654f26a0SLuca Weiss 				     0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
86654f26a0SLuca Weiss 				     0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
87654f26a0SLuca Weiss 				     0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
88654f26a0SLuca Weiss 				     0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
89654f26a0SLuca Weiss 				     0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
90654f26a0SLuca Weiss 				     0x40);
9132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
9232e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT,
93654f26a0SLuca Weiss 				     0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
94654f26a0SLuca Weiss 				     0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
95654f26a0SLuca Weiss 				     0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
96654f26a0SLuca Weiss 				     0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
97654f26a0SLuca Weiss 				     0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
98654f26a0SLuca Weiss 				     0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
99654f26a0SLuca Weiss 				     0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
100654f26a0SLuca Weiss 				     0x40);
10132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
10232e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT,
103654f26a0SLuca Weiss 				     0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
104654f26a0SLuca Weiss 				     0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
105654f26a0SLuca Weiss 				     0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
106654f26a0SLuca Weiss 				     0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
107654f26a0SLuca Weiss 				     0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
108654f26a0SLuca Weiss 				     0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
109654f26a0SLuca Weiss 				     0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
110654f26a0SLuca Weiss 				     0x40);
11132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
11232e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT, 0x01);
11332e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTCON,
114654f26a0SLuca Weiss 				     0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
11532e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPANEL, 0x08);
11632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPOWER2, 0x2b, 0x2b);
11732e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP0,
118654f26a0SLuca Weiss 				     0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
119654f26a0SLuca Weiss 				     0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
120654f26a0SLuca Weiss 				     0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
121654f26a0SLuca Weiss 				     0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
122654f26a0SLuca Weiss 				     0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
123654f26a0SLuca Weiss 				     0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
124654f26a0SLuca Weiss 				     0x0f);
12532e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
12632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP0,
127654f26a0SLuca Weiss 				     0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
12832e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
12932e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP1,
130654f26a0SLuca Weiss 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
131654f26a0SLuca Weiss 				     0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
132654f26a0SLuca Weiss 				     0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
133654f26a0SLuca Weiss 				     0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
134654f26a0SLuca Weiss 				     0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
135654f26a0SLuca Weiss 				     0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
13632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP2,
137654f26a0SLuca Weiss 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
138654f26a0SLuca Weiss 				     0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
139654f26a0SLuca Weiss 				     0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
140654f26a0SLuca Weiss 				     0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
141654f26a0SLuca Weiss 				     0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
142654f26a0SLuca Weiss 				     0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
14332e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
144654f26a0SLuca Weiss 				     0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
145654f26a0SLuca Weiss 				     0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
146654f26a0SLuca Weiss 				     0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
14732e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
14832e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
149654f26a0SLuca Weiss 				     0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
150654f26a0SLuca Weiss 				     0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
151654f26a0SLuca Weiss 				     0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
15232e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
15332e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
154654f26a0SLuca Weiss 				     0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
155654f26a0SLuca Weiss 				     0xff, 0xff, 0xff, 0xff);
15632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x03);
15732e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
158654f26a0SLuca Weiss 				     0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
159654f26a0SLuca Weiss 				     0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
160654f26a0SLuca Weiss 				     0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
16132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
16232e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1,
163654f26a0SLuca Weiss 				     0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
164654f26a0SLuca Weiss 				     0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
165654f26a0SLuca Weiss 				     0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
16632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
16732e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1,
168654f26a0SLuca Weiss 				     0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
16932e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
17032e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1,
171654f26a0SLuca Weiss 				     0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
172654f26a0SLuca Weiss 				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173654f26a0SLuca Weiss 				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
174654f26a0SLuca Weiss 				     0x00, 0x00, 0x00, 0x02, 0x00);
17532e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
17632e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0xc3);
17732e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETCLOCK, 0xd1, 0xd6);
17832e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0x3f);
17932e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0xc6);
18032e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPTBA, 0x37);
18132e5666bSTejas Vipin 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0x3f);
182654f26a0SLuca Weiss 
18332e5666bSTejas Vipin 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
18432e5666bSTejas Vipin 	mipi_dsi_msleep(&dsi_ctx, 150);
185654f26a0SLuca Weiss 
18632e5666bSTejas Vipin 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
18732e5666bSTejas Vipin 	mipi_dsi_msleep(&dsi_ctx, 50);
188654f26a0SLuca Weiss 
18932e5666bSTejas Vipin 	return dsi_ctx.accum_err;
190654f26a0SLuca Weiss }
191654f26a0SLuca Weiss 
hx83112a_disable(struct drm_panel * panel)192654f26a0SLuca Weiss static int hx83112a_disable(struct drm_panel *panel)
193654f26a0SLuca Weiss {
194654f26a0SLuca Weiss 	struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
195654f26a0SLuca Weiss 	struct mipi_dsi_device *dsi = ctx->dsi;
19632e5666bSTejas Vipin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
197654f26a0SLuca Weiss 
198654f26a0SLuca Weiss 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
199654f26a0SLuca Weiss 
20032e5666bSTejas Vipin 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
20132e5666bSTejas Vipin 	mipi_dsi_msleep(&dsi_ctx, 20);
20232e5666bSTejas Vipin 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
20332e5666bSTejas Vipin 	mipi_dsi_msleep(&dsi_ctx, 120);
204654f26a0SLuca Weiss 
20532e5666bSTejas Vipin 	return dsi_ctx.accum_err;
206654f26a0SLuca Weiss }
207654f26a0SLuca Weiss 
hx83112a_prepare(struct drm_panel * panel)208654f26a0SLuca Weiss static int hx83112a_prepare(struct drm_panel *panel)
209654f26a0SLuca Weiss {
210654f26a0SLuca Weiss 	struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
211654f26a0SLuca Weiss 	int ret;
212654f26a0SLuca Weiss 
213654f26a0SLuca Weiss 	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
21432e5666bSTejas Vipin 	if (ret < 0)
215654f26a0SLuca Weiss 		return ret;
216654f26a0SLuca Weiss 
217654f26a0SLuca Weiss 	hx83112a_reset(ctx);
218654f26a0SLuca Weiss 
21932e5666bSTejas Vipin 	ret = hx83112a_on(ctx->dsi);
220654f26a0SLuca Weiss 	if (ret < 0) {
221654f26a0SLuca Weiss 		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
222654f26a0SLuca Weiss 		regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
223654f26a0SLuca Weiss 	}
224654f26a0SLuca Weiss 
22532e5666bSTejas Vipin 	return ret;
226654f26a0SLuca Weiss }
227654f26a0SLuca Weiss 
hx83112a_unprepare(struct drm_panel * panel)228654f26a0SLuca Weiss static int hx83112a_unprepare(struct drm_panel *panel)
229654f26a0SLuca Weiss {
230654f26a0SLuca Weiss 	struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
231654f26a0SLuca Weiss 
232654f26a0SLuca Weiss 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
233654f26a0SLuca Weiss 	regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
234654f26a0SLuca Weiss 
235654f26a0SLuca Weiss 	return 0;
236654f26a0SLuca Weiss }
237654f26a0SLuca Weiss 
238654f26a0SLuca Weiss static const struct drm_display_mode hx83112a_mode = {
239654f26a0SLuca Weiss 	.clock = (1080 + 28 + 8 + 8) * (2340 + 27 + 5 + 5) * 60 / 1000,
240654f26a0SLuca Weiss 	.hdisplay = 1080,
241654f26a0SLuca Weiss 	.hsync_start = 1080 + 28,
242654f26a0SLuca Weiss 	.hsync_end = 1080 + 28 + 8,
243654f26a0SLuca Weiss 	.htotal = 1080 + 28 + 8 + 8,
244654f26a0SLuca Weiss 	.vdisplay = 2340,
245654f26a0SLuca Weiss 	.vsync_start = 2340 + 27,
246654f26a0SLuca Weiss 	.vsync_end = 2340 + 27 + 5,
247654f26a0SLuca Weiss 	.vtotal = 2340 + 27 + 5 + 5,
248654f26a0SLuca Weiss 	.width_mm = 67,
249654f26a0SLuca Weiss 	.height_mm = 145,
250654f26a0SLuca Weiss 	.type = DRM_MODE_TYPE_DRIVER,
251654f26a0SLuca Weiss };
252654f26a0SLuca Weiss 
hx83112a_get_modes(struct drm_panel * panel,struct drm_connector * connector)253654f26a0SLuca Weiss static int hx83112a_get_modes(struct drm_panel *panel,
254654f26a0SLuca Weiss 				  struct drm_connector *connector)
255654f26a0SLuca Weiss {
256654f26a0SLuca Weiss 	return drm_connector_helper_get_modes_fixed(connector, &hx83112a_mode);
257654f26a0SLuca Weiss }
258654f26a0SLuca Weiss 
259654f26a0SLuca Weiss static const struct drm_panel_funcs hx83112a_panel_funcs = {
260654f26a0SLuca Weiss 	.prepare = hx83112a_prepare,
261654f26a0SLuca Weiss 	.unprepare = hx83112a_unprepare,
262654f26a0SLuca Weiss 	.disable = hx83112a_disable,
263654f26a0SLuca Weiss 	.get_modes = hx83112a_get_modes,
264654f26a0SLuca Weiss };
265654f26a0SLuca Weiss 
hx83112a_probe(struct mipi_dsi_device * dsi)266654f26a0SLuca Weiss static int hx83112a_probe(struct mipi_dsi_device *dsi)
267654f26a0SLuca Weiss {
268654f26a0SLuca Weiss 	struct device *dev = &dsi->dev;
269654f26a0SLuca Weiss 	struct hx83112a_panel *ctx;
270654f26a0SLuca Weiss 	int ret;
271654f26a0SLuca Weiss 
272*7b8c3296SAnusha Srivatsa 	ctx = devm_drm_panel_alloc(dev, struct hx83112a_panel, panel,
273*7b8c3296SAnusha Srivatsa 				   &hx83112a_panel_funcs,
274*7b8c3296SAnusha Srivatsa 				   DRM_MODE_CONNECTOR_DSI);
275*7b8c3296SAnusha Srivatsa 	if (IS_ERR(ctx))
276*7b8c3296SAnusha Srivatsa 		return PTR_ERR(ctx);
277654f26a0SLuca Weiss 
278654f26a0SLuca Weiss 	ctx->supplies[0].supply = "vdd1";
279654f26a0SLuca Weiss 	ctx->supplies[1].supply = "vsn";
280654f26a0SLuca Weiss 	ctx->supplies[2].supply = "vsp";
281654f26a0SLuca Weiss 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
282654f26a0SLuca Weiss 				      ctx->supplies);
283654f26a0SLuca Weiss 	if (ret < 0)
284654f26a0SLuca Weiss 		return dev_err_probe(dev, ret, "Failed to get regulators\n");
285654f26a0SLuca Weiss 
286654f26a0SLuca Weiss 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
287654f26a0SLuca Weiss 	if (IS_ERR(ctx->reset_gpio))
288654f26a0SLuca Weiss 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
289654f26a0SLuca Weiss 				     "Failed to get reset-gpios\n");
290654f26a0SLuca Weiss 
291654f26a0SLuca Weiss 	ctx->dsi = dsi;
292654f26a0SLuca Weiss 	mipi_dsi_set_drvdata(dsi, ctx);
293654f26a0SLuca Weiss 
294654f26a0SLuca Weiss 	dsi->lanes = 4;
295654f26a0SLuca Weiss 	dsi->format = MIPI_DSI_FMT_RGB888;
296654f26a0SLuca Weiss 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
297654f26a0SLuca Weiss 			  MIPI_DSI_MODE_VIDEO_HSE |
298654f26a0SLuca Weiss 			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
299654f26a0SLuca Weiss 
300654f26a0SLuca Weiss 	ctx->panel.prepare_prev_first = true;
301654f26a0SLuca Weiss 
302654f26a0SLuca Weiss 	ret = drm_panel_of_backlight(&ctx->panel);
303654f26a0SLuca Weiss 	if (ret)
304654f26a0SLuca Weiss 		return dev_err_probe(dev, ret, "Failed to get backlight\n");
305654f26a0SLuca Weiss 
306654f26a0SLuca Weiss 	drm_panel_add(&ctx->panel);
307654f26a0SLuca Weiss 
308654f26a0SLuca Weiss 	ret = mipi_dsi_attach(dsi);
309654f26a0SLuca Weiss 	if (ret < 0) {
310654f26a0SLuca Weiss 		dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
311654f26a0SLuca Weiss 		drm_panel_remove(&ctx->panel);
312654f26a0SLuca Weiss 		return ret;
313654f26a0SLuca Weiss 	}
314654f26a0SLuca Weiss 
315654f26a0SLuca Weiss 	return 0;
316654f26a0SLuca Weiss }
317654f26a0SLuca Weiss 
hx83112a_remove(struct mipi_dsi_device * dsi)318654f26a0SLuca Weiss static void hx83112a_remove(struct mipi_dsi_device *dsi)
319654f26a0SLuca Weiss {
320654f26a0SLuca Weiss 	struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi);
321654f26a0SLuca Weiss 	int ret;
322654f26a0SLuca Weiss 
323654f26a0SLuca Weiss 	ret = mipi_dsi_detach(dsi);
324654f26a0SLuca Weiss 	if (ret < 0)
325654f26a0SLuca Weiss 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
326654f26a0SLuca Weiss 
327654f26a0SLuca Weiss 	drm_panel_remove(&ctx->panel);
328654f26a0SLuca Weiss }
329654f26a0SLuca Weiss 
330654f26a0SLuca Weiss static const struct of_device_id hx83112a_of_match[] = {
331654f26a0SLuca Weiss 	{ .compatible = "djn,9a-3r063-1102b" },
332654f26a0SLuca Weiss 	{ /* sentinel */ }
333654f26a0SLuca Weiss };
334654f26a0SLuca Weiss MODULE_DEVICE_TABLE(of, hx83112a_of_match);
335654f26a0SLuca Weiss 
336654f26a0SLuca Weiss static struct mipi_dsi_driver hx83112a_driver = {
337654f26a0SLuca Weiss 	.probe = hx83112a_probe,
338654f26a0SLuca Weiss 	.remove = hx83112a_remove,
339654f26a0SLuca Weiss 	.driver = {
340654f26a0SLuca Weiss 		.name = "panel-himax-hx83112a",
341654f26a0SLuca Weiss 		.of_match_table = hx83112a_of_match,
342654f26a0SLuca Weiss 	},
343654f26a0SLuca Weiss };
344654f26a0SLuca Weiss module_mipi_dsi_driver(hx83112a_driver);
345654f26a0SLuca Weiss 
346654f26a0SLuca Weiss MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels");
347654f26a0SLuca Weiss MODULE_LICENSE("GPL");
348