Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  LCD/Backlight Driver for Sharp Zaurus Handhelds (various models)
0004  *
0005  *  Copyright (c) 2004-2006 Richard Purdie
0006  *
0007  *  Based on Sharp's 2.4 Backlight Driver
0008  *
0009  *  Copyright (c) 2008 Marvell International Ltd.
0010  *  Converted to SPI device based LCD/Backlight device driver
0011  *  by Eric Miao <eric.miao@marvell.com>
0012  */
0013 
0014 #include <linux/module.h>
0015 #include <linux/kernel.h>
0016 #include <linux/init.h>
0017 #include <linux/delay.h>
0018 #include <linux/gpio/consumer.h>
0019 #include <linux/fb.h>
0020 #include <linux/lcd.h>
0021 #include <linux/spi/spi.h>
0022 #include <linux/spi/corgi_lcd.h>
0023 #include <linux/slab.h>
0024 #include <asm/mach/sharpsl_param.h>
0025 
0026 #define POWER_IS_ON(pwr)    ((pwr) <= FB_BLANK_NORMAL)
0027 
0028 /* Register Addresses */
0029 #define RESCTL_ADRS     0x00
0030 #define PHACTRL_ADRS    0x01
0031 #define DUTYCTRL_ADRS   0x02
0032 #define POWERREG0_ADRS  0x03
0033 #define POWERREG1_ADRS  0x04
0034 #define GPOR3_ADRS      0x05
0035 #define PICTRL_ADRS     0x06
0036 #define POLCTRL_ADRS    0x07
0037 
0038 /* Register Bit Definitions */
0039 #define RESCTL_QVGA     0x01
0040 #define RESCTL_VGA      0x00
0041 
0042 #define POWER1_VW_ON    0x01  /* VW Supply FET ON */
0043 #define POWER1_GVSS_ON  0x02  /* GVSS(-8V) Power Supply ON */
0044 #define POWER1_VDD_ON   0x04  /* VDD(8V),SVSS(-4V) Power Supply ON */
0045 
0046 #define POWER1_VW_OFF   0x00  /* VW Supply FET OFF */
0047 #define POWER1_GVSS_OFF 0x00  /* GVSS(-8V) Power Supply OFF */
0048 #define POWER1_VDD_OFF  0x00  /* VDD(8V),SVSS(-4V) Power Supply OFF */
0049 
0050 #define POWER0_COM_DCLK 0x01  /* COM Voltage DC Bias DAC Serial Data Clock */
0051 #define POWER0_COM_DOUT 0x02  /* COM Voltage DC Bias DAC Serial Data Out */
0052 #define POWER0_DAC_ON   0x04  /* DAC Power Supply ON */
0053 #define POWER0_COM_ON   0x08  /* COM Power Supply ON */
0054 #define POWER0_VCC5_ON  0x10  /* VCC5 Power Supply ON */
0055 
0056 #define POWER0_DAC_OFF  0x00  /* DAC Power Supply OFF */
0057 #define POWER0_COM_OFF  0x00  /* COM Power Supply OFF */
0058 #define POWER0_VCC5_OFF 0x00  /* VCC5 Power Supply OFF */
0059 
0060 #define PICTRL_INIT_STATE      0x01
0061 #define PICTRL_INIOFF          0x02
0062 #define PICTRL_POWER_DOWN      0x04
0063 #define PICTRL_COM_SIGNAL_OFF  0x08
0064 #define PICTRL_DAC_SIGNAL_OFF  0x10
0065 
0066 #define POLCTRL_SYNC_POL_FALL  0x01
0067 #define POLCTRL_EN_POL_FALL    0x02
0068 #define POLCTRL_DATA_POL_FALL  0x04
0069 #define POLCTRL_SYNC_ACT_H     0x08
0070 #define POLCTRL_EN_ACT_L       0x10
0071 
0072 #define POLCTRL_SYNC_POL_RISE  0x00
0073 #define POLCTRL_EN_POL_RISE    0x00
0074 #define POLCTRL_DATA_POL_RISE  0x00
0075 #define POLCTRL_SYNC_ACT_L     0x00
0076 #define POLCTRL_EN_ACT_H       0x00
0077 
0078 #define PHACTRL_PHASE_MANUAL   0x01
0079 #define DEFAULT_PHAD_QVGA     (9)
0080 #define DEFAULT_COMADJ        (125)
0081 
0082 struct corgi_lcd {
0083     struct spi_device   *spi_dev;
0084     struct lcd_device   *lcd_dev;
0085     struct backlight_device *bl_dev;
0086 
0087     int limit_mask;
0088     int intensity;
0089     int power;
0090     int mode;
0091     char    buf[2];
0092 
0093     struct gpio_desc *backlight_on;
0094     struct gpio_desc *backlight_cont;
0095 
0096     void (*kick_battery)(void);
0097 };
0098 
0099 static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val);
0100 
0101 static struct corgi_lcd *the_corgi_lcd;
0102 static unsigned long corgibl_flags;
0103 #define CORGIBL_SUSPENDED     0x01
0104 #define CORGIBL_BATTLOW       0x02
0105 
0106 /*
0107  * This is only a pseudo I2C interface. We can't use the standard kernel
0108  * routines as the interface is write only. We just assume the data is acked...
0109  */
0110 static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data)
0111 {
0112     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data);
0113     udelay(10);
0114 }
0115 
0116 static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data)
0117 {
0118     lcdtg_ssp_i2c_send(lcd, data);
0119     lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK);
0120     lcdtg_ssp_i2c_send(lcd, data);
0121 }
0122 
0123 static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base)
0124 {
0125     lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
0126     lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
0127     lcdtg_ssp_i2c_send(lcd, base);
0128 }
0129 
0130 static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base)
0131 {
0132     lcdtg_ssp_i2c_send(lcd, base);
0133     lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
0134     lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
0135 }
0136 
0137 static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd,
0138                 uint8_t base, uint8_t data)
0139 {
0140     int i;
0141 
0142     for (i = 0; i < 8; i++) {
0143         if (data & 0x80)
0144             lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
0145         else
0146             lcdtg_i2c_send_bit(lcd, base);
0147         data <<= 1;
0148     }
0149 }
0150 
0151 static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base)
0152 {
0153     lcdtg_i2c_send_bit(lcd, base);
0154 }
0155 
0156 static void lcdtg_set_common_voltage(struct corgi_lcd *lcd,
0157                      uint8_t base_data, uint8_t data)
0158 {
0159     /* Set Common Voltage to M62332FP via I2C */
0160     lcdtg_i2c_send_start(lcd, base_data);
0161     lcdtg_i2c_send_byte(lcd, base_data, 0x9c);
0162     lcdtg_i2c_wait_ack(lcd, base_data);
0163     lcdtg_i2c_send_byte(lcd, base_data, 0x00);
0164     lcdtg_i2c_wait_ack(lcd, base_data);
0165     lcdtg_i2c_send_byte(lcd, base_data, data);
0166     lcdtg_i2c_wait_ack(lcd, base_data);
0167     lcdtg_i2c_send_stop(lcd, base_data);
0168 }
0169 
0170 static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data)
0171 {
0172     struct spi_message msg;
0173     struct spi_transfer xfer = {
0174         .len        = 1,
0175         .cs_change  = 0,
0176         .tx_buf     = lcd->buf,
0177     };
0178 
0179     lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
0180     spi_message_init(&msg);
0181     spi_message_add_tail(&xfer, &msg);
0182 
0183     return spi_sync(lcd->spi_dev, &msg);
0184 }
0185 
0186 /* Set Phase Adjust */
0187 static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode)
0188 {
0189     int adj;
0190 
0191     switch (mode) {
0192     case CORGI_LCD_MODE_VGA:
0193         /* Setting for VGA */
0194         adj = sharpsl_param.phadadj;
0195         adj = (adj < 0) ? PHACTRL_PHASE_MANUAL :
0196                   PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1);
0197         break;
0198     case CORGI_LCD_MODE_QVGA:
0199     default:
0200         /* Setting for QVGA */
0201         adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL;
0202         break;
0203     }
0204 
0205     corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj);
0206 }
0207 
0208 static void corgi_lcd_power_on(struct corgi_lcd *lcd)
0209 {
0210     int comadj;
0211 
0212     /* Initialize Internal Logic & Port */
0213     corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
0214             PICTRL_POWER_DOWN | PICTRL_INIOFF |
0215             PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF |
0216             PICTRL_DAC_SIGNAL_OFF);
0217 
0218     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0219             POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF |
0220             POWER0_COM_OFF | POWER0_VCC5_OFF);
0221 
0222     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0223             POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
0224 
0225     /* VDD(+8V), SVSS(-4V) ON */
0226     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0227             POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
0228     mdelay(3);
0229 
0230     /* DAC ON */
0231     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0232             POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
0233             POWER0_COM_OFF | POWER0_VCC5_OFF);
0234 
0235     /* INIB = H, INI = L  */
0236     /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
0237     corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
0238             PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF);
0239 
0240     /* Set Common Voltage */
0241     comadj = sharpsl_param.comadj;
0242     if (comadj < 0)
0243         comadj = DEFAULT_COMADJ;
0244 
0245     lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
0246                  POWER0_VCC5_OFF, comadj);
0247 
0248     /* VCC5 ON, DAC ON */
0249     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0250             POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
0251             POWER0_COM_OFF | POWER0_VCC5_ON);
0252 
0253     /* GVSS(-8V) ON, VDD ON */
0254     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0255             POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
0256     mdelay(2);
0257 
0258     /* COM SIGNAL ON (PICTL[3] = L) */
0259     corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE);
0260 
0261     /* COM ON, DAC ON, VCC5_ON */
0262     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0263             POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
0264             POWER0_COM_ON | POWER0_VCC5_ON);
0265 
0266     /* VW ON, GVSS ON, VDD ON */
0267     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0268             POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON);
0269 
0270     /* Signals output enable */
0271     corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0);
0272 
0273     /* Set Phase Adjust */
0274     lcdtg_set_phadadj(lcd, lcd->mode);
0275 
0276     /* Initialize for Input Signals from ATI */
0277     corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS,
0278             POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE |
0279             POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L |
0280             POLCTRL_EN_ACT_H);
0281     udelay(1000);
0282 
0283     switch (lcd->mode) {
0284     case CORGI_LCD_MODE_VGA:
0285         corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
0286         break;
0287     case CORGI_LCD_MODE_QVGA:
0288     default:
0289         corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
0290         break;
0291     }
0292 }
0293 
0294 static void corgi_lcd_power_off(struct corgi_lcd *lcd)
0295 {
0296     /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
0297     msleep(34);
0298 
0299     /* (1)VW OFF */
0300     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0301             POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
0302 
0303     /* (2)COM OFF */
0304     corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
0305     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0306             POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
0307 
0308     /* (3)Set Common Voltage Bias 0V */
0309     lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
0310             POWER0_VCC5_ON, 0);
0311 
0312     /* (4)GVSS OFF */
0313     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0314             POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
0315 
0316     /* (5)VCC5 OFF */
0317     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0318             POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
0319 
0320     /* (6)Set PDWN, INIOFF, DACOFF */
0321     corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
0322             PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
0323             PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
0324 
0325     /* (7)DAC OFF */
0326     corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
0327             POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
0328 
0329     /* (8)VDD OFF */
0330     corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
0331             POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
0332 }
0333 
0334 static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
0335 {
0336     struct corgi_lcd *lcd = lcd_get_data(ld);
0337     int mode = CORGI_LCD_MODE_QVGA;
0338 
0339     if (m->xres == 640 || m->xres == 480)
0340         mode = CORGI_LCD_MODE_VGA;
0341 
0342     if (lcd->mode == mode)
0343         return 0;
0344 
0345     lcdtg_set_phadadj(lcd, mode);
0346 
0347     switch (mode) {
0348     case CORGI_LCD_MODE_VGA:
0349         corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
0350         break;
0351     case CORGI_LCD_MODE_QVGA:
0352     default:
0353         corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
0354         break;
0355     }
0356 
0357     lcd->mode = mode;
0358     return 0;
0359 }
0360 
0361 static int corgi_lcd_set_power(struct lcd_device *ld, int power)
0362 {
0363     struct corgi_lcd *lcd = lcd_get_data(ld);
0364 
0365     if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
0366         corgi_lcd_power_on(lcd);
0367 
0368     if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
0369         corgi_lcd_power_off(lcd);
0370 
0371     lcd->power = power;
0372     return 0;
0373 }
0374 
0375 static int corgi_lcd_get_power(struct lcd_device *ld)
0376 {
0377     struct corgi_lcd *lcd = lcd_get_data(ld);
0378 
0379     return lcd->power;
0380 }
0381 
0382 static struct lcd_ops corgi_lcd_ops = {
0383     .get_power  = corgi_lcd_get_power,
0384     .set_power  = corgi_lcd_set_power,
0385     .set_mode   = corgi_lcd_set_mode,
0386 };
0387 
0388 static int corgi_bl_get_intensity(struct backlight_device *bd)
0389 {
0390     struct corgi_lcd *lcd = bl_get_data(bd);
0391 
0392     return lcd->intensity;
0393 }
0394 
0395 static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
0396 {
0397     int cont;
0398 
0399     if (intensity > 0x10)
0400         intensity += 0x10;
0401 
0402     corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity);
0403 
0404     /* Bit 5 via GPIO_BACKLIGHT_CONT */
0405     cont = !!(intensity & 0x20);
0406 
0407     if (lcd->backlight_cont)
0408         gpiod_set_value_cansleep(lcd->backlight_cont, cont);
0409 
0410     if (lcd->backlight_on)
0411         gpiod_set_value_cansleep(lcd->backlight_on, intensity);
0412 
0413     if (lcd->kick_battery)
0414         lcd->kick_battery();
0415 
0416     lcd->intensity = intensity;
0417     return 0;
0418 }
0419 
0420 static int corgi_bl_update_status(struct backlight_device *bd)
0421 {
0422     struct corgi_lcd *lcd = bl_get_data(bd);
0423     int intensity = backlight_get_brightness(bd);
0424 
0425     if (corgibl_flags & CORGIBL_SUSPENDED)
0426         intensity = 0;
0427 
0428     if ((corgibl_flags & CORGIBL_BATTLOW) && intensity > lcd->limit_mask)
0429         intensity = lcd->limit_mask;
0430 
0431     return corgi_bl_set_intensity(lcd, intensity);
0432 }
0433 
0434 void corgi_lcd_limit_intensity(int limit)
0435 {
0436     if (limit)
0437         corgibl_flags |= CORGIBL_BATTLOW;
0438     else
0439         corgibl_flags &= ~CORGIBL_BATTLOW;
0440 
0441     backlight_update_status(the_corgi_lcd->bl_dev);
0442 }
0443 EXPORT_SYMBOL(corgi_lcd_limit_intensity);
0444 
0445 static const struct backlight_ops corgi_bl_ops = {
0446     .get_brightness = corgi_bl_get_intensity,
0447     .update_status  = corgi_bl_update_status,
0448 };
0449 
0450 #ifdef CONFIG_PM_SLEEP
0451 static int corgi_lcd_suspend(struct device *dev)
0452 {
0453     struct corgi_lcd *lcd = dev_get_drvdata(dev);
0454 
0455     corgibl_flags |= CORGIBL_SUSPENDED;
0456     corgi_bl_set_intensity(lcd, 0);
0457     corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
0458     return 0;
0459 }
0460 
0461 static int corgi_lcd_resume(struct device *dev)
0462 {
0463     struct corgi_lcd *lcd = dev_get_drvdata(dev);
0464 
0465     corgibl_flags &= ~CORGIBL_SUSPENDED;
0466     corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
0467     backlight_update_status(lcd->bl_dev);
0468     return 0;
0469 }
0470 #endif
0471 
0472 static SIMPLE_DEV_PM_OPS(corgi_lcd_pm_ops, corgi_lcd_suspend, corgi_lcd_resume);
0473 
0474 static int setup_gpio_backlight(struct corgi_lcd *lcd,
0475                 struct corgi_lcd_platform_data *pdata)
0476 {
0477     struct spi_device *spi = lcd->spi_dev;
0478 
0479     lcd->backlight_on = devm_gpiod_get_optional(&spi->dev,
0480                             "BL_ON", GPIOD_OUT_LOW);
0481     if (IS_ERR(lcd->backlight_on))
0482         return PTR_ERR(lcd->backlight_on);
0483 
0484     lcd->backlight_cont = devm_gpiod_get_optional(&spi->dev, "BL_CONT",
0485                               GPIOD_OUT_LOW);
0486     if (IS_ERR(lcd->backlight_cont))
0487         return PTR_ERR(lcd->backlight_cont);
0488 
0489     return 0;
0490 }
0491 
0492 static int corgi_lcd_probe(struct spi_device *spi)
0493 {
0494     struct backlight_properties props;
0495     struct corgi_lcd_platform_data *pdata = dev_get_platdata(&spi->dev);
0496     struct corgi_lcd *lcd;
0497     int ret = 0;
0498 
0499     if (pdata == NULL) {
0500         dev_err(&spi->dev, "platform data not available\n");
0501         return -EINVAL;
0502     }
0503 
0504     lcd = devm_kzalloc(&spi->dev, sizeof(struct corgi_lcd), GFP_KERNEL);
0505     if (!lcd)
0506         return -ENOMEM;
0507 
0508     lcd->spi_dev = spi;
0509 
0510     lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "corgi_lcd",
0511                         &spi->dev, lcd, &corgi_lcd_ops);
0512     if (IS_ERR(lcd->lcd_dev))
0513         return PTR_ERR(lcd->lcd_dev);
0514 
0515     lcd->power = FB_BLANK_POWERDOWN;
0516     lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
0517 
0518     memset(&props, 0, sizeof(struct backlight_properties));
0519     props.type = BACKLIGHT_RAW;
0520     props.max_brightness = pdata->max_intensity;
0521     lcd->bl_dev = devm_backlight_device_register(&spi->dev, "corgi_bl",
0522                         &spi->dev, lcd, &corgi_bl_ops,
0523                         &props);
0524     if (IS_ERR(lcd->bl_dev))
0525         return PTR_ERR(lcd->bl_dev);
0526 
0527     lcd->bl_dev->props.brightness = pdata->default_intensity;
0528     lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
0529 
0530     ret = setup_gpio_backlight(lcd, pdata);
0531     if (ret)
0532         return ret;
0533 
0534     lcd->kick_battery = pdata->kick_battery;
0535 
0536     spi_set_drvdata(spi, lcd);
0537     corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
0538     backlight_update_status(lcd->bl_dev);
0539 
0540     lcd->limit_mask = pdata->limit_mask;
0541     the_corgi_lcd = lcd;
0542     return 0;
0543 }
0544 
0545 static void corgi_lcd_remove(struct spi_device *spi)
0546 {
0547     struct corgi_lcd *lcd = spi_get_drvdata(spi);
0548 
0549     lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
0550     lcd->bl_dev->props.brightness = 0;
0551     backlight_update_status(lcd->bl_dev);
0552     corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
0553 }
0554 
0555 static struct spi_driver corgi_lcd_driver = {
0556     .driver     = {
0557         .name   = "corgi-lcd",
0558         .pm = &corgi_lcd_pm_ops,
0559     },
0560     .probe      = corgi_lcd_probe,
0561     .remove     = corgi_lcd_remove,
0562 };
0563 
0564 module_spi_driver(corgi_lcd_driver);
0565 
0566 MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
0567 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
0568 MODULE_LICENSE("GPL");
0569 MODULE_ALIAS("spi:corgi-lcd");