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/gpio.h>
23
#include <linux/fb.h>
24
#include <linux/lcd.h>
25
#include <linux/spi/spi.h>
26
#include <linux/spi/corgi_lcd.h>
27
#include <asm/mach/sharpsl_param.h>
28
29
#define POWER_IS_ON(pwr)	((pwr) <= FB_BLANK_NORMAL)
30
31
/* Register Addresses */
32
#define RESCTL_ADRS     0x00
33
#define PHACTRL_ADRS    0x01
34
#define DUTYCTRL_ADRS   0x02
35
#define POWERREG0_ADRS  0x03
36
#define POWERREG1_ADRS  0x04
37
#define GPOR3_ADRS      0x05
38
#define PICTRL_ADRS     0x06
39
#define POLCTRL_ADRS    0x07
40
41
/* Register Bit Definitions */
42
#define RESCTL_QVGA     0x01
43
#define RESCTL_VGA      0x00
44
45
#define POWER1_VW_ON    0x01  /* VW Supply FET ON */
46
#define POWER1_GVSS_ON  0x02  /* GVSS(-8V) Power Supply ON */
47
#define POWER1_VDD_ON   0x04  /* VDD(8V),SVSS(-4V) Power Supply ON */
48
49
#define POWER1_VW_OFF   0x00  /* VW Supply FET OFF */
50
#define POWER1_GVSS_OFF 0x00  /* GVSS(-8V) Power Supply OFF */
51
#define POWER1_VDD_OFF  0x00  /* VDD(8V),SVSS(-4V) Power Supply OFF */
52
53
#define POWER0_COM_DCLK 0x01  /* COM Voltage DC Bias DAC Serial Data Clock */
54
#define POWER0_COM_DOUT 0x02  /* COM Voltage DC Bias DAC Serial Data Out */
55
#define POWER0_DAC_ON   0x04  /* DAC Power Supply ON */
56
#define POWER0_COM_ON   0x08  /* COM Power Supply ON */
57
#define POWER0_VCC5_ON  0x10  /* VCC5 Power Supply ON */
58
59
#define POWER0_DAC_OFF  0x00  /* DAC Power Supply OFF */
60
#define POWER0_COM_OFF  0x00  /* COM Power Supply OFF */
61
#define POWER0_VCC5_OFF 0x00  /* VCC5 Power Supply OFF */
62
63
#define PICTRL_INIT_STATE      0x01
64
#define PICTRL_INIOFF          0x02
65
#define PICTRL_POWER_DOWN      0x04
66
#define PICTRL_COM_SIGNAL_OFF  0x08
67
#define PICTRL_DAC_SIGNAL_OFF  0x10
68
69
#define POLCTRL_SYNC_POL_FALL  0x01
70
#define POLCTRL_EN_POL_FALL    0x02
71
#define POLCTRL_DATA_POL_FALL  0x04
72
#define POLCTRL_SYNC_ACT_H     0x08
73
#define POLCTRL_EN_ACT_L       0x10
74
75
#define POLCTRL_SYNC_POL_RISE  0x00
76
#define POLCTRL_EN_POL_RISE    0x00
77
#define POLCTRL_DATA_POL_RISE  0x00
78
#define POLCTRL_SYNC_ACT_L     0x00
79
#define POLCTRL_EN_ACT_H       0x00
80
81
#define PHACTRL_PHASE_MANUAL   0x01
82
#define DEFAULT_PHAD_QVGA     (9)
83
#define DEFAULT_COMADJ        (125)
84
85
struct corgi_lcd {
86
	struct spi_device	*spi_dev;
87
	struct lcd_device	*lcd_dev;
88
	struct backlight_device	*bl_dev;
89
90
	int	limit_mask;
91
	int	intensity;
92
	int	power;
93
	int	mode;
94
	char	buf[2];
95
96
	int	gpio_backlight_on;
97
	int	gpio_backlight_cont;
98
	int	gpio_backlight_cont_inverted;
99
100
	void (*kick_battery)(void);
101
};
102
103
static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val);
104
105
static struct corgi_lcd *the_corgi_lcd;
106
static unsigned long corgibl_flags;
107
#define CORGIBL_SUSPENDED     0x01
108
#define CORGIBL_BATTLOW       0x02
109
110
/*
111
 * This is only a psuedo I2C interface. We can't use the standard kernel
112
 * routines as the interface is write only. We just assume the data is acked...
113
 */
