ili9320.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /* drivers/video/backlight/ili9320.c
  2. *
  3. * ILI9320 LCD controller driver core.
  4. *
  5. * Copyright 2007 Simtec Electronics
  6. * http://armlinux.simtec.co.uk/
  7. * Ben Dooks <ben@simtec.co.uk>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2 as
  11. * published by the Free Software Foundation.
  12. */
  13. #include <linux/delay.h>
  14. #include <linux/err.h>
  15. #include <linux/fb.h>
  16. #include <linux/init.h>
  17. #include <linux/lcd.h>
  18. #include <linux/module.h>
  19. #include <linux/slab.h>
  20. #include <linux/spi/spi.h>
  21. #include <video/ili9320.h>
  22. #include "ili9320.h"
  23. static inline int ili9320_write_spi(struct ili9320 *ili,
  24. unsigned int reg,
  25. unsigned int value)
  26. {
  27. struct ili9320_spi *spi = &ili->access.spi;
  28. unsigned char *addr = spi->buffer_addr;
  29. unsigned char *data = spi->buffer_data;
  30. /* spi message consits of:
  31. * first byte: ID and operation
  32. */
  33. addr[0] = spi->id | ILI9320_SPI_INDEX | ILI9320_SPI_WRITE;
  34. addr[1] = reg >> 8;
  35. addr[2] = reg;
  36. /* second message is the data to transfer */
  37. data[0] = spi->id | ILI9320_SPI_DATA | ILI9320_SPI_WRITE;
  38. data[1] = value >> 8;
  39. data[2] = value;
  40. return spi_sync(spi->dev, &spi->message);
  41. }
  42. int ili9320_write(struct ili9320 *ili, unsigned int reg, unsigned int value)
  43. {
  44. dev_dbg(ili->dev, "write: reg=%02x, val=%04x\n", reg, value);
  45. return ili->write(ili, reg, value);
  46. }
  47. EXPORT_SYMBOL_GPL(ili9320_write);
  48. int ili9320_write_regs(struct ili9320 *ili,
  49. struct ili9320_reg *values,
  50. int nr_values)
  51. {
  52. int index;
  53. int ret;
  54. for (index = 0; index < nr_values; index++, values++) {
  55. ret = ili9320_write(ili, values->address, values->value);
  56. if (ret != 0)
  57. return ret;
  58. }
  59. return 0;
  60. }
  61. EXPORT_SYMBOL_GPL(ili9320_write_regs);
  62. static void ili9320_reset(struct ili9320 *lcd)
  63. {
  64. struct ili9320_platdata *cfg = lcd->platdata;
  65. cfg->reset(1);
  66. mdelay(50);
  67. cfg->reset(0);
  68. mdelay(50);
  69. cfg->reset(1);
  70. mdelay(100);
  71. }
  72. static inline int ili9320_init_chip(struct ili9320 *lcd)
  73. {
  74. int ret;
  75. ili9320_reset(lcd);
  76. ret = lcd->client->init(lcd, lcd->platdata);
  77. if (ret != 0) {
  78. dev_err(lcd->dev, "failed to initialise display\n");
  79. return ret;
  80. }
  81. lcd->initialised = 1;
  82. return 0;
  83. }
  84. static inline int ili9320_power_on(struct ili9320 *lcd)
  85. {
  86. if (!lcd->initialised)
  87. ili9320_init_chip(lcd);
  88. lcd->display1 |= (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE);
  89. ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1);
  90. return 0;
  91. }
  92. static inline int ili9320_power_off(struct ili9320 *lcd)
  93. {
  94. lcd->display1 &= ~(ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE);
  95. ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1);
  96. return 0;
  97. }
  98. #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
  99. static int ili9320_power(struct ili9320 *lcd, int power)
  100. {
  101. int ret = 0;
  102. dev_dbg(lcd->dev, "power %d => %d\n", lcd->power, power);
  103. if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
  104. ret = ili9320_power_on(lcd);
  105. else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
  106. ret = ili9320_power_off(lcd);
  107. if (ret == 0)
  108. lcd->power = power;
  109. else
  110. dev_warn(lcd->dev, "failed to set power mode %d\n", power);
  111. return ret;
  112. }
  113. static inline struct ili9320 *to_our_lcd(struct lcd_device *lcd)
  114. {
  115. return lcd_get_data(lcd);
  116. }
  117. static int ili9320_set_power(struct lcd_device *ld, int power)
  118. {
  119. struct ili9320 *lcd = to_our_lcd(ld);
  120. return ili9320_power(lcd, power);
  121. }
  122. static int ili9320_get_power(struct lcd_device *ld)
  123. {
  124. struct ili9320 *lcd = to_our_lcd(ld);
  125. return lcd->power;
  126. }
  127. static struct lcd_ops ili9320_ops = {
  128. .get_power = ili9320_get_power,
  129. .set_power = ili9320_set_power,
  130. };
  131. static void __devinit ili9320_setup_spi(struct ili9320 *ili,
  132. struct spi_device *dev)
  133. {
  134. struct ili9320_spi *spi = &ili->access.spi;
  135. ili->write = ili9320_write_spi;
  136. spi->dev = dev;
  137. /* fill the two messages we are going to use to send the data
  138. * with, the first the address followed by the data. The datasheet
  139. * says they should be done as two distinct cycles of the SPI CS line.
  140. */
  141. spi->xfer[0].tx_buf = spi->buffer_addr;
  142. spi->xfer[1].tx_buf = spi->buffer_data;
  143. spi->xfer[0].len = 3;
  144. spi->xfer[1].len = 3;
  145. spi->xfer[0].bits_per_word = 8;
  146. spi->xfer[1].bits_per_word = 8;
  147. spi->xfer[0].cs_change = 1;
  148. spi_message_init(&spi->message);
  149. spi_message_add_tail(&spi->xfer[0], &spi->message);
  150. spi_message_add_tail(&spi->xfer[1], &spi->message);
  151. }
  152. int __devinit ili9320_probe_spi(struct spi_device *spi,
  153. struct ili9320_client *client)
  154. {
  155. struct ili9320_platdata *cfg = spi->dev.platform_data;
  156. struct device *dev = &spi->dev;
  157. struct ili9320 *ili;
  158. struct lcd_device *lcd;
  159. int ret = 0;
  160. /* verify we where given some information */
  161. if (cfg == NULL) {
  162. dev_err(dev, "no platform data supplied\n");
  163. return -EINVAL;
  164. }
  165. if (cfg->hsize <= 0 || cfg->vsize <= 0 || cfg->reset == NULL) {
  166. dev_err(dev, "invalid platform data supplied\n");
  167. return -EINVAL;
  168. }
  169. /* allocate and initialse our state */
  170. ili = kzalloc(sizeof(struct ili9320), GFP_KERNEL);
  171. if (ili == NULL) {
  172. dev_err(dev, "no memory for device\n");
  173. return -ENOMEM;
  174. }
  175. ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1);
  176. ili->dev = dev;
  177. ili->client = client;
  178. ili->power = FB_BLANK_POWERDOWN;
  179. ili->platdata = cfg;
  180. dev_set_drvdata(&spi->dev, ili);
  181. ili9320_setup_spi(ili, spi);
  182. lcd = lcd_device_register("ili9320", dev, ili, &ili9320_ops);
  183. if (IS_ERR(lcd)) {
  184. dev_err(dev, "failed to register lcd device\n");
  185. ret = PTR_ERR(lcd);
  186. goto err_free;
  187. }
  188. ili->lcd = lcd;
  189. dev_info(dev, "initialising %s\n", client->name);
  190. ret = ili9320_power(ili, FB_BLANK_UNBLANK);
  191. if (ret != 0) {
  192. dev_err(dev, "failed to set lcd power state\n");
  193. goto err_unregister;
  194. }
  195. return 0;
  196. err_unregister:
  197. lcd_device_unregister(lcd);
  198. err_free:
  199. kfree(ili);
  200. return ret;
  201. }
  202. EXPORT_SYMBOL_GPL(ili9320_probe_spi);
  203. int __devexit ili9320_remove(struct ili9320 *ili)
  204. {
  205. ili9320_power(ili, FB_BLANK_POWERDOWN);
  206. lcd_device_unregister(ili->lcd);
  207. kfree(ili);
  208. return 0;
  209. }
  210. EXPORT_SYMBOL_GPL(ili9320_remove);
  211. #ifdef CONFIG_PM
  212. int ili9320_suspend(struct ili9320 *lcd, pm_message_t state)
  213. {
  214. int ret;
  215. dev_dbg(lcd->dev, "%s: event %d\n", __func__, state.event);
  216. if (state.event == PM_EVENT_SUSPEND) {
  217. ret = ili9320_power(lcd, FB_BLANK_POWERDOWN);
  218. if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
  219. ili9320_write(lcd, ILI9320_POWER1, lcd->power1 |
  220. ILI9320_POWER1_SLP |
  221. ILI9320_POWER1_DSTB);
  222. lcd->initialised = 0;
  223. }
  224. return ret;
  225. }
  226. return 0;
  227. }
  228. EXPORT_SYMBOL_GPL(ili9320_suspend);
  229. int ili9320_resume(struct ili9320 *lcd)
  230. {
  231. dev_info(lcd->dev, "resuming from power state %d\n", lcd->power);
  232. if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
  233. ili9320_write(lcd, ILI9320_POWER1, 0x00);
  234. }
  235. return ili9320_power(lcd, FB_BLANK_UNBLANK);
  236. }
  237. EXPORT_SYMBOL_GPL(ili9320_resume);
  238. #endif
  239. /* Power down all displays on reboot, poweroff or halt */
  240. void ili9320_shutdown(struct ili9320 *lcd)
  241. {
  242. ili9320_power(lcd, FB_BLANK_POWERDOWN);
  243. }
  244. EXPORT_SYMBOL_GPL(ili9320_shutdown);
  245. MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
  246. MODULE_DESCRIPTION("ILI9320 LCD Driver");
  247. MODULE_LICENSE("GPL v2");