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 /* 274 * This field is tested by functions directly accessing bus before 275 * transfer, transfer is skipped if it is set. In case of transfer 276 * failure or unexpected response the field is set to error value. 277 * Such construct allows to eliminate many checks in higher level 278 * functions. 279 */ 280 int error; 281 }; 282 283 static const struct drm_display_mode default_mode = { 284 .clock = 25628, 285 .hdisplay = 480, 286 .hsync_start = 480 + 16, 287 .hsync_end = 480 + 16 + 2, 288 .htotal = 480 + 16 + 2 + 16, 289 .vdisplay = 800, 290 .vsync_start = 800 + 28, 291 .vsync_end = 800 + 28 + 2, 292 .vtotal = 800 + 28 + 2 + 1, 293 .width_mm = 53, 294 .height_mm = 89, 295 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 296 }; 297 298 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel) 299 { 300 return container_of(panel, struct s6e63m0, panel); 301 } 302 303 static int s6e63m0_clear_error(struct s6e63m0 *ctx) 304 { 305 int ret = ctx->error; 306 307 ctx->error = 0; 308 return ret; 309 } 310 311 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data) 312 { 313 if (ctx->error < 0) 314 return; 315 316 ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data); 317 } 318 319 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len) 320 { 321 if (ctx->error < 0 || len == 0) 322 return; 323 324 ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len); 325 } 326 327 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \ 328 ({ \ 329 static const u8 d[] = { seq }; \ 330 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ 331 }) 332 333 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx) 334 { 335 u8 id1, id2, id3; 336 int ret; 337 338 s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1); 339 s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2); 340 s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3); 341 342 ret = s6e63m0_clear_error(ctx); 343 if (ret) { 344 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret); 345 ctx->lcd_type = 0x00; 346 return ret; 347 } 348 349 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3); 350 351 /* 352 * We attempt to detect what panel is mounted on the controller. 353 * The third ID byte represents the desired ELVSS pulse for 354 * some displays. 355 */ 356 switch (id2) { 357 case S6E63M0_LCD_ID_VALUE_M2: 358 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n"); 359 ctx->elvss_pulse = id3; 360 break; 361 case S6E63M0_LCD_ID_VALUE_SM2: 362 case S6E63M0_LCD_ID_VALUE_SM2_1: 363 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n"); 364 ctx->elvss_pulse = id3; 365 break; 366 default: 367 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2); 368 /* Default ELVSS pulse level */ 369 ctx->elvss_pulse = 0x16; 370 break; 371 } 372 373 ctx->lcd_type = id2; 374 375 return 0; 376 } 377 378 static void s6e63m0_init(struct s6e63m0 *ctx) 379 { 380 /* 381 * We do not know why there is a difference in the DSI mode. 382 * (No datasheet.) 383 * 384 * In the vendor driver this sequence is called 385 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET". 386 */ 387 if (ctx->dsi_mode) 388 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, 389 0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3, 390 0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00); 391 else 392 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, 393 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f, 394 0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00); 395 396 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL, 397 0x02, 0x03, 0x1c, 0x10, 0x10); 398 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL, 399 0x03, 0x00, 0x00); 400 401 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 402 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 403 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1, 404 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00, 405 0xd6); 406 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 407 0x01); 408 409 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL, 410 0x00, 0x8e, 0x07); 411 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c); 412 413 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED, 414 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 415 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 416 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 417 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 418 0x21, 0x20, 0x1e, 0x1e); 419 420 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED, 421 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 422 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 423 0x66, 0x66); 424 425 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN, 426 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 427 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 428 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 429 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 430 0x21, 0x20, 0x1e, 0x1e); 431 432 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN, 433 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 434 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 435 0x66, 0x66); 436 437 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE, 438 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 439 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 440 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 441 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 442 0x21, 0x20, 0x1e, 0x1e); 443 444 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE, 445 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 446 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 447 0x66, 0x66); 448 449 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE, 450 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf, 451 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, 452 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 453 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18); 454 455 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE, 456 0x10, 0x10, 0x0b, 0x05); 457 458 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1, 459 0x01); 460 461 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 462 0x0b); 463 } 464 465 static int s6e63m0_power_on(struct s6e63m0 *ctx) 466 { 467 int ret; 468 469 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 470 if (ret < 0) 471 return ret; 472 473 msleep(25); 474 475 /* Be sure to send a reset pulse */ 476 gpiod_set_value(ctx->reset_gpio, 1); 477 msleep(5); 478 gpiod_set_value(ctx->reset_gpio, 0); 479 msleep(120); 480 481 return 0; 482 } 483 484 static int s6e63m0_power_off(struct s6e63m0 *ctx) 485 { 486 int ret; 487 488 gpiod_set_value(ctx->reset_gpio, 1); 489 msleep(120); 490 491 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 492 if (ret < 0) 493 return ret; 494 495 return 0; 496 } 497 498 static int s6e63m0_disable(struct drm_panel *panel) 499 { 500 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 501 502 backlight_disable(ctx->bl_dev); 503 504 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); 505 msleep(10); 506 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); 507 msleep(120); 508 509 return 0; 510 } 511 512 static int s6e63m0_unprepare(struct drm_panel *panel) 513 { 514 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 515 int ret; 516 517 s6e63m0_clear_error(ctx); 518 519 ret = s6e63m0_power_off(ctx); 520 if (ret < 0) 521 return ret; 522 523 return 0; 524 } 525 526 static int s6e63m0_prepare(struct drm_panel *panel) 527 { 528 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 529 int ret; 530 531 ret = s6e63m0_power_on(ctx); 532 if (ret < 0) 533 return ret; 534 535 /* Magic to unlock level 2 control of the display */ 536 s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a); 537 /* Magic to unlock MTP reading */ 538 s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a); 539 540 ret = s6e63m0_check_lcd_type(ctx); 541 if (ret < 0) 542 return ret; 543 544 s6e63m0_init(ctx); 545 546 ret = s6e63m0_clear_error(ctx); 547 548 if (ret < 0) 549 s6e63m0_unprepare(panel); 550 551 return ret; 552 } 553 554 static int s6e63m0_enable(struct drm_panel *panel) 555 { 556 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 557 558 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); 559 msleep(120); 560 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); 561 msleep(10); 562 563 s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK, 564 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3, 565 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20, 566 0x0F, 0x00); 567 568 backlight_enable(ctx->bl_dev); 569 570 return 0; 571 } 572 573 static int s6e63m0_get_modes(struct drm_panel *panel, 574 struct drm_connector *connector) 575 { 576 struct drm_display_mode *mode; 577 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 578 579 mode = drm_mode_duplicate(connector->dev, &default_mode); 580 if (!mode) { 581 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 582 default_mode.hdisplay, default_mode.vdisplay, 583 drm_mode_vrefresh(&default_mode)); 584 return -ENOMEM; 585 } 586 587 connector->display_info.width_mm = mode->width_mm; 588 connector->display_info.height_mm = mode->height_mm; 589 drm_display_info_set_bus_formats(&connector->display_info, 590 &bus_format, 1); 591 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW | 592 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 593 594 drm_mode_set_name(mode); 595 596 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 597 drm_mode_probed_add(connector, mode); 598 599 return 1; 600 } 601 602 static const struct drm_panel_funcs s6e63m0_drm_funcs = { 603 .disable = s6e63m0_disable, 604 .unprepare = s6e63m0_unprepare, 605 .prepare = s6e63m0_prepare, 606 .enable = s6e63m0_enable, 607 .get_modes = s6e63m0_get_modes, 608 }; 609 610 static int s6e63m0_set_brightness(struct backlight_device *bd) 611 { 612 struct s6e63m0 *ctx = bl_get_data(bd); 613 int brightness = bd->props.brightness; 614 u8 elvss_val; 615 u8 elvss_cmd_set[5]; 616 int i; 617 618 /* Adjust ELVSS to candela level */ 619 i = s6e63m0_elvss_per_gamma[brightness]; 620 elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i]; 621 if (elvss_val > 0x1f) 622 elvss_val = 0x1f; 623 elvss_cmd_set[0] = MCS_TEMP_SWIRE; 624 elvss_cmd_set[1] = elvss_val; 625 elvss_cmd_set[2] = elvss_val; 626 elvss_cmd_set[3] = elvss_val; 627 elvss_cmd_set[4] = elvss_val; 628 s6e63m0_dcs_write(ctx, elvss_cmd_set, 5); 629 630 /* Update the ACL per gamma value */ 631 i = s6e63m0_acl_per_gamma[brightness]; 632 s6e63m0_dcs_write(ctx, s6e63m0_acl[i], 633 ARRAY_SIZE(s6e63m0_acl[i])); 634 635 /* Update gamma table */ 636 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness], 637 ARRAY_SIZE(s6e63m0_gamma_22[brightness])); 638 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03); 639 640 641 return s6e63m0_clear_error(ctx); 642 } 643 644 static const struct backlight_ops s6e63m0_backlight_ops = { 645 .update_status = s6e63m0_set_brightness, 646 }; 647 648 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness) 649 { 650 struct backlight_properties props = { 651 .type = BACKLIGHT_RAW, 652 .brightness = max_brightness, 653 .max_brightness = max_brightness, 654 }; 655 struct device *dev = ctx->dev; 656 int ret = 0; 657 658 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx, 659 &s6e63m0_backlight_ops, 660 &props); 661 if (IS_ERR(ctx->bl_dev)) { 662 ret = PTR_ERR(ctx->bl_dev); 663 dev_err(dev, "error registering backlight device (%d)\n", ret); 664 } 665 666 return ret; 667 } 668 669 int s6e63m0_probe(struct device *dev, void *trsp, 670 int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val), 671 int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len), 672 bool dsi_mode) 673 { 674 struct s6e63m0 *ctx; 675 u32 max_brightness; 676 int ret; 677 678 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL); 679 if (!ctx) 680 return -ENOMEM; 681 682 ctx->transport_data = trsp; 683 ctx->dsi_mode = dsi_mode; 684 ctx->dcs_read = dcs_read; 685 ctx->dcs_write = dcs_write; 686 dev_set_drvdata(dev, ctx); 687 688 ctx->dev = dev; 689 690 ret = device_property_read_u32(dev, "max-brightness", &max_brightness); 691 if (ret) 692 max_brightness = MAX_BRIGHTNESS; 693 if (max_brightness > MAX_BRIGHTNESS) { 694 dev_err(dev, "illegal max brightness specified\n"); 695 max_brightness = MAX_BRIGHTNESS; 696 } 697 698 ctx->supplies[0].supply = "vdd3"; 699 ctx->supplies[1].supply = "vci"; 700 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 701 ctx->supplies); 702 if (ret < 0) { 703 dev_err(dev, "failed to get regulators: %d\n", ret); 704 return ret; 705 } 706 707 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 708 if (IS_ERR(ctx->reset_gpio)) { 709 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio)); 710 return PTR_ERR(ctx->reset_gpio); 711 } 712 713 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs, 714 dsi_mode ? DRM_MODE_CONNECTOR_DSI : 715 DRM_MODE_CONNECTOR_DPI); 716 717 ret = s6e63m0_backlight_register(ctx, max_brightness); 718 if (ret < 0) 719 return ret; 720 721 drm_panel_add(&ctx->panel); 722 723 return 0; 724 } 725 EXPORT_SYMBOL_GPL(s6e63m0_probe); 726 727 void s6e63m0_remove(struct device *dev) 728 { 729 struct s6e63m0 *ctx = dev_get_drvdata(dev); 730 731 drm_panel_remove(&ctx->panel); 732 } 733 EXPORT_SYMBOL_GPL(s6e63m0_remove); 734 735 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>"); 736 MODULE_DESCRIPTION("s6e63m0 LCD Driver"); 737 MODULE_LICENSE("GPL v2"); 738