114
static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data)
115
{
116
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data);
117
	udelay(10);
118
}
119
120
static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data)
121
{
122
	lcdtg_ssp_i2c_send(lcd, data);
123
	lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK);
124
	lcdtg_ssp_i2c_send(lcd, data);
125
}
126
127
static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base)
128
{
129
	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
130
	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
131
	lcdtg_ssp_i2c_send(lcd, base);
132
}
133
134
static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base)
135
{
136
	lcdtg_ssp_i2c_send(lcd, base);
137
	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
138
	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
139
}
140
141
static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd,
142
				uint8_t base, uint8_t data)
143
{
144
	int i;
145
	for (i = 0; i < 8; i++) {
146
		if (data & 0x80)
147
			lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
148
		else
149
			lcdtg_i2c_send_bit(lcd, base);
150
		data <<= 1;
151
	}
152
}
153
154
static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base)
155
{
156
	lcdtg_i2c_send_bit(lcd, base);
157
}
158
159
static void lcdtg_set_common_voltage(struct corgi_lcd *lcd,
160
				     uint8_t base_data, uint8_t data)
161
{
162
	/* Set Common Voltage to M62332FP via I2C */
163
	lcdtg_i2c_send_start(lcd, base_data);
164
	lcdtg_i2c_send_byte(lcd, base_data, 0x9c);
165
	lcdtg_i2c_wait_ack(lcd, base_data);
166
	lcdtg_i2c_send_byte(lcd, base_data, 0x00);
167
	lcdtg_i2c_wait_ack(lcd, base_data);
168
	lcdtg_i2c_send_byte(lcd, base_data, data);
169
	lcdtg_i2c_wait_ack(lcd, base_data);
170
	lcdtg_i2c_send_stop(lcd, base_data);
171
}
172
173
static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data)
174
{
175
	struct spi_message msg;
176
	struct spi_transfer xfer = {
177
		.len		= 1,
178
		.cs_change	= 1,
179
		.tx_buf		= lcd->buf,
180
	};
181
182
	lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
183
	spi_message_init(&msg);
184
	spi_message_add_tail(&xfer, &msg);
185
186
	return spi_sync(lcd->spi_dev, &msg);
187
}
188
189
/* Set Phase Adjust */
190
static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode)
191
{
192
	int adj;
193
194
	switch(mode) {
195
	case CORGI_LCD_MODE_VGA:
196
		/* Setting for VGA */
197
		adj = sharpsl_param.phadadj;
198
		adj = (adj < 0) ? PHACTRL_PHASE_MANUAL :
199
				  PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1);
200
		break;
201
	case CORGI_LCD_MODE_QVGA:
202
	default:
203
		/* Setting for QVGA */
204
		adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL;
205
		break;
206
	}
207
208
	corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj);
209
}
210
211
static void corgi_lcd_power_on(struct corgi_lcd *lcd)
212
{
213
	int comadj;
214
215
	/* Initialize Internal Logic & Port */
216
	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
217
			PICTRL_POWER_DOWN | PICTRL_INIOFF |
218
			PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF |
219
			PICTRL_DAC_SIGNAL_OFF);
220
221
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
222
			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF |
223
			POWER0_COM_OFF | POWER0_VCC5_OFF);
224
225
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
226
			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
227
228
	/* VDD(+8V), SVSS(-4V) ON */
229
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
230
			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
231
	mdelay(3);
232
233
	/* DAC ON */
234
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
235
			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
236
			POWER0_COM_OFF | POWER0_VCC5_OFF);
237
238
	/* INIB = H, INI = L  */
239
	/* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
240
	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
241
			PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF);
242
243
	/* Set Common Voltage */
244
	comadj = sharpsl_param.comadj;
245
	if (comadj < 0)
246
		comadj = DEFAULT_COMADJ;
247
248
	lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
