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 static const struct nv3052c_reg ltk035c5444t_panel_regs[] = { 47 // EXTC Command set enable, select page 1 48 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, 49 // Mostly unknown registers 50 { 0xe3, 0x00 }, 51 { 0x40, 0x00 }, 52 { 0x03, 0x40 }, 53 { 0x04, 0x00 }, 54 { 0x05, 0x03 }, 55 { 0x08, 0x00 }, 56 { 0x09, 0x07 }, 57 { 0x0a, 0x01 }, 58 { 0x0b, 0x32 }, 59 { 0x0c, 0x32 }, 60 { 0x0d, 0x0b }, 61 { 0x0e, 0x00 }, 62 { 0x23, 0xa0 }, 63 { 0x24, 0x0c }, 64 { 0x25, 0x06 }, 65 { 0x26, 0x14 }, 66 { 0x27, 0x14 }, 67 { 0x38, 0xcc }, // VCOM_ADJ1 68 { 0x39, 0xd7 }, // VCOM_ADJ2 69 { 0x3a, 0x4a }, // VCOM_ADJ3 70 { 0x28, 0x40 }, 71 { 0x29, 0x01 }, 72 { 0x2a, 0xdf }, 73 { 0x49, 0x3c }, 74 { 0x91, 0x77 }, // EXTPW_CTRL2 75 { 0x92, 0x77 }, // EXTPW_CTRL3 76 { 0xa0, 0x55 }, 77 { 0xa1, 0x50 }, 78 { 0xa4, 0x9c }, 79 { 0xa7, 0x02 }, 80 { 0xa8, 0x01 }, 81 { 0xa9, 0x01 }, 82 { 0xaa, 0xfc }, 83 { 0xab, 0x28 }, 84 { 0xac, 0x06 }, 85 { 0xad, 0x06 }, 86 { 0xae, 0x06 }, 87 { 0xaf, 0x03 }, 88 { 0xb0, 0x08 }, 89 { 0xb1, 0x26 }, 90 { 0xb2, 0x28 }, 91 { 0xb3, 0x28 }, 92 { 0xb4, 0x33 }, 93 { 0xb5, 0x08 }, 94 { 0xb6, 0x26 }, 95 { 0xb7, 0x08 }, 96 { 0xb8, 0x26 }, 97 { 0xf0, 0x00 }, 98 { 0xf6, 0xc0 }, 99 // EXTC Command set enable, select page 2 100 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 101 // Set gray scale voltage to adjust gamma 102 { 0xb0, 0x0b }, // PGAMVR0 103 { 0xb1, 0x16 }, // PGAMVR1 104 { 0xb2, 0x17 }, // PGAMVR2 105 { 0xb3, 0x2c }, // PGAMVR3 106 { 0xb4, 0x32 }, // PGAMVR4 107 { 0xb5, 0x3b }, // PGAMVR5 108 { 0xb6, 0x29 }, // PGAMPR0 109 { 0xb7, 0x40 }, // PGAMPR1 110 { 0xb8, 0x0d }, // PGAMPK0 111 { 0xb9, 0x05 }, // PGAMPK1 112 { 0xba, 0x12 }, // PGAMPK2 113 { 0xbb, 0x10 }, // PGAMPK3 114 { 0xbc, 0x12 }, // PGAMPK4 115 { 0xbd, 0x15 }, // PGAMPK5 116 { 0xbe, 0x19 }, // PGAMPK6 117 { 0xbf, 0x0e }, // PGAMPK7 118 { 0xc0, 0x16 }, // PGAMPK8 119 { 0xc1, 0x0a }, // PGAMPK9 120 // Set gray scale voltage to adjust gamma 121 { 0xd0, 0x0c }, // NGAMVR0 122 { 0xd1, 0x17 }, // NGAMVR0 123 { 0xd2, 0x14 }, // NGAMVR1 124 { 0xd3, 0x2e }, // NGAMVR2 125 { 0xd4, 0x32 }, // NGAMVR3 126 { 0xd5, 0x3c }, // NGAMVR4 127 { 0xd6, 0x22 }, // NGAMPR0 128 { 0xd7, 0x3d }, // NGAMPR1 129 { 0xd8, 0x0d }, // NGAMPK0 130 { 0xd9, 0x07 }, // NGAMPK1 131 { 0xda, 0x13 }, // NGAMPK2 132 { 0xdb, 0x13 }, // NGAMPK3 133 { 0xdc, 0x11 }, // NGAMPK4 134 { 0xdd, 0x15 }, // NGAMPK5 135 { 0xde, 0x19 }, // NGAMPK6 136 { 0xdf, 0x10 }, // NGAMPK7 137 { 0xe0, 0x17 }, // NGAMPK8 138 { 0xe1, 0x0a }, // NGAMPK9 139 // EXTC Command set enable, select page 3 140 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 }, 141 // Set various timing settings 142 { 0x00, 0x2a }, // GIP_VST_1 143 { 0x01, 0x2a }, // GIP_VST_2 144 { 0x02, 0x2a }, // GIP_VST_3 145 { 0x03, 0x2a }, // GIP_VST_4 146 { 0x04, 0x61 }, // GIP_VST_5 147 { 0x05, 0x80 }, // GIP_VST_6 148 { 0x06, 0xc7 }, // GIP_VST_7 149 { 0x07, 0x01 }, // GIP_VST_8 150 { 0x08, 0x03 }, // GIP_VST_9 151 { 0x09, 0x04 }, // GIP_VST_10 152 { 0x70, 0x22 }, // GIP_ECLK1 153 { 0x71, 0x80 }, // GIP_ECLK2 154 { 0x30, 0x2a }, // GIP_CLK_1 155 { 0x31, 0x2a }, // GIP_CLK_2 156 { 0x32, 0x2a }, // GIP_CLK_3 157 { 0x33, 0x2a }, // GIP_CLK_4 158 { 0x34, 0x61 }, // GIP_CLK_5 159 { 0x35, 0xc5 }, // GIP_CLK_6 160 { 0x36, 0x80 }, // GIP_CLK_7 161 { 0x37, 0x23 }, // GIP_CLK_8 162 { 0x40, 0x03 }, // GIP_CLKA_1 163 { 0x41, 0x04 }, // GIP_CLKA_2 164 { 0x42, 0x05 }, // GIP_CLKA_3 165 { 0x43, 0x06 }, // GIP_CLKA_4 166 { 0x44, 0x11 }, // GIP_CLKA_5 167 { 0x45, 0xe8 }, // GIP_CLKA_6 168 { 0x46, 0xe9 }, // GIP_CLKA_7 169 { 0x47, 0x11 }, // GIP_CLKA_8 170 { 0x48, 0xea }, // GIP_CLKA_9 171 { 0x49, 0xeb }, // GIP_CLKA_10 172 { 0x50, 0x07 }, // GIP_CLKB_1 173 { 0x51, 0x08 }, // GIP_CLKB_2 174 { 0x52, 0x09 }, // GIP_CLKB_3 175 { 0x53, 0x0a }, // GIP_CLKB_4 176 { 0x54, 0x11 }, // GIP_CLKB_5 177 { 0x55, 0xec }, // GIP_CLKB_6 178 { 0x56, 0xed }, // GIP_CLKB_7 179 { 0x57, 0x11 }, // GIP_CLKB_8 180 { 0x58, 0xef }, // GIP_CLKB_9 181 { 0x59, 0xf0 }, // GIP_CLKB_10 182 // Map internal GOA signals to GOA output pad 183 { 0xb1, 0x01 }, // PANELD2U2 184 { 0xb4, 0x15 }, // PANELD2U5 185 { 0xb5, 0x16 }, // PANELD2U6 186 { 0xb6, 0x09 }, // PANELD2U7 187 { 0xb7, 0x0f }, // PANELD2U8 188 { 0xb8, 0x0d }, // PANELD2U9 189 { 0xb9, 0x0b }, // PANELD2U10 190 { 0xba, 0x00 }, // PANELD2U11 191 { 0xc7, 0x02 }, // PANELD2U24 192 { 0xca, 0x17 }, // PANELD2U27 193 { 0xcb, 0x18 }, // PANELD2U28 194 { 0xcc, 0x0a }, // PANELD2U29 195 { 0xcd, 0x10 }, // PANELD2U30 196 { 0xce, 0x0e }, // PANELD2U31 197 { 0xcf, 0x0c }, // PANELD2U32 198 { 0xd0, 0x00 }, // PANELD2U33 199 // Map internal GOA signals to GOA output pad 200 { 0x81, 0x00 }, // PANELU2D2 201 { 0x84, 0x15 }, // PANELU2D5 202 { 0x85, 0x16 }, // PANELU2D6 203 { 0x86, 0x10 }, // PANELU2D7 204 { 0x87, 0x0a }, // PANELU2D8 205 { 0x88, 0x0c }, // PANELU2D9 206 { 0x89, 0x0e }, // PANELU2D10 207 { 0x8a, 0x02 }, // PANELU2D11 208 { 0x97, 0x00 }, // PANELU2D24 209 { 0x9a, 0x17 }, // PANELU2D27 210 { 0x9b, 0x18 }, // PANELU2D28 211 { 0x9c, 0x0f }, // PANELU2D29 212 { 0x9d, 0x09 }, // PANELU2D30 213 { 0x9e, 0x0b }, // PANELU2D31 214 { 0x9f, 0x0d }, // PANELU2D32 215 { 0xa0, 0x01 }, // PANELU2D33 216 // EXTC Command set enable, select page 2 217 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 218 // Unknown registers 219 { 0x01, 0x01 }, 220 { 0x02, 0xda }, 221 { 0x03, 0xba }, 222 { 0x04, 0xa8 }, 223 { 0x05, 0x9a }, 224 { 0x06, 0x70 }, 225 { 0x07, 0xff }, 226 { 0x08, 0x91 }, 227 { 0x09, 0x90 }, 228 { 0x0a, 0xff }, 229 { 0x0b, 0x8f }, 230 { 0x0c, 0x60 }, 231 { 0x0d, 0x58 }, 232 { 0x0e, 0x48 }, 233 { 0x0f, 0x38 }, 234 { 0x10, 0x2b }, 235 // EXTC Command set enable, select page 0 236 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 }, 237 // Display Access Control 238 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0 239 }; 240 241 static const struct nv3052c_reg fs035vg158_panel_regs[] = { 242 // EXTC Command set enable, select page 1 243 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, 244 // Mostly unknown registers 245 { 0xe3, 0x00 }, 246 { 0x40, 0x00 }, 247 { 0x03, 0x40 }, 248 { 0x04, 0x00 }, 249 { 0x05, 0x03 }, 250 { 0x08, 0x00 }, 251 { 0x09, 0x07 }, 252 { 0x0a, 0x01 }, 253 { 0x0b, 0x32 }, 254 { 0x0c, 0x32 }, 255 { 0x0d, 0x0b }, 256 { 0x0e, 0x00 }, 257 { 0x23, 0x20 }, // RGB interface control: DE MODE PCLK-N 258 { 0x24, 0x0c }, 259 { 0x25, 0x06 }, 260 { 0x26, 0x14 }, 261 { 0x27, 0x14 }, 262 { 0x38, 0x9c }, //VCOM_ADJ1, different to ltk035c5444t 263 { 0x39, 0xa7 }, //VCOM_ADJ2, different to ltk035c5444t 264 { 0x3a, 0x50 }, //VCOM_ADJ3, different to ltk035c5444t 265 { 0x28, 0x40 }, 266 { 0x29, 0x01 }, 267 { 0x2a, 0xdf }, 268 { 0x49, 0x3c }, 269 { 0x91, 0x57 }, //EXTPW_CTRL2, different to ltk035c5444t 270 { 0x92, 0x57 }, //EXTPW_CTRL3, different to ltk035c5444t 271 { 0xa0, 0x55 }, 272 { 0xa1, 0x50 }, 273 { 0xa4, 0x9c }, 274 { 0xa7, 0x02 }, 275 { 0xa8, 0x01 }, 276 { 0xa9, 0x01 }, 277 { 0xaa, 0xfc }, 278 { 0xab, 0x28 }, 279 { 0xac, 0x06 }, 280 { 0xad, 0x06 }, 281 { 0xae, 0x06 }, 282 { 0xaf, 0x03 }, 283 { 0xb0, 0x08 }, 284 { 0xb1, 0x26 }, 285 { 0xb2, 0x28 }, 286 { 0xb3, 0x28 }, 287 { 0xb4, 0x03 }, // Unknown, different to ltk035c5444 288 { 0xb5, 0x08 }, 289 { 0xb6, 0x26 }, 290 { 0xb7, 0x08 }, 291 { 0xb8, 0x26 }, 292 { 0xf0, 0x00 }, 293 { 0xf6, 0xc0 }, 294 // EXTC Command set enable, select page 0 295 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 296 // Set gray scale voltage to adjust gamma 297 { 0xb0, 0x0b }, // PGAMVR0 298 { 0xb1, 0x16 }, // PGAMVR1 299 { 0xb2, 0x17 }, // PGAMVR2 300 { 0xb3, 0x2c }, // PGAMVR3 301 { 0xb4, 0x32 }, // PGAMVR4 302 { 0xb5, 0x3b }, // PGAMVR5 303 { 0xb6, 0x29 }, // PGAMPR0 304 { 0xb7, 0x40 }, // PGAMPR1 305 { 0xb8, 0x0d }, // PGAMPK0 306 { 0xb9, 0x05 }, // PGAMPK1 307 { 0xba, 0x12 }, // PGAMPK2 308 { 0xbb, 0x10 }, // PGAMPK3 309 { 0xbc, 0x12 }, // PGAMPK4 310 { 0xbd, 0x15 }, // PGAMPK5 311 { 0xbe, 0x19 }, // PGAMPK6 312 { 0xbf, 0x0e }, // PGAMPK7 313 { 0xc0, 0x16 }, // PGAMPK8 314 { 0xc1, 0x0a }, // PGAMPK9 315 // Set gray scale voltage to adjust gamma 316 { 0xd0, 0x0c }, // NGAMVR0 317 { 0xd1, 0x17 }, // NGAMVR0 318 { 0xd2, 0x14 }, // NGAMVR1 319 { 0xd3, 0x2e }, // NGAMVR2 320 { 0xd4, 0x32 }, // NGAMVR3 321 { 0xd5, 0x3c }, // NGAMVR4 322 { 0xd6, 0x22 }, // NGAMPR0 323 { 0xd7, 0x3d }, // NGAMPR1 324 { 0xd8, 0x0d }, // NGAMPK0 325 { 0xd9, 0x07 }, // NGAMPK1 326 { 0xda, 0x13 }, // NGAMPK2 327 { 0xdb, 0x13 }, // NGAMPK3 328 { 0xdc, 0x11 }, // NGAMPK4 329 { 0xdd, 0x15 }, // NGAMPK5 330 { 0xde, 0x19 }, // NGAMPK6 331 { 0xdf, 0x10 }, // NGAMPK7 332 { 0xe0, 0x17 }, // NGAMPK8 333 { 0xe1, 0x0a }, // NGAMPK9 334 // EXTC Command set enable, select page 3 335 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 }, 336 // Set various timing settings 337 { 0x00, 0x2a }, // GIP_VST_1 338 { 0x01, 0x2a }, // GIP_VST_2 339 { 0x02, 0x2a }, // GIP_VST_3 340 { 0x03, 0x2a }, // GIP_VST_4 341 { 0x04, 0x61 }, // GIP_VST_5 342 { 0x05, 0x80 }, // GIP_VST_6 343 { 0x06, 0xc7 }, // GIP_VST_7 344 { 0x07, 0x01 }, // GIP_VST_8 345 { 0x08, 0x03 }, // GIP_VST_9 346 { 0x09, 0x04 }, // GIP_VST_10 347 { 0x70, 0x22 }, // GIP_ECLK1 348 { 0x71, 0x80 }, // GIP_ECLK2 349 { 0x30, 0x2a }, // GIP_CLK_1 350 { 0x31, 0x2a }, // GIP_CLK_2 351 { 0x32, 0x2a }, // GIP_CLK_3 352 { 0x33, 0x2a }, // GIP_CLK_4 353 { 0x34, 0x61 }, // GIP_CLK_5 354 { 0x35, 0xc5 }, // GIP_CLK_6 355 { 0x36, 0x80 }, // GIP_CLK_7 356 { 0x37, 0x23 }, // GIP_CLK_8 357 { 0x40, 0x03 }, // GIP_CLKA_1 358 { 0x41, 0x04 }, // GIP_CLKA_2 359 { 0x42, 0x05 }, // GIP_CLKA_3 360 { 0x43, 0x06 }, // GIP_CLKA_4 361 { 0x44, 0x11 }, // GIP_CLKA_5 362 { 0x45, 0xe8 }, // GIP_CLKA_6 363 { 0x46, 0xe9 }, // GIP_CLKA_7 364 { 0x47, 0x11 }, // GIP_CLKA_8 365 { 0x48, 0xea }, // GIP_CLKA_9 366 { 0x49, 0xeb }, // GIP_CLKA_10 367 { 0x50, 0x07 }, // GIP_CLKB_1 368 { 0x51, 0x08 }, // GIP_CLKB_2 369 { 0x52, 0x09 }, // GIP_CLKB_3 370 { 0x53, 0x0a }, // GIP_CLKB_4 371 { 0x54, 0x11 }, // GIP_CLKB_5 372 { 0x55, 0xec }, // GIP_CLKB_6 373 { 0x56, 0xed }, // GIP_CLKB_7 374 { 0x57, 0x11 }, // GIP_CLKB_8 375 { 0x58, 0xef }, // GIP_CLKB_9 376 { 0x59, 0xf0 }, // GIP_CLKB_10 377 // Map internal GOA signals to GOA output pad 378 { 0xb1, 0x01 }, // PANELD2U2 379 { 0xb4, 0x15 }, // PANELD2U5 380 { 0xb5, 0x16 }, // PANELD2U6 381 { 0xb6, 0x09 }, // PANELD2U7 382 { 0xb7, 0x0f }, // PANELD2U8 383 { 0xb8, 0x0d }, // PANELD2U9 384 { 0xb9, 0x0b }, // PANELD2U10 385 { 0xba, 0x00 }, // PANELD2U11 386 { 0xc7, 0x02 }, // PANELD2U24 387 { 0xca, 0x17 }, // PANELD2U27 388 { 0xcb, 0x18 }, // PANELD2U28 389 { 0xcc, 0x0a }, // PANELD2U29 390 { 0xcd, 0x10 }, // PANELD2U30 391 { 0xce, 0x0e }, // PANELD2U31 392 { 0xcf, 0x0c }, // PANELD2U32 393 { 0xd0, 0x00 }, // PANELD2U33 394 // Map internal GOA signals to GOA output pad 395 { 0x81, 0x00 }, // PANELU2D2 396 { 0x84, 0x15 }, // PANELU2D5 397 { 0x85, 0x16 }, // PANELU2D6 398 { 0x86, 0x10 }, // PANELU2D7 399 { 0x87, 0x0a }, // PANELU2D8 400 { 0x88, 0x0c }, // PANELU2D9 401 { 0x89, 0x0e }, // PANELU2D10 402 { 0x8a, 0x02 }, // PANELU2D11 403 { 0x97, 0x00 }, // PANELU2D24 404 { 0x9a, 0x17 }, // PANELU2D27 405 { 0x9b, 0x18 }, // PANELU2D28 406 { 0x9c, 0x0f }, // PANELU2D29 407 { 0x9d, 0x09 }, // PANELU2D30 408 { 0x9e, 0x0b }, // PANELU2D31 409 { 0x9f, 0x0d }, // PANELU2D32 410 { 0xa0, 0x01 }, // PANELU2D33 411 // EXTC Command set enable, select page 2 412 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 413 // Unknown registers 414 { 0x01, 0x01 }, 415 { 0x02, 0xda }, 416 { 0x03, 0xba }, 417 { 0x04, 0xa8 }, 418 { 0x05, 0x9a }, 419 { 0x06, 0x70 }, 420 { 0x07, 0xff }, 421 { 0x08, 0x91 }, 422 { 0x09, 0x90 }, 423 { 0x0a, 0xff }, 424 { 0x0b, 0x8f }, 425 { 0x0c, 0x60 }, 426 { 0x0d, 0x58 }, 427 { 0x0e, 0x48 }, 428 { 0x0f, 0x38 }, 429 { 0x10, 0x2b }, 430 // EXTC Command set enable, select page 0 431 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 }, 432 // Display Access Control 433 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0 434 }; 435 436 437 static const struct nv3052c_reg wl_355608_a8_panel_regs[] = { 438 // EXTC Command set enable, select page 1 439 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 }, 440 // Mostly unknown registers 441 { 0xe3, 0x00 }, 442 { 0x40, 0x00 }, 443 { 0x03, 0x40 }, 444 { 0x04, 0x00 }, 445 { 0x05, 0x03 }, 446 { 0x08, 0x00 }, 447 { 0x09, 0x07 }, 448 { 0x0a, 0x01 }, 449 { 0x0b, 0x32 }, 450 { 0x0c, 0x32 }, 451 { 0x0d, 0x0b }, 452 { 0x0e, 0x00 }, 453 { 0x23, 0xa0 }, 454 { 0x24, 0x0c }, 455 { 0x25, 0x06 }, 456 { 0x26, 0x14 }, 457 { 0x27, 0x14 }, 458 { 0x38, 0xcc }, // VCOM_ADJ1 459 { 0x39, 0xd7 }, // VCOM_ADJ2 460 { 0x3a, 0x44 }, // VCOM_ADJ3 461 { 0x28, 0x40 }, 462 { 0x29, 0x01 }, 463 { 0x2a, 0xdf }, 464 { 0x49, 0x3c }, 465 { 0x91, 0x77 }, // EXTPW_CTRL2 466 { 0x92, 0x77 }, // EXTPW_CTRL3 467 { 0xa0, 0x55 }, 468 { 0xa1, 0x50 }, 469 { 0xa4, 0x9c }, 470 { 0xa7, 0x02 }, 471 { 0xa8, 0x01 }, 472 { 0xa9, 0x01 }, 473 { 0xaa, 0xfc }, 474 { 0xab, 0x28 }, 475 { 0xac, 0x06 }, 476 { 0xad, 0x06 }, 477 { 0xae, 0x06 }, 478 { 0xaf, 0x03 }, 479 { 0xb0, 0x08 }, 480 { 0xb1, 0x26 }, 481 { 0xb2, 0x28 }, 482 { 0xb3, 0x28 }, 483 { 0xb4, 0x33 }, 484 { 0xb5, 0x08 }, 485 { 0xb6, 0x26 }, 486 { 0xb7, 0x08 }, 487 { 0xb8, 0x26 }, 488 { 0xf0, 0x00 }, 489 { 0xf6, 0xc0 }, 490 // EXTC Command set enable, select page 2 491 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 492 // Set gray scale voltage to adjust gamma 493 { 0xb0, 0x0b }, // PGAMVR0 494 { 0xb1, 0x16 }, // PGAMVR1 495 { 0xb2, 0x17 }, // PGAMVR2 496 { 0xb3, 0x2c }, // PGAMVR3 497 { 0xb4, 0x32 }, // PGAMVR4 498 { 0xb5, 0x3b }, // PGAMVR5 499 { 0xb6, 0x29 }, // PGAMPR0 500 { 0xb7, 0x40 }, // PGAMPR1 501 { 0xb8, 0x0d }, // PGAMPK0 502 { 0xb9, 0x05 }, // PGAMPK1 503 { 0xba, 0x12 }, // PGAMPK2 504 { 0xbb, 0x10 }, // PGAMPK3 505 { 0xbc, 0x12 }, // PGAMPK4 506 { 0xbd, 0x15 }, // PGAMPK5 507 { 0xbe, 0x19 }, // PGAMPK6 508 { 0xbf, 0x0e }, // PGAMPK7 509 { 0xc0, 0x16 }, // PGAMPK8 510 { 0xc1, 0x0a }, // PGAMPK9 511 // Set gray scale voltage to adjust gamma 512 { 0xd0, 0x0c }, // NGAMVR0 513 { 0xd1, 0x17 }, // NGAMVR0 514 { 0xd2, 0x14 }, // NGAMVR1 515 { 0xd3, 0x2e }, // NGAMVR2 516 { 0xd4, 0x32 }, // NGAMVR3 517 { 0xd5, 0x3c }, // NGAMVR4 518 { 0xd6, 0x22 }, // NGAMPR0 519 { 0xd7, 0x3d }, // NGAMPR1 520 { 0xd8, 0x0d }, // NGAMPK0 521 { 0xd9, 0x07 }, // NGAMPK1 522 { 0xda, 0x13 }, // NGAMPK2 523 { 0xdb, 0x13 }, // NGAMPK3 524 { 0xdc, 0x11 }, // NGAMPK4 525 { 0xdd, 0x15 }, // NGAMPK5 526 { 0xde, 0x19 }, // NGAMPK6 527 { 0xdf, 0x10 }, // NGAMPK7 528 { 0xe0, 0x17 }, // NGAMPK8 529 { 0xe1, 0x0a }, // NGAMPK9 530 // EXTC Command set enable, select page 3 531 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 }, 532 // Set various timing settings 533 { 0x00, 0x2a }, // GIP_VST_1 534 { 0x01, 0x2a }, // GIP_VST_2 535 { 0x02, 0x2a }, // GIP_VST_3 536 { 0x03, 0x2a }, // GIP_VST_4 537 { 0x04, 0x61 }, // GIP_VST_5 538 { 0x05, 0x80 }, // GIP_VST_6 539 { 0x06, 0xc7 }, // GIP_VST_7 540 { 0x07, 0x01 }, // GIP_VST_8 541 { 0x08, 0x03 }, // GIP_VST_9 542 { 0x09, 0x04 }, // GIP_VST_10 543 { 0x70, 0x22 }, // GIP_ECLK1 544 { 0x71, 0x80 }, // GIP_ECLK2 545 { 0x30, 0x2a }, // GIP_CLK_1 546 { 0x31, 0x2a }, // GIP_CLK_2 547 { 0x32, 0x2a }, // GIP_CLK_3 548 { 0x33, 0x2a }, // GIP_CLK_4 549 { 0x34, 0x61 }, // GIP_CLK_5 550 { 0x35, 0xc5 }, // GIP_CLK_6 551 { 0x36, 0x80 }, // GIP_CLK_7 552 { 0x37, 0x23 }, // GIP_CLK_8 553 { 0x40, 0x03 }, // GIP_CLKA_1 554 { 0x41, 0x04 }, // GIP_CLKA_2 555 { 0x42, 0x05 }, // GIP_CLKA_3 556 { 0x43, 0x06 }, // GIP_CLKA_4 557 { 0x44, 0x11 }, // GIP_CLKA_5 558 { 0x45, 0xe8 }, // GIP_CLKA_6 559 { 0x46, 0xe9 }, // GIP_CLKA_7 560 { 0x47, 0x11 }, // GIP_CLKA_8 561 { 0x48, 0xea }, // GIP_CLKA_9 562 { 0x49, 0xeb }, // GIP_CLKA_10 563 { 0x50, 0x07 }, // GIP_CLKB_1 564 { 0x51, 0x08 }, // GIP_CLKB_2 565 { 0x52, 0x09 }, // GIP_CLKB_3 566 { 0x53, 0x0a }, // GIP_CLKB_4 567 { 0x54, 0x11 }, // GIP_CLKB_5 568 { 0x55, 0xec }, // GIP_CLKB_6 569 { 0x56, 0xed }, // GIP_CLKB_7 570 { 0x57, 0x11 }, // GIP_CLKB_8 571 { 0x58, 0xef }, // GIP_CLKB_9 572 { 0x59, 0xf0 }, // GIP_CLKB_10 573 // Map internal GOA signals to GOA output pad 574 { 0xb1, 0x01 }, // PANELD2U2 575 { 0xb4, 0x15 }, // PANELD2U5 576 { 0xb5, 0x16 }, // PANELD2U6 577 { 0xb6, 0x09 }, // PANELD2U7 578 { 0xb7, 0x0f }, // PANELD2U8 579 { 0xb8, 0x0d }, // PANELD2U9 580 { 0xb9, 0x0b }, // PANELD2U10 581 { 0xba, 0x00 }, // PANELD2U11 582 { 0xc7, 0x02 }, // PANELD2U24 583 { 0xca, 0x17 }, // PANELD2U27 584 { 0xcb, 0x18 }, // PANELD2U28 585 { 0xcc, 0x0a }, // PANELD2U29 586 { 0xcd, 0x10 }, // PANELD2U30 587 { 0xce, 0x0e }, // PANELD2U31 588 { 0xcf, 0x0c }, // PANELD2U32 589 { 0xd0, 0x00 }, // PANELD2U33 590 // Map internal GOA signals to GOA output pad 591 { 0x81, 0x00 }, // PANELU2D2 592 { 0x84, 0x15 }, // PANELU2D5 593 { 0x85, 0x16 }, // PANELU2D6 594 { 0x86, 0x10 }, // PANELU2D7 595 { 0x87, 0x0a }, // PANELU2D8 596 { 0x88, 0x0c }, // PANELU2D9 597 { 0x89, 0x0e }, // PANELU2D10 598 { 0x8a, 0x02 }, // PANELU2D11 599 { 0x97, 0x00 }, // PANELU2D24 600 { 0x9a, 0x17 }, // PANELU2D27 601 { 0x9b, 0x18 }, // PANELU2D28 602 { 0x9c, 0x0f }, // PANELU2D29 603 { 0x9d, 0x09 }, // PANELU2D30 604 { 0x9e, 0x0b }, // PANELU2D31 605 { 0x9f, 0x0d }, // PANELU2D32 606 { 0xa0, 0x01 }, // PANELU2D33 607 // EXTC Command set enable, select page 2 608 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 }, 609 // Unknown registers 610 { 0x01, 0x01 }, 611 { 0x02, 0xda }, 612 { 0x03, 0xba }, 613 { 0x04, 0xa8 }, 614 { 0x05, 0x9a }, 615 { 0x06, 0x70 }, 616 { 0x07, 0xff }, 617 { 0x08, 0x91 }, 618 { 0x09, 0x90 }, 619 { 0x0a, 0xff }, 620 { 0x0b, 0x8f }, 621 { 0x0c, 0x60 }, 622 { 0x0d, 0x58 }, 623 { 0x0e, 0x48 }, 624 { 0x0f, 0x38 }, 625 { 0x10, 0x2b }, 626 // EXTC Command set enable, select page 0 627 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 }, 628 // Display Access Control 629 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0 630 }; 631 632 static inline struct nv3052c *to_nv3052c(struct drm_panel *panel) 633 { 634 return container_of(panel, struct nv3052c, panel); 635 } 636 637 static int nv3052c_prepare(struct drm_panel *panel) 638 { 639 struct nv3052c *priv = to_nv3052c(panel); 640 const struct nv3052c_reg *panel_regs = priv->panel_info->panel_regs; 641 unsigned int panel_regs_len = priv->panel_info->panel_regs_len; 642 struct mipi_dbi *dbi = &priv->dbi; 643 unsigned int i; 644 int err; 645 646 err = regulator_enable(priv->supply); 647 if (err) { 648 dev_err(priv->dev, "Failed to enable power supply: %d\n", err); 649 return err; 650 } 651 652 /* Reset the chip */ 653 gpiod_set_value_cansleep(priv->reset_gpio, 1); 654 usleep_range(10, 1000); 655 gpiod_set_value_cansleep(priv->reset_gpio, 0); 656 usleep_range(5000, 20000); 657 658 for (i = 0; i < panel_regs_len; i++) { 659 err = mipi_dbi_command(dbi, panel_regs[i].cmd, 660 panel_regs[i].val); 661 662 if (err) { 663 dev_err(priv->dev, "Unable to set register: %d\n", err); 664 goto err_disable_regulator; 665 } 666 } 667 668 err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 669 if (err) { 670 dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err); 671 goto err_disable_regulator; 672 } 673 674 return 0; 675 676 err_disable_regulator: 677 regulator_disable(priv->supply); 678 return err; 679 } 680 681 static int nv3052c_unprepare(struct drm_panel *panel) 682 { 683 struct nv3052c *priv = to_nv3052c(panel); 684 struct mipi_dbi *dbi = &priv->dbi; 685 int err; 686 687 err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); 688 if (err) 689 dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err); 690 691 gpiod_set_value_cansleep(priv->reset_gpio, 1); 692 regulator_disable(priv->supply); 693 694 return 0; 695 } 696 697 static int nv3052c_enable(struct drm_panel *panel) 698 { 699 struct nv3052c *priv = to_nv3052c(panel); 700 struct mipi_dbi *dbi = &priv->dbi; 701 int err; 702 703 err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 704 if (err) { 705 dev_err(priv->dev, "Unable to enable display: %d\n", err); 706 return err; 707 } 708 709 if (panel->backlight) { 710 /* Wait for the picture to be ready before enabling backlight */ 711 msleep(120); 712 } 713 714 return 0; 715 } 716 717 static int nv3052c_disable(struct drm_panel *panel) 718 { 719 struct nv3052c *priv = to_nv3052c(panel); 720 struct mipi_dbi *dbi = &priv->dbi; 721 int err; 722 723 err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 724 if (err) { 725 dev_err(priv->dev, "Unable to disable display: %d\n", err); 726 return err; 727 } 728 729 return 0; 730 } 731 732 static int nv3052c_get_modes(struct drm_panel *panel, 733 struct drm_connector *connector) 734 { 735 struct nv3052c *priv = to_nv3052c(panel); 736 const struct nv3052c_panel_info *panel_info = priv->panel_info; 737 struct drm_display_mode *mode; 738 unsigned int i; 739 740 for (i = 0; i < panel_info->num_modes; i++) { 741 mode = drm_mode_duplicate(connector->dev, 742 &panel_info->display_modes[i]); 743 if (!mode) 744 return -ENOMEM; 745 746 drm_mode_set_name(mode); 747 748 mode->type = DRM_MODE_TYPE_DRIVER; 749 if (panel_info->num_modes == 1) 750 mode->type |= DRM_MODE_TYPE_PREFERRED; 751 752 drm_mode_probed_add(connector, mode); 753 } 754 755 connector->display_info.bpc = 8; 756 connector->display_info.width_mm = panel_info->width_mm; 757 connector->display_info.height_mm = panel_info->height_mm; 758 759 drm_display_info_set_bus_formats(&connector->display_info, 760 &panel_info->bus_format, 1); 761 connector->display_info.bus_flags = panel_info->bus_flags; 762 763 return panel_info->num_modes; 764 } 765 766 static const struct drm_panel_funcs nv3052c_funcs = { 767 .prepare = nv3052c_prepare, 768 .unprepare = nv3052c_unprepare, 769 .enable = nv3052c_enable, 770 .disable = nv3052c_disable, 771 .get_modes = nv3052c_get_modes, 772 }; 773 774 static int nv3052c_probe(struct spi_device *spi) 775 { 776 struct device *dev = &spi->dev; 777 struct nv3052c *priv; 778 int err; 779 780 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 781 if (!priv) 782 return -ENOMEM; 783 784 priv->dev = dev; 785 786 priv->panel_info = of_device_get_match_data(dev); 787 if (!priv->panel_info) 788 return -EINVAL; 789 790 priv->supply = devm_regulator_get(dev, "power"); 791 if (IS_ERR(priv->supply)) 792 return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n"); 793 794 priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 795 if (IS_ERR(priv->reset_gpio)) 796 return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); 797 798 err = mipi_dbi_spi_init(spi, &priv->dbi, NULL); 799 if (err) 800 return dev_err_probe(dev, err, "MIPI DBI init failed\n"); 801 802 priv->dbi.read_commands = NULL; 803 804 spi_set_drvdata(spi, priv); 805 806 drm_panel_init(&priv->panel, dev, &nv3052c_funcs, 807 DRM_MODE_CONNECTOR_DPI); 808 809 err = drm_panel_of_backlight(&priv->panel); 810 if (err) 811 return dev_err_probe(dev, err, "Failed to attach backlight\n"); 812 813 drm_panel_add(&priv->panel); 814 815 return 0; 816 } 817 818 static void nv3052c_remove(struct spi_device *spi) 819 { 820 struct nv3052c *priv = spi_get_drvdata(spi); 821 822 drm_panel_remove(&priv->panel); 823 drm_panel_disable(&priv->panel); 824 drm_panel_unprepare(&priv->panel); 825 } 826 827 static const struct drm_display_mode ltk035c5444t_modes[] = { 828 { /* 60 Hz */ 829 .clock = 24000, 830 .hdisplay = 640, 831 .hsync_start = 640 + 96, 832 .hsync_end = 640 + 96 + 16, 833 .htotal = 640 + 96 + 16 + 48, 834 .vdisplay = 480, 835 .vsync_start = 480 + 5, 836 .vsync_end = 480 + 5 + 2, 837 .vtotal = 480 + 5 + 2 + 13, 838 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 839 }, 840 { /* 50 Hz */ 841 .clock = 18000, 842 .hdisplay = 640, 843 .hsync_start = 640 + 39, 844 .hsync_end = 640 + 39 + 2, 845 .htotal = 640 + 39 + 2 + 39, 846 .vdisplay = 480, 847 .vsync_start = 480 + 5, 848 .vsync_end = 480 + 5 + 2, 849 .vtotal = 480 + 5 + 2 + 13, 850 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 851 }, 852 }; 853 854 static const struct drm_display_mode fs035vg158_modes[] = { 855 { /* 60 Hz */ 856 .clock = 21000, 857 .hdisplay = 640, 858 .hsync_start = 640 + 34, 859 .hsync_end = 640 + 34 + 4, 860 .htotal = 640 + 34 + 4 + 20, 861 .vdisplay = 480, 862 .vsync_start = 480 + 12, 863 .vsync_end = 480 + 12 + 4, 864 .vtotal = 480 + 12 + 4 + 6, 865 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 866 }, 867 }; 868 869 static const struct drm_display_mode wl_355608_a8_mode[] = { 870 { 871 .clock = 24000, 872 .hdisplay = 640, 873 .hsync_start = 640 + 64, 874 .hsync_end = 640 + 64 + 20, 875 .htotal = 640 + 64 + 20 + 46, 876 .vdisplay = 480, 877 .vsync_start = 480 + 21, 878 .vsync_end = 480 + 21 + 4, 879 .vtotal = 480 + 21 + 4 + 15, 880 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 881 }, 882 }; 883 884 static const struct nv3052c_panel_info ltk035c5444t_panel_info = { 885 .display_modes = ltk035c5444t_modes, 886 .num_modes = ARRAY_SIZE(ltk035c5444t_modes), 887 .width_mm = 77, 888 .height_mm = 64, 889 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 890 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 891 .panel_regs = ltk035c5444t_panel_regs, 892 .panel_regs_len = ARRAY_SIZE(ltk035c5444t_panel_regs), 893 }; 894 895 static const struct nv3052c_panel_info fs035vg158_panel_info = { 896 .display_modes = fs035vg158_modes, 897 .num_modes = ARRAY_SIZE(fs035vg158_modes), 898 .width_mm = 70, 899 .height_mm = 53, 900 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 901 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 902 .panel_regs = fs035vg158_panel_regs, 903 .panel_regs_len = ARRAY_SIZE(fs035vg158_panel_regs), 904 }; 905 906 static const struct nv3052c_panel_info wl_355608_a8_panel_info = { 907 .display_modes = wl_355608_a8_mode, 908 .num_modes = ARRAY_SIZE(wl_355608_a8_mode), 909 .width_mm = 150, 910 .height_mm = 94, 911 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 912 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 913 .panel_regs = wl_355608_a8_panel_regs, 914 .panel_regs_len = ARRAY_SIZE(wl_355608_a8_panel_regs), 915 }; 916 917 static const struct spi_device_id nv3052c_ids[] = { 918 { "ltk035c5444t", }, 919 { "fs035vg158", }, 920 { "wl-355608-a8", }, 921 { /* sentinel */ } 922 }; 923 MODULE_DEVICE_TABLE(spi, nv3052c_ids); 924 925 static const struct of_device_id nv3052c_of_match[] = { 926 { .compatible = "leadtek,ltk035c5444t", .data = <k035c5444t_panel_info }, 927 { .compatible = "fascontek,fs035vg158", .data = &fs035vg158_panel_info }, 928 { .compatible = "anbernic,rg35xx-plus-panel", .data = &wl_355608_a8_panel_info }, 929 { /* sentinel */ } 930 }; 931 MODULE_DEVICE_TABLE(of, nv3052c_of_match); 932 933 static struct spi_driver nv3052c_driver = { 934 .driver = { 935 .name = "nv3052c", 936 .of_match_table = nv3052c_of_match, 937 }, 938 .id_table = nv3052c_ids, 939 .probe = nv3052c_probe, 940 .remove = nv3052c_remove, 941 }; 942 module_spi_driver(nv3052c_driver); 943 944 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 945 MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); 946 MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com"); 947 MODULE_DESCRIPTION("NewVision NV3052C IPS LCD panel driver"); 948 MODULE_LICENSE("GPL v2"); 949