xref: /linux/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2023 Alexander Warnecke <awarnecke002@hotmail.com>
4  * Copyright (c) 2023 Manuel Traut <manut@mecka.net>
5  * Copyright (c) 2023 Dang Huynh <danct12@riseup.net>
6  */
7 
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_device.h>
13 #include <linux/regulator/consumer.h>
14 
15 #include <drm/drm_connector.h>
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 struct boe_th101mb31ig002;
22 
23 struct panel_desc {
24 	const struct drm_display_mode *modes;
25 	unsigned long mode_flags;
26 	enum mipi_dsi_pixel_format format;
27 	int (*init)(struct boe_th101mb31ig002 *ctx);
28 	unsigned int lanes;
29 	bool lp11_before_reset;
30 	unsigned int vcioo_to_lp11_delay_ms;
31 	unsigned int lp11_to_reset_delay_ms;
32 	unsigned int backlight_off_to_display_off_delay_ms;
33 	unsigned int enter_sleep_to_reset_down_delay_ms;
34 	unsigned int power_off_delay_ms;
35 };
36 
37 struct boe_th101mb31ig002 {
38 	struct drm_panel panel;
39 
40 	struct mipi_dsi_device *dsi;
41 
42 	const struct panel_desc *desc;
43 
44 	struct regulator *power;
45 	struct gpio_desc *enable;
46 	struct gpio_desc *reset;
47 
48 	enum drm_panel_orientation orientation;
49 };
50 
boe_th101mb31ig002_reset(struct boe_th101mb31ig002 * ctx)51 static void boe_th101mb31ig002_reset(struct boe_th101mb31ig002 *ctx)
52 {
53 	gpiod_direction_output(ctx->reset, 0);
54 	usleep_range(10, 100);
55 	gpiod_direction_output(ctx->reset, 1);
56 	usleep_range(10, 100);
57 	gpiod_direction_output(ctx->reset, 0);
58 	usleep_range(5000, 6000);
59 }
60 
boe_th101mb31ig002_enable(struct boe_th101mb31ig002 * ctx)61 static int boe_th101mb31ig002_enable(struct boe_th101mb31ig002 *ctx)
62 {
63 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
64 
65 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0xab, 0xba);
66 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0xba, 0xab);
67 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x10, 0x01, 0x47, 0xff);
68 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x0c, 0x14, 0x04, 0x50, 0x50, 0x14);
69 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x56, 0x53, 0x00);
70 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x33, 0x30, 0x04);
71 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0xb0, 0x00, 0x00, 0x10, 0x00, 0x10,
72 					       0x00);
73 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x05, 0x12, 0x29, 0x49, 0x48, 0x00,
74 					       0x00);
75 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x7c, 0x65, 0x55, 0x49, 0x46, 0x36,
76 					       0x3b, 0x24, 0x3d, 0x3c, 0x3d, 0x5c, 0x4c,
77 					       0x55, 0x47, 0x46, 0x39, 0x26, 0x06, 0x7c,
78 					       0x65, 0x55, 0x49, 0x46, 0x36, 0x3b, 0x24,
79 					       0x3d, 0x3c, 0x3d, 0x5c, 0x4c, 0x55, 0x47,
80 					       0x46, 0x39, 0x26, 0x06);
81 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0xff, 0x87, 0x12, 0x34, 0x44, 0x44,
82 					       0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0f,
83 					       0x00, 0x00, 0xc1);
84 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x54, 0x94, 0x02, 0x85, 0x9f, 0x00,
85 					       0x7f, 0x00, 0x54, 0x00);
86 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11,
87 					       0x22, 0x20, 0x44, 0xff, 0x18, 0x00);
88 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc3, 0x86, 0x46, 0x05, 0x05, 0x1c, 0x1c,
89 					       0x1d, 0x1d, 0x02, 0x1f, 0x1f, 0x1e, 0x1e,
90 					       0x0f, 0x0f, 0x0d, 0x0d, 0x13, 0x13, 0x11,
91 					       0x11, 0x00);
92 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 0x07, 0x07, 0x04, 0x04, 0x1c, 0x1c,
93 					       0x1d, 0x1d, 0x02, 0x1f, 0x1f, 0x1e, 0x1e,
94 					       0x0e, 0x0e, 0x0c, 0x0c, 0x12, 0x12, 0x10,
95 					       0x10, 0x00);
96 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc6, 0x2a, 0x2a);
97 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
98 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xca, 0xcb, 0x43);
99 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcd, 0x0e, 0x4b, 0x4b, 0x20, 0x19, 0x6b,
100 					       0x06, 0xb3);
101 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd2, 0xe3, 0x2b, 0x38, 0x00);
102 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x00, 0x01, 0x00, 0x0e, 0x04, 0x44,
103 					       0x08, 0x10, 0x00, 0x00, 0x00);
104 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff,
105 					       0xff, 0xff);
106 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x12, 0x03, 0x20, 0x00, 0xff);
107 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x00);
108 
109 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
110 
111 	mipi_dsi_msleep(&dsi_ctx, 120);
112 
113 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
114 
115 	return dsi_ctx.accum_err;
116 }
117 
starry_er88577_init_cmd(struct boe_th101mb31ig002 * ctx)118 static int starry_er88577_init_cmd(struct boe_th101mb31ig002 *ctx)
119 {
120 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
121 
122 	msleep(70);
123 
124 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0xab, 0xba);
125 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0xba, 0xab);
126 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x10, 0x01, 0x47, 0xff);
127 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x0c, 0x14, 0x04, 0x50, 0x50, 0x14);
128 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x56, 0x53, 0x00);
129 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x33, 0x30, 0x04);
130 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0xb0, 0x00, 0x00, 0x10, 0x00, 0x10,
131 					       0x00);
132 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x05, 0x12, 0x29, 0x49, 0x40);
133 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x7c, 0x61, 0x4f, 0x42, 0x3e, 0x2d,
134 					       0x31, 0x1a, 0x33, 0x33, 0x33, 0x52, 0x40,
135 					       0x47, 0x38, 0x34, 0x26, 0x0e, 0x06, 0x7c,
136 					       0x61, 0x4f, 0x42, 0x3e, 0x2d, 0x31, 0x1a,
137 					       0x33, 0x33, 0x33, 0x52, 0x40, 0x47, 0x38,
138 					       0x34, 0x26, 0x0e, 0x06);
139 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc0, 0xcc, 0x76, 0x12, 0x34, 0x44, 0x44,
140 					       0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0f,
141 					       0x00, 0x00, 0xc1);
142 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x54, 0x94, 0x02, 0x85, 0x9f, 0x00,
143 					       0x6f, 0x00, 0x54, 0x00);
144 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11,
145 					       0x22, 0x20, 0x44, 0xff, 0x18, 0x00);
146 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc3, 0x87, 0x47, 0x05, 0x05, 0x1c, 0x1c,
147 					       0x1d, 0x1d, 0x02, 0x1e, 0x1e, 0x1f, 0x1f,
148 					       0x0f, 0x0f, 0x0d, 0x0d, 0x13, 0x13, 0x11,
149 					       0x11, 0x24);
150 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 0x06, 0x06, 0x04, 0x04, 0x1c, 0x1c,
151 					       0x1d, 0x1d, 0x02, 0x1e, 0x1e, 0x1f, 0x1f,
152 					       0x0e, 0x0e, 0x0c, 0x0c, 0x12, 0x12, 0x10,
153 					       0x10, 0x24);
154 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
155 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xca, 0xcb, 0x43);
156 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcd, 0x0e, 0x4b, 0x4b, 0x20, 0x19, 0x6b,
157 					       0x06, 0xb3);
158 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd1, 0x40, 0x0d, 0xff, 0x0f);
159 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd2, 0xe3, 0x2b, 0x38, 0x08);
160 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd3, 0x00, 0x00, 0x00, 0x00,
161 					       0x00, 0x33, 0x20, 0x3a, 0xd5, 0x86, 0xf3);
162 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x00, 0x01, 0x00, 0x0e, 0x04, 0x44,
163 					       0x08, 0x10, 0x00, 0x00, 0x00);
164 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x80, 0x09, 0xff, 0xff, 0xff, 0xff,
165 					       0xff, 0xff);
166 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x12, 0x03, 0x20, 0x00, 0xff);
167 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x00);
168 
169 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
170 
171 	mipi_dsi_msleep(&dsi_ctx, 120);
172 
173 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
174 
175 	mipi_dsi_msleep(&dsi_ctx, 20);
176 
177 	return dsi_ctx.accum_err;
178 }
179 
boe_th101mb31ig002_disable(struct drm_panel * panel)180 static int boe_th101mb31ig002_disable(struct drm_panel *panel)
181 {
182 	struct boe_th101mb31ig002 *ctx = container_of(panel,
183 						      struct boe_th101mb31ig002,
184 						      panel);
185 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
186 
187 	if (ctx->desc->backlight_off_to_display_off_delay_ms)
188 		mipi_dsi_msleep(&dsi_ctx, ctx->desc->backlight_off_to_display_off_delay_ms);
189 
190 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
191 
192 	mipi_dsi_msleep(&dsi_ctx, 120);
193 
194 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
195 
196 	if (ctx->desc->enter_sleep_to_reset_down_delay_ms)
197 		mipi_dsi_msleep(&dsi_ctx, ctx->desc->enter_sleep_to_reset_down_delay_ms);
198 
199 	return dsi_ctx.accum_err;
200 }
201 
boe_th101mb31ig002_unprepare(struct drm_panel * panel)202 static int boe_th101mb31ig002_unprepare(struct drm_panel *panel)
203 {
204 	struct boe_th101mb31ig002 *ctx = container_of(panel,
205 						      struct boe_th101mb31ig002,
206 						      panel);
207 
208 	gpiod_set_value_cansleep(ctx->reset, 1);
209 	gpiod_set_value_cansleep(ctx->enable, 0);
210 	regulator_disable(ctx->power);
211 
212 	if (ctx->desc->power_off_delay_ms)
213 		msleep(ctx->desc->power_off_delay_ms);
214 
215 	return 0;
216 }
217 
boe_th101mb31ig002_prepare(struct drm_panel * panel)218 static int boe_th101mb31ig002_prepare(struct drm_panel *panel)
219 {
220 	struct boe_th101mb31ig002 *ctx = container_of(panel,
221 						      struct boe_th101mb31ig002,
222 						      panel);
223 	struct device *dev = &ctx->dsi->dev;
224 	int ret;
225 
226 	ret = regulator_enable(ctx->power);
227 	if (ret) {
228 		dev_err(dev, "Failed to enable power supply: %d\n", ret);
229 		return ret;
230 	}
231 
232 	if (ctx->desc->vcioo_to_lp11_delay_ms)
233 		msleep(ctx->desc->vcioo_to_lp11_delay_ms);
234 
235 	if (ctx->desc->lp11_before_reset) {
236 		ret = mipi_dsi_dcs_nop(ctx->dsi);
237 		if (ret)
238 			return ret;
239 	}
240 
241 	if (ctx->desc->lp11_to_reset_delay_ms)
242 		msleep(ctx->desc->lp11_to_reset_delay_ms);
243 
244 	gpiod_set_value_cansleep(ctx->enable, 1);
245 	msleep(50);
246 	boe_th101mb31ig002_reset(ctx);
247 
248 	ret = ctx->desc->init(ctx);
249 	if (ret)
250 		return ret;
251 
252 	return 0;
253 }
254 
255 static const struct drm_display_mode boe_th101mb31ig002_default_mode = {
256 	.clock		= 73500,
257 	.hdisplay	= 800,
258 	.hsync_start	= 800 + 64,
259 	.hsync_end	= 800 + 64 + 16,
260 	.htotal		= 800 + 64 + 16 + 64,
261 	.vdisplay	= 1280,
262 	.vsync_start	= 1280 + 2,
263 	.vsync_end	= 1280 + 2 + 4,
264 	.vtotal		= 1280 + 2 + 4 + 12,
265 	.width_mm	= 135,
266 	.height_mm	= 216,
267 	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
268 };
269 
270 static const struct panel_desc boe_th101mb31ig002_desc = {
271 	.modes = &boe_th101mb31ig002_default_mode,
272 	.lanes = 4,
273 	.format = MIPI_DSI_FMT_RGB888,
274 	.mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
275 			  MIPI_DSI_MODE_NO_EOT_PACKET |
276 			  MIPI_DSI_MODE_LPM,
277 	.init = boe_th101mb31ig002_enable,
278 };
279 
280 static const struct drm_display_mode starry_er88577_default_mode = {
281 	.clock	= (800 + 25 + 25 + 25) * (1280 + 20 + 4 + 12) * 60 / 1000,
282 	.hdisplay = 800,
283 	.hsync_start = 800 + 25,
284 	.hsync_end = 800 + 25 + 25,
285 	.htotal = 800 + 25 + 25 + 25,
286 	.vdisplay = 1280,
287 	.vsync_start = 1280 + 20,
288 	.vsync_end = 1280 + 20 + 4,
289 	.vtotal = 1280 + 20 + 4 + 12,
290 	.width_mm = 135,
291 	.height_mm = 216,
292 	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
293 };
294 
295 static const struct panel_desc starry_er88577_desc = {
296 	.modes = &starry_er88577_default_mode,
297 	.lanes = 4,
298 	.format = MIPI_DSI_FMT_RGB888,
299 	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
300 		      MIPI_DSI_MODE_LPM,
301 	.init = starry_er88577_init_cmd,
302 	.lp11_before_reset = true,
303 	.vcioo_to_lp11_delay_ms = 5,
304 	.lp11_to_reset_delay_ms = 50,
305 	.backlight_off_to_display_off_delay_ms = 100,
306 	.enter_sleep_to_reset_down_delay_ms = 100,
307 	.power_off_delay_ms = 1000,
308 };
309 
boe_th101mb31ig002_get_modes(struct drm_panel * panel,struct drm_connector * connector)310 static int boe_th101mb31ig002_get_modes(struct drm_panel *panel,
311 					struct drm_connector *connector)
312 {
313 	struct boe_th101mb31ig002 *ctx = container_of(panel,
314 						      struct boe_th101mb31ig002,
315 						      panel);
316 	const struct drm_display_mode *desc_mode = ctx->desc->modes;
317 
318 	connector->display_info.bpc = 8;
319 	/*
320 	 * TODO: Remove once all drm drivers call
321 	 * drm_connector_set_orientation_from_panel()
322 	 */
323 	drm_connector_set_panel_orientation(connector, ctx->orientation);
324 
325 	return drm_connector_helper_get_modes_fixed(connector, desc_mode);
326 }
327 
328 static enum drm_panel_orientation
boe_th101mb31ig002_get_orientation(struct drm_panel * panel)329 boe_th101mb31ig002_get_orientation(struct drm_panel *panel)
330 {
331 	struct boe_th101mb31ig002 *ctx = container_of(panel,
332 						      struct boe_th101mb31ig002,
333 						      panel);
334 
335 	return ctx->orientation;
336 }
337 
338 static const struct drm_panel_funcs boe_th101mb31ig002_funcs = {
339 	.prepare = boe_th101mb31ig002_prepare,
340 	.unprepare = boe_th101mb31ig002_unprepare,
341 	.disable = boe_th101mb31ig002_disable,
342 	.get_modes = boe_th101mb31ig002_get_modes,
343 	.get_orientation = boe_th101mb31ig002_get_orientation,
344 };
345 
boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device * dsi)346 static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi)
347 {
348 	struct boe_th101mb31ig002 *ctx;
349 	const struct panel_desc *desc;
350 	int ret;
351 
352 	ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
353 	if (!ctx)
354 		return -ENOMEM;
355 
356 	mipi_dsi_set_drvdata(dsi, ctx);
357 	ctx->dsi = dsi;
358 
359 	desc = of_device_get_match_data(&dsi->dev);
360 	dsi->lanes = desc->lanes;
361 	dsi->format = desc->format;
362 	dsi->mode_flags = desc->mode_flags;
363 	ctx->desc = desc;
364 
365 	ctx->power = devm_regulator_get(&dsi->dev, "power");
366 	if (IS_ERR(ctx->power))
367 		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power),
368 				     "Failed to get power regulator\n");
369 
370 	ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_LOW);
371 	if (IS_ERR(ctx->enable))
372 		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->enable),
373 				     "Failed to get enable GPIO\n");
374 
375 	ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_HIGH);
376 	if (IS_ERR(ctx->reset))
377 		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
378 				     "Failed to get reset GPIO\n");
379 
380 	ret = of_drm_get_panel_orientation(dsi->dev.of_node,
381 					   &ctx->orientation);
382 	if (ret)
383 		return dev_err_probe(&dsi->dev, ret,
384 				     "Failed to get orientation\n");
385 
386 	drm_panel_init(&ctx->panel, &dsi->dev, &boe_th101mb31ig002_funcs,
387 		       DRM_MODE_CONNECTOR_DSI);
388 
389 	ret = drm_panel_of_backlight(&ctx->panel);
390 	if (ret)
391 		return ret;
392 
393 	drm_panel_add(&ctx->panel);
394 
395 	ret = mipi_dsi_attach(dsi);
396 	if (ret < 0) {
397 		dev_err_probe(&dsi->dev, ret,
398 			      "Failed to attach panel to DSI host\n");
399 		drm_panel_remove(&ctx->panel);
400 		return ret;
401 	}
402 
403 	return 0;
404 }
405 
boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device * dsi)406 static void boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device *dsi)
407 {
408 	struct boe_th101mb31ig002 *ctx = mipi_dsi_get_drvdata(dsi);
409 
410 	mipi_dsi_detach(dsi);
411 	drm_panel_remove(&ctx->panel);
412 }
413 
414 static const struct of_device_id boe_th101mb31ig002_of_match[] = {
415 	{
416 		.compatible = "boe,th101mb31ig002-28a",
417 		.data = &boe_th101mb31ig002_desc
418 	},
419 	{
420 		.compatible = "starry,er88577",
421 		.data = &starry_er88577_desc
422 	},
423 	{ /* sentinel */ }
424 };
425 MODULE_DEVICE_TABLE(of, boe_th101mb31ig002_of_match);
426 
427 static struct mipi_dsi_driver boe_th101mb31ig002_driver = {
428 	.driver = {
429 		.name = "boe-th101mb31ig002-28a",
430 		.of_match_table = boe_th101mb31ig002_of_match,
431 	},
432 	.probe = boe_th101mb31ig002_dsi_probe,
433 	.remove = boe_th101mb31ig002_dsi_remove,
434 };
435 module_mipi_dsi_driver(boe_th101mb31ig002_driver);
436 
437 MODULE_AUTHOR("Alexander Warnecke <awarnecke002@hotmail.com>");
438 MODULE_DESCRIPTION("BOE TH101MB31IG002-28A MIPI-DSI LCD panel");
439 MODULE_LICENSE("GPL");
440