249
				 POWER0_VCC5_OFF, comadj);
250
251
	/* VCC5 ON, DAC ON */
252
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
253
			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
254
			POWER0_COM_OFF | POWER0_VCC5_ON);
255
256
	/* GVSS(-8V) ON, VDD ON */
257
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
258
			POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
259
	mdelay(2);
260
261
	/* COM SIGNAL ON (PICTL[3] = L) */
262
	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE);
263
264
	/* COM ON, DAC ON, VCC5_ON */
265
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
266
			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
267
			POWER0_COM_ON | POWER0_VCC5_ON);
268
269
	/* VW ON, GVSS ON, VDD ON */
270
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
271
			POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON);
272
273
	/* Signals output enable */
274
	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0);
275
276
	/* Set Phase Adjust */
277
	lcdtg_set_phadadj(lcd, lcd->mode);
278
279
	/* Initialize for Input Signals from ATI */
280
	corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS,
281
			POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE |
282
			POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L |
283
			POLCTRL_EN_ACT_H);
284
	udelay(1000);
285
286
	switch (lcd->mode) {
287
	case CORGI_LCD_MODE_VGA:
288
		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
289
		break;
290
	case CORGI_LCD_MODE_QVGA:
291
	default:
292
		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
293
		break;
294
	}
295
}
296
297
static void corgi_lcd_power_off(struct corgi_lcd *lcd)
298
{
299
	/* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
300
	msleep(34);
301
302
	/* (1)VW OFF */
303
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
304
			POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
305
306
	/* (2)COM OFF */
307
	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
308
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
309
			POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
310
311
	/* (3)Set Common Voltage Bias 0V */
312
	lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
313
			POWER0_VCC5_ON, 0);
314
315
	/* (4)GVSS OFF */
316
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
317
			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
318
319
	/* (5)VCC5 OFF */
320
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
321
			POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
322
323
	/* (6)Set PDWN, INIOFF, DACOFF */
324
	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
325
			PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
326
			PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
327
328
	/* (7)DAC OFF */
329
	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
330
			POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
331
332
	/* (8)VDD OFF */
333
	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
334
			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
335
}
336
337
static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
338
{
339
	struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev);
340
	int mode = CORGI_LCD_MODE_QVGA;
341
342
	if (m->xres == 640 || m->xres == 480)
343
		mode = CORGI_LCD_MODE_VGA;
344
345
	if (lcd->mode == mode)
346
		return 0;
347
348
	lcdtg_set_phadadj(lcd, mode);
349
350
	switch (mode) {
351
	case CORGI_LCD_MODE_VGA:
352
		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
353
		break;
354
	case CORGI_LCD_MODE_QVGA:
355
	default:
356
		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
357
		break;
358
	}
359
360
	lcd->mode = mode;
361
	return 0;
362
}
363
364
static int corgi_lcd_set_power(struct lcd_device *ld, int power)
365
{
366
	struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev);
367
368
	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
369
		corgi_lcd_power_on(lcd);
370
371
	if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
372
		corgi_lcd_power_off(lcd);
373
374
	lcd->power = power;
375
	return 0;
376
}
377
378
static int corgi_lcd_get_power(struct lcd_device *ld)
379
{
380
	struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev);
381
382
	return lcd->power;
383
}
384
385
static struct lcd_ops corgi_lcd_ops = {
386
	.get_power	= corgi_lcd_get_power,
387
	.set_power	= corgi_lcd_set_power,
388
	.set_mode	= corgi_lcd_set_mode,
389
};
390
391
static int corgi_bl_get_intensity(struct backlight_device *bd)
392
{
393
	struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev);
394
395
	return lcd->intensity;
396
}
397
398
static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
399
{
400
	int cont;
401
402
	if (intensity > 0x10)
403
		intensity += 0x10;
404
405
	corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity);
406
407
	/* Bit 5 via GPIO_BACKLIGHT_CONT */
408
	cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted;
409
410
	if (gpio_is_valid(lcd->gpio_backlight_cont))
411
		gpio_set_value(lcd->gpio_backlight_cont, cont);
