Skip to content

Commit

Permalink
thunderbolt: Honor TMU requirements in the domain when setting TMU mode
Browse files Browse the repository at this point in the history
Currently, when configuring TMU (Time Management Unit) mode of a given
router, we take into account only its own TMU requirements ignoring
other routers in the domain. This is problematic if the router we are
configuring has lower TMU requirements than what is already configured
in the domain.

In the scenario below, we have a host router with two USB4 ports: A and
B. Port A connected to device router #1 (which supports CL states) and
existing DisplayPort tunnel, thus, the TMU mode is HiFi uni-directional.

1. Initial topology

          [Host]
         A/
         /
 [Device #1]
   /
Monitor

2. Plug in device #2 (that supports CL states) to downstream port B of
   the host router

         [Host]
        A/    B\
        /       \
 [Device #1]    [Device #2]
   /
Monitor

The TMU mode on port B and port A will be configured to LowRes which is
not what we want and will cause monitor to start flickering.

To address this we first scan the domain and search for any router
configured to HiFi uni-directional mode, and if found, configure TMU
mode of the given router to HiFi uni-directional as well.

Cc: stable@vger.kernel.org
Signed-off-by: Gil Fine <gil.fine@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
  • Loading branch information
Gil Fine authored and westeri committed Oct 21, 2024
1 parent e9e1b20 commit 3cea8af
Showing 1 changed file with 42 additions and 6 deletions.
48 changes: 42 additions & 6 deletions drivers/thunderbolt/tb.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,24 @@ static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel)
device_for_each_child(&sw->dev, NULL, tb_increase_switch_tmu_accuracy);
}

static int tb_switch_tmu_hifi_uni_required(struct device *dev, void *not_used)
{
struct tb_switch *sw = tb_to_switch(dev);

if (sw && tb_switch_tmu_is_enabled(sw) &&
tb_switch_tmu_is_configured(sw, TB_SWITCH_TMU_MODE_HIFI_UNI))
return 1;

return device_for_each_child(dev, NULL,
tb_switch_tmu_hifi_uni_required);
}

static bool tb_tmu_hifi_uni_required(struct tb *tb)
{
return device_for_each_child(&tb->dev, NULL,
tb_switch_tmu_hifi_uni_required) == 1;
}

static int tb_enable_tmu(struct tb_switch *sw)
{
int ret;
Expand All @@ -302,12 +320,30 @@ static int tb_enable_tmu(struct tb_switch *sw)
ret = tb_switch_tmu_configure(sw,
TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI);
if (ret == -EOPNOTSUPP) {
if (tb_switch_clx_is_enabled(sw, TB_CL1))
ret = tb_switch_tmu_configure(sw,
TB_SWITCH_TMU_MODE_LOWRES);
else
ret = tb_switch_tmu_configure(sw,
TB_SWITCH_TMU_MODE_HIFI_BI);
if (tb_switch_clx_is_enabled(sw, TB_CL1)) {
/*
* Figure out uni-directional HiFi TMU requirements
* currently in the domain. If there are no
* uni-directional HiFi requirements we can put the TMU
* into LowRes mode.
*
* Deliberately skip bi-directional HiFi links
* as these work independently of other links
* (and they do not allow any CL states anyway).
*/
if (tb_tmu_hifi_uni_required(sw->tb))
ret = tb_switch_tmu_configure(sw,
TB_SWITCH_TMU_MODE_HIFI_UNI);
else
ret = tb_switch_tmu_configure(sw,
TB_SWITCH_TMU_MODE_LOWRES);
} else {
ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_HIFI_BI);
}

/* If not supported, fallback to bi-directional HiFi */
if (ret == -EOPNOTSUPP)
ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_HIFI_BI);
}
if (ret)
return ret;
Expand Down

0 comments on commit 3cea8af

Please sign in to comment.