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