412
413
	if (gpio_is_valid(lcd->gpio_backlight_on))
414
		gpio_set_value(lcd->gpio_backlight_on, intensity);
415
416
	if (lcd->kick_battery)
417
		lcd->kick_battery();
418
419
	lcd->intensity = intensity;
420
	return 0;
421
}
422
423
static int corgi_bl_update_status(struct backlight_device *bd)
424
{
425
	struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev);
426
	int intensity = bd->props.brightness;
427
428
	if (bd->props.power != FB_BLANK_UNBLANK)
429
		intensity = 0;
430
431
	if (bd->props.fb_blank != FB_BLANK_UNBLANK)
432
		intensity = 0;
433
434
	if (corgibl_flags & CORGIBL_SUSPENDED)
435
		intensity = 0;
436
437
	if ((corgibl_flags & CORGIBL_BATTLOW) && intensity > lcd->limit_mask)
438
		intensity = lcd->limit_mask;
439
440
	return corgi_bl_set_intensity(lcd, intensity);
441
}
442
443
void corgi_lcd_limit_intensity(int limit)
444
{
445
	if (limit)
446
		corgibl_flags |= CORGIBL_BATTLOW;
447
	else
448
		corgibl_flags &= ~CORGIBL_BATTLOW;
449
450
	backlight_update_status(the_corgi_lcd->bl_dev);
451
}
452
EXPORT_SYMBOL(corgi_lcd_limit_intensity);
453
454
static struct backlight_ops corgi_bl_ops = {
455
	.get_brightness	= corgi_bl_get_intensity,
456
	.update_status  = corgi_bl_update_status,
457
};
458
459
#ifdef CONFIG_PM
460
static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state)
461
{
462
	struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev);
463
464
	corgibl_flags |= CORGIBL_SUSPENDED;
465
	corgi_bl_set_intensity(lcd, 0);
466
	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
467
	return 0;
468
}
469
470
static int corgi_lcd_resume(struct spi_device *spi)
471
{
472
	struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev);
473
474
	corgibl_flags &= ~CORGIBL_SUSPENDED;
475
	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
476
	backlight_update_status(lcd->bl_dev);
477
	return 0;
478
}
479
#else
480
#define corgi_lcd_suspend	NULL
481
#define corgi_lcd_resume	NULL
482
#endif
483
484
static int setup_gpio_backlight(struct corgi_lcd *lcd,
485
				struct corgi_lcd_platform_data *pdata)
486
{
487
	struct spi_device *spi = lcd->spi_dev;
488
	int err;
489
490
	lcd->gpio_backlight_on = -1;
491
	lcd->gpio_backlight_cont = -1;
492
493
	if (gpio_is_valid(pdata->gpio_backlight_on)) {
494
		err = gpio_request(pdata->gpio_backlight_on, "BL_ON");
495
		if (err) {
496
			dev_err(&spi->dev, "failed to request GPIO%d for "
497
				"backlight_on\n", pdata->gpio_backlight_on);
498
			return err;
499
		}
500
501
		lcd->gpio_backlight_on = pdata->gpio_backlight_on;
502
		gpio_direction_output(lcd->gpio_backlight_on, 0);
503
	}
504
505
	if (gpio_is_valid(pdata->gpio_backlight_cont)) {
506
		err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT");
507
		if (err) {
508
			dev_err(&spi->dev, "failed to request GPIO%d for "
509
				"backlight_cont\n", pdata->gpio_backlight_cont);
510
			goto err_free_backlight_on;
511
		}
512
513
		lcd->gpio_backlight_cont = pdata->gpio_backlight_cont;
514
515
		/* spitz and akita use both GPIOs for backlight, and
516
		 * have inverted polarity of GPIO_BACKLIGHT_CONT
517
		 */
518
		if (gpio_is_valid(lcd->gpio_backlight_on)) {
519
			lcd->gpio_backlight_cont_inverted = 1;
520
			gpio_direction_output(lcd->gpio_backlight_cont, 1);
521
		} else {
522
			lcd->gpio_backlight_cont_inverted = 0;
523
			gpio_direction_output(lcd->gpio_backlight_cont, 0);
524
		}
525
	}
526
	return 0;
527
528
err_free_backlight_on:
529
	if (gpio_is_valid(lcd->gpio_backlight_on))
530
		gpio_free(lcd->gpio_backlight_on);
531
	return err;
532
}
533
534
static int __devinit corgi_lcd_probe(struct spi_device *spi)
535
{
536
	struct corgi_lcd_platform_data *pdata = spi->dev.platform_data;
537
	struct corgi_lcd *lcd;
538
	int ret = 0;
539
540
	if (pdata == NULL) {
541
		dev_err(&spi->dev, "platform data not available\n");
542
		return -EINVAL;
543
	}
544
545
	lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL);
