linux/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2017-2018, Bootlin
   4 */
   5
   6#include <linux/delay.h>
   7#include <linux/device.h>
   8#include <linux/err.h>
   9#include <linux/errno.h>
  10#include <linux/fb.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/of_device.h>
  14
  15#include <linux/gpio/consumer.h>
  16#include <linux/regulator/consumer.h>
  17
  18#include <drm/drm_mipi_dsi.h>
  19#include <drm/drm_modes.h>
  20#include <drm/drm_panel.h>
  21
  22#include <video/mipi_display.h>
  23
  24enum ili9881c_op {
  25        ILI9881C_SWITCH_PAGE,
  26        ILI9881C_COMMAND,
  27};
  28
  29struct ili9881c_instr {
  30        enum ili9881c_op        op;
  31
  32        union arg {
  33                struct cmd {
  34                        u8      cmd;
  35                        u8      data;
  36                } cmd;
  37                u8      page;
  38        } arg;
  39};
  40
  41struct ili9881c_desc {
  42        const struct ili9881c_instr *init;
  43        const size_t init_length;
  44        const struct drm_display_mode *mode;
  45};
  46
  47struct ili9881c {
  48        struct drm_panel        panel;
  49        struct mipi_dsi_device  *dsi;
  50        const struct ili9881c_desc      *desc;
  51
  52        struct regulator        *power;
  53        struct gpio_desc        *reset;
  54};
  55
  56#define ILI9881C_SWITCH_PAGE_INSTR(_page)       \
  57        {                                       \
  58                .op = ILI9881C_SWITCH_PAGE,     \
  59                .arg = {                        \
  60                        .page = (_page),        \
  61                },                              \
  62        }
  63
  64#define ILI9881C_COMMAND_INSTR(_cmd, _data)             \
  65        {                                               \
  66                .op = ILI9881C_COMMAND,         \
  67                .arg = {                                \
  68                        .cmd = {                        \
  69                                .cmd = (_cmd),          \
  70                                .data = (_data),        \
  71                        },                              \
  72                },                                      \
  73        }
  74
  75static const struct ili9881c_instr lhr050h41_init[] = {
  76        ILI9881C_SWITCH_PAGE_INSTR(3),
  77        ILI9881C_COMMAND_INSTR(0x01, 0x00),
  78        ILI9881C_COMMAND_INSTR(0x02, 0x00),
  79        ILI9881C_COMMAND_INSTR(0x03, 0x73),
  80        ILI9881C_COMMAND_INSTR(0x04, 0x03),
  81        ILI9881C_COMMAND_INSTR(0x05, 0x00),
  82        ILI9881C_COMMAND_INSTR(0x06, 0x06),
  83        ILI9881C_COMMAND_INSTR(0x07, 0x06),
  84        ILI9881C_COMMAND_INSTR(0x08, 0x00),
  85        ILI9881C_COMMAND_INSTR(0x09, 0x18),
  86        ILI9881C_COMMAND_INSTR(0x0a, 0x04),
  87        ILI9881C_COMMAND_INSTR(0x0b, 0x00),
  88        ILI9881C_COMMAND_INSTR(0x0c, 0x02),
  89        ILI9881C_COMMAND_INSTR(0x0d, 0x03),
  90        ILI9881C_COMMAND_INSTR(0x0e, 0x00),
  91        ILI9881C_COMMAND_INSTR(0x0f, 0x25),
  92        ILI9881C_COMMAND_INSTR(0x10, 0x25),
  93        ILI9881C_COMMAND_INSTR(0x11, 0x00),
  94        ILI9881C_COMMAND_INSTR(0x12, 0x00),
  95        ILI9881C_COMMAND_INSTR(0x13, 0x00),
  96        ILI9881C_COMMAND_INSTR(0x14, 0x00),
  97        ILI9881C_COMMAND_INSTR(0x15, 0x00),
  98        ILI9881C_COMMAND_INSTR(0x16, 0x0C),
  99        ILI9881C_COMMAND_INSTR(0x17, 0x00),
 100        ILI9881C_COMMAND_INSTR(0x18, 0x00),
 101        ILI9881C_COMMAND_INSTR(0x19, 0x00),
 102        ILI9881C_COMMAND_INSTR(0x1a, 0x00),
 103        ILI9881C_COMMAND_INSTR(0x1b, 0x00),
 104        ILI9881C_COMMAND_INSTR(0x1c, 0x00),
 105        ILI9881C_COMMAND_INSTR(0x1d, 0x00),
 106        ILI9881C_COMMAND_INSTR(0x1e, 0xC0),
 107        ILI9881C_COMMAND_INSTR(0x1f, 0x80),
 108        ILI9881C_COMMAND_INSTR(0x20, 0x04),
 109        ILI9881C_COMMAND_INSTR(0x21, 0x01),
 110        ILI9881C_COMMAND_INSTR(0x22, 0x00),
 111        ILI9881C_COMMAND_INSTR(0x23, 0x00),
 112        ILI9881C_COMMAND_INSTR(0x24, 0x00),
 113        ILI9881C_COMMAND_INSTR(0x25, 0x00),
 114        ILI9881C_COMMAND_INSTR(0x26, 0x00),
 115        ILI9881C_COMMAND_INSTR(0x27, 0x00),
 116        ILI9881C_COMMAND_INSTR(0x28, 0x33),
 117        ILI9881C_COMMAND_INSTR(0x29, 0x03),
 118        ILI9881C_COMMAND_INSTR(0x2a, 0x00),
 119        ILI9881C_COMMAND_INSTR(0x2b, 0x00),
 120        ILI9881C_COMMAND_INSTR(0x2c, 0x00),
 121        ILI9881C_COMMAND_INSTR(0x2d, 0x00),
 122        ILI9881C_COMMAND_INSTR(0x2e, 0x00),
 123        ILI9881C_COMMAND_INSTR(0x2f, 0x00),
 124        ILI9881C_COMMAND_INSTR(0x30, 0x00),
 125        ILI9881C_COMMAND_INSTR(0x31, 0x00),
 126        ILI9881C_COMMAND_INSTR(0x32, 0x00),
 127        ILI9881C_COMMAND_INSTR(0x33, 0x00),
 128        ILI9881C_COMMAND_INSTR(0x34, 0x04),
 129        ILI9881C_COMMAND_INSTR(0x35, 0x00),
 130        ILI9881C_COMMAND_INSTR(0x36, 0x00),
 131        ILI9881C_COMMAND_INSTR(0x37, 0x00),
 132        ILI9881C_COMMAND_INSTR(0x38, 0x3C),
 133        ILI9881C_COMMAND_INSTR(0x39, 0x00),
 134        ILI9881C_COMMAND_INSTR(0x3a, 0x00),
 135        ILI9881C_COMMAND_INSTR(0x3b, 0x00),
 136        ILI9881C_COMMAND_INSTR(0x3c, 0x00),
 137        ILI9881C_COMMAND_INSTR(0x3d, 0x00),
 138        ILI9881C_COMMAND_INSTR(0x3e, 0x00),
 139        ILI9881C_COMMAND_INSTR(0x3f, 0x00),
 140        ILI9881C_COMMAND_INSTR(0x40, 0x00),
 141        ILI9881C_COMMAND_INSTR(0x41, 0x00),
 142        ILI9881C_COMMAND_INSTR(0x42, 0x00),
 143        ILI9881C_COMMAND_INSTR(0x43, 0x00),
 144        ILI9881C_COMMAND_INSTR(0x44, 0x00),
 145        ILI9881C_COMMAND_INSTR(0x50, 0x01),
 146        ILI9881C_COMMAND_INSTR(0x51, 0x23),
 147        ILI9881C_COMMAND_INSTR(0x52, 0x45),
 148        ILI9881C_COMMAND_INSTR(0x53, 0x67),
 149        ILI9881C_COMMAND_INSTR(0x54, 0x89),
 150        ILI9881C_COMMAND_INSTR(0x55, 0xab),
 151        ILI9881C_COMMAND_INSTR(0x56, 0x01),
 152        ILI9881C_COMMAND_INSTR(0x57, 0x23),
 153        ILI9881C_COMMAND_INSTR(0x58, 0x45),
 154        ILI9881C_COMMAND_INSTR(0x59, 0x67),
 155        ILI9881C_COMMAND_INSTR(0x5a, 0x89),
 156        ILI9881C_COMMAND_INSTR(0x5b, 0xab),
 157        ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
 158        ILI9881C_COMMAND_INSTR(0x5d, 0xef),
 159        ILI9881C_COMMAND_INSTR(0x5e, 0x11),
 160        ILI9881C_COMMAND_INSTR(0x5f, 0x02),
 161        ILI9881C_COMMAND_INSTR(0x60, 0x02),
 162        ILI9881C_COMMAND_INSTR(0x61, 0x02),
 163        ILI9881C_COMMAND_INSTR(0x62, 0x02),
 164        ILI9881C_COMMAND_INSTR(0x63, 0x02),
 165        ILI9881C_COMMAND_INSTR(0x64, 0x02),
 166        ILI9881C_COMMAND_INSTR(0x65, 0x02),
 167        ILI9881C_COMMAND_INSTR(0x66, 0x02),
 168        ILI9881C_COMMAND_INSTR(0x67, 0x02),
 169        ILI9881C_COMMAND_INSTR(0x68, 0x02),
 170        ILI9881C_COMMAND_INSTR(0x69, 0x02),
 171        ILI9881C_COMMAND_INSTR(0x6a, 0x0C),
 172        ILI9881C_COMMAND_INSTR(0x6b, 0x02),
 173        ILI9881C_COMMAND_INSTR(0x6c, 0x0F),
 174        ILI9881C_COMMAND_INSTR(0x6d, 0x0E),
 175        ILI9881C_COMMAND_INSTR(0x6e, 0x0D),
 176        ILI9881C_COMMAND_INSTR(0x6f, 0x06),
 177        ILI9881C_COMMAND_INSTR(0x70, 0x07),
 178        ILI9881C_COMMAND_INSTR(0x71, 0x02),
 179        ILI9881C_COMMAND_INSTR(0x72, 0x02),
 180        ILI9881C_COMMAND_INSTR(0x73, 0x02),
 181        ILI9881C_COMMAND_INSTR(0x74, 0x02),
 182        ILI9881C_COMMAND_INSTR(0x75, 0x02),
 183        ILI9881C_COMMAND_INSTR(0x76, 0x02),
 184        ILI9881C_COMMAND_INSTR(0x77, 0x02),
 185        ILI9881C_COMMAND_INSTR(0x78, 0x02),
 186        ILI9881C_COMMAND_INSTR(0x79, 0x02),
 187        ILI9881C_COMMAND_INSTR(0x7a, 0x02),
 188        ILI9881C_COMMAND_INSTR(0x7b, 0x02),
 189        ILI9881C_COMMAND_INSTR(0x7c, 0x02),
 190        ILI9881C_COMMAND_INSTR(0x7d, 0x02),
 191        ILI9881C_COMMAND_INSTR(0x7e, 0x02),
 192        ILI9881C_COMMAND_INSTR(0x7f, 0x02),
 193        ILI9881C_COMMAND_INSTR(0x80, 0x0C),
 194        ILI9881C_COMMAND_INSTR(0x81, 0x02),
 195        ILI9881C_COMMAND_INSTR(0x82, 0x0F),
 196        ILI9881C_COMMAND_INSTR(0x83, 0x0E),
 197        ILI9881C_COMMAND_INSTR(0x84, 0x0D),
 198        ILI9881C_COMMAND_INSTR(0x85, 0x06),
 199        ILI9881C_COMMAND_INSTR(0x86, 0x07),
 200        ILI9881C_COMMAND_INSTR(0x87, 0x02),
 201        ILI9881C_COMMAND_INSTR(0x88, 0x02),
 202        ILI9881C_COMMAND_INSTR(0x89, 0x02),
 203        ILI9881C_COMMAND_INSTR(0x8A, 0x02),
 204        ILI9881C_SWITCH_PAGE_INSTR(4),
 205        ILI9881C_COMMAND_INSTR(0x6C, 0x15),
 206        ILI9881C_COMMAND_INSTR(0x6E, 0x22),
 207        ILI9881C_COMMAND_INSTR(0x6F, 0x33),
 208        ILI9881C_COMMAND_INSTR(0x3A, 0xA4),
 209        ILI9881C_COMMAND_INSTR(0x8D, 0x0D),
 210        ILI9881C_COMMAND_INSTR(0x87, 0xBA),
 211        ILI9881C_COMMAND_INSTR(0x26, 0x76),
 212        ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
 213        ILI9881C_SWITCH_PAGE_INSTR(1),
 214        ILI9881C_COMMAND_INSTR(0x22, 0x0A),
 215        ILI9881C_COMMAND_INSTR(0x53, 0xDC),
 216        ILI9881C_COMMAND_INSTR(0x55, 0xA7),
 217        ILI9881C_COMMAND_INSTR(0x50, 0x78),
 218        ILI9881C_COMMAND_INSTR(0x51, 0x78),
 219        ILI9881C_COMMAND_INSTR(0x31, 0x02),
 220        ILI9881C_COMMAND_INSTR(0x60, 0x14),
 221        ILI9881C_COMMAND_INSTR(0xA0, 0x2A),
 222        ILI9881C_COMMAND_INSTR(0xA1, 0x39),
 223        ILI9881C_COMMAND_INSTR(0xA2, 0x46),
 224        ILI9881C_COMMAND_INSTR(0xA3, 0x0e),
 225        ILI9881C_COMMAND_INSTR(0xA4, 0x12),
 226        ILI9881C_COMMAND_INSTR(0xA5, 0x25),
 227        ILI9881C_COMMAND_INSTR(0xA6, 0x19),
 228        ILI9881C_COMMAND_INSTR(0xA7, 0x1d),
 229        ILI9881C_COMMAND_INSTR(0xA8, 0xa6),
 230        ILI9881C_COMMAND_INSTR(0xA9, 0x1C),
 231        ILI9881C_COMMAND_INSTR(0xAA, 0x29),
 232        ILI9881C_COMMAND_INSTR(0xAB, 0x85),
 233        ILI9881C_COMMAND_INSTR(0xAC, 0x1C),
 234        ILI9881C_COMMAND_INSTR(0xAD, 0x1B),
 235        ILI9881C_COMMAND_INSTR(0xAE, 0x51),
 236        ILI9881C_COMMAND_INSTR(0xAF, 0x22),
 237        ILI9881C_COMMAND_INSTR(0xB0, 0x2d),
 238        ILI9881C_COMMAND_INSTR(0xB1, 0x4f),
 239        ILI9881C_COMMAND_INSTR(0xB2, 0x59),
 240        ILI9881C_COMMAND_INSTR(0xB3, 0x3F),
 241        ILI9881C_COMMAND_INSTR(0xC0, 0x2A),
 242        ILI9881C_COMMAND_INSTR(0xC1, 0x3a),
 243        ILI9881C_COMMAND_INSTR(0xC2, 0x45),
 244        ILI9881C_COMMAND_INSTR(0xC3, 0x0e),
 245        ILI9881C_COMMAND_INSTR(0xC4, 0x11),
 246        ILI9881C_COMMAND_INSTR(0xC5, 0x24),
 247        ILI9881C_COMMAND_INSTR(0xC6, 0x1a),
 248        ILI9881C_COMMAND_INSTR(0xC7, 0x1c),
 249        ILI9881C_COMMAND_INSTR(0xC8, 0xaa),
 250        ILI9881C_COMMAND_INSTR(0xC9, 0x1C),
 251        ILI9881C_COMMAND_INSTR(0xCA, 0x29),
 252        ILI9881C_COMMAND_INSTR(0xCB, 0x96),
 253        ILI9881C_COMMAND_INSTR(0xCC, 0x1C),
 254        ILI9881C_COMMAND_INSTR(0xCD, 0x1B),
 255        ILI9881C_COMMAND_INSTR(0xCE, 0x51),
 256        ILI9881C_COMMAND_INSTR(0xCF, 0x22),
 257        ILI9881C_COMMAND_INSTR(0xD0, 0x2b),
 258        ILI9881C_COMMAND_INSTR(0xD1, 0x4b),
 259        ILI9881C_COMMAND_INSTR(0xD2, 0x59),
 260        ILI9881C_COMMAND_INSTR(0xD3, 0x3F),
 261};
 262
 263static const struct ili9881c_instr k101_im2byl02_init[] = {
 264        ILI9881C_SWITCH_PAGE_INSTR(3),
 265        ILI9881C_COMMAND_INSTR(0x01, 0x00),
 266        ILI9881C_COMMAND_INSTR(0x02, 0x00),
 267        ILI9881C_COMMAND_INSTR(0x03, 0x73),
 268        ILI9881C_COMMAND_INSTR(0x04, 0x00),
 269        ILI9881C_COMMAND_INSTR(0x05, 0x00),
 270        ILI9881C_COMMAND_INSTR(0x06, 0x08),
 271        ILI9881C_COMMAND_INSTR(0x07, 0x00),
 272        ILI9881C_COMMAND_INSTR(0x08, 0x00),
 273        ILI9881C_COMMAND_INSTR(0x09, 0x00),
 274        ILI9881C_COMMAND_INSTR(0x0A, 0x01),
 275        ILI9881C_COMMAND_INSTR(0x0B, 0x01),
 276        ILI9881C_COMMAND_INSTR(0x0C, 0x00),
 277        ILI9881C_COMMAND_INSTR(0x0D, 0x01),
 278        ILI9881C_COMMAND_INSTR(0x0E, 0x01),
 279        ILI9881C_COMMAND_INSTR(0x0F, 0x00),
 280        ILI9881C_COMMAND_INSTR(0x10, 0x00),
 281        ILI9881C_COMMAND_INSTR(0x11, 0x00),
 282        ILI9881C_COMMAND_INSTR(0x12, 0x00),
 283        ILI9881C_COMMAND_INSTR(0x13, 0x00),
 284        ILI9881C_COMMAND_INSTR(0x14, 0x00),
 285        ILI9881C_COMMAND_INSTR(0x15, 0x00),
 286        ILI9881C_COMMAND_INSTR(0x16, 0x00),
 287        ILI9881C_COMMAND_INSTR(0x17, 0x00),
 288        ILI9881C_COMMAND_INSTR(0x18, 0x00),
 289        ILI9881C_COMMAND_INSTR(0x19, 0x00),
 290        ILI9881C_COMMAND_INSTR(0x1A, 0x00),
 291        ILI9881C_COMMAND_INSTR(0x1B, 0x00),
 292        ILI9881C_COMMAND_INSTR(0x1C, 0x00),
 293        ILI9881C_COMMAND_INSTR(0x1D, 0x00),
 294        ILI9881C_COMMAND_INSTR(0x1E, 0x40),
 295        ILI9881C_COMMAND_INSTR(0x1F, 0xC0),
 296        ILI9881C_COMMAND_INSTR(0x20, 0x06),
 297        ILI9881C_COMMAND_INSTR(0x21, 0x01),
 298        ILI9881C_COMMAND_INSTR(0x22, 0x06),
 299        ILI9881C_COMMAND_INSTR(0x23, 0x01),
 300        ILI9881C_COMMAND_INSTR(0x24, 0x88),
 301        ILI9881C_COMMAND_INSTR(0x25, 0x88),
 302        ILI9881C_COMMAND_INSTR(0x26, 0x00),
 303        ILI9881C_COMMAND_INSTR(0x27, 0x00),
 304        ILI9881C_COMMAND_INSTR(0x28, 0x3B),
 305        ILI9881C_COMMAND_INSTR(0x29, 0x03),
 306        ILI9881C_COMMAND_INSTR(0x2A, 0x00),
 307        ILI9881C_COMMAND_INSTR(0x2B, 0x00),
 308        ILI9881C_COMMAND_INSTR(0x2C, 0x00),
 309        ILI9881C_COMMAND_INSTR(0x2D, 0x00),
 310        ILI9881C_COMMAND_INSTR(0x2E, 0x00),
 311        ILI9881C_COMMAND_INSTR(0x2F, 0x00),
 312        ILI9881C_COMMAND_INSTR(0x30, 0x00),
 313        ILI9881C_COMMAND_INSTR(0x31, 0x00),
 314        ILI9881C_COMMAND_INSTR(0x32, 0x00),
 315        ILI9881C_COMMAND_INSTR(0x33, 0x00),
 316        ILI9881C_COMMAND_INSTR(0x34, 0x00), /* GPWR1/2 non overlap time 2.62us */
 317        ILI9881C_COMMAND_INSTR(0x35, 0x00),
 318        ILI9881C_COMMAND_INSTR(0x36, 0x00),
 319        ILI9881C_COMMAND_INSTR(0x37, 0x00),
 320        ILI9881C_COMMAND_INSTR(0x38, 0x00),
 321        ILI9881C_COMMAND_INSTR(0x39, 0x00),
 322        ILI9881C_COMMAND_INSTR(0x3A, 0x00),
 323        ILI9881C_COMMAND_INSTR(0x3B, 0x00),
 324        ILI9881C_COMMAND_INSTR(0x3C, 0x00),
 325        ILI9881C_COMMAND_INSTR(0x3D, 0x00),
 326        ILI9881C_COMMAND_INSTR(0x3E, 0x00),
 327        ILI9881C_COMMAND_INSTR(0x3F, 0x00),
 328        ILI9881C_COMMAND_INSTR(0x40, 0x00),
 329        ILI9881C_COMMAND_INSTR(0x41, 0x00),
 330        ILI9881C_COMMAND_INSTR(0x42, 0x00),
 331        ILI9881C_COMMAND_INSTR(0x43, 0x00),
 332        ILI9881C_COMMAND_INSTR(0x44, 0x00),
 333        ILI9881C_COMMAND_INSTR(0x50, 0x01),
 334        ILI9881C_COMMAND_INSTR(0x51, 0x23),
 335        ILI9881C_COMMAND_INSTR(0x52, 0x45),
 336        ILI9881C_COMMAND_INSTR(0x53, 0x67),
 337        ILI9881C_COMMAND_INSTR(0x54, 0x89),
 338        ILI9881C_COMMAND_INSTR(0x55, 0xAB),
 339        ILI9881C_COMMAND_INSTR(0x56, 0x01),
 340        ILI9881C_COMMAND_INSTR(0x57, 0x23),
 341        ILI9881C_COMMAND_INSTR(0x58, 0x45),
 342        ILI9881C_COMMAND_INSTR(0x59, 0x67),
 343        ILI9881C_COMMAND_INSTR(0x5A, 0x89),
 344        ILI9881C_COMMAND_INSTR(0x5B, 0xAB),
 345        ILI9881C_COMMAND_INSTR(0x5C, 0xCD),
 346        ILI9881C_COMMAND_INSTR(0x5D, 0xEF),
 347        ILI9881C_COMMAND_INSTR(0x5E, 0x00),
 348        ILI9881C_COMMAND_INSTR(0x5F, 0x01),
 349        ILI9881C_COMMAND_INSTR(0x60, 0x01),
 350        ILI9881C_COMMAND_INSTR(0x61, 0x06),
 351        ILI9881C_COMMAND_INSTR(0x62, 0x06),
 352        ILI9881C_COMMAND_INSTR(0x63, 0x07),
 353        ILI9881C_COMMAND_INSTR(0x64, 0x07),
 354        ILI9881C_COMMAND_INSTR(0x65, 0x00),
 355        ILI9881C_COMMAND_INSTR(0x66, 0x00),
 356        ILI9881C_COMMAND_INSTR(0x67, 0x02),
 357        ILI9881C_COMMAND_INSTR(0x68, 0x02),
 358        ILI9881C_COMMAND_INSTR(0x69, 0x05),
 359        ILI9881C_COMMAND_INSTR(0x6A, 0x05),
 360        ILI9881C_COMMAND_INSTR(0x6B, 0x02),
 361        ILI9881C_COMMAND_INSTR(0x6C, 0x0D),
 362        ILI9881C_COMMAND_INSTR(0x6D, 0x0D),
 363        ILI9881C_COMMAND_INSTR(0x6E, 0x0C),
 364        ILI9881C_COMMAND_INSTR(0x6F, 0x0C),
 365        ILI9881C_COMMAND_INSTR(0x70, 0x0F),
 366        ILI9881C_COMMAND_INSTR(0x71, 0x0F),
 367        ILI9881C_COMMAND_INSTR(0x72, 0x0E),
 368        ILI9881C_COMMAND_INSTR(0x73, 0x0E),
 369        ILI9881C_COMMAND_INSTR(0x74, 0x02),
 370        ILI9881C_COMMAND_INSTR(0x75, 0x01),
 371        ILI9881C_COMMAND_INSTR(0x76, 0x01),
 372        ILI9881C_COMMAND_INSTR(0x77, 0x06),
 373        ILI9881C_COMMAND_INSTR(0x78, 0x06),
 374        ILI9881C_COMMAND_INSTR(0x79, 0x07),
 375        ILI9881C_COMMAND_INSTR(0x7A, 0x07),
 376        ILI9881C_COMMAND_INSTR(0x7B, 0x00),
 377        ILI9881C_COMMAND_INSTR(0x7C, 0x00),
 378        ILI9881C_COMMAND_INSTR(0x7D, 0x02),
 379        ILI9881C_COMMAND_INSTR(0x7E, 0x02),
 380        ILI9881C_COMMAND_INSTR(0x7F, 0x05),
 381        ILI9881C_COMMAND_INSTR(0x80, 0x05),
 382        ILI9881C_COMMAND_INSTR(0x81, 0x02),
 383        ILI9881C_COMMAND_INSTR(0x82, 0x0D),
 384        ILI9881C_COMMAND_INSTR(0x83, 0x0D),
 385        ILI9881C_COMMAND_INSTR(0x84, 0x0C),
 386        ILI9881C_COMMAND_INSTR(0x85, 0x0C),
 387        ILI9881C_COMMAND_INSTR(0x86, 0x0F),
 388        ILI9881C_COMMAND_INSTR(0x87, 0x0F),
 389        ILI9881C_COMMAND_INSTR(0x88, 0x0E),
 390        ILI9881C_COMMAND_INSTR(0x89, 0x0E),
 391        ILI9881C_COMMAND_INSTR(0x8A, 0x02),
 392        ILI9881C_SWITCH_PAGE_INSTR(4),
 393        ILI9881C_COMMAND_INSTR(0x3B, 0xC0), /* ILI4003D sel */
 394        ILI9881C_COMMAND_INSTR(0x6C, 0x15), /* Set VCORE voltage = 1.5V */
 395        ILI9881C_COMMAND_INSTR(0x6E, 0x2A), /* di_pwr_reg=0 for power mode 2A, VGH clamp 18V */
 396        ILI9881C_COMMAND_INSTR(0x6F, 0x33), /* pumping ratio VGH=5x VGL=-3x */
 397        ILI9881C_COMMAND_INSTR(0x8D, 0x1B), /* VGL clamp -10V */
 398        ILI9881C_COMMAND_INSTR(0x87, 0xBA), /* ESD */
 399        ILI9881C_COMMAND_INSTR(0x3A, 0x24), /* POWER SAVING */
 400        ILI9881C_COMMAND_INSTR(0x26, 0x76),
 401        ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
 402        ILI9881C_SWITCH_PAGE_INSTR(1),
 403        ILI9881C_COMMAND_INSTR(0x22, 0x0A), /* BGR, SS */
 404        ILI9881C_COMMAND_INSTR(0x31, 0x00), /* Zigzag type3 inversion */
 405        ILI9881C_COMMAND_INSTR(0x40, 0x53), /* ILI4003D sel */
 406        ILI9881C_COMMAND_INSTR(0x43, 0x66),
 407        ILI9881C_COMMAND_INSTR(0x53, 0x4C),
 408        ILI9881C_COMMAND_INSTR(0x50, 0x87),
 409        ILI9881C_COMMAND_INSTR(0x51, 0x82),
 410        ILI9881C_COMMAND_INSTR(0x60, 0x15),
 411        ILI9881C_COMMAND_INSTR(0x61, 0x01),
 412        ILI9881C_COMMAND_INSTR(0x62, 0x0C),
 413        ILI9881C_COMMAND_INSTR(0x63, 0x00),
 414        ILI9881C_COMMAND_INSTR(0xA0, 0x00),
 415        ILI9881C_COMMAND_INSTR(0xA1, 0x13), /* VP251 */
 416        ILI9881C_COMMAND_INSTR(0xA2, 0x23), /* VP247 */
 417        ILI9881C_COMMAND_INSTR(0xA3, 0x14), /* VP243 */
 418        ILI9881C_COMMAND_INSTR(0xA4, 0x16), /* VP239 */
 419        ILI9881C_COMMAND_INSTR(0xA5, 0x29), /* VP231 */
 420        ILI9881C_COMMAND_INSTR(0xA6, 0x1E), /* VP219 */
 421        ILI9881C_COMMAND_INSTR(0xA7, 0x1D), /* VP203 */
 422        ILI9881C_COMMAND_INSTR(0xA8, 0x86), /* VP175 */
 423        ILI9881C_COMMAND_INSTR(0xA9, 0x1E), /* VP144 */
 424        ILI9881C_COMMAND_INSTR(0xAA, 0x29), /* VP111 */
 425        ILI9881C_COMMAND_INSTR(0xAB, 0x74), /* VP80 */
 426        ILI9881C_COMMAND_INSTR(0xAC, 0x19), /* VP52 */
 427        ILI9881C_COMMAND_INSTR(0xAD, 0x17), /* VP36 */
 428        ILI9881C_COMMAND_INSTR(0xAE, 0x4B), /* VP24 */
 429        ILI9881C_COMMAND_INSTR(0xAF, 0x20), /* VP16 */
 430        ILI9881C_COMMAND_INSTR(0xB0, 0x26), /* VP12 */
 431        ILI9881C_COMMAND_INSTR(0xB1, 0x4C), /* VP8 */
 432        ILI9881C_COMMAND_INSTR(0xB2, 0x5D), /* VP4 */
 433        ILI9881C_COMMAND_INSTR(0xB3, 0x3F), /* VP0 */
 434        ILI9881C_COMMAND_INSTR(0xC0, 0x00), /* VN255 GAMMA N */
 435        ILI9881C_COMMAND_INSTR(0xC1, 0x13), /* VN251 */
 436        ILI9881C_COMMAND_INSTR(0xC2, 0x23), /* VN247 */
 437        ILI9881C_COMMAND_INSTR(0xC3, 0x14), /* VN243 */
 438        ILI9881C_COMMAND_INSTR(0xC4, 0x16), /* VN239 */
 439        ILI9881C_COMMAND_INSTR(0xC5, 0x29), /* VN231 */
 440        ILI9881C_COMMAND_INSTR(0xC6, 0x1E), /* VN219 */
 441        ILI9881C_COMMAND_INSTR(0xC7, 0x1D), /* VN203 */
 442        ILI9881C_COMMAND_INSTR(0xC8, 0x86), /* VN175 */
 443        ILI9881C_COMMAND_INSTR(0xC9, 0x1E), /* VN144 */
 444        ILI9881C_COMMAND_INSTR(0xCA, 0x29), /* VN111 */
 445        ILI9881C_COMMAND_INSTR(0xCB, 0x74), /* VN80 */
 446        ILI9881C_COMMAND_INSTR(0xCC, 0x19), /* VN52 */
 447        ILI9881C_COMMAND_INSTR(0xCD, 0x17), /* VN36 */
 448        ILI9881C_COMMAND_INSTR(0xCE, 0x4B), /* VN24 */
 449        ILI9881C_COMMAND_INSTR(0xCF, 0x20), /* VN16 */
 450        ILI9881C_COMMAND_INSTR(0xD0, 0x26), /* VN12 */
 451        ILI9881C_COMMAND_INSTR(0xD1, 0x4C), /* VN8 */
 452        ILI9881C_COMMAND_INSTR(0xD2, 0x5D), /* VN4 */
 453        ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */
 454};
 455
 456static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel)
 457{
 458        return container_of(panel, struct ili9881c, panel);
 459}
 460
 461/*
 462 * The panel seems to accept some private DCS commands that map
 463 * directly to registers.
 464 *
 465 * It is organised by page, with each page having its own set of
 466 * registers, and the first page looks like it's holding the standard
 467 * DCS commands.
 468 *
 469 * So before any attempt at sending a command or data, we have to be
 470 * sure if we're in the right page or not.
 471 */
 472static int ili9881c_switch_page(struct ili9881c *ctx, u8 page)
 473{
 474        u8 buf[4] = { 0xff, 0x98, 0x81, page };
 475        int ret;
 476
 477        ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
 478        if (ret < 0)
 479                return ret;
 480
 481        return 0;
 482}
 483
 484static int ili9881c_send_cmd_data(struct ili9881c *ctx, u8 cmd, u8 data)
 485{
 486        u8 buf[2] = { cmd, data };
 487        int ret;
 488
 489        ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
 490        if (ret < 0)
 491                return ret;
 492
 493        return 0;
 494}
 495
 496static int ili9881c_prepare(struct drm_panel *panel)
 497{
 498        struct ili9881c *ctx = panel_to_ili9881c(panel);
 499        unsigned int i;
 500        int ret;
 501
 502        /* Power the panel */
 503        ret = regulator_enable(ctx->power);
 504        if (ret)
 505                return ret;
 506        msleep(5);
 507
 508        /* And reset it */
 509        gpiod_set_value(ctx->reset, 1);
 510        msleep(20);
 511
 512        gpiod_set_value(ctx->reset, 0);
 513        msleep(20);
 514
 515        for (i = 0; i < ctx->desc->init_length; i++) {
 516                const struct ili9881c_instr *instr = &ctx->desc->init[i];
 517
 518                if (instr->op == ILI9881C_SWITCH_PAGE)
 519                        ret = ili9881c_switch_page(ctx, instr->arg.page);
 520                else if (instr->op == ILI9881C_COMMAND)
 521                        ret = ili9881c_send_cmd_data(ctx, instr->arg.cmd.cmd,
 522                                                      instr->arg.cmd.data);
 523
 524                if (ret)
 525                        return ret;
 526        }
 527
 528        ret = ili9881c_switch_page(ctx, 0);
 529        if (ret)
 530                return ret;
 531
 532        ret = mipi_dsi_dcs_set_tear_on(ctx->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
 533        if (ret)
 534                return ret;
 535
 536        ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
 537        if (ret)
 538                return ret;
 539
 540        return 0;
 541}
 542
 543static int ili9881c_enable(struct drm_panel *panel)
 544{
 545        struct ili9881c *ctx = panel_to_ili9881c(panel);
 546
 547        msleep(120);
 548
 549        mipi_dsi_dcs_set_display_on(ctx->dsi);
 550
 551        return 0;
 552}
 553
 554static int ili9881c_disable(struct drm_panel *panel)
 555{
 556        struct ili9881c *ctx = panel_to_ili9881c(panel);
 557
 558        return mipi_dsi_dcs_set_display_off(ctx->dsi);
 559}
 560
 561static int ili9881c_unprepare(struct drm_panel *panel)
 562{
 563        struct ili9881c *ctx = panel_to_ili9881c(panel);
 564
 565        mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
 566        regulator_disable(ctx->power);
 567        gpiod_set_value(ctx->reset, 1);
 568
 569        return 0;
 570}
 571
 572static const struct drm_display_mode lhr050h41_default_mode = {
 573        .clock          = 62000,
 574
 575        .hdisplay       = 720,
 576        .hsync_start    = 720 + 10,
 577        .hsync_end      = 720 + 10 + 20,
 578        .htotal         = 720 + 10 + 20 + 30,
 579
 580        .vdisplay       = 1280,
 581        .vsync_start    = 1280 + 10,
 582        .vsync_end      = 1280 + 10 + 10,
 583        .vtotal         = 1280 + 10 + 10 + 20,
 584
 585        .width_mm       = 62,
 586        .height_mm      = 110,
 587};
 588
 589static const struct drm_display_mode k101_im2byl02_default_mode = {
 590        .clock          = 69700,
 591
 592        .hdisplay       = 800,
 593        .hsync_start    = 800 + 52,
 594        .hsync_end      = 800 + 52 + 8,
 595        .htotal         = 800 + 52 + 8 + 48,
 596
 597        .vdisplay       = 1280,
 598        .vsync_start    = 1280 + 16,
 599        .vsync_end      = 1280 + 16 + 6,
 600        .vtotal         = 1280 + 16 + 6 + 15,
 601
 602        .width_mm       = 135,
 603        .height_mm      = 217,
 604};
 605
 606static int ili9881c_get_modes(struct drm_panel *panel,
 607                              struct drm_connector *connector)
 608{
 609        struct ili9881c *ctx = panel_to_ili9881c(panel);
 610        struct drm_display_mode *mode;
 611
 612        mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
 613        if (!mode) {
 614                dev_err(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
 615                        ctx->desc->mode->hdisplay,
 616                        ctx->desc->mode->vdisplay,
 617                        drm_mode_vrefresh(ctx->desc->mode));
 618                return -ENOMEM;
 619        }
 620
 621        drm_mode_set_name(mode);
 622
 623        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 624        drm_mode_probed_add(connector, mode);
 625
 626        connector->display_info.width_mm = mode->width_mm;
 627        connector->display_info.height_mm = mode->height_mm;
 628
 629        return 1;
 630}
 631
 632static const struct drm_panel_funcs ili9881c_funcs = {
 633        .prepare        = ili9881c_prepare,
 634        .unprepare      = ili9881c_unprepare,
 635        .enable         = ili9881c_enable,
 636        .disable        = ili9881c_disable,
 637        .get_modes      = ili9881c_get_modes,
 638};
 639
 640static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
 641{
 642        struct ili9881c *ctx;
 643        int ret;
 644
 645        ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
 646        if (!ctx)
 647                return -ENOMEM;
 648        mipi_dsi_set_drvdata(dsi, ctx);
 649        ctx->dsi = dsi;
 650        ctx->desc = of_device_get_match_data(&dsi->dev);
 651
 652        drm_panel_init(&ctx->panel, &dsi->dev, &ili9881c_funcs,
 653                       DRM_MODE_CONNECTOR_DSI);
 654
 655        ctx->power = devm_regulator_get(&dsi->dev, "power");
 656        if (IS_ERR(ctx->power)) {
 657                dev_err(&dsi->dev, "Couldn't get our power regulator\n");
 658                return PTR_ERR(ctx->power);
 659        }
 660
 661        ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
 662        if (IS_ERR(ctx->reset)) {
 663                dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
 664                return PTR_ERR(ctx->reset);
 665        }
 666
 667        ret = drm_panel_of_backlight(&ctx->panel);
 668        if (ret)
 669                return ret;
 670
 671        drm_panel_add(&ctx->panel);
 672
 673        dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
 674        dsi->format = MIPI_DSI_FMT_RGB888;
 675        dsi->lanes = 4;
 676
 677        return mipi_dsi_attach(dsi);
 678}
 679
 680static int ili9881c_dsi_remove(struct mipi_dsi_device *dsi)
 681{
 682        struct ili9881c *ctx = mipi_dsi_get_drvdata(dsi);
 683
 684        mipi_dsi_detach(dsi);
 685        drm_panel_remove(&ctx->panel);
 686
 687        return 0;
 688}
 689
 690static const struct ili9881c_desc lhr050h41_desc = {
 691        .init = lhr050h41_init,
 692        .init_length = ARRAY_SIZE(lhr050h41_init),
 693        .mode = &lhr050h41_default_mode,
 694};
 695
 696static const struct ili9881c_desc k101_im2byl02_desc = {
 697        .init = k101_im2byl02_init,
 698        .init_length = ARRAY_SIZE(k101_im2byl02_init),
 699        .mode = &k101_im2byl02_default_mode,
 700};
 701
 702static const struct of_device_id ili9881c_of_match[] = {
 703        { .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc },
 704        { .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc },
 705        { }
 706};
 707MODULE_DEVICE_TABLE(of, ili9881c_of_match);
 708
 709static struct mipi_dsi_driver ili9881c_dsi_driver = {
 710        .probe          = ili9881c_dsi_probe,
 711        .remove         = ili9881c_dsi_remove,
 712        .driver = {
 713                .name           = "ili9881c-dsi",
 714                .of_match_table = ili9881c_of_match,
 715        },
 716};
 717module_mipi_dsi_driver(ili9881c_dsi_driver);
 718
 719MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 720MODULE_DESCRIPTION("Ilitek ILI9881C Controller Driver");
 721MODULE_LICENSE("GPL v2");
 722