aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorEric Miao <eric.miao@marvell.com>2008-08-28 16:21:44 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-09-23 17:04:30 -0400
commitb18250a8f66050bd2a52287cd543fb93100e8ee0 (patch)
tree20e0d142575d3147d2c58542d5bd8ed377e5f3cb /drivers/video
parentfaa312da9cd0b044bdc84483162c6ee10b9c83c0 (diff)
lcd: add SPI-based LCD and backlight driver for SHARP corgi/spitz
The driver is based on different source files including corgi_ssp.c, corgi_lcd.c and corgi_bl.c, previously authored by Richard Purdie and many others. The LCD and Backlight device actually share the same SPI device, so they are made into this single driver. Signed-off-by: Eric Miao <eric.miao@marvell.com> Cc: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/backlight/Kconfig7
-rw-r--r--drivers/video/backlight/Makefile1
-rw-r--r--drivers/video/backlight/corgi_lcd.c541
3 files changed, 549 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 452b770d8cc9..f5406cd78e28 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -24,6 +24,13 @@ config LCD_CLASS_DEVICE
24 To have support for your specific LCD panel you will have to 24 To have support for your specific LCD panel you will have to
25 select the proper drivers which depend on this option. 25 select the proper drivers which depend on this option.
26 26
27config LCD_CORGI
28 tristate "LCD Panel support for SHARP corgi/spitz model"
29 depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL
30 help
31 Say y here to support the LCD panels usually found on SHARP
32 corgi (C7x0) and spitz (Cxx00) models.
33
27config LCD_LTV350QV 34config LCD_LTV350QV
28 tristate "Samsung LTV350QV LCD Panel" 35 tristate "Samsung LTV350QV LCD Panel"
29 depends on LCD_CLASS_DEVICE && SPI_MASTER 36 depends on LCD_CLASS_DEVICE && SPI_MASTER
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index b405aace803f..cf12d58392e0 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -1,6 +1,7 @@
1# Backlight & LCD drivers 1# Backlight & LCD drivers
2 2
3obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o 3obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
4obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o
4obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o 5obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
5obj-$(CONFIG_LCD_ILI9320) += ili9320.o 6obj-$(CONFIG_LCD_ILI9320) += ili9320.o
6obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o 7obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
new file mode 100644
index 000000000000..bf69e50d262e
--- /dev/null
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -0,0 +1,541 @@
1/*
2 * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models)
3 *
4 * Copyright (c) 2004-2006 Richard Purdie
5 *
6 * Based on Sharp's 2.4 Backlight Driver
7 *
8 * Copyright (c) 2008 Marvell International Ltd.
9 * Converted to SPI device based LCD/Backlight device driver
10 * by Eric Miao <eric.miao@marvell.com>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/delay.h>
22#include <linux/fb.h>
23#include <linux/lcd.h>
24#include <linux/spi/spi.h>
25#include <linux/spi/corgi_lcd.h>
26#include <asm/mach/sharpsl_param.h>
27
28#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
29
30/* Register Addresses */
31#define RESCTL_ADRS 0x00
32#define PHACTRL_ADRS 0x01
33#define DUTYCTRL_ADRS 0x02
34#define POWERREG0_ADRS 0x03
35#define POWERREG1_ADRS 0x04
36#define GPOR3_ADRS 0x05
37#define PICTRL_ADRS 0x06
38#define POLCTRL_ADRS 0x07
39
40/* Register Bit Definitions */
41#define RESCTL_QVGA 0x01
42#define RESCTL_VGA 0x00
43
44#define POWER1_VW_ON 0x01 /* VW Supply FET ON */
45#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */
46#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */
47
48#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */
49#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */
50#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */
51
52#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */
53#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */
54#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */
55#define POWER0_COM_ON 0x08 /* COM Power Supply ON */
56#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */
57
58#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */
59#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */
60#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */
61
62#define PICTRL_INIT_STATE 0x01
63#define PICTRL_INIOFF 0x02
64#define PICTRL_POWER_DOWN 0x04
65#define PICTRL_COM_SIGNAL_OFF 0x08
66#define PICTRL_DAC_SIGNAL_OFF 0x10
67
68#define POLCTRL_SYNC_POL_FALL 0x01
69#define POLCTRL_EN_POL_FALL 0x02
70#define POLCTRL_DATA_POL_FALL 0x04
71#define POLCTRL_SYNC_ACT_H 0x08
72#define POLCTRL_EN_ACT_L 0x10
73
74#define POLCTRL_SYNC_POL_RISE 0x00
75#define POLCTRL_EN_POL_RISE 0x00
76#define POLCTRL_DATA_POL_RISE 0x00
77#define POLCTRL_SYNC_ACT_L 0x00
78#define POLCTRL_EN_ACT_H 0x00
79
80#define PHACTRL_PHASE_MANUAL 0x01
81#define DEFAULT_PHAD_QVGA (9)
82#define DEFAULT_COMADJ (125)
83
84struct corgi_lcd {
85 struct spi_device *spi_dev;
86 struct lcd_device *lcd_dev;
87 struct backlight_device *bl_dev;
88
89 int intensity;
90 int power;
91 int mode;
92 char buf[2];
93
94 void (*notify)(int intensity);
95 void (*kick_battery)(void);
96};
97
98static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val);
99
100/*
101 * This is only a psuedo I2C interface. We can't use the standard kernel
102 * routines as the interface is write only. We just assume the data is acked...
103 */
104static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data)
105{
106 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data);
107 udelay(10);
108}
109
110static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data)
111{
112 lcdtg_ssp_i2c_send(lcd, data);
113 lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK);
114 lcdtg_ssp_i2c_send(lcd, data);
115}
116
117static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base)
118{
119 lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
120 lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
121 lcdtg_ssp_i2c_send(lcd, base);
122}
123
124static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base)
125{
126 lcdtg_ssp_i2c_send(lcd, base);
127 lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
128 lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
129}
130
131static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd,
132 uint8_t base, uint8_t data)
133{
134 int i;
135 for (i = 0; i < 8; i++) {
136 if (data & 0x80)
137 lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
138 else
139 lcdtg_i2c_send_bit(lcd, base);
140 data <<= 1;
141 }
142}
143
144static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base)
145{
146 lcdtg_i2c_send_bit(lcd, base);
147}
148
149static void lcdtg_set_common_voltage(struct corgi_lcd *lcd,
150 uint8_t base_data, uint8_t data)
151{
152 /* Set Common Voltage to M62332FP via I2C */
153 lcdtg_i2c_send_start(lcd, base_data);
154 lcdtg_i2c_send_byte(lcd, base_data, 0x9c);
155 lcdtg_i2c_wait_ack(lcd, base_data);
156 lcdtg_i2c_send_byte(lcd, base_data, 0x00);
157 lcdtg_i2c_wait_ack(lcd, base_data);
158 lcdtg_i2c_send_byte(lcd, base_data, data);
159 lcdtg_i2c_wait_ack(lcd, base_data);
160 lcdtg_i2c_send_stop(lcd, base_data);
161}
162
163static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data)
164{
165 struct spi_message msg;
166 struct spi_transfer xfer = {
167 .len = 1,
168 .cs_change = 1,
169 .tx_buf = lcd->buf,
170 };
171
172 lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
173 spi_message_init(&msg);
174 spi_message_add_tail(&xfer, &msg);
175
176 return spi_sync(lcd->spi_dev, &msg);
177}
178
179/* Set Phase Adjust */
180static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode)
181{
182 int adj;
183
184 switch(mode) {
185 case CORGI_LCD_MODE_VGA:
186 /* Setting for VGA */
187 adj = sharpsl_param.phadadj;
188 adj = (adj < 0) ? PHACTRL_PHASE_MANUAL :
189 PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1);
190 break;
191 case CORGI_LCD_MODE_QVGA:
192 default:
193 /* Setting for QVGA */
194 adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL;
195 break;
196 }
197
198 corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj);
199}
200
201static void corgi_lcd_power_on(struct corgi_lcd *lcd)
202{
203 int comadj;
204
205 /* Initialize Internal Logic & Port */
206 corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
207 PICTRL_POWER_DOWN | PICTRL_INIOFF |
208 PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF |
209 PICTRL_DAC_SIGNAL_OFF);
210
211 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
212 POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF |
213 POWER0_COM_OFF | POWER0_VCC5_OFF);
214
215 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
216 POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
217
218 /* VDD(+8V), SVSS(-4V) ON */
219 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
220 POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
221 mdelay(3);
222
223 /* DAC ON */
224 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
225 POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
226 POWER0_COM_OFF | POWER0_VCC5_OFF);
227
228 /* INIB = H, INI = L */
229 /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
230 corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
231 PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF);
232
233 /* Set Common Voltage */
234 comadj = sharpsl_param.comadj;
235 if (comadj < 0)
236 comadj = DEFAULT_COMADJ;
237
238 lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
239 POWER0_VCC5_OFF, comadj);
240
241 /* VCC5 ON, DAC ON */
242 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
243 POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
244 POWER0_COM_OFF | POWER0_VCC5_ON);
245
246 /* GVSS(-8V) ON, VDD ON */
247 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
248 POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
249 mdelay(2);
250
251 /* COM SIGNAL ON (PICTL[3] = L) */
252 corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE);
253
254 /* COM ON, DAC ON, VCC5_ON */
255 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
256 POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
257 POWER0_COM_ON | POWER0_VCC5_ON);
258
259 /* VW ON, GVSS ON, VDD ON */
260 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
261 POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON);
262
263 /* Signals output enable */
264 corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0);
265
266 /* Set Phase Adjust */
267 lcdtg_set_phadadj(lcd, lcd->mode);
268
269 /* Initialize for Input Signals from ATI */
270 corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS,
271 POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE |
272 POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L |
273 POLCTRL_EN_ACT_H);
274 udelay(1000);
275
276 switch (lcd->mode) {
277 case CORGI_LCD_MODE_VGA:
278 corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
279 break;
280 case CORGI_LCD_MODE_QVGA:
281 default:
282 corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
283 break;
284 }
285}
286
287static void corgi_lcd_power_off(struct corgi_lcd *lcd)
288{
289 /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
290 msleep(34);
291
292 /* (1)VW OFF */
293 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
294 POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
295
296 /* (2)COM OFF */
297 corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
298 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
299 POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
300
301 /* (3)Set Common Voltage Bias 0V */
302 lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
303 POWER0_VCC5_ON, 0);
304
305 /* (4)GVSS OFF */
306 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
307 POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
308
309 /* (5)VCC5 OFF */
310 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
311 POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
312
313 /* (6)Set PDWN, INIOFF, DACOFF */
314 corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
315 PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
316 PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
317
318 /* (7)DAC OFF */
319 corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
320 POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
321
322 /* (8)VDD OFF */
323 corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
324 POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
325}
326
327static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
328{
329 struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev);
330 int mode = CORGI_LCD_MODE_QVGA;
331
332 if (m->xres == 640 || m->xres == 480)
333 mode = CORGI_LCD_MODE_VGA;
334
335 if (lcd->mode == mode)
336 return 0;
337
338 lcdtg_set_phadadj(lcd, mode);
339
340 switch (mode) {
341 case CORGI_LCD_MODE_VGA:
342 corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
343 break;
344 case CORGI_LCD_MODE_QVGA:
345 default:
346 corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
347 break;
348 }
349
350 lcd->mode = mode;
351 return 0;
352}
353
354static int corgi_lcd_set_power(struct lcd_device *ld, int power)
355{
356 struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev);
357
358 if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
359 corgi_lcd_power_on(lcd);
360
361 if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
362 corgi_lcd_power_off(lcd);
363
364 lcd->power = power;
365 return 0;
366}
367
368static int corgi_lcd_get_power(struct lcd_device *ld)
369{
370 struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev);
371
372 return lcd->power;
373}
374
375static struct lcd_ops corgi_lcd_ops = {
376 .get_power = corgi_lcd_get_power,
377 .set_power = corgi_lcd_set_power,
378 .set_mode = corgi_lcd_set_mode,
379};
380
381static int corgi_bl_get_intensity(struct backlight_device *bd)
382{
383 struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev);
384
385 return lcd->intensity;
386}
387
388static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
389{
390 if (intensity > 0x10)
391 intensity += 0x10;
392
393 corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity);
394 lcd->intensity = intensity;
395
396 if (lcd->notify)
397 lcd->notify(intensity);
398
399 if (lcd->kick_battery)
400 lcd->kick_battery();
401
402 return 0;
403}
404
405static int corgi_bl_update_status(struct backlight_device *bd)
406{
407 struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev);
408 int intensity = bd->props.brightness;
409
410 if (bd->props.power != FB_BLANK_UNBLANK)
411 intensity = 0;
412
413 if (bd->props.fb_blank != FB_BLANK_UNBLANK)
414 intensity = 0;
415
416 return corgi_bl_set_intensity(lcd, intensity);
417}
418
419static struct backlight_ops corgi_bl_ops = {
420 .get_brightness = corgi_bl_get_intensity,
421 .update_status = corgi_bl_update_status,
422};
423
424#ifdef CONFIG_PM
425static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state)
426{
427 struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev);
428
429 corgi_bl_set_intensity(lcd, 0);
430 corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
431 return 0;
432}
433
434static int corgi_lcd_resume(struct spi_device *spi)
435{
436 struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev);
437
438 corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
439 backlight_update_status(lcd->bl_dev);
440 return 0;
441}
442#else
443#define corgi_lcd_suspend NULL
444#define corgi_lcd_resume NULL
445#endif
446
447static int __devinit corgi_lcd_probe(struct spi_device *spi)
448{
449 struct corgi_lcd_platform_data *pdata = spi->dev.platform_data;
450 struct corgi_lcd *lcd;
451 int ret = 0;
452
453 if (pdata == NULL) {
454 dev_err(&spi->dev, "platform data not available\n");
455 return -EINVAL;
456 }
457
458 lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL);
459 if (!lcd) {
460 dev_err(&spi->dev, "failed to allocate memory\n");
461 return -ENOMEM;
462 }
463
464 lcd->spi_dev = spi;
465
466 lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev,
467 lcd, &corgi_lcd_ops);
468 if (IS_ERR(lcd->lcd_dev)) {
469 ret = PTR_ERR(lcd->lcd_dev);
470 goto err_free_lcd;
471 }
472 lcd->power = FB_BLANK_POWERDOWN;
473 lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
474
475 lcd->bl_dev = backlight_device_register("corgi_bl", &spi->dev,
476 lcd, &corgi_bl_ops);
477 if (IS_ERR(lcd->bl_dev)) {
478 ret = PTR_ERR(lcd->bl_dev);
479 goto err_unregister_lcd;
480 }
481 lcd->bl_dev->props.max_brightness = pdata->max_intensity;
482 lcd->bl_dev->props.brightness = pdata->default_intensity;
483 lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
484
485 lcd->notify = pdata->notify;
486 lcd->kick_battery = pdata->kick_battery;
487
488 dev_set_drvdata(&spi->dev, lcd);
489 corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
490 backlight_update_status(lcd->bl_dev);
491 return 0;
492
493err_unregister_lcd:
494 lcd_device_unregister(lcd->lcd_dev);
495err_free_lcd:
496 kfree(lcd);
497 return ret;
498}
499
500static int __devexit corgi_lcd_remove(struct spi_device *spi)
501{
502 struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev);
503
504 lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
505 lcd->bl_dev->props.brightness = 0;
506 backlight_update_status(lcd->bl_dev);
507 backlight_device_unregister(lcd->bl_dev);
508
509 corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
510 lcd_device_unregister(lcd->lcd_dev);
511 kfree(lcd);
512
513 return 0;
514}
515
516static struct spi_driver corgi_lcd_driver = {
517 .driver = {
518 .name = "corgi-lcd",
519 .owner = THIS_MODULE,
520 },
521 .probe = corgi_lcd_probe,
522 .remove = __devexit_p(corgi_lcd_remove),
523 .suspend = corgi_lcd_suspend,
524 .resume = corgi_lcd_resume,
525};
526
527static int __init corgi_lcd_init(void)
528{
529 return spi_register_driver(&corgi_lcd_driver);
530}
531module_init(corgi_lcd_init);
532
533static void __exit corgi_lcd_exit(void)
534{
535 spi_unregister_driver(&corgi_lcd_driver);
536}
537module_exit(corgi_lcd_exit);
538
539MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
540MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
541MODULE_LICENSE("GPL");