xref: /linux/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1*3ee01b86STeguh Sobirin // SPDX-License-Identifier: GPL-2.0-only
2*3ee01b86STeguh Sobirin /*
3*3ee01b86STeguh Sobirin  * ChipWealth CH13726A MIPI-DSI panel driver
4*3ee01b86STeguh Sobirin  * Copyright (c) 2024, Teguh Sobirin <teguh@sobir.in>.
5*3ee01b86STeguh Sobirin  */
6*3ee01b86STeguh Sobirin 
7*3ee01b86STeguh Sobirin #include <linux/backlight.h>
8*3ee01b86STeguh Sobirin #include <linux/delay.h>
9*3ee01b86STeguh Sobirin #include <linux/gpio/consumer.h>
10*3ee01b86STeguh Sobirin #include <linux/module.h>
11*3ee01b86STeguh Sobirin #include <linux/of.h>
12*3ee01b86STeguh Sobirin #include <linux/regulator/consumer.h>
13*3ee01b86STeguh Sobirin 
14*3ee01b86STeguh Sobirin #include <drm/drm_mipi_dsi.h>
15*3ee01b86STeguh Sobirin #include <drm/drm_modes.h>
16*3ee01b86STeguh Sobirin #include <drm/drm_panel.h>
17*3ee01b86STeguh Sobirin 
18*3ee01b86STeguh Sobirin #include <video/mipi_display.h>
19*3ee01b86STeguh Sobirin 
20*3ee01b86STeguh Sobirin static const struct regulator_bulk_data ch13726a_supplies[] = {
21*3ee01b86STeguh Sobirin 	{ .supply = "vdd1v2", },
22*3ee01b86STeguh Sobirin 	{ .supply = "vddio", },
23*3ee01b86STeguh Sobirin 	{ .supply = "vdd", },
24*3ee01b86STeguh Sobirin 	{ .supply = "avdd", },
25*3ee01b86STeguh Sobirin };
26*3ee01b86STeguh Sobirin 
27*3ee01b86STeguh Sobirin struct ch13726a_panel {
28*3ee01b86STeguh Sobirin 	struct drm_panel panel;
29*3ee01b86STeguh Sobirin 	struct mipi_dsi_device *dsi;
30*3ee01b86STeguh Sobirin 	struct regulator_bulk_data *supplies;
31*3ee01b86STeguh Sobirin 	struct gpio_desc *reset_gpio;
32*3ee01b86STeguh Sobirin 	struct ch13726a_desc *desc;
33*3ee01b86STeguh Sobirin 	enum drm_panel_orientation orientation;
34*3ee01b86STeguh Sobirin };
35*3ee01b86STeguh Sobirin 
36*3ee01b86STeguh Sobirin struct ch13726a_desc {
37*3ee01b86STeguh Sobirin 	unsigned int width_mm;
38*3ee01b86STeguh Sobirin 	unsigned int height_mm;
39*3ee01b86STeguh Sobirin 	unsigned int bpc;
40*3ee01b86STeguh Sobirin 
41*3ee01b86STeguh Sobirin 	const struct drm_display_mode *modes;
42*3ee01b86STeguh Sobirin 	unsigned int num_modes;
43*3ee01b86STeguh Sobirin };
44*3ee01b86STeguh Sobirin 
45*3ee01b86STeguh Sobirin static inline struct ch13726a_panel *to_ch13726a_panel(struct drm_panel *panel)
46*3ee01b86STeguh Sobirin {
47*3ee01b86STeguh Sobirin 	return container_of(panel, struct ch13726a_panel, panel);
48*3ee01b86STeguh Sobirin }
49*3ee01b86STeguh Sobirin 
50*3ee01b86STeguh Sobirin static void ch13726a_reset(struct ch13726a_panel *ctx)
51*3ee01b86STeguh Sobirin {
52*3ee01b86STeguh Sobirin 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
53*3ee01b86STeguh Sobirin 	usleep_range(10000, 11000);
54*3ee01b86STeguh Sobirin 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
55*3ee01b86STeguh Sobirin 	usleep_range(10000, 11000);
56*3ee01b86STeguh Sobirin 	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
57*3ee01b86STeguh Sobirin 	usleep_range(10000, 11000);
58*3ee01b86STeguh Sobirin }
59*3ee01b86STeguh Sobirin 
60*3ee01b86STeguh Sobirin static int ch13726a_on(struct ch13726a_panel *ctx)
61*3ee01b86STeguh Sobirin {
62*3ee01b86STeguh Sobirin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
63*3ee01b86STeguh Sobirin 
64*3ee01b86STeguh Sobirin 	ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
65*3ee01b86STeguh Sobirin 
66*3ee01b86STeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x50);
67*3ee01b86STeguh Sobirin 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb9, 0x00);
68*3ee01b86STeguh Sobirin 
69*3ee01b86STeguh Sobirin 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
70*3ee01b86STeguh Sobirin 
71*3ee01b86STeguh Sobirin 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
72*3ee01b86STeguh Sobirin 
73*3ee01b86STeguh Sobirin 	return dsi_ctx.accum_err;
74*3ee01b86STeguh Sobirin }
75*3ee01b86STeguh Sobirin 
76*3ee01b86STeguh Sobirin static int ch13726a_disable(struct drm_panel *panel)
77*3ee01b86STeguh Sobirin {
78*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
79*3ee01b86STeguh Sobirin 	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
80*3ee01b86STeguh Sobirin 
81*3ee01b86STeguh Sobirin 	ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
82*3ee01b86STeguh Sobirin 
83*3ee01b86STeguh Sobirin 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
84*3ee01b86STeguh Sobirin 	mipi_dsi_msleep(&dsi_ctx, 50);
85*3ee01b86STeguh Sobirin 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
86*3ee01b86STeguh Sobirin 
87*3ee01b86STeguh Sobirin 	return dsi_ctx.accum_err;
88*3ee01b86STeguh Sobirin }
89*3ee01b86STeguh Sobirin 
90*3ee01b86STeguh Sobirin static int ch13726a_prepare(struct drm_panel *panel)
91*3ee01b86STeguh Sobirin {
92*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
93*3ee01b86STeguh Sobirin 	struct device *dev = &ctx->dsi->dev;
94*3ee01b86STeguh Sobirin 	int ret;
95*3ee01b86STeguh Sobirin 
96*3ee01b86STeguh Sobirin 	ret = regulator_bulk_enable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
97*3ee01b86STeguh Sobirin 	if (ret < 0) {
98*3ee01b86STeguh Sobirin 		dev_err(dev, "Failed to enable regulators: %d\n", ret);
99*3ee01b86STeguh Sobirin 		return ret;
100*3ee01b86STeguh Sobirin 	}
101*3ee01b86STeguh Sobirin 
102*3ee01b86STeguh Sobirin 	ch13726a_reset(ctx);
103*3ee01b86STeguh Sobirin 
104*3ee01b86STeguh Sobirin 	ret = ch13726a_on(ctx);
105*3ee01b86STeguh Sobirin 	if (ret < 0) {
106*3ee01b86STeguh Sobirin 		dev_err(dev, "Failed to initialize panel: %d\n", ret);
107*3ee01b86STeguh Sobirin 		gpiod_set_value_cansleep(ctx->reset_gpio, 1);
108*3ee01b86STeguh Sobirin 		regulator_bulk_disable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
109*3ee01b86STeguh Sobirin 		return ret;
110*3ee01b86STeguh Sobirin 	}
111*3ee01b86STeguh Sobirin 
112*3ee01b86STeguh Sobirin 	msleep(28);
113*3ee01b86STeguh Sobirin 
114*3ee01b86STeguh Sobirin 	return 0;
115*3ee01b86STeguh Sobirin }
116*3ee01b86STeguh Sobirin 
117*3ee01b86STeguh Sobirin static int ch13726a_unprepare(struct drm_panel *panel)
118*3ee01b86STeguh Sobirin {
119*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
120*3ee01b86STeguh Sobirin 
121*3ee01b86STeguh Sobirin 	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
122*3ee01b86STeguh Sobirin 	regulator_bulk_disable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
123*3ee01b86STeguh Sobirin 
124*3ee01b86STeguh Sobirin 	return 0;
125*3ee01b86STeguh Sobirin }
126*3ee01b86STeguh Sobirin 
127*3ee01b86STeguh Sobirin static const struct drm_display_mode thor_bottom_modes[] = {
128*3ee01b86STeguh Sobirin 	{
129*3ee01b86STeguh Sobirin 		/* 120Hz */
130*3ee01b86STeguh Sobirin 		.clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 120 / 1000,
131*3ee01b86STeguh Sobirin 		.hdisplay = 1080,
132*3ee01b86STeguh Sobirin 		.hsync_start = 1080 + 28,
133*3ee01b86STeguh Sobirin 		.hsync_end = 1080 + 28 + 4,
134*3ee01b86STeguh Sobirin 		.htotal = 1080 + 28 + 4 + 36,
135*3ee01b86STeguh Sobirin 		.vdisplay = 1240,
136*3ee01b86STeguh Sobirin 		.vsync_start = 1240 + 16,
137*3ee01b86STeguh Sobirin 		.vsync_end = 1240 + 16 + 4,
138*3ee01b86STeguh Sobirin 		.vtotal = 1240 + 16 + 4 + 8,
139*3ee01b86STeguh Sobirin 	},
140*3ee01b86STeguh Sobirin 	{
141*3ee01b86STeguh Sobirin 		/* 60Hz */
142*3ee01b86STeguh Sobirin 		.clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 60 / 1000,
143*3ee01b86STeguh Sobirin 		.hdisplay = 1080,
144*3ee01b86STeguh Sobirin 		.hsync_start = 1080 + 28,
145*3ee01b86STeguh Sobirin 		.hsync_end = 1080 + 28 + 4,
146*3ee01b86STeguh Sobirin 		.htotal = 1080 + 28 + 4 + 36,
147*3ee01b86STeguh Sobirin 		.vdisplay = 1240,
148*3ee01b86STeguh Sobirin 		.vsync_start = 1240 + 16,
149*3ee01b86STeguh Sobirin 		.vsync_end = 1240 + 16 + 4,
150*3ee01b86STeguh Sobirin 		.vtotal = 1240 + 16 + 4 + 8,
151*3ee01b86STeguh Sobirin 	}
152*3ee01b86STeguh Sobirin };
153*3ee01b86STeguh Sobirin 
154*3ee01b86STeguh Sobirin static struct ch13726a_desc thor_bottom_desc = {
155*3ee01b86STeguh Sobirin 	.modes = thor_bottom_modes,
156*3ee01b86STeguh Sobirin 	.num_modes = ARRAY_SIZE(thor_bottom_modes),
157*3ee01b86STeguh Sobirin 	.width_mm = 65,
158*3ee01b86STeguh Sobirin 	.height_mm = 75,
159*3ee01b86STeguh Sobirin 	.bpc = 8,
160*3ee01b86STeguh Sobirin };
161*3ee01b86STeguh Sobirin 
162*3ee01b86STeguh Sobirin static int ch13726a_get_modes(struct drm_panel *panel,
163*3ee01b86STeguh Sobirin 					struct drm_connector *connector)
164*3ee01b86STeguh Sobirin {
165*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
166*3ee01b86STeguh Sobirin 
167*3ee01b86STeguh Sobirin 	for (uint8_t i = 0; i < ctx->desc->num_modes; i++) {
168*3ee01b86STeguh Sobirin 		const struct drm_display_mode *m = &ctx->desc->modes[i];
169*3ee01b86STeguh Sobirin 		struct drm_display_mode *mode;
170*3ee01b86STeguh Sobirin 
171*3ee01b86STeguh Sobirin 		mode = drm_mode_duplicate(connector->dev, m);
172*3ee01b86STeguh Sobirin 		if (!mode) {
173*3ee01b86STeguh Sobirin 			dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
174*3ee01b86STeguh Sobirin 				m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
175*3ee01b86STeguh Sobirin 			return -ENOMEM;
176*3ee01b86STeguh Sobirin 		}
177*3ee01b86STeguh Sobirin 
178*3ee01b86STeguh Sobirin 		mode->type = DRM_MODE_TYPE_DRIVER;
179*3ee01b86STeguh Sobirin 		if (i == 0)
180*3ee01b86STeguh Sobirin 			mode->type |= DRM_MODE_TYPE_PREFERRED;
181*3ee01b86STeguh Sobirin 
182*3ee01b86STeguh Sobirin 		drm_mode_set_name(mode);
183*3ee01b86STeguh Sobirin 		drm_mode_probed_add(connector, mode);
184*3ee01b86STeguh Sobirin 	}
185*3ee01b86STeguh Sobirin 
186*3ee01b86STeguh Sobirin 	connector->display_info.width_mm = ctx->desc->width_mm;
187*3ee01b86STeguh Sobirin 	connector->display_info.height_mm = ctx->desc->height_mm;
188*3ee01b86STeguh Sobirin 	connector->display_info.bpc = ctx->desc->bpc;
189*3ee01b86STeguh Sobirin 
190*3ee01b86STeguh Sobirin 	return ctx->desc->num_modes;
191*3ee01b86STeguh Sobirin }
192*3ee01b86STeguh Sobirin 
193*3ee01b86STeguh Sobirin static enum drm_panel_orientation ch13726a_get_orientation(struct drm_panel *panel)
194*3ee01b86STeguh Sobirin {
195*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
196*3ee01b86STeguh Sobirin 
197*3ee01b86STeguh Sobirin 	return ctx->orientation;
198*3ee01b86STeguh Sobirin }
199*3ee01b86STeguh Sobirin 
200*3ee01b86STeguh Sobirin static const struct drm_panel_funcs ch13726a_panel_funcs = {
201*3ee01b86STeguh Sobirin 	.prepare = ch13726a_prepare,
202*3ee01b86STeguh Sobirin 	.unprepare = ch13726a_unprepare,
203*3ee01b86STeguh Sobirin 	.disable = ch13726a_disable,
204*3ee01b86STeguh Sobirin 	.get_modes = ch13726a_get_modes,
205*3ee01b86STeguh Sobirin 	.get_orientation = ch13726a_get_orientation,
206*3ee01b86STeguh Sobirin };
207*3ee01b86STeguh Sobirin 
208*3ee01b86STeguh Sobirin static int ch13726a_bl_update_status(struct backlight_device *bl)
209*3ee01b86STeguh Sobirin {
210*3ee01b86STeguh Sobirin 	struct mipi_dsi_device *dsi = bl_get_data(bl);
211*3ee01b86STeguh Sobirin 	u16 brightness = backlight_get_brightness(bl);
212*3ee01b86STeguh Sobirin 	int ret;
213*3ee01b86STeguh Sobirin 
214*3ee01b86STeguh Sobirin 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
215*3ee01b86STeguh Sobirin 
216*3ee01b86STeguh Sobirin 	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
217*3ee01b86STeguh Sobirin 	if (ret < 0)
218*3ee01b86STeguh Sobirin 		return ret;
219*3ee01b86STeguh Sobirin 
220*3ee01b86STeguh Sobirin 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
221*3ee01b86STeguh Sobirin 
222*3ee01b86STeguh Sobirin 	return 0;
223*3ee01b86STeguh Sobirin }
224*3ee01b86STeguh Sobirin 
225*3ee01b86STeguh Sobirin static const struct backlight_ops ch13726a_bl_ops = {
226*3ee01b86STeguh Sobirin 	.update_status = ch13726a_bl_update_status,
227*3ee01b86STeguh Sobirin };
228*3ee01b86STeguh Sobirin 
229*3ee01b86STeguh Sobirin static struct backlight_device *
230*3ee01b86STeguh Sobirin ch13726a_create_backlight(struct mipi_dsi_device *dsi)
231*3ee01b86STeguh Sobirin {
232*3ee01b86STeguh Sobirin 	struct device *dev = &dsi->dev;
233*3ee01b86STeguh Sobirin 	const struct backlight_properties props = {
234*3ee01b86STeguh Sobirin 		.type = BACKLIGHT_RAW,
235*3ee01b86STeguh Sobirin 		.brightness = 255,
236*3ee01b86STeguh Sobirin 		.max_brightness = 255,
237*3ee01b86STeguh Sobirin 	};
238*3ee01b86STeguh Sobirin 
239*3ee01b86STeguh Sobirin 	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
240*3ee01b86STeguh Sobirin 					      &ch13726a_bl_ops, &props);
241*3ee01b86STeguh Sobirin }
242*3ee01b86STeguh Sobirin 
243*3ee01b86STeguh Sobirin static int ch13726a_probe(struct mipi_dsi_device *dsi)
244*3ee01b86STeguh Sobirin {
245*3ee01b86STeguh Sobirin 	struct device *dev = &dsi->dev;
246*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx;
247*3ee01b86STeguh Sobirin 	int ret;
248*3ee01b86STeguh Sobirin 
249*3ee01b86STeguh Sobirin 	ctx = devm_drm_panel_alloc(dev, __typeof(*ctx), panel,
250*3ee01b86STeguh Sobirin 				   &ch13726a_panel_funcs,
251*3ee01b86STeguh Sobirin 				   DRM_MODE_CONNECTOR_DSI);
252*3ee01b86STeguh Sobirin 	if (IS_ERR(ctx))
253*3ee01b86STeguh Sobirin 		return PTR_ERR(ctx);
254*3ee01b86STeguh Sobirin 
255*3ee01b86STeguh Sobirin 	ctx->desc = (struct ch13726a_desc *)of_device_get_match_data(dev);
256*3ee01b86STeguh Sobirin 	if (!ctx->desc)
257*3ee01b86STeguh Sobirin 		return -ENODEV;
258*3ee01b86STeguh Sobirin 
259*3ee01b86STeguh Sobirin 	ret = devm_regulator_bulk_get_const(dev,
260*3ee01b86STeguh Sobirin 					    ARRAY_SIZE(ch13726a_supplies),
261*3ee01b86STeguh Sobirin 					    ch13726a_supplies,
262*3ee01b86STeguh Sobirin 					    &ctx->supplies);
263*3ee01b86STeguh Sobirin 	if (ret < 0)
264*3ee01b86STeguh Sobirin 		return dev_err_probe(dev, ret, "Failed to get regulators\n");
265*3ee01b86STeguh Sobirin 
266*3ee01b86STeguh Sobirin 	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
267*3ee01b86STeguh Sobirin 	if (IS_ERR(ctx->reset_gpio))
268*3ee01b86STeguh Sobirin 		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
269*3ee01b86STeguh Sobirin 				     "Failed to get reset-gpios\n");
270*3ee01b86STeguh Sobirin 
271*3ee01b86STeguh Sobirin 	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
272*3ee01b86STeguh Sobirin 	if (ret < 0) {
273*3ee01b86STeguh Sobirin 		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
274*3ee01b86STeguh Sobirin 		return ret;
275*3ee01b86STeguh Sobirin 	}
276*3ee01b86STeguh Sobirin 
277*3ee01b86STeguh Sobirin 	ctx->dsi = dsi;
278*3ee01b86STeguh Sobirin 	mipi_dsi_set_drvdata(dsi, ctx);
279*3ee01b86STeguh Sobirin 
280*3ee01b86STeguh Sobirin 	dsi->lanes = 4;
281*3ee01b86STeguh Sobirin 	dsi->format = MIPI_DSI_FMT_RGB888;
282*3ee01b86STeguh Sobirin 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
283*3ee01b86STeguh Sobirin 			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
284*3ee01b86STeguh Sobirin 
285*3ee01b86STeguh Sobirin 	ctx->panel.prepare_prev_first = true;
286*3ee01b86STeguh Sobirin 
287*3ee01b86STeguh Sobirin 	ctx->panel.backlight = ch13726a_create_backlight(dsi);
288*3ee01b86STeguh Sobirin 	if (IS_ERR(ctx->panel.backlight))
289*3ee01b86STeguh Sobirin 		return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
290*3ee01b86STeguh Sobirin 				     "Failed to create backlight\n");
291*3ee01b86STeguh Sobirin 
292*3ee01b86STeguh Sobirin 	drm_panel_add(&ctx->panel);
293*3ee01b86STeguh Sobirin 
294*3ee01b86STeguh Sobirin 	ret = mipi_dsi_attach(dsi);
295*3ee01b86STeguh Sobirin 	if (ret < 0) {
296*3ee01b86STeguh Sobirin 		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
297*3ee01b86STeguh Sobirin 		drm_panel_remove(&ctx->panel);
298*3ee01b86STeguh Sobirin 		return ret;
299*3ee01b86STeguh Sobirin 	}
300*3ee01b86STeguh Sobirin 
301*3ee01b86STeguh Sobirin 	return 0;
302*3ee01b86STeguh Sobirin }
303*3ee01b86STeguh Sobirin 
304*3ee01b86STeguh Sobirin static void ch13726a_remove(struct mipi_dsi_device *dsi)
305*3ee01b86STeguh Sobirin {
306*3ee01b86STeguh Sobirin 	struct ch13726a_panel *ctx = mipi_dsi_get_drvdata(dsi);
307*3ee01b86STeguh Sobirin 	int ret;
308*3ee01b86STeguh Sobirin 
309*3ee01b86STeguh Sobirin 	ret = mipi_dsi_detach(dsi);
310*3ee01b86STeguh Sobirin 	if (ret < 0)
311*3ee01b86STeguh Sobirin 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
312*3ee01b86STeguh Sobirin 
313*3ee01b86STeguh Sobirin 	drm_panel_remove(&ctx->panel);
314*3ee01b86STeguh Sobirin }
315*3ee01b86STeguh Sobirin 
316*3ee01b86STeguh Sobirin static const struct of_device_id ch13726a_of_match[] = {
317*3ee01b86STeguh Sobirin 	{ .compatible = "ayntec,thor-panel-bottom", .data = &thor_bottom_desc },
318*3ee01b86STeguh Sobirin 	{ /* sentinel */ }
319*3ee01b86STeguh Sobirin };
320*3ee01b86STeguh Sobirin MODULE_DEVICE_TABLE(of, ch13726a_of_match);
321*3ee01b86STeguh Sobirin 
322*3ee01b86STeguh Sobirin static struct mipi_dsi_driver ch13726a_driver = {
323*3ee01b86STeguh Sobirin 	.probe = ch13726a_probe,
324*3ee01b86STeguh Sobirin 	.remove = ch13726a_remove,
325*3ee01b86STeguh Sobirin 	.driver = {
326*3ee01b86STeguh Sobirin 		.name = "panel-ch13726a-amoled",
327*3ee01b86STeguh Sobirin 		.of_match_table = ch13726a_of_match,
328*3ee01b86STeguh Sobirin 	},
329*3ee01b86STeguh Sobirin };
330*3ee01b86STeguh Sobirin module_mipi_dsi_driver(ch13726a_driver);
331*3ee01b86STeguh Sobirin 
332*3ee01b86STeguh Sobirin MODULE_DESCRIPTION("DRM driver for CH13726A DSI panels");
333*3ee01b86STeguh Sobirin MODULE_LICENSE("GPL");
334