thunderbolt: Add DisplayPort 2.x tunneling support
authorMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 23 Jan 2023 08:42:58 +0000 (10:42 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Fri, 16 Jun 2023 06:53:29 +0000 (09:53 +0300)
This adds support for the UHBR (Ultra High Bit Rate) bandwidths
introduced with DisplayPort 2.0 (and refined in 2.1). These can go up to
80 Gbit/s and their support is represent in additional bits in the DP IN
capability.

This updates the DisplayPort tunneling to support these new rates too.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/tunnel.c

index c95fc7f..cf9f237 100644 (file)
@@ -450,6 +450,9 @@ struct tb_regs_port_header {
 #define DP_COMMON_CAP_1_LANE                   0x0
 #define DP_COMMON_CAP_2_LANES                  0x1
 #define DP_COMMON_CAP_4_LANES                  0x2
+#define DP_COMMON_CAP_UHBR10                   BIT(17)
+#define DP_COMMON_CAP_UHBR20                   BIT(18)
+#define DP_COMMON_CAP_UHBR13_5                 BIT(19)
 #define DP_COMMON_CAP_LTTPR_NS                 BIT(27)
 #define DP_COMMON_CAP_BW_MODE                  BIT(28)
 #define DP_COMMON_CAP_DPRX_DONE                        BIT(31)
index 224f426..a6810fb 100644 (file)
@@ -418,6 +418,10 @@ static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out,
        return -ETIMEDOUT;
 }
 
+/*
+ * Returns maximum possible rate from capability supporting only DP 2.0
+ * and below. Used when DP BW allocation mode is not enabled.
+ */
 static inline u32 tb_dp_cap_get_rate(u32 val)
 {
        u32 rate = (val & DP_COMMON_CAP_RATE_MASK) >> DP_COMMON_CAP_RATE_SHIFT;
@@ -436,6 +440,28 @@ static inline u32 tb_dp_cap_get_rate(u32 val)
        }
 }
 
+/*
+ * Returns maximum possible rate from capability supporting DP 2.1
+ * UHBR20, 13.5 and 10 rates as well. Use only when DP BW allocation
+ * mode is enabled.
+ */
+static inline u32 tb_dp_cap_get_rate_ext(u32 val)
+{
+       if (val & DP_COMMON_CAP_UHBR20)
+               return 20000;
+       else if (val & DP_COMMON_CAP_UHBR13_5)
+               return 13500;
+       else if (val & DP_COMMON_CAP_UHBR10)
+               return 10000;
+
+       return tb_dp_cap_get_rate(val);
+}
+
+static inline bool tb_dp_is_uhbr_rate(unsigned int rate)
+{
+       return rate >= 10000;
+}
+
 static inline u32 tb_dp_cap_set_rate(u32 val, u32 rate)
 {
        val &= ~DP_COMMON_CAP_RATE_MASK;
@@ -498,7 +524,9 @@ static inline u32 tb_dp_cap_set_lanes(u32 val, u32 lanes)
 
 static unsigned int tb_dp_bandwidth(unsigned int rate, unsigned int lanes)
 {
-       /* Tunneling removes the DP 8b/10b encoding */
+       /* Tunneling removes the DP 8b/10b 128/132b encoding */
+       if (tb_dp_is_uhbr_rate(rate))
+               return rate * lanes * 128 / 132;
        return rate * lanes * 8 / 10;
 }
 
@@ -691,6 +719,19 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel)
        if (ret)
                return ret;
 
+       /*
+        * Pick up granularity that supports maximum possible bandwidth.
+        * For that we use the UHBR rates too.
+        */
+       in_rate = tb_dp_cap_get_rate_ext(in_dp_cap);
+       out_rate = tb_dp_cap_get_rate_ext(out_dp_cap);
+       rate = min(in_rate, out_rate);
+       tmp = tb_dp_bandwidth(rate, lanes);
+
+       tb_port_dbg(in,
+                   "maximum bandwidth through allocation mode %u Mb/s x%u = %u Mb/s\n",
+                   rate, lanes, tmp);
+
        for (granularity = 250; tmp / granularity > 255 && granularity <= 1000;
             granularity *= 2)
                ;
