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