Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * S6E63M0 AMOLED LCD drm_panel driver.
0004  *
0005  * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
0006  * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
0007  *
0008  * Andrzej Hajda <a.hajda@samsung.com>
0009  */
0010 
0011 #include <drm/drm_modes.h>
0012 #include <drm/drm_panel.h>
0013 
0014 #include <linux/backlight.h>
0015 #include <linux/delay.h>
0016 #include <linux/gpio/consumer.h>
0017 #include <linux/module.h>
0018 #include <linux/regulator/consumer.h>
0019 #include <linux/media-bus-format.h>
0020 
0021 #include <video/mipi_display.h>
0022 
0023 #include "panel-samsung-s6e63m0.h"
0024 
0025 #define S6E63M0_LCD_ID_VALUE_M2     0xA4
0026 #define S6E63M0_LCD_ID_VALUE_SM2    0xB4
0027 #define S6E63M0_LCD_ID_VALUE_SM2_1  0xB6
0028 
0029 #define NUM_GAMMA_LEVELS    28
0030 #define GAMMA_TABLE_COUNT   23
0031 
0032 #define MAX_BRIGHTNESS      (NUM_GAMMA_LEVELS - 1)
0033 
0034 /* array of gamma tables for gamma value 2.2 */
0035 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
0036     /* 30 cd */
0037     { MCS_PGAMMACTL, 0x02,
0038       0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
0039       0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
0040       0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
0041     /* 40 cd */
0042     { MCS_PGAMMACTL, 0x02,
0043       0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
0044       0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
0045       0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
0046     /* 50 cd */
0047     { MCS_PGAMMACTL, 0x02,
0048       0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
0049       0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
0050       0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
0051     /* 60 cd */
0052     { MCS_PGAMMACTL, 0x02,
0053       0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
0054       0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
0055       0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
0056     /* 70 cd */
0057     { MCS_PGAMMACTL, 0x02,
0058       0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
0059       0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
0060       0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
0061     /* 80 cd */
0062     { MCS_PGAMMACTL, 0x02,
0063       0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
0064       0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
0065       0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
0066     /* 90 cd */
0067     { MCS_PGAMMACTL, 0x02,
0068       0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
0069       0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
0070       0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
0071     /* 100 cd */
0072     { MCS_PGAMMACTL, 0x02,
0073       0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
0074       0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
0075       0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
0076     /* 110 cd */
0077     { MCS_PGAMMACTL, 0x02,
0078       0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
0079       0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
0080       0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
0081     /* 120 cd */
0082     { MCS_PGAMMACTL, 0x02,
0083       0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
0084       0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
0085       0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
0086     /* 130 cd */
0087     { MCS_PGAMMACTL, 0x02,
0088       0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
0089       0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
0090       0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
0091     /* 140 cd */
0092     { MCS_PGAMMACTL, 0x02,
0093       0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
0094       0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
0095       0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
0096     /* 150 cd */
0097     { MCS_PGAMMACTL, 0x02,
0098       0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
0099       0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
0100       0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
0101     /* 160 cd */
0102     { MCS_PGAMMACTL, 0x02,
0103       0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
0104       0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
0105       0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
0106     /* 170 cd */
0107     { MCS_PGAMMACTL, 0x02,
0108       0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
0109       0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
0110       0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
0111     /* 180 cd */
0112     { MCS_PGAMMACTL, 0x02,
0113       0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
0114       0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
0115       0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
0116     /* 190 cd */
0117     { MCS_PGAMMACTL, 0x02,
0118       0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
0119       0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
0120       0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
0121     /* 200 cd */
0122     { MCS_PGAMMACTL, 0x02,
0123       0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
0124       0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
0125       0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
0126     /* 210 cd */
0127     { MCS_PGAMMACTL, 0x02,
0128       0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
0129       0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
0130       0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
0131     /* 220 cd */
0132     { MCS_PGAMMACTL, 0x02,
0133       0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
0134       0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
0135       0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
0136     /* 230 cd */
0137     { MCS_PGAMMACTL, 0x02,
0138       0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
0139       0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
0140       0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
0141     /* 240 cd */
0142     { MCS_PGAMMACTL, 0x02,
0143       0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
0144       0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
0145       0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
0146     /* 250 cd */
0147     { MCS_PGAMMACTL, 0x02,
0148       0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
0149       0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
0150       0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
0151     /* 260 cd */
0152     { MCS_PGAMMACTL, 0x02,
0153       0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
0154       0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
0155       0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
0156     /* 270 cd */
0157     { MCS_PGAMMACTL, 0x02,
0158       0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
0159       0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
0160       0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
0161     /* 280 cd */
0162     { MCS_PGAMMACTL, 0x02,
0163       0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
0164       0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
0165       0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
0166     /* 290 cd */
0167     { MCS_PGAMMACTL, 0x02,
0168       0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
0169       0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
0170       0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
0171     /* 300 cd */
0172     { MCS_PGAMMACTL, 0x02,
0173       0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
0174       0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
0175       0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
0176 };
0177 
0178 #define NUM_ACL_LEVELS 7
0179 #define ACL_TABLE_COUNT 28
0180 
0181 static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
0182     /* NULL ACL */
0183     { MCS_BCMODE,
0184       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0185       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0186       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0187       0x00, 0x00, 0x00 },
0188     /* 40P ACL */
0189     { MCS_BCMODE,
0190       0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
0191       0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
0192       0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
0193       0x2B, 0x31, 0x36 },
0194     /* 43P ACL */
0195     { MCS_BCMODE,
0196       0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
0197       0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
0198       0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
0199       0x2F, 0x34, 0x3A },
0200     /* 45P ACL */
0201     { MCS_BCMODE,
0202       0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
0203       0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
0204       0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
0205       0x31, 0x37, 0x3D },
0206     /* 47P ACL */
0207     { MCS_BCMODE,
0208       0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
0209       0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
0210       0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
0211       0x34, 0x3B, 0x41 },
0212     /* 48P ACL */
0213     { MCS_BCMODE,
0214       0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
0215       0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
0216       0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
0217       0x36, 0x3C, 0x43 },
0218     /* 50P ACL */
0219     { MCS_BCMODE,
0220       0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
0221       0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
0222       0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
0223       0x38, 0x3F, 0x46 },
0224 };
0225 
0226 /* This tells us which ACL level goes with which gamma */
0227 static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
0228     /* 30 - 60 cd: ACL off/NULL */
0229     0, 0, 0, 0,
0230     /* 70 - 250 cd: 40P ACL */
0231     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0232     /* 260 - 300 cd: 50P ACL */
0233     6, 6, 6, 6, 6,
0234 };
0235 
0236 /* The ELVSS backlight regulator has 5 levels */
0237 #define S6E63M0_ELVSS_LEVELS 5
0238 
0239 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
0240     0x00,   /* not set */
0241     0x0D,   /* 30 cd - 100 cd */
0242     0x09,   /* 110 cd - 160 cd */
0243     0x07,   /* 170 cd - 200 cd */
0244     0x00,   /* 210 cd - 300 cd */
0245 };
0246 
0247 /* This tells us which ELVSS level goes with which gamma */
0248 static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
0249     /* 30 - 100 cd */
0250     1, 1, 1, 1, 1, 1, 1, 1,
0251     /* 110 - 160 cd */
0252     2, 2, 2, 2, 2, 2,
0253     /* 170 - 200 cd */
0254     3, 3, 3, 3,
0255     /* 210 - 300 cd */
0256     4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
0257 };
0258 
0259 struct s6e63m0 {
0260     struct device *dev;
0261     void *transport_data;
0262     int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val);
0263     int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len);
0264     struct drm_panel panel;
0265     struct backlight_device *bl_dev;
0266     u8 lcd_type;
0267     u8 elvss_pulse;
0268     bool dsi_mode;
0269 
0270     struct regulator_bulk_data supplies[2];
0271     struct gpio_desc *reset_gpio;
0272 
0273     bool prepared;
0274     bool enabled;
0275 
0276     /*
0277      * This field is tested by functions directly accessing bus before
0278      * transfer, transfer is skipped if it is set. In case of transfer
0279      * failure or unexpected response the field is set to error value.
0280      * Such construct allows to eliminate many checks in higher level
0281      * functions.
0282      */
0283     int error;
0284 };
0285 
0286 static const struct drm_display_mode default_mode = {
0287     .clock      = 25628,
0288     .hdisplay   = 480,
0289     .hsync_start    = 480 + 16,
0290     .hsync_end  = 480 + 16 + 2,
0291     .htotal     = 480 + 16 + 2 + 16,
0292     .vdisplay   = 800,
0293     .vsync_start    = 800 + 28,
0294     .vsync_end  = 800 + 28 + 2,
0295     .vtotal     = 800 + 28 + 2 + 1,
0296     .width_mm   = 53,
0297     .height_mm  = 89,
0298     .flags      = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
0299 };
0300 
0301 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
0302 {
0303     return container_of(panel, struct s6e63m0, panel);
0304 }
0305 
0306 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
0307 {
0308     int ret = ctx->error;
0309 
0310     ctx->error = 0;
0311     return ret;
0312 }
0313 
0314 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
0315 {
0316     if (ctx->error < 0)
0317         return;
0318 
0319     ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
0320 }
0321 
0322 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
0323 {
0324     if (ctx->error < 0 || len == 0)
0325         return;
0326 
0327     ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
0328 }
0329 
0330 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
0331     ({ \
0332         static const u8 d[] = { seq }; \
0333         s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
0334     })
0335 
0336 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
0337 {
0338     u8 id1, id2, id3;
0339     int ret;
0340 
0341     s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
0342     s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
0343     s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
0344 
0345     ret = s6e63m0_clear_error(ctx);
0346     if (ret) {
0347         dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
0348         ctx->lcd_type = 0x00;
0349         return ret;
0350     }
0351 
0352     dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
0353 
0354     /*
0355      * We attempt to detect what panel is mounted on the controller.
0356      * The third ID byte represents the desired ELVSS pulse for
0357      * some displays.
0358      */
0359     switch (id2) {
0360     case S6E63M0_LCD_ID_VALUE_M2:
0361         dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
0362         ctx->elvss_pulse = id3;
0363         break;
0364     case S6E63M0_LCD_ID_VALUE_SM2:
0365     case S6E63M0_LCD_ID_VALUE_SM2_1:
0366         dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
0367         ctx->elvss_pulse = id3;
0368         break;
0369     default:
0370         dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
0371         /* Default ELVSS pulse level */
0372         ctx->elvss_pulse = 0x16;
0373         break;
0374     }
0375 
0376     ctx->lcd_type = id2;
0377 
0378     return 0;
0379 }
0380 
0381 static void s6e63m0_init(struct s6e63m0 *ctx)
0382 {
0383     /*
0384      * We do not know why there is a difference in the DSI mode.
0385      * (No datasheet.)
0386      *
0387      * In the vendor driver this sequence is called
0388      * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
0389      */
0390     if (ctx->dsi_mode)
0391         s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
0392                          0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
0393                          0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
0394     else
0395         s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
0396                          0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
0397                          0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
0398 
0399     s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
0400                      0x02, 0x03, 0x1c, 0x10, 0x10);
0401     s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
0402                      0x03, 0x00, 0x00);
0403 
0404     s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
0405                      0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
0406                      0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
0407                      0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
0408                      0xd6);
0409     s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
0410                      0x01);
0411 
0412     s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
0413                      0x00, 0x8e, 0x07);
0414     s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
0415 
0416     s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
0417                      0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
0418                      0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
0419                      0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
0420                      0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
0421                      0x21, 0x20, 0x1e, 0x1e);
0422 
0423     s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,
0424                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
0425                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
0426                      0x66, 0x66);
0427 
0428     s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
0429                      0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
0430                      0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
0431                      0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
0432                      0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
0433                      0x21, 0x20, 0x1e, 0x1e);
0434 
0435     s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
0436                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
0437                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
0438                      0x66, 0x66);
0439 
0440     s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
0441                      0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
0442                      0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
0443                      0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
0444                      0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
0445                      0x21, 0x20, 0x1e, 0x1e);
0446 
0447     s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,
0448                      0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
0449                      0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
0450                      0x66, 0x66);
0451 
0452     s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
0453                      0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
0454                      0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
0455                      0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
0456                      0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
0457 
0458     s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
0459                      0x10, 0x10, 0x0b, 0x05);
0460 
0461     s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
0462                      0x01);
0463 
0464     s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
0465                      0x0b);
0466 }
0467 
0468 static int s6e63m0_power_on(struct s6e63m0 *ctx)
0469 {
0470     int ret;
0471 
0472     ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0473     if (ret < 0)
0474         return ret;
0475 
0476     msleep(25);
0477 
0478     /* Be sure to send a reset pulse */
0479     gpiod_set_value(ctx->reset_gpio, 1);
0480     msleep(5);
0481     gpiod_set_value(ctx->reset_gpio, 0);
0482     msleep(120);
0483 
0484     return 0;
0485 }
0486 
0487 static int s6e63m0_power_off(struct s6e63m0 *ctx)
0488 {
0489     int ret;
0490 
0491     gpiod_set_value(ctx->reset_gpio, 1);
0492     msleep(120);
0493 
0494     ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0495     if (ret < 0)
0496         return ret;
0497 
0498     return 0;
0499 }
0500 
0501 static int s6e63m0_disable(struct drm_panel *panel)
0502 {
0503     struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
0504 
0505     if (!ctx->enabled)
0506         return 0;
0507 
0508     backlight_disable(ctx->bl_dev);
0509 
0510     s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
0511     msleep(10);
0512     s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
0513     msleep(120);
0514 
0515     ctx->enabled = false;
0516 
0517     return 0;
0518 }
0519 
0520 static int s6e63m0_unprepare(struct drm_panel *panel)
0521 {
0522     struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
0523     int ret;
0524 
0525     if (!ctx->prepared)
0526         return 0;
0527 
0528     s6e63m0_clear_error(ctx);
0529 
0530     ret = s6e63m0_power_off(ctx);
0531     if (ret < 0)
0532         return ret;
0533 
0534     ctx->prepared = false;
0535 
0536     return 0;
0537 }
0538 
0539 static int s6e63m0_prepare(struct drm_panel *panel)
0540 {
0541     struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
0542     int ret;
0543 
0544     if (ctx->prepared)
0545         return 0;
0546 
0547     ret = s6e63m0_power_on(ctx);
0548     if (ret < 0)
0549         return ret;
0550 
0551     /* Magic to unlock level 2 control of the display */
0552     s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
0553     /* Magic to unlock MTP reading */
0554     s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
0555 
0556     ret = s6e63m0_check_lcd_type(ctx);
0557     if (ret < 0)
0558         return ret;
0559 
0560     s6e63m0_init(ctx);
0561 
0562     ret = s6e63m0_clear_error(ctx);
0563 
0564     if (ret < 0)
0565         s6e63m0_unprepare(panel);
0566 
0567     ctx->prepared = true;
0568 
0569     return ret;
0570 }
0571 
0572 static int s6e63m0_enable(struct drm_panel *panel)
0573 {
0574     struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
0575 
0576     if (ctx->enabled)
0577         return 0;
0578 
0579     s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
0580     msleep(120);
0581     s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
0582     msleep(10);
0583 
0584     s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
0585                      0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
0586                      0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
0587                      0x0F, 0x00);
0588 
0589     backlight_enable(ctx->bl_dev);
0590 
0591     ctx->enabled = true;
0592 
0593     return 0;
0594 }
0595 
0596 static int s6e63m0_get_modes(struct drm_panel *panel,
0597                  struct drm_connector *connector)
0598 {
0599     struct drm_display_mode *mode;
0600     static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
0601 
0602     mode = drm_mode_duplicate(connector->dev, &default_mode);
0603     if (!mode) {
0604         dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
0605             default_mode.hdisplay, default_mode.vdisplay,
0606             drm_mode_vrefresh(&default_mode));
0607         return -ENOMEM;
0608     }
0609 
0610     connector->display_info.width_mm = mode->width_mm;
0611     connector->display_info.height_mm = mode->height_mm;
0612     drm_display_info_set_bus_formats(&connector->display_info,
0613                      &bus_format, 1);
0614     connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
0615         DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
0616 
0617     drm_mode_set_name(mode);
0618 
0619     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0620     drm_mode_probed_add(connector, mode);
0621 
0622     return 1;
0623 }
0624 
0625 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
0626     .disable    = s6e63m0_disable,
0627     .unprepare  = s6e63m0_unprepare,
0628     .prepare    = s6e63m0_prepare,
0629     .enable     = s6e63m0_enable,
0630     .get_modes  = s6e63m0_get_modes,
0631 };
0632 
0633 static int s6e63m0_set_brightness(struct backlight_device *bd)
0634 {
0635     struct s6e63m0 *ctx = bl_get_data(bd);
0636     int brightness = bd->props.brightness;
0637     u8 elvss_val;
0638     u8 elvss_cmd_set[5];
0639     int i;
0640 
0641     /* Adjust ELVSS to candela level */
0642     i = s6e63m0_elvss_per_gamma[brightness];
0643     elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
0644     if (elvss_val > 0x1f)
0645         elvss_val = 0x1f;
0646     elvss_cmd_set[0] = MCS_TEMP_SWIRE;
0647     elvss_cmd_set[1] = elvss_val;
0648     elvss_cmd_set[2] = elvss_val;
0649     elvss_cmd_set[3] = elvss_val;
0650     elvss_cmd_set[4] = elvss_val;
0651     s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
0652 
0653     /* Update the ACL per gamma value */
0654     i = s6e63m0_acl_per_gamma[brightness];
0655     s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
0656               ARRAY_SIZE(s6e63m0_acl[i]));
0657 
0658     /* Update gamma table */
0659     s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
0660               ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
0661     s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
0662 
0663 
0664     return s6e63m0_clear_error(ctx);
0665 }
0666 
0667 static const struct backlight_ops s6e63m0_backlight_ops = {
0668     .update_status  = s6e63m0_set_brightness,
0669 };
0670 
0671 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
0672 {
0673     struct backlight_properties props = {
0674         .type       = BACKLIGHT_RAW,
0675         .brightness = max_brightness,
0676         .max_brightness = max_brightness,
0677     };
0678     struct device *dev = ctx->dev;
0679     int ret = 0;
0680 
0681     ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
0682                              &s6e63m0_backlight_ops,
0683                              &props);
0684     if (IS_ERR(ctx->bl_dev)) {
0685         ret = PTR_ERR(ctx->bl_dev);
0686         dev_err(dev, "error registering backlight device (%d)\n", ret);
0687     }
0688 
0689     return ret;
0690 }
0691 
0692 int s6e63m0_probe(struct device *dev, void *trsp,
0693           int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
0694           int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
0695           bool dsi_mode)
0696 {
0697     struct s6e63m0 *ctx;
0698     u32 max_brightness;
0699     int ret;
0700 
0701     ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
0702     if (!ctx)
0703         return -ENOMEM;
0704 
0705     ctx->transport_data = trsp;
0706     ctx->dsi_mode = dsi_mode;
0707     ctx->dcs_read = dcs_read;
0708     ctx->dcs_write = dcs_write;
0709     dev_set_drvdata(dev, ctx);
0710 
0711     ctx->dev = dev;
0712     ctx->enabled = false;
0713     ctx->prepared = false;
0714 
0715     ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
0716     if (ret)
0717         max_brightness = MAX_BRIGHTNESS;
0718     if (max_brightness > MAX_BRIGHTNESS) {
0719         dev_err(dev, "illegal max brightness specified\n");
0720         max_brightness = MAX_BRIGHTNESS;
0721     }
0722 
0723     ctx->supplies[0].supply = "vdd3";
0724     ctx->supplies[1].supply = "vci";
0725     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
0726                       ctx->supplies);
0727     if (ret < 0) {
0728         dev_err(dev, "failed to get regulators: %d\n", ret);
0729         return ret;
0730     }
0731 
0732     ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
0733     if (IS_ERR(ctx->reset_gpio)) {
0734         dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
0735         return PTR_ERR(ctx->reset_gpio);
0736     }
0737 
0738     drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
0739                dsi_mode ? DRM_MODE_CONNECTOR_DSI :
0740                DRM_MODE_CONNECTOR_DPI);
0741 
0742     ret = s6e63m0_backlight_register(ctx, max_brightness);
0743     if (ret < 0)
0744         return ret;
0745 
0746     drm_panel_add(&ctx->panel);
0747 
0748     return 0;
0749 }
0750 EXPORT_SYMBOL_GPL(s6e63m0_probe);
0751 
0752 void s6e63m0_remove(struct device *dev)
0753 {
0754     struct s6e63m0 *ctx = dev_get_drvdata(dev);
0755 
0756     drm_panel_remove(&ctx->panel);
0757 }
0758 EXPORT_SYMBOL_GPL(s6e63m0_remove);
0759 
0760 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
0761 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
0762 MODULE_LICENSE("GPL v2");