panel-lg-lg4573.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2015 Heiko Schocher <hs@denx.de>
  4. *
  5. * from:
  6. * drivers/gpu/drm/panel/panel-ld9040.c
  7. * ld9040 AMOLED LCD drm_panel driver.
  8. *
  9. * Copyright (c) 2014 Samsung Electronics Co., Ltd
  10. * Derived from drivers/video/backlight/ld9040.c
  11. *
  12. * Andrzej Hajda <a.hajda@samsung.com>
  13. */
  14. #include <linux/delay.h>
  15. #include <linux/gpio/consumer.h>
  16. #include <linux/module.h>
  17. #include <linux/regulator/consumer.h>
  18. #include <linux/spi/spi.h>
  19. #include <video/mipi_display.h>
  20. #include <video/of_videomode.h>
  21. #include <video/videomode.h>
  22. #include <drm/drm_device.h>
  23. #include <drm/drm_modes.h>
  24. #include <drm/drm_panel.h>
  25. struct lg4573 {
  26. struct drm_panel panel;
  27. struct spi_device *spi;
  28. struct videomode vm;
  29. };
  30. static inline struct lg4573 *panel_to_lg4573(struct drm_panel *panel)
  31. {
  32. return container_of(panel, struct lg4573, panel);
  33. }
  34. static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data)
  35. {
  36. struct spi_transfer xfer = {
  37. .len = 2,
  38. };
  39. __be16 temp = cpu_to_be16(data);
  40. struct spi_message msg;
  41. dev_dbg(ctx->panel.dev, "writing data: %x\n", data);
  42. xfer.tx_buf = &temp;
  43. spi_message_init(&msg);
  44. spi_message_add_tail(&xfer, &msg);
  45. return spi_sync(ctx->spi, &msg);
  46. }
  47. static int lg4573_spi_write_u16_array(struct lg4573 *ctx, const u16 *buffer,
  48. unsigned int count)
  49. {
  50. unsigned int i;
  51. int ret;
  52. for (i = 0; i < count; i++) {
  53. ret = lg4573_spi_write_u16(ctx, buffer[i]);
  54. if (ret)
  55. return ret;
  56. }
  57. return 0;
  58. }
  59. static int lg4573_spi_write_dcs(struct lg4573 *ctx, u8 dcs)
  60. {
  61. return lg4573_spi_write_u16(ctx, (0x70 << 8 | dcs));
  62. }
  63. static int lg4573_display_on(struct lg4573 *ctx)
  64. {
  65. int ret;
  66. ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
  67. if (ret)
  68. return ret;
  69. msleep(5);
  70. return lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_ON);
  71. }
  72. static int lg4573_display_off(struct lg4573 *ctx)
  73. {
  74. int ret;
  75. ret = lg4573_spi_write_dcs(ctx, MIPI_DCS_SET_DISPLAY_OFF);
  76. if (ret)
  77. return ret;
  78. msleep(120);
  79. return lg4573_spi_write_dcs(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
  80. }
  81. static int lg4573_display_mode_settings(struct lg4573 *ctx)
  82. {
  83. static const u16 display_mode_settings[] = {
  84. 0x703A, 0x7270, 0x70B1, 0x7208,
  85. 0x723B, 0x720F, 0x70B2, 0x7200,
  86. 0x72C8, 0x70B3, 0x7200, 0x70B4,
  87. 0x7200, 0x70B5, 0x7242, 0x7210,
  88. 0x7210, 0x7200, 0x7220, 0x70B6,
  89. 0x720B, 0x720F, 0x723C, 0x7213,
  90. 0x7213, 0x72E8, 0x70B7, 0x7246,
  91. 0x7206, 0x720C, 0x7200, 0x7200,
  92. };
  93. dev_dbg(ctx->panel.dev, "transfer display mode settings\n");
  94. return lg4573_spi_write_u16_array(ctx, display_mode_settings,
  95. ARRAY_SIZE(display_mode_settings));
  96. }
  97. static int lg4573_power_settings(struct lg4573 *ctx)
  98. {
  99. static const u16 power_settings[] = {
  100. 0x70C0, 0x7201, 0x7211, 0x70C3,
  101. 0x7207, 0x7203, 0x7204, 0x7204,
  102. 0x7204, 0x70C4, 0x7212, 0x7224,
  103. 0x7218, 0x7218, 0x7202, 0x7249,
  104. 0x70C5, 0x726F, 0x70C6, 0x7241,
  105. 0x7263,
  106. };
  107. dev_dbg(ctx->panel.dev, "transfer power settings\n");
  108. return lg4573_spi_write_u16_array(ctx, power_settings,
  109. ARRAY_SIZE(power_settings));
  110. }
  111. static int lg4573_gamma_settings(struct lg4573 *ctx)
  112. {
  113. static const u16 gamma_settings[] = {
  114. 0x70D0, 0x7203, 0x7207, 0x7273,
  115. 0x7235, 0x7200, 0x7201, 0x7220,
  116. 0x7200, 0x7203, 0x70D1, 0x7203,
  117. 0x7207, 0x7273, 0x7235, 0x7200,
  118. 0x7201, 0x7220, 0x7200, 0x7203,
  119. 0x70D2, 0x7203, 0x7207, 0x7273,
  120. 0x7235, 0x7200, 0x7201, 0x7220,
  121. 0x7200, 0x7203, 0x70D3, 0x7203,
  122. 0x7207, 0x7273, 0x7235, 0x7200,
  123. 0x7201, 0x7220, 0x7200, 0x7203,
  124. 0x70D4, 0x7203, 0x7207, 0x7273,
  125. 0x7235, 0x7200, 0x7201, 0x7220,
  126. 0x7200, 0x7203, 0x70D5, 0x7203,
  127. 0x7207, 0x7273, 0x7235, 0x7200,
  128. 0x7201, 0x7220, 0x7200, 0x7203,
  129. };
  130. dev_dbg(ctx->panel.dev, "transfer gamma settings\n");
  131. return lg4573_spi_write_u16_array(ctx, gamma_settings,
  132. ARRAY_SIZE(gamma_settings));
  133. }
  134. static int lg4573_init(struct lg4573 *ctx)
  135. {
  136. int ret;
  137. dev_dbg(ctx->panel.dev, "initializing LCD\n");
  138. ret = lg4573_display_mode_settings(ctx);
  139. if (ret)
  140. return ret;
  141. ret = lg4573_power_settings(ctx);
  142. if (ret)
  143. return ret;
  144. return lg4573_gamma_settings(ctx);
  145. }
  146. static int lg4573_power_on(struct lg4573 *ctx)
  147. {
  148. return lg4573_display_on(ctx);
  149. }
  150. static int lg4573_disable(struct drm_panel *panel)
  151. {
  152. struct lg4573 *ctx = panel_to_lg4573(panel);
  153. return lg4573_display_off(ctx);
  154. }
  155. static int lg4573_enable(struct drm_panel *panel)
  156. {
  157. struct lg4573 *ctx = panel_to_lg4573(panel);
  158. lg4573_init(ctx);
  159. return lg4573_power_on(ctx);
  160. }
  161. static const struct drm_display_mode default_mode = {
  162. .clock = 28341,
  163. .hdisplay = 480,
  164. .hsync_start = 480 + 10,
  165. .hsync_end = 480 + 10 + 59,
  166. .htotal = 480 + 10 + 59 + 10,
  167. .vdisplay = 800,
  168. .vsync_start = 800 + 15,
  169. .vsync_end = 800 + 15 + 15,
  170. .vtotal = 800 + 15 + 15 + 15,
  171. };
  172. static int lg4573_get_modes(struct drm_panel *panel,
  173. struct drm_connector *connector)
  174. {
  175. struct drm_display_mode *mode;
  176. mode = drm_mode_duplicate(connector->dev, &default_mode);
  177. if (!mode) {
  178. dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
  179. default_mode.hdisplay, default_mode.vdisplay,
  180. drm_mode_vrefresh(&default_mode));
  181. return -ENOMEM;
  182. }
  183. drm_mode_set_name(mode);
  184. mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  185. drm_mode_probed_add(connector, mode);
  186. connector->display_info.width_mm = 61;
  187. connector->display_info.height_mm = 103;
  188. return 1;
  189. }
  190. static const struct drm_panel_funcs lg4573_drm_funcs = {
  191. .disable = lg4573_disable,
  192. .enable = lg4573_enable,
  193. .get_modes = lg4573_get_modes,
  194. };
  195. static int lg4573_probe(struct spi_device *spi)
  196. {
  197. struct lg4573 *ctx;
  198. int ret;
  199. ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
  200. if (!ctx)
  201. return -ENOMEM;
  202. ctx->spi = spi;
  203. spi_set_drvdata(spi, ctx);
  204. spi->bits_per_word = 8;
  205. ret = spi_setup(spi);
  206. if (ret < 0) {
  207. dev_err(&spi->dev, "SPI setup failed: %d\n", ret);
  208. return ret;
  209. }
  210. drm_panel_init(&ctx->panel, &spi->dev, &lg4573_drm_funcs,
  211. DRM_MODE_CONNECTOR_DPI);
  212. drm_panel_add(&ctx->panel);
  213. return 0;
  214. }
  215. static int lg4573_remove(struct spi_device *spi)
  216. {
  217. struct lg4573 *ctx = spi_get_drvdata(spi);
  218. lg4573_display_off(ctx);
  219. drm_panel_remove(&ctx->panel);
  220. return 0;
  221. }
  222. static const struct of_device_id lg4573_of_match[] = {
  223. { .compatible = "lg,lg4573" },
  224. { }
  225. };
  226. MODULE_DEVICE_TABLE(of, lg4573_of_match);
  227. static struct spi_driver lg4573_driver = {
  228. .probe = lg4573_probe,
  229. .remove = lg4573_remove,
  230. .driver = {
  231. .name = "lg4573",
  232. .of_match_table = lg4573_of_match,
  233. },
  234. };
  235. module_spi_driver(lg4573_driver);
  236. MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
  237. MODULE_DESCRIPTION("lg4573 LCD Driver");
  238. MODULE_LICENSE("GPL v2");