1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Panels based on the Ilitek ILI9882T display controller. 4 */ 5 #include <linux/delay.h> 6 #include <linux/gpio/consumer.h> 7 #include <linux/module.h> 8 #include <linux/of.h> 9 #include <linux/regulator/consumer.h> 10 11 #include <drm/drm_connector.h> 12 #include <drm/drm_crtc.h> 13 #include <drm/drm_mipi_dsi.h> 14 #include <drm/drm_panel.h> 15 16 #include <video/mipi_display.h> 17 18 /* 19 * Use this descriptor struct to describe different panels using the 20 * Ilitek ILI9882T display controller. 21 */ 22 struct panel_desc { 23 const struct drm_display_mode *modes; 24 unsigned int bpc; 25 26 /** 27 * @width_mm: width of the panel's active display area 28 * @height_mm: height of the panel's active display area 29 */ 30 struct { 31 unsigned int width_mm; 32 unsigned int height_mm; 33 } size; 34 35 unsigned long mode_flags; 36 enum mipi_dsi_pixel_format format; 37 const struct panel_init_cmd *init_cmds; 38 unsigned int lanes; 39 }; 40 41 struct ili9882t { 42 struct drm_panel base; 43 struct mipi_dsi_device *dsi; 44 45 const struct panel_desc *desc; 46 47 enum drm_panel_orientation orientation; 48 struct regulator *pp3300; 49 struct regulator *pp1800; 50 struct regulator *avee; 51 struct regulator *avdd; 52 struct gpio_desc *enable_gpio; 53 }; 54 55 enum dsi_cmd_type { 56 INIT_DCS_CMD, 57 DELAY_CMD, 58 }; 59 60 struct panel_init_cmd { 61 enum dsi_cmd_type type; 62 size_t len; 63 const char *data; 64 }; 65 66 #define _INIT_DCS_CMD(...) { \ 67 .type = INIT_DCS_CMD, \ 68 .len = sizeof((char[]){__VA_ARGS__}), \ 69 .data = (char[]){__VA_ARGS__} } 70 71 #define _INIT_DELAY_CMD(...) { \ 72 .type = DELAY_CMD,\ 73 .len = sizeof((char[]){__VA_ARGS__}), \ 74 .data = (char[]){__VA_ARGS__} } 75 76 /* ILI9882-specific commands, add new commands as you decode them */ 77 #define ILI9882T_DCS_SWITCH_PAGE 0xFF 78 79 #define _INIT_SWITCH_PAGE_CMD(page) \ 80 _INIT_DCS_CMD(ILI9882T_DCS_SWITCH_PAGE, 0x98, 0x82, (page)) 81 82 static const struct panel_init_cmd starry_ili9882t_init_cmd[] = { 83 _INIT_DELAY_CMD(5), 84 _INIT_SWITCH_PAGE_CMD(0x01), 85 _INIT_DCS_CMD(0x00, 0x42), 86 _INIT_DCS_CMD(0x01, 0x11), 87 _INIT_DCS_CMD(0x02, 0x00), 88 _INIT_DCS_CMD(0x03, 0x00), 89 90 _INIT_DCS_CMD(0x04, 0x01), 91 _INIT_DCS_CMD(0x05, 0x11), 92 _INIT_DCS_CMD(0x06, 0x00), 93 _INIT_DCS_CMD(0x07, 0x00), 94 95 _INIT_DCS_CMD(0x08, 0x80), 96 _INIT_DCS_CMD(0x09, 0x81), 97 _INIT_DCS_CMD(0x0A, 0x71), 98 _INIT_DCS_CMD(0x0B, 0x00), 99 100 _INIT_DCS_CMD(0x0C, 0x00), 101 _INIT_DCS_CMD(0x0E, 0x1A), 102 103 _INIT_DCS_CMD(0x24, 0x00), 104 _INIT_DCS_CMD(0x25, 0x00), 105 _INIT_DCS_CMD(0x26, 0x00), 106 _INIT_DCS_CMD(0x27, 0x00), 107 108 _INIT_DCS_CMD(0x2C, 0xD4), 109 _INIT_DCS_CMD(0xB9, 0x40), 110 111 _INIT_DCS_CMD(0xB0, 0x11), 112 113 _INIT_DCS_CMD(0xE6, 0x32), 114 _INIT_DCS_CMD(0xD1, 0x30), 115 116 _INIT_DCS_CMD(0xD6, 0x55), 117 118 _INIT_DCS_CMD(0xD0, 0x01), 119 _INIT_DCS_CMD(0xE3, 0x93), 120 _INIT_DCS_CMD(0xE4, 0x00), 121 _INIT_DCS_CMD(0xE5, 0x80), 122 123 _INIT_DCS_CMD(0x31, 0x07), 124 _INIT_DCS_CMD(0x32, 0x07), 125 _INIT_DCS_CMD(0x33, 0x07), 126 _INIT_DCS_CMD(0x34, 0x07), 127 _INIT_DCS_CMD(0x35, 0x07), 128 _INIT_DCS_CMD(0x36, 0x01), 129 _INIT_DCS_CMD(0x37, 0x00), 130 _INIT_DCS_CMD(0x38, 0x28), 131 _INIT_DCS_CMD(0x39, 0x29), 132 _INIT_DCS_CMD(0x3A, 0x11), 133 _INIT_DCS_CMD(0x3B, 0x13), 134 _INIT_DCS_CMD(0x3C, 0x15), 135 _INIT_DCS_CMD(0x3D, 0x17), 136 _INIT_DCS_CMD(0x3E, 0x09), 137 _INIT_DCS_CMD(0x3F, 0x0D), 138 _INIT_DCS_CMD(0x40, 0x02), 139 _INIT_DCS_CMD(0x41, 0x02), 140 _INIT_DCS_CMD(0x42, 0x02), 141 _INIT_DCS_CMD(0x43, 0x02), 142 _INIT_DCS_CMD(0x44, 0x02), 143 _INIT_DCS_CMD(0x45, 0x02), 144 _INIT_DCS_CMD(0x46, 0x02), 145 146 _INIT_DCS_CMD(0x47, 0x07), 147 _INIT_DCS_CMD(0x48, 0x07), 148 _INIT_DCS_CMD(0x49, 0x07), 149 _INIT_DCS_CMD(0x4A, 0x07), 150 _INIT_DCS_CMD(0x4B, 0x07), 151 _INIT_DCS_CMD(0x4C, 0x01), 152 _INIT_DCS_CMD(0x4D, 0x00), 153 _INIT_DCS_CMD(0x4E, 0x28), 154 _INIT_DCS_CMD(0x4F, 0x29), 155 _INIT_DCS_CMD(0x50, 0x10), 156 _INIT_DCS_CMD(0x51, 0x12), 157 _INIT_DCS_CMD(0x52, 0x14), 158 _INIT_DCS_CMD(0x53, 0x16), 159 _INIT_DCS_CMD(0x54, 0x08), 160 _INIT_DCS_CMD(0x55, 0x0C), 161 _INIT_DCS_CMD(0x56, 0x02), 162 _INIT_DCS_CMD(0x57, 0x02), 163 _INIT_DCS_CMD(0x58, 0x02), 164 _INIT_DCS_CMD(0x59, 0x02), 165 _INIT_DCS_CMD(0x5A, 0x02), 166 _INIT_DCS_CMD(0x5B, 0x02), 167 _INIT_DCS_CMD(0x5C, 0x02), 168 169 _INIT_DCS_CMD(0x61, 0x07), 170 _INIT_DCS_CMD(0x62, 0x07), 171 _INIT_DCS_CMD(0x63, 0x07), 172 _INIT_DCS_CMD(0x64, 0x07), 173 _INIT_DCS_CMD(0x65, 0x07), 174 _INIT_DCS_CMD(0x66, 0x01), 175 _INIT_DCS_CMD(0x67, 0x00), 176 _INIT_DCS_CMD(0x68, 0x28), 177 _INIT_DCS_CMD(0x69, 0x29), 178 _INIT_DCS_CMD(0x6A, 0x16), 179 _INIT_DCS_CMD(0x6B, 0x14), 180 _INIT_DCS_CMD(0x6C, 0x12), 181 _INIT_DCS_CMD(0x6D, 0x10), 182 _INIT_DCS_CMD(0x6E, 0x0C), 183 _INIT_DCS_CMD(0x6F, 0x08), 184 _INIT_DCS_CMD(0x70, 0x02), 185 _INIT_DCS_CMD(0x71, 0x02), 186 _INIT_DCS_CMD(0x72, 0x02), 187 _INIT_DCS_CMD(0x73, 0x02), 188 _INIT_DCS_CMD(0x74, 0x02), 189 _INIT_DCS_CMD(0x75, 0x02), 190 _INIT_DCS_CMD(0x76, 0x02), 191 192 _INIT_DCS_CMD(0x77, 0x07), 193 _INIT_DCS_CMD(0x78, 0x07), 194 _INIT_DCS_CMD(0x79, 0x07), 195 _INIT_DCS_CMD(0x7A, 0x07), 196 _INIT_DCS_CMD(0x7B, 0x07), 197 _INIT_DCS_CMD(0x7C, 0x01), 198 _INIT_DCS_CMD(0x7D, 0x00), 199 _INIT_DCS_CMD(0x7E, 0x28), 200 _INIT_DCS_CMD(0x7F, 0x29), 201 _INIT_DCS_CMD(0x80, 0x17), 202 _INIT_DCS_CMD(0x81, 0x15), 203 _INIT_DCS_CMD(0x82, 0x13), 204 _INIT_DCS_CMD(0x83, 0x11), 205 _INIT_DCS_CMD(0x84, 0x0D), 206 _INIT_DCS_CMD(0x85, 0x09), 207 _INIT_DCS_CMD(0x86, 0x02), 208 _INIT_DCS_CMD(0x87, 0x07), 209 _INIT_DCS_CMD(0x88, 0x07), 210 _INIT_DCS_CMD(0x89, 0x07), 211 _INIT_DCS_CMD(0x8A, 0x07), 212 _INIT_DCS_CMD(0x8B, 0x07), 213 _INIT_DCS_CMD(0x8C, 0x07), 214 215 _INIT_SWITCH_PAGE_CMD(0x02), 216 _INIT_DCS_CMD(0x29, 0x3A), 217 _INIT_DCS_CMD(0x2A, 0x3B), 218 219 _INIT_DCS_CMD(0x06, 0x01), 220 _INIT_DCS_CMD(0x07, 0x01), 221 _INIT_DCS_CMD(0x08, 0x0C), 222 _INIT_DCS_CMD(0x09, 0x44), 223 224 _INIT_DCS_CMD(0x3C, 0x0A), 225 _INIT_DCS_CMD(0x39, 0x11), 226 _INIT_DCS_CMD(0x3D, 0x00), 227 _INIT_DCS_CMD(0x3A, 0x0C), 228 _INIT_DCS_CMD(0x3B, 0x44), 229 230 _INIT_DCS_CMD(0x53, 0x1F), 231 _INIT_DCS_CMD(0x5E, 0x40), 232 _INIT_DCS_CMD(0x84, 0x00), 233 234 _INIT_SWITCH_PAGE_CMD(0x03), 235 _INIT_DCS_CMD(0x20, 0x01), 236 _INIT_DCS_CMD(0x21, 0x3C), 237 _INIT_DCS_CMD(0x22, 0xFA), 238 239 _INIT_SWITCH_PAGE_CMD(0x0A), 240 _INIT_DCS_CMD(0xE0, 0x01), 241 _INIT_DCS_CMD(0xE2, 0x01), 242 _INIT_DCS_CMD(0xE5, 0x91), 243 _INIT_DCS_CMD(0xE6, 0x3C), 244 _INIT_DCS_CMD(0xE7, 0x00), 245 _INIT_DCS_CMD(0xE8, 0xFA), 246 247 _INIT_SWITCH_PAGE_CMD(0x12), 248 _INIT_DCS_CMD(0x87, 0x2C), 249 250 _INIT_SWITCH_PAGE_CMD(0x05), 251 _INIT_DCS_CMD(0x73, 0xE5), 252 _INIT_DCS_CMD(0x7F, 0x6B), 253 _INIT_DCS_CMD(0x6D, 0xA4), 254 _INIT_DCS_CMD(0x79, 0x54), 255 _INIT_DCS_CMD(0x69, 0x97), 256 _INIT_DCS_CMD(0x6A, 0x97), 257 _INIT_DCS_CMD(0xA5, 0x3F), 258 _INIT_DCS_CMD(0x61, 0xDA), 259 _INIT_DCS_CMD(0xA7, 0xF1), 260 _INIT_DCS_CMD(0x5F, 0x01), 261 _INIT_DCS_CMD(0x62, 0x3F), 262 _INIT_DCS_CMD(0x1D, 0x90), 263 _INIT_DCS_CMD(0x86, 0x87), 264 265 _INIT_SWITCH_PAGE_CMD(0x06), 266 _INIT_DCS_CMD(0xC0, 0x80), 267 _INIT_DCS_CMD(0xC1, 0x07), 268 _INIT_DCS_CMD(0xCA, 0x58), 269 _INIT_DCS_CMD(0xCB, 0x02), 270 _INIT_DCS_CMD(0xCE, 0x58), 271 _INIT_DCS_CMD(0xCF, 0x02), 272 _INIT_DCS_CMD(0x67, 0x60), 273 _INIT_DCS_CMD(0x10, 0x00), 274 _INIT_DCS_CMD(0x92, 0x22), 275 _INIT_DCS_CMD(0xD3, 0x08), 276 _INIT_DCS_CMD(0xD6, 0x55), 277 _INIT_DCS_CMD(0xDC, 0x38), 278 279 _INIT_SWITCH_PAGE_CMD(0x08), 280 _INIT_DCS_CMD(0xE0, 0x00, 0x10, 0x2A, 0x4D, 0x61, 0x56, 0x6A, 0x6E, 0x79, 0x76, 0x8F, 0x95, 0x98, 0xAE, 0xAA, 0xB2, 0xBB, 0xCE, 0xC6, 0xBD, 0xD5, 0xE2, 0xE8), 281 _INIT_DCS_CMD(0xE1, 0x00, 0x10, 0x2A, 0x4D, 0x61, 0x56, 0x6A, 0x6E, 0x79, 0x76, 0x8F, 0x95, 0x98, 0xAE, 0xAA, 0xB2, 0xBB, 0xCE, 0xC6, 0xBD, 0xD5, 0xE2, 0xE8), 282 283 _INIT_SWITCH_PAGE_CMD(0x04), 284 _INIT_DCS_CMD(0xBA, 0x81), 285 286 _INIT_SWITCH_PAGE_CMD(0x0C), 287 _INIT_DCS_CMD(0x00, 0x02), 288 _INIT_DCS_CMD(0x01, 0x00), 289 _INIT_DCS_CMD(0x02, 0x03), 290 _INIT_DCS_CMD(0x03, 0x01), 291 _INIT_DCS_CMD(0x04, 0x03), 292 _INIT_DCS_CMD(0x05, 0x02), 293 _INIT_DCS_CMD(0x06, 0x04), 294 _INIT_DCS_CMD(0x07, 0x03), 295 _INIT_DCS_CMD(0x08, 0x03), 296 _INIT_DCS_CMD(0x09, 0x04), 297 _INIT_DCS_CMD(0x0A, 0x04), 298 _INIT_DCS_CMD(0x0B, 0x05), 299 _INIT_DCS_CMD(0x0C, 0x04), 300 _INIT_DCS_CMD(0x0D, 0x06), 301 _INIT_DCS_CMD(0x0E, 0x05), 302 _INIT_DCS_CMD(0x0F, 0x07), 303 _INIT_DCS_CMD(0x10, 0x04), 304 _INIT_DCS_CMD(0x11, 0x08), 305 _INIT_DCS_CMD(0x12, 0x05), 306 _INIT_DCS_CMD(0x13, 0x09), 307 _INIT_DCS_CMD(0x14, 0x05), 308 _INIT_DCS_CMD(0x15, 0x0A), 309 _INIT_DCS_CMD(0x16, 0x06), 310 _INIT_DCS_CMD(0x17, 0x0B), 311 _INIT_DCS_CMD(0x18, 0x05), 312 _INIT_DCS_CMD(0x19, 0x0C), 313 _INIT_DCS_CMD(0x1A, 0x06), 314 _INIT_DCS_CMD(0x1B, 0x0D), 315 _INIT_DCS_CMD(0x1C, 0x06), 316 _INIT_DCS_CMD(0x1D, 0x0E), 317 _INIT_DCS_CMD(0x1E, 0x07), 318 _INIT_DCS_CMD(0x1F, 0x0F), 319 _INIT_DCS_CMD(0x20, 0x06), 320 _INIT_DCS_CMD(0x21, 0x10), 321 _INIT_DCS_CMD(0x22, 0x07), 322 _INIT_DCS_CMD(0x23, 0x11), 323 _INIT_DCS_CMD(0x24, 0x07), 324 _INIT_DCS_CMD(0x25, 0x12), 325 _INIT_DCS_CMD(0x26, 0x08), 326 _INIT_DCS_CMD(0x27, 0x13), 327 _INIT_DCS_CMD(0x28, 0x07), 328 _INIT_DCS_CMD(0x29, 0x14), 329 _INIT_DCS_CMD(0x2A, 0x08), 330 _INIT_DCS_CMD(0x2B, 0x15), 331 _INIT_DCS_CMD(0x2C, 0x08), 332 _INIT_DCS_CMD(0x2D, 0x16), 333 _INIT_DCS_CMD(0x2E, 0x09), 334 _INIT_DCS_CMD(0x2F, 0x17), 335 _INIT_DCS_CMD(0x30, 0x08), 336 _INIT_DCS_CMD(0x31, 0x18), 337 _INIT_DCS_CMD(0x32, 0x09), 338 _INIT_DCS_CMD(0x33, 0x19), 339 _INIT_DCS_CMD(0x34, 0x09), 340 _INIT_DCS_CMD(0x35, 0x1A), 341 _INIT_DCS_CMD(0x36, 0x0A), 342 _INIT_DCS_CMD(0x37, 0x1B), 343 _INIT_DCS_CMD(0x38, 0x0A), 344 _INIT_DCS_CMD(0x39, 0x1C), 345 _INIT_DCS_CMD(0x3A, 0x0A), 346 _INIT_DCS_CMD(0x3B, 0x1D), 347 _INIT_DCS_CMD(0x3C, 0x0A), 348 _INIT_DCS_CMD(0x3D, 0x1E), 349 _INIT_DCS_CMD(0x3E, 0x0A), 350 _INIT_DCS_CMD(0x3F, 0x1F), 351 352 _INIT_SWITCH_PAGE_CMD(0x04), 353 _INIT_DCS_CMD(0xBA, 0x01), 354 355 _INIT_SWITCH_PAGE_CMD(0x0E), 356 _INIT_DCS_CMD(0x02, 0x0C), 357 _INIT_DCS_CMD(0x20, 0x10), 358 _INIT_DCS_CMD(0x25, 0x16), 359 _INIT_DCS_CMD(0x26, 0xE0), 360 _INIT_DCS_CMD(0x27, 0x00), 361 _INIT_DCS_CMD(0x29, 0x71), 362 _INIT_DCS_CMD(0x2A, 0x46), 363 _INIT_DCS_CMD(0x2B, 0x1F), 364 _INIT_DCS_CMD(0x2D, 0xC7), 365 _INIT_DCS_CMD(0x31, 0x02), 366 _INIT_DCS_CMD(0x32, 0xDF), 367 _INIT_DCS_CMD(0x33, 0x5A), 368 _INIT_DCS_CMD(0x34, 0xC0), 369 _INIT_DCS_CMD(0x35, 0x5A), 370 _INIT_DCS_CMD(0x36, 0xC0), 371 _INIT_DCS_CMD(0x38, 0x65), 372 _INIT_DCS_CMD(0x80, 0x3E), 373 _INIT_DCS_CMD(0x81, 0xA0), 374 _INIT_DCS_CMD(0xB0, 0x01), 375 _INIT_DCS_CMD(0xB1, 0xCC), 376 _INIT_DCS_CMD(0xC0, 0x12), 377 _INIT_DCS_CMD(0xC2, 0xCC), 378 _INIT_DCS_CMD(0xC3, 0xCC), 379 _INIT_DCS_CMD(0xC4, 0xCC), 380 _INIT_DCS_CMD(0xC5, 0xCC), 381 _INIT_DCS_CMD(0xC6, 0xCC), 382 _INIT_DCS_CMD(0xC7, 0xCC), 383 _INIT_DCS_CMD(0xC8, 0xCC), 384 _INIT_DCS_CMD(0xC9, 0xCC), 385 _INIT_DCS_CMD(0x30, 0x00), 386 _INIT_DCS_CMD(0x00, 0x81), 387 _INIT_DCS_CMD(0x08, 0x02), 388 _INIT_DCS_CMD(0x09, 0x00), 389 _INIT_DCS_CMD(0x07, 0x21), 390 _INIT_DCS_CMD(0x04, 0x10), 391 392 _INIT_SWITCH_PAGE_CMD(0x1E), 393 _INIT_DCS_CMD(0x60, 0x00), 394 _INIT_DCS_CMD(0x64, 0x00), 395 _INIT_DCS_CMD(0x6D, 0x00), 396 397 _INIT_SWITCH_PAGE_CMD(0x0B), 398 _INIT_DCS_CMD(0xA6, 0x44), 399 _INIT_DCS_CMD(0xA7, 0xB6), 400 _INIT_DCS_CMD(0xA8, 0x03), 401 _INIT_DCS_CMD(0xA9, 0x03), 402 _INIT_DCS_CMD(0xAA, 0x51), 403 _INIT_DCS_CMD(0xAB, 0x51), 404 _INIT_DCS_CMD(0xAC, 0x04), 405 _INIT_DCS_CMD(0xBD, 0x92), 406 _INIT_DCS_CMD(0xBE, 0xA1), 407 408 _INIT_SWITCH_PAGE_CMD(0x05), 409 _INIT_DCS_CMD(0x86, 0x87), 410 411 _INIT_SWITCH_PAGE_CMD(0x06), 412 _INIT_DCS_CMD(0x92, 0x22), 413 414 _INIT_SWITCH_PAGE_CMD(0x00), 415 _INIT_DCS_CMD(MIPI_DCS_EXIT_SLEEP_MODE), 416 _INIT_DELAY_CMD(120), 417 _INIT_DCS_CMD(MIPI_DCS_SET_DISPLAY_ON), 418 _INIT_DELAY_CMD(20), 419 {}, 420 }; 421 422 static inline struct ili9882t *to_ili9882t(struct drm_panel *panel) 423 { 424 return container_of(panel, struct ili9882t, base); 425 } 426 427 static int ili9882t_init_dcs_cmd(struct ili9882t *ili) 428 { 429 struct mipi_dsi_device *dsi = ili->dsi; 430 struct drm_panel *panel = &ili->base; 431 int i, err = 0; 432 433 if (ili->desc->init_cmds) { 434 const struct panel_init_cmd *init_cmds = ili->desc->init_cmds; 435 436 for (i = 0; init_cmds[i].len != 0; i++) { 437 const struct panel_init_cmd *cmd = &init_cmds[i]; 438 439 switch (cmd->type) { 440 case DELAY_CMD: 441 msleep(cmd->data[0]); 442 err = 0; 443 break; 444 445 case INIT_DCS_CMD: 446 err = mipi_dsi_dcs_write(dsi, cmd->data[0], 447 cmd->len <= 1 ? NULL : 448 &cmd->data[1], 449 cmd->len - 1); 450 break; 451 452 default: 453 err = -EINVAL; 454 } 455 456 if (err < 0) { 457 dev_err(panel->dev, 458 "failed to write command %u\n", i); 459 return err; 460 } 461 } 462 } 463 return 0; 464 } 465 466 static int ili9882t_switch_page(struct mipi_dsi_device *dsi, u8 page) 467 { 468 int ret; 469 const struct panel_init_cmd cmd = _INIT_SWITCH_PAGE_CMD(page); 470 471 ret = mipi_dsi_dcs_write(dsi, cmd.data[0], 472 cmd.len <= 1 ? NULL : 473 &cmd.data[1], 474 cmd.len - 1); 475 if (ret) { 476 dev_err(&dsi->dev, 477 "error switching panel controller page (%d)\n", ret); 478 return ret; 479 } 480 481 return 0; 482 } 483 484 static int ili9882t_enter_sleep_mode(struct ili9882t *ili) 485 { 486 struct mipi_dsi_device *dsi = ili->dsi; 487 int ret; 488 489 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 490 491 ret = mipi_dsi_dcs_set_display_off(dsi); 492 if (ret < 0) 493 return ret; 494 495 ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 496 if (ret < 0) 497 return ret; 498 499 return 0; 500 } 501 502 static int ili9882t_disable(struct drm_panel *panel) 503 { 504 struct ili9882t *ili = to_ili9882t(panel); 505 struct mipi_dsi_device *dsi = ili->dsi; 506 int ret; 507 508 ili9882t_switch_page(dsi, 0x00); 509 ret = ili9882t_enter_sleep_mode(ili); 510 if (ret < 0) { 511 dev_err(panel->dev, "failed to set panel off: %d\n", ret); 512 return ret; 513 } 514 515 msleep(150); 516 517 return 0; 518 } 519 520 static int ili9882t_unprepare(struct drm_panel *panel) 521 { 522 struct ili9882t *ili = to_ili9882t(panel); 523 524 gpiod_set_value(ili->enable_gpio, 0); 525 usleep_range(1000, 2000); 526 regulator_disable(ili->avee); 527 regulator_disable(ili->avdd); 528 usleep_range(5000, 7000); 529 regulator_disable(ili->pp1800); 530 regulator_disable(ili->pp3300); 531 532 return 0; 533 } 534 535 static int ili9882t_prepare(struct drm_panel *panel) 536 { 537 struct ili9882t *ili = to_ili9882t(panel); 538 int ret; 539 540 gpiod_set_value(ili->enable_gpio, 0); 541 usleep_range(1000, 1500); 542 543 ret = regulator_enable(ili->pp3300); 544 if (ret < 0) 545 return ret; 546 547 ret = regulator_enable(ili->pp1800); 548 if (ret < 0) 549 return ret; 550 551 usleep_range(3000, 5000); 552 553 ret = regulator_enable(ili->avdd); 554 if (ret < 0) 555 goto poweroff1v8; 556 ret = regulator_enable(ili->avee); 557 if (ret < 0) 558 goto poweroffavdd; 559 560 usleep_range(10000, 11000); 561 562 // MIPI needs to keep the LP11 state before the lcm_reset pin is pulled high 563 mipi_dsi_dcs_nop(ili->dsi); 564 usleep_range(1000, 2000); 565 566 gpiod_set_value(ili->enable_gpio, 1); 567 usleep_range(1000, 2000); 568 gpiod_set_value(ili->enable_gpio, 0); 569 msleep(50); 570 gpiod_set_value(ili->enable_gpio, 1); 571 usleep_range(6000, 10000); 572 573 ret = ili9882t_init_dcs_cmd(ili); 574 if (ret < 0) { 575 dev_err(panel->dev, "failed to init panel: %d\n", ret); 576 goto poweroff; 577 } 578 579 return 0; 580 581 poweroff: 582 regulator_disable(ili->avee); 583 poweroffavdd: 584 regulator_disable(ili->avdd); 585 poweroff1v8: 586 usleep_range(5000, 7000); 587 regulator_disable(ili->pp1800); 588 gpiod_set_value(ili->enable_gpio, 0); 589 590 return ret; 591 } 592 593 static int ili9882t_enable(struct drm_panel *panel) 594 { 595 msleep(130); 596 return 0; 597 } 598 599 static const struct drm_display_mode starry_ili9882t_default_mode = { 600 .clock = 165280, 601 .hdisplay = 1200, 602 .hsync_start = 1200 + 72, 603 .hsync_end = 1200 + 72 + 30, 604 .htotal = 1200 + 72 + 30 + 72, 605 .vdisplay = 1920, 606 .vsync_start = 1920 + 68, 607 .vsync_end = 1920 + 68 + 2, 608 .vtotal = 1920 + 68 + 2 + 10, 609 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 610 }; 611 612 static const struct panel_desc starry_ili9882t_desc = { 613 .modes = &starry_ili9882t_default_mode, 614 .bpc = 8, 615 .size = { 616 .width_mm = 141, 617 .height_mm = 226, 618 }, 619 .lanes = 4, 620 .format = MIPI_DSI_FMT_RGB888, 621 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 622 MIPI_DSI_MODE_LPM, 623 .init_cmds = starry_ili9882t_init_cmd, 624 }; 625 626 static int ili9882t_get_modes(struct drm_panel *panel, 627 struct drm_connector *connector) 628 { 629 struct ili9882t *ili = to_ili9882t(panel); 630 const struct drm_display_mode *m = ili->desc->modes; 631 struct drm_display_mode *mode; 632 633 mode = drm_mode_duplicate(connector->dev, m); 634 if (!mode) { 635 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 636 m->hdisplay, m->vdisplay, drm_mode_vrefresh(m)); 637 return -ENOMEM; 638 } 639 640 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 641 drm_mode_set_name(mode); 642 drm_mode_probed_add(connector, mode); 643 644 connector->display_info.width_mm = ili->desc->size.width_mm; 645 connector->display_info.height_mm = ili->desc->size.height_mm; 646 connector->display_info.bpc = ili->desc->bpc; 647 648 return 1; 649 } 650 651 static enum drm_panel_orientation ili9882t_get_orientation(struct drm_panel *panel) 652 { 653 struct ili9882t *ili = to_ili9882t(panel); 654 655 return ili->orientation; 656 } 657 658 static const struct drm_panel_funcs ili9882t_funcs = { 659 .disable = ili9882t_disable, 660 .unprepare = ili9882t_unprepare, 661 .prepare = ili9882t_prepare, 662 .enable = ili9882t_enable, 663 .get_modes = ili9882t_get_modes, 664 .get_orientation = ili9882t_get_orientation, 665 }; 666 667 static int ili9882t_add(struct ili9882t *ili) 668 { 669 struct device *dev = &ili->dsi->dev; 670 int err; 671 672 ili->avdd = devm_regulator_get(dev, "avdd"); 673 if (IS_ERR(ili->avdd)) 674 return PTR_ERR(ili->avdd); 675 676 ili->avee = devm_regulator_get(dev, "avee"); 677 if (IS_ERR(ili->avee)) 678 return PTR_ERR(ili->avee); 679 680 ili->pp3300 = devm_regulator_get(dev, "pp3300"); 681 if (IS_ERR(ili->pp3300)) 682 return PTR_ERR(ili->pp3300); 683 684 ili->pp1800 = devm_regulator_get(dev, "pp1800"); 685 if (IS_ERR(ili->pp1800)) 686 return PTR_ERR(ili->pp1800); 687 688 ili->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); 689 if (IS_ERR(ili->enable_gpio)) { 690 dev_err(dev, "cannot get reset-gpios %ld\n", 691 PTR_ERR(ili->enable_gpio)); 692 return PTR_ERR(ili->enable_gpio); 693 } 694 695 gpiod_set_value(ili->enable_gpio, 0); 696 697 drm_panel_init(&ili->base, dev, &ili9882t_funcs, 698 DRM_MODE_CONNECTOR_DSI); 699 err = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); 700 if (err < 0) { 701 dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); 702 return err; 703 } 704 705 err = drm_panel_of_backlight(&ili->base); 706 if (err) 707 return err; 708 709 ili->base.funcs = &ili9882t_funcs; 710 ili->base.dev = &ili->dsi->dev; 711 712 drm_panel_add(&ili->base); 713 714 return 0; 715 } 716 717 static int ili9882t_probe(struct mipi_dsi_device *dsi) 718 { 719 struct ili9882t *ili; 720 int ret; 721 const struct panel_desc *desc; 722 723 ili = devm_kzalloc(&dsi->dev, sizeof(*ili), GFP_KERNEL); 724 if (!ili) 725 return -ENOMEM; 726 727 desc = of_device_get_match_data(&dsi->dev); 728 dsi->lanes = desc->lanes; 729 dsi->format = desc->format; 730 dsi->mode_flags = desc->mode_flags; 731 ili->desc = desc; 732 ili->dsi = dsi; 733 ret = ili9882t_add(ili); 734 if (ret < 0) 735 return ret; 736 737 mipi_dsi_set_drvdata(dsi, ili); 738 739 ret = mipi_dsi_attach(dsi); 740 if (ret) 741 drm_panel_remove(&ili->base); 742 743 return ret; 744 } 745 746 static void ili9882t_remove(struct mipi_dsi_device *dsi) 747 { 748 struct ili9882t *ili = mipi_dsi_get_drvdata(dsi); 749 int ret; 750 751 ret = mipi_dsi_detach(dsi); 752 if (ret < 0) 753 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 754 755 if (ili->base.dev) 756 drm_panel_remove(&ili->base); 757 } 758 759 static const struct of_device_id ili9882t_of_match[] = { 760 { .compatible = "starry,ili9882t", 761 .data = &starry_ili9882t_desc 762 }, 763 { /* sentinel */ } 764 }; 765 MODULE_DEVICE_TABLE(of, ili9882t_of_match); 766 767 static struct mipi_dsi_driver ili9882t_driver = { 768 .driver = { 769 .name = "panel-ili9882t", 770 .of_match_table = ili9882t_of_match, 771 }, 772 .probe = ili9882t_probe, 773 .remove = ili9882t_remove, 774 }; 775 module_mipi_dsi_driver(ili9882t_driver); 776 777 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 778 MODULE_DESCRIPTION("Ilitek ILI9882T-based panels driver"); 779 MODULE_LICENSE("GPL"); 780