tilcdc_external.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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, int *bpp,
  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. *bpp = panel_info_tda998x.bpp;
  56. connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs),
  57. GFP_KERNEL);
  58. if (!connector_funcs)
  59. return -ENOMEM;
  60. /* connector->helper_private contains always struct
  61. * connector_helper_funcs pointer. For tilcdc crtc to have a
  62. * say if a specific mode is Ok, we need to install our own
  63. * helper functions. In our helper functions we copy
  64. * everything else but use our own mode_valid() (above).
  65. */
  66. if (connector->helper_private) {
  67. priv->connector_funcs[priv->num_connectors] =
  68. connector->helper_private;
  69. *connector_funcs = *priv->connector_funcs[priv->num_connectors];
  70. } else {
  71. priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT);
  72. }
  73. connector_funcs->mode_valid = tilcdc_external_mode_valid;
  74. drm_connector_helper_add(connector, connector_funcs);
  75. priv->num_connectors++;
  76. dev_dbg(dev->dev, "External encoder '%s' connected\n",
  77. connector->encoder->name);
  78. return 0;
  79. }
  80. int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp)
  81. {
  82. struct tilcdc_drm_private *priv = dev->dev_private;
  83. struct drm_connector *connector;
  84. int num_internal_connectors = priv->num_connectors;
  85. list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
  86. bool found = false;
  87. int i, ret;
  88. for (i = 0; i < num_internal_connectors; i++)
  89. if (connector == priv->connectors[i])
  90. found = true;
  91. if (!found) {
  92. ret = tilcdc_add_external_encoder(dev, bpp, connector);
  93. if (ret)
  94. return ret;
  95. }
  96. }
  97. return 0;
  98. }
  99. void tilcdc_remove_external_encoders(struct drm_device *dev)
  100. {
  101. struct tilcdc_drm_private *priv = dev->dev_private;
  102. int i;
  103. /* Restore the original helper functions, if any. */
  104. for (i = 0; i < priv->num_connectors; i++)
  105. if (IS_ERR(priv->connector_funcs[i]))
  106. drm_connector_helper_add(priv->connectors[i], NULL);
  107. else if (priv->connector_funcs[i])
  108. drm_connector_helper_add(priv->connectors[i],
  109. priv->connector_funcs[i]);
  110. }
  111. static int dev_match_of(struct device *dev, void *data)
  112. {
  113. return dev->of_node == data;
  114. }
  115. int tilcdc_get_external_components(struct device *dev,
  116. struct component_match **match)
  117. {
  118. struct device_node *ep = NULL;
  119. int count = 0;
  120. while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {
  121. struct device_node *node;
  122. node = of_graph_get_remote_port_parent(ep);
  123. if (!node && !of_device_is_available(node)) {
  124. of_node_put(node);
  125. continue;
  126. }
  127. dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
  128. if (match)
  129. component_match_add(dev, match, dev_match_of, node);
  130. of_node_put(node);
  131. count++;
  132. }
  133. if (count > 1) {
  134. dev_err(dev, "Only one external encoder is supported\n");
  135. return -EINVAL;
  136. }
  137. return count;
  138. }