cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

panel-leadtek-ltk050h3146w.c (16032B)


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