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