xref: /linux/drivers/gpu/drm/panel/panel-himax-hx83112b.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
4  * Copyright (c) 2025 Luca Weiss <luca@lucaweiss.eu>
5  */
6 
7 #include <linux/backlight.h>
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/module.h>
12 #include <linux/regulator/consumer.h>
13 
14 #include <video/mipi_display.h>
15 
16 #include <drm/drm_mipi_dsi.h>
17 #include <drm/drm_modes.h>
18 #include <drm/drm_panel.h>
19 #include <drm/drm_probe_helper.h>
20 
21 /* Manufacturer specific DSI commands */
22 #define HX83112B_SETPOWER1	0xb1
23 #define HX83112B_SETDISP	0xb2
24 #define HX83112B_SETDRV		0xb4
25 #define HX83112B_SETEXTC	0xb9
26 #define HX83112B_SETBANK	0xbd
27 #define HX83112B_SETDGCLUT	0xc1
28 #define HX83112B_SETDISMO	0xc2
29 #define HX83112B_UNKNOWN1	0xc6
30 #define HX83112B_SETPANEL	0xcc
31 #define HX83112B_UNKNOWN2	0xd1
32 #define HX83112B_SETPOWER2	0xd2
33 #define HX83112B_SETGIP0	0xd3
34 #define HX83112B_SETGIP1	0xd5
35 #define HX83112B_SETGIP2	0xd6
36 #define HX83112B_SETGIP3	0xd8
37 #define HX83112B_SETIDLE	0xdd
38 #define HX83112B_UNKNOWN3	0xe7
39 #define HX83112B_UNKNOWN4	0xe9
40 
41 struct hx83112b_panel {
42 	struct drm_panel panel;
43 	struct mipi_dsi_device *dsi;
44 	struct regulator_bulk_data *supplies;
45 	struct gpio_desc *reset_gpio;
46 };
47 
48 static const struct regulator_bulk_data hx83112b_supplies[] = {
49 	{ .supply = "iovcc" },
50 	{ .supply = "vsn" },
51 	{ .supply = "vsp" },
52 };
53 
54 static inline struct hx83112b_panel *to_hx83112b_panel(struct drm_panel *panel)
55 {
56 	return container_of(panel, struct hx83112b_panel, panel);
57 }
58 
59 static void hx83112b_reset(struct hx83112b_panel *ctx)
60 {
61 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
62 	usleep_range(10000, 11000);
63 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
64 	usleep_range(10000, 11000);
65 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
66 	usleep_range(10000, 11000);
67 }
68 
69 static int hx83112b_on(struct hx83112b_panel *ctx)
70 {
71 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
72 
73 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETEXTC, 0x83, 0x11, 0x2b);
74 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
75 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISMO, 0x08, 0x70);
76 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
77 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0x04, 0x38, 0x08, 0x70);
78 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
79 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPOWER1,
80 				     0xf8, 0x27, 0x27, 0x00, 0x00, 0x0b, 0x0e,
81 				     0x0b, 0x0e, 0x33);
82 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPOWER2, 0x2d, 0x2d);
83 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP,
84 				     0x80, 0x02, 0x18, 0x80, 0x70, 0x00, 0x08,
85 				     0x1c, 0x08, 0x11, 0x05);
86 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xd1);
87 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0x00, 0x08);
88 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
89 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
90 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0xb5, 0x0a);
91 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
92 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETIDLE,
93 				     0x00, 0x00, 0x08, 0x1c, 0x08, 0x34, 0x34,
94 				     0x88);
95 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDRV,
96 				     0x65, 0x6b, 0x00, 0x00, 0xd0, 0xd4, 0x36,
97 				     0xcf, 0x06, 0xce, 0x00, 0xce, 0x00, 0x00,
98 				     0x00, 0x07, 0x00, 0x2a, 0x07, 0x01, 0x07,
99 				     0x00, 0x00, 0x2a);
100 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
101 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc3);
102 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDRV, 0x01, 0x67, 0x2a);
103 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
104 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
105 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT, 0x01);
106 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
107 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT,
108 				     0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef,
109 				     0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda,
110 				     0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe,
111 				     0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85,
112 				     0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e,
113 				     0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07,
114 				     0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25,
115 				     0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b,
116 				     0x00);
117 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
118 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT,
119 				     0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef,
120 				     0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda,
121 				     0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe,
122 				     0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85,
123 				     0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e,
124 				     0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07,
125 				     0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25,
126 				     0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b,
127 				     0x00);
128 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
129 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT,
130 				     0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef,
131 				     0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda,
132 				     0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe,
133 				     0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85,
134 				     0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e,
135 				     0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07,
136 				     0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25,
137 				     0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b,
138 				     0x00);
139 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
140 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISMO, 0xc8);
141 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPANEL, 0x08);
142 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0,
143 				     0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
144 				     0x04, 0x00, 0x01, 0x13, 0x40, 0x04, 0x09,
145 				     0x09, 0x0b, 0x0b, 0x32, 0x10, 0x08, 0x00,
146 				     0x08, 0x32, 0x10, 0x08, 0x00, 0x08, 0x32,
147 				     0x10, 0x08, 0x00, 0x08, 0x00, 0x00, 0x0a,
148 				     0x08, 0x7b);
149 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc5);
150 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN1, 0xf7);
151 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
152 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xd4);
153 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN1, 0x6e);
154 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
155 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xef);
156 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 0x0c);
157 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
158 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
159 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc8);
160 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 0xa1);
161 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
162 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
163 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP1,
164 				     0x18, 0x18, 0x19, 0x18, 0x18, 0x20, 0x18,
165 				     0x18, 0x18, 0x10, 0x10, 0x18, 0x18, 0x00,
166 				     0x00, 0x18, 0x18, 0x01, 0x01, 0x18, 0x18,
167 				     0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18,
168 				     0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x35,
169 				     0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0x18,
170 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xfc,
171 				     0xfc, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00);
172 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP2,
173 				     0x18, 0x18, 0x19, 0x18, 0x18, 0x20, 0x19,
174 				     0x18, 0x18, 0x10, 0x10, 0x18, 0x18, 0x00,
175 				     0x00, 0x18, 0x18, 0x01, 0x01, 0x18, 0x18,
176 				     0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18,
177 				     0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x35,
178 				     0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0x18,
179 				     0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
180 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
181 				     0xaa, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa,
182 				     0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 0xaa,
183 				     0xab, 0xaf, 0xef, 0xaa, 0xaa, 0xaa, 0xaa,
184 				     0xaf, 0xea, 0xaa);
185 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
186 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
187 				     0xaa, 0xaa, 0xab, 0xaf, 0xea, 0xaa, 0xaa,
188 				     0xaa, 0xae, 0xaf, 0xea, 0xaa);
189 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
190 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
191 				     0xaa, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa,
192 				     0xaa, 0xaa, 0xaf, 0xea, 0xaa);
193 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
194 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
195 				     0xba, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa,
196 				     0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xba, 0xaa,
197 				     0xaa, 0xaf, 0xea, 0xaa, 0xaa, 0xaa, 0xaa,
198 				     0xaf, 0xea, 0xaa);
199 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
200 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xe4);
201 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 0x17, 0x69);
202 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
203 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
204 				     0x09, 0x09, 0x00, 0x07, 0xe8, 0x00, 0x26,
205 				     0x00, 0x07, 0x00, 0x00, 0xe8, 0x32, 0x00,
206 				     0xe9, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x01,
207 				     0x01, 0x00, 0x12, 0x04);
208 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
209 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
210 				     0x02, 0x00, 0x01, 0x20, 0x01, 0x18, 0x08,
211 				     0xa8, 0x09);
212 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
213 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 0x20, 0x20, 0x00);
214 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
215 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
216 				     0x00, 0xdc, 0x11, 0x70, 0x00, 0x20);
217 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc9);
218 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
219 				     0x2a, 0xce, 0x02, 0x70, 0x01, 0x04);
220 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
221 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
222 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN2, 0x27);
223 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
224 	mipi_dsi_msleep(&dsi_ctx, 120);
225 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
226 	mipi_dsi_msleep(&dsi_ctx, 20);
227 	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000);
228 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
229 				     0x24);
230 	mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
231 
232 	return dsi_ctx.accum_err;
233 }
234 
235 static int hx83112b_off(struct hx83112b_panel *ctx)
236 {
237 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
238 
239 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
240 	mipi_dsi_msleep(&dsi_ctx, 20);
241 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
242 	mipi_dsi_msleep(&dsi_ctx, 120);
243 
244 	return dsi_ctx.accum_err;
245 }
246 
247 static int hx83112b_prepare(struct drm_panel *panel)
248 {
249 	struct hx83112b_panel *ctx = to_hx83112b_panel(panel);
250 	struct device *dev = &ctx->dsi->dev;
251 	int ret;
252 
253 	ret = regulator_bulk_enable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies);
254 	if (ret < 0) {
255 		dev_err(dev, "Failed to enable regulators: %d\n", ret);
256 		return ret;
257 	}
258 
259 	hx83112b_reset(ctx);
260 
261 	ret = hx83112b_on(ctx);
262 	if (ret < 0) {
263 		dev_err(dev, "Failed to initialize panel: %d\n", ret);
264 		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
265 		regulator_bulk_disable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies);
266 		return ret;
267 	}
268 
269 	return 0;
270 }
271 
272 static int hx83112b_unprepare(struct drm_panel *panel)
273 {
274 	struct hx83112b_panel *ctx = to_hx83112b_panel(panel);
275 	struct device *dev = &ctx->dsi->dev;
276 	int ret;
277 
278 	ret = hx83112b_off(ctx);
279 	if (ret < 0)
280 		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
281 
282 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
283 	regulator_bulk_disable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies);
284 
285 	return 0;
286 }
287 
288 static const struct drm_display_mode hx83112b_mode = {
289 	.clock = (1080 + 40 + 4 + 12) * (2160 + 32 + 2 + 2) * 60 / 1000,
290 	.hdisplay = 1080,
291 	.hsync_start = 1080 + 40,
292 	.hsync_end = 1080 + 40 + 4,
293 	.htotal = 1080 + 40 + 4 + 12,
294 	.vdisplay = 2160,
295 	.vsync_start = 2160 + 32,
296 	.vsync_end = 2160 + 32 + 2,
297 	.vtotal = 2160 + 32 + 2 + 2,
298 	.width_mm = 65,
299 	.height_mm = 128,
300 	.type = DRM_MODE_TYPE_DRIVER,
301 };
302 
303 static int hx83112b_get_modes(struct drm_panel *panel,
304 			      struct drm_connector *connector)
305 {
306 	return drm_connector_helper_get_modes_fixed(connector, &hx83112b_mode);
307 }
308 
309 static const struct drm_panel_funcs hx83112b_panel_funcs = {
310 	.prepare = hx83112b_prepare,
311 	.unprepare = hx83112b_unprepare,
312 	.get_modes = hx83112b_get_modes,
313 };
314 
315 static int hx83112b_bl_update_status(struct backlight_device *bl)
316 {
317 	struct mipi_dsi_device *dsi = bl_get_data(bl);
318 	u16 brightness = backlight_get_brightness(bl);
319 	int ret;
320 
321 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
322 
323 	ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
324 	if (ret < 0)
325 		return ret;
326 
327 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
328 
329 	return 0;
330 }
331 
332 static const struct backlight_ops hx83112b_bl_ops = {
333 	.update_status = hx83112b_bl_update_status,
334 };
335 
336 static struct backlight_device *
337 hx83112b_create_backlight(struct mipi_dsi_device *dsi)
338 {
339 	struct device *dev = &dsi->dev;
340 	const struct backlight_properties props = {
341 		.type = BACKLIGHT_RAW,
342 		.brightness = 4095,
343 		.max_brightness = 4095,
344 	};
345 
346 	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
347 					      &hx83112b_bl_ops, &props);
348 }
349 
350 static int hx83112b_probe(struct mipi_dsi_device *dsi)
351 {
352 	struct device *dev = &dsi->dev;
353 	struct hx83112b_panel *ctx;
354 	int ret;
355 
356 	ctx = devm_drm_panel_alloc(dev, struct hx83112b_panel, panel,
357 				   &hx83112b_panel_funcs,
358 				   DRM_MODE_CONNECTOR_DSI);
359 	if (IS_ERR(ctx))
360 		return PTR_ERR(ctx);
361 
362 	ret = devm_regulator_bulk_get_const(dev,
363 					    ARRAY_SIZE(hx83112b_supplies),
364 					    hx83112b_supplies,
365 					    &ctx->supplies);
366 	if (ret < 0)
367 		return ret;
368 
369 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
370 	if (IS_ERR(ctx->reset_gpio))
371 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
372 				     "Failed to get reset-gpios\n");
373 
374 	ctx->dsi = dsi;
375 	mipi_dsi_set_drvdata(dsi, ctx);
376 
377 	dsi->lanes = 4;
378 	dsi->format = MIPI_DSI_FMT_RGB888;
379 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
380 			  MIPI_DSI_CLOCK_NON_CONTINUOUS |
381 			  MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_LPM;
382 
383 	ctx->panel.prepare_prev_first = true;
384 
385 	ctx->panel.backlight = hx83112b_create_backlight(dsi);
386 	if (IS_ERR(ctx->panel.backlight))
387 		return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
388 				     "Failed to create backlight\n");
389 
390 	drm_panel_add(&ctx->panel);
391 
392 	ret = mipi_dsi_attach(dsi);
393 	if (ret < 0) {
394 		drm_panel_remove(&ctx->panel);
395 		return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
396 	}
397 
398 	return 0;
399 }
400 
401 static void hx83112b_remove(struct mipi_dsi_device *dsi)
402 {
403 	struct hx83112b_panel *ctx = mipi_dsi_get_drvdata(dsi);
404 	int ret;
405 
406 	ret = mipi_dsi_detach(dsi);
407 	if (ret < 0)
408 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
409 
410 	drm_panel_remove(&ctx->panel);
411 }
412 
413 static const struct of_device_id hx83112b_of_match[] = {
414 	{ .compatible = "djn,98-03057-6598b-i" },
415 	{ /* sentinel */ }
416 };
417 MODULE_DEVICE_TABLE(of, hx83112b_of_match);
418 
419 static struct mipi_dsi_driver hx83112b_driver = {
420 	.probe = hx83112b_probe,
421 	.remove = hx83112b_remove,
422 	.driver = {
423 		.name = "panel-himax-hx83112b",
424 		.of_match_table = hx83112b_of_match,
425 	},
426 };
427 module_mipi_dsi_driver(hx83112b_driver);
428 
429 MODULE_DESCRIPTION("DRM driver for hx83112b-equipped DSI panels");
430 MODULE_LICENSE("GPL");
431