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 void (*init)(struct mipi_dsi_multi_context *dsi_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 void ltk050h3148w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) 247 { 248 /* 249 * Init sequence was supplied by the panel vendor without much 250 * documentation. 251 */ 252 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0xff, 0x83, 0x94); 253 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44, 254 0x71, 0x31, 0x55, 0x2f); 255 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0); 256 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x88); 257 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07); 258 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70, 259 0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74, 260 0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86); 261 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e, 262 0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54, 263 0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05, 264 0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07, 265 0x17, 0x11, 0x40); 266 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b, 267 0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 268 0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23, 269 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 270 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 271 0x18, 0x18, 0x18, 0x18, 0x18, 0x18); 272 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b, 273 0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 274 0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20, 275 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 276 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 277 0x18, 0x18, 0x18, 0x18, 0x18, 0x18); 278 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14, 279 0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c, 280 0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58, 281 0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00, 282 0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e, 283 0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88, 284 0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b, 285 0x5d, 0x61, 0x65, 0x7f); 286 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcc, 0x0b); 287 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x1f, 0x31); 288 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb6, 0xc4, 0xc4); 289 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x01); 290 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x00); 291 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x00); 292 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc6, 0xef); 293 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x02); 294 295 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1); 296 mipi_dsi_msleep(dsi_ctx, 60); 297 } 298 299 static const struct drm_display_mode ltk050h3148w_mode = { 300 .hdisplay = 720, 301 .hsync_start = 720 + 12, 302 .hsync_end = 720 + 12 + 6, 303 .htotal = 720 + 12 + 6 + 24, 304 .vdisplay = 1280, 305 .vsync_start = 1280 + 9, 306 .vsync_end = 1280 + 9 + 2, 307 .vtotal = 1280 + 9 + 2 + 16, 308 .clock = 59756, 309 .width_mm = 62, 310 .height_mm = 110, 311 }; 312 313 static const struct ltk050h3146w_desc ltk050h3148w_data = { 314 .mode = <k050h3148w_mode, 315 .init = ltk050h3148w_init_sequence, 316 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 317 MIPI_DSI_MODE_VIDEO_BURST, 318 }; 319 320 static void ltk050h3146w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) 321 { 322 /* 323 * Init sequence was supplied by the panel vendor without much 324 * documentation. 325 */ 326 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdf, 0x93, 0x65, 0xf8); 327 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06, 328 0x01); 329 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0xb5); 330 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb3, 0x00, 0xb5); 331 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00); 332 333 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0x00, 0xc4, 0x23, 0x07); 334 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f, 335 0x28, 0x04, 0xcc, 0xcc, 0xcc); 336 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbc, 0x0f, 0x04); 337 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbe, 0x1e, 0xf2); 338 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x26, 0x03); 339 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x00, 0x12); 340 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80, 341 0x80); 342 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f, 343 0x16, 0x00, 0x00); 344 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50, 345 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f, 346 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67, 347 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55, 348 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08); 349 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a, 350 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f, 351 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 352 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b, 353 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f, 354 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 355 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05, 356 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f, 357 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 358 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04, 359 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f, 360 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 361 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20, 362 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03, 363 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08); 364 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00, 365 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05, 366 0x21, 0x00, 0x60); 367 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdd, 0x2c, 0xa3, 0x00); 368 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x02); 369 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x32, 0x1c); 370 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x3b, 0x70, 0x00, 0x04); 371 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x11); 372 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37); 373 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc2, 0x20, 0x38, 0x1e, 0x84); 374 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x00); 375 376 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1); 377 mipi_dsi_msleep(dsi_ctx, 60); 378 } 379 380 static const struct drm_display_mode ltk050h3146w_mode = { 381 .hdisplay = 720, 382 .hsync_start = 720 + 42, 383 .hsync_end = 720 + 42 + 8, 384 .htotal = 720 + 42 + 8 + 42, 385 .vdisplay = 1280, 386 .vsync_start = 1280 + 12, 387 .vsync_end = 1280 + 12 + 4, 388 .vtotal = 1280 + 12 + 4 + 18, 389 .clock = 64018, 390 .width_mm = 62, 391 .height_mm = 110, 392 }; 393 394 static const struct ltk050h3146w_desc ltk050h3146w_data = { 395 .mode = <k050h3146w_mode, 396 .init = ltk050h3146w_init_sequence, 397 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 398 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET, 399 }; 400 401 static void ltk050h3146w_a2_select_page(struct mipi_dsi_multi_context *dsi_ctx, int page) 402 { 403 u8 d[4] = { 0xff, 0x98, 0x81, page }; 404 405 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, d, ARRAY_SIZE(d)); 406 } 407 408 static void ltk050h3146w_a2_write_page(struct mipi_dsi_multi_context *dsi_ctx, int page, 409 const struct ltk050h3146w_cmd *cmds, 410 int num) 411 { 412 ltk050h3146w_a2_select_page(dsi_ctx, page); 413 414 for (int i = 0; i < num; i++) 415 mipi_dsi_generic_write_multi(dsi_ctx, &cmds[i], 416 sizeof(struct ltk050h3146w_cmd)); 417 } 418 419 static void ltk050h3146w_a2_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) 420 { 421 /* 422 * Init sequence was supplied by the panel vendor without much 423 * documentation. 424 */ 425 ltk050h3146w_a2_write_page(dsi_ctx, 3, page3_cmds, 426 ARRAY_SIZE(page3_cmds)); 427 ltk050h3146w_a2_write_page(dsi_ctx, 4, page4_cmds, 428 ARRAY_SIZE(page4_cmds)); 429 ltk050h3146w_a2_write_page(dsi_ctx, 1, page1_cmds, 430 ARRAY_SIZE(page1_cmds)); 431 ltk050h3146w_a2_select_page(dsi_ctx, 0); 432 433 /* vendor code called this without param, where there should be one */ 434 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 0); 435 436 mipi_dsi_msleep(dsi_ctx, 60); 437 } 438 439 static const struct drm_display_mode ltk050h3146w_a2_mode = { 440 .hdisplay = 720, 441 .hsync_start = 720 + 42, 442 .hsync_end = 720 + 42 + 10, 443 .htotal = 720 + 42 + 10 + 60, 444 .vdisplay = 1280, 445 .vsync_start = 1280 + 18, 446 .vsync_end = 1280 + 18 + 4, 447 .vtotal = 1280 + 18 + 4 + 12, 448 .clock = 65595, 449 .width_mm = 62, 450 .height_mm = 110, 451 }; 452 453 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = { 454 .mode = <k050h3146w_a2_mode, 455 .init = ltk050h3146w_a2_init_sequence, 456 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 457 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET, 458 }; 459 460 static int ltk050h3146w_unprepare(struct drm_panel *panel) 461 { 462 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 463 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 464 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 465 466 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 467 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 468 if (dsi_ctx.accum_err) 469 return dsi_ctx.accum_err; 470 471 regulator_disable(ctx->iovcc); 472 regulator_disable(ctx->vci); 473 474 return 0; 475 } 476 477 static int ltk050h3146w_prepare(struct drm_panel *panel) 478 { 479 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 480 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 481 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 482 483 dev_dbg(ctx->dev, "Resetting the panel\n"); 484 dsi_ctx.accum_err = regulator_enable(ctx->vci); 485 if (dsi_ctx.accum_err) { 486 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", dsi_ctx.accum_err); 487 return dsi_ctx.accum_err; 488 } 489 dsi_ctx.accum_err = regulator_enable(ctx->iovcc); 490 if (dsi_ctx.accum_err) { 491 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", dsi_ctx.accum_err); 492 goto disable_vci; 493 } 494 495 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 496 usleep_range(5000, 6000); 497 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 498 msleep(20); 499 500 ctx->panel_desc->init(&dsi_ctx); 501 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 502 /* T9: 120ms */ 503 mipi_dsi_msleep(&dsi_ctx, 120); 504 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 505 mipi_dsi_msleep(&dsi_ctx, 50); 506 507 if (dsi_ctx.accum_err) 508 goto disable_iovcc; 509 510 return 0; 511 512 disable_iovcc: 513 regulator_disable(ctx->iovcc); 514 disable_vci: 515 regulator_disable(ctx->vci); 516 return dsi_ctx.accum_err; 517 } 518 519 static int ltk050h3146w_get_modes(struct drm_panel *panel, 520 struct drm_connector *connector) 521 { 522 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 523 struct drm_display_mode *mode; 524 525 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode); 526 if (!mode) 527 return -ENOMEM; 528 529 drm_mode_set_name(mode); 530 531 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 532 connector->display_info.width_mm = mode->width_mm; 533 connector->display_info.height_mm = mode->height_mm; 534 drm_mode_probed_add(connector, mode); 535 536 return 1; 537 } 538 539 static const struct drm_panel_funcs ltk050h3146w_funcs = { 540 .unprepare = ltk050h3146w_unprepare, 541 .prepare = ltk050h3146w_prepare, 542 .get_modes = ltk050h3146w_get_modes, 543 }; 544 545 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) 546 { 547 struct device *dev = &dsi->dev; 548 struct ltk050h3146w *ctx; 549 int ret; 550 551 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 552 if (!ctx) 553 return -ENOMEM; 554 555 ctx->panel_desc = of_device_get_match_data(dev); 556 if (!ctx->panel_desc) 557 return -EINVAL; 558 559 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 560 if (IS_ERR(ctx->reset_gpio)) 561 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n"); 562 563 ctx->vci = devm_regulator_get(dev, "vci"); 564 if (IS_ERR(ctx->vci)) 565 return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n"); 566 567 ctx->iovcc = devm_regulator_get(dev, "iovcc"); 568 if (IS_ERR(ctx->iovcc)) 569 return dev_err_probe(dev, PTR_ERR(ctx->iovcc), 570 "Failed to request iovcc regulator\n"); 571 572 mipi_dsi_set_drvdata(dsi, ctx); 573 574 ctx->dev = dev; 575 576 dsi->lanes = 4; 577 dsi->format = MIPI_DSI_FMT_RGB888; 578 dsi->mode_flags = ctx->panel_desc->mode_flags; 579 580 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs, 581 DRM_MODE_CONNECTOR_DSI); 582 583 ret = drm_panel_of_backlight(&ctx->panel); 584 if (ret) 585 return ret; 586 587 drm_panel_add(&ctx->panel); 588 589 ret = mipi_dsi_attach(dsi); 590 if (ret < 0) { 591 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 592 drm_panel_remove(&ctx->panel); 593 return ret; 594 } 595 596 return 0; 597 } 598 599 static void ltk050h3146w_remove(struct mipi_dsi_device *dsi) 600 { 601 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 602 int ret; 603 604 ret = mipi_dsi_detach(dsi); 605 if (ret < 0) 606 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 607 608 drm_panel_remove(&ctx->panel); 609 } 610 611 static const struct of_device_id ltk050h3146w_of_match[] = { 612 { 613 .compatible = "leadtek,ltk050h3146w", 614 .data = <k050h3146w_data, 615 }, 616 { 617 .compatible = "leadtek,ltk050h3146w-a2", 618 .data = <k050h3146w_a2_data, 619 }, 620 { 621 .compatible = "leadtek,ltk050h3148w", 622 .data = <k050h3148w_data, 623 }, 624 { /* sentinel */ } 625 }; 626 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match); 627 628 static struct mipi_dsi_driver ltk050h3146w_driver = { 629 .driver = { 630 .name = "panel-leadtek-ltk050h3146w", 631 .of_match_table = ltk050h3146w_of_match, 632 }, 633 .probe = ltk050h3146w_probe, 634 .remove = ltk050h3146w_remove, 635 }; 636 module_mipi_dsi_driver(ltk050h3146w_driver); 637 638 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 639 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel"); 640 MODULE_LICENSE("GPL v2"); 641