tilcdc_external.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Copyright (C) 2015 Texas Instruments
  3. * Author: Jyri Sarha <jsarha@ti.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 as published by
  7. * the Free Software Foundation.
  8. *
  9. */
  10. #include <linux/component.h>
  11. #include <linux/of_graph.h>
  12. #include "tilcdc_drv.h"
  13. #include "tilcdc_external.h"
  14. static const struct tilcdc_panel_info panel_info_tda998x = {
  15. .ac_bias = 255,
  16. .ac_bias_intrpt = 0,
  17. .dma_burst_sz = 16,
  18. .bpp = 16,
  19. .fdd = 0x80,
  20. .tft_alt_mode = 0,
  21. .invert_pxl_clk = 1,
  22. .sync_edge = 1,
  23. .sync_ctrl = 1,
  24. .raster_order = 0,
  25. };
  26. static int tilcdc_external_mode_valid(struct drm_connector *connector,
  27. struct drm_display_mode *mode)
  28. {
  29. struct tilcdc_drm_private *priv = connector->dev->dev_private;
  30. int ret, i;
  31. ret = tilcdc_crtc_mode_valid(priv->crtc, mode);
  32. if (ret != MODE_OK)
  33. return ret;
  34. for (i = 0; i < priv->num_connectors &&
  35. priv->connectors[i] != connector; i++)
  36. ;
  37. BUG_ON(priv->connectors[i] != connector);
  38. BUG_ON(!priv->connector_funcs[i]);
  39. /* If the connector has its own mode_valid call it. */
  40. if (!IS_ERR(priv->connector_funcs[i]) &&
  41. priv->connector_funcs[i]->mode_valid)
  42. return priv->connector_funcs[i]->mode_valid(connector, mode);
  43. return MODE_OK;
  44. }
  45. static int tilcdc_add_external_encoder(struct drm_device *dev,
  46. struct drm_connector *connector)
  47. {
  48. struct tilcdc_drm_private *priv = dev->dev_private;
  49. struct drm_connector_helper_funcs *connector_funcs;
  50. priv->connectors[priv->num_connectors] = connector;
  51. priv->encoders[priv->num_encoders++] = connector->encoder;
  52. /* Only tda998x is supported at the moment. */
  53. tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true);
  54. tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x);
  55. connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs),
  56. GFP_KERNEL);
  57. if (!connector_funcs)
  58. return -ENOMEM;
  59. /* connector->helper_private contains always struct
  60. * connector_helper_funcs pointer. For tilcdc crtc to have a
  61. * say if a specific mode is Ok, we need to install our own
  62. * helper functions. In our helper functions we copy
  63. * everything else but use our own mode_valid() (above).
  64. */
  65. if (connector->helper_private) {
  66. priv->connector_funcs[priv->num_connectors] =
  67. connector->helper_private;
  68. *connector_funcs = *priv->connector_funcs[priv->num_connectors];
  69. } else {
  70. priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT);
  71. }
  72. connector_funcs->mode_valid = tilcdc_external_mode_valid;
  73. drm_connector_helper_add(connector, connector_funcs);
  74. priv->num_connectors++;
  75. dev_dbg(dev->dev, "External encoder '%s' connected\n",
  76. connector->encoder->name);
  77. return 0;
  78. }
  79. int tilcdc_add_external_encoders(struct drm_device *dev)
  80. {
  81. struct tilcdc_drm_private *priv = dev->dev_private;
  82. struct drm_connector *connector;
  83. int num_internal_connectors = priv->num_connectors;
  84. list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
  85. bool found = false;
  86. int i, ret;
  87. for (i = 0; i < num_internal_connectors; i++)
  88. if (connector == priv->connectors[i])
  89. found = true;
  90. if (!found) {
  91. ret = tilcdc_add_external_encoder(dev, connector);
  92. if (ret)
  93. return ret;
  94. }
  95. }
  96. return 0;
  97. }
  98. void tilcdc_remove_external_encoders(struct drm_device *dev)
  99. {
  100. struct tilcdc_drm_private *priv = dev->dev_private;
  101. int i;
  102. /* Restore the original helper functions, if any. */
  103. for (i = 0; i < priv->num_connectors; i++)
  104. if (IS_ERR(priv->connector_funcs[i]))
  105. drm_connector_helper_add(priv->connectors[i], NULL);
  106. else if (priv->connector_funcs[i])
  107. drm_connector_helper_add(priv->connectors[i],
  108. priv->connector_funcs[i]);
  109. }
  110. static int dev_match_of(struct device *dev, void *data)
  111. {
  112. return dev->of_node == data;
  113. }
  114. int tilcdc_get_external_components(struct device *dev,
  115. struct component_match **match)
  116. {
  117. struct device_node *node;
  118. struct device_node *ep = NULL;
  119. int count = 0;
  120. /* Avoid error print by of_graph_get_next_endpoint() if there
  121. * is no ports present.
  122. */
  123. node = of_get_child_by_name(dev->of_node, "ports");
  124. if (!node)
  125. node = of_get_child_by_name(dev->of_node, "port");
  126. if (!node)
  127. return 0;
  128. of_node_put(node);
  129. while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {
  130. node = of_graph_get_remote_port_parent(ep);
  131. if (!node || !of_device_is_available(node)) {
  132. of_node_put(node);
  133. continue;
  134. }
  135. dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
  136. if (match)
  137. component_match_add(dev, match, dev_match_of, node);
  138. of_node_put(node);
  139. count++;
  140. }
  141. if (count > 1) {
  142. dev_err(dev, "Only one external encoder is supported\n");
  143. return -EINVAL;
  144. }
  145. return count;
  146. }