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