panel-tdo-tl070wsh30.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020 BayLibre, SAS
  4. * Author: Neil Armstrong <[email protected]>
  5. */
  6. #include <linux/delay.h>
  7. #include <linux/gpio/consumer.h>
  8. #include <linux/module.h>
  9. #include <linux/of.h>
  10. #include <linux/regulator/consumer.h>
  11. #include <video/mipi_display.h>
  12. #include <drm/drm_crtc.h>
  13. #include <drm/drm_device.h>
  14. #include <drm/drm_mipi_dsi.h>
  15. #include <drm/drm_modes.h>
  16. #include <drm/drm_panel.h>
  17. struct tdo_tl070wsh30_panel {
  18. struct drm_panel base;
  19. struct mipi_dsi_device *link;
  20. struct regulator *supply;
  21. struct gpio_desc *reset_gpio;
  22. bool prepared;
  23. };
  24. static inline
  25. struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
  26. {
  27. return container_of(panel, struct tdo_tl070wsh30_panel, base);
  28. }
  29. static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
  30. {
  31. struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
  32. int err;
  33. if (tdo_tl070wsh30->prepared)
  34. return 0;
  35. err = regulator_enable(tdo_tl070wsh30->supply);
  36. if (err < 0)
  37. return err;
  38. usleep_range(10000, 11000);
  39. gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 1);
  40. usleep_range(10000, 11000);
  41. gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 0);
  42. msleep(200);
  43. err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
  44. if (err < 0) {
  45. dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
  46. regulator_disable(tdo_tl070wsh30->supply);
  47. return err;
  48. }
  49. msleep(200);
  50. err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
  51. if (err < 0) {
  52. dev_err(panel->dev, "failed to set display on: %d\n", err);
  53. regulator_disable(tdo_tl070wsh30->supply);
  54. return err;
  55. }
  56. msleep(20);
  57. tdo_tl070wsh30->prepared = true;
  58. return 0;
  59. }
  60. static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
  61. {
  62. struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
  63. int err;
  64. if (!tdo_tl070wsh30->prepared)
  65. return 0;
  66. err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
  67. if (err < 0)
  68. dev_err(panel->dev, "failed to set display off: %d\n", err);
  69. usleep_range(10000, 11000);
  70. err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
  71. if (err < 0) {
  72. dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
  73. return err;
  74. }
  75. usleep_range(10000, 11000);
  76. regulator_disable(tdo_tl070wsh30->supply);
  77. tdo_tl070wsh30->prepared = false;
  78. return 0;
  79. }
  80. static const struct drm_display_mode default_mode = {
  81. .clock = 47250,
  82. .hdisplay = 1024,
  83. .hsync_start = 1024 + 46,
  84. .hsync_end = 1024 + 46 + 80,
  85. .htotal = 1024 + 46 + 80 + 100,
  86. .vdisplay = 600,
  87. .vsync_start = 600 + 5,
  88. .vsync_end = 600 + 5 + 5,
  89. .vtotal = 600 + 5 + 5 + 20,
  90. .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
  91. };
  92. static int tdo_tl070wsh30_panel_get_modes(struct drm_panel *panel,
  93. struct drm_connector *connector)
  94. {
  95. struct drm_display_mode *mode;
  96. mode = drm_mode_duplicate(connector->dev, &default_mode);
  97. if (!mode) {
  98. dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
  99. default_mode.hdisplay, default_mode.vdisplay,
  100. drm_mode_vrefresh(&default_mode));
  101. return -ENOMEM;
  102. }
  103. drm_mode_set_name(mode);
  104. drm_mode_probed_add(connector, mode);
  105. connector->display_info.width_mm = 154;
  106. connector->display_info.height_mm = 85;
  107. connector->display_info.bpc = 8;
  108. return 1;
  109. }
  110. static const struct drm_panel_funcs tdo_tl070wsh30_panel_funcs = {
  111. .unprepare = tdo_tl070wsh30_panel_unprepare,
  112. .prepare = tdo_tl070wsh30_panel_prepare,
  113. .get_modes = tdo_tl070wsh30_panel_get_modes,
  114. };
  115. static const struct of_device_id tdo_tl070wsh30_of_match[] = {
  116. { .compatible = "tdo,tl070wsh30", },
  117. { /* sentinel */ }
  118. };
  119. MODULE_DEVICE_TABLE(of, tdo_tl070wsh30_of_match);
  120. static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30)
  121. {
  122. struct device *dev = &tdo_tl070wsh30->link->dev;
  123. int err;
  124. tdo_tl070wsh30->supply = devm_regulator_get(dev, "power");
  125. if (IS_ERR(tdo_tl070wsh30->supply))
  126. return PTR_ERR(tdo_tl070wsh30->supply);
  127. tdo_tl070wsh30->reset_gpio = devm_gpiod_get(dev, "reset",
  128. GPIOD_OUT_LOW);
  129. if (IS_ERR(tdo_tl070wsh30->reset_gpio)) {
  130. err = PTR_ERR(tdo_tl070wsh30->reset_gpio);
  131. dev_dbg(dev, "failed to get reset gpio: %d\n", err);
  132. return err;
  133. }
  134. drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev,
  135. &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI);
  136. err = drm_panel_of_backlight(&tdo_tl070wsh30->base);
  137. if (err)
  138. return err;
  139. drm_panel_add(&tdo_tl070wsh30->base);
  140. return 0;
  141. }
  142. static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi)
  143. {
  144. struct tdo_tl070wsh30_panel *tdo_tl070wsh30;
  145. int err;
  146. dsi->lanes = 4;
  147. dsi->format = MIPI_DSI_FMT_RGB888;
  148. dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
  149. tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30),
  150. GFP_KERNEL);
  151. if (!tdo_tl070wsh30)
  152. return -ENOMEM;
  153. mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30);
  154. tdo_tl070wsh30->link = dsi;
  155. err = tdo_tl070wsh30_panel_add(tdo_tl070wsh30);
  156. if (err < 0)
  157. return err;
  158. return mipi_dsi_attach(dsi);
  159. }
  160. static void tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi)
  161. {
  162. struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
  163. int err;
  164. err = mipi_dsi_detach(dsi);
  165. if (err < 0)
  166. dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
  167. drm_panel_remove(&tdo_tl070wsh30->base);
  168. drm_panel_disable(&tdo_tl070wsh30->base);
  169. drm_panel_unprepare(&tdo_tl070wsh30->base);
  170. }
  171. static void tdo_tl070wsh30_panel_shutdown(struct mipi_dsi_device *dsi)
  172. {
  173. struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi);
  174. drm_panel_disable(&tdo_tl070wsh30->base);
  175. drm_panel_unprepare(&tdo_tl070wsh30->base);
  176. }
  177. static struct mipi_dsi_driver tdo_tl070wsh30_panel_driver = {
  178. .driver = {
  179. .name = "panel-tdo-tl070wsh30",
  180. .of_match_table = tdo_tl070wsh30_of_match,
  181. },
  182. .probe = tdo_tl070wsh30_panel_probe,
  183. .remove = tdo_tl070wsh30_panel_remove,
  184. .shutdown = tdo_tl070wsh30_panel_shutdown,
  185. };
  186. module_mipi_dsi_driver(tdo_tl070wsh30_panel_driver);
  187. MODULE_AUTHOR("Neil Armstrong <[email protected]>");
  188. MODULE_DESCRIPTION("TDO TL070WSH30 panel driver");
  189. MODULE_LICENSE("GPL v2");