18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tdo24m - SPI-based drivers for Toppoly TDO24M series LCD panels 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Marvell International Ltd. 68c2ecf20Sopenharmony_ci * Eric Miao <eric.miao@marvell.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 148c2ecf20Sopenharmony_ci#include <linux/spi/tdo24m.h> 158c2ecf20Sopenharmony_ci#include <linux/fb.h> 168c2ecf20Sopenharmony_ci#include <linux/lcd.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define TDO24M_SPI_BUFF_SIZE (4) 228c2ecf20Sopenharmony_ci#define MODE_QVGA 0 238c2ecf20Sopenharmony_ci#define MODE_VGA 1 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct tdo24m { 268c2ecf20Sopenharmony_ci struct spi_device *spi_dev; 278c2ecf20Sopenharmony_ci struct lcd_device *lcd_dev; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci struct spi_message msg; 308c2ecf20Sopenharmony_ci struct spi_transfer xfer; 318c2ecf20Sopenharmony_ci uint8_t *buf; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci int (*adj_mode)(struct tdo24m *lcd, int mode); 348c2ecf20Sopenharmony_ci int color_invert; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci int power; 378c2ecf20Sopenharmony_ci int mode; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* use bit 30, 31 as the indicator of command parameter number */ 418c2ecf20Sopenharmony_ci#define CMD0(x) ((0 << 30) | (x)) 428c2ecf20Sopenharmony_ci#define CMD1(x, x1) ((1 << 30) | ((x) << 9) | 0x100 | (x1)) 438c2ecf20Sopenharmony_ci#define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\ 448c2ecf20Sopenharmony_ci ((x1) << 9) | 0x100 | (x2)) 458c2ecf20Sopenharmony_ci#define CMD_NULL (-1) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const uint32_t lcd_panel_reset[] = { 488c2ecf20Sopenharmony_ci CMD0(0x1), /* reset */ 498c2ecf20Sopenharmony_ci CMD0(0x0), /* nop */ 508c2ecf20Sopenharmony_ci CMD0(0x0), /* nop */ 518c2ecf20Sopenharmony_ci CMD0(0x0), /* nop */ 528c2ecf20Sopenharmony_ci CMD_NULL, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const uint32_t lcd_panel_on[] = { 568c2ecf20Sopenharmony_ci CMD0(0x29), /* Display ON */ 578c2ecf20Sopenharmony_ci CMD2(0xB8, 0xFF, 0xF9), /* Output Control */ 588c2ecf20Sopenharmony_ci CMD0(0x11), /* Sleep out */ 598c2ecf20Sopenharmony_ci CMD1(0xB0, 0x16), /* Wake */ 608c2ecf20Sopenharmony_ci CMD_NULL, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const uint32_t lcd_panel_off[] = { 648c2ecf20Sopenharmony_ci CMD0(0x28), /* Display OFF */ 658c2ecf20Sopenharmony_ci CMD2(0xB8, 0x80, 0x02), /* Output Control */ 668c2ecf20Sopenharmony_ci CMD0(0x10), /* Sleep in */ 678c2ecf20Sopenharmony_ci CMD1(0xB0, 0x00), /* Deep stand by in */ 688c2ecf20Sopenharmony_ci CMD_NULL, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const uint32_t lcd_vga_pass_through_tdo24m[] = { 728c2ecf20Sopenharmony_ci CMD1(0xB0, 0x16), 738c2ecf20Sopenharmony_ci CMD1(0xBC, 0x80), 748c2ecf20Sopenharmony_ci CMD1(0xE1, 0x00), 758c2ecf20Sopenharmony_ci CMD1(0x36, 0x50), 768c2ecf20Sopenharmony_ci CMD1(0x3B, 0x00), 778c2ecf20Sopenharmony_ci CMD_NULL, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const uint32_t lcd_qvga_pass_through_tdo24m[] = { 818c2ecf20Sopenharmony_ci CMD1(0xB0, 0x16), 828c2ecf20Sopenharmony_ci CMD1(0xBC, 0x81), 838c2ecf20Sopenharmony_ci CMD1(0xE1, 0x00), 848c2ecf20Sopenharmony_ci CMD1(0x36, 0x50), 858c2ecf20Sopenharmony_ci CMD1(0x3B, 0x22), 868c2ecf20Sopenharmony_ci CMD_NULL, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const uint32_t lcd_vga_transfer_tdo24m[] = { 908c2ecf20Sopenharmony_ci CMD1(0xcf, 0x02), /* Blanking period control (1) */ 918c2ecf20Sopenharmony_ci CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */ 928c2ecf20Sopenharmony_ci CMD1(0xd1, 0x01), /* CKV timing control on/off */ 938c2ecf20Sopenharmony_ci CMD2(0xd2, 0x14, 0x00), /* CKV 1,2 timing control */ 948c2ecf20Sopenharmony_ci CMD2(0xd3, 0x1a, 0x0f), /* OEV timing control */ 958c2ecf20Sopenharmony_ci CMD2(0xd4, 0x1f, 0xaf), /* ASW timing control (1) */ 968c2ecf20Sopenharmony_ci CMD1(0xd5, 0x14), /* ASW timing control (2) */ 978c2ecf20Sopenharmony_ci CMD0(0x21), /* Invert for normally black display */ 988c2ecf20Sopenharmony_ci CMD0(0x29), /* Display on */ 998c2ecf20Sopenharmony_ci CMD_NULL, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const uint32_t lcd_qvga_transfer[] = { 1038c2ecf20Sopenharmony_ci CMD1(0xd6, 0x02), /* Blanking period control (1) */ 1048c2ecf20Sopenharmony_ci CMD2(0xd7, 0x08, 0x04), /* Blanking period control (2) */ 1058c2ecf20Sopenharmony_ci CMD1(0xd8, 0x01), /* CKV timing control on/off */ 1068c2ecf20Sopenharmony_ci CMD2(0xd9, 0x00, 0x08), /* CKV 1,2 timing control */ 1078c2ecf20Sopenharmony_ci CMD2(0xde, 0x05, 0x0a), /* OEV timing control */ 1088c2ecf20Sopenharmony_ci CMD2(0xdf, 0x0a, 0x19), /* ASW timing control (1) */ 1098c2ecf20Sopenharmony_ci CMD1(0xe0, 0x0a), /* ASW timing control (2) */ 1108c2ecf20Sopenharmony_ci CMD0(0x21), /* Invert for normally black display */ 1118c2ecf20Sopenharmony_ci CMD0(0x29), /* Display on */ 1128c2ecf20Sopenharmony_ci CMD_NULL, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const uint32_t lcd_vga_pass_through_tdo35s[] = { 1168c2ecf20Sopenharmony_ci CMD1(0xB0, 0x16), 1178c2ecf20Sopenharmony_ci CMD1(0xBC, 0x80), 1188c2ecf20Sopenharmony_ci CMD1(0xE1, 0x00), 1198c2ecf20Sopenharmony_ci CMD1(0x3B, 0x00), 1208c2ecf20Sopenharmony_ci CMD_NULL, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic const uint32_t lcd_qvga_pass_through_tdo35s[] = { 1248c2ecf20Sopenharmony_ci CMD1(0xB0, 0x16), 1258c2ecf20Sopenharmony_ci CMD1(0xBC, 0x81), 1268c2ecf20Sopenharmony_ci CMD1(0xE1, 0x00), 1278c2ecf20Sopenharmony_ci CMD1(0x3B, 0x22), 1288c2ecf20Sopenharmony_ci CMD_NULL, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic const uint32_t lcd_vga_transfer_tdo35s[] = { 1328c2ecf20Sopenharmony_ci CMD1(0xcf, 0x02), /* Blanking period control (1) */ 1338c2ecf20Sopenharmony_ci CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */ 1348c2ecf20Sopenharmony_ci CMD1(0xd1, 0x01), /* CKV timing control on/off */ 1358c2ecf20Sopenharmony_ci CMD2(0xd2, 0x00, 0x1e), /* CKV 1,2 timing control */ 1368c2ecf20Sopenharmony_ci CMD2(0xd3, 0x14, 0x28), /* OEV timing control */ 1378c2ecf20Sopenharmony_ci CMD2(0xd4, 0x28, 0x64), /* ASW timing control (1) */ 1388c2ecf20Sopenharmony_ci CMD1(0xd5, 0x28), /* ASW timing control (2) */ 1398c2ecf20Sopenharmony_ci CMD0(0x21), /* Invert for normally black display */ 1408c2ecf20Sopenharmony_ci CMD0(0x29), /* Display on */ 1418c2ecf20Sopenharmony_ci CMD_NULL, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const uint32_t lcd_panel_config[] = { 1458c2ecf20Sopenharmony_ci CMD2(0xb8, 0xff, 0xf9), /* Output control */ 1468c2ecf20Sopenharmony_ci CMD0(0x11), /* sleep out */ 1478c2ecf20Sopenharmony_ci CMD1(0xba, 0x01), /* Display mode (1) */ 1488c2ecf20Sopenharmony_ci CMD1(0xbb, 0x00), /* Display mode (2) */ 1498c2ecf20Sopenharmony_ci CMD1(0x3a, 0x60), /* Display mode 18-bit RGB */ 1508c2ecf20Sopenharmony_ci CMD1(0xbf, 0x10), /* Drive system change control */ 1518c2ecf20Sopenharmony_ci CMD1(0xb1, 0x56), /* Booster operation setup */ 1528c2ecf20Sopenharmony_ci CMD1(0xb2, 0x33), /* Booster mode setup */ 1538c2ecf20Sopenharmony_ci CMD1(0xb3, 0x11), /* Booster frequency setup */ 1548c2ecf20Sopenharmony_ci CMD1(0xb4, 0x02), /* Op amp/system clock */ 1558c2ecf20Sopenharmony_ci CMD1(0xb5, 0x35), /* VCS voltage */ 1568c2ecf20Sopenharmony_ci CMD1(0xb6, 0x40), /* VCOM voltage */ 1578c2ecf20Sopenharmony_ci CMD1(0xb7, 0x03), /* External display signal */ 1588c2ecf20Sopenharmony_ci CMD1(0xbd, 0x00), /* ASW slew rate */ 1598c2ecf20Sopenharmony_ci CMD1(0xbe, 0x00), /* Dummy data for QuadData operation */ 1608c2ecf20Sopenharmony_ci CMD1(0xc0, 0x11), /* Sleep out FR count (A) */ 1618c2ecf20Sopenharmony_ci CMD1(0xc1, 0x11), /* Sleep out FR count (B) */ 1628c2ecf20Sopenharmony_ci CMD1(0xc2, 0x11), /* Sleep out FR count (C) */ 1638c2ecf20Sopenharmony_ci CMD2(0xc3, 0x20, 0x40), /* Sleep out FR count (D) */ 1648c2ecf20Sopenharmony_ci CMD2(0xc4, 0x60, 0xc0), /* Sleep out FR count (E) */ 1658c2ecf20Sopenharmony_ci CMD2(0xc5, 0x10, 0x20), /* Sleep out FR count (F) */ 1668c2ecf20Sopenharmony_ci CMD1(0xc6, 0xc0), /* Sleep out FR count (G) */ 1678c2ecf20Sopenharmony_ci CMD2(0xc7, 0x33, 0x43), /* Gamma 1 fine tuning (1) */ 1688c2ecf20Sopenharmony_ci CMD1(0xc8, 0x44), /* Gamma 1 fine tuning (2) */ 1698c2ecf20Sopenharmony_ci CMD1(0xc9, 0x33), /* Gamma 1 inclination adjustment */ 1708c2ecf20Sopenharmony_ci CMD1(0xca, 0x00), /* Gamma 1 blue offset adjustment */ 1718c2ecf20Sopenharmony_ci CMD2(0xec, 0x01, 0xf0), /* Horizontal clock cycles */ 1728c2ecf20Sopenharmony_ci CMD_NULL, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int tdo24m_writes(struct tdo24m *lcd, const uint32_t *array) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct spi_transfer *x = &lcd->xfer; 1788c2ecf20Sopenharmony_ci const uint32_t *p = array; 1798c2ecf20Sopenharmony_ci uint32_t data; 1808c2ecf20Sopenharmony_ci int nparams, err = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (; *p != CMD_NULL; p++) { 1838c2ecf20Sopenharmony_ci if (!lcd->color_invert && *p == CMD0(0x21)) 1848c2ecf20Sopenharmony_ci continue; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci nparams = (*p >> 30) & 0x3; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci data = *p << (7 - nparams); 1898c2ecf20Sopenharmony_ci switch (nparams) { 1908c2ecf20Sopenharmony_ci case 0: 1918c2ecf20Sopenharmony_ci lcd->buf[0] = (data >> 8) & 0xff; 1928c2ecf20Sopenharmony_ci lcd->buf[1] = data & 0xff; 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case 1: 1958c2ecf20Sopenharmony_ci lcd->buf[0] = (data >> 16) & 0xff; 1968c2ecf20Sopenharmony_ci lcd->buf[1] = (data >> 8) & 0xff; 1978c2ecf20Sopenharmony_ci lcd->buf[2] = data & 0xff; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case 2: 2008c2ecf20Sopenharmony_ci lcd->buf[0] = (data >> 24) & 0xff; 2018c2ecf20Sopenharmony_ci lcd->buf[1] = (data >> 16) & 0xff; 2028c2ecf20Sopenharmony_ci lcd->buf[2] = (data >> 8) & 0xff; 2038c2ecf20Sopenharmony_ci lcd->buf[3] = data & 0xff; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci continue; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci x->len = nparams + 2; 2098c2ecf20Sopenharmony_ci err = spi_sync(lcd->spi_dev, &lcd->msg); 2108c2ecf20Sopenharmony_ci if (err) 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return err; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int tdo24m_adj_mode(struct tdo24m *lcd, int mode) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci switch (mode) { 2208c2ecf20Sopenharmony_ci case MODE_VGA: 2218c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m); 2228c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_panel_config); 2238c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_vga_transfer_tdo24m); 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci case MODE_QVGA: 2268c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m); 2278c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_panel_config); 2288c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_qvga_transfer); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci default: 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci lcd->mode = mode; 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int tdo35s_adj_mode(struct tdo24m *lcd, int mode) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci switch (mode) { 2418c2ecf20Sopenharmony_ci case MODE_VGA: 2428c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s); 2438c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_panel_config); 2448c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_vga_transfer_tdo35s); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case MODE_QVGA: 2478c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s); 2488c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_panel_config); 2498c2ecf20Sopenharmony_ci tdo24m_writes(lcd, lcd_qvga_transfer); 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci lcd->mode = mode; 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int tdo24m_power_on(struct tdo24m *lcd) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci int err; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci err = tdo24m_writes(lcd, lcd_panel_on); 2648c2ecf20Sopenharmony_ci if (err) 2658c2ecf20Sopenharmony_ci goto out; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci err = tdo24m_writes(lcd, lcd_panel_reset); 2688c2ecf20Sopenharmony_ci if (err) 2698c2ecf20Sopenharmony_ci goto out; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci err = lcd->adj_mode(lcd, lcd->mode); 2728c2ecf20Sopenharmony_ciout: 2738c2ecf20Sopenharmony_ci return err; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int tdo24m_power_off(struct tdo24m *lcd) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci return tdo24m_writes(lcd, lcd_panel_off); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int tdo24m_power(struct tdo24m *lcd, int power) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci int ret = 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) 2868c2ecf20Sopenharmony_ci ret = tdo24m_power_on(lcd); 2878c2ecf20Sopenharmony_ci else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) 2888c2ecf20Sopenharmony_ci ret = tdo24m_power_off(lcd); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!ret) 2918c2ecf20Sopenharmony_ci lcd->power = power; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int tdo24m_set_power(struct lcd_device *ld, int power) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct tdo24m *lcd = lcd_get_data(ld); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return tdo24m_power(lcd, power); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int tdo24m_get_power(struct lcd_device *ld) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct tdo24m *lcd = lcd_get_data(ld); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return lcd->power; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct tdo24m *lcd = lcd_get_data(ld); 3148c2ecf20Sopenharmony_ci int mode = MODE_QVGA; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (m->xres == 640 || m->xres == 480) 3178c2ecf20Sopenharmony_ci mode = MODE_VGA; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (lcd->mode == mode) 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return lcd->adj_mode(lcd, mode); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic struct lcd_ops tdo24m_ops = { 3268c2ecf20Sopenharmony_ci .get_power = tdo24m_get_power, 3278c2ecf20Sopenharmony_ci .set_power = tdo24m_set_power, 3288c2ecf20Sopenharmony_ci .set_mode = tdo24m_set_mode, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int tdo24m_probe(struct spi_device *spi) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct tdo24m *lcd; 3348c2ecf20Sopenharmony_ci struct spi_message *m; 3358c2ecf20Sopenharmony_ci struct spi_transfer *x; 3368c2ecf20Sopenharmony_ci struct tdo24m_platform_data *pdata; 3378c2ecf20Sopenharmony_ci enum tdo24m_model model; 3388c2ecf20Sopenharmony_ci int err; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&spi->dev); 3418c2ecf20Sopenharmony_ci if (pdata) 3428c2ecf20Sopenharmony_ci model = pdata->model; 3438c2ecf20Sopenharmony_ci else 3448c2ecf20Sopenharmony_ci model = TDO24M; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci spi->bits_per_word = 8; 3478c2ecf20Sopenharmony_ci spi->mode = SPI_MODE_3; 3488c2ecf20Sopenharmony_ci err = spi_setup(spi); 3498c2ecf20Sopenharmony_ci if (err) 3508c2ecf20Sopenharmony_ci return err; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL); 3538c2ecf20Sopenharmony_ci if (!lcd) 3548c2ecf20Sopenharmony_ci return -ENOMEM; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci lcd->spi_dev = spi; 3578c2ecf20Sopenharmony_ci lcd->power = FB_BLANK_POWERDOWN; 3588c2ecf20Sopenharmony_ci lcd->mode = MODE_VGA; /* default to VGA */ 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL); 3618c2ecf20Sopenharmony_ci if (lcd->buf == NULL) 3628c2ecf20Sopenharmony_ci return -ENOMEM; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci m = &lcd->msg; 3658c2ecf20Sopenharmony_ci x = &lcd->xfer; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci spi_message_init(m); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci x->cs_change = 0; 3708c2ecf20Sopenharmony_ci x->tx_buf = &lcd->buf[0]; 3718c2ecf20Sopenharmony_ci spi_message_add_tail(x, m); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci switch (model) { 3748c2ecf20Sopenharmony_ci case TDO24M: 3758c2ecf20Sopenharmony_ci lcd->color_invert = 1; 3768c2ecf20Sopenharmony_ci lcd->adj_mode = tdo24m_adj_mode; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case TDO35S: 3798c2ecf20Sopenharmony_ci lcd->adj_mode = tdo35s_adj_mode; 3808c2ecf20Sopenharmony_ci lcd->color_invert = 0; 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci default: 3838c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Unsupported model"); 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "tdo24m", &spi->dev, 3888c2ecf20Sopenharmony_ci lcd, &tdo24m_ops); 3898c2ecf20Sopenharmony_ci if (IS_ERR(lcd->lcd_dev)) 3908c2ecf20Sopenharmony_ci return PTR_ERR(lcd->lcd_dev); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci spi_set_drvdata(spi, lcd); 3938c2ecf20Sopenharmony_ci err = tdo24m_power(lcd, FB_BLANK_UNBLANK); 3948c2ecf20Sopenharmony_ci if (err) 3958c2ecf20Sopenharmony_ci return err; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int tdo24m_remove(struct spi_device *spi) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct tdo24m *lcd = spi_get_drvdata(spi); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci tdo24m_power(lcd, FB_BLANK_POWERDOWN); 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4098c2ecf20Sopenharmony_cistatic int tdo24m_suspend(struct device *dev) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct tdo24m *lcd = dev_get_drvdata(dev); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return tdo24m_power(lcd, FB_BLANK_POWERDOWN); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int tdo24m_resume(struct device *dev) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct tdo24m *lcd = dev_get_drvdata(dev); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return tdo24m_power(lcd, FB_BLANK_UNBLANK); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci#endif 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tdo24m_pm_ops, tdo24m_suspend, tdo24m_resume); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/* Power down all displays on reboot, poweroff or halt */ 4278c2ecf20Sopenharmony_cistatic void tdo24m_shutdown(struct spi_device *spi) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct tdo24m *lcd = spi_get_drvdata(spi); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci tdo24m_power(lcd, FB_BLANK_POWERDOWN); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic struct spi_driver tdo24m_driver = { 4358c2ecf20Sopenharmony_ci .driver = { 4368c2ecf20Sopenharmony_ci .name = "tdo24m", 4378c2ecf20Sopenharmony_ci .pm = &tdo24m_pm_ops, 4388c2ecf20Sopenharmony_ci }, 4398c2ecf20Sopenharmony_ci .probe = tdo24m_probe, 4408c2ecf20Sopenharmony_ci .remove = tdo24m_remove, 4418c2ecf20Sopenharmony_ci .shutdown = tdo24m_shutdown, 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cimodule_spi_driver(tdo24m_driver); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); 4478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); 4488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4498c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:tdo24m"); 450