linux/drivers/video/backlight/corgi_lcd.c
<<
>>
Prefs
   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 int 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        return 0;
 554}
 555
 556static struct spi_driver corgi_lcd_driver = {
 557        .driver         = {
 558                .name   = "corgi-lcd",
 559                .pm     = &corgi_lcd_pm_ops,
 560        },
 561        .probe          = corgi_lcd_probe,
 562        .remove         = corgi_lcd_remove,
 563};
 564
 565module_spi_driver(corgi_lcd_driver);
 566
 567MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
 568MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
 569MODULE_LICENSE("GPL");
 570MODULE_ALIAS("spi:corgi-lcd");
 571