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