drm/tegra: dp: Add support for eDP link rates
authorThierry Reding <treding@nvidia.com>
Thu, 1 Feb 2018 16:46:42 +0000 (17:46 +0100)
committerThierry Reding <treding@nvidia.com>
Mon, 28 Oct 2019 10:18:53 +0000 (11:18 +0100)
Parses additional link rates from DPCD if the sink supports eDP 1.4.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dp.c
drivers/gpu/drm/tegra/dp.h

index e9a5db7..bcf9df9 100644 (file)
@@ -33,6 +33,8 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
 
 static void drm_dp_link_reset(struct drm_dp_link *link)
 {
+       unsigned int i;
+
        if (!link)
                return;
 
@@ -47,6 +49,111 @@ static void drm_dp_link_reset(struct drm_dp_link *link)
 
        link->rate = 0;
        link->lanes = 0;
+
+       for (i = 0; i < DP_MAX_SUPPORTED_RATES; i++)
+               link->rates[i] = 0;
+
+       link->num_rates = 0;
+}
+
+/**
+ * drm_dp_link_add_rate() - add a rate to the list of supported rates
+ * @link: the link to add the rate to
+ * @rate: the rate to add
+ *
+ * Add a link rate to the list of supported link rates.
+ *
+ * Returns:
+ * 0 on success or one of the following negative error codes on failure:
+ * - ENOSPC if the maximum number of supported rates has been reached
+ * - EEXISTS if the link already supports this rate
+ *
+ * See also:
+ * drm_dp_link_remove_rate()
+ */
+int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate)
+{
+       unsigned int i, pivot;
+
+       if (link->num_rates == DP_MAX_SUPPORTED_RATES)
+               return -ENOSPC;
+
+       for (pivot = 0; pivot < link->num_rates; pivot++)
+               if (rate <= link->rates[pivot])
+                       break;
+
+       if (pivot != link->num_rates && rate == link->rates[pivot])
+               return -EEXIST;
+
+       for (i = link->num_rates; i > pivot; i--)
+               link->rates[i] = link->rates[i - 1];
+
+       link->rates[pivot] = rate;
+       link->num_rates++;
+
+       return 0;
+}
+
+/**
+ * drm_dp_link_remove_rate() - remove a rate from the list of supported rates
+ * @link: the link from which to remove the rate
+ * @rate: the rate to remove
+ *
+ * Removes a link rate from the list of supported link rates.
+ *
+ * Returns:
+ * 0 on success or one of the following negative error codes on failure:
+ * - EINVAL if the specified rate is not among the supported rates
+ *
+ * See also:
+ * drm_dp_link_add_rate()
+ */
+int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate)
+{
+       unsigned int i;
+
+       for (i = 0; i < link->num_rates; i++)
+               if (rate == link->rates[i])
+                       break;
+
+       if (i == link->num_rates)
+               return -EINVAL;
+
+       link->num_rates--;
+
+       while (i < link->num_rates) {
+               link->rates[i] = link->rates[i + 1];
+               i++;
+       }
+
+       return 0;
+}
+
+/**
+ * drm_dp_link_update_rates() - normalize the supported link rates array
+ * @link: the link for which to normalize the supported link rates
+ *
+ * Users should call this function after they've manually modified the array
+ * of supported link rates. This function removes any stale entries, compacts
+ * the array and updates the supported link rate count. Note that calling the
+ * drm_dp_link_remove_rate() function already does this janitorial work.
+ *
+ * See also:
+ * drm_dp_link_add_rate(), drm_dp_link_remove_rate()
+ */
+void drm_dp_link_update_rates(struct drm_dp_link *link)
+{
+       unsigned int i, count = 0;
+
+       for (i = 0; i < link->num_rates; i++) {
+               if (link->rates[i] != 0)
+                       link->rates[count++] = link->rates[i];
+       }
+
+       for (i = count; i < link->num_rates; i++)
+               link->rates[i] = 0;
+
+       link->num_rates = count;
 }
 
 /**
@@ -125,6 +232,26 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
        link->rate = link->max_rate;
        link->lanes = link->max_lanes;
 
+       /* Parse SUPPORTED_LINK_RATES from eDP 1.4 */
+       if (link->edp >= 0x14) {
+               u8 supported_rates[DP_MAX_SUPPORTED_RATES * 2];
+               unsigned int i;
+               u16 rate;
+
+               err = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES,
+                                      supported_rates,
+                                      sizeof(supported_rates));
+               if (err < 0)
+                       return err;
+
+               for (i = 0; i < DP_MAX_SUPPORTED_RATES; i++) {
+                       rate = supported_rates[i * 2 + 1] << 8 |
+                              supported_rates[i * 2 + 0];
+
+                       drm_dp_link_add_rate(link, rate * 200);
+               }
+       }
+
        return 0;
 }
 
index e07b9a1..a20ee9f 100644 (file)
@@ -66,6 +66,8 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
  * @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...)
  * @rate: currently configured link rate
  * @lanes: currently configured number of lanes
+ * @rates: additional supported link rates in kHz (eDP 1.4)
+ * @num_rates: number of additional supported link rates (eDP 1.4)
  */
 struct drm_dp_link {
        unsigned char revision;
@@ -87,8 +89,15 @@ struct drm_dp_link {
 
        unsigned int rate;
        unsigned int lanes;
+
+       unsigned long rates[DP_MAX_SUPPORTED_RATES];
+       unsigned int num_rates;
 };
 
+int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate);
+int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate);
+void drm_dp_link_update_rates(struct drm_dp_link *link);
+
 int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link);