atmel_hlcdc_output.c 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2014 Traphandler
  4. * Copyright (C) 2014 Free Electrons
  5. * Copyright (C) 2014 Atmel
  6. *
  7. * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
  8. * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
  9. */
  10. #include <linux/media-bus-format.h>
  11. #include <linux/of.h>
  12. #include <linux/of_graph.h>
  13. #include <drm/drm_bridge.h>
  14. #include <drm/drm_encoder.h>
  15. #include <drm/drm_of.h>
  16. #include <drm/drm_print.h>
  17. #include <drm/drm_simple_kms_helper.h>
  18. #include "atmel_hlcdc_dc.h"
  19. struct atmel_hlcdc_rgb_output {
  20. struct drm_encoder encoder;
  21. int bus_fmt;
  22. };
  23. static struct atmel_hlcdc_rgb_output *
  24. atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder)
  25. {
  26. return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
  27. }
  28. int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder)
  29. {
  30. struct atmel_hlcdc_rgb_output *output;
  31. output = atmel_hlcdc_encoder_to_rgb_output(encoder);
  32. return output->bus_fmt;
  33. }
  34. static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep)
  35. {
  36. u32 bus_width;
  37. int ret;
  38. ret = of_property_read_u32(ep, "bus-width", &bus_width);
  39. if (ret == -EINVAL)
  40. return 0;
  41. if (ret)
  42. return ret;
  43. switch (bus_width) {
  44. case 12:
  45. return MEDIA_BUS_FMT_RGB444_1X12;
  46. case 16:
  47. return MEDIA_BUS_FMT_RGB565_1X16;
  48. case 18:
  49. return MEDIA_BUS_FMT_RGB666_1X18;
  50. case 24:
  51. return MEDIA_BUS_FMT_RGB888_1X24;
  52. default:
  53. return -EINVAL;
  54. }
  55. }
  56. static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
  57. {
  58. struct atmel_hlcdc_rgb_output *output;
  59. struct device_node *ep;
  60. struct drm_bridge *bridge;
  61. struct atmel_hlcdc_dc *dc = dev->dev_private;
  62. struct drm_crtc *crtc = dc->crtc;
  63. int ret = 0;
  64. bridge = devm_drm_of_get_bridge(dev->dev, dev->dev->of_node, 0, endpoint);
  65. if (IS_ERR(bridge))
  66. return PTR_ERR(bridge);
  67. output = drmm_simple_encoder_alloc(dev, struct atmel_hlcdc_rgb_output,
  68. encoder, DRM_MODE_ENCODER_NONE);
  69. if (IS_ERR(output))
  70. return PTR_ERR(output);
  71. ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);
  72. if (!ep)
  73. return -ENODEV;
  74. output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
  75. of_node_put(ep);
  76. if (output->bus_fmt < 0) {
  77. drm_err(dev, "endpoint %d: invalid bus width\n", endpoint);
  78. return -EINVAL;
  79. }
  80. output->encoder.possible_crtcs = drm_crtc_mask(crtc);
  81. if (bridge)
  82. ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);
  83. return ret;
  84. }
  85. int atmel_hlcdc_create_outputs(struct drm_device *dev)
  86. {
  87. int endpoint, ret = 0;
  88. int attached = 0;
  89. /*
  90. * Always scan the first few endpoints even if we get -ENODEV,
  91. * but keep going after that as long as we keep getting hits.
  92. */
  93. for (endpoint = 0; !ret || endpoint < 4; endpoint++) {
  94. ret = atmel_hlcdc_attach_endpoint(dev, endpoint);
  95. if (ret == -ENODEV)
  96. continue;
  97. if (ret)
  98. break;
  99. attached++;
  100. }
  101. /* At least one device was successfully attached.*/
  102. if (ret == -ENODEV && attached)
  103. return 0;
  104. return ret;
  105. }