1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * S6E63M0 AMOLED LCD drm_panel driver. 4 * 5 * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com> 6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c 7 * 8 * Andrzej Hajda <a.hajda@samsung.com> 9 */ 10 11 #include <drm/drm_modes.h> 12 #include <drm/drm_panel.h> 13 14 #include <linux/backlight.h> 15 #include <linux/delay.h> 16 #include <linux/gpio/consumer.h> 17 #include <linux/module.h> 18 #include <linux/regulator/consumer.h> 19 #include <linux/media-bus-format.h> 20 21 #include <video/mipi_display.h> 22 23 #include "panel-samsung-s6e63m0.h" 24 25 #define S6E63M0_LCD_ID_VALUE_M2 0xA4 26 #define S6E63M0_LCD_ID_VALUE_SM2 0xB4 27 #define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6 28 29 #define NUM_GAMMA_LEVELS 28 30 #define GAMMA_TABLE_COUNT 23 31 32 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1) 33 34 /* array of gamma tables for gamma value 2.2 */ 35 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = { 36 /* 30 cd */ 37 { MCS_PGAMMACTL, 0x02, 38 0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE, 39 0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD, 40 0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, }, 41 /* 40 cd */ 42 { MCS_PGAMMACTL, 0x02, 43 0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC, 44 0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC, 45 0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, }, 46 /* 50 cd */ 47 { MCS_PGAMMACTL, 0x02, 48 0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB, 49 0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9, 50 0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, }, 51 /* 60 cd */ 52 { MCS_PGAMMACTL, 0x02, 53 0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9, 54 0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7, 55 0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, }, 56 /* 70 cd */ 57 { MCS_PGAMMACTL, 0x02, 58 0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7, 59 0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7, 60 0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, }, 61 /* 80 cd */ 62 { MCS_PGAMMACTL, 0x02, 63 0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9, 64 0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5, 65 0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, }, 66 /* 90 cd */ 67 { MCS_PGAMMACTL, 0x02, 68 0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7, 69 0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5, 70 0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, }, 71 /* 100 cd */ 72 { MCS_PGAMMACTL, 0x02, 73 0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6, 74 0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4, 75 0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, }, 76 /* 110 cd */ 77 { MCS_PGAMMACTL, 0x02, 78 0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5, 79 0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4, 80 0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, }, 81 /* 120 cd */ 82 { MCS_PGAMMACTL, 0x02, 83 0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4, 84 0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2, 85 0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, }, 86 /* 130 cd */ 87 { MCS_PGAMMACTL, 0x02, 88 0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3, 89 0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2, 90 0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, }, 91 /* 140 cd */ 92 { MCS_PGAMMACTL, 0x02, 93 0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1, 94 0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0, 95 0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, }, 96 /* 150 cd */ 97 { MCS_PGAMMACTL, 0x02, 98 0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1, 99 0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0, 100 0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, }, 101 /* 160 cd */ 102 { MCS_PGAMMACTL, 0x02, 103 0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0, 104 0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF, 105 0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, }, 106 /* 170 cd */ 107 { MCS_PGAMMACTL, 0x02, 108 0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0, 109 0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF, 110 0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, }, 111 /* 180 cd */ 112 { MCS_PGAMMACTL, 0x02, 113 0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0, 114 0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE, 115 0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, }, 116 /* 190 cd */ 117 { MCS_PGAMMACTL, 0x02, 118 0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1, 119 0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE, 120 0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, }, 121 /* 200 cd */ 122 { MCS_PGAMMACTL, 0x02, 123 0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1, 124 0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD, 125 0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, }, 126 /* 210 cd */ 127 { MCS_PGAMMACTL, 0x02, 128 0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0, 129 0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC, 130 0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, }, 131 /* 220 cd */ 132 { MCS_PGAMMACTL, 0x02, 133 0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF, 134 0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC, 135 0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, }, 136 /* 230 cd */ 137 { MCS_PGAMMACTL, 0x02, 138 0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF, 139 0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB, 140 0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, }, 141 /* 240 cd */ 142 { MCS_PGAMMACTL, 0x02, 143 0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0, 144 0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB, 145 0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, }, 146 /* 250 cd */ 147 { MCS_PGAMMACTL, 0x02, 148 0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF, 149 0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA, 150 0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, }, 151 /* 260 cd */ 152 { MCS_PGAMMACTL, 0x02, 153 0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD, 154 0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9, 155 0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, }, 156 /* 270 cd */ 157 { MCS_PGAMMACTL, 0x02, 158 0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD, 159 0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9, 160 0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, }, 161 /* 280 cd */ 162 { MCS_PGAMMACTL, 0x02, 163 0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE, 164 0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8, 165 0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, }, 166 /* 290 cd */ 167 { MCS_PGAMMACTL, 0x02, 168 0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD, 169 0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7, 170 0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, }, 171 /* 300 cd */ 172 { MCS_PGAMMACTL, 0x02, 173 0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC, 174 0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7, 175 0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, }, 176 }; 177 178 #define NUM_ACL_LEVELS 7 179 #define ACL_TABLE_COUNT 28 180 181 static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = { 182 /* NULL ACL */ 183 { MCS_BCMODE, 184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 187 0x00, 0x00, 0x00 }, 188 /* 40P ACL */ 189 { MCS_BCMODE, 190 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00, 191 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 192 0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26, 193 0x2B, 0x31, 0x36 }, 194 /* 43P ACL */ 195 { MCS_BCMODE, 196 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00, 197 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 198 0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29, 199 0x2F, 0x34, 0x3A }, 200 /* 45P ACL */ 201 { MCS_BCMODE, 202 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00, 203 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 204 0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B, 205 0x31, 0x37, 0x3D }, 206 /* 47P ACL */ 207 { MCS_BCMODE, 208 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00, 209 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 210 0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E, 211 0x34, 0x3B, 0x41 }, 212 /* 48P ACL */ 213 { MCS_BCMODE, 214 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00, 215 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 216 0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F, 217 0x36, 0x3C, 0x43 }, 218 /* 50P ACL */ 219 { MCS_BCMODE, 220 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00, 221 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 222 0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31, 223 0x38, 0x3F, 0x46 }, 224 }; 225 226 /* This tells us which ACL level goes with which gamma */ 227 static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = { 228 /* 30 - 60 cd: ACL off/NULL */ 229 0, 0, 0, 0, 230 /* 70 - 250 cd: 40P ACL */ 231 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 232 /* 260 - 300 cd: 50P ACL */ 233 6, 6, 6, 6, 6, 234 }; 235 236 /* The ELVSS backlight regulator has 5 levels */ 237 #define S6E63M0_ELVSS_LEVELS 5 238 239 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = { 240 0x00, /* not set */ 241 0x0D, /* 30 cd - 100 cd */ 242 0x09, /* 110 cd - 160 cd */ 243 0x07, /* 170 cd - 200 cd */ 244 0x00, /* 210 cd - 300 cd */ 245 }; 246 247 /* This tells us which ELVSS level goes with which gamma */ 248 static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = { 249 /* 30 - 100 cd */ 250 1, 1, 1, 1, 1, 1, 1, 1, 251 /* 110 - 160 cd */ 252 2, 2, 2, 2, 2, 2, 253 /* 170 - 200 cd */ 254 3, 3, 3, 3, 255 /* 210 - 300 cd */ 256 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 257 }; 258 259 struct s6e63m0 { 260 struct device *dev; 261 void *transport_data; 262 int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val); 263 int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len); 264 struct drm_panel panel; 265 struct backlight_device *bl_dev; 266 u8 lcd_type; 267 u8 elvss_pulse; 268 bool dsi_mode; 269 270 struct regulator_bulk_data supplies[2]; 271 struct gpio_desc *reset_gpio; 272 273 bool prepared; 274 bool enabled; 275 276 /* 277 * This field is tested by functions directly accessing bus before 278 * transfer, transfer is skipped if it is set. In case of transfer 279 * failure or unexpected response the field is set to error value. 280 * Such construct allows to eliminate many checks in higher level 281 * functions. 282 */ 283 int error; 284 }; 285 286 static const struct drm_display_mode default_mode = { 287 .clock = 25628, 288 .hdisplay = 480, 289 .hsync_start = 480 + 16, 290 .hsync_end = 480 + 16 + 2, 291 .htotal = 480 + 16 + 2 + 16, 292 .vdisplay = 800, 293 .vsync_start = 800 + 28, 294 .vsync_end = 800 + 28 + 2, 295 .vtotal = 800 + 28 + 2 + 1, 296 .width_mm = 53, 297 .height_mm = 89, 298 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 299 }; 300 301 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel) 302 { 303 return container_of(panel, struct s6e63m0, panel); 304 } 305 306 static int s6e63m0_clear_error(struct s6e63m0 *ctx) 307 { 308 int ret = ctx->error; 309 310 ctx->error = 0; 311 return ret; 312 } 313 314 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data) 315 { 316 if (ctx->error < 0) 317 return; 318 319 ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data); 320 } 321 322 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len) 323 { 324 if (ctx->error < 0 || len == 0) 325 return; 326 327 ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len); 328 } 329 330 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \ 331 ({ \ 332 static const u8 d[] = { seq }; \ 333 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ 334 }) 335 336 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx) 337 { 338 u8 id1, id2, id3; 339 int ret; 340 341 s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1); 342 s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2); 343 s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3); 344 345 ret = s6e63m0_clear_error(ctx); 346 if (ret) { 347 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret); 348 ctx->lcd_type = 0x00; 349 return ret; 350 } 351 352 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3); 353 354 /* 355 * We attempt to detect what panel is mounted on the controller. 356 * The third ID byte represents the desired ELVSS pulse for 357 * some displays. 358 */ 359 switch (id2) { 360 case S6E63M0_LCD_ID_VALUE_M2: 361 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n"); 362 ctx->elvss_pulse = id3; 363 break; 364 case S6E63M0_LCD_ID_VALUE_SM2: 365 case S6E63M0_LCD_ID_VALUE_SM2_1: 366 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n"); 367 ctx->elvss_pulse = id3; 368 break; 369 default: 370 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2); 371 /* Default ELVSS pulse level */ 372 ctx->elvss_pulse = 0x16; 373 break; 374 } 375 376 ctx->lcd_type = id2; 377 378 return 0; 379 } 380 381 static void s6e63m0_init(struct s6e63m0 *ctx) 382 { 383 /* 384 * We do not know why there is a difference in the DSI mode. 385 * (No datasheet.) 386 * 387 * In the vendor driver this sequence is called 388 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET". 389 */ 390 if (ctx->dsi_mode) 391 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, 392 0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3, 393 0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00); 394 else 395 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, 396 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f, 397 0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00); 398 399 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL, 400 0x02, 0x03, 0x1c, 0x10, 0x10); 401 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL, 402 0x03, 0x00, 0x00); 403 404 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 405 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 406 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1, 407 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00, 408 0xd6); 409 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 410 0x01); 411 412 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL, 413 0x00, 0x8e, 0x07); 414 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c); 415 416 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED, 417 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 418 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 419 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 420 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 421 0x21, 0x20, 0x1e, 0x1e); 422 423 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED, 424 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 425 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 426 0x66, 0x66); 427 428 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN, 429 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 430 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 431 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 432 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 433 0x21, 0x20, 0x1e, 0x1e); 434 435 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN, 436 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 437 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 438 0x66, 0x66); 439 440 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE, 441 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 442 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 443 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 444 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 445 0x21, 0x20, 0x1e, 0x1e); 446 447 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE, 448 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 449 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 450 0x66, 0x66); 451 452 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE, 453 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf, 454 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, 455 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 456 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18); 457 458 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE, 459 0x10, 0x10, 0x0b, 0x05); 460 461 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1, 462 0x01); 463 464 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 465 0x0b); 466 } 467 468 static int s6e63m0_power_on(struct s6e63m0 *ctx) 469 { 470 int ret; 471 472 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 473 if (ret < 0) 474 return ret; 475 476 msleep(25); 477 478 /* Be sure to send a reset pulse */ 479 gpiod_set_value(ctx->reset_gpio, 1); 480 msleep(5); 481 gpiod_set_value(ctx->reset_gpio, 0); 482 msleep(120); 483 484 return 0; 485 } 486 487 static int s6e63m0_power_off(struct s6e63m0 *ctx) 488 { 489 int ret; 490 491 gpiod_set_value(ctx->reset_gpio, 1); 492 msleep(120); 493 494 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 495 if (ret < 0) 496 return ret; 497 498 return 0; 499 } 500 501 static int s6e63m0_disable(struct drm_panel *panel) 502 { 503 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 504 505 if (!ctx->enabled) 506 return 0; 507 508 backlight_disable(ctx->bl_dev); 509 510 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); 511 msleep(10); 512 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); 513 msleep(120); 514 515 ctx->enabled = false; 516 517 return 0; 518 } 519 520 static int s6e63m0_unprepare(struct drm_panel *panel) 521 { 522 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 523 int ret; 524 525 if (!ctx->prepared) 526 return 0; 527 528 s6e63m0_clear_error(ctx); 529 530 ret = s6e63m0_power_off(ctx); 531 if (ret < 0) 532 return ret; 533 534 ctx->prepared = false; 535 536 return 0; 537 } 538 539 static int s6e63m0_prepare(struct drm_panel *panel) 540 { 541 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 542 int ret; 543 544 if (ctx->prepared) 545 return 0; 546 547 ret = s6e63m0_power_on(ctx); 548 if (ret < 0) 549 return ret; 550 551 /* Magic to unlock level 2 control of the display */ 552 s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a); 553 /* Magic to unlock MTP reading */ 554 s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a); 555 556 ret = s6e63m0_check_lcd_type(ctx); 557 if (ret < 0) 558 return ret; 559 560 s6e63m0_init(ctx); 561 562 ret = s6e63m0_clear_error(ctx); 563 564 if (ret < 0) 565 s6e63m0_unprepare(panel); 566 567 ctx->prepared = true; 568 569 return ret; 570 } 571 572 static int s6e63m0_enable(struct drm_panel *panel) 573 { 574 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 575 576 if (ctx->enabled) 577 return 0; 578 579 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); 580 msleep(120); 581 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); 582 msleep(10); 583 584 s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK, 585 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3, 586 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20, 587 0x0F, 0x00); 588 589 backlight_enable(ctx->bl_dev); 590 591 ctx->enabled = true; 592 593 return 0; 594 } 595 596 static int s6e63m0_get_modes(struct drm_panel *panel, 597 struct drm_connector *connector) 598 { 599 struct drm_display_mode *mode; 600 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 601 602 mode = drm_mode_duplicate(connector->dev, &default_mode); 603 if (!mode) { 604 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 605 default_mode.hdisplay, default_mode.vdisplay, 606 drm_mode_vrefresh(&default_mode)); 607 return -ENOMEM; 608 } 609 610 connector->display_info.width_mm = mode->width_mm; 611 connector->display_info.height_mm = mode->height_mm; 612 drm_display_info_set_bus_formats(&connector->display_info, 613 &bus_format, 1); 614 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW | 615 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 616 617 drm_mode_set_name(mode); 618 619 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 620 drm_mode_probed_add(connector, mode); 621 622 return 1; 623 } 624 625 static const struct drm_panel_funcs s6e63m0_drm_funcs = { 626 .disable = s6e63m0_disable, 627 .unprepare = s6e63m0_unprepare, 628 .prepare = s6e63m0_prepare, 629 .enable = s6e63m0_enable, 630 .get_modes = s6e63m0_get_modes, 631 }; 632 633 static int s6e63m0_set_brightness(struct backlight_device *bd) 634 { 635 struct s6e63m0 *ctx = bl_get_data(bd); 636 int brightness = bd->props.brightness; 637 u8 elvss_val; 638 u8 elvss_cmd_set[5]; 639 int i; 640 641 /* Adjust ELVSS to candela level */ 642 i = s6e63m0_elvss_per_gamma[brightness]; 643 elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i]; 644 if (elvss_val > 0x1f) 645 elvss_val = 0x1f; 646 elvss_cmd_set[0] = MCS_TEMP_SWIRE; 647 elvss_cmd_set[1] = elvss_val; 648 elvss_cmd_set[2] = elvss_val; 649 elvss_cmd_set[3] = elvss_val; 650 elvss_cmd_set[4] = elvss_val; 651 s6e63m0_dcs_write(ctx, elvss_cmd_set, 5); 652 653 /* Update the ACL per gamma value */ 654 i = s6e63m0_acl_per_gamma[brightness]; 655 s6e63m0_dcs_write(ctx, s6e63m0_acl[i], 656 ARRAY_SIZE(s6e63m0_acl[i])); 657 658 /* Update gamma table */ 659 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness], 660 ARRAY_SIZE(s6e63m0_gamma_22[brightness])); 661 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03); 662 663 664 return s6e63m0_clear_error(ctx); 665 } 666 667 static const struct backlight_ops s6e63m0_backlight_ops = { 668 .update_status = s6e63m0_set_brightness, 669 }; 670 671 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness) 672 { 673 struct backlight_properties props = { 674 .type = BACKLIGHT_RAW, 675 .brightness = max_brightness, 676 .max_brightness = max_brightness, 677 }; 678 struct device *dev = ctx->dev; 679 int ret = 0; 680 681 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx, 682 &s6e63m0_backlight_ops, 683 &props); 684 if (IS_ERR(ctx->bl_dev)) { 685 ret = PTR_ERR(ctx->bl_dev); 686 dev_err(dev, "error registering backlight device (%d)\n", ret); 687 } 688 689 return ret; 690 } 691 692 int s6e63m0_probe(struct device *dev, void *trsp, 693 int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val), 694 int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len), 695 bool dsi_mode) 696 { 697 struct s6e63m0 *ctx; 698 u32 max_brightness; 699 int ret; 700 701 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL); 702 if (!ctx) 703 return -ENOMEM; 704 705 ctx->transport_data = trsp; 706 ctx->dsi_mode = dsi_mode; 707 ctx->dcs_read = dcs_read; 708 ctx->dcs_write = dcs_write; 709 dev_set_drvdata(dev, ctx); 710 711 ctx->dev = dev; 712 ctx->enabled = false; 713 ctx->prepared = false; 714 715 ret = device_property_read_u32(dev, "max-brightness", &max_brightness); 716 if (ret) 717 max_brightness = MAX_BRIGHTNESS; 718 if (max_brightness > MAX_BRIGHTNESS) { 719 dev_err(dev, "illegal max brightness specified\n"); 720 max_brightness = MAX_BRIGHTNESS; 721 } 722 723 ctx->supplies[0].supply = "vdd3"; 724 ctx->supplies[1].supply = "vci"; 725 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 726 ctx->supplies); 727 if (ret < 0) { 728 dev_err(dev, "failed to get regulators: %d\n", ret); 729 return ret; 730 } 731 732 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 733 if (IS_ERR(ctx->reset_gpio)) { 734 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio)); 735 return PTR_ERR(ctx->reset_gpio); 736 } 737 738 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs, 739 dsi_mode ? DRM_MODE_CONNECTOR_DSI : 740 DRM_MODE_CONNECTOR_DPI); 741 742 ret = s6e63m0_backlight_register(ctx, max_brightness); 743 if (ret < 0) 744 return ret; 745 746 drm_panel_add(&ctx->panel); 747 748 return 0; 749 } 750 EXPORT_SYMBOL_GPL(s6e63m0_probe); 751 752 int s6e63m0_remove(struct device *dev) 753 { 754 struct s6e63m0 *ctx = dev_get_drvdata(dev); 755 756 drm_panel_remove(&ctx->panel); 757 758 return 0; 759 } 760 EXPORT_SYMBOL_GPL(s6e63m0_remove); 761 762 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>"); 763 MODULE_DESCRIPTION("s6e63m0 LCD Driver"); 764 MODULE_LICENSE("GPL v2"); 765