root/drivers/video/backlight/tdo24m.c

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

DEFINITIONS

This source file includes following definitions.
  1. tdo24m_writes
  2. tdo24m_adj_mode
  3. tdo35s_adj_mode
  4. tdo24m_power_on
  5. tdo24m_power_off
  6. tdo24m_power
  7. tdo24m_set_power
  8. tdo24m_get_power
  9. tdo24m_set_mode
  10. tdo24m_probe
  11. tdo24m_remove
  12. tdo24m_suspend
  13. tdo24m_resume
  14. tdo24m_shutdown

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * tdo24m - SPI-based drivers for Toppoly TDO24M series LCD panels
   4  *
   5  * Copyright (C) 2008 Marvell International Ltd.
   6  *      Eric Miao <eric.miao@marvell.com>
   7  */
   8 
   9 #include <linux/module.h>
  10 #include <linux/kernel.h>
  11 #include <linux/init.h>
  12 #include <linux/device.h>
  13 #include <linux/spi/spi.h>
  14 #include <linux/spi/tdo24m.h>
  15 #include <linux/fb.h>
  16 #include <linux/lcd.h>
  17 #include <linux/slab.h>
  18 
  19 #define POWER_IS_ON(pwr)        ((pwr) <= FB_BLANK_NORMAL)
  20 
  21 #define TDO24M_SPI_BUFF_SIZE    (4)
  22 #define MODE_QVGA       0
  23 #define MODE_VGA        1
  24 
  25 struct tdo24m {
  26         struct spi_device       *spi_dev;
  27         struct lcd_device       *lcd_dev;
  28 
  29         struct spi_message      msg;
  30         struct spi_transfer     xfer;
  31         uint8_t                 *buf;
  32 
  33         int (*adj_mode)(struct tdo24m *lcd, int mode);
  34         int color_invert;
  35 
  36         int                     power;
  37         int                     mode;
  38 };
  39 
  40 /* use bit 30, 31 as the indicator of command parameter number */
  41 #define CMD0(x)         ((0 << 30) | (x))
  42 #define CMD1(x, x1)     ((1 << 30) | ((x) << 9) | 0x100 | (x1))
  43 #define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\
  44                         ((x1) << 9) | 0x100 | (x2))
  45 #define CMD_NULL        (-1)
  46 
  47 static const uint32_t lcd_panel_reset[] = {
  48         CMD0(0x1), /* reset */
  49         CMD0(0x0), /* nop */
  50         CMD0(0x0), /* nop */
  51         CMD0(0x0), /* nop */
  52         CMD_NULL,
  53 };
  54 
  55 static const uint32_t lcd_panel_on[] = {
  56         CMD0(0x29),             /* Display ON */
  57         CMD2(0xB8, 0xFF, 0xF9), /* Output Control */
  58         CMD0(0x11),             /* Sleep out */
  59         CMD1(0xB0, 0x16),       /* Wake */
  60         CMD_NULL,
  61 };
  62 
  63 static const uint32_t lcd_panel_off[] = {
  64         CMD0(0x28),             /* Display OFF */
  65         CMD2(0xB8, 0x80, 0x02), /* Output Control */
  66         CMD0(0x10),             /* Sleep in */
  67         CMD1(0xB0, 0x00),       /* Deep stand by in */
  68         CMD_NULL,
  69 };
  70 
  71 static const uint32_t lcd_vga_pass_through_tdo24m[] = {
  72         CMD1(0xB0, 0x16),
  73         CMD1(0xBC, 0x80),
  74         CMD1(0xE1, 0x00),
  75         CMD1(0x36, 0x50),
  76         CMD1(0x3B, 0x00),
  77         CMD_NULL,
  78 };
  79 
  80 static const uint32_t lcd_qvga_pass_through_tdo24m[] = {
  81         CMD1(0xB0, 0x16),
  82         CMD1(0xBC, 0x81),
  83         CMD1(0xE1, 0x00),
  84         CMD1(0x36, 0x50),
  85         CMD1(0x3B, 0x22),
  86         CMD_NULL,
  87 };
  88 
  89 static const uint32_t lcd_vga_transfer_tdo24m[] = {
  90         CMD1(0xcf, 0x02),       /* Blanking period control (1) */
  91         CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */
  92         CMD1(0xd1, 0x01),       /* CKV timing control on/off */
  93         CMD2(0xd2, 0x14, 0x00), /* CKV 1,2 timing control */
  94         CMD2(0xd3, 0x1a, 0x0f), /* OEV timing control */
  95         CMD2(0xd4, 0x1f, 0xaf), /* ASW timing control (1) */
  96         CMD1(0xd5, 0x14),       /* ASW timing control (2) */
  97         CMD0(0x21),             /* Invert for normally black display */
  98         CMD0(0x29),             /* Display on */
  99         CMD_NULL,
 100 };
 101 
 102 static const uint32_t lcd_qvga_transfer[] = {
 103         CMD1(0xd6, 0x02),       /* Blanking period control (1) */
 104         CMD2(0xd7, 0x08, 0x04), /* Blanking period control (2) */
 105         CMD1(0xd8, 0x01),       /* CKV timing control on/off */
 106         CMD2(0xd9, 0x00, 0x08), /* CKV 1,2 timing control */
 107         CMD2(0xde, 0x05, 0x0a), /* OEV timing control */
 108         CMD2(0xdf, 0x0a, 0x19), /* ASW timing control (1) */
 109         CMD1(0xe0, 0x0a),       /* ASW timing control (2) */
 110         CMD0(0x21),             /* Invert for normally black display */
 111         CMD0(0x29),             /* Display on */
 112         CMD_NULL,
 113 };
 114 
 115 static const uint32_t lcd_vga_pass_through_tdo35s[] = {
 116         CMD1(0xB0, 0x16),
 117         CMD1(0xBC, 0x80),
 118         CMD1(0xE1, 0x00),
 119         CMD1(0x3B, 0x00),
 120         CMD_NULL,
 121 };
 122 
 123 static const uint32_t lcd_qvga_pass_through_tdo35s[] = {
 124         CMD1(0xB0, 0x16),
 125         CMD1(0xBC, 0x81),
 126         CMD1(0xE1, 0x00),
 127         CMD1(0x3B, 0x22),
 128         CMD_NULL,
 129 };
 130 
 131 static const uint32_t lcd_vga_transfer_tdo35s[] = {
 132         CMD1(0xcf, 0x02),       /* Blanking period control (1) */
 133         CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */
 134         CMD1(0xd1, 0x01),       /* CKV timing control on/off */
 135         CMD2(0xd2, 0x00, 0x1e), /* CKV 1,2 timing control */
 136         CMD2(0xd3, 0x14, 0x28), /* OEV timing control */
 137         CMD2(0xd4, 0x28, 0x64), /* ASW timing control (1) */
 138         CMD1(0xd5, 0x28),       /* ASW timing control (2) */
 139         CMD0(0x21),             /* Invert for normally black display */
 140         CMD0(0x29),             /* Display on */
 141         CMD_NULL,
 142 };
 143 
 144 static const uint32_t lcd_panel_config[] = {
 145         CMD2(0xb8, 0xff, 0xf9), /* Output control */
 146         CMD0(0x11),             /* sleep out */
 147         CMD1(0xba, 0x01),       /* Display mode (1) */
 148         CMD1(0xbb, 0x00),       /* Display mode (2) */
 149         CMD1(0x3a, 0x60),       /* Display mode 18-bit RGB */
 150         CMD1(0xbf, 0x10),       /* Drive system change control */
 151         CMD1(0xb1, 0x56),       /* Booster operation setup */
 152         CMD1(0xb2, 0x33),       /* Booster mode setup */
 153         CMD1(0xb3, 0x11),       /* Booster frequency setup */
 154         CMD1(0xb4, 0x02),       /* Op amp/system clock */
 155         CMD1(0xb5, 0x35),       /* VCS voltage */
 156         CMD1(0xb6, 0x40),       /* VCOM voltage */
 157         CMD1(0xb7, 0x03),       /* External display signal */
 158         CMD1(0xbd, 0x00),       /* ASW slew rate */
 159         CMD1(0xbe, 0x00),       /* Dummy data for QuadData operation */
 160         CMD1(0xc0, 0x11),       /* Sleep out FR count (A) */
 161         CMD1(0xc1, 0x11),       /* Sleep out FR count (B) */
 162         CMD1(0xc2, 0x11),       /* Sleep out FR count (C) */
 163         CMD2(0xc3, 0x20, 0x40), /* Sleep out FR count (D) */
 164         CMD2(0xc4, 0x60, 0xc0), /* Sleep out FR count (E) */
 165         CMD2(0xc5, 0x10, 0x20), /* Sleep out FR count (F) */
 166         CMD1(0xc6, 0xc0),       /* Sleep out FR count (G) */
 167         CMD2(0xc7, 0x33, 0x43), /* Gamma 1 fine tuning (1) */
 168         CMD1(0xc8, 0x44),       /* Gamma 1 fine tuning (2) */
 169         CMD1(0xc9, 0x33),       /* Gamma 1 inclination adjustment */
 170         CMD1(0xca, 0x00),       /* Gamma 1 blue offset adjustment */
 171         CMD2(0xec, 0x01, 0xf0), /* Horizontal clock cycles */
 172         CMD_NULL,
 173 };
 174 
 175 static int tdo24m_writes(struct tdo24m *lcd, const uint32_t *array)
 176 {
 177         struct spi_transfer *x = &lcd->xfer;
 178         const uint32_t *p = array;
 179         uint32_t data;
 180         int nparams, err = 0;
 181 
 182         for (; *p != CMD_NULL; p++) {
 183                 if (!lcd->color_invert && *p == CMD0(0x21))
 184                         continue;
 185 
 186                 nparams = (*p >> 30) & 0x3;
 187 
 188                 data = *p << (7 - nparams);
 189                 switch (nparams) {
 190                 case 0:
 191                         lcd->buf[0] = (data >> 8) & 0xff;
 192                         lcd->buf[1] = data & 0xff;
 193                         break;
 194                 case 1:
 195                         lcd->buf[0] = (data >> 16) & 0xff;
 196                         lcd->buf[1] = (data >> 8) & 0xff;
 197                         lcd->buf[2] = data & 0xff;
 198                         break;
 199                 case 2:
 200                         lcd->buf[0] = (data >> 24) & 0xff;
 201                         lcd->buf[1] = (data >> 16) & 0xff;
 202                         lcd->buf[2] = (data >> 8) & 0xff;
 203                         lcd->buf[3] = data & 0xff;
 204                         break;
 205                 default:
 206                         continue;
 207                 }
 208                 x->len = nparams + 2;
 209                 err = spi_sync(lcd->spi_dev, &lcd->msg);
 210                 if (err)
 211                         break;
 212         }
 213 
 214         return err;
 215 }
 216 
 217 static int tdo24m_adj_mode(struct tdo24m *lcd, int mode)
 218 {
 219         switch (mode) {
 220         case MODE_VGA:
 221                 tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m);
 222                 tdo24m_writes(lcd, lcd_panel_config);
 223                 tdo24m_writes(lcd, lcd_vga_transfer_tdo24m);
 224                 break;
 225         case MODE_QVGA:
 226                 tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m);
 227                 tdo24m_writes(lcd, lcd_panel_config);
 228                 tdo24m_writes(lcd, lcd_qvga_transfer);
 229                 break;
 230         default:
 231                 return -EINVAL;
 232         }
 233 
 234         lcd->mode = mode;
 235         return 0;
 236 }
 237 
 238 static int tdo35s_adj_mode(struct tdo24m *lcd, int mode)
 239 {
 240         switch (mode) {
 241         case MODE_VGA:
 242                 tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s);
 243                 tdo24m_writes(lcd, lcd_panel_config);
 244                 tdo24m_writes(lcd, lcd_vga_transfer_tdo35s);
 245                 break;
 246         case MODE_QVGA:
 247                 tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s);
 248                 tdo24m_writes(lcd, lcd_panel_config);
 249                 tdo24m_writes(lcd, lcd_qvga_transfer);
 250                 break;
 251         default:
 252                 return -EINVAL;
 253         }
 254 
 255         lcd->mode = mode;
 256         return 0;
 257 }
 258 
 259 static int tdo24m_power_on(struct tdo24m *lcd)
 260 {
 261         int err;
 262 
 263         err = tdo24m_writes(lcd, lcd_panel_on);
 264         if (err)
 265                 goto out;
 266 
 267         err = tdo24m_writes(lcd, lcd_panel_reset);
 268         if (err)
 269                 goto out;
 270 
 271         err = lcd->adj_mode(lcd, lcd->mode);
 272 out:
 273         return err;
 274 }
 275 
 276 static int tdo24m_power_off(struct tdo24m *lcd)
 277 {
 278         return tdo24m_writes(lcd, lcd_panel_off);
 279 }
 280 
 281 static int tdo24m_power(struct tdo24m *lcd, int power)
 282 {
 283         int ret = 0;
 284 
 285         if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
 286                 ret = tdo24m_power_on(lcd);
 287         else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
 288                 ret = tdo24m_power_off(lcd);
 289 
 290         if (!ret)
 291                 lcd->power = power;
 292 
 293         return ret;
 294 }
 295 
 296 
 297 static int tdo24m_set_power(struct lcd_device *ld, int power)
 298 {
 299         struct tdo24m *lcd = lcd_get_data(ld);
 300 
 301         return tdo24m_power(lcd, power);
 302 }
 303 
 304 static int tdo24m_get_power(struct lcd_device *ld)
 305 {
 306         struct tdo24m *lcd = lcd_get_data(ld);
 307 
 308         return lcd->power;
 309 }
 310 
 311 static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m)
 312 {
 313         struct tdo24m *lcd = lcd_get_data(ld);
 314         int mode = MODE_QVGA;
 315 
 316         if (m->xres == 640 || m->xres == 480)
 317                 mode = MODE_VGA;
 318 
 319         if (lcd->mode == mode)
 320                 return 0;
 321 
 322         return lcd->adj_mode(lcd, mode);
 323 }
 324 
 325 static struct lcd_ops tdo24m_ops = {
 326         .get_power      = tdo24m_get_power,
 327         .set_power      = tdo24m_set_power,
 328         .set_mode       = tdo24m_set_mode,
 329 };
 330 
 331 static int tdo24m_probe(struct spi_device *spi)
 332 {
 333         struct tdo24m *lcd;
 334         struct spi_message *m;
 335         struct spi_transfer *x;
 336         struct tdo24m_platform_data *pdata;
 337         enum tdo24m_model model;
 338         int err;
 339 
 340         pdata = dev_get_platdata(&spi->dev);
 341         if (pdata)
 342                 model = pdata->model;
 343         else
 344                 model = TDO24M;
 345 
 346         spi->bits_per_word = 8;
 347         spi->mode = SPI_MODE_3;
 348         err = spi_setup(spi);
 349         if (err)
 350                 return err;
 351 
 352         lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL);
 353         if (!lcd)
 354                 return -ENOMEM;
 355 
 356         lcd->spi_dev = spi;
 357         lcd->power = FB_BLANK_POWERDOWN;
 358         lcd->mode = MODE_VGA;   /* default to VGA */
 359 
 360         lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
 361         if (lcd->buf == NULL)
 362                 return -ENOMEM;
 363 
 364         m = &lcd->msg;
 365         x = &lcd->xfer;
 366 
 367         spi_message_init(m);
 368 
 369         x->cs_change = 0;
 370         x->tx_buf = &lcd->buf[0];
 371         spi_message_add_tail(x, m);
 372 
 373         switch (model) {
 374         case TDO24M:
 375                 lcd->color_invert = 1;
 376                 lcd->adj_mode = tdo24m_adj_mode;
 377                 break;
 378         case TDO35S:
 379                 lcd->adj_mode = tdo35s_adj_mode;
 380                 lcd->color_invert = 0;
 381                 break;
 382         default:
 383                 dev_err(&spi->dev, "Unsupported model");
 384                 return -EINVAL;
 385         }
 386 
 387         lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "tdo24m", &spi->dev,
 388                                                 lcd, &tdo24m_ops);
 389         if (IS_ERR(lcd->lcd_dev))
 390                 return PTR_ERR(lcd->lcd_dev);
 391 
 392         spi_set_drvdata(spi, lcd);
 393         err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
 394         if (err)
 395                 return err;
 396 
 397         return 0;
 398 }
 399 
 400 static int tdo24m_remove(struct spi_device *spi)
 401 {
 402         struct tdo24m *lcd = spi_get_drvdata(spi);
 403 
 404         tdo24m_power(lcd, FB_BLANK_POWERDOWN);
 405         return 0;
 406 }
 407 
 408 #ifdef CONFIG_PM_SLEEP
 409 static int tdo24m_suspend(struct device *dev)
 410 {
 411         struct tdo24m *lcd = dev_get_drvdata(dev);
 412 
 413         return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
 414 }
 415 
 416 static int tdo24m_resume(struct device *dev)
 417 {
 418         struct tdo24m *lcd = dev_get_drvdata(dev);
 419 
 420         return tdo24m_power(lcd, FB_BLANK_UNBLANK);
 421 }
 422 #endif
 423 
 424 static SIMPLE_DEV_PM_OPS(tdo24m_pm_ops, tdo24m_suspend, tdo24m_resume);
 425 
 426 /* Power down all displays on reboot, poweroff or halt */
 427 static void tdo24m_shutdown(struct spi_device *spi)
 428 {
 429         struct tdo24m *lcd = spi_get_drvdata(spi);
 430 
 431         tdo24m_power(lcd, FB_BLANK_POWERDOWN);
 432 }
 433 
 434 static struct spi_driver tdo24m_driver = {
 435         .driver = {
 436                 .name           = "tdo24m",
 437                 .pm             = &tdo24m_pm_ops,
 438         },
 439         .probe          = tdo24m_probe,
 440         .remove         = tdo24m_remove,
 441         .shutdown       = tdo24m_shutdown,
 442 };
 443 
 444 module_spi_driver(tdo24m_driver);
 445 
 446 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
 447 MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
 448 MODULE_LICENSE("GPL");
 449 MODULE_ALIAS("spi:tdo24m");

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