brintos

brintos / linux-shallow public Read only

0
0
Text · 14.3 KiB · 6b18cf0 Raw
703 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (c) 2019 Theobroma Systems Design und Consulting GmbH4 *5 * base on panel-kingdisplay-kd097d04.c6 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd7 */8 9#include <linux/backlight.h>10#include <linux/delay.h>11#include <linux/gpio/consumer.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_device.h>15#include <linux/regulator/consumer.h>16 17#include <video/mipi_display.h>18 19#include <drm/drm_crtc.h>20#include <drm/drm_device.h>21#include <drm/drm_mipi_dsi.h>22#include <drm/drm_modes.h>23#include <drm/drm_panel.h>24 25struct ltk500hd1829_cmd {26	char cmd;27	char data;28};29 30struct ltk500hd1829_desc {31	const struct drm_display_mode *mode;32	const struct ltk500hd1829_cmd *init;33	unsigned int num_init;34};35 36struct ltk500hd1829 {37	struct device *dev;38	struct drm_panel panel;39	struct gpio_desc *reset_gpio;40	struct regulator *vcc;41	struct regulator *iovcc;42	const struct ltk500hd1829_desc *panel_desc;43};44 45static const struct ltk500hd1829_cmd ltk101b4029w_init[] = {46	/* Page0 */47	{ 0xE0, 0x00 },48	/* PASSWORD */49	{ 0xE1, 0x93 },50	{ 0xE2, 0x65 },51	{ 0xE3, 0xF8 },52	{ 0x80, 0x03 }, /* 0X03:4-LANE; 0X02:3-LANE; 0X01:2-LANE */53	/* Page1 */54	{ 0xE0, 0x01 },55	/* Set VCOM */56	{ 0x00, 0x00 },57	{ 0x01, 0x6F },58	/* Set Gamma Power, VGMP,VGMN,VGSP,VGSN */59	{ 0x17, 0x00 },60	{ 0x18, 0xAF }, /* 4.3V */61	{ 0x19, 0x01 }, /* 0.3V */62	{ 0x1A, 0x00 },63	{ 0x1B, 0xAF }, /* 4.3V */64	{ 0x1C, 0x01 }, /* 0.3V */65	/* Set Gate Power */66	{ 0x1F, 0x3E }, /* VGH_R  = 15V */67	{ 0x20, 0x28 }, /* VGL_R  = -12V */68	{ 0x21, 0x28 }, /* VGL_R2 = -12V */69	{ 0x22, 0x7E },70	/* SETPANEL */71	{ 0x35, 0x26 },72	{ 0x37, 0x09 },73	/* SET RGBCYC */74	{ 0x38, 0x04 },75	{ 0x39, 0x00 },76	{ 0x3A, 0x01 },77	{ 0x3C, 0x7C },78	{ 0x3D, 0xFF },79	{ 0x3E, 0xFF },80	{ 0x3F, 0x7F },81	/* Set TCON */82	{ 0x40, 0x06 }, /* RSO = 800 RGB */83	{ 0x41, 0xA0 }, /* LN = 640->1280 line */84	{ 0x42, 0x81 },85	{ 0x43, 0x08 }, /* VFP = 8 */86	{ 0x44, 0x0B }, /* VBP = 12 */87	{ 0x45, 0x28 }, /* HBP = 40 */88	/* power voltage */89	{ 0x55, 0x0F }, /* DCDCM = 0001, JD PWR_IC */90	{ 0x57, 0x69 },91	{ 0x59, 0x0A }, /* VCL = -2.9V */92	{ 0x5A, 0x28 }, /* VGH = 15V */93	{ 0x5B, 0x14 }, /* VGL = -11V */94	/* Gamma */95	{ 0x5D, 0x7C },96	{ 0x5E, 0x65 },97	{ 0x5F, 0x55 },98	{ 0x60, 0x47 },99	{ 0x61, 0x43 },100	{ 0x62, 0x32 },101	{ 0x63, 0x34 },102	{ 0x64, 0x1C },103	{ 0x65, 0x33 },104	{ 0x66, 0x31 },105	{ 0x67, 0x30 },106	{ 0x68, 0x4E },107	{ 0x69, 0x3C },108	{ 0x6A, 0x44 },109	{ 0x6B, 0x35 },110	{ 0x6C, 0x31 },111	{ 0x6D, 0x23 },112	{ 0x6E, 0x11 },113	{ 0x6F, 0x00 },114	{ 0x70, 0x7C },115	{ 0x71, 0x65 },116	{ 0x72, 0x55 },117	{ 0x73, 0x47 },118	{ 0x74, 0x43 },119	{ 0x75, 0x32 },120	{ 0x76, 0x34 },121	{ 0x77, 0x1C },122	{ 0x78, 0x33 },123	{ 0x79, 0x31 },124	{ 0x7A, 0x30 },125	{ 0x7B, 0x4E },126	{ 0x7C, 0x3C },127	{ 0x7D, 0x44 },128	{ 0x7E, 0x35 },129	{ 0x7F, 0x31 },130	{ 0x80, 0x23 },131	{ 0x81, 0x11 },132	{ 0x82, 0x00 },133	 /* Page2, for GIP */134	{ 0xE0, 0x02 },135	/* GIP_L Pin mapping */136	{ 0x00, 0x1E },137	{ 0x01, 0x1E },138	{ 0x02, 0x41 },139	{ 0x03, 0x41 },140	{ 0x04, 0x43 },141	{ 0x05, 0x43 },142	{ 0x06, 0x1F },143	{ 0x07, 0x1F },144	{ 0x08, 0x35 },145	{ 0x09, 0x1F },146	{ 0x0A, 0x15 },147	{ 0x0B, 0x15 },148	{ 0x0C, 0x1F },149	{ 0x0D, 0x47 },150	{ 0x0E, 0x47 },151	{ 0x0F, 0x45 },152	{ 0x10, 0x45 },153	{ 0x11, 0x4B },154	{ 0x12, 0x4B },155	{ 0x13, 0x49 },156	{ 0x14, 0x49 },157	{ 0x15, 0x1F },158	/* GIP_R Pin mapping */159	{ 0x16, 0x1E },160	{ 0x17, 0x1E },161	{ 0x18, 0x40 },162	{ 0x19, 0x40 },163	{ 0x1A, 0x42 },164	{ 0x1B, 0x42 },165	{ 0x1C, 0x1F },166	{ 0x1D, 0x1F },167	{ 0x1E, 0x35 },168	{ 0x1F, 0x1F },169	{ 0x20, 0x15 },170	{ 0x21, 0x15 },171	{ 0x22, 0x1f },172	{ 0x23, 0x46 },173	{ 0x24, 0x46 },174	{ 0x25, 0x44 },175	{ 0x26, 0x44 },176	{ 0x27, 0x4A },177	{ 0x28, 0x4A },178	{ 0x29, 0x48 },179	{ 0x2A, 0x48 },180	{ 0x2B, 0x1F },181	/* GIP Timing */182	{ 0x58, 0x40 },183	{ 0x5B, 0x30 },184	{ 0x5C, 0x03 },185	{ 0x5D, 0x30 },186	{ 0x5E, 0x01 },187	{ 0x5F, 0x02 },188	{ 0x63, 0x14 },189	{ 0x64, 0x6A },190	{ 0x67, 0x73 },191	{ 0x68, 0x05 },192	{ 0x69, 0x14 },193	{ 0x6A, 0x6A },194	{ 0x6B, 0x08 },195	{ 0x6C, 0x00 },196	{ 0x6D, 0x00 },197	{ 0x6E, 0x00 },198	{ 0x6F, 0x88 },199	{ 0x77, 0xDD },200	{ 0x79, 0x0E },201	{ 0x7A, 0x03 },202	{ 0x7D, 0x14 },203	{ 0x7E, 0x6A },204	/* Page4 */205	{ 0xE0, 0x04 },206	{ 0x09, 0x11 },207	{ 0x0E, 0x48 },208	{ 0x2B, 0x2B },209	{ 0x2D, 0x03 },210	{ 0x2E, 0x44 },211	/* Page0 */212	{ 0xE0, 0x00 },213	{ 0xE6, 0x02 },214	{ 0xE7, 0x0C },215};216 217static const struct drm_display_mode ltk101b4029w_mode = {218	.hdisplay	= 800,219	.hsync_start	= 800 + 18,220	.hsync_end	= 800 + 18 + 18,221	.htotal		= 800 + 18 + 18 + 18,222	.vdisplay	= 1280,223	.vsync_start	= 1280 + 24,224	.vsync_end	= 1280 + 24 + 4,225	.vtotal		= 1280 + 24 + 4 + 8,226	.clock		= 67330,227	.width_mm	= 136,228	.height_mm	= 218,229};230 231static const struct ltk500hd1829_desc ltk101b4029w_data = {232	.mode = &ltk101b4029w_mode,233	.init = ltk101b4029w_init,234	.num_init = ARRAY_SIZE(ltk101b4029w_init),235};236 237/*238 * There is no description in the Reference Manual about these commands.239 * We received them from the vendor, so just use them as is.240 */241static const struct ltk500hd1829_cmd ltk500hd1829_init[] = {242	{ 0xE0, 0x00 },243	{ 0xE1, 0x93 },244	{ 0xE2, 0x65 },245	{ 0xE3, 0xF8 },246	{ 0x80, 0x03 },247	{ 0xE0, 0x04 },248	{ 0x2D, 0x03 },249	{ 0xE0, 0x01 },250	{ 0x00, 0x00 },251	{ 0x01, 0xB6 },252	{ 0x03, 0x00 },253	{ 0x04, 0xC5 },254	{ 0x17, 0x00 },255	{ 0x18, 0xBF },256	{ 0x19, 0x01 },257	{ 0x1A, 0x00 },258	{ 0x1B, 0xBF },259	{ 0x1C, 0x01 },260	{ 0x1F, 0x7C },261	{ 0x20, 0x26 },262	{ 0x21, 0x26 },263	{ 0x22, 0x4E },264	{ 0x37, 0x09 },265	{ 0x38, 0x04 },266	{ 0x39, 0x08 },267	{ 0x3A, 0x1F },268	{ 0x3B, 0x1F },269	{ 0x3C, 0x78 },270	{ 0x3D, 0xFF },271	{ 0x3E, 0xFF },272	{ 0x3F, 0x00 },273	{ 0x40, 0x04 },274	{ 0x41, 0xA0 },275	{ 0x43, 0x0F },276	{ 0x44, 0x0A },277	{ 0x45, 0x24 },278	{ 0x55, 0x01 },279	{ 0x56, 0x01 },280	{ 0x57, 0xA5 },281	{ 0x58, 0x0A },282	{ 0x59, 0x4A },283	{ 0x5A, 0x38 },284	{ 0x5B, 0x10 },285	{ 0x5C, 0x19 },286	{ 0x5D, 0x7C },287	{ 0x5E, 0x64 },288	{ 0x5F, 0x54 },289	{ 0x60, 0x48 },290	{ 0x61, 0x44 },291	{ 0x62, 0x35 },292	{ 0x63, 0x3A },293	{ 0x64, 0x24 },294	{ 0x65, 0x3B },295	{ 0x66, 0x39 },296	{ 0x67, 0x37 },297	{ 0x68, 0x56 },298	{ 0x69, 0x41 },299	{ 0x6A, 0x47 },300	{ 0x6B, 0x2F },301	{ 0x6C, 0x23 },302	{ 0x6D, 0x13 },303	{ 0x6E, 0x02 },304	{ 0x6F, 0x08 },305	{ 0x70, 0x7C },306	{ 0x71, 0x64 },307	{ 0x72, 0x54 },308	{ 0x73, 0x48 },309	{ 0x74, 0x44 },310	{ 0x75, 0x35 },311	{ 0x76, 0x3A },312	{ 0x77, 0x22 },313	{ 0x78, 0x3B },314	{ 0x79, 0x39 },315	{ 0x7A, 0x38 },316	{ 0x7B, 0x52 },317	{ 0x7C, 0x41 },318	{ 0x7D, 0x47 },319	{ 0x7E, 0x2F },320	{ 0x7F, 0x23 },321	{ 0x80, 0x13 },322	{ 0x81, 0x02 },323	{ 0x82, 0x08 },324	{ 0xE0, 0x02 },325	{ 0x00, 0x57 },326	{ 0x01, 0x77 },327	{ 0x02, 0x44 },328	{ 0x03, 0x46 },329	{ 0x04, 0x48 },330	{ 0x05, 0x4A },331	{ 0x06, 0x4C },332	{ 0x07, 0x4E },333	{ 0x08, 0x50 },334	{ 0x09, 0x55 },335	{ 0x0A, 0x52 },336	{ 0x0B, 0x55 },337	{ 0x0C, 0x55 },338	{ 0x0D, 0x55 },339	{ 0x0E, 0x55 },340	{ 0x0F, 0x55 },341	{ 0x10, 0x55 },342	{ 0x11, 0x55 },343	{ 0x12, 0x55 },344	{ 0x13, 0x40 },345	{ 0x14, 0x55 },346	{ 0x15, 0x55 },347	{ 0x16, 0x57 },348	{ 0x17, 0x77 },349	{ 0x18, 0x45 },350	{ 0x19, 0x47 },351	{ 0x1A, 0x49 },352	{ 0x1B, 0x4B },353	{ 0x1C, 0x4D },354	{ 0x1D, 0x4F },355	{ 0x1E, 0x51 },356	{ 0x1F, 0x55 },357	{ 0x20, 0x53 },358	{ 0x21, 0x55 },359	{ 0x22, 0x55 },360	{ 0x23, 0x55 },361	{ 0x24, 0x55 },362	{ 0x25, 0x55 },363	{ 0x26, 0x55 },364	{ 0x27, 0x55 },365	{ 0x28, 0x55 },366	{ 0x29, 0x41 },367	{ 0x2A, 0x55 },368	{ 0x2B, 0x55 },369	{ 0x2C, 0x57 },370	{ 0x2D, 0x77 },371	{ 0x2E, 0x4F },372	{ 0x2F, 0x4D },373	{ 0x30, 0x4B },374	{ 0x31, 0x49 },375	{ 0x32, 0x47 },376	{ 0x33, 0x45 },377	{ 0x34, 0x41 },378	{ 0x35, 0x55 },379	{ 0x36, 0x53 },380	{ 0x37, 0x55 },381	{ 0x38, 0x55 },382	{ 0x39, 0x55 },383	{ 0x3A, 0x55 },384	{ 0x3B, 0x55 },385	{ 0x3C, 0x55 },386	{ 0x3D, 0x55 },387	{ 0x3E, 0x55 },388	{ 0x3F, 0x51 },389	{ 0x40, 0x55 },390	{ 0x41, 0x55 },391	{ 0x42, 0x57 },392	{ 0x43, 0x77 },393	{ 0x44, 0x4E },394	{ 0x45, 0x4C },395	{ 0x46, 0x4A },396	{ 0x47, 0x48 },397	{ 0x48, 0x46 },398	{ 0x49, 0x44 },399	{ 0x4A, 0x40 },400	{ 0x4B, 0x55 },401	{ 0x4C, 0x52 },402	{ 0x4D, 0x55 },403	{ 0x4E, 0x55 },404	{ 0x4F, 0x55 },405	{ 0x50, 0x55 },406	{ 0x51, 0x55 },407	{ 0x52, 0x55 },408	{ 0x53, 0x55 },409	{ 0x54, 0x55 },410	{ 0x55, 0x50 },411	{ 0x56, 0x55 },412	{ 0x57, 0x55 },413	{ 0x58, 0x40 },414	{ 0x59, 0x00 },415	{ 0x5A, 0x00 },416	{ 0x5B, 0x10 },417	{ 0x5C, 0x09 },418	{ 0x5D, 0x30 },419	{ 0x5E, 0x01 },420	{ 0x5F, 0x02 },421	{ 0x60, 0x30 },422	{ 0x61, 0x03 },423	{ 0x62, 0x04 },424	{ 0x63, 0x06 },425	{ 0x64, 0x6A },426	{ 0x65, 0x75 },427	{ 0x66, 0x0F },428	{ 0x67, 0xB3 },429	{ 0x68, 0x0B },430	{ 0x69, 0x06 },431	{ 0x6A, 0x6A },432	{ 0x6B, 0x10 },433	{ 0x6C, 0x00 },434	{ 0x6D, 0x04 },435	{ 0x6E, 0x04 },436	{ 0x6F, 0x88 },437	{ 0x70, 0x00 },438	{ 0x71, 0x00 },439	{ 0x72, 0x06 },440	{ 0x73, 0x7B },441	{ 0x74, 0x00 },442	{ 0x75, 0xBC },443	{ 0x76, 0x00 },444	{ 0x77, 0x05 },445	{ 0x78, 0x2E },446	{ 0x79, 0x00 },447	{ 0x7A, 0x00 },448	{ 0x7B, 0x00 },449	{ 0x7C, 0x00 },450	{ 0x7D, 0x03 },451	{ 0x7E, 0x7B },452	{ 0xE0, 0x04 },453	{ 0x09, 0x10 },454	{ 0x2B, 0x2B },455	{ 0x2E, 0x44 },456	{ 0xE0, 0x00 },457	{ 0xE6, 0x02 },458	{ 0xE7, 0x02 },459	{ 0x35, 0x00 },460};461 462static const struct drm_display_mode ltk500hd1829_mode = {463	.hdisplay	= 720,464	.hsync_start	= 720 + 50,465	.hsync_end	= 720 + 50 + 50,466	.htotal		= 720 + 50 + 50 + 50,467	.vdisplay	= 1280,468	.vsync_start	= 1280 + 30,469	.vsync_end	= 1280 + 30 + 4,470	.vtotal		= 1280 + 30 + 4 + 12,471	.clock		= 69217,472	.width_mm	= 62,473	.height_mm	= 110,474};475 476static const struct ltk500hd1829_desc ltk500hd1829_data = {477	.mode = &ltk500hd1829_mode,478	.init = ltk500hd1829_init,479	.num_init = ARRAY_SIZE(ltk500hd1829_init),480};481 482static inline483struct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel)484{485	return container_of(panel, struct ltk500hd1829, panel);486}487 488static int ltk500hd1829_unprepare(struct drm_panel *panel)489{490	struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);491	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);492	int ret;493 494	ret = mipi_dsi_dcs_set_display_off(dsi);495	if (ret < 0)496		dev_err(panel->dev, "failed to set display off: %d\n", ret);497 498	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);499	if (ret < 0) {500		dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);501	}502 503	/* 120ms to enter sleep mode */504	msleep(120);505 506	regulator_disable(ctx->iovcc);507	regulator_disable(ctx->vcc);508 509	return 0;510}511 512static int ltk500hd1829_prepare(struct drm_panel *panel)513{514	struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);515	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);516	unsigned int i;517	int ret;518 519	ret = regulator_enable(ctx->vcc);520	if (ret < 0) {521		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);522		return ret;523	}524	ret = regulator_enable(ctx->iovcc);525	if (ret < 0) {526		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);527		goto disable_vcc;528	}529 530	gpiod_set_value_cansleep(ctx->reset_gpio, 1);531	/* tRW: 10us */532	usleep_range(10, 20);533	gpiod_set_value_cansleep(ctx->reset_gpio, 0);534 535	/* tRT: >= 5ms */536	usleep_range(5000, 6000);537 538	for (i = 0; i < ctx->panel_desc->num_init; i++) {539		ret = mipi_dsi_generic_write(dsi, &ctx->panel_desc->init[i],540					     sizeof(struct ltk500hd1829_cmd));541		if (ret < 0) {542			dev_err(panel->dev, "failed to write init cmds: %d\n", ret);543			goto disable_iovcc;544		}545	}546 547	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);548	if (ret < 0) {549		dev_err(panel->dev, "failed to exit sleep mode: %d\n", ret);550		goto disable_iovcc;551	}552 553	/* 120ms to exit sleep mode */554	msleep(120);555 556	ret = mipi_dsi_dcs_set_display_on(dsi);557	if (ret < 0) {558		dev_err(panel->dev, "failed to set display on: %d\n", ret);559		goto disable_iovcc;560	}561 562	return 0;563 564disable_iovcc:565	regulator_disable(ctx->iovcc);566disable_vcc:567	regulator_disable(ctx->vcc);568	return ret;569}570 571static int ltk500hd1829_get_modes(struct drm_panel *panel,572				  struct drm_connector *connector)573{574	struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);575	struct drm_display_mode *mode;576 577	mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);578	if (!mode) {579		dev_err(ctx->dev, "failed to add mode %ux%u@%u\n",580			ctx->panel_desc->mode->hdisplay, ctx->panel_desc->mode->vdisplay,581			drm_mode_vrefresh(ctx->panel_desc->mode));582		return -ENOMEM;583	}584 585	drm_mode_set_name(mode);586 587	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;588	connector->display_info.width_mm = mode->width_mm;589	connector->display_info.height_mm = mode->height_mm;590	drm_mode_probed_add(connector, mode);591 592	return 1;593}594 595static const struct drm_panel_funcs ltk500hd1829_funcs = {596	.unprepare = ltk500hd1829_unprepare,597	.prepare = ltk500hd1829_prepare,598	.get_modes = ltk500hd1829_get_modes,599};600 601static int ltk500hd1829_probe(struct mipi_dsi_device *dsi)602{603	struct ltk500hd1829 *ctx;604	struct device *dev = &dsi->dev;605	int ret;606 607	ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);608	if (!ctx)609		return -ENOMEM;610 611	ctx->panel_desc = of_device_get_match_data(dev);612	if (!ctx->panel_desc)613		return -EINVAL;614 615	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);616	if (IS_ERR(ctx->reset_gpio)) {617		dev_err(dev, "cannot get reset gpio\n");618		return PTR_ERR(ctx->reset_gpio);619	}620 621	ctx->vcc = devm_regulator_get(dev, "vcc");622	if (IS_ERR(ctx->vcc)) {623		ret = PTR_ERR(ctx->vcc);624		if (ret != -EPROBE_DEFER)625			dev_err(dev, "Failed to request vcc regulator: %d\n", ret);626		return ret;627	}628 629	ctx->iovcc = devm_regulator_get(dev, "iovcc");630	if (IS_ERR(ctx->iovcc)) {631		ret = PTR_ERR(ctx->iovcc);632		if (ret != -EPROBE_DEFER)633			dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);634		return ret;635	}636 637	mipi_dsi_set_drvdata(dsi, ctx);638 639	ctx->dev = dev;640 641	dsi->lanes = 4;642	dsi->format = MIPI_DSI_FMT_RGB888;643	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |644			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;645 646	drm_panel_init(&ctx->panel, &dsi->dev, &ltk500hd1829_funcs,647		       DRM_MODE_CONNECTOR_DSI);648 649	ret = drm_panel_of_backlight(&ctx->panel);650	if (ret)651		return ret;652 653	drm_panel_add(&ctx->panel);654 655	ret = mipi_dsi_attach(dsi);656	if (ret < 0) {657		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);658		drm_panel_remove(&ctx->panel);659		return ret;660	}661 662	return 0;663}664 665static void ltk500hd1829_remove(struct mipi_dsi_device *dsi)666{667	struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi);668	int ret;669 670	ret = mipi_dsi_detach(dsi);671	if (ret < 0)672		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);673 674	drm_panel_remove(&ctx->panel);675}676 677static const struct of_device_id ltk500hd1829_of_match[] = {678	{679		.compatible = "leadtek,ltk101b4029w",680		.data = &ltk101b4029w_data,681	},682	{683		.compatible = "leadtek,ltk500hd1829",684		.data = &ltk500hd1829_data,685	},686	{ /* sentinel */ }687};688MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match);689 690static struct mipi_dsi_driver ltk500hd1829_driver = {691	.driver = {692		.name = "panel-leadtek-ltk500hd1829",693		.of_match_table = ltk500hd1829_of_match,694	},695	.probe = ltk500hd1829_probe,696	.remove = ltk500hd1829_remove,697};698module_mipi_dsi_driver(ltk500hd1829_driver);699 700MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");701MODULE_DESCRIPTION("Leadtek LTK500HD1829 panel driver");702MODULE_LICENSE("GPL v2");703