corgi_lcd.c 16 KB

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