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