546
	if (!lcd) {
547
		dev_err(&spi->dev, "failed to allocate memory\n");
548
		return -ENOMEM;
549
	}
550
551
	lcd->spi_dev = spi;
552
553
	lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev,
554
					lcd, &corgi_lcd_ops);
555
	if (IS_ERR(lcd->lcd_dev)) {
556
		ret = PTR_ERR(lcd->lcd_dev);
557
		goto err_free_lcd;
558
	}
559
	lcd->power = FB_BLANK_POWERDOWN;
560
	lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
561
562
	lcd->bl_dev = backlight_device_register("corgi_bl", &spi->dev,
563
					lcd, &corgi_bl_ops);
564
	if (IS_ERR(lcd->bl_dev)) {
565
		ret = PTR_ERR(lcd->bl_dev);
566
		goto err_unregister_lcd;
567
	}
568
	lcd->bl_dev->props.max_brightness = pdata->max_intensity;
569
	lcd->bl_dev->props.brightness = pdata->default_intensity;
570
	lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
571
572
	ret = setup_gpio_backlight(lcd, pdata);
573
	if (ret)
574
		goto err_unregister_bl;
575
576
	lcd->kick_battery = pdata->kick_battery;
577
578
	dev_set_drvdata(&spi->dev, lcd);
579
	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
580
	backlight_update_status(lcd->bl_dev);
581
582
	lcd->limit_mask = pdata->limit_mask;
583
	the_corgi_lcd = lcd;
584
	return 0;
585
586
err_unregister_bl:
587
	backlight_device_unregister(lcd->bl_dev);
588
err_unregister_lcd:
589
	lcd_device_unregister(lcd->lcd_dev);
590
err_free_lcd:
591
	kfree(lcd);
592
	return ret;
593
}
594
595
static int __devexit corgi_lcd_remove(struct spi_device *spi)
596
{
597
	struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev);
598
599
	lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
600
	lcd->bl_dev->props.brightness = 0;
601
	backlight_update_status(lcd->bl_dev);
602
	backlight_device_unregister(lcd->bl_dev);
603
604
	if (gpio_is_valid(lcd->gpio_backlight_on))
605
		gpio_free(lcd->gpio_backlight_on);
606
607
	if (gpio_is_valid(lcd->gpio_backlight_cont))
608
		gpio_free(lcd->gpio_backlight_cont);
609
610
	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
611
	lcd_device_unregister(lcd->lcd_dev);
612
	kfree(lcd);
613
614
	return 0;
615
}
616
617
static struct spi_driver corgi_lcd_driver = {
618
	.driver		= {
619
		.name	= "corgi-lcd",
620
		.owner	= THIS_MODULE,
621
	},
622
	.probe		= corgi_lcd_probe,
623
	.remove		= __devexit_p(corgi_lcd_remove),
624
	.suspend	= corgi_lcd_suspend,
625
	.resume		= corgi_lcd_resume,
626
};
627
628
static int __init corgi_lcd_init(void)
629
{
630
	return spi_register_driver(&corgi_lcd_driver);
631
}
632
module_init(corgi_lcd_init);
633
634
static void __exit corgi_lcd_exit(void)
635
{
636
	spi_unregister_driver(&corgi_lcd_driver);
637
}
638
module_exit(corgi_lcd_exit);
639
640
MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
641
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
642
MODULE_LICENSE("GPL");
643
MODULE_ALIAS("spi:corgi-lcd");