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