@@ -806,15 +847,42 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
 }
 
 /* max_bw is rounded up to next granularity */
-static int tb_dp_nrd_bandwidth(struct tb_tunnel *tunnel, int *max_bw)
+static int tb_dp_bandwidth_mode_maximum_bandwidth(struct tb_tunnel *tunnel,
+                                                 int *max_bw)
 {
        struct tb_port *in = tunnel->src_port;
        int ret, rate, lanes, nrd_bw;
+       u32 cap;
 
-       ret = usb4_dp_port_nrd(in, &rate, &lanes);
+       /*
+        * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX
+        * read parameter values so this so we can use this to determine
+        * the maximum possible bandwidth over this link.
+        *
+        * See USB4 v2 spec 1.0 10.4.4.5.
+        */
+       ret = tb_port_read(in, &cap, TB_CFG_PORT,
+                          in->cap_adap + DP_LOCAL_CAP, 1);
        if (ret)
                return ret;
 
+       rate = tb_dp_cap_get_rate_ext(cap);
+       if (tb_dp_is_uhbr_rate(rate)) {
+               /*
+                * When UHBR is used there is no reduction in lanes so
+                * we can use this directly.
+                */
+               lanes = tb_dp_cap_get_lanes(cap);
+       } else {
+               /*
+                * If there is no UHBR supported then check the
+                * non-reduced rate and lanes.
+                */
+               ret = usb4_dp_port_nrd(in, &rate, &lanes);
+               if (ret)
+                       return ret;
+       }
+
        nrd_bw = tb_dp_bandwidth(rate, lanes);
 
        if (max_bw) {
@@ -847,7 +915,7 @@ static int tb_dp_bandwidth_mode_consumed_bandwidth(struct tb_tunnel *tunnel,
                return ret;
        allocated_bw = ret;
 
-       ret = tb_dp_nrd_bandwidth(tunnel, &max_bw);
+       ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, &max_bw);
        if (ret < 0)
                return ret;
        if (allocated_bw == max_bw)
@@ -885,7 +953,7 @@ static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up
                        return ret;
                allocated_bw = ret;
 
-               ret = tb_dp_nrd_bandwidth(tunnel, &max_bw);
+               ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, &max_bw);
                if (ret < 0)
                        return ret;
                if (allocated_bw == max_bw)
@@ -915,7 +983,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
        if (!usb4_dp_port_bandwidth_mode_enabled(in))
                return -EOPNOTSUPP;
 
-       ret = tb_dp_nrd_bandwidth(tunnel, &max_bw);
+       ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, &max_bw);
        if (ret < 0)
                return ret;
 
@@ -938,6 +1006,9 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
        /* Now we can use BW mode registers to figure out the bandwidth */
        /* TODO: need to handle discovery too */
        tunnel->bw_mode = true;
+
+       tb_port_dbg(in, "allocated bandwidth through allocation mode %d Mb/s\n",
+                   tmp);
        return 0;
 }
 
@@ -1012,23 +1083,20 @@ static int tb_dp_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
                                   int *max_down)
 {
        struct tb_port *in = tunnel->src_port;
-       u32 rate, lanes;
        int ret;
 
-       /*
-        * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX read
-        * parameter values so this so we can use this to determine the
-        * maximum possible bandwidth over this link.
-        */
-       ret = tb_dp_read_cap(tunnel, DP_LOCAL_CAP, &rate, &lanes);
-       if (ret)
+       if (!usb4_dp_port_bandwidth_mode_enabled(in))
+               return -EOPNOTSUPP;
+
+       ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, NULL);
+       if (ret < 0)
                return ret;
 
        if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) {
                *max_up = 0;
-               *max_down = tb_dp_bandwidth(rate, lanes);
+               *max_down = ret;
        } else {
-               *max_up = tb_dp_bandwidth(rate, lanes);
+               *max_up = ret;
                *max_down = 0;
        }