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/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 20 struct ltk050h3146w_cmd { 21 char cmd; 22 char data; 23 }; 24 25 struct ltk050h3146w; 26 struct ltk050h3146w_desc { 27 const unsigned long mode_flags; 28 const struct drm_display_mode *mode; 29 int (*init)(struct ltk050h3146w *ctx); 30 }; 31 32 struct 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 41 static 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 95 static 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 225 static 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 240 static inline 241 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel) 242 { 243 return container_of(panel, struct ltk050h3146w, panel); 244 } 245 246 static 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 much 253 * 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 309 static 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 323 static const struct ltk050h3146w_desc ltk050h3148w_data = { 324 .mode = <k050h3148w_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 330 static 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 much 337 * 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 400 static 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 414 static const struct ltk050h3146w_desc ltk050h3146w_data = { 415 .mode = <k050h3146w_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 421 static 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 429 static 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 454 static 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 much 461 * 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 496 static 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 510 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = { 511 .mode = <k050h3146w_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 517 static 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 541 static 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 589 disable_iovcc: 590 regulator_disable(ctx->iovcc); 591 disable_vci: 592 regulator_disable(ctx->vci); 593 return ret; 594 } 595 596 static 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 616 static const struct drm_panel_funcs ltk050h3146w_funcs = { 617 .unprepare = ltk050h3146w_unprepare, 618 .prepare = ltk050h3146w_prepare, 619 .get_modes = ltk050h3146w_get_modes, 620 }; 621 622 static 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, <k050h3146w_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 676 static 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 688 static const struct of_device_id ltk050h3146w_of_match[] = { 689 { 690 .compatible = "leadtek,ltk050h3146w", 691 .data = <k050h3146w_data, 692 }, 693 { 694 .compatible = "leadtek,ltk050h3146w-a2", 695 .data = <k050h3146w_a2_data, 696 }, 697 { 698 .compatible = "leadtek,ltk050h3148w", 699 .data = <k050h3148w_data, 700 }, 701 { /* sentinel */ } 702 }; 703 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match); 704 705 static 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 }; 713 module_mipi_dsi_driver(ltk050h3146w_driver); 714 715 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 716 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel"); 717 MODULE_LICENSE("GPL v2"); 718