1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * NewVision NV3052C IPS LCD panel driver 4 * 5 * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> 6 * Copyright (C) 2022, Christophe Branchereau <cbranchereau@gmail.com> 7 */ 8 9 #include <linux/delay.h> 10 #include <linux/device.h> 11 #include <linux/gpio/consumer.h> 12 #include <linux/media-bus-format.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/regulator/consumer.h> 17 #include <linux/spi/spi.h> 18 #include <video/mipi_display.h> 19 #include <drm/drm_mipi_dbi.h> 20 #include <drm/drm_modes.h> 21 #include <drm/drm_panel.h> 22 23 struct nv3052c_reg { 24 u8 cmd; 25 u8 val; 26 }; 27 28 struct nv3052c_panel_info { 29 const struct drm_display_mode *display_modes; 30 unsigned int num_modes; 31 u16 width_mm, height_mm; 32 u32 bus_format, bus_flags; 33 const struct nv3052c_reg *panel_regs; 34 unsigned int panel_regs_len; 35 }; 36 37 struct nv3052c { 38 struct device *dev; 39 struct drm_panel panel; 40 struct mipi_dbi dbi; 41 const struct nv3052c_panel_info *panel_info; 42 struct regulator *supply; 43 struct gpio_desc *reset_gpio; 44 }; 45 46 /* 47 * Common initialization registers for all currently 48 * supported displays. Mostly seem to be related 49 * to Gamma correction curves and output pad mappings. 50 */ 51 static const struct nv3052c_reg common_init_regs[] = { 52 // EXTC Command set enable, select page 2 53 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 54 // Set gray scale voltage to adjust gamma 55 { 0xb0, 0x0b }, // PGAMVR0 56 { 0xb1, 0x16 }, // PGAMVR1 57 { 0xb2, 0x17 }, // PGAMVR2 58 { 0xb3, 0x2c }, // PGAMVR3 59 { 0xb4, 0x32 }, // PGAMVR4 60 { 0xb5, 0x3b }, // PGAMVR5 61 { 0xb6, 0x29 }, // PGAMPR0 62 { 0xb7, 0x40 }, // PGAMPR1 63 { 0xb8, 0x0d }, // PGAMPK0 64 { 0xb9, 0x05 }, // PGAMPK1 65 { 0xba, 0x12 }, // PGAMPK2 66 { 0xbb, 0x10 }, // PGAMPK3 67 { 0xbc, 0x12 }, // PGAMPK4 68 { 0xbd, 0x15 }, // PGAMPK5 69 { 0xbe, 0x19 }, // PGAMPK6 70 { 0xbf, 0x0e }, // PGAMPK7 71 { 0xc0, 0x16 }, // PGAMPK8 72 { 0xc1, 0x0a }, // PGAMPK9 73 // Set gray scale voltage to adjust gamma 74 { 0xd0, 0x0c }, // NGAMVR0 75 { 0xd1, 0x17 }, // NGAMVR0 76 { 0xd2, 0x14 }, // NGAMVR1 77 { 0xd3, 0x2e }, // NGAMVR2 78 { 0xd4, 0x32 }, // NGAMVR3 79 { 0xd5, 0x3c }, // NGAMVR4 80 { 0xd6, 0x22 }, // NGAMPR0 81 { 0xd7, 0x3d }, // NGAMPR1 82 { 0xd8, 0x0d }, // NGAMPK0 83 { 0xd9, 0x07 }, // NGAMPK1 84 { 0xda, 0x13 }, // NGAMPK2 85 { 0xdb, 0x13 }, // NGAMPK3 86 { 0xdc, 0x11 }, // NGAMPK4 87 { 0xdd, 0x15 }, // NGAMPK5 88 { 0xde, 0x19 }, // NGAMPK6 89 { 0xdf, 0x10 }, // NGAMPK7 90 { 0xe0, 0x17 }, // NGAMPK8 91 { 0xe1, 0x0a }, // NGAMPK9 92 // EXTC Command set enable, select page 3 93 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 }, 94 // Set various timing settings 95 { 0x00, 0x2a }, // GIP_VST_1 96 { 0x01, 0x2a }, // GIP_VST_2 97 { 0x02, 0x2a }, // GIP_VST_3 98 { 0x03, 0x2a }, // GIP_VST_4 99 { 0x04, 0x61 }, // GIP_VST_5 100 { 0x05, 0x80 }, // GIP_VST_6 101 { 0x06, 0xc7 }, // GIP_VST_7 102 { 0x07, 0x01 }, // GIP_VST_8 103 { 0x08, 0x03 }, // GIP_VST_9 104 { 0x09, 0x04 }, // GIP_VST_10 105 { 0x70, 0x22 }, // GIP_ECLK1 106 { 0x71, 0x80 }, // GIP_ECLK2 107 { 0x30, 0x2a }, // GIP_CLK_1 108 { 0x31, 0x2a }, // GIP_CLK_2 109 { 0x32, 0x2a }, // GIP_CLK_3 110 { 0x33, 0x2a }, // GIP_CLK_4 111 { 0x34, 0x61 }, // GIP_CLK_5 112 { 0x35, 0xc5 }, // GIP_CLK_6 113 { 0x36, 0x80 }, // GIP_CLK_7 114 { 0x37, 0x23 }, // GIP_CLK_8 115 { 0x40, 0x03 }, // GIP_CLKA_1 116 { 0x41, 0x04 }, // GIP_CLKA_2 117 { 0x42, 0x05 }, // GIP_CLKA_3 118 { 0x43, 0x06 }, // GIP_CLKA_4 119 { 0x44, 0x11 }, // GIP_CLKA_5 120 { 0x45, 0xe8 }, // GIP_CLKA_6 121 { 0x46, 0xe9 }, // GIP_CLKA_7 122 { 0x47, 0x11 }, // GIP_CLKA_8 123 { 0x48, 0xea }, // GIP_CLKA_9 124 { 0x49, 0xeb }, // GIP_CLKA_10 125 { 0x50, 0x07 }, // GIP_CLKB_1 126 { 0x51, 0x08 }, // GIP_CLKB_2 127 { 0x52, 0x09 }, // GIP_CLKB_3 128 { 0x53, 0x0a }, // GIP_CLKB_4 129 { 0x54, 0x11 }, // GIP_CLKB_5 130 { 0x55, 0xec }, // GIP_CLKB_6 131 { 0x56, 0xed }, // GIP_CLKB_7 132 { 0x57, 0x11 }, // GIP_CLKB_8 133 { 0x58, 0xef }, // GIP_CLKB_9 134 { 0x59, 0xf0 }, // GIP_CLKB_10 135 // Map internal GOA signals to GOA output pad 136 { 0xb1, 0x01 }, // PANELD2U2 137 { 0xb4, 0x15 }, // PANELD2U5 138 { 0xb5, 0x16 }, // PANELD2U6 139 { 0xb6, 0x09 }, // PANELD2U7 140 { 0xb7, 0x0f }, // PANELD2U8 141 { 0xb8, 0x0d }, // PANELD2U9 142 { 0xb9, 0x0b }, // PANELD2U10 143 { 0xba, 0x00 }, // PANELD2U11 144 { 0xc7, 0x02 }, // PANELD2U24 145 { 0xca, 0x17 }, // PANELD2U27 146 { 0xcb, 0x18 }, // PANELD2U28 147 { 0xcc, 0x0a }, // PANELD2U29 148 { 0xcd, 0x10 }, // PANELD2U30 149 { 0xce, 0x0e }, // PANELD2U31 150 { 0xcf, 0x0c }, // PANELD2U32 151 { 0xd0, 0x00 }, // PANELD2U33 152 // Map internal GOA signals to GOA output pad 153 { 0x81, 0x00 }, // PANELU2D2 154 { 0x84, 0x15 }, // PANELU2D5 155 { 0x85, 0x16 }, // PANELU2D6 156 { 0x86, 0x10 }, // PANELU2D7 157 { 0x87, 0x0a }, // PANELU2D8 158 { 0x88, 0x0c }, // PANELU2D9 159 { 0x89, 0x0e }, // PANELU2D10 160 { 0x8a, 0x02 }, // PANELU2D11 161 { 0x97, 0x00 }, // PANELU2D24 162 { 0x9a, 0x17 }, // PANELU2D27 163 { 0x9b, 0x18 }, // PANELU2D28 164 { 0x9c, 0x0f }, // PANELU2D29 165 { 0x9d, 0x09 }, // PANELU2D30 166 { 0x9e, 0x0b }, // PANELU2D31 167 { 0x9f, 0x0d }, // PANELU2D32 168 { 0xa0, 0x01 }, // PANELU2D33 169 // EXTC Command set enable, select page 2 170 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 171 // Page 2 register values (0x01..0x10) are same for nv3051d and nv3052c 172 { 0x01, 0x01 }, 173 { 0x02, 0xda }, 174 { 0x03, 0xba }, 175 { 0x04, 0xa8 }, 176 { 0x05, 0x9a }, 177 { 0x06, 0x70 }, 178 { 0x07, 0xff }, 179 { 0x08, 0x91 }, 180 { 0x09, 0x90 }, 181 { 0x0a, 0xff }, 182 { 0x0b, 0x8f }, 183 { 0x0c, 0x60 }, 184 { 0x0d, 0x58 }, 185 { 0x0e, 0x48 }, 186 { 0x0f, 0x38 }, 187 { 0x10, 0x2b }, 188 // EXTC Command set enable, select page 0 189 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 }, 190 // Display Access Control 191 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0 192 193 }; 194 195 static const struct nv3052c_reg ltk035c5444t_panel_regs[] = { 196 // EXTC Command set enable, select page 1 197 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, 198 // Mostly unknown registers 199 { 0xe3, 0x00 }, 200 { 0x40, 0x00 }, 201 { 0x03, 0x40 }, 202 { 0x04, 0x00 }, 203 { 0x05, 0x03 }, 204 { 0x08, 0x00 }, 205 { 0x09, 0x07 }, 206 { 0x0a, 0x01 }, 207 { 0x0b, 0x32 }, 208 { 0x0c, 0x32 }, 209 { 0x0d, 0x0b }, 210 { 0x0e, 0x00 }, 211 { 0x23, 0xa0 }, 212 { 0x24, 0x0c }, 213 { 0x25, 0x06 }, 214 { 0x26, 0x14 }, 215 { 0x27, 0x14 }, 216 { 0x38, 0xcc }, // VCOM_ADJ1 217 { 0x39, 0xd7 }, // VCOM_ADJ2 218 { 0x3a, 0x4a }, // VCOM_ADJ3 219 { 0x28, 0x40 }, 220 { 0x29, 0x01 }, 221 { 0x2a, 0xdf }, 222 { 0x49, 0x3c }, 223 { 0x91, 0x77 }, // EXTPW_CTRL2 224 { 0x92, 0x77 }, // EXTPW_CTRL3 225 { 0xa0, 0x55 }, 226 { 0xa1, 0x50 }, 227 { 0xa4, 0x9c }, 228 { 0xa7, 0x02 }, 229 { 0xa8, 0x01 }, 230 { 0xa9, 0x01 }, 231 { 0xaa, 0xfc }, 232 { 0xab, 0x28 }, 233 { 0xac, 0x06 }, 234 { 0xad, 0x06 }, 235 { 0xae, 0x06 }, 236 { 0xaf, 0x03 }, 237 { 0xb0, 0x08 }, 238 { 0xb1, 0x26 }, 239 { 0xb2, 0x28 }, 240 { 0xb3, 0x28 }, 241 { 0xb4, 0x33 }, 242 { 0xb5, 0x08 }, 243 { 0xb6, 0x26 }, 244 { 0xb7, 0x08 }, 245 { 0xb8, 0x26 }, 246 { 0xf0, 0x00 }, 247 { 0xf6, 0xc0 }, 248 }; 249 250 static const struct nv3052c_reg fs035vg158_panel_regs[] = { 251 // EXTC Command set enable, select page 1 252 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, 253 // Mostly unknown registers 254 { 0xe3, 0x00 }, 255 { 0x40, 0x00 }, 256 { 0x03, 0x40 }, 257 { 0x04, 0x00 }, 258 { 0x05, 0x03 }, 259 { 0x08, 0x00 }, 260 { 0x09, 0x07 }, 261 { 0x0a, 0x01 }, 262 { 0x0b, 0x32 }, 263 { 0x0c, 0x32 }, 264 { 0x0d, 0x0b }, 265 { 0x0e, 0x00 }, 266 { 0x23, 0x20 }, // RGB interface control: DE MODE PCLK-N 267 { 0x24, 0x0c }, 268 { 0x25, 0x06 }, 269 { 0x26, 0x14 }, 270 { 0x27, 0x14 }, 271 { 0x38, 0x9c }, //VCOM_ADJ1, different to ltk035c5444t 272 { 0x39, 0xa7 }, //VCOM_ADJ2, different to ltk035c5444t 273 { 0x3a, 0x50 }, //VCOM_ADJ3, different to ltk035c5444t 274 { 0x28, 0x40 }, 275 { 0x29, 0x01 }, 276 { 0x2a, 0xdf }, 277 { 0x49, 0x3c }, 278 { 0x91, 0x57 }, //EXTPW_CTRL2, different to ltk035c5444t 279 { 0x92, 0x57 }, //EXTPW_CTRL3, different to ltk035c5444t 280 { 0xa0, 0x55 }, 281 { 0xa1, 0x50 }, 282 { 0xa4, 0x9c }, 283 { 0xa7, 0x02 }, 284 { 0xa8, 0x01 }, 285 { 0xa9, 0x01 }, 286 { 0xaa, 0xfc }, 287 { 0xab, 0x28 }, 288 { 0xac, 0x06 }, 289 { 0xad, 0x06 }, 290 { 0xae, 0x06 }, 291 { 0xaf, 0x03 }, 292 { 0xb0, 0x08 }, 293 { 0xb1, 0x26 }, 294 { 0xb2, 0x28 }, 295 { 0xb3, 0x28 }, 296 { 0xb4, 0x03 }, // Unknown, different to ltk035c5444 297 { 0xb5, 0x08 }, 298 { 0xb6, 0x26 }, 299 { 0xb7, 0x08 }, 300 { 0xb8, 0x26 }, 301 { 0xf0, 0x00 }, 302 { 0xf6, 0xc0 }, 303 }; 304 305 306 static const struct nv3052c_reg wl_355608_a8_panel_regs[] = { 307 // EXTC Command set enable, select page 1 308 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, 309 // Mostly unknown registers 310 { 0xe3, 0x00 }, 311 { 0x40, 0x00 }, 312 { 0x03, 0x40 }, 313 { 0x04, 0x00 }, 314 { 0x05, 0x03 }, 315 { 0x08, 0x00 }, 316 { 0x09, 0x07 }, 317 { 0x0a, 0x01 }, 318 { 0x0b, 0x32 }, 319 { 0x0c, 0x32 }, 320 { 0x0d, 0x0b }, 321 { 0x0e, 0x00 }, 322 { 0x23, 0xa0 }, 323 { 0x24, 0x0c }, 324 { 0x25, 0x06 }, 325 { 0x26, 0x14 }, 326 { 0x27, 0x14 }, 327 { 0x38, 0xcc }, // VCOM_ADJ1 328 { 0x39, 0xd7 }, // VCOM_ADJ2 329 { 0x3a, 0x44 }, // VCOM_ADJ3 330 { 0x28, 0x40 }, 331 { 0x29, 0x01 }, 332 { 0x2a, 0xdf }, 333 { 0x49, 0x3c }, 334 { 0x91, 0x77 }, // EXTPW_CTRL2 335 { 0x92, 0x77 }, // EXTPW_CTRL3 336 { 0xa0, 0x55 }, 337 { 0xa1, 0x50 }, 338 { 0xa4, 0x9c }, 339 { 0xa7, 0x02 }, 340 { 0xa8, 0x01 }, 341 { 0xa9, 0x01 }, 342 { 0xaa, 0xfc }, 343 { 0xab, 0x28 }, 344 { 0xac, 0x06 }, 345 { 0xad, 0x06 }, 346 { 0xae, 0x06 }, 347 { 0xaf, 0x03 }, 348 { 0xb0, 0x08 }, 349 { 0xb1, 0x26 }, 350 { 0xb2, 0x28 }, 351 { 0xb3, 0x28 }, 352 { 0xb4, 0x33 }, 353 { 0xb5, 0x08 }, 354 { 0xb6, 0x26 }, 355 { 0xb7, 0x08 }, 356 { 0xb8, 0x26 }, 357 { 0xf0, 0x00 }, 358 { 0xf6, 0xc0 }, 359 }; 360 361 static inline struct nv3052c *to_nv3052c(struct drm_panel *panel) 362 { 363 return container_of(panel, struct nv3052c, panel); 364 } 365 366 static int nv3052c_prepare(struct drm_panel *panel) 367 { 368 struct nv3052c *priv = to_nv3052c(panel); 369 const struct nv3052c_reg *panel_regs = priv->panel_info->panel_regs; 370 unsigned int panel_regs_len = priv->panel_info->panel_regs_len; 371 struct mipi_dbi *dbi = &priv->dbi; 372 unsigned int i; 373 int err; 374 375 err = regulator_enable(priv->supply); 376 if (err) { 377 dev_err(priv->dev, "Failed to enable power supply: %d\n", err); 378 return err; 379 } 380 381 /* Reset the chip */ 382 gpiod_set_value_cansleep(priv->reset_gpio, 1); 383 usleep_range(10, 1000); 384 gpiod_set_value_cansleep(priv->reset_gpio, 0); 385 usleep_range(5000, 20000); 386 387 /* Apply panel-specific initialization registers */ 388 for (i = 0; i < panel_regs_len; i++) { 389 err = mipi_dbi_command(dbi, panel_regs[i].cmd, 390 panel_regs[i].val); 391 392 if (err) { 393 dev_err(priv->dev, "Unable to set register: %d\n", err); 394 goto err_disable_regulator; 395 } 396 } 397 398 /* Apply common initialization registers */ 399 for (i = 0; i < ARRAY_SIZE(common_init_regs); i++) { 400 err = mipi_dbi_command(dbi, common_init_regs[i].cmd, 401 common_init_regs[i].val); 402 if (err) { 403 dev_err(priv->dev, "Unable to set register: %d\n", err); 404 goto err_disable_regulator; 405 } 406 } 407 408 err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 409 if (err) { 410 dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err); 411 goto err_disable_regulator; 412 } 413 414 return 0; 415 416 err_disable_regulator: 417 regulator_disable(priv->supply); 418 return err; 419 } 420 421 static int nv3052c_unprepare(struct drm_panel *panel) 422 { 423 struct nv3052c *priv = to_nv3052c(panel); 424 struct mipi_dbi *dbi = &priv->dbi; 425 int err; 426 427 err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); 428 if (err) 429 dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err); 430 431 gpiod_set_value_cansleep(priv->reset_gpio, 1); 432 regulator_disable(priv->supply); 433 434 return 0; 435 } 436 437 static int nv3052c_enable(struct drm_panel *panel) 438 { 439 struct nv3052c *priv = to_nv3052c(panel); 440 struct mipi_dbi *dbi = &priv->dbi; 441 int err; 442 443 err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 444 if (err) { 445 dev_err(priv->dev, "Unable to enable display: %d\n", err); 446 return err; 447 } 448 449 if (panel->backlight) { 450 /* Wait for the picture to be ready before enabling backlight */ 451 msleep(120); 452 } 453 454 return 0; 455 } 456 457 static int nv3052c_disable(struct drm_panel *panel) 458 { 459 struct nv3052c *priv = to_nv3052c(panel); 460 struct mipi_dbi *dbi = &priv->dbi; 461 int err; 462 463 err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 464 if (err) { 465 dev_err(priv->dev, "Unable to disable display: %d\n", err); 466 return err; 467 } 468 469 return 0; 470 } 471 472 static int nv3052c_get_modes(struct drm_panel *panel, 473 struct drm_connector *connector) 474 { 475 struct nv3052c *priv = to_nv3052c(panel); 476 const struct nv3052c_panel_info *panel_info = priv->panel_info; 477 struct drm_display_mode *mode; 478 unsigned int i; 479 480 for (i = 0; i < panel_info->num_modes; i++) { 481 mode = drm_mode_duplicate(connector->dev, 482 &panel_info->display_modes[i]); 483 if (!mode) 484 return -ENOMEM; 485 486 drm_mode_set_name(mode); 487 488 mode->type = DRM_MODE_TYPE_DRIVER; 489 if (panel_info->num_modes == 1) 490 mode->type |= DRM_MODE_TYPE_PREFERRED; 491 492 drm_mode_probed_add(connector, mode); 493 } 494 495 connector->display_info.bpc = 8; 496 connector->display_info.width_mm = panel_info->width_mm; 497 connector->display_info.height_mm = panel_info->height_mm; 498 499 drm_display_info_set_bus_formats(&connector->display_info, 500 &panel_info->bus_format, 1); 501 connector->display_info.bus_flags = panel_info->bus_flags; 502 503 return panel_info->num_modes; 504 } 505 506 static const struct drm_panel_funcs nv3052c_funcs = { 507 .prepare = nv3052c_prepare, 508 .unprepare = nv3052c_unprepare, 509 .enable = nv3052c_enable, 510 .disable = nv3052c_disable, 511 .get_modes = nv3052c_get_modes, 512 }; 513 514 static int nv3052c_probe(struct spi_device *spi) 515 { 516 struct device *dev = &spi->dev; 517 struct nv3052c *priv; 518 int err; 519 520 priv = devm_drm_panel_alloc(dev, struct nv3052c, panel, &nv3052c_funcs, 521 DRM_MODE_CONNECTOR_DPI); 522 if (IS_ERR(priv)) 523 return PTR_ERR(priv); 524 525 priv->dev = dev; 526 527 priv->panel_info = of_device_get_match_data(dev); 528 if (!priv->panel_info) 529 return -EINVAL; 530 531 priv->supply = devm_regulator_get(dev, "power"); 532 if (IS_ERR(priv->supply)) 533 return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n"); 534 535 priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 536 if (IS_ERR(priv->reset_gpio)) 537 return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); 538 539 err = mipi_dbi_spi_init(spi, &priv->dbi, NULL); 540 if (err) 541 return dev_err_probe(dev, err, "MIPI DBI init failed\n"); 542 543 priv->dbi.read_commands = NULL; 544 545 spi_set_drvdata(spi, priv); 546 547 err = drm_panel_of_backlight(&priv->panel); 548 if (err) 549 return dev_err_probe(dev, err, "Failed to attach backlight\n"); 550 551 drm_panel_add(&priv->panel); 552 553 return 0; 554 } 555 556 static void nv3052c_remove(struct spi_device *spi) 557 { 558 struct nv3052c *priv = spi_get_drvdata(spi); 559 560 drm_panel_remove(&priv->panel); 561 drm_panel_disable(&priv->panel); 562 drm_panel_unprepare(&priv->panel); 563 } 564 565 static const struct drm_display_mode ltk035c5444t_modes[] = { 566 { /* 60 Hz */ 567 .clock = 24000, 568 .hdisplay = 640, 569 .hsync_start = 640 + 96, 570 .hsync_end = 640 + 96 + 16, 571 .htotal = 640 + 96 + 16 + 48, 572 .vdisplay = 480, 573 .vsync_start = 480 + 5, 574 .vsync_end = 480 + 5 + 2, 575 .vtotal = 480 + 5 + 2 + 13, 576 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 577 }, 578 { /* 50 Hz */ 579 .clock = 18000, 580 .hdisplay = 640, 581 .hsync_start = 640 + 39, 582 .hsync_end = 640 + 39 + 2, 583 .htotal = 640 + 39 + 2 + 39, 584 .vdisplay = 480, 585 .vsync_start = 480 + 5, 586 .vsync_end = 480 + 5 + 2, 587 .vtotal = 480 + 5 + 2 + 13, 588 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 589 }, 590 }; 591 592 static const struct drm_display_mode fs035vg158_modes[] = { 593 { /* 60 Hz */ 594 .clock = 21000, 595 .hdisplay = 640, 596 .hsync_start = 640 + 34, 597 .hsync_end = 640 + 34 + 4, 598 .htotal = 640 + 34 + 4 + 20, 599 .vdisplay = 480, 600 .vsync_start = 480 + 12, 601 .vsync_end = 480 + 12 + 4, 602 .vtotal = 480 + 12 + 4 + 6, 603 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 604 }, 605 }; 606 607 static const struct drm_display_mode wl_355608_a8_mode[] = { 608 { 609 .clock = 24000, 610 .hdisplay = 640, 611 .hsync_start = 640 + 64, 612 .hsync_end = 640 + 64 + 20, 613 .htotal = 640 + 64 + 20 + 46, 614 .vdisplay = 480, 615 .vsync_start = 480 + 21, 616 .vsync_end = 480 + 21 + 4, 617 .vtotal = 480 + 21 + 4 + 15, 618 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 619 }, 620 }; 621 622 static const struct nv3052c_panel_info ltk035c5444t_panel_info = { 623 .display_modes = ltk035c5444t_modes, 624 .num_modes = ARRAY_SIZE(ltk035c5444t_modes), 625 .width_mm = 77, 626 .height_mm = 64, 627 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 628 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 629 .panel_regs = ltk035c5444t_panel_regs, 630 .panel_regs_len = ARRAY_SIZE(ltk035c5444t_panel_regs), 631 }; 632 633 static const struct nv3052c_panel_info fs035vg158_panel_info = { 634 .display_modes = fs035vg158_modes, 635 .num_modes = ARRAY_SIZE(fs035vg158_modes), 636 .width_mm = 70, 637 .height_mm = 53, 638 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 639 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 640 .panel_regs = fs035vg158_panel_regs, 641 .panel_regs_len = ARRAY_SIZE(fs035vg158_panel_regs), 642 }; 643 644 static const struct nv3052c_panel_info wl_355608_a8_panel_info = { 645 .display_modes = wl_355608_a8_mode, 646 .num_modes = ARRAY_SIZE(wl_355608_a8_mode), 647 .width_mm = 150, 648 .height_mm = 94, 649 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 650 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 651 .panel_regs = wl_355608_a8_panel_regs, 652 .panel_regs_len = ARRAY_SIZE(wl_355608_a8_panel_regs), 653 }; 654 655 static const struct spi_device_id nv3052c_ids[] = { 656 { "ltk035c5444t", }, 657 { "fs035vg158", }, 658 { "rg35xx-plus-panel", }, 659 { /* sentinel */ } 660 }; 661 MODULE_DEVICE_TABLE(spi, nv3052c_ids); 662 663 static const struct of_device_id nv3052c_of_match[] = { 664 { .compatible = "leadtek,ltk035c5444t", .data = <k035c5444t_panel_info }, 665 { .compatible = "fascontek,fs035vg158", .data = &fs035vg158_panel_info }, 666 { .compatible = "anbernic,rg35xx-plus-panel", .data = &wl_355608_a8_panel_info }, 667 { /* sentinel */ } 668 }; 669 MODULE_DEVICE_TABLE(of, nv3052c_of_match); 670 671 static struct spi_driver nv3052c_driver = { 672 .driver = { 673 .name = "nv3052c", 674 .of_match_table = nv3052c_of_match, 675 }, 676 .id_table = nv3052c_ids, 677 .probe = nv3052c_probe, 678 .remove = nv3052c_remove, 679 }; 680 module_spi_driver(nv3052c_driver); 681 682 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 683 MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); 684 MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com"); 685 MODULE_DESCRIPTION("NewVision NV3052C IPS LCD panel driver"); 686 MODULE_LICENSE("GPL v2"); 687