linux/drivers/video/exynos/s6e8ax0.c
<<
>>
Prefs
   1/* linux/drivers/video/exynos/s6e8ax0.c
   2 *
   3 * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
   4 *
   5 * Inki Dae, <inki.dae@samsung.com>
   6 * Donghwa Lee, <dh09.lee@samsung.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11*/
  12
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/errno.h>
  16#include <linux/mutex.h>
  17#include <linux/wait.h>
  18#include <linux/ctype.h>
  19#include <linux/io.h>
  20#include <linux/delay.h>
  21#include <linux/irq.h>
  22#include <linux/interrupt.h>
  23#include <linux/lcd.h>
  24#include <linux/fb.h>
  25#include <linux/backlight.h>
  26#include <linux/regulator/consumer.h>
  27
  28#include <video/mipi_display.h>
  29#include <video/exynos_mipi_dsim.h>
  30
  31#define LDI_MTP_LENGTH          24
  32#define DSIM_PM_STABLE_TIME     10
  33#define MIN_BRIGHTNESS          0
  34#define MAX_BRIGHTNESS          24
  35#define GAMMA_TABLE_COUNT       26
  36
  37#define POWER_IS_ON(pwr)        ((pwr) == FB_BLANK_UNBLANK)
  38#define POWER_IS_OFF(pwr)       ((pwr) == FB_BLANK_POWERDOWN)
  39#define POWER_IS_NRM(pwr)       ((pwr) == FB_BLANK_NORMAL)
  40
  41#define lcd_to_master(a)        (a->dsim_dev->master)
  42#define lcd_to_master_ops(a)    ((lcd_to_master(a))->master_ops)
  43
  44enum {
  45        DSIM_NONE_STATE = 0,
  46        DSIM_RESUME_COMPLETE = 1,
  47        DSIM_FRAME_DONE = 2,
  48};
  49
  50struct s6e8ax0 {
  51        struct device   *dev;
  52        unsigned int                    power;
  53        unsigned int                    id;
  54        unsigned int                    gamma;
  55        unsigned int                    acl_enable;
  56        unsigned int                    cur_acl;
  57
  58        struct lcd_device       *ld;
  59        struct backlight_device *bd;
  60
  61        struct mipi_dsim_lcd_device     *dsim_dev;
  62        struct lcd_platform_data        *ddi_pd;
  63        struct mutex                    lock;
  64        bool  enabled;
  65};
  66
  67
  68static struct regulator_bulk_data supplies[] = {
  69        { .supply = "vdd3", },
  70        { .supply = "vci", },
  71};
  72
  73static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd)
  74{
  75        int ret = 0;
  76        struct lcd_platform_data *pd = NULL;
  77
  78        pd = lcd->ddi_pd;
  79        mutex_lock(&lcd->lock);
  80        if (!lcd->enabled) {
  81                ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
  82                if (ret)
  83                        goto out;
  84
  85                lcd->enabled = true;
  86        }
  87        msleep(pd->power_on_delay);
  88out:
  89        mutex_unlock(&lcd->lock);
  90}
  91
  92static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd)
  93{
  94        int ret = 0;
  95
  96        mutex_lock(&lcd->lock);
  97        if (lcd->enabled) {
  98                ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
  99                if (ret)
 100                        goto out;
 101
 102                lcd->enabled = false;
 103        }
 104out:
 105        mutex_unlock(&lcd->lock);
 106}
 107
 108static const unsigned char s6e8ax0_22_gamma_30[] = {
 109        0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
 110        0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
 111        0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
 112};
 113
 114static const unsigned char s6e8ax0_22_gamma_50[] = {
 115        0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
 116        0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
 117        0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
 118};
 119
 120static const unsigned char s6e8ax0_22_gamma_60[] = {
 121        0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
 122        0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
 123        0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
 124};
 125
 126static const unsigned char s6e8ax0_22_gamma_70[] = {
 127        0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
 128        0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
 129        0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
 130};
 131
 132static const unsigned char s6e8ax0_22_gamma_80[] = {
 133        0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
 134        0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
 135        0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
 136};
 137
 138static const unsigned char s6e8ax0_22_gamma_90[] = {
 139        0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
 140        0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
 141        0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
 142};
 143
 144static const unsigned char s6e8ax0_22_gamma_100[] = {
 145        0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
 146        0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
 147        0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
 148};
 149
 150static const unsigned char s6e8ax0_22_gamma_120[] = {
 151        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
 152        0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
 153        0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
 154};
 155
 156static const unsigned char s6e8ax0_22_gamma_130[] = {
 157        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
 158        0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
 159        0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
 160};
 161
 162static const unsigned char s6e8ax0_22_gamma_140[] = {
 163        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
 164        0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
 165        0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
 166};
 167
 168static const unsigned char s6e8ax0_22_gamma_150[] = {
 169        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
 170        0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
 171        0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
 172};
 173
 174static const unsigned char s6e8ax0_22_gamma_160[] = {
 175        0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
 176        0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
 177        0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
 178};
 179
 180static const unsigned char s6e8ax0_22_gamma_170[] = {
 181        0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
 182        0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
 183        0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
 184};
 185
 186static const unsigned char s6e8ax0_22_gamma_180[] = {
 187        0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
 188        0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
 189        0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
 190};
 191
 192static const unsigned char s6e8ax0_22_gamma_190[] = {
 193        0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
 194        0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
 195        0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
 196};
 197
 198static const unsigned char s6e8ax0_22_gamma_200[] = {
 199        0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
 200        0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
 201        0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
 202};
 203
 204static const unsigned char s6e8ax0_22_gamma_210[] = {
 205        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
 206        0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
 207        0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
 208};
 209
 210static const unsigned char s6e8ax0_22_gamma_220[] = {
 211        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
 212        0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
 213        0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
 214};
 215
 216static const unsigned char s6e8ax0_22_gamma_230[] = {
 217        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
 218        0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
 219        0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
 220};
 221
 222static const unsigned char s6e8ax0_22_gamma_240[] = {
 223        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
 224        0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
 225        0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
 226};
 227
 228static const unsigned char s6e8ax0_22_gamma_250[] = {
 229        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
 230        0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
 231        0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
 232};
 233
 234static const unsigned char s6e8ax0_22_gamma_260[] = {
 235        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
 236        0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
 237        0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
 238};
 239
 240static const unsigned char s6e8ax0_22_gamma_270[] = {
 241        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
 242        0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
 243        0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
 244};
 245
 246static const unsigned char s6e8ax0_22_gamma_280[] = {
 247        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
 248        0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
 249        0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
 250};
 251
 252static const unsigned char s6e8ax0_22_gamma_300[] = {
 253        0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
 254        0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
 255        0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
 256};
 257
 258static const unsigned char *s6e8ax0_22_gamma_table[] = {
 259        s6e8ax0_22_gamma_30,
 260        s6e8ax0_22_gamma_50,
 261        s6e8ax0_22_gamma_60,
 262        s6e8ax0_22_gamma_70,
 263        s6e8ax0_22_gamma_80,
 264        s6e8ax0_22_gamma_90,
 265        s6e8ax0_22_gamma_100,
 266        s6e8ax0_22_gamma_120,
 267        s6e8ax0_22_gamma_130,
 268        s6e8ax0_22_gamma_140,
 269        s6e8ax0_22_gamma_150,
 270        s6e8ax0_22_gamma_160,
 271        s6e8ax0_22_gamma_170,
 272        s6e8ax0_22_gamma_180,
 273        s6e8ax0_22_gamma_190,
 274        s6e8ax0_22_gamma_200,
 275        s6e8ax0_22_gamma_210,
 276        s6e8ax0_22_gamma_220,
 277        s6e8ax0_22_gamma_230,
 278        s6e8ax0_22_gamma_240,
 279        s6e8ax0_22_gamma_250,
 280        s6e8ax0_22_gamma_260,
 281        s6e8ax0_22_gamma_270,
 282        s6e8ax0_22_gamma_280,
 283        s6e8ax0_22_gamma_300,
 284};
 285
 286static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
 287{
 288        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 289
 290        static const unsigned char data_to_send[] = {
 291                0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
 292                0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
 293                0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
 294                0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
 295        };
 296        static const unsigned char data_to_send_panel_reverse[] = {
 297                0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
 298                0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
 299                0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
 300                0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
 301        };
 302
 303        if (lcd->dsim_dev->panel_reverse)
 304                ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 305                                data_to_send_panel_reverse,
 306                                ARRAY_SIZE(data_to_send_panel_reverse));
 307        else
 308                ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 309                                data_to_send, ARRAY_SIZE(data_to_send));
 310}
 311
 312static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
 313{
 314        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 315        static const unsigned char data_to_send[] = {
 316                0xf2, 0x80, 0x03, 0x0d
 317        };
 318
 319        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 320                data_to_send, ARRAY_SIZE(data_to_send));
 321}
 322
 323/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
 324static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
 325{
 326        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 327        unsigned int gamma = lcd->bd->props.brightness;
 328
 329        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 330                        s6e8ax0_22_gamma_table[gamma],
 331                        GAMMA_TABLE_COUNT);
 332}
 333
 334static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
 335{
 336        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 337        static const unsigned char data_to_send[] = {
 338                0xf7, 0x03
 339        };
 340
 341        ops->cmd_write(lcd_to_master(lcd),
 342                MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send,
 343                ARRAY_SIZE(data_to_send));
 344}
 345
 346static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
 347{
 348        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 349        static const unsigned char data_to_send[] = {
 350                0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
 351                0x0d, 0x00, 0x00
 352        };
 353
 354        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 355                data_to_send, ARRAY_SIZE(data_to_send));
 356}
 357
 358static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
 359{
 360        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 361        static const unsigned char data_to_send[] = {
 362                0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
 363                0x00
 364        };
 365
 366        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 367                data_to_send, ARRAY_SIZE(data_to_send));
 368}
 369
 370static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
 371{
 372        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 373        static const unsigned char data_to_send[] = {
 374                0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
 375        };
 376
 377        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 378                data_to_send, ARRAY_SIZE(data_to_send));
 379}
 380
 381static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
 382{
 383        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 384        static const unsigned char data_to_send[] = {
 385                0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
 386        };
 387
 388        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 389                data_to_send, ARRAY_SIZE(data_to_send));
 390}
 391
 392static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
 393{
 394        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 395        static const unsigned char data_to_send[] = {
 396                0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
 397        };
 398
 399        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 400                data_to_send, ARRAY_SIZE(data_to_send));
 401}
 402static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
 403{
 404        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 405        static const unsigned char data_to_send[] = {
 406                0xe3, 0x40
 407        };
 408
 409        ops->cmd_write(lcd_to_master(lcd),
 410                MIPI_DSI_DCS_SHORT_WRITE_PARAM,
 411                data_to_send, ARRAY_SIZE(data_to_send));
 412}
 413
 414static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
 415{
 416        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 417        static const unsigned char data_to_send[] = {
 418                0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
 419        };
 420
 421        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 422                data_to_send, ARRAY_SIZE(data_to_send));
 423}
 424
 425static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
 426{
 427        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 428        static const unsigned char data_to_send[] = {
 429                0xb1, 0x04, 0x00
 430        };
 431
 432        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 433                data_to_send, ARRAY_SIZE(data_to_send));
 434}
 435
 436static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
 437{
 438        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 439        static const unsigned char data_to_send[] = {
 440                0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
 441                0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
 442                0x64, 0xaf
 443        };
 444
 445        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 446                data_to_send, ARRAY_SIZE(data_to_send));
 447}
 448
 449static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
 450{
 451        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 452        static const unsigned char data_to_send[] = {
 453                0x10, 0x00
 454        };
 455
 456        ops->cmd_write(lcd_to_master(lcd),
 457                MIPI_DSI_DCS_SHORT_WRITE,
 458                data_to_send, ARRAY_SIZE(data_to_send));
 459}
 460
 461static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
 462{
 463        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 464        static const unsigned char data_to_send[] = {
 465                0x11, 0x00
 466        };
 467
 468        ops->cmd_write(lcd_to_master(lcd),
 469                MIPI_DSI_DCS_SHORT_WRITE,
 470                data_to_send, ARRAY_SIZE(data_to_send));
 471}
 472
 473static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
 474{
 475        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 476        static const unsigned char data_to_send[] = {
 477                0x29, 0x00
 478        };
 479
 480        ops->cmd_write(lcd_to_master(lcd),
 481                MIPI_DSI_DCS_SHORT_WRITE,
 482                data_to_send, ARRAY_SIZE(data_to_send));
 483}
 484
 485static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
 486{
 487        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 488        static const unsigned char data_to_send[] = {
 489                0x28, 0x00
 490        };
 491
 492        ops->cmd_write(lcd_to_master(lcd),
 493                MIPI_DSI_DCS_SHORT_WRITE,
 494                data_to_send, ARRAY_SIZE(data_to_send));
 495}
 496
 497static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
 498{
 499        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 500        static const unsigned char data_to_send[] = {
 501                0xf0, 0x5a, 0x5a
 502        };
 503
 504        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 505                data_to_send, ARRAY_SIZE(data_to_send));
 506}
 507
 508static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
 509{
 510        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 511        static const unsigned char data_to_send[] = {
 512                0xc0, 0x01
 513        };
 514
 515        ops->cmd_write(lcd_to_master(lcd),
 516                MIPI_DSI_DCS_SHORT_WRITE,
 517                data_to_send, ARRAY_SIZE(data_to_send));
 518}
 519
 520static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
 521{
 522        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 523        static const unsigned char data_to_send[] = {
 524                0xc0, 0x00
 525        };
 526
 527        ops->cmd_write(lcd_to_master(lcd),
 528                MIPI_DSI_DCS_SHORT_WRITE,
 529                data_to_send, ARRAY_SIZE(data_to_send));
 530}
 531
 532/* Full white 50% reducing setting */
 533static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
 534{
 535        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 536        /* Full white 50% reducing setting */
 537        static const unsigned char cutoff_50[] = {
 538                0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
 539                0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
 540                0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
 541                0x3f, 0x46
 542        };
 543        /* Full white 45% reducing setting */
 544        static const unsigned char cutoff_45[] = {
 545                0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
 546                0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
 547                0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
 548                0x37, 0x3d
 549        };
 550        /* Full white 40% reducing setting */
 551        static const unsigned char cutoff_40[] = {
 552                0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
 553                0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
 554                0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
 555                0x31, 0x36
 556        };
 557
 558        if (lcd->acl_enable) {
 559                if (lcd->cur_acl == 0) {
 560                        if (lcd->gamma == 0 || lcd->gamma == 1) {
 561                                s6e8ax0_acl_off(lcd);
 562                                dev_dbg(&lcd->ld->dev,
 563                                        "cur_acl=%d\n", lcd->cur_acl);
 564                        } else
 565                                s6e8ax0_acl_on(lcd);
 566                }
 567                switch (lcd->gamma) {
 568                case 0: /* 30cd */
 569                        s6e8ax0_acl_off(lcd);
 570                        lcd->cur_acl = 0;
 571                        break;
 572                case 1 ... 3: /* 50cd ~ 90cd */
 573                        ops->cmd_write(lcd_to_master(lcd),
 574                                MIPI_DSI_DCS_LONG_WRITE,
 575                                cutoff_40,
 576                                ARRAY_SIZE(cutoff_40));
 577                        lcd->cur_acl = 40;
 578                        break;
 579                case 4 ... 7: /* 120cd ~ 210cd */
 580                        ops->cmd_write(lcd_to_master(lcd),
 581                                MIPI_DSI_DCS_LONG_WRITE,
 582                                cutoff_45,
 583                                ARRAY_SIZE(cutoff_45));
 584                        lcd->cur_acl = 45;
 585                        break;
 586                case 8 ... 10: /* 220cd ~ 300cd */
 587                        ops->cmd_write(lcd_to_master(lcd),
 588                                MIPI_DSI_DCS_LONG_WRITE,
 589                                cutoff_50,
 590                                ARRAY_SIZE(cutoff_50));
 591                        lcd->cur_acl = 50;
 592                        break;
 593                default:
 594                        break;
 595                }
 596        } else {
 597                s6e8ax0_acl_off(lcd);
 598                lcd->cur_acl = 0;
 599                dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl);
 600        }
 601}
 602
 603static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
 604{
 605        unsigned int ret;
 606        unsigned int addr = 0xd1;       /* MTP ID */
 607        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 608
 609        ret = ops->cmd_read(lcd_to_master(lcd),
 610                        MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM,
 611                        addr, 3, mtp_id);
 612}
 613
 614static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
 615{
 616        s6e8ax0_apply_level2_key(lcd);
 617        s6e8ax0_sleep_out(lcd);
 618        msleep(1);
 619        s6e8ax0_panel_cond(lcd);
 620        s6e8ax0_display_cond(lcd);
 621        s6e8ax0_gamma_cond(lcd);
 622        s6e8ax0_gamma_update(lcd);
 623
 624        s6e8ax0_etc_cond1(lcd);
 625        s6e8ax0_etc_cond2(lcd);
 626        s6e8ax0_etc_cond3(lcd);
 627        s6e8ax0_etc_cond4(lcd);
 628        s6e8ax0_etc_cond5(lcd);
 629        s6e8ax0_etc_cond6(lcd);
 630        s6e8ax0_etc_cond7(lcd);
 631
 632        s6e8ax0_elvss_nvm_set(lcd);
 633        s6e8ax0_elvss_set(lcd);
 634
 635        s6e8ax0_acl_ctrl_set(lcd);
 636        s6e8ax0_acl_on(lcd);
 637
 638        /* if ID3 value is not 33h, branch private elvss mode */
 639        msleep(lcd->ddi_pd->power_on_delay);
 640
 641        return 0;
 642}
 643
 644static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
 645{
 646        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 647
 648        ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
 649                        s6e8ax0_22_gamma_table[brightness],
 650                        ARRAY_SIZE(s6e8ax0_22_gamma_table));
 651
 652        /* update gamma table. */
 653        s6e8ax0_gamma_update(lcd);
 654        lcd->gamma = brightness;
 655
 656        return 0;
 657}
 658
 659static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
 660{
 661        s6e8ax0_update_gamma_ctrl(lcd, gamma);
 662
 663        return 0;
 664}
 665
 666static int s6e8ax0_set_power(struct lcd_device *ld, int power)
 667{
 668        struct s6e8ax0 *lcd = lcd_get_data(ld);
 669        struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
 670        int ret = 0;
 671
 672        if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
 673                        power != FB_BLANK_NORMAL) {
 674                dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
 675                return -EINVAL;
 676        }
 677
 678        if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) {
 679                /* LCD power on */
 680                if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power))
 681                        || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) {
 682                        ret = ops->set_blank_mode(lcd_to_master(lcd), power);
 683                        if (!ret && lcd->power != power)
 684                                lcd->power = power;
 685                }
 686        } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) {
 687                /* LCD power off */
 688                if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) ||
 689                (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) {
 690                        ret = ops->set_early_blank_mode(lcd_to_master(lcd),
 691                                                        power);
 692                        if (!ret && lcd->power != power)
 693                                lcd->power = power;
 694                }
 695        }
 696
 697        return ret;
 698}
 699
 700static int s6e8ax0_get_power(struct lcd_device *ld)
 701{
 702        struct s6e8ax0 *lcd = lcd_get_data(ld);
 703
 704        return lcd->power;
 705}
 706
 707static int s6e8ax0_get_brightness(struct backlight_device *bd)
 708{
 709        return bd->props.brightness;
 710}
 711
 712static int s6e8ax0_set_brightness(struct backlight_device *bd)
 713{
 714        int ret = 0, brightness = bd->props.brightness;
 715        struct s6e8ax0 *lcd = bl_get_data(bd);
 716
 717        if (brightness < MIN_BRIGHTNESS ||
 718                brightness > bd->props.max_brightness) {
 719                dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
 720                        MIN_BRIGHTNESS, MAX_BRIGHTNESS);
 721                return -EINVAL;
 722        }
 723
 724        ret = s6e8ax0_gamma_ctrl(lcd, brightness);
 725        if (ret) {
 726                dev_err(&bd->dev, "lcd brightness setting failed.\n");
 727                return -EIO;
 728        }
 729
 730        return ret;
 731}
 732
 733static struct lcd_ops s6e8ax0_lcd_ops = {
 734        .set_power = s6e8ax0_set_power,
 735        .get_power = s6e8ax0_get_power,
 736};
 737
 738static const struct backlight_ops s6e8ax0_backlight_ops = {
 739        .get_brightness = s6e8ax0_get_brightness,
 740        .update_status = s6e8ax0_set_brightness,
 741};
 742
 743static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
 744{
 745        struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
 746
 747        msleep(lcd->ddi_pd->power_on_delay);
 748
 749        /* lcd power on */
 750        if (power)
 751                s6e8ax0_regulator_enable(lcd);
 752        else
 753                s6e8ax0_regulator_disable(lcd);
 754
 755        msleep(lcd->ddi_pd->reset_delay);
 756
 757        /* lcd reset */
 758        if (lcd->ddi_pd->reset)
 759                lcd->ddi_pd->reset(lcd->ld);
 760        msleep(5);
 761}
 762
 763static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
 764{
 765        struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
 766
 767        s6e8ax0_panel_init(lcd);
 768        s6e8ax0_display_on(lcd);
 769
 770        lcd->power = FB_BLANK_UNBLANK;
 771}
 772
 773static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
 774{
 775        struct s6e8ax0 *lcd;
 776        int ret;
 777        u8 mtp_id[3] = {0, };
 778
 779        lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL);
 780        if (!lcd) {
 781                dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
 782                return -ENOMEM;
 783        }
 784
 785        lcd->dsim_dev = dsim_dev;
 786        lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
 787        lcd->dev = &dsim_dev->dev;
 788
 789        mutex_init(&lcd->lock);
 790
 791        ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
 792        if (ret) {
 793                dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
 794                return ret;
 795        }
 796
 797        lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd,
 798                        &s6e8ax0_lcd_ops);
 799        if (IS_ERR(lcd->ld)) {
 800                dev_err(lcd->dev, "failed to register lcd ops.\n");
 801                return PTR_ERR(lcd->ld);
 802        }
 803
 804        lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd,
 805                        &s6e8ax0_backlight_ops, NULL);
 806        if (IS_ERR(lcd->bd)) {
 807                dev_err(lcd->dev, "failed to register backlight ops.\n");
 808                ret = PTR_ERR(lcd->bd);
 809                goto err_backlight_register;
 810        }
 811
 812        lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
 813        lcd->bd->props.brightness = MAX_BRIGHTNESS;
 814
 815        s6e8ax0_read_id(lcd, mtp_id);
 816        if (mtp_id[0] == 0x00)
 817                dev_err(lcd->dev, "read id failed\n");
 818
 819        dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
 820                        mtp_id[0], mtp_id[1], mtp_id[2]);
 821
 822        if (mtp_id[2] == 0x33)
 823                dev_info(lcd->dev,
 824                        "ID-3 is 0xff does not support dynamic elvss\n");
 825        else
 826                dev_info(lcd->dev,
 827                        "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
 828
 829        lcd->acl_enable = 1;
 830        lcd->cur_acl = 0;
 831
 832        dev_set_drvdata(&dsim_dev->dev, lcd);
 833
 834        dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
 835
 836        return 0;
 837
 838err_backlight_register:
 839        lcd_device_unregister(lcd->ld);
 840        return ret;
 841}
 842
 843#ifdef CONFIG_PM
 844static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
 845{
 846        struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
 847
 848        s6e8ax0_sleep_in(lcd);
 849        msleep(lcd->ddi_pd->power_off_delay);
 850        s6e8ax0_display_off(lcd);
 851
 852        s6e8ax0_regulator_disable(lcd);
 853
 854        return 0;
 855}
 856
 857static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
 858{
 859        struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
 860
 861        s6e8ax0_sleep_out(lcd);
 862        msleep(lcd->ddi_pd->power_on_delay);
 863
 864        s6e8ax0_regulator_enable(lcd);
 865        s6e8ax0_set_sequence(dsim_dev);
 866
 867        return 0;
 868}
 869#else
 870#define s6e8ax0_suspend         NULL
 871#define s6e8ax0_resume          NULL
 872#endif
 873
 874static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
 875        .name = "s6e8ax0",
 876        .id = -1,
 877
 878        .power_on = s6e8ax0_power_on,
 879        .set_sequence = s6e8ax0_set_sequence,
 880        .probe = s6e8ax0_probe,
 881        .suspend = s6e8ax0_suspend,
 882        .resume = s6e8ax0_resume,
 883};
 884
 885static int s6e8ax0_init(void)
 886{
 887        exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
 888
 889        return 0;
 890}
 891
 892static void s6e8ax0_exit(void)
 893{
 894        return;
 895}
 896
 897module_init(s6e8ax0_init);
 898module_exit(s6e8ax0_exit);
 899
 900MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
 901MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
 902MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
 903MODULE_LICENSE("GPL");
 904