xref: /openbmc/linux/drivers/gpu/drm/panel/panel-innolux-p079zca.c (revision 7ad4e4636c54dcfbcaf43c8737af3c294ed17d11)
114c8f2e9SChris Zhong /*
214c8f2e9SChris Zhong  * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
314c8f2e9SChris Zhong  *
414c8f2e9SChris Zhong  * This program is free software; you can redistribute it and/or modify
514c8f2e9SChris Zhong  * it under the terms of the GNU General Public License as published by
614c8f2e9SChris Zhong  * the Free Software Foundation; either version 2 of the License, or
714c8f2e9SChris Zhong  * (at your option) any later version.
814c8f2e9SChris Zhong  */
914c8f2e9SChris Zhong 
1014c8f2e9SChris Zhong #include <linux/backlight.h>
1114c8f2e9SChris Zhong #include <linux/gpio/consumer.h>
1214c8f2e9SChris Zhong #include <linux/module.h>
1314c8f2e9SChris Zhong #include <linux/of.h>
1414c8f2e9SChris Zhong #include <linux/regulator/consumer.h>
1514c8f2e9SChris Zhong 
1614c8f2e9SChris Zhong #include <drm/drmP.h>
1714c8f2e9SChris Zhong #include <drm/drm_crtc.h>
1814c8f2e9SChris Zhong #include <drm/drm_mipi_dsi.h>
1914c8f2e9SChris Zhong #include <drm/drm_panel.h>
2014c8f2e9SChris Zhong 
2114c8f2e9SChris Zhong #include <video/mipi_display.h>
2214c8f2e9SChris Zhong 
23*7ad4e463SLin Huang struct panel_desc {
24*7ad4e463SLin Huang 	const struct drm_display_mode *mode;
25*7ad4e463SLin Huang 	unsigned int bpc;
26*7ad4e463SLin Huang 	struct {
27*7ad4e463SLin Huang 		unsigned int width;
28*7ad4e463SLin Huang 		unsigned int height;
29*7ad4e463SLin Huang 	} size;
30*7ad4e463SLin Huang 
31*7ad4e463SLin Huang 	unsigned long flags;
32*7ad4e463SLin Huang 	enum mipi_dsi_pixel_format format;
33*7ad4e463SLin Huang 	unsigned int lanes;
34*7ad4e463SLin Huang 	const char * const *supply_names;
35*7ad4e463SLin Huang 	unsigned int num_supplies;
36*7ad4e463SLin Huang };
37*7ad4e463SLin Huang 
3814c8f2e9SChris Zhong struct innolux_panel {
3914c8f2e9SChris Zhong 	struct drm_panel base;
4014c8f2e9SChris Zhong 	struct mipi_dsi_device *link;
41*7ad4e463SLin Huang 	const struct panel_desc *desc;
4214c8f2e9SChris Zhong 
4314c8f2e9SChris Zhong 	struct backlight_device *backlight;
44*7ad4e463SLin Huang 	struct regulator_bulk_data *supplies;
45*7ad4e463SLin Huang 	unsigned int num_supplies;
4614c8f2e9SChris Zhong 	struct gpio_desc *enable_gpio;
4714c8f2e9SChris Zhong 
4814c8f2e9SChris Zhong 	bool prepared;
4914c8f2e9SChris Zhong 	bool enabled;
5014c8f2e9SChris Zhong };
5114c8f2e9SChris Zhong 
5214c8f2e9SChris Zhong static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
5314c8f2e9SChris Zhong {
5414c8f2e9SChris Zhong 	return container_of(panel, struct innolux_panel, base);
5514c8f2e9SChris Zhong }
5614c8f2e9SChris Zhong 
5714c8f2e9SChris Zhong static int innolux_panel_disable(struct drm_panel *panel)
5814c8f2e9SChris Zhong {
5914c8f2e9SChris Zhong 	struct innolux_panel *innolux = to_innolux_panel(panel);
6014c8f2e9SChris Zhong 	int err;
6114c8f2e9SChris Zhong 
6214c8f2e9SChris Zhong 	if (!innolux->enabled)
6314c8f2e9SChris Zhong 		return 0;
6414c8f2e9SChris Zhong 
65d593bfdbSMeghana Madhyastha 	backlight_disable(innolux->backlight);
6614c8f2e9SChris Zhong 
6714c8f2e9SChris Zhong 	err = mipi_dsi_dcs_set_display_off(innolux->link);
6814c8f2e9SChris Zhong 	if (err < 0)
6914c8f2e9SChris Zhong 		DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
7014c8f2e9SChris Zhong 			      err);
7114c8f2e9SChris Zhong 
7214c8f2e9SChris Zhong 	innolux->enabled = false;
7314c8f2e9SChris Zhong 
7414c8f2e9SChris Zhong 	return 0;
7514c8f2e9SChris Zhong }
7614c8f2e9SChris Zhong 
7714c8f2e9SChris Zhong static int innolux_panel_unprepare(struct drm_panel *panel)
7814c8f2e9SChris Zhong {
7914c8f2e9SChris Zhong 	struct innolux_panel *innolux = to_innolux_panel(panel);
8014c8f2e9SChris Zhong 	int err;
8114c8f2e9SChris Zhong 
8214c8f2e9SChris Zhong 	if (!innolux->prepared)
8314c8f2e9SChris Zhong 		return 0;
8414c8f2e9SChris Zhong 
8514c8f2e9SChris Zhong 	err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
8614c8f2e9SChris Zhong 	if (err < 0) {
8714c8f2e9SChris Zhong 		DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
8814c8f2e9SChris Zhong 			      err);
8914c8f2e9SChris Zhong 		return err;
9014c8f2e9SChris Zhong 	}
9114c8f2e9SChris Zhong 
9214c8f2e9SChris Zhong 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
9314c8f2e9SChris Zhong 
9414c8f2e9SChris Zhong 	/* T8: 80ms - 1000ms */
9514c8f2e9SChris Zhong 	msleep(80);
9614c8f2e9SChris Zhong 
97*7ad4e463SLin Huang 	err = regulator_bulk_disable(innolux->desc->num_supplies,
98*7ad4e463SLin Huang 				     innolux->supplies);
9914c8f2e9SChris Zhong 	if (err < 0)
10014c8f2e9SChris Zhong 		return err;
10114c8f2e9SChris Zhong 
10214c8f2e9SChris Zhong 	innolux->prepared = false;
10314c8f2e9SChris Zhong 
10414c8f2e9SChris Zhong 	return 0;
10514c8f2e9SChris Zhong }
10614c8f2e9SChris Zhong 
10714c8f2e9SChris Zhong static int innolux_panel_prepare(struct drm_panel *panel)
10814c8f2e9SChris Zhong {
10914c8f2e9SChris Zhong 	struct innolux_panel *innolux = to_innolux_panel(panel);
110*7ad4e463SLin Huang 	int err;
11114c8f2e9SChris Zhong 
11214c8f2e9SChris Zhong 	if (innolux->prepared)
11314c8f2e9SChris Zhong 		return 0;
11414c8f2e9SChris Zhong 
11514c8f2e9SChris Zhong 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
11614c8f2e9SChris Zhong 
117*7ad4e463SLin Huang 	err = regulator_bulk_enable(innolux->desc->num_supplies,
118*7ad4e463SLin Huang 				    innolux->supplies);
11914c8f2e9SChris Zhong 	if (err < 0)
12014c8f2e9SChris Zhong 		return err;
12114c8f2e9SChris Zhong 
12214c8f2e9SChris Zhong 	/* T2: 15ms - 1000ms */
12314c8f2e9SChris Zhong 	usleep_range(15000, 16000);
12414c8f2e9SChris Zhong 
12514c8f2e9SChris Zhong 	gpiod_set_value_cansleep(innolux->enable_gpio, 1);
12614c8f2e9SChris Zhong 
12714c8f2e9SChris Zhong 	/* T4: 15ms - 1000ms */
12814c8f2e9SChris Zhong 	usleep_range(15000, 16000);
12914c8f2e9SChris Zhong 
13014c8f2e9SChris Zhong 	err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
13114c8f2e9SChris Zhong 	if (err < 0) {
13214c8f2e9SChris Zhong 		DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
13314c8f2e9SChris Zhong 			      err);
13414c8f2e9SChris Zhong 		goto poweroff;
13514c8f2e9SChris Zhong 	}
13614c8f2e9SChris Zhong 
13714c8f2e9SChris Zhong 	/* T6: 120ms - 1000ms*/
13814c8f2e9SChris Zhong 	msleep(120);
13914c8f2e9SChris Zhong 
14014c8f2e9SChris Zhong 	err = mipi_dsi_dcs_set_display_on(innolux->link);
14114c8f2e9SChris Zhong 	if (err < 0) {
14214c8f2e9SChris Zhong 		DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
14314c8f2e9SChris Zhong 			      err);
14414c8f2e9SChris Zhong 		goto poweroff;
14514c8f2e9SChris Zhong 	}
14614c8f2e9SChris Zhong 
14714c8f2e9SChris Zhong 	/* T7: 5ms */
14814c8f2e9SChris Zhong 	usleep_range(5000, 6000);
14914c8f2e9SChris Zhong 
15014c8f2e9SChris Zhong 	innolux->prepared = true;
15114c8f2e9SChris Zhong 
15214c8f2e9SChris Zhong 	return 0;
15314c8f2e9SChris Zhong 
15414c8f2e9SChris Zhong poweroff:
15514c8f2e9SChris Zhong 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
156*7ad4e463SLin Huang 	regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);
157*7ad4e463SLin Huang 
15814c8f2e9SChris Zhong 	return err;
15914c8f2e9SChris Zhong }
16014c8f2e9SChris Zhong 
16114c8f2e9SChris Zhong static int innolux_panel_enable(struct drm_panel *panel)
16214c8f2e9SChris Zhong {
16314c8f2e9SChris Zhong 	struct innolux_panel *innolux = to_innolux_panel(panel);
16414c8f2e9SChris Zhong 	int ret;
16514c8f2e9SChris Zhong 
16614c8f2e9SChris Zhong 	if (innolux->enabled)
16714c8f2e9SChris Zhong 		return 0;
16814c8f2e9SChris Zhong 
169d593bfdbSMeghana Madhyastha 	ret = backlight_enable(innolux->backlight);
17014c8f2e9SChris Zhong 	if (ret) {
17114c8f2e9SChris Zhong 		DRM_DEV_ERROR(panel->drm->dev,
17214c8f2e9SChris Zhong 			      "Failed to enable backlight %d\n", ret);
17314c8f2e9SChris Zhong 		return ret;
17414c8f2e9SChris Zhong 	}
17514c8f2e9SChris Zhong 
17614c8f2e9SChris Zhong 	innolux->enabled = true;
17714c8f2e9SChris Zhong 
17814c8f2e9SChris Zhong 	return 0;
17914c8f2e9SChris Zhong }
18014c8f2e9SChris Zhong 
181*7ad4e463SLin Huang static const char * const innolux_p079zca_supply_names[] = {
182*7ad4e463SLin Huang 	"power",
183*7ad4e463SLin Huang };
184*7ad4e463SLin Huang 
185*7ad4e463SLin Huang static const struct drm_display_mode innolux_p079zca_mode = {
18614c8f2e9SChris Zhong 	.clock = 56900,
18714c8f2e9SChris Zhong 	.hdisplay = 768,
18814c8f2e9SChris Zhong 	.hsync_start = 768 + 40,
18914c8f2e9SChris Zhong 	.hsync_end = 768 + 40 + 40,
19014c8f2e9SChris Zhong 	.htotal = 768 + 40 + 40 + 40,
19114c8f2e9SChris Zhong 	.vdisplay = 1024,
19214c8f2e9SChris Zhong 	.vsync_start = 1024 + 20,
19314c8f2e9SChris Zhong 	.vsync_end = 1024 + 20 + 4,
19414c8f2e9SChris Zhong 	.vtotal = 1024 + 20 + 4 + 20,
19514c8f2e9SChris Zhong 	.vrefresh = 60,
19614c8f2e9SChris Zhong };
19714c8f2e9SChris Zhong 
198*7ad4e463SLin Huang static const struct panel_desc innolux_p079zca_panel_desc = {
199*7ad4e463SLin Huang 	.mode = &innolux_p079zca_mode,
200*7ad4e463SLin Huang 	.bpc = 8,
201*7ad4e463SLin Huang 	.size = {
202*7ad4e463SLin Huang 		.width = 120,
203*7ad4e463SLin Huang 		.height = 160,
204*7ad4e463SLin Huang 	},
205*7ad4e463SLin Huang 	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
206*7ad4e463SLin Huang 		 MIPI_DSI_MODE_LPM,
207*7ad4e463SLin Huang 	.format = MIPI_DSI_FMT_RGB888,
208*7ad4e463SLin Huang 	.lanes = 4,
209*7ad4e463SLin Huang 	.supply_names = innolux_p079zca_supply_names,
210*7ad4e463SLin Huang 	.num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names),
211*7ad4e463SLin Huang };
212*7ad4e463SLin Huang 
21314c8f2e9SChris Zhong static int innolux_panel_get_modes(struct drm_panel *panel)
21414c8f2e9SChris Zhong {
215*7ad4e463SLin Huang 	struct innolux_panel *innolux = to_innolux_panel(panel);
216*7ad4e463SLin Huang 	const struct drm_display_mode *m = innolux->desc->mode;
21714c8f2e9SChris Zhong 	struct drm_display_mode *mode;
21814c8f2e9SChris Zhong 
219*7ad4e463SLin Huang 	mode = drm_mode_duplicate(panel->drm, m);
22014c8f2e9SChris Zhong 	if (!mode) {
22114c8f2e9SChris Zhong 		DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
222*7ad4e463SLin Huang 			      m->hdisplay, m->vdisplay, m->vrefresh);
22314c8f2e9SChris Zhong 		return -ENOMEM;
22414c8f2e9SChris Zhong 	}
22514c8f2e9SChris Zhong 
22614c8f2e9SChris Zhong 	drm_mode_set_name(mode);
22714c8f2e9SChris Zhong 
22814c8f2e9SChris Zhong 	drm_mode_probed_add(panel->connector, mode);
22914c8f2e9SChris Zhong 
230*7ad4e463SLin Huang 	panel->connector->display_info.width_mm =
231*7ad4e463SLin Huang 			innolux->desc->size.width;
232*7ad4e463SLin Huang 	panel->connector->display_info.height_mm =
233*7ad4e463SLin Huang 			innolux->desc->size.height;
234*7ad4e463SLin Huang 	panel->connector->display_info.bpc = innolux->desc->bpc;
23514c8f2e9SChris Zhong 
23614c8f2e9SChris Zhong 	return 1;
23714c8f2e9SChris Zhong }
23814c8f2e9SChris Zhong 
23914c8f2e9SChris Zhong static const struct drm_panel_funcs innolux_panel_funcs = {
24014c8f2e9SChris Zhong 	.disable = innolux_panel_disable,
24114c8f2e9SChris Zhong 	.unprepare = innolux_panel_unprepare,
24214c8f2e9SChris Zhong 	.prepare = innolux_panel_prepare,
24314c8f2e9SChris Zhong 	.enable = innolux_panel_enable,
24414c8f2e9SChris Zhong 	.get_modes = innolux_panel_get_modes,
24514c8f2e9SChris Zhong };
24614c8f2e9SChris Zhong 
24714c8f2e9SChris Zhong static const struct of_device_id innolux_of_match[] = {
248*7ad4e463SLin Huang 	{ .compatible = "innolux,p079zca",
249*7ad4e463SLin Huang 	  .data = &innolux_p079zca_panel_desc
250*7ad4e463SLin Huang 	},
25114c8f2e9SChris Zhong 	{ }
25214c8f2e9SChris Zhong };
25314c8f2e9SChris Zhong MODULE_DEVICE_TABLE(of, innolux_of_match);
25414c8f2e9SChris Zhong 
255*7ad4e463SLin Huang static int innolux_panel_add(struct mipi_dsi_device *dsi,
256*7ad4e463SLin Huang 			     const struct panel_desc *desc)
25714c8f2e9SChris Zhong {
258*7ad4e463SLin Huang 	struct innolux_panel *innolux;
259*7ad4e463SLin Huang 	struct device *dev = &dsi->dev;
260*7ad4e463SLin Huang 	int err, i;
26114c8f2e9SChris Zhong 
262*7ad4e463SLin Huang 	innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL);
263*7ad4e463SLin Huang 	if (!innolux)
264*7ad4e463SLin Huang 		return -ENOMEM;
265*7ad4e463SLin Huang 
266*7ad4e463SLin Huang 	innolux->desc = desc;
267*7ad4e463SLin Huang 
268*7ad4e463SLin Huang 	innolux->supplies = devm_kcalloc(dev, desc->num_supplies,
269*7ad4e463SLin Huang 					 sizeof(*innolux->supplies),
270*7ad4e463SLin Huang 					 GFP_KERNEL);
271*7ad4e463SLin Huang 	if (!innolux->supplies)
272*7ad4e463SLin Huang 		return -ENOMEM;
273*7ad4e463SLin Huang 
274*7ad4e463SLin Huang 	for (i = 0; i < desc->num_supplies; i++)
275*7ad4e463SLin Huang 		innolux->supplies[i].supply = desc->supply_names[i];
276*7ad4e463SLin Huang 
277*7ad4e463SLin Huang 	err = devm_regulator_bulk_get(dev, desc->num_supplies,
278*7ad4e463SLin Huang 				      innolux->supplies);
279*7ad4e463SLin Huang 	if (err < 0)
280*7ad4e463SLin Huang 		return err;
28114c8f2e9SChris Zhong 
28214c8f2e9SChris Zhong 	innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
28314c8f2e9SChris Zhong 						       GPIOD_OUT_HIGH);
28414c8f2e9SChris Zhong 	if (IS_ERR(innolux->enable_gpio)) {
28514c8f2e9SChris Zhong 		err = PTR_ERR(innolux->enable_gpio);
28614c8f2e9SChris Zhong 		dev_dbg(dev, "failed to get enable gpio: %d\n", err);
28714c8f2e9SChris Zhong 		innolux->enable_gpio = NULL;
28814c8f2e9SChris Zhong 	}
28914c8f2e9SChris Zhong 
290c69f9457SMeghana Madhyastha 	innolux->backlight = devm_of_find_backlight(dev);
291c69f9457SMeghana Madhyastha 	if (IS_ERR(innolux->backlight))
292c69f9457SMeghana Madhyastha 		return PTR_ERR(innolux->backlight);
29314c8f2e9SChris Zhong 
29414c8f2e9SChris Zhong 	drm_panel_init(&innolux->base);
29514c8f2e9SChris Zhong 	innolux->base.funcs = &innolux_panel_funcs;
296*7ad4e463SLin Huang 	innolux->base.dev = dev;
29714c8f2e9SChris Zhong 
298*7ad4e463SLin Huang 	err = drm_panel_add(&innolux->base);
299*7ad4e463SLin Huang 	if (err < 0)
300*7ad4e463SLin Huang 		return err;
301*7ad4e463SLin Huang 
302*7ad4e463SLin Huang 	mipi_dsi_set_drvdata(dsi, innolux);
303*7ad4e463SLin Huang 	innolux->link = dsi;
304*7ad4e463SLin Huang 
305*7ad4e463SLin Huang 	return 0;
30614c8f2e9SChris Zhong }
30714c8f2e9SChris Zhong 
30814c8f2e9SChris Zhong static void innolux_panel_del(struct innolux_panel *innolux)
30914c8f2e9SChris Zhong {
31014c8f2e9SChris Zhong 	if (innolux->base.dev)
31114c8f2e9SChris Zhong 		drm_panel_remove(&innolux->base);
31214c8f2e9SChris Zhong }
31314c8f2e9SChris Zhong 
31414c8f2e9SChris Zhong static int innolux_panel_probe(struct mipi_dsi_device *dsi)
31514c8f2e9SChris Zhong {
316*7ad4e463SLin Huang 	const struct panel_desc *desc;
317*7ad4e463SLin Huang 	const struct of_device_id *id;
31814c8f2e9SChris Zhong 	int err;
31914c8f2e9SChris Zhong 
320*7ad4e463SLin Huang 	id = of_match_node(innolux_of_match, dsi->dev.of_node);
321*7ad4e463SLin Huang 	if (!id)
322*7ad4e463SLin Huang 		return -ENODEV;
32314c8f2e9SChris Zhong 
324*7ad4e463SLin Huang 	desc = id->data;
325*7ad4e463SLin Huang 	dsi->mode_flags = desc->flags;
326*7ad4e463SLin Huang 	dsi->format = desc->format;
327*7ad4e463SLin Huang 	dsi->lanes = desc->lanes;
32814c8f2e9SChris Zhong 
329*7ad4e463SLin Huang 	err = innolux_panel_add(dsi, desc);
33014c8f2e9SChris Zhong 	if (err < 0)
33114c8f2e9SChris Zhong 		return err;
33214c8f2e9SChris Zhong 
333*7ad4e463SLin Huang 	return mipi_dsi_attach(dsi);
33414c8f2e9SChris Zhong }
33514c8f2e9SChris Zhong 
33614c8f2e9SChris Zhong static int innolux_panel_remove(struct mipi_dsi_device *dsi)
33714c8f2e9SChris Zhong {
33814c8f2e9SChris Zhong 	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
33914c8f2e9SChris Zhong 	int err;
34014c8f2e9SChris Zhong 
34114c8f2e9SChris Zhong 	err = innolux_panel_unprepare(&innolux->base);
34214c8f2e9SChris Zhong 	if (err < 0)
34314c8f2e9SChris Zhong 		DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
34414c8f2e9SChris Zhong 			      err);
34514c8f2e9SChris Zhong 
34614c8f2e9SChris Zhong 	err = innolux_panel_disable(&innolux->base);
34714c8f2e9SChris Zhong 	if (err < 0)
34814c8f2e9SChris Zhong 		DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
34914c8f2e9SChris Zhong 
35014c8f2e9SChris Zhong 	err = mipi_dsi_detach(dsi);
35114c8f2e9SChris Zhong 	if (err < 0)
35214c8f2e9SChris Zhong 		DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
35314c8f2e9SChris Zhong 			      err);
35414c8f2e9SChris Zhong 
35514c8f2e9SChris Zhong 	innolux_panel_del(innolux);
35614c8f2e9SChris Zhong 
35714c8f2e9SChris Zhong 	return 0;
35814c8f2e9SChris Zhong }
35914c8f2e9SChris Zhong 
36014c8f2e9SChris Zhong static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
36114c8f2e9SChris Zhong {
36214c8f2e9SChris Zhong 	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
36314c8f2e9SChris Zhong 
36414c8f2e9SChris Zhong 	innolux_panel_unprepare(&innolux->base);
36514c8f2e9SChris Zhong 	innolux_panel_disable(&innolux->base);
36614c8f2e9SChris Zhong }
36714c8f2e9SChris Zhong 
36814c8f2e9SChris Zhong static struct mipi_dsi_driver innolux_panel_driver = {
36914c8f2e9SChris Zhong 	.driver = {
37014c8f2e9SChris Zhong 		.name = "panel-innolux-p079zca",
37114c8f2e9SChris Zhong 		.of_match_table = innolux_of_match,
37214c8f2e9SChris Zhong 	},
37314c8f2e9SChris Zhong 	.probe = innolux_panel_probe,
37414c8f2e9SChris Zhong 	.remove = innolux_panel_remove,
37514c8f2e9SChris Zhong 	.shutdown = innolux_panel_shutdown,
37614c8f2e9SChris Zhong };
37714c8f2e9SChris Zhong module_mipi_dsi_driver(innolux_panel_driver);
37814c8f2e9SChris Zhong 
37914c8f2e9SChris Zhong MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
380*7ad4e463SLin Huang MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
38114c8f2e9SChris Zhong MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
38214c8f2e9SChris Zhong MODULE_LICENSE("GPL v2");
383