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