panel-samsung-s6d16d0.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * MIPI-DSI Samsung s6d16d0 panel driver. This is a 864x480
  4. * AMOLED panel with a command-only DSI interface.
  5. */
  6. #include <drm/drm_modes.h>
  7. #include <drm/drm_mipi_dsi.h>
  8. #include <drm/drm_panel.h>
  9. #include <linux/gpio/consumer.h>
  10. #include <linux/regulator/consumer.h>
  11. #include <linux/delay.h>
  12. #include <linux/of_device.h>
  13. #include <linux/module.h>
  14. struct s6d16d0 {
  15. struct device *dev;
  16. struct drm_panel panel;
  17. struct regulator *supply;
  18. struct gpio_desc *reset_gpio;
  19. };
  20. /*
  21. * The timings are not very helpful as the display is used in
  22. * command mode.
  23. */
  24. static const struct drm_display_mode samsung_s6d16d0_mode = {
  25. /* HS clock, (htotal*vtotal*vrefresh)/1000 */
  26. .clock = 420160,
  27. .hdisplay = 864,
  28. .hsync_start = 864 + 154,
  29. .hsync_end = 864 + 154 + 16,
  30. .htotal = 864 + 154 + 16 + 32,
  31. .vdisplay = 480,
  32. .vsync_start = 480 + 1,
  33. .vsync_end = 480 + 1 + 1,
  34. .vtotal = 480 + 1 + 1 + 1,
  35. .width_mm = 84,
  36. .height_mm = 48,
  37. };
  38. static inline struct s6d16d0 *panel_to_s6d16d0(struct drm_panel *panel)
  39. {
  40. return container_of(panel, struct s6d16d0, panel);
  41. }
  42. static int s6d16d0_unprepare(struct drm_panel *panel)
  43. {
  44. struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
  45. struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
  46. int ret;
  47. /* Enter sleep mode */
  48. ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
  49. if (ret) {
  50. dev_err(s6->dev, "failed to enter sleep mode (%d)\n", ret);
  51. return ret;
  52. }
  53. /* Assert RESET */
  54. gpiod_set_value_cansleep(s6->reset_gpio, 1);
  55. regulator_disable(s6->supply);
  56. return 0;
  57. }
  58. static int s6d16d0_prepare(struct drm_panel *panel)
  59. {
  60. struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
  61. struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
  62. int ret;
  63. ret = regulator_enable(s6->supply);
  64. if (ret) {
  65. dev_err(s6->dev, "failed to enable supply (%d)\n", ret);
  66. return ret;
  67. }
  68. /* Assert RESET */
  69. gpiod_set_value_cansleep(s6->reset_gpio, 1);
  70. udelay(10);
  71. /* De-assert RESET */
  72. gpiod_set_value_cansleep(s6->reset_gpio, 0);
  73. msleep(120);
  74. /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
  75. ret = mipi_dsi_dcs_set_tear_on(dsi,
  76. MIPI_DSI_DCS_TEAR_MODE_VBLANK);
  77. if (ret) {
  78. dev_err(s6->dev, "failed to enable vblank TE (%d)\n", ret);
  79. return ret;
  80. }
  81. /* Exit sleep mode and power on */
  82. ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
  83. if (ret) {
  84. dev_err(s6->dev, "failed to exit sleep mode (%d)\n", ret);
  85. return ret;
  86. }
  87. return 0;
  88. }
  89. static int s6d16d0_enable(struct drm_panel *panel)
  90. {
  91. struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
  92. struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
  93. int ret;
  94. ret = mipi_dsi_dcs_set_display_on(dsi);
  95. if (ret) {
  96. dev_err(s6->dev, "failed to turn display on (%d)\n", ret);
  97. return ret;
  98. }
  99. return 0;
  100. }
  101. static int s6d16d0_disable(struct drm_panel *panel)
  102. {
  103. struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
  104. struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
  105. int ret;
  106. ret = mipi_dsi_dcs_set_display_off(dsi);
  107. if (ret) {
  108. dev_err(s6->dev, "failed to turn display off (%d)\n", ret);
  109. return ret;
  110. }
  111. return 0;
  112. }
  113. static int s6d16d0_get_modes(struct drm_panel *panel,
  114. struct drm_connector *connector)
  115. {
  116. struct drm_display_mode *mode;
  117. mode = drm_mode_duplicate(connector->dev, &samsung_s6d16d0_mode);
  118. if (!mode) {
  119. dev_err(panel->dev, "bad mode or failed to add mode\n");
  120. return -EINVAL;
  121. }
  122. drm_mode_set_name(mode);
  123. mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  124. connector->display_info.width_mm = mode->width_mm;
  125. connector->display_info.height_mm = mode->height_mm;
  126. drm_mode_probed_add(connector, mode);
  127. return 1; /* Number of modes */
  128. }
  129. static const struct drm_panel_funcs s6d16d0_drm_funcs = {
  130. .disable = s6d16d0_disable,
  131. .unprepare = s6d16d0_unprepare,
  132. .prepare = s6d16d0_prepare,
  133. .enable = s6d16d0_enable,
  134. .get_modes = s6d16d0_get_modes,
  135. };
  136. static int s6d16d0_probe(struct mipi_dsi_device *dsi)
  137. {
  138. struct device *dev = &dsi->dev;
  139. struct s6d16d0 *s6;
  140. int ret;
  141. s6 = devm_kzalloc(dev, sizeof(struct s6d16d0), GFP_KERNEL);
  142. if (!s6)
  143. return -ENOMEM;
  144. mipi_dsi_set_drvdata(dsi, s6);
  145. s6->dev = dev;
  146. dsi->lanes = 2;
  147. dsi->format = MIPI_DSI_FMT_RGB888;
  148. dsi->hs_rate = 420160000;
  149. dsi->lp_rate = 19200000;
  150. /*
  151. * This display uses command mode so no MIPI_DSI_MODE_VIDEO
  152. * or MIPI_DSI_MODE_VIDEO_SYNC_PULSE
  153. *
  154. * As we only send commands we do not need to be continuously
  155. * clocked.
  156. */
  157. dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
  158. s6->supply = devm_regulator_get(dev, "vdd1");
  159. if (IS_ERR(s6->supply))
  160. return PTR_ERR(s6->supply);
  161. /* This asserts RESET by default */
  162. s6->reset_gpio = devm_gpiod_get_optional(dev, "reset",
  163. GPIOD_OUT_HIGH);
  164. if (IS_ERR(s6->reset_gpio)) {
  165. ret = PTR_ERR(s6->reset_gpio);
  166. if (ret != -EPROBE_DEFER)
  167. dev_err(dev, "failed to request GPIO (%d)\n", ret);
  168. return ret;
  169. }
  170. drm_panel_init(&s6->panel, dev, &s6d16d0_drm_funcs,
  171. DRM_MODE_CONNECTOR_DSI);
  172. drm_panel_add(&s6->panel);
  173. ret = mipi_dsi_attach(dsi);
  174. if (ret < 0)
  175. drm_panel_remove(&s6->panel);
  176. return ret;
  177. }
  178. static int s6d16d0_remove(struct mipi_dsi_device *dsi)
  179. {
  180. struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi);
  181. mipi_dsi_detach(dsi);
  182. drm_panel_remove(&s6->panel);
  183. return 0;
  184. }
  185. static const struct of_device_id s6d16d0_of_match[] = {
  186. { .compatible = "samsung,s6d16d0" },
  187. { }
  188. };
  189. MODULE_DEVICE_TABLE(of, s6d16d0_of_match);
  190. static struct mipi_dsi_driver s6d16d0_driver = {
  191. .probe = s6d16d0_probe,
  192. .remove = s6d16d0_remove,
  193. .driver = {
  194. .name = "panel-samsung-s6d16d0",
  195. .of_match_table = s6d16d0_of_match,
  196. },
  197. };
  198. module_mipi_dsi_driver(s6d16d0_driver);
  199. MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
  200. MODULE_DESCRIPTION("MIPI-DSI s6d16d0 Panel Driver");
  201. MODULE_LICENSE("GPL v2");