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

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

DEFINITIONS

This source file includes following definitions.
  1. panel_to_s6e63m0
  2. s6e63m0_clear_error
  3. s6e63m0_spi_write_word
  4. s6e63m0_dcs_write
  5. s6e63m0_init
  6. s6e63m0_power_on
  7. s6e63m0_power_off
  8. s6e63m0_disable
  9. s6e63m0_unprepare
  10. s6e63m0_prepare
  11. s6e63m0_enable
  12. s6e63m0_get_modes
  13. s6e63m0_set_brightness
  14. s6e63m0_backlight_register
  15. s6e63m0_probe
  16. s6e63m0_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * S6E63M0 AMOLED LCD drm_panel driver.
   4  *
   5  * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
   6  * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
   7  *
   8  * Andrzej Hajda <a.hajda@samsung.com>
   9  */
  10 
  11 #include <drm/drm_modes.h>
  12 #include <drm/drm_panel.h>
  13 #include <drm/drm_print.h>
  14 
  15 #include <linux/backlight.h>
  16 #include <linux/delay.h>
  17 #include <linux/gpio/consumer.h>
  18 #include <linux/module.h>
  19 #include <linux/regulator/consumer.h>
  20 #include <linux/spi/spi.h>
  21 
  22 #include <video/mipi_display.h>
  23 
  24 /* Manufacturer Command Set */
  25 #define MCS_ELVSS_ON                0xb1
  26 #define MCS_MIECTL1                0xc0
  27 #define MCS_BCMODE                              0xc1
  28 #define MCS_DISCTL   0xf2
  29 #define MCS_SRCCTL           0xf6
  30 #define MCS_IFCTL                       0xf7
  31 #define MCS_PANELCTL         0xF8
  32 #define MCS_PGAMMACTL                   0xfa
  33 
  34 #define NUM_GAMMA_LEVELS             11
  35 #define GAMMA_TABLE_COUNT           23
  36 
  37 #define DATA_MASK                                       0x100
  38 
  39 #define MAX_BRIGHTNESS              (NUM_GAMMA_LEVELS - 1)
  40 
  41 /* array of gamma tables for gamma value 2.2 */
  42 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
  43         { MCS_PGAMMACTL, 0x00,
  44           0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8,
  45           0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7,
  46           0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 },
  47         { MCS_PGAMMACTL, 0x00,
  48           0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0,
  49           0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF,
  50           0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 },
  51         { MCS_PGAMMACTL, 0x00,
  52           0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF,
  53           0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC,
  54           0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D },
  55         { MCS_PGAMMACTL, 0x00,
  56           0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC,
  57           0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9,
  58           0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E },
  59         { MCS_PGAMMACTL, 0x00,
  60           0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB,
  61           0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8,
  62           0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB },
  63         { MCS_PGAMMACTL, 0x00,
  64           0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA,
  65           0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6,
  66           0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA },
  67         { MCS_PGAMMACTL, 0x00,
  68           0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8,
  69           0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4,
  70           0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 },
  71         { MCS_PGAMMACTL, 0x00,
  72           0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9,
  73           0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3,
  74           0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA },
  75         { MCS_PGAMMACTL, 0x00,
  76           0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6,
  77           0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2,
  78           0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 },
  79         { MCS_PGAMMACTL, 0x00,
  80           0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6,
  81           0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1,
  82           0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 },
  83         { MCS_PGAMMACTL, 0x00,
  84           0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6,
  85           0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0,
  86           0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb },
  87 };
  88 
  89 struct s6e63m0 {
  90         struct device *dev;
  91         struct drm_panel panel;
  92         struct backlight_device *bl_dev;
  93 
  94         struct regulator_bulk_data supplies[2];
  95         struct gpio_desc *reset_gpio;
  96 
  97         bool prepared;
  98         bool enabled;
  99 
 100         /*
 101          * This field is tested by functions directly accessing bus before
 102          * transfer, transfer is skipped if it is set. In case of transfer
 103          * failure or unexpected response the field is set to error value.
 104          * Such construct allows to eliminate many checks in higher level
 105          * functions.
 106          */
 107         int error;
 108 };
 109 
 110 static const struct drm_display_mode default_mode = {
 111         .clock          = 25628,
 112         .hdisplay       = 480,
 113         .hsync_start    = 480 + 16,
 114         .hsync_end      = 480 + 16 + 2,
 115         .htotal         = 480 + 16 + 2 + 16,
 116         .vdisplay       = 800,
 117         .vsync_start    = 800 + 28,
 118         .vsync_end      = 800 + 28 + 2,
 119         .vtotal         = 800 + 28 + 2 + 1,
 120         .vrefresh       = 60,
 121         .width_mm       = 53,
 122         .height_mm      = 89,
 123         .flags          = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
 124 };
 125 
 126 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
 127 {
 128         return container_of(panel, struct s6e63m0, panel);
 129 }
 130 
 131 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
 132 {
 133         int ret = ctx->error;
 134 
 135         ctx->error = 0;
 136         return ret;
 137 }
 138 
 139 static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data)
 140 {
 141         struct spi_device *spi = to_spi_device(ctx->dev);
 142         struct spi_transfer xfer = {
 143                 .len    = 2,
 144                 .tx_buf = &data,
 145         };
 146         struct spi_message msg;
 147 
 148         spi_message_init(&msg);
 149         spi_message_add_tail(&xfer, &msg);
 150 
 151         return spi_sync(spi, &msg);
 152 }
 153 
 154 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
 155 {
 156         int ret = 0;
 157 
 158         if (ctx->error < 0 || len == 0)
 159                 return;
 160 
 161         DRM_DEV_DEBUG(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
 162         ret = s6e63m0_spi_write_word(ctx, *data);
 163 
 164         while (!ret && --len) {
 165                 ++data;
 166                 ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK);
 167         }
 168 
 169         if (ret) {
 170                 DRM_DEV_ERROR(ctx->dev, "error %d writing dcs seq: %*ph\n", ret,
 171                               (int)len, data);
 172                 ctx->error = ret;
 173         }
 174 
 175         usleep_range(300, 310);
 176 }
 177 
 178 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
 179         ({ \
 180                 static const u8 d[] = { seq }; \
 181                 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
 182         })
 183 
 184 static void s6e63m0_init(struct s6e63m0 *ctx)
 185 {
 186         s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
 187                                      0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
 188                                      0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00);
 189 
 190         s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
 191                                      0x02, 0x03, 0x1c, 0x10, 0x10);
 192         s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
 193                                      0x03, 0x00, 0x00);
 194 
 195         s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
 196                                      0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
 197                                      0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
 198                                      0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
 199                                      0xd6);
 200         s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
 201                                      0x01);
 202 
 203         s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
 204                                      0x00, 0x8c, 0x07);
 205         s6e63m0_dcs_write_seq_static(ctx, 0xb3,
 206                                      0xc);
 207 
 208         s6e63m0_dcs_write_seq_static(ctx, 0xb5,
 209                                      0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
 210                                      0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
 211                                      0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
 212                                      0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
 213                                      0x21, 0x20, 0x1e, 0x1e);
 214 
 215         s6e63m0_dcs_write_seq_static(ctx, 0xb6,
 216                                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
 217                                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
 218                                      0x66, 0x66);
 219 
 220         s6e63m0_dcs_write_seq_static(ctx, 0xb7,
 221                                      0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
 222                                      0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
 223                                      0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
 224                                      0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
 225                                      0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11,
 226                                      0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55,
 227                                      0x66, 0x66, 0x66, 0x66, 0x66, 0x66);
 228 
 229         s6e63m0_dcs_write_seq_static(ctx, 0xb9,
 230                                      0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
 231                                      0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
 232                                      0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
 233                                      0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
 234                                      0x21, 0x20, 0x1e, 0x1e);
 235 
 236         s6e63m0_dcs_write_seq_static(ctx, 0xba,
 237                                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
 238                                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
 239                                      0x66, 0x66);
 240 
 241         s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
 242                                      0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
 243                                      0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
 244                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
 245                                      0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
 246 
 247         s6e63m0_dcs_write_seq_static(ctx, 0xb2,
 248                                      0x10, 0x10, 0x0b, 0x05);
 249 
 250         s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
 251                                      0x01);
 252 
 253         s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
 254                                      0x0b);
 255 
 256         s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
 257 }
 258 
 259 static int s6e63m0_power_on(struct s6e63m0 *ctx)
 260 {
 261         int ret;
 262 
 263         ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 264         if (ret < 0)
 265                 return ret;
 266 
 267         msleep(25);
 268 
 269         gpiod_set_value(ctx->reset_gpio, 0);
 270         msleep(120);
 271 
 272         return 0;
 273 }
 274 
 275 static int s6e63m0_power_off(struct s6e63m0 *ctx)
 276 {
 277         int ret;
 278 
 279         gpiod_set_value(ctx->reset_gpio, 1);
 280         msleep(120);
 281 
 282         ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 283         if (ret < 0)
 284                 return ret;
 285 
 286         return 0;
 287 }
 288 
 289 static int s6e63m0_disable(struct drm_panel *panel)
 290 {
 291         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 292 
 293         if (!ctx->enabled)
 294                 return 0;
 295 
 296         backlight_disable(ctx->bl_dev);
 297 
 298         s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
 299         msleep(200);
 300 
 301         ctx->enabled = false;
 302 
 303         return 0;
 304 }
 305 
 306 static int s6e63m0_unprepare(struct drm_panel *panel)
 307 {
 308         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 309         int ret;
 310 
 311         if (!ctx->prepared)
 312                 return 0;
 313 
 314         s6e63m0_clear_error(ctx);
 315 
 316         ret = s6e63m0_power_off(ctx);
 317         if (ret < 0)
 318                 return ret;
 319 
 320         ctx->prepared = false;
 321 
 322         return 0;
 323 }
 324 
 325 static int s6e63m0_prepare(struct drm_panel *panel)
 326 {
 327         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 328         int ret;
 329 
 330         if (ctx->prepared)
 331                 return 0;
 332 
 333         ret = s6e63m0_power_on(ctx);
 334         if (ret < 0)
 335                 return ret;
 336 
 337         s6e63m0_init(ctx);
 338 
 339         ret = s6e63m0_clear_error(ctx);
 340 
 341         if (ret < 0)
 342                 s6e63m0_unprepare(panel);
 343 
 344         ctx->prepared = true;
 345 
 346         return ret;
 347 }
 348 
 349 static int s6e63m0_enable(struct drm_panel *panel)
 350 {
 351         struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 352 
 353         if (ctx->enabled)
 354                 return 0;
 355 
 356         s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
 357 
 358         backlight_enable(ctx->bl_dev);
 359 
 360         ctx->enabled = true;
 361 
 362         return 0;
 363 }
 364 
 365 static int s6e63m0_get_modes(struct drm_panel *panel)
 366 {
 367         struct drm_connector *connector = panel->connector;
 368         struct drm_display_mode *mode;
 369 
 370         mode = drm_mode_duplicate(panel->drm, &default_mode);
 371         if (!mode) {
 372                 DRM_ERROR("failed to add mode %ux%ux@%u\n",
 373                           default_mode.hdisplay, default_mode.vdisplay,
 374                           default_mode.vrefresh);
 375                 return -ENOMEM;
 376         }
 377 
 378         drm_mode_set_name(mode);
 379 
 380         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 381         drm_mode_probed_add(connector, mode);
 382 
 383         return 1;
 384 }
 385 
 386 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
 387         .disable        = s6e63m0_disable,
 388         .unprepare      = s6e63m0_unprepare,
 389         .prepare        = s6e63m0_prepare,
 390         .enable         = s6e63m0_enable,
 391         .get_modes      = s6e63m0_get_modes,
 392 };
 393 
 394 static int s6e63m0_set_brightness(struct backlight_device *bd)
 395 {
 396         struct s6e63m0 *ctx = bl_get_data(bd);
 397 
 398         int brightness = bd->props.brightness;
 399 
 400         /* disable and set new gamma */
 401         s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
 402                           ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
 403 
 404         /* update gamma table. */
 405         s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
 406 
 407         return s6e63m0_clear_error(ctx);
 408 }
 409 
 410 static const struct backlight_ops s6e63m0_backlight_ops = {
 411         .update_status  = s6e63m0_set_brightness,
 412 };
 413 
 414 static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
 415 {
 416         struct backlight_properties props = {
 417                 .type           = BACKLIGHT_RAW,
 418                 .brightness     = MAX_BRIGHTNESS,
 419                 .max_brightness = MAX_BRIGHTNESS
 420         };
 421         struct device *dev = ctx->dev;
 422         int ret = 0;
 423 
 424         ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
 425                                                      &s6e63m0_backlight_ops,
 426                                                      &props);
 427         if (IS_ERR(ctx->bl_dev)) {
 428                 ret = PTR_ERR(ctx->bl_dev);
 429                 DRM_DEV_ERROR(dev, "error registering backlight device (%d)\n",
 430                               ret);
 431         }
 432 
 433         return ret;
 434 }
 435 
 436 static int s6e63m0_probe(struct spi_device *spi)
 437 {
 438         struct device *dev = &spi->dev;
 439         struct s6e63m0 *ctx;
 440         int ret;
 441 
 442         ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
 443         if (!ctx)
 444                 return -ENOMEM;
 445 
 446         spi_set_drvdata(spi, ctx);
 447 
 448         ctx->dev = dev;
 449         ctx->enabled = false;
 450         ctx->prepared = false;
 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                 DRM_DEV_ERROR(dev, "failed to get regulators: %d\n", ret);
 458                 return ret;
 459         }
 460 
 461         ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 462         if (IS_ERR(ctx->reset_gpio)) {
 463                 DRM_DEV_ERROR(dev, "cannot get reset-gpios %ld\n",
 464                               PTR_ERR(ctx->reset_gpio));
 465                 return PTR_ERR(ctx->reset_gpio);
 466         }
 467 
 468         spi->bits_per_word = 9;
 469         spi->mode = SPI_MODE_3;
 470         ret = spi_setup(spi);
 471         if (ret < 0) {
 472                 DRM_DEV_ERROR(dev, "spi setup failed.\n");
 473                 return ret;
 474         }
 475 
 476         drm_panel_init(&ctx->panel);
 477         ctx->panel.dev = dev;
 478         ctx->panel.funcs = &s6e63m0_drm_funcs;
 479 
 480         ret = s6e63m0_backlight_register(ctx);
 481         if (ret < 0)
 482                 return ret;
 483 
 484         return drm_panel_add(&ctx->panel);
 485 }
 486 
 487 static int s6e63m0_remove(struct spi_device *spi)
 488 {
 489         struct s6e63m0 *ctx = spi_get_drvdata(spi);
 490 
 491         drm_panel_remove(&ctx->panel);
 492 
 493         return 0;
 494 }
 495 
 496 static const struct of_device_id s6e63m0_of_match[] = {
 497         { .compatible = "samsung,s6e63m0" },
 498         { /* sentinel */ }
 499 };
 500 MODULE_DEVICE_TABLE(of, s6e63m0_of_match);
 501 
 502 static struct spi_driver s6e63m0_driver = {
 503         .probe                  = s6e63m0_probe,
 504         .remove                 = s6e63m0_remove,
 505         .driver                 = {
 506                 .name           = "panel-samsung-s6e63m0",
 507                 .of_match_table = s6e63m0_of_match,
 508         },
 509 };
 510 module_spi_driver(s6e63m0_driver);
 511 
 512 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
 513 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
 514 MODULE_LICENSE("GPL v2");

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