1 // SPDX-License-Identifier: GPL-2.0-only
3 * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models)
5 * Copyright (c) 2004-2006 Richard Purdie
7 * Based on Sharp's 2.4 Backlight Driver
9 * Copyright (c) 2008 Marvell International Ltd.
10 * Converted to SPI device based LCD/Backlight device driver
11 * by Eric Miao <eric.miao@marvell.com>
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
17 #include <linux/delay.h>
18 #include <linux/gpio.h>
20 #include <linux/lcd.h>
21 #include <linux/spi/spi.h>
22 #include <linux/spi/corgi_lcd.h>
23 #include <linux/slab.h>
24 #include <asm/mach/sharpsl_param.h>
26 #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
28 /* Register Addresses */
29 #define RESCTL_ADRS 0x00
30 #define PHACTRL_ADRS 0x01
31 #define DUTYCTRL_ADRS 0x02
32 #define POWERREG0_ADRS 0x03
33 #define POWERREG1_ADRS 0x04
34 #define GPOR3_ADRS 0x05
35 #define PICTRL_ADRS 0x06
36 #define POLCTRL_ADRS 0x07
38 /* Register Bit Definitions */
39 #define RESCTL_QVGA 0x01
40 #define RESCTL_VGA 0x00
42 #define POWER1_VW_ON 0x01 /* VW Supply FET ON */
43 #define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */
44 #define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */
46 #define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */
47 #define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */
48 #define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */
50 #define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */
51 #define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */
52 #define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */
53 #define POWER0_COM_ON 0x08 /* COM Power Supply ON */
54 #define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */
56 #define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */
57 #define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */
58 #define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */
60 #define PICTRL_INIT_STATE 0x01
61 #define PICTRL_INIOFF 0x02
62 #define PICTRL_POWER_DOWN 0x04
63 #define PICTRL_COM_SIGNAL_OFF 0x08
64 #define PICTRL_DAC_SIGNAL_OFF 0x10
66 #define POLCTRL_SYNC_POL_FALL 0x01
67 #define POLCTRL_EN_POL_FALL 0x02
68 #define POLCTRL_DATA_POL_FALL 0x04
69 #define POLCTRL_SYNC_ACT_H 0x08
70 #define POLCTRL_EN_ACT_L 0x10
72 #define POLCTRL_SYNC_POL_RISE 0x00
73 #define POLCTRL_EN_POL_RISE 0x00
74 #define POLCTRL_DATA_POL_RISE 0x00
75 #define POLCTRL_SYNC_ACT_L 0x00
76 #define POLCTRL_EN_ACT_H 0x00
78 #define PHACTRL_PHASE_MANUAL 0x01
79 #define DEFAULT_PHAD_QVGA (9)
80 #define DEFAULT_COMADJ (125)
83 struct spi_device
*spi_dev
;
84 struct lcd_device
*lcd_dev
;
85 struct backlight_device
*bl_dev
;
93 int gpio_backlight_on
;
94 int gpio_backlight_cont
;
95 int gpio_backlight_cont_inverted
;
97 void (*kick_battery
)(void);
100 static int corgi_ssp_lcdtg_send(struct corgi_lcd
*lcd
, int reg
, uint8_t val
);
102 static struct corgi_lcd
*the_corgi_lcd
;
103 static unsigned long corgibl_flags
;
104 #define CORGIBL_SUSPENDED 0x01
105 #define CORGIBL_BATTLOW 0x02
108 * This is only a pseudo I2C interface. We can't use the standard kernel
109 * routines as the interface is write only. We just assume the data is acked...
111 static void lcdtg_ssp_i2c_send(struct corgi_lcd
*lcd
, uint8_t data
)
113 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
, data
);
117 static void lcdtg_i2c_send_bit(struct corgi_lcd
*lcd
, uint8_t data
)
119 lcdtg_ssp_i2c_send(lcd
, data
);
120 lcdtg_ssp_i2c_send(lcd
, data
| POWER0_COM_DCLK
);
121 lcdtg_ssp_i2c_send(lcd
, data
);
124 static void lcdtg_i2c_send_start(struct corgi_lcd
*lcd
, uint8_t base
)
126 lcdtg_ssp_i2c_send(lcd
, base
| POWER0_COM_DCLK
| POWER0_COM_DOUT
);
127 lcdtg_ssp_i2c_send(lcd
, base
| POWER0_COM_DCLK
);
128 lcdtg_ssp_i2c_send(lcd
, base
);
131 static void lcdtg_i2c_send_stop(struct corgi_lcd
*lcd
, uint8_t base
)
133 lcdtg_ssp_i2c_send(lcd
, base
);
134 lcdtg_ssp_i2c_send(lcd
, base
| POWER0_COM_DCLK
);
135 lcdtg_ssp_i2c_send(lcd
, base
| POWER0_COM_DCLK
| POWER0_COM_DOUT
);
138 static void lcdtg_i2c_send_byte(struct corgi_lcd
*lcd
,
139 uint8_t base
, uint8_t data
)
143 for (i
= 0; i
< 8; i
++) {
145 lcdtg_i2c_send_bit(lcd
, base
| POWER0_COM_DOUT
);
147 lcdtg_i2c_send_bit(lcd
, base
);
152 static void lcdtg_i2c_wait_ack(struct corgi_lcd
*lcd
, uint8_t base
)
154 lcdtg_i2c_send_bit(lcd
, base
);
157 static void lcdtg_set_common_voltage(struct corgi_lcd
*lcd
,
158 uint8_t base_data
, uint8_t data
)
160 /* Set Common Voltage to M62332FP via I2C */
161 lcdtg_i2c_send_start(lcd
, base_data
);
162 lcdtg_i2c_send_byte(lcd
, base_data
, 0x9c);
163 lcdtg_i2c_wait_ack(lcd
, base_data
);
164 lcdtg_i2c_send_byte(lcd
, base_data
, 0x00);
165 lcdtg_i2c_wait_ack(lcd
, base_data
);
166 lcdtg_i2c_send_byte(lcd
, base_data
, data
);
167 lcdtg_i2c_wait_ack(lcd
, base_data
);
168 lcdtg_i2c_send_stop(lcd
, base_data
);
171 static int corgi_ssp_lcdtg_send(struct corgi_lcd
*lcd
, int adrs
, uint8_t data
)
173 struct spi_message msg
;
174 struct spi_transfer xfer
= {
180 lcd
->buf
[0] = ((adrs
& 0x07) << 5) | (data
& 0x1f);
181 spi_message_init(&msg
);
182 spi_message_add_tail(&xfer
, &msg
);
184 return spi_sync(lcd
->spi_dev
, &msg
);
187 /* Set Phase Adjust */
188 static void lcdtg_set_phadadj(struct corgi_lcd
*lcd
, int mode
)
193 case CORGI_LCD_MODE_VGA
:
194 /* Setting for VGA */
195 adj
= sharpsl_param
.phadadj
;
196 adj
= (adj
< 0) ? PHACTRL_PHASE_MANUAL
:
197 PHACTRL_PHASE_MANUAL
| ((adj
& 0xf) << 1);
199 case CORGI_LCD_MODE_QVGA
:
201 /* Setting for QVGA */
202 adj
= (DEFAULT_PHAD_QVGA
<< 1) | PHACTRL_PHASE_MANUAL
;
206 corgi_ssp_lcdtg_send(lcd
, PHACTRL_ADRS
, adj
);
209 static void corgi_lcd_power_on(struct corgi_lcd
*lcd
)
213 /* Initialize Internal Logic & Port */
214 corgi_ssp_lcdtg_send(lcd
, PICTRL_ADRS
,
215 PICTRL_POWER_DOWN
| PICTRL_INIOFF
|
216 PICTRL_INIT_STATE
| PICTRL_COM_SIGNAL_OFF
|
217 PICTRL_DAC_SIGNAL_OFF
);
219 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
220 POWER0_COM_DCLK
| POWER0_COM_DOUT
| POWER0_DAC_OFF
|
221 POWER0_COM_OFF
| POWER0_VCC5_OFF
);
223 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
224 POWER1_VW_OFF
| POWER1_GVSS_OFF
| POWER1_VDD_OFF
);
226 /* VDD(+8V), SVSS(-4V) ON */
227 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
228 POWER1_VW_OFF
| POWER1_GVSS_OFF
| POWER1_VDD_ON
);
232 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
233 POWER0_COM_DCLK
| POWER0_COM_DOUT
| POWER0_DAC_ON
|
234 POWER0_COM_OFF
| POWER0_VCC5_OFF
);
236 /* INIB = H, INI = L */
237 /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
238 corgi_ssp_lcdtg_send(lcd
, PICTRL_ADRS
,
239 PICTRL_INIT_STATE
| PICTRL_COM_SIGNAL_OFF
);
241 /* Set Common Voltage */
242 comadj
= sharpsl_param
.comadj
;
244 comadj
= DEFAULT_COMADJ
;
246 lcdtg_set_common_voltage(lcd
, POWER0_DAC_ON
| POWER0_COM_OFF
|
247 POWER0_VCC5_OFF
, comadj
);
249 /* VCC5 ON, DAC ON */
250 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
251 POWER0_COM_DCLK
| POWER0_COM_DOUT
| POWER0_DAC_ON
|
252 POWER0_COM_OFF
| POWER0_VCC5_ON
);
254 /* GVSS(-8V) ON, VDD ON */
255 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
256 POWER1_VW_OFF
| POWER1_GVSS_ON
| POWER1_VDD_ON
);
259 /* COM SIGNAL ON (PICTL[3] = L) */
260 corgi_ssp_lcdtg_send(lcd
, PICTRL_ADRS
, PICTRL_INIT_STATE
);
262 /* COM ON, DAC ON, VCC5_ON */
263 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
264 POWER0_COM_DCLK
| POWER0_COM_DOUT
| POWER0_DAC_ON
|
265 POWER0_COM_ON
| POWER0_VCC5_ON
);
267 /* VW ON, GVSS ON, VDD ON */
268 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
269 POWER1_VW_ON
| POWER1_GVSS_ON
| POWER1_VDD_ON
);
271 /* Signals output enable */
272 corgi_ssp_lcdtg_send(lcd
, PICTRL_ADRS
, 0);
274 /* Set Phase Adjust */
275 lcdtg_set_phadadj(lcd
, lcd
->mode
);
277 /* Initialize for Input Signals from ATI */
278 corgi_ssp_lcdtg_send(lcd
, POLCTRL_ADRS
,
279 POLCTRL_SYNC_POL_RISE
| POLCTRL_EN_POL_RISE
|
280 POLCTRL_DATA_POL_RISE
| POLCTRL_SYNC_ACT_L
|
285 case CORGI_LCD_MODE_VGA
:
286 corgi_ssp_lcdtg_send(lcd
, RESCTL_ADRS
, RESCTL_VGA
);
288 case CORGI_LCD_MODE_QVGA
:
290 corgi_ssp_lcdtg_send(lcd
, RESCTL_ADRS
, RESCTL_QVGA
);
295 static void corgi_lcd_power_off(struct corgi_lcd
*lcd
)
297 /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
301 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
302 POWER1_VW_OFF
| POWER1_GVSS_ON
| POWER1_VDD_ON
);
305 corgi_ssp_lcdtg_send(lcd
, PICTRL_ADRS
, PICTRL_COM_SIGNAL_OFF
);
306 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
307 POWER0_DAC_ON
| POWER0_COM_OFF
| POWER0_VCC5_ON
);
309 /* (3)Set Common Voltage Bias 0V */
310 lcdtg_set_common_voltage(lcd
, POWER0_DAC_ON
| POWER0_COM_OFF
|
314 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
315 POWER1_VW_OFF
| POWER1_GVSS_OFF
| POWER1_VDD_ON
);
318 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
319 POWER0_DAC_ON
| POWER0_COM_OFF
| POWER0_VCC5_OFF
);
321 /* (6)Set PDWN, INIOFF, DACOFF */
322 corgi_ssp_lcdtg_send(lcd
, PICTRL_ADRS
,
323 PICTRL_INIOFF
| PICTRL_DAC_SIGNAL_OFF
|
324 PICTRL_POWER_DOWN
| PICTRL_COM_SIGNAL_OFF
);
327 corgi_ssp_lcdtg_send(lcd
, POWERREG0_ADRS
,
328 POWER0_DAC_OFF
| POWER0_COM_OFF
| POWER0_VCC5_OFF
);
331 corgi_ssp_lcdtg_send(lcd
, POWERREG1_ADRS
,
332 POWER1_VW_OFF
| POWER1_GVSS_OFF
| POWER1_VDD_OFF
);
335 static int corgi_lcd_set_mode(struct lcd_device
*ld
, struct fb_videomode
*m
)
337 struct corgi_lcd
*lcd
= lcd_get_data(ld
);
338 int mode
= CORGI_LCD_MODE_QVGA
;
340 if (m
->xres
== 640 || m
->xres
== 480)
341 mode
= CORGI_LCD_MODE_VGA
;
343 if (lcd
->mode
== mode
)
346 lcdtg_set_phadadj(lcd
, mode
);
349 case CORGI_LCD_MODE_VGA
:
350 corgi_ssp_lcdtg_send(lcd
, RESCTL_ADRS
, RESCTL_VGA
);
352 case CORGI_LCD_MODE_QVGA
:
354 corgi_ssp_lcdtg_send(lcd
, RESCTL_ADRS
, RESCTL_QVGA
);
362 static int corgi_lcd_set_power(struct lcd_device
*ld
, int power
)
364 struct corgi_lcd
*lcd
= lcd_get_data(ld
);
366 if (POWER_IS_ON(power
) && !POWER_IS_ON(lcd
->power
))
367 corgi_lcd_power_on(lcd
);
369 if (!POWER_IS_ON(power
) && POWER_IS_ON(lcd
->power
))
370 corgi_lcd_power_off(lcd
);
376 static int corgi_lcd_get_power(struct lcd_device
*ld
)
378 struct corgi_lcd
*lcd
= lcd_get_data(ld
);
383 static struct lcd_ops corgi_lcd_ops
= {
384 .get_power
= corgi_lcd_get_power
,
385 .set_power
= corgi_lcd_set_power
,
386 .set_mode
= corgi_lcd_set_mode
,
389 static int corgi_bl_get_intensity(struct backlight_device
*bd
)
391 struct corgi_lcd
*lcd
= bl_get_data(bd
);
393 return lcd
->intensity
;
396 static int corgi_bl_set_intensity(struct corgi_lcd
*lcd
, int intensity
)
400 if (intensity
> 0x10)
403 corgi_ssp_lcdtg_send(lcd
, DUTYCTRL_ADRS
, intensity
);
405 /* Bit 5 via GPIO_BACKLIGHT_CONT */
406 cont
= !!(intensity
& 0x20) ^ lcd
->gpio_backlight_cont_inverted
;
408 if (gpio_is_valid(lcd
->gpio_backlight_cont
))
409 gpio_set_value_cansleep(lcd
->gpio_backlight_cont
, cont
);
411 if (gpio_is_valid(lcd
->gpio_backlight_on
))
412 gpio_set_value_cansleep(lcd
->gpio_backlight_on
, intensity
);
414 if (lcd
->kick_battery
)
417 lcd
->intensity
= intensity
;
421 static int corgi_bl_update_status(struct backlight_device
*bd
)
423 struct corgi_lcd
*lcd
= bl_get_data(bd
);
424 int intensity
= bd
->props
.brightness
;
426 if (bd
->props
.power
!= FB_BLANK_UNBLANK
)
429 if (bd
->props
.fb_blank
!= FB_BLANK_UNBLANK
)
432 if (corgibl_flags
& CORGIBL_SUSPENDED
)
435 if ((corgibl_flags
& CORGIBL_BATTLOW
) && intensity
> lcd
->limit_mask
)
436 intensity
= lcd
->limit_mask
;
438 return corgi_bl_set_intensity(lcd
, intensity
);
441 void corgi_lcd_limit_intensity(int limit
)
444 corgibl_flags
|= CORGIBL_BATTLOW
;
446 corgibl_flags
&= ~CORGIBL_BATTLOW
;
448 backlight_update_status(the_corgi_lcd
->bl_dev
);
450 EXPORT_SYMBOL(corgi_lcd_limit_intensity
);
452 static const struct backlight_ops corgi_bl_ops
= {
453 .get_brightness
= corgi_bl_get_intensity
,
454 .update_status
= corgi_bl_update_status
,
457 #ifdef CONFIG_PM_SLEEP
458 static int corgi_lcd_suspend(struct device
*dev
)
460 struct corgi_lcd
*lcd
= dev_get_drvdata(dev
);
462 corgibl_flags
|= CORGIBL_SUSPENDED
;
463 corgi_bl_set_intensity(lcd
, 0);
464 corgi_lcd_set_power(lcd
->lcd_dev
, FB_BLANK_POWERDOWN
);
468 static int corgi_lcd_resume(struct device
*dev
)
470 struct corgi_lcd
*lcd
= dev_get_drvdata(dev
);
472 corgibl_flags
&= ~CORGIBL_SUSPENDED
;
473 corgi_lcd_set_power(lcd
->lcd_dev
, FB_BLANK_UNBLANK
);
474 backlight_update_status(lcd
->bl_dev
);
479 static SIMPLE_DEV_PM_OPS(corgi_lcd_pm_ops
, corgi_lcd_suspend
, corgi_lcd_resume
);
481 static int setup_gpio_backlight(struct corgi_lcd
*lcd
,
482 struct corgi_lcd_platform_data
*pdata
)
484 struct spi_device
*spi
= lcd
->spi_dev
;
487 lcd
->gpio_backlight_on
= -1;
488 lcd
->gpio_backlight_cont
= -1;
490 if (gpio_is_valid(pdata
->gpio_backlight_on
)) {
491 err
= devm_gpio_request(&spi
->dev
, pdata
->gpio_backlight_on
,
495 "failed to request GPIO%d for backlight_on\n",
496 pdata
->gpio_backlight_on
);
500 lcd
->gpio_backlight_on
= pdata
->gpio_backlight_on
;
501 gpio_direction_output(lcd
->gpio_backlight_on
, 0);
504 if (gpio_is_valid(pdata
->gpio_backlight_cont
)) {
505 err
= devm_gpio_request(&spi
->dev
, pdata
->gpio_backlight_cont
,
509 "failed to request GPIO%d for backlight_cont\n",
510 pdata
->gpio_backlight_cont
);
514 lcd
->gpio_backlight_cont
= pdata
->gpio_backlight_cont
;
516 /* spitz and akita use both GPIOs for backlight, and
517 * have inverted polarity of GPIO_BACKLIGHT_CONT
519 if (gpio_is_valid(lcd
->gpio_backlight_on
)) {
520 lcd
->gpio_backlight_cont_inverted
= 1;
521 gpio_direction_output(lcd
->gpio_backlight_cont
, 1);
523 lcd
->gpio_backlight_cont_inverted
= 0;
524 gpio_direction_output(lcd
->gpio_backlight_cont
, 0);
530 static int corgi_lcd_probe(struct spi_device
*spi
)
532 struct backlight_properties props
;
533 struct corgi_lcd_platform_data
*pdata
= dev_get_platdata(&spi
->dev
);
534 struct corgi_lcd
*lcd
;
538 dev_err(&spi
->dev
, "platform data not available\n");
542 lcd
= devm_kzalloc(&spi
->dev
, sizeof(struct corgi_lcd
), GFP_KERNEL
);
548 lcd
->lcd_dev
= devm_lcd_device_register(&spi
->dev
, "corgi_lcd",
549 &spi
->dev
, lcd
, &corgi_lcd_ops
);
550 if (IS_ERR(lcd
->lcd_dev
))
551 return PTR_ERR(lcd
->lcd_dev
);
553 lcd
->power
= FB_BLANK_POWERDOWN
;
554 lcd
->mode
= (pdata
) ? pdata
->init_mode
: CORGI_LCD_MODE_VGA
;
556 memset(&props
, 0, sizeof(struct backlight_properties
));
557 props
.type
= BACKLIGHT_RAW
;
558 props
.max_brightness
= pdata
->max_intensity
;
559 lcd
->bl_dev
= devm_backlight_device_register(&spi
->dev
, "corgi_bl",
560 &spi
->dev
, lcd
, &corgi_bl_ops
,
562 if (IS_ERR(lcd
->bl_dev
))
563 return PTR_ERR(lcd
->bl_dev
);
565 lcd
->bl_dev
->props
.brightness
= pdata
->default_intensity
;
566 lcd
->bl_dev
->props
.power
= FB_BLANK_UNBLANK
;
568 ret
= setup_gpio_backlight(lcd
, pdata
);
572 lcd
->kick_battery
= pdata
->kick_battery
;
574 spi_set_drvdata(spi
, lcd
);
575 corgi_lcd_set_power(lcd
->lcd_dev
, FB_BLANK_UNBLANK
);
576 backlight_update_status(lcd
->bl_dev
);
578 lcd
->limit_mask
= pdata
->limit_mask
;
583 static int corgi_lcd_remove(struct spi_device
*spi
)
585 struct corgi_lcd
*lcd
= spi_get_drvdata(spi
);
587 lcd
->bl_dev
->props
.power
= FB_BLANK_UNBLANK
;
588 lcd
->bl_dev
->props
.brightness
= 0;
589 backlight_update_status(lcd
->bl_dev
);
590 corgi_lcd_set_power(lcd
->lcd_dev
, FB_BLANK_POWERDOWN
);
594 static struct spi_driver corgi_lcd_driver
= {
597 .pm
= &corgi_lcd_pm_ops
,
599 .probe
= corgi_lcd_probe
,
600 .remove
= corgi_lcd_remove
,
603 module_spi_driver(corgi_lcd_driver
);
605 MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
606 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
607 MODULE_LICENSE("GPL");
608 MODULE_ALIAS("spi:corgi-lcd");