linux/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
<<
>>
Prefs
   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
  14#include <linux/backlight.h>
  15#include <linux/delay.h>
  16#include <linux/gpio/consumer.h>
  17#include <linux/module.h>
  18#include <linux/regulator/consumer.h>
  19#include <linux/media-bus-format.h>
  20
  21#include <video/mipi_display.h>
  22
  23#include "panel-samsung-s6e63m0.h"
  24
  25#define S6E63M0_LCD_ID_VALUE_M2         0xA4
  26#define S6E63M0_LCD_ID_VALUE_SM2        0xB4
  27#define S6E63M0_LCD_ID_VALUE_SM2_1      0xB6
  28
  29#define NUM_GAMMA_LEVELS        28
  30#define GAMMA_TABLE_COUNT       23
  31
  32#define MAX_BRIGHTNESS          (NUM_GAMMA_LEVELS - 1)
  33
  34/* array of gamma tables for gamma value 2.2 */
  35static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
  36        /* 30 cd */
  37        { MCS_PGAMMACTL, 0x02,
  38          0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
  39          0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
  40          0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
  41        /* 40 cd */
  42        { MCS_PGAMMACTL, 0x02,
  43          0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
  44          0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
  45          0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
  46        /* 50 cd */
  47        { MCS_PGAMMACTL, 0x02,
  48          0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
  49          0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
  50          0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
  51        /* 60 cd */
  52        { MCS_PGAMMACTL, 0x02,
  53          0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
  54          0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
  55          0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
  56        /* 70 cd */
  57        { MCS_PGAMMACTL, 0x02,
  58          0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
  59          0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
  60          0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
  61        /* 80 cd */
  62        { MCS_PGAMMACTL, 0x02,
  63          0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
  64          0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
  65          0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
  66        /* 90 cd */
  67        { MCS_PGAMMACTL, 0x02,
  68          0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
  69          0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
  70          0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
  71        /* 100 cd */
  72        { MCS_PGAMMACTL, 0x02,
  73          0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
  74          0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
  75          0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
  76        /* 110 cd */
  77        { MCS_PGAMMACTL, 0x02,
  78          0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
  79          0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
  80          0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
  81        /* 120 cd */
  82        { MCS_PGAMMACTL, 0x02,
  83          0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
  84          0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
  85          0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
  86        /* 130 cd */
  87        { MCS_PGAMMACTL, 0x02,
  88          0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
  89          0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
  90          0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
  91        /* 140 cd */
  92        { MCS_PGAMMACTL, 0x02,
  93          0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
  94          0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
  95          0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
  96        /* 150 cd */
  97        { MCS_PGAMMACTL, 0x02,
  98          0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
  99          0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
 100          0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
 101        /* 160 cd */
 102        { MCS_PGAMMACTL, 0x02,
 103          0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
 104          0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
 105          0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
 106        /* 170 cd */
 107        { MCS_PGAMMACTL, 0x02,
 108          0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
 109          0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
 110          0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
 111        /* 180 cd */
 112        { MCS_PGAMMACTL, 0x02,
 113          0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
 114          0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
 115          0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
 116        /* 190 cd */
 117        { MCS_PGAMMACTL, 0x02,
 118          0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
 119          0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
 120          0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
 121        /* 200 cd */
 122        { MCS_PGAMMACTL, 0x02,
 123          0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
 124          0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
 125          0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
 126        /* 210 cd */
 127        { MCS_PGAMMACTL, 0x02,
 128          0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
 129          0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
 130          0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
 131        /* 220 cd */
 132        { MCS_PGAMMACTL, 0x02,
 133          0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
 134          0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
 135          0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
 136        /* 230 cd */
 137        { MCS_PGAMMACTL, 0x02,
 138          0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
 139          0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
 140          0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
 141        /* 240 cd */
 142        { MCS_PGAMMACTL, 0x02,
 143          0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
 144          0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
 145          0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
 146        /* 250 cd */
 147        { MCS_PGAMMACTL, 0x02,
 148          0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
 149          0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
 150          0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
 151        /* 260 cd */
 152        { MCS_PGAMMACTL, 0x02,
 153          0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
 154          0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
 155          0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
 156        /* 270 cd */
 157        { MCS_PGAMMACTL, 0x02,
 158          0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
 159          0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
 160          0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
 161        /* 280 cd */
 162        { MCS_PGAMMACTL, 0x02,
 163          0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
 164          0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
 165          0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
 166        /* 290 cd */
 167        { MCS_PGAMMACTL, 0x02,
 168          0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
 169          0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
 170          0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
 171        /* 300 cd */
 172        { MCS_PGAMMACTL, 0x02,
 173          0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
 174          0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
 175          0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
 176};
 177
 178#define NUM_ACL_LEVELS 7
 179#define ACL_TABLE_COUNT 28
 180
 181static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
 182        /* NULL ACL */
 183        { MCS_BCMODE,
 184          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 185          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 186          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 187          0x00, 0x00, 0x00 },
 188        /* 40P ACL */
 189        { MCS_BCMODE,
 190          0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
 191          0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
 192          0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
 193          0x2B, 0x31, 0x36 },
 194        /* 43P ACL */
 195        { MCS_BCMODE,
 196          0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
 197          0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
 198          0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
 199          0x2F, 0x34, 0x3A },
 200        /* 45P ACL */
 201        { MCS_BCMODE,
 202          0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
 203          0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
 204          0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
 205          0x31, 0x37, 0x3D },
 206        /* 47P ACL */
 207        { MCS_BCMODE,
 208          0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
 209          0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
 210          0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
 211          0x34, 0x3B, 0x41 },
 212        /* 48P ACL */
 213        { MCS_BCMODE,
 214          0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
 215          0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
 216          0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
 217          0x36, 0x3C, 0x43 },
 218        /* 50P ACL */
 219        { MCS_BCMODE,
 220          0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
 221          0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
 222          0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
 223          0x38, 0x3F, 0x46 },
 224};
 225
 226/* This tells us which ACL level goes with which gamma */
 227static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
 228        /* 30 - 60 cd: ACL off/NULL */
 229        0, 0, 0, 0,
 230        /* 70 - 250 cd: 40P ACL */
 231        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 232        /* 260 - 300 cd: 50P ACL */
 233        6, 6, 6, 6, 6,
 234};
 235
 236/* The ELVSS backlight regulator has 5 levels */
 237#define S6E63M0_ELVSS_LEVELS 5
 238
 239static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
 240        0x00,   /* not set */
 241        0x0D,   /* 30 cd - 100 cd */
 242        0x09,   /* 110 cd - 160 cd */
 243        0x07,   /* 170 cd - 200 cd */
 244        0x00,   /* 210 cd - 300 cd */
 245};
 246
 247/* This tells us which ELVSS level goes with which gamma */
 248static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
 249        /* 30 - 100 cd */
 250        1, 1, 1, 1, 1, 1, 1, 1,
 251        /* 110 - 160 cd */
 252        2, 2, 2, 2, 2, 2,
 253        /* 170 - 200 cd */
 254        3, 3, 3, 3,
 255        /* 210 - 300 cd */
 256        4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 257};
 258
 259struct s6e63m0 {
 260        struct device *dev;
 261        void *transport_data;
 262        int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val);
 263        int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len);
 264        struct drm_panel panel;
 265        struct backlight_device *bl_dev;
 266        u8 lcd_type;
 267        u8 elvss_pulse;
 268        bool dsi_mode;
 269
 270        struct regulator_bulk_data supplies[2];
 271        struct gpio_desc *reset_gpio;
 272
 273        bool prepared;
 274        bool enabled;
 275
 276        /*
 277         * This field is tested by functions directly accessing bus before
 278         * transfer, transfer is skipped if it is set. In case of transfer
 279         * failure or unexpected response the field is set to error value.
 280         * Such construct allows to eliminate many checks in higher level
 281         * functions.
 282         */
 283        int error;
 284};
 285
 286static const struct drm_display_mode default_mode = {
 287        .clock          = 25628,
 288        .hdisplay       = 480,
 289        .hsync_start    = 480 + 16,
 290        .hsync_end      = 480 + 16 + 2,
 291        .htotal         = 480 + 16 + 2 + 16,
 292        .vdisplay       = 800,
 293        .vsync_start    = 800 + 28,
 294        .vsync_end      = 800 + 28 + 2,
 295        .vtotal         = 800 + 28 + 2 + 1,
 296        .width_mm       = 53,
 297        .height_mm      = 89,
 298        .flags          = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
 299};
 300
 301static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
 302{
 303        return container_of(panel, struct s6e63m0, panel);
 304}
 305
 306static int s6e63m0_clear_error(struct s6e63m0 *ctx)
 307{
 308        int ret = ctx->error;
 309
 310        ctx->error = 0;
 311        return ret;
 312}
 313
 314static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
 315{
 316        if (ctx->error < 0)
 317                return;
 318
 319        ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
 320}
 321
 322static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
 323{
 324        if (ctx->error < 0 || len == 0)
 325                return;
 326
 327        ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
 328}
 329
 330#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
 331        ({ \
 332                static const u8 d[] = { seq }; \
 333                s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
 334        })
 335
 336static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
 337{
 338        u8 id1, id2, id3;
 339        int ret;
 340
 341        s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
 342        s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
 343        s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
 344
 345        ret = s6e63m0_clear_error(ctx);
 346        if (ret) {
 347                dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
 348                ctx->lcd_type = 0x00;
 349                return ret;
 350        }
 351
 352        dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
 353
 354        /*
 355         * We attempt to detect what panel is mounted on the controller.
 356         * The third ID byte represents the desired ELVSS pulse for
 357         * some displays.
 358         */
 359        switch (id2) {
 360        case S6E63M0_LCD_ID_VALUE_M2:
 361                dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
 362                ctx->elvss_pulse = id3;
 363                break;
 364        case S6E63M0_LCD_ID_VALUE_SM2:
 365        case S6E63M0_LCD_ID_VALUE_SM2_1:
 366                dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
 367                ctx->elvss_pulse = id3;
 368                break;
 369        default:
 370                dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
 371                /* Default ELVSS pulse level */
 372                ctx->elvss_pulse = 0x16;
 373                break;
 374        }
 375
 376        ctx->lcd_type = id2;
 377
 378        return 0;
 379}
 380
 381static void s6e63m0_init(struct s6e63m0 *ctx)
 382{
 383        /*
 384         * We do not know why there is a difference in the DSI mode.
 385         * (No datasheet.)
 386         *
 387         * In the vendor driver this sequence is called
 388         * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
 389         */
 390        if (ctx->dsi_mode)
 391                s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
 392                                             0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
 393                                             0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
 394        else
 395                s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
 396                                             0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
 397                                             0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
 398
 399        s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
 400                                     0x02, 0x03, 0x1c, 0x10, 0x10);
 401        s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
 402                                     0x03, 0x00, 0x00);
 403
 404        s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
 405                                     0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
 406                                     0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
 407                                     0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
 408                                     0xd6);
 409        s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
 410                                     0x01);
 411
 412        s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
 413                                     0x00, 0x8e, 0x07);
 414        s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
 415
 416        s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
 417                                     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
 418                                     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
 419                                     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
 420                                     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
 421                                     0x21, 0x20, 0x1e, 0x1e);
 422
 423        s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,
 424                                     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
 425                                     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
 426                                     0x66, 0x66);
 427
 428        s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
 429                                     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
 430                                     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
 431                                     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
 432                                     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
 433                                     0x21, 0x20, 0x1e, 0x1e);
 434
 435        s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
 436                                     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
 437                                     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
 438                                     0x66, 0x66);
 439
 440        s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
 441                                     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
 442                                     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
 443                                     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
 444                                     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
 445                                     0x21, 0x20, 0x1e, 0x1e);
 446
 447        s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,
 448                                     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
 449                                     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
 450                                     0x66, 0x66);
 451
 452        s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
 453                                     0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
 454                                     0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
 455                                     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
 456                                     0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
 457
 458        s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
 459                                     0x10, 0x10, 0x0b, 0x05);
 460
 461        s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
 462                                     0x01);
 463
 464        s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
 465                                     0x0b);
 466}
 467
 468static int s6e63m0_power_on(struct s6e63m0 *ctx)
 469{
 470        int ret;
 471
 472        ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 473        if (ret < 0)
 474                return ret;
 475
 476        msleep(25);
 477
 478        /* Be sure to send a reset pulse */
 479        gpiod_set_value(ctx->reset_gpio, 1);
 480        msleep(5);
 481        gpiod_set_value(ctx->reset_gpio, 0);
 482        msleep(120);
 483
 484        return 0;
 485}
 486
 487static int s6e63m0_power_off(struct s6e63m0 *ctx)
 488{
 489        int ret;
 490
 491        gpiod_set_value(ctx->reset_gpio, 1);
 492        msleep(120);
 493
 494        ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 495        if (ret < 0)
 496                return ret;
 497
 498        return 0;
 499}
 500
 501static int s6e63m0_disable(struct drm_panel *panel)
 502{
 503        struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 504
 505        if (!ctx->enabled)
 506                return 0;
 507
 508        backlight_disable(ctx->bl_dev);
 509
 510        s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
 511        msleep(10);
 512        s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
 513        msleep(120);
 514
 515        ctx->enabled = false;
 516
 517        return 0;
 518}
 519
 520static int s6e63m0_unprepare(struct drm_panel *panel)
 521{
 522        struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 523        int ret;
 524
 525        if (!ctx->prepared)
 526                return 0;
 527
 528        s6e63m0_clear_error(ctx);
 529
 530        ret = s6e63m0_power_off(ctx);
 531        if (ret < 0)
 532                return ret;
 533
 534        ctx->prepared = false;
 535
 536        return 0;
 537}
 538
 539static int s6e63m0_prepare(struct drm_panel *panel)
 540{
 541        struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 542        int ret;
 543
 544        if (ctx->prepared)
 545                return 0;
 546
 547        ret = s6e63m0_power_on(ctx);
 548        if (ret < 0)
 549                return ret;
 550
 551        /* Magic to unlock level 2 control of the display */
 552        s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
 553        /* Magic to unlock MTP reading */
 554        s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
 555
 556        ret = s6e63m0_check_lcd_type(ctx);
 557        if (ret < 0)
 558                return ret;
 559
 560        s6e63m0_init(ctx);
 561
 562        ret = s6e63m0_clear_error(ctx);
 563
 564        if (ret < 0)
 565                s6e63m0_unprepare(panel);
 566
 567        ctx->prepared = true;
 568
 569        return ret;
 570}
 571
 572static int s6e63m0_enable(struct drm_panel *panel)
 573{
 574        struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
 575
 576        if (ctx->enabled)
 577                return 0;
 578
 579        s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
 580        msleep(120);
 581        s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
 582        msleep(10);
 583
 584        s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
 585                                     0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
 586                                     0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
 587                                     0x0F, 0x00);
 588
 589        backlight_enable(ctx->bl_dev);
 590
 591        ctx->enabled = true;
 592
 593        return 0;
 594}
 595
 596static int s6e63m0_get_modes(struct drm_panel *panel,
 597                             struct drm_connector *connector)
 598{
 599        struct drm_display_mode *mode;
 600        static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 601
 602        mode = drm_mode_duplicate(connector->dev, &default_mode);
 603        if (!mode) {
 604                dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
 605                        default_mode.hdisplay, default_mode.vdisplay,
 606                        drm_mode_vrefresh(&default_mode));
 607                return -ENOMEM;
 608        }
 609
 610        connector->display_info.width_mm = mode->width_mm;
 611        connector->display_info.height_mm = mode->height_mm;
 612        drm_display_info_set_bus_formats(&connector->display_info,
 613                                         &bus_format, 1);
 614        connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
 615                DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
 616
 617        drm_mode_set_name(mode);
 618
 619        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 620        drm_mode_probed_add(connector, mode);
 621
 622        return 1;
 623}
 624
 625static const struct drm_panel_funcs s6e63m0_drm_funcs = {
 626        .disable        = s6e63m0_disable,
 627        .unprepare      = s6e63m0_unprepare,
 628        .prepare        = s6e63m0_prepare,
 629        .enable         = s6e63m0_enable,
 630        .get_modes      = s6e63m0_get_modes,
 631};
 632
 633static int s6e63m0_set_brightness(struct backlight_device *bd)
 634{
 635        struct s6e63m0 *ctx = bl_get_data(bd);
 636        int brightness = bd->props.brightness;
 637        u8 elvss_val;
 638        u8 elvss_cmd_set[5];
 639        int i;
 640
 641        /* Adjust ELVSS to candela level */
 642        i = s6e63m0_elvss_per_gamma[brightness];
 643        elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
 644        if (elvss_val > 0x1f)
 645                elvss_val = 0x1f;
 646        elvss_cmd_set[0] = MCS_TEMP_SWIRE;
 647        elvss_cmd_set[1] = elvss_val;
 648        elvss_cmd_set[2] = elvss_val;
 649        elvss_cmd_set[3] = elvss_val;
 650        elvss_cmd_set[4] = elvss_val;
 651        s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
 652
 653        /* Update the ACL per gamma value */
 654        i = s6e63m0_acl_per_gamma[brightness];
 655        s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
 656                          ARRAY_SIZE(s6e63m0_acl[i]));
 657
 658        /* Update gamma table */
 659        s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
 660                          ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
 661        s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
 662
 663
 664        return s6e63m0_clear_error(ctx);
 665}
 666
 667static const struct backlight_ops s6e63m0_backlight_ops = {
 668        .update_status  = s6e63m0_set_brightness,
 669};
 670
 671static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
 672{
 673        struct backlight_properties props = {
 674                .type           = BACKLIGHT_RAW,
 675                .brightness     = max_brightness,
 676                .max_brightness = max_brightness,
 677        };
 678        struct device *dev = ctx->dev;
 679        int ret = 0;
 680
 681        ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
 682                                                     &s6e63m0_backlight_ops,
 683                                                     &props);
 684        if (IS_ERR(ctx->bl_dev)) {
 685                ret = PTR_ERR(ctx->bl_dev);
 686                dev_err(dev, "error registering backlight device (%d)\n", ret);
 687        }
 688
 689        return ret;
 690}
 691
 692int s6e63m0_probe(struct device *dev, void *trsp,
 693                  int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
 694                  int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
 695                  bool dsi_mode)
 696{
 697        struct s6e63m0 *ctx;
 698        u32 max_brightness;
 699        int ret;
 700
 701        ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
 702        if (!ctx)
 703                return -ENOMEM;
 704
 705        ctx->transport_data = trsp;
 706        ctx->dsi_mode = dsi_mode;
 707        ctx->dcs_read = dcs_read;
 708        ctx->dcs_write = dcs_write;
 709        dev_set_drvdata(dev, ctx);
 710
 711        ctx->dev = dev;
 712        ctx->enabled = false;
 713        ctx->prepared = false;
 714
 715        ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
 716        if (ret)
 717                max_brightness = MAX_BRIGHTNESS;
 718        if (max_brightness > MAX_BRIGHTNESS) {
 719                dev_err(dev, "illegal max brightness specified\n");
 720                max_brightness = MAX_BRIGHTNESS;
 721        }
 722
 723        ctx->supplies[0].supply = "vdd3";
 724        ctx->supplies[1].supply = "vci";
 725        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
 726                                      ctx->supplies);
 727        if (ret < 0) {
 728                dev_err(dev, "failed to get regulators: %d\n", ret);
 729                return ret;
 730        }
 731
 732        ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 733        if (IS_ERR(ctx->reset_gpio)) {
 734                dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
 735                return PTR_ERR(ctx->reset_gpio);
 736        }
 737
 738        drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
 739                       dsi_mode ? DRM_MODE_CONNECTOR_DSI :
 740                       DRM_MODE_CONNECTOR_DPI);
 741
 742        ret = s6e63m0_backlight_register(ctx, max_brightness);
 743        if (ret < 0)
 744                return ret;
 745
 746        drm_panel_add(&ctx->panel);
 747
 748        return 0;
 749}
 750EXPORT_SYMBOL_GPL(s6e63m0_probe);
 751
 752int s6e63m0_remove(struct device *dev)
 753{
 754        struct s6e63m0 *ctx = dev_get_drvdata(dev);
 755
 756        drm_panel_remove(&ctx->panel);
 757
 758        return 0;
 759}
 760EXPORT_SYMBOL_GPL(s6e63m0_remove);
 761
 762MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
 763MODULE_DESCRIPTION("s6e63m0 LCD Driver");
 764MODULE_LICENSE("GPL v2");
 765