thc63lvd1024.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * THC63LVD1024 LVDS to parallel data DRM bridge driver.
  4. *
  5. * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
  6. */
  7. #include <drm/drmP.h>
  8. #include <drm/drm_bridge.h>
  9. #include <drm/drm_panel.h>
  10. #include <linux/gpio/consumer.h>
  11. #include <linux/of_graph.h>
  12. #include <linux/regulator/consumer.h>
  13. #include <linux/slab.h>
  14. enum thc63_ports {
  15. THC63_LVDS_IN0,
  16. THC63_LVDS_IN1,
  17. THC63_RGB_OUT0,
  18. THC63_RGB_OUT1,
  19. };
  20. struct thc63_dev {
  21. struct device *dev;
  22. struct regulator *vcc;
  23. struct gpio_desc *pdwn;
  24. struct gpio_desc *oe;
  25. struct drm_bridge bridge;
  26. struct drm_bridge *next;
  27. };
  28. static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
  29. {
  30. return container_of(bridge, struct thc63_dev, bridge);
  31. }
  32. static int thc63_attach(struct drm_bridge *bridge)
  33. {
  34. struct thc63_dev *thc63 = to_thc63(bridge);
  35. return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
  36. }
  37. static void thc63_enable(struct drm_bridge *bridge)
  38. {
  39. struct thc63_dev *thc63 = to_thc63(bridge);
  40. int ret;
  41. ret = regulator_enable(thc63->vcc);
  42. if (ret) {
  43. dev_err(thc63->dev,
  44. "Failed to enable regulator \"vcc\": %d\n", ret);
  45. return;
  46. }
  47. gpiod_set_value(thc63->pdwn, 0);
  48. gpiod_set_value(thc63->oe, 1);
  49. }
  50. static void thc63_disable(struct drm_bridge *bridge)
  51. {
  52. struct thc63_dev *thc63 = to_thc63(bridge);
  53. int ret;
  54. gpiod_set_value(thc63->oe, 0);
  55. gpiod_set_value(thc63->pdwn, 1);
  56. ret = regulator_disable(thc63->vcc);
  57. if (ret)
  58. dev_err(thc63->dev,
  59. "Failed to disable regulator \"vcc\": %d\n", ret);
  60. }
  61. static const struct drm_bridge_funcs thc63_bridge_func = {
  62. .attach = thc63_attach,
  63. .enable = thc63_enable,
  64. .disable = thc63_disable,
  65. };
  66. static int thc63_parse_dt(struct thc63_dev *thc63)
  67. {
  68. struct device_node *thc63_out;
  69. struct device_node *remote;
  70. thc63_out = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
  71. THC63_RGB_OUT0, -1);
  72. if (!thc63_out) {
  73. dev_err(thc63->dev, "Missing endpoint in port@%u\n",
  74. THC63_RGB_OUT0);
  75. return -ENODEV;
  76. }
  77. remote = of_graph_get_remote_port_parent(thc63_out);
  78. of_node_put(thc63_out);
  79. if (!remote) {
  80. dev_err(thc63->dev, "Endpoint in port@%u unconnected\n",
  81. THC63_RGB_OUT0);
  82. return -ENODEV;
  83. }
  84. if (!of_device_is_available(remote)) {
  85. dev_err(thc63->dev, "port@%u remote endpoint is disabled\n",
  86. THC63_RGB_OUT0);
  87. of_node_put(remote);
  88. return -ENODEV;
  89. }
  90. thc63->next = of_drm_find_bridge(remote);
  91. of_node_put(remote);
  92. if (!thc63->next)
  93. return -EPROBE_DEFER;
  94. return 0;
  95. }
  96. static int thc63_gpio_init(struct thc63_dev *thc63)
  97. {
  98. thc63->oe = devm_gpiod_get_optional(thc63->dev, "oe", GPIOD_OUT_LOW);
  99. if (IS_ERR(thc63->oe)) {
  100. dev_err(thc63->dev, "Unable to get \"oe-gpios\": %ld\n",
  101. PTR_ERR(thc63->oe));
  102. return PTR_ERR(thc63->oe);
  103. }
  104. thc63->pdwn = devm_gpiod_get_optional(thc63->dev, "powerdown",
  105. GPIOD_OUT_HIGH);
  106. if (IS_ERR(thc63->pdwn)) {
  107. dev_err(thc63->dev, "Unable to get \"powerdown-gpios\": %ld\n",
  108. PTR_ERR(thc63->pdwn));
  109. return PTR_ERR(thc63->pdwn);
  110. }
  111. return 0;
  112. }
  113. static int thc63_probe(struct platform_device *pdev)
  114. {
  115. struct thc63_dev *thc63;
  116. int ret;
  117. thc63 = devm_kzalloc(&pdev->dev, sizeof(*thc63), GFP_KERNEL);
  118. if (!thc63)
  119. return -ENOMEM;
  120. thc63->dev = &pdev->dev;
  121. platform_set_drvdata(pdev, thc63);
  122. thc63->vcc = devm_regulator_get_optional(thc63->dev, "vcc");
  123. if (IS_ERR(thc63->vcc)) {
  124. if (PTR_ERR(thc63->vcc) == -EPROBE_DEFER)
  125. return -EPROBE_DEFER;
  126. dev_err(thc63->dev, "Unable to get \"vcc\" supply: %ld\n",
  127. PTR_ERR(thc63->vcc));
  128. return PTR_ERR(thc63->vcc);
  129. }
  130. ret = thc63_gpio_init(thc63);
  131. if (ret)
  132. return ret;
  133. ret = thc63_parse_dt(thc63);
  134. if (ret)
  135. return ret;
  136. thc63->bridge.driver_private = thc63;
  137. thc63->bridge.of_node = pdev->dev.of_node;
  138. thc63->bridge.funcs = &thc63_bridge_func;
  139. drm_bridge_add(&thc63->bridge);
  140. return 0;
  141. }
  142. static int thc63_remove(struct platform_device *pdev)
  143. {
  144. struct thc63_dev *thc63 = platform_get_drvdata(pdev);
  145. drm_bridge_remove(&thc63->bridge);
  146. return 0;
  147. }
  148. static const struct of_device_id thc63_match[] = {
  149. { .compatible = "thine,thc63lvd1024", },
  150. { },
  151. };
  152. MODULE_DEVICE_TABLE(of, thc63_match);
  153. static struct platform_driver thc63_driver = {
  154. .probe = thc63_probe,
  155. .remove = thc63_remove,
  156. .driver = {
  157. .name = "thc63lvd1024",
  158. .of_match_table = thc63_match,
  159. },
  160. };
  161. module_platform_driver(thc63_driver);
  162. MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
  163. MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
  164. MODULE_LICENSE("GPL v2");