thunderbolt: Use different lane for second DisplayPort tunnel
Brad reported that on Apple hardware with Light Ridge or Falcon Ridge controller, plugging in a chain of Thunderbolt displays (Light Ridge based controllers) causes all kinds of tearing and flickering. The reason for this is that on Thunderbolt 1 hardware there is no lane bonding so we have two independent 10 Gb/s lanes, and currently Linux tunnels both displays through the lane 1. This makes the displays to share the 10 Gb/s bandwidth which may not be enough for higher resolutions. For this reason make the second tunnel go through the lane 0 instead. This seems to match what the macOS connection manager is also doing. Reported-by: Brad Campbell <lists2009@fnarfbargle.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Tested-by: Brad Campbell <lists2009@fnarfbargle.com>
This commit is contained in:
parent
259e0c71e5
commit
9d2d0a5cf0
@ -867,7 +867,7 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
|
||||
|
||||
static void tb_tunnel_dp(struct tb *tb)
|
||||
{
|
||||
int available_up, available_down, ret;
|
||||
int available_up, available_down, ret, link_nr;
|
||||
struct tb_cm *tcm = tb_priv(tb);
|
||||
struct tb_port *port, *in, *out;
|
||||
struct tb_tunnel *tunnel;
|
||||
@ -912,6 +912,20 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is only applicable to links that are not bonded (so
|
||||
* when Thunderbolt 1 hardware is involved somewhere in the
|
||||
* topology). For these try to share the DP bandwidth between
|
||||
* the two lanes.
|
||||
*/
|
||||
link_nr = 1;
|
||||
list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
|
||||
if (tb_tunnel_is_dp(tunnel)) {
|
||||
link_nr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DP stream needs the domain to be active so runtime resume
|
||||
* both ends of the tunnel.
|
||||
@ -943,7 +957,8 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
|
||||
available_up, available_down);
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(tb, in, out, available_up, available_down);
|
||||
tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
|
||||
available_down);
|
||||
if (!tunnel) {
|
||||
tb_port_dbg(out, "could not allocate DP tunnel\n");
|
||||
goto err_reclaim;
|
||||
|
@ -1348,7 +1348,7 @@ static void tb_test_tunnel_dp(struct kunit *test)
|
||||
in = &host->ports[5];
|
||||
out = &dev->ports[13];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
@ -1394,7 +1394,7 @@ static void tb_test_tunnel_dp_chain(struct kunit *test)
|
||||
in = &host->ports[5];
|
||||
out = &dev4->ports[14];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
@ -1444,7 +1444,7 @@ static void tb_test_tunnel_dp_tree(struct kunit *test)
|
||||
in = &dev2->ports[13];
|
||||
out = &dev5->ports[13];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
@ -1509,7 +1509,7 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test)
|
||||
in = &dev6->ports[13];
|
||||
out = &dev12->ports[13];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
@ -1627,7 +1627,7 @@ static void tb_test_tunnel_port_on_path(struct kunit *test)
|
||||
in = &dev2->ports[13];
|
||||
out = &dev5->ports[13];
|
||||
|
||||
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, dp_tunnel != NULL);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
|
||||
@ -2009,7 +2009,7 @@ static void tb_test_credit_alloc_dp(struct kunit *test)
|
||||
in = &host->ports[5];
|
||||
out = &dev->ports[14];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel != NULL);
|
||||
KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
|
||||
|
||||
@ -2245,7 +2245,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test,
|
||||
|
||||
in = &host->ports[5];
|
||||
out = &dev->ports[13];
|
||||
dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, dp_tunnel1 != NULL);
|
||||
KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
|
||||
|
||||
@ -2282,7 +2282,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test,
|
||||
|
||||
in = &host->ports[6];
|
||||
out = &dev->ports[14];
|
||||
dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
|
||||
dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
|
||||
KUNIT_ASSERT_TRUE(test, dp_tunnel2 != NULL);
|
||||
KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
|
||||
|
||||
|
@ -858,6 +858,7 @@ err_free:
|
||||
* @tb: Pointer to the domain structure
|
||||
* @in: DP in adapter port
|
||||
* @out: DP out adapter port
|
||||
* @link_nr: Preferred lane adapter when the link is not bonded
|
||||
* @max_up: Maximum available upstream bandwidth for the DP tunnel (%0
|
||||
* if not limited)
|
||||
* @max_down: Maximum available downstream bandwidth for the DP tunnel
|
||||
@ -869,8 +870,8 @@ err_free:
|
||||
* Return: Returns a tb_tunnel on success or NULL on failure.
|
||||
*/
|
||||
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
struct tb_port *out, int max_up,
|
||||
int max_down)
|
||||
struct tb_port *out, int link_nr,
|
||||
int max_up, int max_down)
|
||||
{
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb_path **paths;
|
||||
@ -894,21 +895,21 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
paths = tunnel->paths;
|
||||
|
||||
path = tb_path_alloc(tb, in, TB_DP_VIDEO_HOPID, out, TB_DP_VIDEO_HOPID,
|
||||
1, "Video");
|
||||
link_nr, "Video");
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_dp_init_video_path(path);
|
||||
paths[TB_DP_VIDEO_PATH_OUT] = path;
|
||||
|
||||
path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out,
|
||||
TB_DP_AUX_TX_HOPID, 1, "AUX TX");
|
||||
TB_DP_AUX_TX_HOPID, link_nr, "AUX TX");
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_dp_init_aux_path(path);
|
||||
paths[TB_DP_AUX_PATH_OUT] = path;
|
||||
|
||||
path = tb_path_alloc(tb, out, TB_DP_AUX_RX_HOPID, in,
|
||||
TB_DP_AUX_RX_HOPID, 1, "AUX RX");
|
||||
TB_DP_AUX_RX_HOPID, link_nr, "AUX RX");
|
||||
if (!path)
|
||||
goto err_free;
|
||||
tb_dp_init_aux_path(path);
|
||||
|
@ -71,8 +71,8 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
|
||||
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
bool alloc_hopid);
|
||||
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
struct tb_port *out, int max_up,
|
||||
int max_down);
|
||||
struct tb_port *out, int link_nr,
|
||||
int max_up, int max_down);
|
||||
struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
|
||||
struct tb_port *dst, int transmit_path,
|
||||
int transmit_ring, int receive_path,
|
||||
|
Loading…
Reference in New Issue
Block a user