cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

corgi_lcd.c (14938B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  LCD/Backlight Driver for Sharp Zaurus Handhelds (various models)
      4 *
      5 *  Copyright (c) 2004-2006 Richard Purdie
      6 *
      7 *  Based on Sharp's 2.4 Backlight Driver
      8 *
      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>
     12 */
     13
     14#include <linux/module.h>
     15#include <linux/kernel.h>
     16#include <linux/init.h>
     17#include <linux/delay.h>
     18#include <linux/gpio/consumer.h>
     19#include <linux/fb.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>
     25
     26#define POWER_IS_ON(pwr)	((pwr) <= FB_BLANK_NORMAL)
     27
     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
     37
     38/* Register Bit Definitions */
     39#define RESCTL_QVGA     0x01
     40#define RESCTL_VGA      0x00
     41
     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 */
     45
     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 */
     49
     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 */
     55
     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 */
     59
     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
     65
     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
     71
     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
     77
     78#define PHACTRL_PHASE_MANUAL   0x01
     79#define DEFAULT_PHAD_QVGA     (9)
     80#define DEFAULT_COMADJ        (125)
     81
     82struct corgi_lcd {
     83	struct spi_device	*spi_dev;
     84	struct lcd_device	*lcd_dev;
     85	struct backlight_device	*bl_dev;
     86
     87	int	limit_mask;
     88	int	intensity;
     89	int	power;
     90	int	mode;
     91	char	buf[2];
     92
     93	struct gpio_desc *backlight_on;
     94	struct gpio_desc *backlight_cont;
     95
     96	void (*kick_battery)(void);
     97};
     98
     99static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val);
    100
    101static struct corgi_lcd *the_corgi_lcd;
    102static unsigned long corgibl_flags;
    103#define CORGIBL_SUSPENDED     0x01
    104#define CORGIBL_BATTLOW       0x02
    105
    106/*
    107 * This is only a pseudo I2C interface. We can't use the standard kernel
    108 * routines as the interface is write only. We just assume the data is acked...
    109 */
    110static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data)
    111{
    112	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data);
    113	udelay(10);
    114}
    115
    116static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data)
    117{
    118	lcdtg_ssp_i2c_send(lcd, data);
    119	lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK);
    120	lcdtg_ssp_i2c_send(lcd, data);
    121}
    122
    123static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base)
    124{
    125	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
    126	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
    127	lcdtg_ssp_i2c_send(lcd, base);
    128}
    129
    130static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base)
    131{
    132	lcdtg_ssp_i2c_send(lcd, base);
    133	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK);
    134	lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT);
    135}
    136
    137static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd,
    138				uint8_t base, uint8_t data)
    139{
    140	int i;
    141
    142	for (i = 0; i < 8; i++) {
    143		if (data & 0x80)
    144			lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
    145		else
    146			lcdtg_i2c_send_bit(lcd, base);
    147		data <<= 1;
    148	}
    149}
    150
    151static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base)
    152{
    153	lcdtg_i2c_send_bit(lcd, base);
    154}
    155
    156static void lcdtg_set_common_voltage(struct corgi_lcd *lcd,
    157				     uint8_t base_data, uint8_t data)
    158{
    159	/* Set Common Voltage to M62332FP via I2C */
    160	lcdtg_i2c_send_start(lcd, base_data);
    161	lcdtg_i2c_send_byte(lcd, base_data, 0x9c);
    162	lcdtg_i2c_wait_ack(lcd, base_data);
    163	lcdtg_i2c_send_byte(lcd, base_data, 0x00);
    164	lcdtg_i2c_wait_ack(lcd, base_data);
    165	lcdtg_i2c_send_byte(lcd, base_data, data);
    166	lcdtg_i2c_wait_ack(lcd, base_data);
    167	lcdtg_i2c_send_stop(lcd, base_data);
    168}
    169
    170static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data)
    171{
    172	struct spi_message msg;
    173	struct spi_transfer xfer = {
    174		.len		= 1,
    175		.cs_change	= 0,
    176		.tx_buf		= lcd->buf,
    177	};
    178
    179	lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
    180	spi_message_init(&msg);
    181	spi_message_add_tail(&xfer, &msg);
    182
    183	return spi_sync(lcd->spi_dev, &msg);
    184}
    185
    186/* Set Phase Adjust */
    187static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode)
    188{
    189	int adj;
    190
    191	switch (mode) {
    192	case CORGI_LCD_MODE_VGA:
    193		/* Setting for VGA */
    194		adj = sharpsl_param.phadadj;
    195		adj = (adj < 0) ? PHACTRL_PHASE_MANUAL :
    196				  PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1);
    197		break;
    198	case CORGI_LCD_MODE_QVGA:
    199	default:
    200		/* Setting for QVGA */
    201		adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL;
    202		break;
    203	}
    204
    205	corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj);
    206}
    207
    208static void corgi_lcd_power_on(struct corgi_lcd *lcd)
    209{
    210	int comadj;
    211
    212	/* Initialize Internal Logic & Port */
    213	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
    214			PICTRL_POWER_DOWN | PICTRL_INIOFF |
    215			PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF |
    216			PICTRL_DAC_SIGNAL_OFF);
    217
    218	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    219			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF |
    220			POWER0_COM_OFF | POWER0_VCC5_OFF);
    221
    222	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    223			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
    224
    225	/* VDD(+8V), SVSS(-4V) ON */
    226	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    227			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
    228	mdelay(3);
    229
    230	/* DAC ON */
    231	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    232			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
    233			POWER0_COM_OFF | POWER0_VCC5_OFF);
    234
    235	/* INIB = H, INI = L  */
    236	/* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
    237	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
    238			PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF);
    239
    240	/* Set Common Voltage */
    241	comadj = sharpsl_param.comadj;
    242	if (comadj < 0)
    243		comadj = DEFAULT_COMADJ;
    244
    245	lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
    246				 POWER0_VCC5_OFF, comadj);
    247
    248	/* VCC5 ON, DAC ON */
    249	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    250			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
    251			POWER0_COM_OFF | POWER0_VCC5_ON);
    252
    253	/* GVSS(-8V) ON, VDD ON */
    254	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    255			POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
    256	mdelay(2);
    257
    258	/* COM SIGNAL ON (PICTL[3] = L) */
    259	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE);
    260
    261	/* COM ON, DAC ON, VCC5_ON */
    262	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    263			POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
    264			POWER0_COM_ON | POWER0_VCC5_ON);
    265
    266	/* VW ON, GVSS ON, VDD ON */
    267	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    268			POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON);
    269
    270	/* Signals output enable */
    271	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0);
    272
    273	/* Set Phase Adjust */
    274	lcdtg_set_phadadj(lcd, lcd->mode);
    275
    276	/* Initialize for Input Signals from ATI */
    277	corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS,
    278			POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE |
    279			POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L |
    280			POLCTRL_EN_ACT_H);
    281	udelay(1000);
    282
    283	switch (lcd->mode) {
    284	case CORGI_LCD_MODE_VGA:
    285		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
    286		break;
    287	case CORGI_LCD_MODE_QVGA:
    288	default:
    289		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
    290		break;
    291	}
    292}
    293
    294static void corgi_lcd_power_off(struct corgi_lcd *lcd)
    295{
    296	/* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
    297	msleep(34);
    298
    299	/* (1)VW OFF */
    300	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    301			POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
    302
    303	/* (2)COM OFF */
    304	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
    305	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    306			POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
    307
    308	/* (3)Set Common Voltage Bias 0V */
    309	lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
    310			POWER0_VCC5_ON, 0);
    311
    312	/* (4)GVSS OFF */
    313	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    314			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
    315
    316	/* (5)VCC5 OFF */
    317	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    318			POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
    319
    320	/* (6)Set PDWN, INIOFF, DACOFF */
    321	corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
    322			PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
    323			PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
    324
    325	/* (7)DAC OFF */
    326	corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
    327			POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
    328
    329	/* (8)VDD OFF */
    330	corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
    331			POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
    332}
    333
    334static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
    335{
    336	struct corgi_lcd *lcd = lcd_get_data(ld);
    337	int mode = CORGI_LCD_MODE_QVGA;
    338
    339	if (m->xres == 640 || m->xres == 480)
    340		mode = CORGI_LCD_MODE_VGA;
    341
    342	if (lcd->mode == mode)
    343		return 0;
    344
    345	lcdtg_set_phadadj(lcd, mode);
    346
    347	switch (mode) {
    348	case CORGI_LCD_MODE_VGA:
    349		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
    350		break;
    351	case CORGI_LCD_MODE_QVGA:
    352	default:
    353		corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
    354		break;
    355	}
    356
    357	lcd->mode = mode;
    358	return 0;
    359}
    360
    361static int corgi_lcd_set_power(struct lcd_device *ld, int power)
    362{
    363	struct corgi_lcd *lcd = lcd_get_data(ld);
    364
    365	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
    366		corgi_lcd_power_on(lcd);
    367
    368	if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
    369		corgi_lcd_power_off(lcd);
    370
    371	lcd->power = power;
    372	return 0;
    373}
    374
    375static int corgi_lcd_get_power(struct lcd_device *ld)
    376{
    377	struct corgi_lcd *lcd = lcd_get_data(ld);
    378
    379	return lcd->power;
    380}
    381
    382static struct lcd_ops corgi_lcd_ops = {
    383	.get_power	= corgi_lcd_get_power,
    384	.set_power	= corgi_lcd_set_power,
    385	.set_mode	= corgi_lcd_set_mode,
    386};
    387
    388static int corgi_bl_get_intensity(struct backlight_device *bd)
    389{
    390	struct corgi_lcd *lcd = bl_get_data(bd);
    391
    392	return lcd->intensity;
    393}
    394
    395static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
    396{
    397	int cont;
    398
    399	if (intensity > 0x10)
    400		intensity += 0x10;
    401
    402	corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity);
    403
    404	/* Bit 5 via GPIO_BACKLIGHT_CONT */
    405	cont = !!(intensity & 0x20);
    406
    407	if (lcd->backlight_cont)
    408		gpiod_set_value_cansleep(lcd->backlight_cont, cont);
    409
    410	if (lcd->backlight_on)
    411		gpiod_set_value_cansleep(lcd->backlight_on, intensity);
    412
    413	if (lcd->kick_battery)
    414		lcd->kick_battery();
    415
    416	lcd->intensity = intensity;
    417	return 0;
    418}
    419
    420static int corgi_bl_update_status(struct backlight_device *bd)
    421{
    422	struct corgi_lcd *lcd = bl_get_data(bd);
    423	int intensity = backlight_get_brightness(bd);
    424
    425	if (corgibl_flags & CORGIBL_SUSPENDED)
    426		intensity = 0;
    427
    428	if ((corgibl_flags & CORGIBL_BATTLOW) && intensity > lcd->limit_mask)
    429		intensity = lcd->limit_mask;
    430
    431	return corgi_bl_set_intensity(lcd, intensity);
    432}
    433
    434void corgi_lcd_limit_intensity(int limit)
    435{
    436	if (limit)
    437		corgibl_flags |= CORGIBL_BATTLOW;
    438	else
    439		corgibl_flags &= ~CORGIBL_BATTLOW;
    440
    441	backlight_update_status(the_corgi_lcd->bl_dev);
    442}
    443EXPORT_SYMBOL(corgi_lcd_limit_intensity);
    444
    445static const struct backlight_ops corgi_bl_ops = {
    446	.get_brightness	= corgi_bl_get_intensity,
    447	.update_status  = corgi_bl_update_status,
    448};
    449
    450#ifdef CONFIG_PM_SLEEP
    451static int corgi_lcd_suspend(struct device *dev)
    452{
    453	struct corgi_lcd *lcd = dev_get_drvdata(dev);
    454
    455	corgibl_flags |= CORGIBL_SUSPENDED;
    456	corgi_bl_set_intensity(lcd, 0);
    457	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
    458	return 0;
    459}
    460
    461static int corgi_lcd_resume(struct device *dev)
    462{
    463	struct corgi_lcd *lcd = dev_get_drvdata(dev);
    464
    465	corgibl_flags &= ~CORGIBL_SUSPENDED;
    466	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
    467	backlight_update_status(lcd->bl_dev);
    468	return 0;
    469}
    470#endif
    471
    472static SIMPLE_DEV_PM_OPS(corgi_lcd_pm_ops, corgi_lcd_suspend, corgi_lcd_resume);
    473
    474static int setup_gpio_backlight(struct corgi_lcd *lcd,
    475				struct corgi_lcd_platform_data *pdata)
    476{
    477	struct spi_device *spi = lcd->spi_dev;
    478
    479	lcd->backlight_on = devm_gpiod_get_optional(&spi->dev,
    480						    "BL_ON", GPIOD_OUT_LOW);
    481	if (IS_ERR(lcd->backlight_on))
    482		return PTR_ERR(lcd->backlight_on);
    483
    484	lcd->backlight_cont = devm_gpiod_get_optional(&spi->dev, "BL_CONT",
    485						      GPIOD_OUT_LOW);
    486	if (IS_ERR(lcd->backlight_cont))
    487		return PTR_ERR(lcd->backlight_cont);
    488
    489	return 0;
    490}
    491
    492static int corgi_lcd_probe(struct spi_device *spi)
    493{
    494	struct backlight_properties props;
    495	struct corgi_lcd_platform_data *pdata = dev_get_platdata(&spi->dev);
    496	struct corgi_lcd *lcd;
    497	int ret = 0;
    498
    499	if (pdata == NULL) {
    500		dev_err(&spi->dev, "platform data not available\n");
    501		return -EINVAL;
    502	}
    503
    504	lcd = devm_kzalloc(&spi->dev, sizeof(struct corgi_lcd), GFP_KERNEL);
    505	if (!lcd)
    506		return -ENOMEM;
    507
    508	lcd->spi_dev = spi;
    509
    510	lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "corgi_lcd",
    511						&spi->dev, lcd, &corgi_lcd_ops);
    512	if (IS_ERR(lcd->lcd_dev))
    513		return PTR_ERR(lcd->lcd_dev);
    514
    515	lcd->power = FB_BLANK_POWERDOWN;
    516	lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
    517
    518	memset(&props, 0, sizeof(struct backlight_properties));
    519	props.type = BACKLIGHT_RAW;
    520	props.max_brightness = pdata->max_intensity;
    521	lcd->bl_dev = devm_backlight_device_register(&spi->dev, "corgi_bl",
    522						&spi->dev, lcd, &corgi_bl_ops,
    523						&props);
    524	if (IS_ERR(lcd->bl_dev))
    525		return PTR_ERR(lcd->bl_dev);
    526
    527	lcd->bl_dev->props.brightness = pdata->default_intensity;
    528	lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
    529
    530	ret = setup_gpio_backlight(lcd, pdata);
    531	if (ret)
    532		return ret;
    533
    534	lcd->kick_battery = pdata->kick_battery;
    535
    536	spi_set_drvdata(spi, lcd);
    537	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
    538	backlight_update_status(lcd->bl_dev);
    539
    540	lcd->limit_mask = pdata->limit_mask;
    541	the_corgi_lcd = lcd;
    542	return 0;
    543}
    544
    545static void corgi_lcd_remove(struct spi_device *spi)
    546{
    547	struct corgi_lcd *lcd = spi_get_drvdata(spi);
    548
    549	lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
    550	lcd->bl_dev->props.brightness = 0;
    551	backlight_update_status(lcd->bl_dev);
    552	corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
    553}
    554
    555static struct spi_driver corgi_lcd_driver = {
    556	.driver		= {
    557		.name	= "corgi-lcd",
    558		.pm	= &corgi_lcd_pm_ops,
    559	},
    560	.probe		= corgi_lcd_probe,
    561	.remove		= corgi_lcd_remove,
    562};
    563
    564module_spi_driver(corgi_lcd_driver);
    565
    566MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
    567MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
    568MODULE_LICENSE("GPL");
    569MODULE_ALIAS("spi:corgi-lcd");