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