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
 147        for (i = 0; i < 8; i++) {
 148                if (data & 0x80)
 149                        lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT);
 150                else
 151                        lcdtg_i2c_send_bit(lcd, base);
 152                data <<= 1;
 153        }
 154}
 155
 156static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base)
 157{
 158        lcdtg_i2c_send_bit(lcd, base);
 159}
 160
 161static void lcdtg_set_common_voltage(struct corgi_lcd *lcd,
 162                                     uint8_t base_data, uint8_t data)
 163{
 164        /* Set Common Voltage to M62332FP via I2C */
 165        lcdtg_i2c_send_start(lcd, base_data);
 166        lcdtg_i2c_send_byte(lcd, base_data, 0x9c);
 167        lcdtg_i2c_wait_ack(lcd, base_data);
 168        lcdtg_i2c_send_byte(lcd, base_data, 0x00);
 169        lcdtg_i2c_wait_ack(lcd, base_data);
 170        lcdtg_i2c_send_byte(lcd, base_data, data);
 171        lcdtg_i2c_wait_ack(lcd, base_data);
 172        lcdtg_i2c_send_stop(lcd, base_data);
 173}
 174
 175static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data)
 176{
 177        struct spi_message msg;
 178        struct spi_transfer xfer = {
 179                .len            = 1,
 180                .cs_change      = 1,
 181                .tx_buf         = lcd->buf,
 182        };
 183
 184        lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f);
 185        spi_message_init(&msg);
 186        spi_message_add_tail(&xfer, &msg);
 187
 188        return spi_sync(lcd->spi_dev, &msg);
 189}
 190
 191/* Set Phase Adjust */
 192static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode)
 193{
 194        int adj;
 195
 196        switch (mode) {
 197        case CORGI_LCD_MODE_VGA:
 198                /* Setting for VGA */
 199                adj = sharpsl_param.phadadj;
 200                adj = (adj < 0) ? PHACTRL_PHASE_MANUAL :
 201                                  PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1);
 202                break;
 203        case CORGI_LCD_MODE_QVGA:
 204        default:
 205                /* Setting for QVGA */
 206                adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL;
 207                break;
 208        }
 209
 210        corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj);
 211}
 212
 213static void corgi_lcd_power_on(struct corgi_lcd *lcd)
 214{
 215        int comadj;
 216
 217        /* Initialize Internal Logic & Port */
 218        corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
 219                        PICTRL_POWER_DOWN | PICTRL_INIOFF |
 220                        PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF |
 221                        PICTRL_DAC_SIGNAL_OFF);
 222
 223        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 224                        POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF |
 225                        POWER0_COM_OFF | POWER0_VCC5_OFF);
 226
 227        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 228                        POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
 229
 230        /* VDD(+8V), SVSS(-4V) ON */
 231        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 232                        POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
 233        mdelay(3);
 234
 235        /* DAC ON */
 236        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 237                        POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
 238                        POWER0_COM_OFF | POWER0_VCC5_OFF);
 239
 240        /* INIB = H, INI = L  */
 241        /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
 242        corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
 243                        PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF);
 244
 245        /* Set Common Voltage */
 246        comadj = sharpsl_param.comadj;
 247        if (comadj < 0)
 248                comadj = DEFAULT_COMADJ;
 249
 250        lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
 251                                 POWER0_VCC5_OFF, comadj);
 252
 253        /* VCC5 ON, DAC ON */
 254        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 255                        POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
 256                        POWER0_COM_OFF | POWER0_VCC5_ON);
 257
 258        /* GVSS(-8V) ON, VDD ON */
 259        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 260                        POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
 261        mdelay(2);
 262
 263        /* COM SIGNAL ON (PICTL[3] = L) */
 264        corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE);
 265
 266        /* COM ON, DAC ON, VCC5_ON */
 267        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 268                        POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON |
 269                        POWER0_COM_ON | POWER0_VCC5_ON);
 270
 271        /* VW ON, GVSS ON, VDD ON */
 272        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 273                        POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON);
 274
 275        /* Signals output enable */
 276        corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0);
 277
 278        /* Set Phase Adjust */
 279        lcdtg_set_phadadj(lcd, lcd->mode);
 280
 281        /* Initialize for Input Signals from ATI */
 282        corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS,
 283                        POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE |
 284                        POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L |
 285                        POLCTRL_EN_ACT_H);
 286        udelay(1000);
 287
 288        switch (lcd->mode) {
 289        case CORGI_LCD_MODE_VGA:
 290                corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
 291                break;
 292        case CORGI_LCD_MODE_QVGA:
 293        default:
 294                corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
 295                break;
 296        }
 297}
 298
 299static void corgi_lcd_power_off(struct corgi_lcd *lcd)
 300{
 301        /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
 302        msleep(34);
 303
 304        /* (1)VW OFF */
 305        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 306                        POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
 307
 308        /* (2)COM OFF */
 309        corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
 310        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 311                        POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
 312
 313        /* (3)Set Common Voltage Bias 0V */
 314        lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF |
 315                        POWER0_VCC5_ON, 0);
 316
 317        /* (4)GVSS OFF */
 318        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 319                        POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
 320
 321        /* (5)VCC5 OFF */
 322        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 323                        POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
 324
 325        /* (6)Set PDWN, INIOFF, DACOFF */
 326        corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS,
 327                        PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
 328                        PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
 329
 330        /* (7)DAC OFF */
 331        corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS,
 332                        POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
 333
 334        /* (8)VDD OFF */
 335        corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS,
 336                        POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
 337}
 338
 339static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
 340{
 341        struct corgi_lcd *lcd = lcd_get_data(ld);
 342        int mode = CORGI_LCD_MODE_QVGA;
 343
 344        if (m->xres == 640 || m->xres == 480)
 345                mode = CORGI_LCD_MODE_VGA;
 346
 347        if (lcd->mode == mode)
 348                return 0;
 349
 350        lcdtg_set_phadadj(lcd, mode);
 351
 352        switch (mode) {
 353        case CORGI_LCD_MODE_VGA:
 354                corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA);
 355                break;
 356        case CORGI_LCD_MODE_QVGA:
 357        default:
 358                corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA);
 359                break;
 360        }
 361
 362        lcd->mode = mode;
 363        return 0;
 364}
 365
 366static int corgi_lcd_set_power(struct lcd_device *ld, int power)
 367{
 368        struct corgi_lcd *lcd = lcd_get_data(ld);
 369
 370        if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
 371                corgi_lcd_power_on(lcd);
 372
 373        if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
 374                corgi_lcd_power_off(lcd);
 375
 376        lcd->power = power;
 377        return 0;
 378}
 379
 380static int corgi_lcd_get_power(struct lcd_device *ld)
 381{
 382        struct corgi_lcd *lcd = lcd_get_data(ld);
 383
 384        return lcd->power;
 385}
 386
 387static struct lcd_ops corgi_lcd_ops = {
 388        .get_power      = corgi_lcd_get_power,
 389        .set_power      = corgi_lcd_set_power,
 390        .set_mode       = corgi_lcd_set_mode,
 391};
 392
 393static int corgi_bl_get_intensity(struct backlight_device *bd)
 394{
 395        struct corgi_lcd *lcd = bl_get_data(bd);
 396
 397        return lcd->intensity;
 398}
 399
 400static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
 401{
 402        int cont;
 403
 404        if (intensity > 0x10)
 405                intensity += 0x10;
 406
 407        corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity);
 408
 409        /* Bit 5 via GPIO_BACKLIGHT_CONT */
 410        cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted;
 411
 412        if (gpio_is_valid(lcd->gpio_backlight_cont))
 413                gpio_set_value_cansleep(lcd->gpio_backlight_cont, cont);
 414
 415        if (gpio_is_valid(lcd->gpio_backlight_on))
 416                gpio_set_value_cansleep(lcd->gpio_backlight_on, intensity);
 417
 418        if (lcd->kick_battery)
 419                lcd->kick_battery();
 420
 421        lcd->intensity = intensity;
 422        return 0;
 423}
 424
 425static int corgi_bl_update_status(struct backlight_device *bd)
 426{
 427        struct corgi_lcd *lcd = bl_get_data(bd);
 428        int intensity = bd->props.brightness;
 429
 430        if (bd->props.power != FB_BLANK_UNBLANK)
 431                intensity = 0;
 432
 433        if (bd->props.fb_blank != FB_BLANK_UNBLANK)
 434                intensity = 0;
 435
 436        if (corgibl_flags & CORGIBL_SUSPENDED)
 437                intensity = 0;
 438
 439        if ((corgibl_flags & CORGIBL_BATTLOW) && intensity > lcd->limit_mask)
 440                intensity = lcd->limit_mask;
 441
 442        return corgi_bl_set_intensity(lcd, intensity);
 443}
 444
 445void corgi_lcd_limit_intensity(int limit)
 446{
 447        if (limit)
 448                corgibl_flags |= CORGIBL_BATTLOW;
 449        else
 450                corgibl_flags &= ~CORGIBL_BATTLOW;
 451
 452        backlight_update_status(the_corgi_lcd->bl_dev);
 453}
 454EXPORT_SYMBOL(corgi_lcd_limit_intensity);
 455
 456static const struct backlight_ops corgi_bl_ops = {
 457        .get_brightness = corgi_bl_get_intensity,
 458        .update_status  = corgi_bl_update_status,
 459};
 460
 461#ifdef CONFIG_PM_SLEEP
 462static int corgi_lcd_suspend(struct device *dev)
 463{
 464        struct corgi_lcd *lcd = dev_get_drvdata(dev);
 465
 466        corgibl_flags |= CORGIBL_SUSPENDED;
 467        corgi_bl_set_intensity(lcd, 0);
 468        corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
 469        return 0;
 470}
 471
 472static int corgi_lcd_resume(struct device *dev)
 473{
 474        struct corgi_lcd *lcd = dev_get_drvdata(dev);
 475
 476        corgibl_flags &= ~CORGIBL_SUSPENDED;
 477        corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
 478        backlight_update_status(lcd->bl_dev);
 479        return 0;
 480}
 481#endif
 482
 483static SIMPLE_DEV_PM_OPS(corgi_lcd_pm_ops, corgi_lcd_suspend, corgi_lcd_resume);
 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 = dev_get_platdata(&spi->dev);
 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                return -ENOMEM;
 549
 550        lcd->spi_dev = spi;
 551
 552        lcd->lcd_dev = devm_lcd_device_register(&spi->dev, "corgi_lcd",
 553                                                &spi->dev, lcd, &corgi_lcd_ops);
 554        if (IS_ERR(lcd->lcd_dev))
 555                return PTR_ERR(lcd->lcd_dev);
 556
 557        lcd->power = FB_BLANK_POWERDOWN;
 558        lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
 559
 560        memset(&props, 0, sizeof(struct backlight_properties));
 561        props.type = BACKLIGHT_RAW;
 562        props.max_brightness = pdata->max_intensity;
 563        lcd->bl_dev = devm_backlight_device_register(&spi->dev, "corgi_bl",
 564                                                &spi->dev, lcd, &corgi_bl_ops,
 565                                                &props);
 566        if (IS_ERR(lcd->bl_dev))
 567                return PTR_ERR(lcd->bl_dev);
 568
 569        lcd->bl_dev->props.brightness = pdata->default_intensity;
 570        lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
 571
 572        ret = setup_gpio_backlight(lcd, pdata);
 573        if (ret)
 574                return ret;
 575
 576        lcd->kick_battery = pdata->kick_battery;
 577
 578        spi_set_drvdata(spi, lcd);
 579        corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
 580        backlight_update_status(lcd->bl_dev);
 581
 582        lcd->limit_mask = pdata->limit_mask;
 583        the_corgi_lcd = lcd;
 584        return 0;
 585}
 586
 587static int corgi_lcd_remove(struct spi_device *spi)
 588{
 589        struct corgi_lcd *lcd = spi_get_drvdata(spi);
 590
 591        lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
 592        lcd->bl_dev->props.brightness = 0;
 593        backlight_update_status(lcd->bl_dev);
 594        corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
 595        return 0;
 596}
 597
 598static struct spi_driver corgi_lcd_driver = {
 599        .driver         = {
 600                .name   = "corgi-lcd",
 601                .owner  = THIS_MODULE,
 602                .pm     = &corgi_lcd_pm_ops,
 603        },
 604        .probe          = corgi_lcd_probe,
 605        .remove         = corgi_lcd_remove,
 606};
 607
 608module_spi_driver(corgi_lcd_driver);
 609
 610MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
 611MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
 612MODULE_LICENSE("GPL");
 613MODULE_ALIAS("spi:corgi-lcd");
 614