Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30 641 lines 17 kB view raw
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 85struct 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 103static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); 104 105static struct corgi_lcd *the_corgi_lcd; 106static 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 */ 114static 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 120static 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 127static 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 134static 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 141static 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 154static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base) 155{ 156 lcdtg_i2c_send_bit(lcd, base); 157} 158 159static 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 173static 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 */ 190static 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 211static 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 297static 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 337static 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 364static 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 378static 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 385static 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 391static 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 398static 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 423static 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 442void 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} 451EXPORT_SYMBOL(corgi_lcd_limit_intensity); 452 453static 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 459static 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 469static 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 483static 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 527err_free_backlight_on: 528 if (gpio_is_valid(lcd->gpio_backlight_on)) 529 gpio_free(lcd->gpio_backlight_on); 530 return err; 531} 532 533static 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 585err_unregister_bl: 586 backlight_device_unregister(lcd->bl_dev); 587err_unregister_lcd: 588 lcd_device_unregister(lcd->lcd_dev); 589err_free_lcd: 590 kfree(lcd); 591 return ret; 592} 593 594static 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 616static 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 627static int __init corgi_lcd_init(void) 628{ 629 return spi_register_driver(&corgi_lcd_driver); 630} 631module_init(corgi_lcd_init); 632 633static void __exit corgi_lcd_exit(void) 634{ 635 spi_unregister_driver(&corgi_lcd_driver); 636} 637module_exit(corgi_lcd_exit); 638 639MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); 640MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); 641MODULE_LICENSE("GPL");