root/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. panel_to_s6e63j0x03
  2. s6e63j0x03_dcs_write_seq
  3. s6e63j0x03_enable_lv2_command
  4. s6e63j0x03_apply_mtp_key
  5. s6e63j0x03_power_on
  6. s6e63j0x03_power_off
  7. s6e63j0x03_get_brightness_index
  8. s6e63j0x03_update_gamma
  9. s6e63j0x03_set_brightness
  10. s6e63j0x03_disable
  11. s6e63j0x03_unprepare
  12. s6e63j0x03_panel_init
  13. s6e63j0x03_prepare
  14. s6e63j0x03_enable
  15. s6e63j0x03_get_modes
  16. s6e63j0x03_probe
  17. s6e63j0x03_remove

   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         .vrefresh = 30,
  56         .flags = 0,
  57 };
  58 
  59 static 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 
 116 static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
 117 {
 118         return container_of(panel, struct s6e63j0x03, panel);
 119 }
 120 
 121 static 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 
 135 static 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 
 140 static 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 
 149 static 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 
 167 static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
 168 {
 169         return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 170 }
 171 
 172 static 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 
 184 static 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 
 208 static 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 
 216 static const struct backlight_ops s6e63j0x03_bl_ops = {
 217         .update_status = s6e63j0x03_set_brightness,
 218 };
 219 
 220 static 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 
 241 static 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 
 255 static 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 
 324 static 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 
 341 err:
 342         s6e63j0x03_power_off(ctx);
 343         return ret;
 344 }
 345 
 346 static 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 
 403 static 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 
 427 static 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 
 435 static 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 
 494 remove_panel:
 495         drm_panel_remove(&ctx->panel);
 496 
 497 unregister_backlight:
 498         backlight_device_unregister(ctx->bl_dev);
 499 
 500         return ret;
 501 }
 502 
 503 static 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 
 515 static const struct of_device_id s6e63j0x03_of_match[] = {
 516         { .compatible = "samsung,s6e63j0x03" },
 517         { }
 518 };
 519 MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
 520 
 521 static 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 };
 529 module_mipi_dsi_driver(s6e63j0x03_driver);
 530 
 531 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
 532 MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
 533 MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
 534 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */