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