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 static inline struct nv3052c *to_nv3052c(struct drm_panel *panel) 437 { 438 return container_of(panel, struct nv3052c, panel); 439 } 440 441 static int nv3052c_prepare(struct drm_panel *panel) 442 { 443 struct nv3052c *priv = to_nv3052c(panel); 444 const struct nv3052c_reg *panel_regs = priv->panel_info->panel_regs; 445 unsigned int panel_regs_len = priv->panel_info->panel_regs_len; 446 struct mipi_dbi *dbi = &priv->dbi; 447 unsigned int i; 448 int err; 449 450 err = regulator_enable(priv->supply); 451 if (err) { 452 dev_err(priv->dev, "Failed to enable power supply: %d\n", err); 453 return err; 454 } 455 456 /* Reset the chip */ 457 gpiod_set_value_cansleep(priv->reset_gpio, 1); 458 usleep_range(10, 1000); 459 gpiod_set_value_cansleep(priv->reset_gpio, 0); 460 usleep_range(5000, 20000); 461 462 for (i = 0; i < panel_regs_len; i++) { 463 err = mipi_dbi_command(dbi, panel_regs[i].cmd, 464 panel_regs[i].val); 465 466 if (err) { 467 dev_err(priv->dev, "Unable to set register: %d\n", err); 468 goto err_disable_regulator; 469 } 470 } 471 472 err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 473 if (err) { 474 dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err); 475 goto err_disable_regulator; 476 } 477 478 return 0; 479 480 err_disable_regulator: 481 regulator_disable(priv->supply); 482 return err; 483 } 484 485 static int nv3052c_unprepare(struct drm_panel *panel) 486 { 487 struct nv3052c *priv = to_nv3052c(panel); 488 struct mipi_dbi *dbi = &priv->dbi; 489 int err; 490 491 err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); 492 if (err) 493 dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err); 494 495 gpiod_set_value_cansleep(priv->reset_gpio, 1); 496 regulator_disable(priv->supply); 497 498 return 0; 499 } 500 501 static int nv3052c_enable(struct drm_panel *panel) 502 { 503 struct nv3052c *priv = to_nv3052c(panel); 504 struct mipi_dbi *dbi = &priv->dbi; 505 int err; 506 507 err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 508 if (err) { 509 dev_err(priv->dev, "Unable to enable display: %d\n", err); 510 return err; 511 } 512 513 if (panel->backlight) { 514 /* Wait for the picture to be ready before enabling backlight */ 515 msleep(120); 516 } 517 518 return 0; 519 } 520 521 static int nv3052c_disable(struct drm_panel *panel) 522 { 523 struct nv3052c *priv = to_nv3052c(panel); 524 struct mipi_dbi *dbi = &priv->dbi; 525 int err; 526 527 err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 528 if (err) { 529 dev_err(priv->dev, "Unable to disable display: %d\n", err); 530 return err; 531 } 532 533 return 0; 534 } 535 536 static int nv3052c_get_modes(struct drm_panel *panel, 537 struct drm_connector *connector) 538 { 539 struct nv3052c *priv = to_nv3052c(panel); 540 const struct nv3052c_panel_info *panel_info = priv->panel_info; 541 struct drm_display_mode *mode; 542 unsigned int i; 543 544 for (i = 0; i < panel_info->num_modes; i++) { 545 mode = drm_mode_duplicate(connector->dev, 546 &panel_info->display_modes[i]); 547 if (!mode) 548 return -ENOMEM; 549 550 drm_mode_set_name(mode); 551 552 mode->type = DRM_MODE_TYPE_DRIVER; 553 if (panel_info->num_modes == 1) 554 mode->type |= DRM_MODE_TYPE_PREFERRED; 555 556 drm_mode_probed_add(connector, mode); 557 } 558 559 connector->display_info.bpc = 8; 560 connector->display_info.width_mm = panel_info->width_mm; 561 connector->display_info.height_mm = panel_info->height_mm; 562 563 drm_display_info_set_bus_formats(&connector->display_info, 564 &panel_info->bus_format, 1); 565 connector->display_info.bus_flags = panel_info->bus_flags; 566 567 return panel_info->num_modes; 568 } 569 570 static const struct drm_panel_funcs nv3052c_funcs = { 571 .prepare = nv3052c_prepare, 572 .unprepare = nv3052c_unprepare, 573 .enable = nv3052c_enable, 574 .disable = nv3052c_disable, 575 .get_modes = nv3052c_get_modes, 576 }; 577 578 static int nv3052c_probe(struct spi_device *spi) 579 { 580 struct device *dev = &spi->dev; 581 struct nv3052c *priv; 582 int err; 583 584 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 585 if (!priv) 586 return -ENOMEM; 587 588 priv->dev = dev; 589 590 priv->panel_info = of_device_get_match_data(dev); 591 if (!priv->panel_info) 592 return -EINVAL; 593 594 priv->supply = devm_regulator_get(dev, "power"); 595 if (IS_ERR(priv->supply)) 596 return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n"); 597 598 priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 599 if (IS_ERR(priv->reset_gpio)) 600 return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); 601 602 err = mipi_dbi_spi_init(spi, &priv->dbi, NULL); 603 if (err) 604 return dev_err_probe(dev, err, "MIPI DBI init failed\n"); 605 606 priv->dbi.read_commands = NULL; 607 608 spi_set_drvdata(spi, priv); 609 610 drm_panel_init(&priv->panel, dev, &nv3052c_funcs, 611 DRM_MODE_CONNECTOR_DPI); 612 613 err = drm_panel_of_backlight(&priv->panel); 614 if (err) 615 return dev_err_probe(dev, err, "Failed to attach backlight\n"); 616 617 drm_panel_add(&priv->panel); 618 619 return 0; 620 } 621 622 static void nv3052c_remove(struct spi_device *spi) 623 { 624 struct nv3052c *priv = spi_get_drvdata(spi); 625 626 drm_panel_remove(&priv->panel); 627 drm_panel_disable(&priv->panel); 628 drm_panel_unprepare(&priv->panel); 629 } 630 631 static const struct drm_display_mode ltk035c5444t_modes[] = { 632 { /* 60 Hz */ 633 .clock = 24000, 634 .hdisplay = 640, 635 .hsync_start = 640 + 96, 636 .hsync_end = 640 + 96 + 16, 637 .htotal = 640 + 96 + 16 + 48, 638 .vdisplay = 480, 639 .vsync_start = 480 + 5, 640 .vsync_end = 480 + 5 + 2, 641 .vtotal = 480 + 5 + 2 + 13, 642 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 643 }, 644 { /* 50 Hz */ 645 .clock = 18000, 646 .hdisplay = 640, 647 .hsync_start = 640 + 39, 648 .hsync_end = 640 + 39 + 2, 649 .htotal = 640 + 39 + 2 + 39, 650 .vdisplay = 480, 651 .vsync_start = 480 + 5, 652 .vsync_end = 480 + 5 + 2, 653 .vtotal = 480 + 5 + 2 + 13, 654 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 655 }, 656 }; 657 658 static const struct drm_display_mode fs035vg158_modes[] = { 659 { /* 60 Hz */ 660 .clock = 21000, 661 .hdisplay = 640, 662 .hsync_start = 640 + 34, 663 .hsync_end = 640 + 34 + 4, 664 .htotal = 640 + 34 + 4 + 20, 665 .vdisplay = 480, 666 .vsync_start = 480 + 12, 667 .vsync_end = 480 + 12 + 4, 668 .vtotal = 480 + 12 + 4 + 6, 669 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 670 }, 671 }; 672 673 static const struct nv3052c_panel_info ltk035c5444t_panel_info = { 674 .display_modes = ltk035c5444t_modes, 675 .num_modes = ARRAY_SIZE(ltk035c5444t_modes), 676 .width_mm = 77, 677 .height_mm = 64, 678 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 679 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 680 .panel_regs = ltk035c5444t_panel_regs, 681 .panel_regs_len = ARRAY_SIZE(ltk035c5444t_panel_regs), 682 }; 683 684 static const struct nv3052c_panel_info fs035vg158_panel_info = { 685 .display_modes = fs035vg158_modes, 686 .num_modes = ARRAY_SIZE(fs035vg158_modes), 687 .width_mm = 70, 688 .height_mm = 53, 689 .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 690 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, 691 .panel_regs = fs035vg158_panel_regs, 692 .panel_regs_len = ARRAY_SIZE(fs035vg158_panel_regs), 693 }; 694 695 static const struct spi_device_id nv3052c_ids[] = { 696 { "ltk035c5444t", }, 697 { "fs035vg158", }, 698 { /* sentinel */ } 699 }; 700 MODULE_DEVICE_TABLE(spi, nv3052c_ids); 701 702 static const struct of_device_id nv3052c_of_match[] = { 703 { .compatible = "leadtek,ltk035c5444t", .data = <k035c5444t_panel_info }, 704 { .compatible = "fascontek,fs035vg158", .data = &fs035vg158_panel_info }, 705 { /* sentinel */ } 706 }; 707 MODULE_DEVICE_TABLE(of, nv3052c_of_match); 708 709 static struct spi_driver nv3052c_driver = { 710 .driver = { 711 .name = "nv3052c", 712 .of_match_table = nv3052c_of_match, 713 }, 714 .id_table = nv3052c_ids, 715 .probe = nv3052c_probe, 716 .remove = nv3052c_remove, 717 }; 718 module_spi_driver(nv3052c_driver); 719 720 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 721 MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); 722 MODULE_LICENSE("GPL v2"); 723