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