panel-lg-lb035q02.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * LG.Philips LB035Q02 LCD Panel Driver
  4. *
  5. * Copyright (C) 2019 Texas Instruments Incorporated
  6. *
  7. * Based on the omapdrm-specific panel-lgphilips-lb035q02 driver
  8. *
  9. * Copyright (C) 2013 Texas Instruments Incorporated
  10. * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
  11. *
  12. * Based on a driver by: Steve Sakoman <steve@sakoman.com>
  13. */
  14. #include <linux/gpio/consumer.h>
  15. #include <linux/module.h>
  16. #include <linux/spi/spi.h>
  17. #include <drm/drm_connector.h>
  18. #include <drm/drm_modes.h>
  19. #include <drm/drm_panel.h>
  20. struct lb035q02_device {
  21. struct drm_panel panel;
  22. struct spi_device *spi;
  23. struct gpio_desc *enable_gpio;
  24. };
  25. #define to_lb035q02_device(p) container_of(p, struct lb035q02_device, panel)
  26. static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val)
  27. {
  28. struct spi_message msg;
  29. struct spi_transfer index_xfer = {
  30. .len = 3,
  31. .cs_change = 1,
  32. };
  33. struct spi_transfer value_xfer = {
  34. .len = 3,
  35. };
  36. u8 buffer[16];
  37. spi_message_init(&msg);
  38. /* register index */
  39. buffer[0] = 0x70;
  40. buffer[1] = 0x00;
  41. buffer[2] = reg & 0x7f;
  42. index_xfer.tx_buf = buffer;
  43. spi_message_add_tail(&index_xfer, &msg);
  44. /* register value */
  45. buffer[4] = 0x72;
  46. buffer[5] = val >> 8;
  47. buffer[6] = val;
  48. value_xfer.tx_buf = buffer + 4;
  49. spi_message_add_tail(&value_xfer, &msg);
  50. return spi_sync(lcd->spi, &msg);
  51. }
  52. static int lb035q02_init(struct lb035q02_device *lcd)
  53. {
  54. /* Init sequence from page 28 of the lb035q02 spec. */
  55. static const struct {
  56. u16 index;
  57. u16 value;
  58. } init_data[] = {
  59. { 0x01, 0x6300 },
  60. { 0x02, 0x0200 },
  61. { 0x03, 0x0177 },
  62. { 0x04, 0x04c7 },
  63. { 0x05, 0xffc0 },
  64. { 0x06, 0xe806 },
  65. { 0x0a, 0x4008 },
  66. { 0x0b, 0x0000 },
  67. { 0x0d, 0x0030 },
  68. { 0x0e, 0x2800 },
  69. { 0x0f, 0x0000 },
  70. { 0x16, 0x9f80 },
  71. { 0x17, 0x0a0f },
  72. { 0x1e, 0x00c1 },
  73. { 0x30, 0x0300 },
  74. { 0x31, 0x0007 },
  75. { 0x32, 0x0000 },
  76. { 0x33, 0x0000 },
  77. { 0x34, 0x0707 },
  78. { 0x35, 0x0004 },
  79. { 0x36, 0x0302 },
  80. { 0x37, 0x0202 },
  81. { 0x3a, 0x0a0d },
  82. { 0x3b, 0x0806 },
  83. };
  84. unsigned int i;
  85. int ret;
  86. for (i = 0; i < ARRAY_SIZE(init_data); ++i) {
  87. ret = lb035q02_write(lcd, init_data[i].index,
  88. init_data[i].value);
  89. if (ret < 0)
  90. return ret;
  91. }
  92. return 0;
  93. }
  94. static int lb035q02_disable(struct drm_panel *panel)
  95. {
  96. struct lb035q02_device *lcd = to_lb035q02_device(panel);
  97. gpiod_set_value_cansleep(lcd->enable_gpio, 0);
  98. return 0;
  99. }
  100. static int lb035q02_enable(struct drm_panel *panel)
  101. {
  102. struct lb035q02_device *lcd = to_lb035q02_device(panel);
  103. gpiod_set_value_cansleep(lcd->enable_gpio, 1);
  104. return 0;
  105. }
  106. static const struct drm_display_mode lb035q02_mode = {
  107. .clock = 6500,
  108. .hdisplay = 320,
  109. .hsync_start = 320 + 20,
  110. .hsync_end = 320 + 20 + 2,
  111. .htotal = 320 + 20 + 2 + 68,
  112. .vdisplay = 240,
  113. .vsync_start = 240 + 4,
  114. .vsync_end = 240 + 4 + 2,
  115. .vtotal = 240 + 4 + 2 + 18,
  116. .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
  117. .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
  118. .width_mm = 70,
  119. .height_mm = 53,
  120. };
  121. static int lb035q02_get_modes(struct drm_panel *panel,
  122. struct drm_connector *connector)
  123. {
  124. struct drm_display_mode *mode;
  125. mode = drm_mode_duplicate(connector->dev, &lb035q02_mode);
  126. if (!mode)
  127. return -ENOMEM;
  128. drm_mode_set_name(mode);
  129. drm_mode_probed_add(connector, mode);
  130. connector->display_info.width_mm = lb035q02_mode.width_mm;
  131. connector->display_info.height_mm = lb035q02_mode.height_mm;
  132. /*
  133. * FIXME: According to the datasheet pixel data is sampled on the
  134. * rising edge of the clock, but the code running on the Gumstix Overo
  135. * Palo35 indicates sampling on the negative edge. This should be
  136. * tested on a real device.
  137. */
  138. connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
  139. | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
  140. | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
  141. return 1;
  142. }
  143. static const struct drm_panel_funcs lb035q02_funcs = {
  144. .disable = lb035q02_disable,
  145. .enable = lb035q02_enable,
  146. .get_modes = lb035q02_get_modes,
  147. };
  148. static int lb035q02_probe(struct spi_device *spi)
  149. {
  150. struct lb035q02_device *lcd;
  151. int ret;
  152. lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
  153. if (!lcd)
  154. return -ENOMEM;
  155. spi_set_drvdata(spi, lcd);
  156. lcd->spi = spi;
  157. lcd->enable_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
  158. if (IS_ERR(lcd->enable_gpio)) {
  159. dev_err(&spi->dev, "failed to parse enable gpio\n");
  160. return PTR_ERR(lcd->enable_gpio);
  161. }
  162. ret = lb035q02_init(lcd);
  163. if (ret < 0)
  164. return ret;
  165. drm_panel_init(&lcd->panel, &lcd->spi->dev, &lb035q02_funcs,
  166. DRM_MODE_CONNECTOR_DPI);
  167. drm_panel_add(&lcd->panel);
  168. return 0;
  169. }
  170. static int lb035q02_remove(struct spi_device *spi)
  171. {
  172. struct lb035q02_device *lcd = spi_get_drvdata(spi);
  173. drm_panel_remove(&lcd->panel);
  174. drm_panel_disable(&lcd->panel);
  175. return 0;
  176. }
  177. static const struct of_device_id lb035q02_of_match[] = {
  178. { .compatible = "lgphilips,lb035q02", },
  179. { /* sentinel */ },
  180. };
  181. MODULE_DEVICE_TABLE(of, lb035q02_of_match);
  182. static const struct spi_device_id lb035q02_ids[] = {
  183. { "lb035q02", 0 },
  184. { /* sentinel */ }
  185. };
  186. MODULE_DEVICE_TABLE(spi, lb035q02_ids);
  187. static struct spi_driver lb035q02_driver = {
  188. .probe = lb035q02_probe,
  189. .remove = lb035q02_remove,
  190. .id_table = lb035q02_ids,
  191. .driver = {
  192. .name = "panel-lg-lb035q02",
  193. .of_match_table = lb035q02_of_match,
  194. },
  195. };
  196. module_spi_driver(lb035q02_driver);
  197. MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
  198. MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
  199. MODULE_LICENSE("GPL");