1 /* 2 * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver. 3 * 4 * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd 5 * 6 * Inki Dae <inki.dae@samsung.com> 7 * Hoegeun Kwon <hoegeun.kwon@samsung.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 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 20 #include <video/mipi_display.h> 21 22 #include <drm/drm_mipi_dsi.h> 23 #include <drm/drm_modes.h> 24 #include <drm/drm_panel.h> 25 #include <drm/drm_print.h> 26 27 #define MCS_LEVEL2_KEY 0xf0 28 #define MCS_MTP_KEY 0xf1 29 #define MCS_MTP_SET3 0xd4 30 31 #define MAX_BRIGHTNESS 100 32 #define DEFAULT_BRIGHTNESS 80 33 34 #define NUM_GAMMA_STEPS 9 35 #define GAMMA_CMD_CNT 28 36 37 #define FIRST_COLUMN 20 38 39 struct s6e63j0x03 { 40 struct device *dev; 41 struct drm_panel panel; 42 struct backlight_device *bl_dev; 43 44 struct regulator_bulk_data supplies[2]; 45 struct gpio_desc *reset_gpio; 46 }; 47 48 static const struct drm_display_mode default_mode = { 49 .clock = 4649, 50 .hdisplay = 320, 51 .hsync_start = 320 + 1, 52 .hsync_end = 320 + 1 + 1, 53 .htotal = 320 + 1 + 1 + 1, 54 .vdisplay = 320, 55 .vsync_start = 320 + 150, 56 .vsync_end = 320 + 150 + 1, 57 .vtotal = 320 + 150 + 1 + 2, 58 .vrefresh = 30, 59 .flags = 0, 60 }; 61 62 static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = { 63 { /* Gamma 10 */ 64 MCS_MTP_SET3, 65 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26, 66 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36, 67 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf 68 }, 69 { /* gamma 30 */ 70 MCS_MTP_SET3, 71 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26, 72 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34, 73 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc 74 }, 75 { /* gamma 60 */ 76 MCS_MTP_SET3, 77 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a, 78 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33, 79 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5 80 }, 81 { /* gamma 90 */ 82 MCS_MTP_SET3, 83 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29, 84 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31, 85 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09 86 }, 87 { /* gamma 120 */ 88 MCS_MTP_SET3, 89 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28, 90 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31, 91 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b 92 }, 93 { /* gamma 150 */ 94 MCS_MTP_SET3, 95 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28, 96 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31, 97 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29 98 }, 99 { /* gamma 200 */ 100 MCS_MTP_SET3, 101 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29, 102 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30, 103 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b 104 }, 105 { /* gamma 240 */ 106 MCS_MTP_SET3, 107 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a, 108 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30, 109 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47 110 }, 111 { /* gamma 300 */ 112 MCS_MTP_SET3, 113 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28, 114 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f, 115 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57 116 } 117 }; 118 119 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel) 120 { 121 return container_of(panel, struct s6e63j0x03, panel); 122 } 123 124 static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx, 125 const void *seq, size_t len) 126 { 127 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 128 129 return mipi_dsi_dcs_write_buffer(dsi, seq, len); 130 } 131 132 #define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \ 133 ({ \ 134 static const u8 d[] = { seq }; \ 135 s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \ 136 }) 137 138 static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx) 139 { 140 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a); 141 } 142 143 static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on) 144 { 145 if (on) 146 return s6e63j0x03_dcs_write_seq_static(ctx, 147 MCS_MTP_KEY, 0x5a, 0x5a); 148 149 return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5); 150 } 151 152 static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx) 153 { 154 int ret; 155 156 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 157 if (ret < 0) 158 return ret; 159 160 msleep(30); 161 162 gpiod_set_value(ctx->reset_gpio, 1); 163 usleep_range(1000, 2000); 164 gpiod_set_value(ctx->reset_gpio, 0); 165 usleep_range(5000, 6000); 166 167 return 0; 168 } 169 170 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx) 171 { 172 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 173 } 174 175 static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness) 176 { 177 unsigned int index; 178 179 index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS); 180 181 if (index >= NUM_GAMMA_STEPS) 182 index = NUM_GAMMA_STEPS - 1; 183 184 return index; 185 } 186 187 static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx, 188 unsigned int brightness) 189 { 190 struct backlight_device *bl_dev = ctx->bl_dev; 191 unsigned int index = s6e63j0x03_get_brightness_index(brightness); 192 int ret; 193 194 ret = s6e63j0x03_apply_mtp_key(ctx, true); 195 if (ret < 0) 196 return ret; 197 198 ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT); 199 if (ret < 0) 200 return ret; 201 202 ret = s6e63j0x03_apply_mtp_key(ctx, false); 203 if (ret < 0) 204 return ret; 205 206 bl_dev->props.brightness = brightness; 207 208 return 0; 209 } 210 211 static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev) 212 { 213 struct s6e63j0x03 *ctx = bl_get_data(bl_dev); 214 unsigned int brightness = bl_dev->props.brightness; 215 216 return s6e63j0x03_update_gamma(ctx, brightness); 217 } 218 219 static const struct backlight_ops s6e63j0x03_bl_ops = { 220 .update_status = s6e63j0x03_set_brightness, 221 }; 222 223 static int s6e63j0x03_disable(struct drm_panel *panel) 224 { 225 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 226 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 227 int ret; 228 229 ret = mipi_dsi_dcs_set_display_off(dsi); 230 if (ret < 0) 231 return ret; 232 233 ctx->bl_dev->props.power = FB_BLANK_NORMAL; 234 235 ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 236 if (ret < 0) 237 return ret; 238 239 msleep(120); 240 241 return 0; 242 } 243 244 static int s6e63j0x03_unprepare(struct drm_panel *panel) 245 { 246 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 247 int ret; 248 249 ret = s6e63j0x03_power_off(ctx); 250 if (ret < 0) 251 return ret; 252 253 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 254 255 return 0; 256 } 257 258 static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx) 259 { 260 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 261 int ret; 262 263 ret = s6e63j0x03_enable_lv2_command(ctx); 264 if (ret < 0) 265 return ret; 266 267 ret = s6e63j0x03_apply_mtp_key(ctx, true); 268 if (ret < 0) 269 return ret; 270 271 /* set porch adjustment */ 272 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28); 273 if (ret < 0) 274 return ret; 275 276 /* set frame freq */ 277 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00); 278 if (ret < 0) 279 return ret; 280 281 /* set caset, paset */ 282 ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN, 283 default_mode.hdisplay - 1 + FIRST_COLUMN); 284 if (ret < 0) 285 return ret; 286 287 ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1); 288 if (ret < 0) 289 return ret; 290 291 /* set ltps timming 0, 1 */ 292 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17, 293 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00); 294 if (ret < 0) 295 return ret; 296 297 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02); 298 if (ret < 0) 299 return ret; 300 301 /* set param pos te_edge */ 302 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01); 303 if (ret < 0) 304 return ret; 305 306 /* set te rising edge */ 307 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f); 308 if (ret < 0) 309 return ret; 310 311 /* set param pos default */ 312 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00); 313 if (ret < 0) 314 return ret; 315 316 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 317 if (ret < 0) 318 return ret; 319 320 ret = s6e63j0x03_apply_mtp_key(ctx, false); 321 if (ret < 0) 322 return ret; 323 324 return 0; 325 } 326 327 static int s6e63j0x03_prepare(struct drm_panel *panel) 328 { 329 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 330 int ret; 331 332 ret = s6e63j0x03_power_on(ctx); 333 if (ret < 0) 334 return ret; 335 336 ret = s6e63j0x03_panel_init(ctx); 337 if (ret < 0) 338 goto err; 339 340 ctx->bl_dev->props.power = FB_BLANK_NORMAL; 341 342 return 0; 343 344 err: 345 s6e63j0x03_power_off(ctx); 346 return ret; 347 } 348 349 static int s6e63j0x03_enable(struct drm_panel *panel) 350 { 351 struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel); 352 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 353 int ret; 354 355 msleep(120); 356 357 ret = s6e63j0x03_apply_mtp_key(ctx, true); 358 if (ret < 0) 359 return ret; 360 361 /* set elvss_cond */ 362 ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09); 363 if (ret < 0) 364 return ret; 365 366 /* set pos */ 367 ret = s6e63j0x03_dcs_write_seq_static(ctx, 368 MIPI_DCS_SET_ADDRESS_MODE, 0x40); 369 if (ret < 0) 370 return ret; 371 372 /* set default white brightness */ 373 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff); 374 if (ret < 0) 375 return ret; 376 377 /* set white ctrl */ 378 ret = s6e63j0x03_dcs_write_seq_static(ctx, 379 MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); 380 if (ret < 0) 381 return ret; 382 383 /* set acl off */ 384 ret = s6e63j0x03_dcs_write_seq_static(ctx, 385 MIPI_DCS_WRITE_POWER_SAVE, 0x00); 386 if (ret < 0) 387 return ret; 388 389 ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 390 if (ret < 0) 391 return ret; 392 393 ret = s6e63j0x03_apply_mtp_key(ctx, false); 394 if (ret < 0) 395 return ret; 396 397 ret = mipi_dsi_dcs_set_display_on(dsi); 398 if (ret < 0) 399 return ret; 400 401 ctx->bl_dev->props.power = FB_BLANK_UNBLANK; 402 403 return 0; 404 } 405 406 static int s6e63j0x03_get_modes(struct drm_panel *panel) 407 { 408 struct drm_connector *connector = panel->connector; 409 struct drm_display_mode *mode; 410 411 mode = drm_mode_duplicate(panel->drm, &default_mode); 412 if (!mode) { 413 DRM_ERROR("failed to add mode %ux%ux@%u\n", 414 default_mode.hdisplay, default_mode.vdisplay, 415 default_mode.vrefresh); 416 return -ENOMEM; 417 } 418 419 drm_mode_set_name(mode); 420 421 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 422 drm_mode_probed_add(connector, mode); 423 424 connector->display_info.width_mm = 29; 425 connector->display_info.height_mm = 29; 426 427 return 1; 428 } 429 430 static const struct drm_panel_funcs s6e63j0x03_funcs = { 431 .disable = s6e63j0x03_disable, 432 .unprepare = s6e63j0x03_unprepare, 433 .prepare = s6e63j0x03_prepare, 434 .enable = s6e63j0x03_enable, 435 .get_modes = s6e63j0x03_get_modes, 436 }; 437 438 static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) 439 { 440 struct device *dev = &dsi->dev; 441 struct s6e63j0x03 *ctx; 442 int ret; 443 444 ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL); 445 if (!ctx) 446 return -ENOMEM; 447 448 mipi_dsi_set_drvdata(dsi, ctx); 449 450 ctx->dev = dev; 451 452 dsi->lanes = 1; 453 dsi->format = MIPI_DSI_FMT_RGB888; 454 dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET; 455 456 ctx->supplies[0].supply = "vdd3"; 457 ctx->supplies[1].supply = "vci"; 458 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 459 ctx->supplies); 460 if (ret < 0) { 461 dev_err(dev, "failed to get regulators: %d\n", ret); 462 return ret; 463 } 464 465 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 466 if (IS_ERR(ctx->reset_gpio)) { 467 dev_err(dev, "cannot get reset-gpio: %ld\n", 468 PTR_ERR(ctx->reset_gpio)); 469 return PTR_ERR(ctx->reset_gpio); 470 } 471 472 drm_panel_init(&ctx->panel); 473 ctx->panel.dev = dev; 474 ctx->panel.funcs = &s6e63j0x03_funcs; 475 476 ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx, 477 &s6e63j0x03_bl_ops, NULL); 478 if (IS_ERR(ctx->bl_dev)) { 479 dev_err(dev, "failed to register backlight device\n"); 480 return PTR_ERR(ctx->bl_dev); 481 } 482 483 ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS; 484 ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS; 485 ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 486 487 ret = drm_panel_add(&ctx->panel); 488 if (ret < 0) 489 goto unregister_backlight; 490 491 ret = mipi_dsi_attach(dsi); 492 if (ret < 0) 493 goto remove_panel; 494 495 return ret; 496 497 remove_panel: 498 drm_panel_remove(&ctx->panel); 499 500 unregister_backlight: 501 backlight_device_unregister(ctx->bl_dev); 502 503 return ret; 504 } 505 506 static int s6e63j0x03_remove(struct mipi_dsi_device *dsi) 507 { 508 struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi); 509 510 mipi_dsi_detach(dsi); 511 drm_panel_remove(&ctx->panel); 512 513 backlight_device_unregister(ctx->bl_dev); 514 515 return 0; 516 } 517 518 static const struct of_device_id s6e63j0x03_of_match[] = { 519 { .compatible = "samsung,s6e63j0x03" }, 520 { } 521 }; 522 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match); 523 524 static struct mipi_dsi_driver s6e63j0x03_driver = { 525 .probe = s6e63j0x03_probe, 526 .remove = s6e63j0x03_remove, 527 .driver = { 528 .name = "panel_samsung_s6e63j0x03", 529 .of_match_table = s6e63j0x03_of_match, 530 }, 531 }; 532 module_mipi_dsi_driver(s6e63j0x03_driver); 533 534 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); 535 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>"); 536 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver"); 537 MODULE_LICENSE("GPL v2"); 538