Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * tdo24m - SPI-based drivers for Toppoly TDO24M series LCD panels
0004  *
0005  * Copyright (C) 2008 Marvell International Ltd.
0006  *  Eric Miao <eric.miao@marvell.com>
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <linux/kernel.h>
0011 #include <linux/init.h>
0012 #include <linux/device.h>
0013 #include <linux/spi/spi.h>
0014 #include <linux/spi/tdo24m.h>
0015 #include <linux/fb.h>
0016 #include <linux/lcd.h>
0017 #include <linux/slab.h>
0018 
0019 #define POWER_IS_ON(pwr)    ((pwr) <= FB_BLANK_NORMAL)
0020 
0021 #define TDO24M_SPI_BUFF_SIZE    (4)
0022 #define MODE_QVGA   0
0023 #define MODE_VGA    1
0024 
0025 struct tdo24m {
0026     struct spi_device   *spi_dev;
0027     struct lcd_device   *lcd_dev;
0028 
0029     struct spi_message  msg;
0030     struct spi_transfer xfer;
0031     uint8_t         *buf;
0032 
0033     int (*adj_mode)(struct tdo24m *lcd, int mode);
0034     int color_invert;
0035 
0036     int         power;
0037     int         mode;
0038 };
0039 
0040 /* use bit 30, 31 as the indicator of command parameter number */
0041 #define CMD0(x)     ((0 << 30) | (x))
0042 #define CMD1(x, x1) ((1 << 30) | ((x) << 9) | 0x100 | (x1))
0043 #define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\
0044             ((x1) << 9) | 0x100 | (x2))
0045 #define CMD_NULL    (-1)
0046 
0047 static const uint32_t lcd_panel_reset[] = {
0048     CMD0(0x1), /* reset */
0049     CMD0(0x0), /* nop */
0050     CMD0(0x0), /* nop */
0051     CMD0(0x0), /* nop */
0052     CMD_NULL,
0053 };
0054 
0055 static const uint32_t lcd_panel_on[] = {
0056     CMD0(0x29),     /* Display ON */
0057     CMD2(0xB8, 0xFF, 0xF9), /* Output Control */
0058     CMD0(0x11),     /* Sleep out */
0059     CMD1(0xB0, 0x16),   /* Wake */
0060     CMD_NULL,
0061 };
0062 
0063 static const uint32_t lcd_panel_off[] = {
0064     CMD0(0x28),     /* Display OFF */
0065     CMD2(0xB8, 0x80, 0x02), /* Output Control */
0066     CMD0(0x10),     /* Sleep in */
0067     CMD1(0xB0, 0x00),   /* Deep stand by in */
0068     CMD_NULL,
0069 };
0070 
0071 static const uint32_t lcd_vga_pass_through_tdo24m[] = {
0072     CMD1(0xB0, 0x16),
0073     CMD1(0xBC, 0x80),
0074     CMD1(0xE1, 0x00),
0075     CMD1(0x36, 0x50),
0076     CMD1(0x3B, 0x00),
0077     CMD_NULL,
0078 };
0079 
0080 static const uint32_t lcd_qvga_pass_through_tdo24m[] = {
0081     CMD1(0xB0, 0x16),
0082     CMD1(0xBC, 0x81),
0083     CMD1(0xE1, 0x00),
0084     CMD1(0x36, 0x50),
0085     CMD1(0x3B, 0x22),
0086     CMD_NULL,
0087 };
0088 
0089 static const uint32_t lcd_vga_transfer_tdo24m[] = {
0090     CMD1(0xcf, 0x02),   /* Blanking period control (1) */
0091     CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */
0092     CMD1(0xd1, 0x01),   /* CKV timing control on/off */
0093     CMD2(0xd2, 0x14, 0x00), /* CKV 1,2 timing control */
0094     CMD2(0xd3, 0x1a, 0x0f), /* OEV timing control */
0095     CMD2(0xd4, 0x1f, 0xaf), /* ASW timing control (1) */
0096     CMD1(0xd5, 0x14),   /* ASW timing control (2) */
0097     CMD0(0x21),     /* Invert for normally black display */
0098     CMD0(0x29),     /* Display on */
0099     CMD_NULL,
0100 };
0101 
0102 static const uint32_t lcd_qvga_transfer[] = {
0103     CMD1(0xd6, 0x02),   /* Blanking period control (1) */
0104     CMD2(0xd7, 0x08, 0x04), /* Blanking period control (2) */
0105     CMD1(0xd8, 0x01),   /* CKV timing control on/off */
0106     CMD2(0xd9, 0x00, 0x08), /* CKV 1,2 timing control */
0107     CMD2(0xde, 0x05, 0x0a), /* OEV timing control */
0108     CMD2(0xdf, 0x0a, 0x19), /* ASW timing control (1) */
0109     CMD1(0xe0, 0x0a),   /* ASW timing control (2) */
0110     CMD0(0x21),     /* Invert for normally black display */
0111     CMD0(0x29),     /* Display on */
0112     CMD_NULL,
0113 };
0114 
0115 static const uint32_t lcd_vga_pass_through_tdo35s[] = {
0116     CMD1(0xB0, 0x16),
0117     CMD1(0xBC, 0x80),
0118     CMD1(0xE1, 0x00),
0119     CMD1(0x3B, 0x00),
0120     CMD_NULL,
0121 };
0122 
0123 static const uint32_t lcd_qvga_pass_through_tdo35s[] = {
0124     CMD1(0xB0, 0x16),
0125     CMD1(0xBC, 0x81),
0126     CMD1(0xE1, 0x00),
0127     CMD1(0x3B, 0x22),
0128     CMD_NULL,
0129 };
0130 
0131 static const uint32_t lcd_vga_transfer_tdo35s[] = {
0132     CMD1(0xcf, 0x02),   /* Blanking period control (1) */
0133     CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */
0134     CMD1(0xd1, 0x01),   /* CKV timing control on/off */
0135     CMD2(0xd2, 0x00, 0x1e), /* CKV 1,2 timing control */
0136     CMD2(0xd3, 0x14, 0x28), /* OEV timing control */
0137     CMD2(0xd4, 0x28, 0x64), /* ASW timing control (1) */
0138     CMD1(0xd5, 0x28),   /* ASW timing control (2) */
0139     CMD0(0x21),     /* Invert for normally black display */
0140     CMD0(0x29),     /* Display on */
0141     CMD_NULL,
0142 };
0143 
0144 static const uint32_t lcd_panel_config[] = {
0145     CMD2(0xb8, 0xff, 0xf9), /* Output control */
0146     CMD0(0x11),     /* sleep out */
0147     CMD1(0xba, 0x01),   /* Display mode (1) */
0148     CMD1(0xbb, 0x00),   /* Display mode (2) */
0149     CMD1(0x3a, 0x60),   /* Display mode 18-bit RGB */
0150     CMD1(0xbf, 0x10),   /* Drive system change control */
0151     CMD1(0xb1, 0x56),   /* Booster operation setup */
0152     CMD1(0xb2, 0x33),   /* Booster mode setup */
0153     CMD1(0xb3, 0x11),   /* Booster frequency setup */
0154     CMD1(0xb4, 0x02),   /* Op amp/system clock */
0155     CMD1(0xb5, 0x35),   /* VCS voltage */
0156     CMD1(0xb6, 0x40),   /* VCOM voltage */
0157     CMD1(0xb7, 0x03),   /* External display signal */
0158     CMD1(0xbd, 0x00),   /* ASW slew rate */
0159     CMD1(0xbe, 0x00),   /* Dummy data for QuadData operation */
0160     CMD1(0xc0, 0x11),   /* Sleep out FR count (A) */
0161     CMD1(0xc1, 0x11),   /* Sleep out FR count (B) */
0162     CMD1(0xc2, 0x11),   /* Sleep out FR count (C) */
0163     CMD2(0xc3, 0x20, 0x40), /* Sleep out FR count (D) */
0164     CMD2(0xc4, 0x60, 0xc0), /* Sleep out FR count (E) */
0165     CMD2(0xc5, 0x10, 0x20), /* Sleep out FR count (F) */
0166     CMD1(0xc6, 0xc0),   /* Sleep out FR count (G) */
0167     CMD2(0xc7, 0x33, 0x43), /* Gamma 1 fine tuning (1) */
0168     CMD1(0xc8, 0x44),   /* Gamma 1 fine tuning (2) */
0169     CMD1(0xc9, 0x33),   /* Gamma 1 inclination adjustment */
0170     CMD1(0xca, 0x00),   /* Gamma 1 blue offset adjustment */
0171     CMD2(0xec, 0x01, 0xf0), /* Horizontal clock cycles */
0172     CMD_NULL,
0173 };
0174 
0175 static int tdo24m_writes(struct tdo24m *lcd, const uint32_t *array)
0176 {
0177     struct spi_transfer *x = &lcd->xfer;
0178     const uint32_t *p = array;
0179     uint32_t data;
0180     int nparams, err = 0;
0181 
0182     for (; *p != CMD_NULL; p++) {
0183         if (!lcd->color_invert && *p == CMD0(0x21))
0184             continue;
0185 
0186         nparams = (*p >> 30) & 0x3;
0187 
0188         data = *p << (7 - nparams);
0189         switch (nparams) {
0190         case 0:
0191             lcd->buf[0] = (data >> 8) & 0xff;
0192             lcd->buf[1] = data & 0xff;
0193             break;
0194         case 1:
0195             lcd->buf[0] = (data >> 16) & 0xff;
0196             lcd->buf[1] = (data >> 8) & 0xff;
0197             lcd->buf[2] = data & 0xff;
0198             break;
0199         case 2:
0200             lcd->buf[0] = (data >> 24) & 0xff;
0201             lcd->buf[1] = (data >> 16) & 0xff;
0202             lcd->buf[2] = (data >> 8) & 0xff;
0203             lcd->buf[3] = data & 0xff;
0204             break;
0205         default:
0206             continue;
0207         }
0208         x->len = nparams + 2;
0209         err = spi_sync(lcd->spi_dev, &lcd->msg);
0210         if (err)
0211             break;
0212     }
0213 
0214     return err;
0215 }
0216 
0217 static int tdo24m_adj_mode(struct tdo24m *lcd, int mode)
0218 {
0219     switch (mode) {
0220     case MODE_VGA:
0221         tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m);
0222         tdo24m_writes(lcd, lcd_panel_config);
0223         tdo24m_writes(lcd, lcd_vga_transfer_tdo24m);
0224         break;
0225     case MODE_QVGA:
0226         tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m);
0227         tdo24m_writes(lcd, lcd_panel_config);
0228         tdo24m_writes(lcd, lcd_qvga_transfer);
0229         break;
0230     default:
0231         return -EINVAL;
0232     }
0233 
0234     lcd->mode = mode;
0235     return 0;
0236 }
0237 
0238 static int tdo35s_adj_mode(struct tdo24m *lcd, int mode)
0239 {
0240     switch (mode) {
0241     case MODE_VGA:
0242         tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s);
0243         tdo24m_writes(lcd, lcd_panel_config);
0244         tdo24m_writes(lcd, lcd_vga_transfer_tdo35s);
0245         break;
0246     case MODE_QVGA:
0247         tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s);
0248         tdo24m_writes(lcd, lcd_panel_config);
0249         tdo24m_writes(lcd, lcd_qvga_transfer);
0250         break;
0251     default:
0252         return -EINVAL;
0253     }
0254 
0255     lcd->mode = mode;
0256     return 0;
0257 }
0258 
0259 static int tdo24m_power_on(struct tdo24m *lcd)
0260 {
0261     int err;
0262 
0263     err = tdo24m_writes(lcd, lcd_panel_on);
0264     if (err)
0265         goto out;
0266 
0267     err = tdo24m_writes(lcd, lcd_panel_reset);
0268     if (err)
0269         goto out;
0270 
0271     err = lcd->adj_mode(lcd, lcd->mode);
0272 out:
0273     return err;
0274 }
0275 
0276 static int tdo24m_power_off(struct tdo24m *lcd)
0277 {
0278     return tdo24m_writes(lcd, lcd_panel_off);
0279 }
0280 
0281 static int tdo24m_power(struct tdo24m *lcd, int power)
0282 {
0283     int ret = 0;
0284 
0285     if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
0286         ret = tdo24m_power_on(lcd);
0287     else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
0288         ret = tdo24m_power_off(lcd);
0289 
0290     if (!ret)
0291         lcd->power = power;
0292 
0293     return ret;
0294 }
0295 
0296 
0297 static int tdo24m_set_power(struct lcd_device *ld, int power)
0298 {
0299     struct tdo24m *lcd = lcd_get_data(ld);
0300 
0301     return tdo24m_power(lcd, power);
0302 }
0303 
0304 static int tdo24m_get_power(struct lcd_device *ld)
0305 {
0306     struct tdo24m *lcd = lcd_get_data(ld);
0307 
0308     return lcd->power;
0309 }
0310 
0311 static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m)
0312 {
0313     struct tdo24m *lcd = lcd_get_data(ld);
0314     int mode = MODE_QVGA;
0315 
0316     if (m->xres == 640 || m->xres == 480)
0317         mode = MODE_VGA;
0318 
0319     if (lcd->mode == mode)
0320         return 0;
0321 
0322     return lcd->adj_mode(lcd, mode);
0323 }
0324 
0325 static struct lcd_ops tdo24m_ops = {
0326     .get_power  = tdo24m_get_power,
0327     .set_power  = tdo24m_set_power,
0328     .set_mode   = tdo24m_set_mode,
0329 };
0330 
0331 static int tdo24m_probe(struct spi_device *spi)
0332 {
0333     struct tdo24m *lcd;
0334     struct spi_message *m;
0335     struct spi_transfer *x;
0336     struct tdo24m_platform_data *pdata;
0337     enum tdo24m_model model;
0338     int err;
0339 
0340     pdata = dev_get_platdata(&spi->dev);
0341     if (pdata)
0342         model = pdata->model;
0343     else
0344         model = TDO24M;
0345 
0346     spi->bits_per_word = 8;
0347     spi->mode = SPI_MODE_3;
0348     err = spi_setup(spi);
0349     if (err)
0350         return err;
0351 
0352     lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL);
0353     if (!lcd)
0354         return -ENOMEM;
0355 
0356     lcd->spi_dev = spi;
0357     lcd->power = FB_BLANK_POWERDOWN;
0358     lcd->mode = MODE_VGA;   /* default to VGA */
0359 
0360     lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
0361     if (lcd->buf == NULL)
0362         return -ENOMEM;
0363 
0364     m = &lcd->msg;
0365     x = &lcd->xfer;
0366 
0367     spi_message_init(m);
0368 
0369     x->cs_change = 0;
0370     x->tx_buf = &lcd->buf[0];
0371     spi_message_add_tail(x, m);
0372 
0373     switch (model) {
0374     case TDO24M:
0375         lcd->color_invert = 1;
0376         lcd->adj_mode = tdo24m_adj_mode;
0377         break;
0378     case TDO35S:
0379         lcd->adj_mode = tdo35s_adj_mode;
0380         lcd->color_invert = 0;
0381         break;
0382     default:
0383         dev_err(&spi->dev, "Unsupported model");
0384         return -EINVAL;
0385     }
0386 
0387     lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "tdo24m", &spi->dev,
0388                         lcd, &tdo24m_ops);
0389     if (IS_ERR(lcd->lcd_dev))
0390         return PTR_ERR(lcd->lcd_dev);
0391 
0392     spi_set_drvdata(spi, lcd);
0393     err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
0394     if (err)
0395         return err;
0396 
0397     return 0;
0398 }
0399 
0400 static void tdo24m_remove(struct spi_device *spi)
0401 {
0402     struct tdo24m *lcd = spi_get_drvdata(spi);
0403 
0404     tdo24m_power(lcd, FB_BLANK_POWERDOWN);
0405 }
0406 
0407 #ifdef CONFIG_PM_SLEEP
0408 static int tdo24m_suspend(struct device *dev)
0409 {
0410     struct tdo24m *lcd = dev_get_drvdata(dev);
0411 
0412     return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
0413 }
0414 
0415 static int tdo24m_resume(struct device *dev)
0416 {
0417     struct tdo24m *lcd = dev_get_drvdata(dev);
0418 
0419     return tdo24m_power(lcd, FB_BLANK_UNBLANK);
0420 }
0421 #endif
0422 
0423 static SIMPLE_DEV_PM_OPS(tdo24m_pm_ops, tdo24m_suspend, tdo24m_resume);
0424 
0425 /* Power down all displays on reboot, poweroff or halt */
0426 static void tdo24m_shutdown(struct spi_device *spi)
0427 {
0428     struct tdo24m *lcd = spi_get_drvdata(spi);
0429 
0430     tdo24m_power(lcd, FB_BLANK_POWERDOWN);
0431 }
0432 
0433 static struct spi_driver tdo24m_driver = {
0434     .driver = {
0435         .name       = "tdo24m",
0436         .pm     = &tdo24m_pm_ops,
0437     },
0438     .probe      = tdo24m_probe,
0439     .remove     = tdo24m_remove,
0440     .shutdown   = tdo24m_shutdown,
0441 };
0442 
0443 module_spi_driver(tdo24m_driver);
0444 
0445 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
0446 MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
0447 MODULE_LICENSE("GPL");
0448 MODULE_ALIAS("spi:tdo24m");