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 .width_mm = 53, 121 .height_mm = 89, 122 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, 123 }; 124 125 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel) 126 { 127 return container_of(panel, struct s6e63m0, panel); 128 } 129 130 static int s6e63m0_clear_error(struct s6e63m0 *ctx) 131 { 132 int ret = ctx->error; 133 134 ctx->error = 0; 135 return ret; 136 } 137 138 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data) 139 { 140 struct spi_device *spi = to_spi_device(ctx->dev); 141 struct spi_transfer xfer = { 142 .len = 2, 143 .tx_buf = &data, 144 }; 145 struct spi_message msg; 146 147 spi_message_init(&msg); 148 spi_message_add_tail(&xfer, &msg); 149 150 return spi_sync(spi, &msg); 151 } 152 153 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len) 154 { 155 int ret = 0; 156 157 if (ctx->error < 0 || len == 0) 158 return; 159 160 DRM_DEV_DEBUG(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data); 161 ret = s6e63m0_spi_write_word(ctx, *data); 162 163 while (!ret && --len) { 164 ++data; 165 ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK); 166 } 167 168 if (ret) { 169 DRM_DEV_ERROR(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, 170 (int)len, data); 171 ctx->error = ret; 172 } 173 174 usleep_range(300, 310); 175 } 176 177 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \ 178 ({ \ 179 static const u8 d[] = { seq }; \ 180 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ 181 }) 182 183 static void s6e63m0_init(struct s6e63m0 *ctx) 184 { 185 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL, 186 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f, 187 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00); 188 189 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL, 190 0x02, 0x03, 0x1c, 0x10, 0x10); 191 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL, 192 0x03, 0x00, 0x00); 193 194 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 195 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 196 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1, 197 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00, 198 0xd6); 199 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 200 0x01); 201 202 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL, 203 0x00, 0x8c, 0x07); 204 s6e63m0_dcs_write_seq_static(ctx, 0xb3, 205 0xc); 206 207 s6e63m0_dcs_write_seq_static(ctx, 0xb5, 208 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 209 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 210 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 211 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 212 0x21, 0x20, 0x1e, 0x1e); 213 214 s6e63m0_dcs_write_seq_static(ctx, 0xb6, 215 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 216 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 217 0x66, 0x66); 218 219 s6e63m0_dcs_write_seq_static(ctx, 0xb7, 220 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 221 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 222 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 223 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 224 0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11, 225 0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55, 226 0x66, 0x66, 0x66, 0x66, 0x66, 0x66); 227 228 s6e63m0_dcs_write_seq_static(ctx, 0xb9, 229 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 230 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 231 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a, 232 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23, 233 0x21, 0x20, 0x1e, 0x1e); 234 235 s6e63m0_dcs_write_seq_static(ctx, 0xba, 236 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 237 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 238 0x66, 0x66); 239 240 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE, 241 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf, 242 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, 243 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 244 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18); 245 246 s6e63m0_dcs_write_seq_static(ctx, 0xb2, 247 0x10, 0x10, 0x0b, 0x05); 248 249 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1, 250 0x01); 251 252 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 253 0x0b); 254 255 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); 256 } 257 258 static int s6e63m0_power_on(struct s6e63m0 *ctx) 259 { 260 int ret; 261 262 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 263 if (ret < 0) 264 return ret; 265 266 msleep(25); 267 268 gpiod_set_value(ctx->reset_gpio, 0); 269 msleep(120); 270 271 return 0; 272 } 273 274 static int s6e63m0_power_off(struct s6e63m0 *ctx) 275 { 276 int ret; 277 278 gpiod_set_value(ctx->reset_gpio, 1); 279 msleep(120); 280 281 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 282 if (ret < 0) 283 return ret; 284 285 return 0; 286 } 287 288 static int s6e63m0_disable(struct drm_panel *panel) 289 { 290 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 291 292 if (!ctx->enabled) 293 return 0; 294 295 backlight_disable(ctx->bl_dev); 296 297 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); 298 msleep(200); 299 300 ctx->enabled = false; 301 302 return 0; 303 } 304 305 static int s6e63m0_unprepare(struct drm_panel *panel) 306 { 307 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 308 int ret; 309 310 if (!ctx->prepared) 311 return 0; 312 313 s6e63m0_clear_error(ctx); 314 315 ret = s6e63m0_power_off(ctx); 316 if (ret < 0) 317 return ret; 318 319 ctx->prepared = false; 320 321 return 0; 322 } 323 324 static int s6e63m0_prepare(struct drm_panel *panel) 325 { 326 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 327 int ret; 328 329 if (ctx->prepared) 330 return 0; 331 332 ret = s6e63m0_power_on(ctx); 333 if (ret < 0) 334 return ret; 335 336 s6e63m0_init(ctx); 337 338 ret = s6e63m0_clear_error(ctx); 339 340 if (ret < 0) 341 s6e63m0_unprepare(panel); 342 343 ctx->prepared = true; 344 345 return ret; 346 } 347 348 static int s6e63m0_enable(struct drm_panel *panel) 349 { 350 struct s6e63m0 *ctx = panel_to_s6e63m0(panel); 351 352 if (ctx->enabled) 353 return 0; 354 355 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); 356 357 backlight_enable(ctx->bl_dev); 358 359 ctx->enabled = true; 360 361 return 0; 362 } 363 364 static int s6e63m0_get_modes(struct drm_panel *panel, 365 struct drm_connector *connector) 366 { 367 struct drm_display_mode *mode; 368 369 mode = drm_mode_duplicate(connector->dev, &default_mode); 370 if (!mode) { 371 DRM_ERROR("failed to add mode %ux%ux@%u\n", 372 default_mode.hdisplay, default_mode.vdisplay, 373 drm_mode_vrefresh(&default_mode)); 374 return -ENOMEM; 375 } 376 377 drm_mode_set_name(mode); 378 379 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 380 drm_mode_probed_add(connector, mode); 381 382 return 1; 383 } 384 385 static const struct drm_panel_funcs s6e63m0_drm_funcs = { 386 .disable = s6e63m0_disable, 387 .unprepare = s6e63m0_unprepare, 388 .prepare = s6e63m0_prepare, 389 .enable = s6e63m0_enable, 390 .get_modes = s6e63m0_get_modes, 391 }; 392 393 static int s6e63m0_set_brightness(struct backlight_device *bd) 394 { 395 struct s6e63m0 *ctx = bl_get_data(bd); 396 397 int brightness = bd->props.brightness; 398 399 /* disable and set new gamma */ 400 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness], 401 ARRAY_SIZE(s6e63m0_gamma_22[brightness])); 402 403 /* update gamma table. */ 404 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01); 405 406 return s6e63m0_clear_error(ctx); 407 } 408 409 static const struct backlight_ops s6e63m0_backlight_ops = { 410 .update_status = s6e63m0_set_brightness, 411 }; 412 413 static int s6e63m0_backlight_register(struct s6e63m0 *ctx) 414 { 415 struct backlight_properties props = { 416 .type = BACKLIGHT_RAW, 417 .brightness = MAX_BRIGHTNESS, 418 .max_brightness = MAX_BRIGHTNESS 419 }; 420 struct device *dev = ctx->dev; 421 int ret = 0; 422 423 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx, 424 &s6e63m0_backlight_ops, 425 &props); 426 if (IS_ERR(ctx->bl_dev)) { 427 ret = PTR_ERR(ctx->bl_dev); 428 DRM_DEV_ERROR(dev, "error registering backlight device (%d)\n", 429 ret); 430 } 431 432 return ret; 433 } 434 435 static int s6e63m0_probe(struct spi_device *spi) 436 { 437 struct device *dev = &spi->dev; 438 struct s6e63m0 *ctx; 439 int ret; 440 441 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL); 442 if (!ctx) 443 return -ENOMEM; 444 445 spi_set_drvdata(spi, ctx); 446 447 ctx->dev = dev; 448 ctx->enabled = false; 449 ctx->prepared = false; 450 451 ctx->supplies[0].supply = "vdd3"; 452 ctx->supplies[1].supply = "vci"; 453 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 454 ctx->supplies); 455 if (ret < 0) { 456 DRM_DEV_ERROR(dev, "failed to get regulators: %d\n", ret); 457 return ret; 458 } 459 460 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 461 if (IS_ERR(ctx->reset_gpio)) { 462 DRM_DEV_ERROR(dev, "cannot get reset-gpios %ld\n", 463 PTR_ERR(ctx->reset_gpio)); 464 return PTR_ERR(ctx->reset_gpio); 465 } 466 467 spi->bits_per_word = 9; 468 spi->mode = SPI_MODE_3; 469 ret = spi_setup(spi); 470 if (ret < 0) { 471 DRM_DEV_ERROR(dev, "spi setup failed.\n"); 472 return ret; 473 } 474 475 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs, 476 DRM_MODE_CONNECTOR_DPI); 477 478 ret = s6e63m0_backlight_register(ctx); 479 if (ret < 0) 480 return ret; 481 482 return drm_panel_add(&ctx->panel); 483 } 484 485 static int s6e63m0_remove(struct spi_device *spi) 486 { 487 struct s6e63m0 *ctx = spi_get_drvdata(spi); 488 489 drm_panel_remove(&ctx->panel); 490 491 return 0; 492 } 493 494 static const struct of_device_id s6e63m0_of_match[] = { 495 { .compatible = "samsung,s6e63m0" }, 496 { /* sentinel */ } 497 }; 498 MODULE_DEVICE_TABLE(of, s6e63m0_of_match); 499 500 static struct spi_driver s6e63m0_driver = { 501 .probe = s6e63m0_probe, 502 .remove = s6e63m0_remove, 503 .driver = { 504 .name = "panel-samsung-s6e63m0", 505 .of_match_table = s6e63m0_of_match, 506 }, 507 }; 508 module_spi_driver(s6e63m0_driver); 509 510 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>"); 511 MODULE_DESCRIPTION("s6e63m0 LCD Driver"); 512 MODULE_LICENSE("GPL v2"); 513