0001
0002
0003
0004
0005
0006
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
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),
0049 CMD0(0x0),
0050 CMD0(0x0),
0051 CMD0(0x0),
0052 CMD_NULL,
0053 };
0054
0055 static const uint32_t lcd_panel_on[] = {
0056 CMD0(0x29),
0057 CMD2(0xB8, 0xFF, 0xF9),
0058 CMD0(0x11),
0059 CMD1(0xB0, 0x16),
0060 CMD_NULL,
0061 };
0062
0063 static const uint32_t lcd_panel_off[] = {
0064 CMD0(0x28),
0065 CMD2(0xB8, 0x80, 0x02),
0066 CMD0(0x10),
0067 CMD1(0xB0, 0x00),
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),
0091 CMD2(0xd0, 0x08, 0x04),
0092 CMD1(0xd1, 0x01),
0093 CMD2(0xd2, 0x14, 0x00),
0094 CMD2(0xd3, 0x1a, 0x0f),
0095 CMD2(0xd4, 0x1f, 0xaf),
0096 CMD1(0xd5, 0x14),
0097 CMD0(0x21),
0098 CMD0(0x29),
0099 CMD_NULL,
0100 };
0101
0102 static const uint32_t lcd_qvga_transfer[] = {
0103 CMD1(0xd6, 0x02),
0104 CMD2(0xd7, 0x08, 0x04),
0105 CMD1(0xd8, 0x01),
0106 CMD2(0xd9, 0x00, 0x08),
0107 CMD2(0xde, 0x05, 0x0a),
0108 CMD2(0xdf, 0x0a, 0x19),
0109 CMD1(0xe0, 0x0a),
0110 CMD0(0x21),
0111 CMD0(0x29),
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),
0133 CMD2(0xd0, 0x08, 0x04),
0134 CMD1(0xd1, 0x01),
0135 CMD2(0xd2, 0x00, 0x1e),
0136 CMD2(0xd3, 0x14, 0x28),
0137 CMD2(0xd4, 0x28, 0x64),
0138 CMD1(0xd5, 0x28),
0139 CMD0(0x21),
0140 CMD0(0x29),
0141 CMD_NULL,
0142 };
0143
0144 static const uint32_t lcd_panel_config[] = {
0145 CMD2(0xb8, 0xff, 0xf9),
0146 CMD0(0x11),
0147 CMD1(0xba, 0x01),
0148 CMD1(0xbb, 0x00),
0149 CMD1(0x3a, 0x60),
0150 CMD1(0xbf, 0x10),
0151 CMD1(0xb1, 0x56),
0152 CMD1(0xb2, 0x33),
0153 CMD1(0xb3, 0x11),
0154 CMD1(0xb4, 0x02),
0155 CMD1(0xb5, 0x35),
0156 CMD1(0xb6, 0x40),
0157 CMD1(0xb7, 0x03),
0158 CMD1(0xbd, 0x00),
0159 CMD1(0xbe, 0x00),
0160 CMD1(0xc0, 0x11),
0161 CMD1(0xc1, 0x11),
0162 CMD1(0xc2, 0x11),
0163 CMD2(0xc3, 0x20, 0x40),
0164 CMD2(0xc4, 0x60, 0xc0),
0165 CMD2(0xc5, 0x10, 0x20),
0166 CMD1(0xc6, 0xc0),
0167 CMD2(0xc7, 0x33, 0x43),
0168 CMD1(0xc8, 0x44),
0169 CMD1(0xc9, 0x33),
0170 CMD1(0xca, 0x00),
0171 CMD2(0xec, 0x01, 0xf0),
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;
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
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");