summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/switch.c
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2018-09-17 16:30:49 +0300
committerMika Westerberg <mika.westerberg@linux.intel.com>2019-04-18 11:18:53 +0300
commit4f807e47ee9a75747d042a8eacf398f436da9452 (patch)
treef8bbff8b83730fe4ea3275ebba63d9ff274350ac /drivers/thunderbolt/switch.c
parentc5ee6feb34709da96f9909b8a2e1e42875020efb (diff)
downloadlinux-4f807e47ee9a75747d042a8eacf398f436da9452.tar.xz
thunderbolt: Add support for Display Port tunnels
Display Port tunnels are somewhat more complex than PCIe tunnels as it requires 3 tunnels (AUX Rx/Tx and Video). In addition we are not supposed to create the tunnels immediately when a DP OUT is enumerated. Instead we need to wait until we get hotplug event to that adapter port or check if the port has HPD set before tunnels can be established. This adds Display Port tunneling support to the software connection manager. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r--drivers/thunderbolt/switch.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 9462e6982061..1de1afa24182 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -747,6 +747,10 @@ bool tb_port_is_enabled(struct tb_port *port)
case TB_TYPE_PCIE_DOWN:
return tb_pci_port_is_enabled(port);
+ case TB_TYPE_DP_HDMI_IN:
+ case TB_TYPE_DP_HDMI_OUT:
+ return tb_dp_port_is_enabled(port);
+
default:
return false;
}
@@ -779,6 +783,113 @@ int tb_pci_port_enable(struct tb_port *port, bool enable)
return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
}
+/**
+ * tb_dp_port_hpd_is_active() - Is HPD already active
+ * @port: DP out port to check
+ *
+ * Checks if the DP OUT adapter port has HDP bit already set.
+ */
+int tb_dp_port_hpd_is_active(struct tb_port *port)
+{
+ u32 data;
+ int ret;
+
+ ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap + 2, 1);
+ if (ret)
+ return ret;
+
+ return !!(data & TB_DP_HDP);
+}
+
+/**
+ * tb_dp_port_hpd_clear() - Clear HPD from DP IN port
+ * @port: Port to clear HPD
+ *
+ * If the DP IN port has HDP set, this function can be used to clear it.
+ */
+int tb_dp_port_hpd_clear(struct tb_port *port)
+{
+ u32 data;
+ int ret;
+
+ ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap + 3, 1);
+ if (ret)
+ return ret;
+
+ data |= TB_DP_HPDC;
+ return tb_port_write(port, &data, TB_CFG_PORT, port->cap_adap + 3, 1);
+}
+
+/**
+ * tb_dp_port_set_hops() - Set video/aux Hop IDs for DP port
+ * @port: DP IN/OUT port to set hops
+ * @video: Video Hop ID
+ * @aux_tx: AUX TX Hop ID
+ * @aux_rx: AUX RX Hop ID
+ *
+ * Programs specified Hop IDs for DP IN/OUT port.
+ */
+int tb_dp_port_set_hops(struct tb_port *port, unsigned int video,
+ unsigned int aux_tx, unsigned int aux_rx)
+{
+ u32 data[2];
+ int ret;
+
+ ret = tb_port_read(port, data, TB_CFG_PORT, port->cap_adap,
+ ARRAY_SIZE(data));
+ if (ret)
+ return ret;
+
+ data[0] &= ~TB_DP_VIDEO_HOPID_MASK;
+ data[1] &= ~(TB_DP_AUX_RX_HOPID_MASK | TB_DP_AUX_TX_HOPID_MASK);
+
+ data[0] |= (video << TB_DP_VIDEO_HOPID_SHIFT) & TB_DP_VIDEO_HOPID_MASK;
+ data[1] |= aux_tx & TB_DP_AUX_TX_HOPID_MASK;
+ data[1] |= (aux_rx << TB_DP_AUX_RX_HOPID_SHIFT) & TB_DP_AUX_RX_HOPID_MASK;
+
+ return tb_port_write(port, data, TB_CFG_PORT, port->cap_adap,
+ ARRAY_SIZE(data));
+}
+
+/**
+ * tb_dp_port_is_enabled() - Is DP adapter port enabled
+ * @port: DP adapter port to check
+ */
+bool tb_dp_port_is_enabled(struct tb_port *port)
+{
+ u32 data;
+
+ if (tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap, 1))
+ return false;
+
+ return !!(data & (TB_DP_VIDEO_EN | TB_DP_AUX_EN));
+}
+
+/**
+ * tb_dp_port_enable() - Enables/disables DP paths of a port
+ * @port: DP IN/OUT port
+ * @enable: Enable/disable DP path
+ *
+ * Once Hop IDs are programmed DP paths can be enabled or disabled by
+ * calling this function.
+ */
+int tb_dp_port_enable(struct tb_port *port, bool enable)
+{
+ u32 data;
+ int ret;
+
+ ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap, 1);
+ if (ret)
+ return ret;
+
+ if (enable)
+ data |= TB_DP_VIDEO_EN | TB_DP_AUX_EN;
+ else
+ data &= ~(TB_DP_VIDEO_EN | TB_DP_AUX_EN);
+
+ return tb_port_write(port, &data, TB_CFG_PORT, port->cap_adap, 1);
+}
+
/* switch utility functions */
static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)