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/spi/spi.h> 20 21 #include <video/mipi_display.h> 22 23 /* Manufacturer Command Set */ 24 #define MCS_ELVSS_ON 0xb1 25 #define MCS_MIECTL1 0xc0 26 #define MCS_BCMODE 0xc1 27 #define MCS_DISCTL 0xf2 28 #define MCS_SRCCTL 0xf6 29 #define MCS_IFCTL 0xf7 30 #define MCS_PANELCTL 0xF8 31 #define MCS_PGAMMACTL 0xfa 32 33 #define NUM_GAMMA_LEVELS 11 34 #define GAMMA_TABLE_COUNT 23 35 36 #define DATA_MASK 0x100 37 38 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1) 39 40 /* array of gamma tables for gamma value 2.2 */ 41 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = { 42 { MCS_PGAMMACTL, 0x00, 43 0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8, 44 0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7, 45 0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 }, 46 { MCS_PGAMMACTL, 0x00, 47 0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0, 48 0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF, 49 0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 }, 50 { MCS_PGAMMACTL, 0x00, 51 0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF, 52 0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC, 53 0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D }, 54 { MCS_PGAMMACTL, 0x00, 55 0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC, 56 0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9, 57 0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E }, 58 { MCS_PGAMMACTL, 0x00, 59 0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB, 60 0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8, 61 0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB }, 62 { MCS_PGAMMACTL, 0x00, 63 0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA, 64 0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6, 65 0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA }, 66 { MCS_PGAMMACTL, 0x00, 67 0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8, 68 0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4, 69 0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 }, 70 { MCS_PGAMMACTL, 0x00, 71 0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9, 72 0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3, 73 0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA }, 74 { MCS_PGAMMACTL, 0x00, 75 0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6, 76 0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2, 77 0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 }, 78 { MCS_PGAMMACTL, 0x00, 79 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6, 80 0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1, 81 0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 }, 82 { MCS_PGAMMACTL, 0x00, 83 0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6, 84 0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0, 85 0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb }, 86 }; 87 88 struct s6e63m0 { 89 struct device *dev; 90 struct drm_panel panel; 91 struct backlight_device *bl_dev; 92 93 struct regulator_bulk_data supplies[2]; 94 struct gpio_desc *reset_gpio; 95 96 bool prepared; 97 bool enabled; 98 99 /* 100 * This field is tested by functions directly accessing bus before 101 * transfer, transfer is skipped if it is set. In case of transfer 102 * failure or unexpected response the field is set to error value. 103 * Such construct allows to eliminate many checks in higher level 104 * functions. 105 */ 106 int error; 107 }; 108 109 static const struct drm_display_mode default_mode = { 110 .clock = 25628, 111 .hdisplay = 480, 112 .hsync_start = 480 + 16, 113 .hsync_end = 480 + 16 + 2, 114 .htotal = 480 + 16 + 2 + 16, 115 .vdisplay = 800, 116 .vsync_start = 800 + 28, 117 .vsync_end = 800 + 28 + 2, 118 .vtotal = 800 + 28 + 2 + 1, 119 .width_mm = 53, 120 .height_mm = 89, 121 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 122 }; 123 124 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel) 125 { 126 return container_of(panel, struct s6e63m0, panel); 127 } 128 129 static int s6e63m0_clear_error(struct s6e63m0 *ctx) 130 { 131 int ret = ctx->error; 132 133 ctx->error = 0; 134 return ret; 135 } 136 137 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data) 138 { 139 struct spi_device *spi = to_spi_device(ctx->dev); 140 struct spi_transfer xfer = { 141 .len = 2, 142 .tx_buf = &data, 143 }; 144 struct spi_message msg; 145 146 spi_message_init(&msg); 147 spi_message_add_tail(&xfer, &msg); 148 149 return spi_sync(spi, &msg); 150 } 151 152 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len) 153 { 154 int ret = 0; 155 156 if (ctx->error < 0 || len == 0) 157 return; 158 159 dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data); 160 ret = s6e63m0_spi_write_word(ctx, *data); 161 162 while (!ret && --len) { 163 ++data; 164 ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK); 165 } 166 167 if (ret) { 168 dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, (int)len, data); 169 ctx->error = ret; 170 } 171 172 usleep_range(300, 310); 173 } 174 175 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \ 176 ({ \ 177 static const u8 d[] = { seq }; \ 178 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ 179 }) 180 181 static void s6e63m0_init(struct s6e63m0 *ctx) 182 { 183 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, 184 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f, 185 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00); 186 187 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL, 188 0x02, 0x03, 0x1c, 0x10, 0x10); 189 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL, 190 0x03, 0x00, 0x00); 191 192 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 193 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 194 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1, 195 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00, 196 0xd6); 197 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 198 0x01); 199 200 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL, 201 0x00, 0x8c, 0x07); 202 s6e63m0_dcs_write_seq_static(ctx, 0xb3, 203 0xc); 204 205 s6e63m0_dcs_write_seq_static(ctx, 0xb5, 206 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 207 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 208 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 209 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 210 0x21, 0x20, 0x1e, 0x1e); 211 212 s6e63m0_dcs_write_seq_static(ctx, 0xb6, 213 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 214 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 215 0x66, 0x66); 216 217 s6e63m0_dcs_write_seq_static(ctx, 0xb7, 218 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 219 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 220 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 221 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 222 0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11, 223 0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55, 224 0x66, 0x66, 0x66, 0x66, 0x66, 0x66); 225 226 s6e63m0_dcs_write_seq_static(ctx, 0xb9, 227 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 228 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 229 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 230 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 231 0x21, 0x20, 0x1e, 0x1e); 232 233 s6e63m0_dcs_write_seq_static(ctx, 0xba, 234 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 235 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 236 0x66, 0x66); 237 238 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE, 239 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf, 240 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, 241 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 242 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18); 243 244 s6e63m0_dcs_write_seq_static(ctx, 0xb2, 245 0x10, 0x10, 0x0b, 0x05); 246 247 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1, 248 0x01); 249 250 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 251 0x0b); 252 253 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); 254 } 255 256 static int s6e63m0_power_on(struct s6e63m0 *ctx) 257 { 258 int ret; 259 260 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 261 if (ret < 0) 262 return ret; 263 264 msleep(25); 265 266 gpiod_set_value(ctx->reset_gpio, 0); 267 msleep(120); 268 269 return 0; 270 } 271 272 static int s6e63m0_power_off(struct s6e63m0 *ctx) 273 { 274 int ret; 275 276 gpiod_set_value(ctx->reset_gpio, 1); 277 msleep(120); 278 279 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 280 if (ret < 0) 281 return ret; 282 283 return 0; 284 } 285 286 static int s6e63m0_disable(struct drm_panel *panel) 287 { 288 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 289 290 if (!ctx->enabled) 291 return 0; 292 293 backlight_disable(ctx->bl_dev); 294 295 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); 296 msleep(200); 297 298 ctx->enabled = false; 299 300 return 0; 301 } 302 303 static int s6e63m0_unprepare(struct drm_panel *panel) 304 { 305 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 306 int ret; 307 308 if (!ctx->prepared) 309 return 0; 310 311 s6e63m0_clear_error(ctx); 312 313 ret = s6e63m0_power_off(ctx); 314 if (ret < 0) 315 return ret; 316 317 ctx->prepared = false; 318 319 return 0; 320 } 321 322 static int s6e63m0_prepare(struct drm_panel *panel) 323 { 324 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 325 int ret; 326 327 if (ctx->prepared) 328 return 0; 329 330 ret = s6e63m0_power_on(ctx); 331 if (ret < 0) 332 return ret; 333 334 s6e63m0_init(ctx); 335 336 ret = s6e63m0_clear_error(ctx); 337 338 if (ret < 0) 339 s6e63m0_unprepare(panel); 340 341 ctx->prepared = true; 342 343 return ret; 344 } 345 346 static int s6e63m0_enable(struct drm_panel *panel) 347 { 348 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 349 350 if (ctx->enabled) 351 return 0; 352 353 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); 354 355 backlight_enable(ctx->bl_dev); 356 357 ctx->enabled = true; 358 359 return 0; 360 } 361 362 static int s6e63m0_get_modes(struct drm_panel *panel, 363 struct drm_connector *connector) 364 { 365 struct drm_display_mode *mode; 366 367 mode = drm_mode_duplicate(connector->dev, &default_mode); 368 if (!mode) { 369 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 370 default_mode.hdisplay, default_mode.vdisplay, 371 drm_mode_vrefresh(&default_mode)); 372 return -ENOMEM; 373 } 374 375 drm_mode_set_name(mode); 376 377 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 378 drm_mode_probed_add(connector, mode); 379 380 return 1; 381 } 382 383 static const struct drm_panel_funcs s6e63m0_drm_funcs = { 384 .disable = s6e63m0_disable, 385 .unprepare = s6e63m0_unprepare, 386 .prepare = s6e63m0_prepare, 387 .enable = s6e63m0_enable, 388 .get_modes = s6e63m0_get_modes, 389 }; 390 391 static int s6e63m0_set_brightness(struct backlight_device *bd) 392 { 393 struct s6e63m0 *ctx = bl_get_data(bd); 394 395 int brightness = bd->props.brightness; 396 397 /* disable and set new gamma */ 398 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness], 399 ARRAY_SIZE(s6e63m0_gamma_22[brightness])); 400 401 /* update gamma table. */ 402 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01); 403 404 return s6e63m0_clear_error(ctx); 405 } 406 407 static const struct backlight_ops s6e63m0_backlight_ops = { 408 .update_status = s6e63m0_set_brightness, 409 }; 410 411 static int s6e63m0_backlight_register(struct s6e63m0 *ctx) 412 { 413 struct backlight_properties props = { 414 .type = BACKLIGHT_RAW, 415 .brightness = MAX_BRIGHTNESS, 416 .max_brightness = MAX_BRIGHTNESS 417 }; 418 struct device *dev = ctx->dev; 419 int ret = 0; 420 421 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx, 422 &s6e63m0_backlight_ops, 423 &props); 424 if (IS_ERR(ctx->bl_dev)) { 425 ret = PTR_ERR(ctx->bl_dev); 426 dev_err(dev, "error registering backlight device (%d)\n", ret); 427 } 428 429 return ret; 430 } 431 432 static int s6e63m0_probe(struct spi_device *spi) 433 { 434 struct device *dev = &spi->dev; 435 struct s6e63m0 *ctx; 436 int ret; 437 438 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL); 439 if (!ctx) 440 return -ENOMEM; 441 442 spi_set_drvdata(spi, ctx); 443 444 ctx->dev = dev; 445 ctx->enabled = false; 446 ctx->prepared = false; 447 448 ctx->supplies[0].supply = "vdd3"; 449 ctx->supplies[1].supply = "vci"; 450 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 451 ctx->supplies); 452 if (ret < 0) { 453 dev_err(dev, "failed to get regulators: %d\n", ret); 454 return ret; 455 } 456 457 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 458 if (IS_ERR(ctx->reset_gpio)) { 459 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio)); 460 return PTR_ERR(ctx->reset_gpio); 461 } 462 463 spi->bits_per_word = 9; 464 spi->mode = SPI_MODE_3; 465 ret = spi_setup(spi); 466 if (ret < 0) { 467 dev_err(dev, "spi setup failed.\n"); 468 return ret; 469 } 470 471 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs, 472 DRM_MODE_CONNECTOR_DPI); 473 474 ret = s6e63m0_backlight_register(ctx); 475 if (ret < 0) 476 return ret; 477 478 drm_panel_add(&ctx->panel); 479 480 return 0; 481 } 482 483 static int s6e63m0_remove(struct spi_device *spi) 484 { 485 struct s6e63m0 *ctx = spi_get_drvdata(spi); 486 487 drm_panel_remove(&ctx->panel); 488 489 return 0; 490 } 491 492 static const struct of_device_id s6e63m0_of_match[] = { 493 { .compatible = "samsung,s6e63m0" }, 494 { /* sentinel */ } 495 }; 496 MODULE_DEVICE_TABLE(of, s6e63m0_of_match); 497 498 static struct spi_driver s6e63m0_driver = { 499 .probe = s6e63m0_probe, 500 .remove = s6e63m0_remove, 501 .driver = { 502 .name = "panel-samsung-s6e63m0", 503 .of_match_table = s6e63m0_of_match, 504 }, 505 }; 506 module_spi_driver(s6e63m0_driver); 507 508 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>"); 509 MODULE_DESCRIPTION("s6e63m0 LCD Driver"); 510 MODULE_LICENSE("GPL v2"); 511