panel-samsung-s6e3fa7.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Driver for the Samsung S6E3FA7 panel.
  4. *
  5. * Copyright (c) 2022-2024, The Linux Foundation. All rights reserved.
  6. * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
  7. * Copyright (c) 2013, The Linux Foundation. All rights reserved.
  8. */
  9. #include <linux/backlight.h>
  10. #include <linux/delay.h>
  11. #include <linux/gpio/consumer.h>
  12. #include <linux/module.h>
  13. #include <linux/of.h>
  14. #include <video/mipi_display.h>
  15. #include <drm/drm_mipi_dsi.h>
  16. #include <drm/drm_modes.h>
  17. #include <drm/drm_panel.h>
  18. struct s6e3fa7_panel {
  19. struct drm_panel panel;
  20. struct mipi_dsi_device *dsi;
  21. struct gpio_desc *reset_gpio;
  22. };
  23. static inline struct s6e3fa7_panel *to_s6e3fa7_panel(struct drm_panel *panel)
  24. {
  25. return container_of(panel, struct s6e3fa7_panel, panel);
  26. }
  27. static void s6e3fa7_panel_reset(struct s6e3fa7_panel *ctx)
  28. {
  29. gpiod_set_value_cansleep(ctx->reset_gpio, 1);
  30. usleep_range(1000, 2000);
  31. gpiod_set_value_cansleep(ctx->reset_gpio, 0);
  32. usleep_range(10000, 11000);
  33. }
  34. static int s6e3fa7_panel_on(struct s6e3fa7_panel *ctx)
  35. {
  36. struct mipi_dsi_device *dsi = ctx->dsi;
  37. struct device *dev = &dsi->dev;
  38. int ret;
  39. ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
  40. if (ret < 0) {
  41. dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
  42. return ret;
  43. }
  44. msleep(120);
  45. ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
  46. if (ret < 0) {
  47. dev_err(dev, "Failed to set tear on: %d\n", ret);
  48. return ret;
  49. }
  50. mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
  51. mipi_dsi_dcs_write_seq(dsi, 0xf4,
  52. 0xbb, 0x23, 0x19, 0x3a, 0x9f, 0x0f, 0x09, 0xc0,
  53. 0x00, 0xb4, 0x37, 0x70, 0x79, 0x69);
  54. mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
  55. mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
  56. ret = mipi_dsi_dcs_set_display_on(dsi);
  57. if (ret < 0) {
  58. dev_err(dev, "Failed to set display on: %d\n", ret);
  59. return ret;
  60. }
  61. return 0;
  62. }
  63. static int s6e3fa7_panel_prepare(struct drm_panel *panel)
  64. {
  65. struct s6e3fa7_panel *ctx = to_s6e3fa7_panel(panel);
  66. struct device *dev = &ctx->dsi->dev;
  67. int ret;
  68. s6e3fa7_panel_reset(ctx);
  69. ret = s6e3fa7_panel_on(ctx);
  70. if (ret < 0) {
  71. dev_err(dev, "Failed to initialize panel: %d\n", ret);
  72. gpiod_set_value_cansleep(ctx->reset_gpio, 1);
  73. return ret;
  74. }
  75. return 0;
  76. }
  77. static int s6e3fa7_panel_unprepare(struct drm_panel *panel)
  78. {
  79. struct s6e3fa7_panel *ctx = to_s6e3fa7_panel(panel);
  80. gpiod_set_value_cansleep(ctx->reset_gpio, 1);
  81. return 0;
  82. }
  83. static int s6e3fa7_panel_disable(struct drm_panel *panel)
  84. {
  85. struct s6e3fa7_panel *ctx = to_s6e3fa7_panel(panel);
  86. struct mipi_dsi_device *dsi = ctx->dsi;
  87. struct device *dev = &dsi->dev;
  88. int ret;
  89. ret = mipi_dsi_dcs_set_display_off(dsi);
  90. if (ret < 0) {
  91. dev_err(dev, "Failed to set display off: %d\n", ret);
  92. return ret;
  93. }
  94. ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
  95. if (ret < 0) {
  96. dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
  97. return ret;
  98. }
  99. msleep(120);
  100. return 0;
  101. }
  102. static const struct drm_display_mode s6e3fa7_panel_mode = {
  103. .clock = (1080 + 32 + 32 + 78) * (2220 + 32 + 4 + 78) * 60 / 1000,
  104. .hdisplay = 1080,
  105. .hsync_start = 1080 + 32,
  106. .hsync_end = 1080 + 32 + 32,
  107. .htotal = 1080 + 32 + 32 + 78,
  108. .vdisplay = 2220,
  109. .vsync_start = 2220 + 32,
  110. .vsync_end = 2220 + 32 + 4,
  111. .vtotal = 2220 + 32 + 4 + 78,
  112. .width_mm = 62,
  113. .height_mm = 127,
  114. };
  115. static int s6e3fa7_panel_get_modes(struct drm_panel *panel,
  116. struct drm_connector *connector)
  117. {
  118. struct drm_display_mode *mode;
  119. mode = drm_mode_duplicate(connector->dev, &s6e3fa7_panel_mode);
  120. if (!mode)
  121. return -ENOMEM;
  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;
  128. }
  129. static const struct drm_panel_funcs s6e3fa7_panel_funcs = {
  130. .prepare = s6e3fa7_panel_prepare,
  131. .unprepare = s6e3fa7_panel_unprepare,
  132. .disable = s6e3fa7_panel_disable,
  133. .get_modes = s6e3fa7_panel_get_modes,
  134. };
  135. static int s6e3fa7_panel_bl_update_status(struct backlight_device *bl)
  136. {
  137. struct mipi_dsi_device *dsi = bl_get_data(bl);
  138. u16 brightness = backlight_get_brightness(bl);
  139. int ret;
  140. ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
  141. if (ret < 0)
  142. return ret;
  143. return 0;
  144. }
  145. static int s6e3fa7_panel_bl_get_brightness(struct backlight_device *bl)
  146. {
  147. struct mipi_dsi_device *dsi = bl_get_data(bl);
  148. u16 brightness;
  149. int ret;
  150. ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
  151. if (ret < 0)
  152. return ret;
  153. return brightness;
  154. }
  155. static const struct backlight_ops s6e3fa7_panel_bl_ops = {
  156. .update_status = s6e3fa7_panel_bl_update_status,
  157. .get_brightness = s6e3fa7_panel_bl_get_brightness,
  158. };
  159. static struct backlight_device *
  160. s6e3fa7_panel_create_backlight(struct mipi_dsi_device *dsi)
  161. {
  162. struct device *dev = &dsi->dev;
  163. const struct backlight_properties props = {
  164. .type = BACKLIGHT_RAW,
  165. .brightness = 1023,
  166. .max_brightness = 1023,
  167. };
  168. return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
  169. &s6e3fa7_panel_bl_ops, &props);
  170. }
  171. static int s6e3fa7_panel_probe(struct mipi_dsi_device *dsi)
  172. {
  173. struct device *dev = &dsi->dev;
  174. struct s6e3fa7_panel *ctx;
  175. int ret;
  176. ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
  177. if (!ctx)
  178. return -ENOMEM;
  179. ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
  180. if (IS_ERR(ctx->reset_gpio))
  181. return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
  182. "Failed to get reset-gpios\n");
  183. ctx->dsi = dsi;
  184. mipi_dsi_set_drvdata(dsi, ctx);
  185. dsi->lanes = 4;
  186. dsi->format = MIPI_DSI_FMT_RGB888;
  187. dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
  188. MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
  189. drm_panel_init(&ctx->panel, dev, &s6e3fa7_panel_funcs,
  190. DRM_MODE_CONNECTOR_DSI);
  191. ctx->panel.prepare_prev_first = true;
  192. ctx->panel.backlight = s6e3fa7_panel_create_backlight(dsi);
  193. if (IS_ERR(ctx->panel.backlight))
  194. return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
  195. "Failed to create backlight\n");
  196. drm_panel_add(&ctx->panel);
  197. ret = mipi_dsi_attach(dsi);
  198. if (ret < 0) {
  199. dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
  200. drm_panel_remove(&ctx->panel);
  201. return ret;
  202. }
  203. return 0;
  204. }
  205. static void s6e3fa7_panel_remove(struct mipi_dsi_device *dsi)
  206. {
  207. struct s6e3fa7_panel *ctx = mipi_dsi_get_drvdata(dsi);
  208. int ret;
  209. ret = mipi_dsi_detach(dsi);
  210. if (ret < 0)
  211. dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
  212. drm_panel_remove(&ctx->panel);
  213. }
  214. static const struct of_device_id s6e3fa7_panel_of_match[] = {
  215. { .compatible = "samsung,s6e3fa7-ams559nk06" },
  216. { /* sentinel */ }
  217. };
  218. MODULE_DEVICE_TABLE(of, s6e3fa7_panel_of_match);
  219. static struct mipi_dsi_driver s6e3fa7_panel_driver = {
  220. .probe = s6e3fa7_panel_probe,
  221. .remove = s6e3fa7_panel_remove,
  222. .driver = {
  223. .name = "panel-samsung-s6e3fa7",
  224. .of_match_table = s6e3fa7_panel_of_match,
  225. },
  226. };
  227. module_mipi_dsi_driver(s6e3fa7_panel_driver);
  228. MODULE_AUTHOR("Richard Acayan <mailingradian@gmail.com>");
  229. MODULE_DESCRIPTION("DRM driver for Samsung S6E3FA7 command mode DSI panel");
  230. MODULE_LICENSE("GPL");