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