949 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * NewVision NV3052C IPS LCD panel driver4 *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 23struct nv3052c_reg {24 u8 cmd;25 u8 val;26};27 28struct 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 37struct 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 46static const struct nv3052c_reg ltk035c5444t_panel_regs[] = {47 // EXTC Command set enable, select page 148 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 },49 // Mostly unknown registers50 { 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_ADJ168 { 0x39, 0xd7 }, // VCOM_ADJ269 { 0x3a, 0x4a }, // VCOM_ADJ370 { 0x28, 0x40 },71 { 0x29, 0x01 },72 { 0x2a, 0xdf },73 { 0x49, 0x3c },74 { 0x91, 0x77 }, // EXTPW_CTRL275 { 0x92, 0x77 }, // EXTPW_CTRL376 { 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 2100 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },101 // Set gray scale voltage to adjust gamma102 { 0xb0, 0x0b }, // PGAMVR0103 { 0xb1, 0x16 }, // PGAMVR1104 { 0xb2, 0x17 }, // PGAMVR2105 { 0xb3, 0x2c }, // PGAMVR3106 { 0xb4, 0x32 }, // PGAMVR4107 { 0xb5, 0x3b }, // PGAMVR5108 { 0xb6, 0x29 }, // PGAMPR0109 { 0xb7, 0x40 }, // PGAMPR1110 { 0xb8, 0x0d }, // PGAMPK0111 { 0xb9, 0x05 }, // PGAMPK1112 { 0xba, 0x12 }, // PGAMPK2113 { 0xbb, 0x10 }, // PGAMPK3114 { 0xbc, 0x12 }, // PGAMPK4115 { 0xbd, 0x15 }, // PGAMPK5116 { 0xbe, 0x19 }, // PGAMPK6117 { 0xbf, 0x0e }, // PGAMPK7118 { 0xc0, 0x16 }, // PGAMPK8119 { 0xc1, 0x0a }, // PGAMPK9120 // Set gray scale voltage to adjust gamma121 { 0xd0, 0x0c }, // NGAMVR0122 { 0xd1, 0x17 }, // NGAMVR0123 { 0xd2, 0x14 }, // NGAMVR1124 { 0xd3, 0x2e }, // NGAMVR2125 { 0xd4, 0x32 }, // NGAMVR3126 { 0xd5, 0x3c }, // NGAMVR4127 { 0xd6, 0x22 }, // NGAMPR0128 { 0xd7, 0x3d }, // NGAMPR1129 { 0xd8, 0x0d }, // NGAMPK0130 { 0xd9, 0x07 }, // NGAMPK1131 { 0xda, 0x13 }, // NGAMPK2132 { 0xdb, 0x13 }, // NGAMPK3133 { 0xdc, 0x11 }, // NGAMPK4134 { 0xdd, 0x15 }, // NGAMPK5135 { 0xde, 0x19 }, // NGAMPK6136 { 0xdf, 0x10 }, // NGAMPK7137 { 0xe0, 0x17 }, // NGAMPK8138 { 0xe1, 0x0a }, // NGAMPK9139 // EXTC Command set enable, select page 3140 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 },141 // Set various timing settings142 { 0x00, 0x2a }, // GIP_VST_1143 { 0x01, 0x2a }, // GIP_VST_2144 { 0x02, 0x2a }, // GIP_VST_3145 { 0x03, 0x2a }, // GIP_VST_4146 { 0x04, 0x61 }, // GIP_VST_5147 { 0x05, 0x80 }, // GIP_VST_6148 { 0x06, 0xc7 }, // GIP_VST_7149 { 0x07, 0x01 }, // GIP_VST_8150 { 0x08, 0x03 }, // GIP_VST_9151 { 0x09, 0x04 }, // GIP_VST_10152 { 0x70, 0x22 }, // GIP_ECLK1153 { 0x71, 0x80 }, // GIP_ECLK2154 { 0x30, 0x2a }, // GIP_CLK_1155 { 0x31, 0x2a }, // GIP_CLK_2156 { 0x32, 0x2a }, // GIP_CLK_3157 { 0x33, 0x2a }, // GIP_CLK_4158 { 0x34, 0x61 }, // GIP_CLK_5159 { 0x35, 0xc5 }, // GIP_CLK_6160 { 0x36, 0x80 }, // GIP_CLK_7161 { 0x37, 0x23 }, // GIP_CLK_8162 { 0x40, 0x03 }, // GIP_CLKA_1163 { 0x41, 0x04 }, // GIP_CLKA_2164 { 0x42, 0x05 }, // GIP_CLKA_3165 { 0x43, 0x06 }, // GIP_CLKA_4166 { 0x44, 0x11 }, // GIP_CLKA_5167 { 0x45, 0xe8 }, // GIP_CLKA_6168 { 0x46, 0xe9 }, // GIP_CLKA_7169 { 0x47, 0x11 }, // GIP_CLKA_8170 { 0x48, 0xea }, // GIP_CLKA_9171 { 0x49, 0xeb }, // GIP_CLKA_10172 { 0x50, 0x07 }, // GIP_CLKB_1173 { 0x51, 0x08 }, // GIP_CLKB_2174 { 0x52, 0x09 }, // GIP_CLKB_3175 { 0x53, 0x0a }, // GIP_CLKB_4176 { 0x54, 0x11 }, // GIP_CLKB_5177 { 0x55, 0xec }, // GIP_CLKB_6178 { 0x56, 0xed }, // GIP_CLKB_7179 { 0x57, 0x11 }, // GIP_CLKB_8180 { 0x58, 0xef }, // GIP_CLKB_9181 { 0x59, 0xf0 }, // GIP_CLKB_10182 // Map internal GOA signals to GOA output pad183 { 0xb1, 0x01 }, // PANELD2U2184 { 0xb4, 0x15 }, // PANELD2U5185 { 0xb5, 0x16 }, // PANELD2U6186 { 0xb6, 0x09 }, // PANELD2U7187 { 0xb7, 0x0f }, // PANELD2U8188 { 0xb8, 0x0d }, // PANELD2U9189 { 0xb9, 0x0b }, // PANELD2U10190 { 0xba, 0x00 }, // PANELD2U11191 { 0xc7, 0x02 }, // PANELD2U24192 { 0xca, 0x17 }, // PANELD2U27193 { 0xcb, 0x18 }, // PANELD2U28194 { 0xcc, 0x0a }, // PANELD2U29195 { 0xcd, 0x10 }, // PANELD2U30196 { 0xce, 0x0e }, // PANELD2U31197 { 0xcf, 0x0c }, // PANELD2U32198 { 0xd0, 0x00 }, // PANELD2U33199 // Map internal GOA signals to GOA output pad200 { 0x81, 0x00 }, // PANELU2D2201 { 0x84, 0x15 }, // PANELU2D5202 { 0x85, 0x16 }, // PANELU2D6203 { 0x86, 0x10 }, // PANELU2D7204 { 0x87, 0x0a }, // PANELU2D8205 { 0x88, 0x0c }, // PANELU2D9206 { 0x89, 0x0e }, // PANELU2D10207 { 0x8a, 0x02 }, // PANELU2D11208 { 0x97, 0x00 }, // PANELU2D24209 { 0x9a, 0x17 }, // PANELU2D27210 { 0x9b, 0x18 }, // PANELU2D28211 { 0x9c, 0x0f }, // PANELU2D29212 { 0x9d, 0x09 }, // PANELU2D30213 { 0x9e, 0x0b }, // PANELU2D31214 { 0x9f, 0x0d }, // PANELU2D32215 { 0xa0, 0x01 }, // PANELU2D33216 // EXTC Command set enable, select page 2217 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },218 // Unknown registers219 { 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 0236 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },237 // Display Access Control238 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0239};240 241static const struct nv3052c_reg fs035vg158_panel_regs[] = {242 // EXTC Command set enable, select page 1243 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 },244 // Mostly unknown registers245 { 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-N258 { 0x24, 0x0c },259 { 0x25, 0x06 },260 { 0x26, 0x14 },261 { 0x27, 0x14 },262 { 0x38, 0x9c }, //VCOM_ADJ1, different to ltk035c5444t263 { 0x39, 0xa7 }, //VCOM_ADJ2, different to ltk035c5444t264 { 0x3a, 0x50 }, //VCOM_ADJ3, different to ltk035c5444t265 { 0x28, 0x40 },266 { 0x29, 0x01 },267 { 0x2a, 0xdf },268 { 0x49, 0x3c },269 { 0x91, 0x57 }, //EXTPW_CTRL2, different to ltk035c5444t270 { 0x92, 0x57 }, //EXTPW_CTRL3, different to ltk035c5444t271 { 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 ltk035c5444288 { 0xb5, 0x08 },289 { 0xb6, 0x26 },290 { 0xb7, 0x08 },291 { 0xb8, 0x26 },292 { 0xf0, 0x00 },293 { 0xf6, 0xc0 },294 // EXTC Command set enable, select page 0295 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },296 // Set gray scale voltage to adjust gamma297 { 0xb0, 0x0b }, // PGAMVR0298 { 0xb1, 0x16 }, // PGAMVR1299 { 0xb2, 0x17 }, // PGAMVR2300 { 0xb3, 0x2c }, // PGAMVR3301 { 0xb4, 0x32 }, // PGAMVR4302 { 0xb5, 0x3b }, // PGAMVR5303 { 0xb6, 0x29 }, // PGAMPR0304 { 0xb7, 0x40 }, // PGAMPR1305 { 0xb8, 0x0d }, // PGAMPK0306 { 0xb9, 0x05 }, // PGAMPK1307 { 0xba, 0x12 }, // PGAMPK2308 { 0xbb, 0x10 }, // PGAMPK3309 { 0xbc, 0x12 }, // PGAMPK4310 { 0xbd, 0x15 }, // PGAMPK5311 { 0xbe, 0x19 }, // PGAMPK6312 { 0xbf, 0x0e }, // PGAMPK7313 { 0xc0, 0x16 }, // PGAMPK8314 { 0xc1, 0x0a }, // PGAMPK9315 // Set gray scale voltage to adjust gamma316 { 0xd0, 0x0c }, // NGAMVR0317 { 0xd1, 0x17 }, // NGAMVR0318 { 0xd2, 0x14 }, // NGAMVR1319 { 0xd3, 0x2e }, // NGAMVR2320 { 0xd4, 0x32 }, // NGAMVR3321 { 0xd5, 0x3c }, // NGAMVR4322 { 0xd6, 0x22 }, // NGAMPR0323 { 0xd7, 0x3d }, // NGAMPR1324 { 0xd8, 0x0d }, // NGAMPK0325 { 0xd9, 0x07 }, // NGAMPK1326 { 0xda, 0x13 }, // NGAMPK2327 { 0xdb, 0x13 }, // NGAMPK3328 { 0xdc, 0x11 }, // NGAMPK4329 { 0xdd, 0x15 }, // NGAMPK5330 { 0xde, 0x19 }, // NGAMPK6331 { 0xdf, 0x10 }, // NGAMPK7332 { 0xe0, 0x17 }, // NGAMPK8333 { 0xe1, 0x0a }, // NGAMPK9334 // EXTC Command set enable, select page 3335 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 },336 // Set various timing settings337 { 0x00, 0x2a }, // GIP_VST_1338 { 0x01, 0x2a }, // GIP_VST_2339 { 0x02, 0x2a }, // GIP_VST_3340 { 0x03, 0x2a }, // GIP_VST_4341 { 0x04, 0x61 }, // GIP_VST_5342 { 0x05, 0x80 }, // GIP_VST_6343 { 0x06, 0xc7 }, // GIP_VST_7344 { 0x07, 0x01 }, // GIP_VST_8345 { 0x08, 0x03 }, // GIP_VST_9346 { 0x09, 0x04 }, // GIP_VST_10347 { 0x70, 0x22 }, // GIP_ECLK1348 { 0x71, 0x80 }, // GIP_ECLK2349 { 0x30, 0x2a }, // GIP_CLK_1350 { 0x31, 0x2a }, // GIP_CLK_2351 { 0x32, 0x2a }, // GIP_CLK_3352 { 0x33, 0x2a }, // GIP_CLK_4353 { 0x34, 0x61 }, // GIP_CLK_5354 { 0x35, 0xc5 }, // GIP_CLK_6355 { 0x36, 0x80 }, // GIP_CLK_7356 { 0x37, 0x23 }, // GIP_CLK_8357 { 0x40, 0x03 }, // GIP_CLKA_1358 { 0x41, 0x04 }, // GIP_CLKA_2359 { 0x42, 0x05 }, // GIP_CLKA_3360 { 0x43, 0x06 }, // GIP_CLKA_4361 { 0x44, 0x11 }, // GIP_CLKA_5362 { 0x45, 0xe8 }, // GIP_CLKA_6363 { 0x46, 0xe9 }, // GIP_CLKA_7364 { 0x47, 0x11 }, // GIP_CLKA_8365 { 0x48, 0xea }, // GIP_CLKA_9366 { 0x49, 0xeb }, // GIP_CLKA_10367 { 0x50, 0x07 }, // GIP_CLKB_1368 { 0x51, 0x08 }, // GIP_CLKB_2369 { 0x52, 0x09 }, // GIP_CLKB_3370 { 0x53, 0x0a }, // GIP_CLKB_4371 { 0x54, 0x11 }, // GIP_CLKB_5372 { 0x55, 0xec }, // GIP_CLKB_6373 { 0x56, 0xed }, // GIP_CLKB_7374 { 0x57, 0x11 }, // GIP_CLKB_8375 { 0x58, 0xef }, // GIP_CLKB_9376 { 0x59, 0xf0 }, // GIP_CLKB_10377 // Map internal GOA signals to GOA output pad378 { 0xb1, 0x01 }, // PANELD2U2379 { 0xb4, 0x15 }, // PANELD2U5380 { 0xb5, 0x16 }, // PANELD2U6381 { 0xb6, 0x09 }, // PANELD2U7382 { 0xb7, 0x0f }, // PANELD2U8383 { 0xb8, 0x0d }, // PANELD2U9384 { 0xb9, 0x0b }, // PANELD2U10385 { 0xba, 0x00 }, // PANELD2U11386 { 0xc7, 0x02 }, // PANELD2U24387 { 0xca, 0x17 }, // PANELD2U27388 { 0xcb, 0x18 }, // PANELD2U28389 { 0xcc, 0x0a }, // PANELD2U29390 { 0xcd, 0x10 }, // PANELD2U30391 { 0xce, 0x0e }, // PANELD2U31392 { 0xcf, 0x0c }, // PANELD2U32393 { 0xd0, 0x00 }, // PANELD2U33394 // Map internal GOA signals to GOA output pad395 { 0x81, 0x00 }, // PANELU2D2396 { 0x84, 0x15 }, // PANELU2D5397 { 0x85, 0x16 }, // PANELU2D6398 { 0x86, 0x10 }, // PANELU2D7399 { 0x87, 0x0a }, // PANELU2D8400 { 0x88, 0x0c }, // PANELU2D9401 { 0x89, 0x0e }, // PANELU2D10402 { 0x8a, 0x02 }, // PANELU2D11403 { 0x97, 0x00 }, // PANELU2D24404 { 0x9a, 0x17 }, // PANELU2D27405 { 0x9b, 0x18 }, // PANELU2D28406 { 0x9c, 0x0f }, // PANELU2D29407 { 0x9d, 0x09 }, // PANELU2D30408 { 0x9e, 0x0b }, // PANELU2D31409 { 0x9f, 0x0d }, // PANELU2D32410 { 0xa0, 0x01 }, // PANELU2D33411 // EXTC Command set enable, select page 2412 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },413 // Unknown registers414 { 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 0431 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },432 // Display Access Control433 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0434};435 436 437static const struct nv3052c_reg wl_355608_a8_panel_regs[] = {438 // EXTC Command set enable, select page 1439 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 },440 // Mostly unknown registers441 { 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_ADJ1459 { 0x39, 0xd7 }, // VCOM_ADJ2460 { 0x3a, 0x44 }, // VCOM_ADJ3461 { 0x28, 0x40 },462 { 0x29, 0x01 },463 { 0x2a, 0xdf },464 { 0x49, 0x3c },465 { 0x91, 0x77 }, // EXTPW_CTRL2466 { 0x92, 0x77 }, // EXTPW_CTRL3467 { 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 2491 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },492 // Set gray scale voltage to adjust gamma493 { 0xb0, 0x0b }, // PGAMVR0494 { 0xb1, 0x16 }, // PGAMVR1495 { 0xb2, 0x17 }, // PGAMVR2496 { 0xb3, 0x2c }, // PGAMVR3497 { 0xb4, 0x32 }, // PGAMVR4498 { 0xb5, 0x3b }, // PGAMVR5499 { 0xb6, 0x29 }, // PGAMPR0500 { 0xb7, 0x40 }, // PGAMPR1501 { 0xb8, 0x0d }, // PGAMPK0502 { 0xb9, 0x05 }, // PGAMPK1503 { 0xba, 0x12 }, // PGAMPK2504 { 0xbb, 0x10 }, // PGAMPK3505 { 0xbc, 0x12 }, // PGAMPK4506 { 0xbd, 0x15 }, // PGAMPK5507 { 0xbe, 0x19 }, // PGAMPK6508 { 0xbf, 0x0e }, // PGAMPK7509 { 0xc0, 0x16 }, // PGAMPK8510 { 0xc1, 0x0a }, // PGAMPK9511 // Set gray scale voltage to adjust gamma512 { 0xd0, 0x0c }, // NGAMVR0513 { 0xd1, 0x17 }, // NGAMVR0514 { 0xd2, 0x14 }, // NGAMVR1515 { 0xd3, 0x2e }, // NGAMVR2516 { 0xd4, 0x32 }, // NGAMVR3517 { 0xd5, 0x3c }, // NGAMVR4518 { 0xd6, 0x22 }, // NGAMPR0519 { 0xd7, 0x3d }, // NGAMPR1520 { 0xd8, 0x0d }, // NGAMPK0521 { 0xd9, 0x07 }, // NGAMPK1522 { 0xda, 0x13 }, // NGAMPK2523 { 0xdb, 0x13 }, // NGAMPK3524 { 0xdc, 0x11 }, // NGAMPK4525 { 0xdd, 0x15 }, // NGAMPK5526 { 0xde, 0x19 }, // NGAMPK6527 { 0xdf, 0x10 }, // NGAMPK7528 { 0xe0, 0x17 }, // NGAMPK8529 { 0xe1, 0x0a }, // NGAMPK9530 // EXTC Command set enable, select page 3531 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 },532 // Set various timing settings533 { 0x00, 0x2a }, // GIP_VST_1534 { 0x01, 0x2a }, // GIP_VST_2535 { 0x02, 0x2a }, // GIP_VST_3536 { 0x03, 0x2a }, // GIP_VST_4537 { 0x04, 0x61 }, // GIP_VST_5538 { 0x05, 0x80 }, // GIP_VST_6539 { 0x06, 0xc7 }, // GIP_VST_7540 { 0x07, 0x01 }, // GIP_VST_8541 { 0x08, 0x03 }, // GIP_VST_9542 { 0x09, 0x04 }, // GIP_VST_10543 { 0x70, 0x22 }, // GIP_ECLK1544 { 0x71, 0x80 }, // GIP_ECLK2545 { 0x30, 0x2a }, // GIP_CLK_1546 { 0x31, 0x2a }, // GIP_CLK_2547 { 0x32, 0x2a }, // GIP_CLK_3548 { 0x33, 0x2a }, // GIP_CLK_4549 { 0x34, 0x61 }, // GIP_CLK_5550 { 0x35, 0xc5 }, // GIP_CLK_6551 { 0x36, 0x80 }, // GIP_CLK_7552 { 0x37, 0x23 }, // GIP_CLK_8553 { 0x40, 0x03 }, // GIP_CLKA_1554 { 0x41, 0x04 }, // GIP_CLKA_2555 { 0x42, 0x05 }, // GIP_CLKA_3556 { 0x43, 0x06 }, // GIP_CLKA_4557 { 0x44, 0x11 }, // GIP_CLKA_5558 { 0x45, 0xe8 }, // GIP_CLKA_6559 { 0x46, 0xe9 }, // GIP_CLKA_7560 { 0x47, 0x11 }, // GIP_CLKA_8561 { 0x48, 0xea }, // GIP_CLKA_9562 { 0x49, 0xeb }, // GIP_CLKA_10563 { 0x50, 0x07 }, // GIP_CLKB_1564 { 0x51, 0x08 }, // GIP_CLKB_2565 { 0x52, 0x09 }, // GIP_CLKB_3566 { 0x53, 0x0a }, // GIP_CLKB_4567 { 0x54, 0x11 }, // GIP_CLKB_5568 { 0x55, 0xec }, // GIP_CLKB_6569 { 0x56, 0xed }, // GIP_CLKB_7570 { 0x57, 0x11 }, // GIP_CLKB_8571 { 0x58, 0xef }, // GIP_CLKB_9572 { 0x59, 0xf0 }, // GIP_CLKB_10573 // Map internal GOA signals to GOA output pad574 { 0xb1, 0x01 }, // PANELD2U2575 { 0xb4, 0x15 }, // PANELD2U5576 { 0xb5, 0x16 }, // PANELD2U6577 { 0xb6, 0x09 }, // PANELD2U7578 { 0xb7, 0x0f }, // PANELD2U8579 { 0xb8, 0x0d }, // PANELD2U9580 { 0xb9, 0x0b }, // PANELD2U10581 { 0xba, 0x00 }, // PANELD2U11582 { 0xc7, 0x02 }, // PANELD2U24583 { 0xca, 0x17 }, // PANELD2U27584 { 0xcb, 0x18 }, // PANELD2U28585 { 0xcc, 0x0a }, // PANELD2U29586 { 0xcd, 0x10 }, // PANELD2U30587 { 0xce, 0x0e }, // PANELD2U31588 { 0xcf, 0x0c }, // PANELD2U32589 { 0xd0, 0x00 }, // PANELD2U33590 // Map internal GOA signals to GOA output pad591 { 0x81, 0x00 }, // PANELU2D2592 { 0x84, 0x15 }, // PANELU2D5593 { 0x85, 0x16 }, // PANELU2D6594 { 0x86, 0x10 }, // PANELU2D7595 { 0x87, 0x0a }, // PANELU2D8596 { 0x88, 0x0c }, // PANELU2D9597 { 0x89, 0x0e }, // PANELU2D10598 { 0x8a, 0x02 }, // PANELU2D11599 { 0x97, 0x00 }, // PANELU2D24600 { 0x9a, 0x17 }, // PANELU2D27601 { 0x9b, 0x18 }, // PANELU2D28602 { 0x9c, 0x0f }, // PANELU2D29603 { 0x9d, 0x09 }, // PANELU2D30604 { 0x9e, 0x0b }, // PANELU2D31605 { 0x9f, 0x0d }, // PANELU2D32606 { 0xa0, 0x01 }, // PANELU2D33607 // EXTC Command set enable, select page 2608 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },609 // Unknown registers610 { 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 0627 { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },628 // Display Access Control629 { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0630};631 632static inline struct nv3052c *to_nv3052c(struct drm_panel *panel)633{634 return container_of(panel, struct nv3052c, panel);635}636 637static 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 676err_disable_regulator:677 regulator_disable(priv->supply);678 return err;679}680 681static 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 697static 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 717static 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 732static 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 766static 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 774static 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 818static 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 827static 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 854static 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 869static 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 884static 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 895static 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 906static 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 917static const struct spi_device_id nv3052c_ids[] = {918 { "ltk035c5444t", },919 { "fs035vg158", },920 { "wl-355608-a8", },921 { /* sentinel */ }922};923MODULE_DEVICE_TABLE(spi, nv3052c_ids);924 925static 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};931MODULE_DEVICE_TABLE(of, nv3052c_of_match);932 933static 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};942module_spi_driver(nv3052c_driver);943 944MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");945MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");946MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com");947MODULE_DESCRIPTION("NewVision NV3052C IPS LCD panel driver");948MODULE_LICENSE("GPL v2");949