brintos

brintos / linux-shallow public Read only

0
0
Text · 18.4 KiB · 292aa26 Raw
718 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH4 */5 6#include <linux/delay.h>7#include <linux/gpio/consumer.h>8#include <linux/media-bus-format.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/regulator/consumer.h>12 13#include <video/display_timing.h>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 20struct ltk050h3146w_cmd {21	char cmd;22	char data;23};24 25struct ltk050h3146w;26struct ltk050h3146w_desc {27	const unsigned long mode_flags;28	const struct drm_display_mode *mode;29	int (*init)(struct ltk050h3146w *ctx);30};31 32struct ltk050h3146w {33	struct device *dev;34	struct drm_panel panel;35	struct gpio_desc *reset_gpio;36	struct regulator *vci;37	struct regulator *iovcc;38	const struct ltk050h3146w_desc *panel_desc;39};40 41static const struct ltk050h3146w_cmd page1_cmds[] = {42	{ 0x22, 0x0A }, /* BGR SS GS */43	{ 0x31, 0x00 }, /* column inversion */44	{ 0x53, 0xA2 }, /* VCOM1 */45	{ 0x55, 0xA2 }, /* VCOM2 */46	{ 0x50, 0x81 }, /* VREG1OUT=5V */47	{ 0x51, 0x85 }, /* VREG2OUT=-5V */48	{ 0x62, 0x0D }, /* EQT Time setting */49/*50 * The vendor init selected page 1 here _again_51 * Is this supposed to be page 2?52 */53	{ 0xA0, 0x00 },54	{ 0xA1, 0x1A },55	{ 0xA2, 0x28 },56	{ 0xA3, 0x13 },57	{ 0xA4, 0x16 },58	{ 0xA5, 0x29 },59	{ 0xA6, 0x1D },60	{ 0xA7, 0x1E },61	{ 0xA8, 0x84 },62	{ 0xA9, 0x1C },63	{ 0xAA, 0x28 },64	{ 0xAB, 0x75 },65	{ 0xAC, 0x1A },66	{ 0xAD, 0x19 },67	{ 0xAE, 0x4D },68	{ 0xAF, 0x22 },69	{ 0xB0, 0x28 },70	{ 0xB1, 0x54 },71	{ 0xB2, 0x66 },72	{ 0xB3, 0x39 },73	{ 0xC0, 0x00 },74	{ 0xC1, 0x1A },75	{ 0xC2, 0x28 },76	{ 0xC3, 0x13 },77	{ 0xC4, 0x16 },78	{ 0xC5, 0x29 },79	{ 0xC6, 0x1D },80	{ 0xC7, 0x1E },81	{ 0xC8, 0x84 },82	{ 0xC9, 0x1C },83	{ 0xCA, 0x28 },84	{ 0xCB, 0x75 },85	{ 0xCC, 0x1A },86	{ 0xCD, 0x19 },87	{ 0xCE, 0x4D },88	{ 0xCF, 0x22 },89	{ 0xD0, 0x28 },90	{ 0xD1, 0x54 },91	{ 0xD2, 0x66 },92	{ 0xD3, 0x39 },93};94 95static const struct ltk050h3146w_cmd page3_cmds[] = {96	{ 0x01, 0x00 },97	{ 0x02, 0x00 },98	{ 0x03, 0x73 },99	{ 0x04, 0x00 },100	{ 0x05, 0x00 },101	{ 0x06, 0x0a },102	{ 0x07, 0x00 },103	{ 0x08, 0x00 },104	{ 0x09, 0x01 },105	{ 0x0a, 0x00 },106	{ 0x0b, 0x00 },107	{ 0x0c, 0x01 },108	{ 0x0d, 0x00 },109	{ 0x0e, 0x00 },110	{ 0x0f, 0x1d },111	{ 0x10, 0x1d },112	{ 0x11, 0x00 },113	{ 0x12, 0x00 },114	{ 0x13, 0x00 },115	{ 0x14, 0x00 },116	{ 0x15, 0x00 },117	{ 0x16, 0x00 },118	{ 0x17, 0x00 },119	{ 0x18, 0x00 },120	{ 0x19, 0x00 },121	{ 0x1a, 0x00 },122	{ 0x1b, 0x00 },123	{ 0x1c, 0x00 },124	{ 0x1d, 0x00 },125	{ 0x1e, 0x40 },126	{ 0x1f, 0x80 },127	{ 0x20, 0x06 },128	{ 0x21, 0x02 },129	{ 0x22, 0x00 },130	{ 0x23, 0x00 },131	{ 0x24, 0x00 },132	{ 0x25, 0x00 },133	{ 0x26, 0x00 },134	{ 0x27, 0x00 },135	{ 0x28, 0x33 },136	{ 0x29, 0x03 },137	{ 0x2a, 0x00 },138	{ 0x2b, 0x00 },139	{ 0x2c, 0x00 },140	{ 0x2d, 0x00 },141	{ 0x2e, 0x00 },142	{ 0x2f, 0x00 },143	{ 0x30, 0x00 },144	{ 0x31, 0x00 },145	{ 0x32, 0x00 },146	{ 0x33, 0x00 },147	{ 0x34, 0x04 },148	{ 0x35, 0x00 },149	{ 0x36, 0x00 },150	{ 0x37, 0x00 },151	{ 0x38, 0x3C },152	{ 0x39, 0x35 },153	{ 0x3A, 0x01 },154	{ 0x3B, 0x40 },155	{ 0x3C, 0x00 },156	{ 0x3D, 0x01 },157	{ 0x3E, 0x00 },158	{ 0x3F, 0x00 },159	{ 0x40, 0x00 },160	{ 0x41, 0x88 },161	{ 0x42, 0x00 },162	{ 0x43, 0x00 },163	{ 0x44, 0x1F },164	{ 0x50, 0x01 },165	{ 0x51, 0x23 },166	{ 0x52, 0x45 },167	{ 0x53, 0x67 },168	{ 0x54, 0x89 },169	{ 0x55, 0xab },170	{ 0x56, 0x01 },171	{ 0x57, 0x23 },172	{ 0x58, 0x45 },173	{ 0x59, 0x67 },174	{ 0x5a, 0x89 },175	{ 0x5b, 0xab },176	{ 0x5c, 0xcd },177	{ 0x5d, 0xef },178	{ 0x5e, 0x11 },179	{ 0x5f, 0x01 },180	{ 0x60, 0x00 },181	{ 0x61, 0x15 },182	{ 0x62, 0x14 },183	{ 0x63, 0x0E },184	{ 0x64, 0x0F },185	{ 0x65, 0x0C },186	{ 0x66, 0x0D },187	{ 0x67, 0x06 },188	{ 0x68, 0x02 },189	{ 0x69, 0x07 },190	{ 0x6a, 0x02 },191	{ 0x6b, 0x02 },192	{ 0x6c, 0x02 },193	{ 0x6d, 0x02 },194	{ 0x6e, 0x02 },195	{ 0x6f, 0x02 },196	{ 0x70, 0x02 },197	{ 0x71, 0x02 },198	{ 0x72, 0x02 },199	{ 0x73, 0x02 },200	{ 0x74, 0x02 },201	{ 0x75, 0x01 },202	{ 0x76, 0x00 },203	{ 0x77, 0x14 },204	{ 0x78, 0x15 },205	{ 0x79, 0x0E },206	{ 0x7a, 0x0F },207	{ 0x7b, 0x0C },208	{ 0x7c, 0x0D },209	{ 0x7d, 0x06 },210	{ 0x7e, 0x02 },211	{ 0x7f, 0x07 },212	{ 0x80, 0x02 },213	{ 0x81, 0x02 },214	{ 0x82, 0x02 },215	{ 0x83, 0x02 },216	{ 0x84, 0x02 },217	{ 0x85, 0x02 },218	{ 0x86, 0x02 },219	{ 0x87, 0x02 },220	{ 0x88, 0x02 },221	{ 0x89, 0x02 },222	{ 0x8A, 0x02 },223};224 225static const struct ltk050h3146w_cmd page4_cmds[] = {226	{ 0x70, 0x00 },227	{ 0x71, 0x00 },228	{ 0x82, 0x0F }, /* VGH_MOD clamp level=15v */229	{ 0x84, 0x0F }, /* VGH clamp level 15V */230	{ 0x85, 0x0D }, /* VGL clamp level (-10V) */231	{ 0x32, 0xAC },232	{ 0x8C, 0x80 },233	{ 0x3C, 0xF5 },234	{ 0xB5, 0x07 }, /* GAMMA OP */235	{ 0x31, 0x45 }, /* SOURCE OP */236	{ 0x3A, 0x24 }, /* PS_EN OFF */237	{ 0x88, 0x33 }, /* LVD */238};239 240static inline241struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)242{243	return container_of(panel, struct ltk050h3146w, panel);244}245 246static int ltk050h3148w_init_sequence(struct ltk050h3146w *ctx)247{248	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);249	int ret;250 251	/*252	 * Init sequence was supplied by the panel vendor without much253	 * documentation.254	 */255	mipi_dsi_dcs_write_seq(dsi, 0xb9, 0xff, 0x83, 0x94);256	mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,257			       0x71, 0x31, 0x55, 0x2f);258	mipi_dsi_dcs_write_seq(dsi, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);259	mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x88);260	mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);261	mipi_dsi_dcs_write_seq(dsi, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,262			       0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,263			       0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);264	mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,265			       0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,266			       0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,267			       0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,268			       0x17, 0x11, 0x40);269	mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,270			       0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,271			       0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,272			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,273			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,274			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18);275	mipi_dsi_dcs_write_seq(dsi, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,276			       0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,277			       0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,278			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,279			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,280			       0x18, 0x18, 0x18, 0x18, 0x18, 0x18);281	mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,282			       0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,283			       0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,284			       0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,285			       0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,286			       0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,287			       0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,288			       0x5d, 0x61, 0x65, 0x7f);289	mipi_dsi_dcs_write_seq(dsi, 0xcc, 0x0b);290	mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x1f, 0x31);291	mipi_dsi_dcs_write_seq(dsi, 0xb6, 0xc4, 0xc4);292	mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);293	mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x00);294	mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);295	mipi_dsi_dcs_write_seq(dsi, 0xc6, 0xef);296	mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x02);297 298	ret = mipi_dsi_dcs_set_tear_on(dsi, 1);299	if (ret < 0) {300		dev_err(ctx->dev, "failed to set tear on: %d\n", ret);301		return ret;302	}303 304	msleep(60);305 306	return 0;307}308 309static const struct drm_display_mode ltk050h3148w_mode = {310	.hdisplay	= 720,311	.hsync_start	= 720 + 12,312	.hsync_end	= 720 + 12 + 6,313	.htotal		= 720 + 12 + 6 + 24,314	.vdisplay	= 1280,315	.vsync_start	= 1280 + 9,316	.vsync_end	= 1280 + 9 + 2,317	.vtotal		= 1280 + 9 + 2 + 16,318	.clock		= 59756,319	.width_mm	= 62,320	.height_mm	= 110,321};322 323static const struct ltk050h3146w_desc ltk050h3148w_data = {324	.mode = &ltk050h3148w_mode,325	.init = ltk050h3148w_init_sequence,326	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |327		      MIPI_DSI_MODE_VIDEO_BURST,328};329 330static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)331{332	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);333	int ret;334 335	/*336	 * Init sequence was supplied by the panel vendor without much337	 * documentation.338	 */339	mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);340	mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,341			       0x01);342	mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);343	mipi_dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);344	mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);345 346	mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);347	mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,348			       0x28, 0x04, 0xcc, 0xcc, 0xcc);349	mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);350	mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);351	mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);352	mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);353	mipi_dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,354			       0x80);355	mipi_dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,356			       0x16, 0x00, 0x00);357	mipi_dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,358			       0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,359			       0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,360			       0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,361			       0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);362	mipi_dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,363			       0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,364			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);365	mipi_dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,366			       0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,367			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);368	mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,369			       0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,370			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);371	mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,372			       0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,373			       0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);374	mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,375			       0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,376			       0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);377	mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,378			       0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,379			       0x21, 0x00, 0x60);380	mipi_dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);381	mipi_dsi_dcs_write_seq(dsi, 0xde, 0x02);382	mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);383	mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);384	mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x11);385	mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);386	mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);387	mipi_dsi_dcs_write_seq(dsi, 0xde, 0x00);388 389	ret = mipi_dsi_dcs_set_tear_on(dsi, 1);390	if (ret < 0) {391		dev_err(ctx->dev, "failed to set tear on: %d\n", ret);392		return ret;393	}394 395	msleep(60);396 397	return 0;398}399 400static const struct drm_display_mode ltk050h3146w_mode = {401	.hdisplay	= 720,402	.hsync_start	= 720 + 42,403	.hsync_end	= 720 + 42 + 8,404	.htotal		= 720 + 42 + 8 + 42,405	.vdisplay	= 1280,406	.vsync_start	= 1280 + 12,407	.vsync_end	= 1280 + 12 + 4,408	.vtotal		= 1280 + 12 + 4 + 18,409	.clock		= 64018,410	.width_mm	= 62,411	.height_mm	= 110,412};413 414static const struct ltk050h3146w_desc ltk050h3146w_data = {415	.mode = &ltk050h3146w_mode,416	.init = ltk050h3146w_init_sequence,417	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |418		MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,419};420 421static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)422{423	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);424	u8 d[3] = { 0x98, 0x81, page };425 426	return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));427}428 429static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,430				      const struct ltk050h3146w_cmd *cmds,431				      int num)432{433	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);434	int i, ret;435 436	ret = ltk050h3146w_a2_select_page(ctx, page);437	if (ret < 0) {438		dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);439		return ret;440	}441 442	for (i = 0; i < num; i++) {443		ret = mipi_dsi_generic_write(dsi, &cmds[i],444					     sizeof(struct ltk050h3146w_cmd));445		if (ret < 0) {446			dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);447			return ret;448		}449	}450 451	return 0;452}453 454static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)455{456	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);457	int ret;458 459	/*460	 * Init sequence was supplied by the panel vendor without much461	 * documentation.462	 */463	ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,464					 ARRAY_SIZE(page3_cmds));465	if (ret < 0)466		return ret;467 468	ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,469					 ARRAY_SIZE(page4_cmds));470	if (ret < 0)471		return ret;472 473	ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,474					 ARRAY_SIZE(page1_cmds));475	if (ret < 0)476		return ret;477 478	ret = ltk050h3146w_a2_select_page(ctx, 0);479	if (ret < 0) {480		dev_err(ctx->dev, "failed to select page 0: %d\n", ret);481		return ret;482	}483 484	/* vendor code called this without param, where there should be one */485	ret = mipi_dsi_dcs_set_tear_on(dsi, 0);486	if (ret < 0) {487		dev_err(ctx->dev, "failed to set tear on: %d\n", ret);488		return ret;489	}490 491	msleep(60);492 493	return 0;494}495 496static const struct drm_display_mode ltk050h3146w_a2_mode = {497	.hdisplay	= 720,498	.hsync_start	= 720 + 42,499	.hsync_end	= 720 + 42 + 10,500	.htotal		= 720 + 42 + 10 + 60,501	.vdisplay	= 1280,502	.vsync_start	= 1280 + 18,503	.vsync_end	= 1280 + 18 + 4,504	.vtotal		= 1280 + 18 + 4 + 12,505	.clock		= 65595,506	.width_mm	= 62,507	.height_mm	= 110,508};509 510static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {511	.mode = &ltk050h3146w_a2_mode,512	.init = ltk050h3146w_a2_init_sequence,513	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |514		MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,515};516 517static int ltk050h3146w_unprepare(struct drm_panel *panel)518{519	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);520	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);521	int ret;522 523	ret = mipi_dsi_dcs_set_display_off(dsi);524	if (ret < 0) {525		dev_err(ctx->dev, "failed to set display off: %d\n", ret);526		return ret;527	}528 529	mipi_dsi_dcs_enter_sleep_mode(dsi);530	if (ret < 0) {531		dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);532		return ret;533	}534 535	regulator_disable(ctx->iovcc);536	regulator_disable(ctx->vci);537 538	return 0;539}540 541static int ltk050h3146w_prepare(struct drm_panel *panel)542{543	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);544	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);545	int ret;546 547	dev_dbg(ctx->dev, "Resetting the panel\n");548	ret = regulator_enable(ctx->vci);549	if (ret < 0) {550		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);551		return ret;552	}553	ret = regulator_enable(ctx->iovcc);554	if (ret < 0) {555		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);556		goto disable_vci;557	}558 559	gpiod_set_value_cansleep(ctx->reset_gpio, 1);560	usleep_range(5000, 6000);561	gpiod_set_value_cansleep(ctx->reset_gpio, 0);562	msleep(20);563 564	ret = ctx->panel_desc->init(ctx);565	if (ret < 0) {566		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);567		goto disable_iovcc;568	}569 570	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);571	if (ret < 0) {572		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);573		goto disable_iovcc;574	}575 576	/* T9: 120ms */577	msleep(120);578 579	ret = mipi_dsi_dcs_set_display_on(dsi);580	if (ret < 0) {581		dev_err(ctx->dev, "Failed to set display on: %d\n", ret);582		goto disable_iovcc;583	}584 585	msleep(50);586 587	return 0;588 589disable_iovcc:590	regulator_disable(ctx->iovcc);591disable_vci:592	regulator_disable(ctx->vci);593	return ret;594}595 596static int ltk050h3146w_get_modes(struct drm_panel *panel,597				  struct drm_connector *connector)598{599	struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);600	struct drm_display_mode *mode;601 602	mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);603	if (!mode)604		return -ENOMEM;605 606	drm_mode_set_name(mode);607 608	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;609	connector->display_info.width_mm = mode->width_mm;610	connector->display_info.height_mm = mode->height_mm;611	drm_mode_probed_add(connector, mode);612 613	return 1;614}615 616static const struct drm_panel_funcs ltk050h3146w_funcs = {617	.unprepare	= ltk050h3146w_unprepare,618	.prepare	= ltk050h3146w_prepare,619	.get_modes	= ltk050h3146w_get_modes,620};621 622static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)623{624	struct device *dev = &dsi->dev;625	struct ltk050h3146w *ctx;626	int ret;627 628	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);629	if (!ctx)630		return -ENOMEM;631 632	ctx->panel_desc = of_device_get_match_data(dev);633	if (!ctx->panel_desc)634		return -EINVAL;635 636	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);637	if (IS_ERR(ctx->reset_gpio))638		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n");639 640	ctx->vci = devm_regulator_get(dev, "vci");641	if (IS_ERR(ctx->vci))642		return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n");643 644	ctx->iovcc = devm_regulator_get(dev, "iovcc");645	if (IS_ERR(ctx->iovcc))646		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),647				     "Failed to request iovcc regulator\n");648 649	mipi_dsi_set_drvdata(dsi, ctx);650 651	ctx->dev = dev;652 653	dsi->lanes = 4;654	dsi->format = MIPI_DSI_FMT_RGB888;655	dsi->mode_flags = ctx->panel_desc->mode_flags;656 657	drm_panel_init(&ctx->panel, &dsi->dev, &ltk050h3146w_funcs,658		       DRM_MODE_CONNECTOR_DSI);659 660	ret = drm_panel_of_backlight(&ctx->panel);661	if (ret)662		return ret;663 664	drm_panel_add(&ctx->panel);665 666	ret = mipi_dsi_attach(dsi);667	if (ret < 0) {668		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);669		drm_panel_remove(&ctx->panel);670		return ret;671	}672 673	return 0;674}675 676static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)677{678	struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);679	int ret;680 681	ret = mipi_dsi_detach(dsi);682	if (ret < 0)683		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);684 685	drm_panel_remove(&ctx->panel);686}687 688static const struct of_device_id ltk050h3146w_of_match[] = {689	{690		.compatible = "leadtek,ltk050h3146w",691		.data = &ltk050h3146w_data,692	},693	{694		.compatible = "leadtek,ltk050h3146w-a2",695		.data = &ltk050h3146w_a2_data,696	},697	{698		.compatible = "leadtek,ltk050h3148w",699		.data = &ltk050h3148w_data,700	},701	{ /* sentinel */ }702};703MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);704 705static struct mipi_dsi_driver ltk050h3146w_driver = {706	.driver = {707		.name = "panel-leadtek-ltk050h3146w",708		.of_match_table = ltk050h3146w_of_match,709	},710	.probe	= ltk050h3146w_probe,711	.remove = ltk050h3146w_remove,712};713module_mipi_dsi_driver(ltk050h3146w_driver);714 715MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");716MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");717MODULE_LICENSE("GPL v2");718