get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1032823/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1032823,
    "url": "http://patchwork.ozlabs.org/api/patches/1032823/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/20190129150143.12681-22-mika.westerberg@linux.intel.com/",
    "project": {
        "id": 7,
        "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api",
        "name": "Linux network development",
        "link_name": "netdev",
        "list_id": "netdev.vger.kernel.org",
        "list_email": "netdev@vger.kernel.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20190129150143.12681-22-mika.westerberg@linux.intel.com>",
    "list_archive_url": null,
    "date": "2019-01-29T15:01:36",
    "name": "[21/28] thunderbolt: Add support for Display Port tunnels",
    "commit_ref": null,
    "pull_url": null,
    "state": "not-applicable",
    "archived": false,
    "hash": "4138ac1cbc9e096aa5a0d72da52ad483ad7c9847",
    "submitter": {
        "id": 14534,
        "url": "http://patchwork.ozlabs.org/api/people/14534/?format=api",
        "name": "Mika Westerberg",
        "email": "mika.westerberg@linux.intel.com"
    },
    "delegate": {
        "id": 34,
        "url": "http://patchwork.ozlabs.org/api/users/34/?format=api",
        "username": "davem",
        "first_name": "David",
        "last_name": "Miller",
        "email": "davem@davemloft.net"
    },
    "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/20190129150143.12681-22-mika.westerberg@linux.intel.com/mbox/",
    "series": [
        {
            "id": 88859,
            "url": "http://patchwork.ozlabs.org/api/series/88859/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=88859",
            "date": "2019-01-29T15:01:18",
            "name": "thunderbolt: Software connection manager improvements",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/88859/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1032823/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/1032823/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<netdev-owner@vger.kernel.org>",
        "X-Original-To": "patchwork-incoming-netdev@ozlabs.org",
        "Delivered-To": "patchwork-incoming-netdev@ozlabs.org",
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org; dmarc=none (p=none dis=none)\n\theader.from=linux.intel.com"
        ],
        "Received": [
            "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 43pqWd4R3Cz9sDr\n\tfor <patchwork-incoming-netdev@ozlabs.org>;\n\tWed, 30 Jan 2019 02:03:41 +1100 (AEDT)",
            "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1728863AbfA2PCw (ORCPT\n\t<rfc822;patchwork-incoming-netdev@ozlabs.org>);\n\tTue, 29 Jan 2019 10:02:52 -0500",
            "from mga12.intel.com ([192.55.52.136]:8099 \"EHLO mga12.intel.com\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1728367AbfA2PBy (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tTue, 29 Jan 2019 10:01:54 -0500",
            "from fmsmga006.fm.intel.com ([10.253.24.20])\n\tby fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t29 Jan 2019 07:01:54 -0800",
            "from black.fi.intel.com ([10.237.72.28])\n\tby fmsmga006.fm.intel.com with ESMTP; 29 Jan 2019 07:01:51 -0800",
            "by black.fi.intel.com (Postfix, from userid 1001)\n\tid 37CE9B5B; Tue, 29 Jan 2019 17:01:45 +0200 (EET)"
        ],
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.56,537,1539673200\"; d=\"scan'208\";a=\"314531764\"",
        "From": "Mika Westerberg <mika.westerberg@linux.intel.com>",
        "To": "linux-kernel@vger.kernel.org",
        "Cc": "Michael Jamet <michael.jamet@intel.com>,\n\tYehezkel Bernat <YehezkelShB@gmail.com>,\n\tAndreas Noever <andreas.noever@gmail.com>,\n\tLukas Wunner <lukas@wunner.de>, \"David S . Miller\" <davem@davemloft.net>,\n\tMika Westerberg <mika.westerberg@linux.intel.com>,\n\tAndy Shevchenko <andriy.shevchenko@linux.intel.com>,\n\tnetdev@vger.kernel.org",
        "Subject": "[PATCH 21/28] thunderbolt: Add support for Display Port tunnels",
        "Date": "Tue, 29 Jan 2019 18:01:36 +0300",
        "Message-Id": "<20190129150143.12681-22-mika.westerberg@linux.intel.com>",
        "X-Mailer": "git-send-email 2.20.1",
        "In-Reply-To": "<20190129150143.12681-1-mika.westerberg@linux.intel.com>",
        "References": "<20190129150143.12681-1-mika.westerberg@linux.intel.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Sender": "netdev-owner@vger.kernel.org",
        "Precedence": "bulk",
        "List-ID": "<netdev.vger.kernel.org>",
        "X-Mailing-List": "netdev@vger.kernel.org"
    },
    "content": "Display Port tunnels are somewhat more complex than PCIe tunnels as it\nrequires 3 tunnels (AUX Rx/Tx and Video). In addition we are not\nsupposed to create the tunnels immediately when a DP OUT is enumerated.\nInstead we need to wait until we get hotplug event to that adapter port\nor check if the port has HPD set before tunnels can be established. This\nadds Display Port tunneling support to the software connection manager.\n\nSigned-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>\n---\n drivers/thunderbolt/switch.c  | 111 ++++++++++++++\n drivers/thunderbolt/tb.c      | 119 ++++++++++++---\n drivers/thunderbolt/tb.h      |  17 +++\n drivers/thunderbolt/tb_regs.h |  22 +++\n drivers/thunderbolt/tunnel.c  | 277 +++++++++++++++++++++++++++++++++-\n drivers/thunderbolt/tunnel.h  |  21 +++\n 6 files changed, 545 insertions(+), 22 deletions(-)",
    "diff": "diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c\nindex 42681431eb7e..13dceddcfa48 100644\n--- a/drivers/thunderbolt/switch.c\n+++ b/drivers/thunderbolt/switch.c\n@@ -760,6 +760,10 @@ bool tb_port_is_enabled(struct tb_port *port)\n \tcase TB_TYPE_PCIE_DOWN:\n \t\treturn tb_pci_port_is_enabled(port);\n \n+\tcase TB_TYPE_DP_HDMI_IN:\n+\tcase TB_TYPE_DP_HDMI_OUT:\n+\t\treturn tb_dp_port_is_enabled(port);\n+\n \tdefault:\n \t\treturn false;\n \t}\n@@ -792,6 +796,113 @@ int tb_pci_port_enable(struct tb_port *port, bool enable)\n \treturn tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);\n }\n \n+/**\n+ * tb_dp_port_hpd_is_active() - Is HPD already active\n+ * @port: DP out port to check\n+ *\n+ * Checks if the DP OUT adapter port has HDP bit already set.\n+ */\n+int tb_dp_port_hpd_is_active(struct tb_port *port)\n+{\n+\tu32 data;\n+\tint ret;\n+\n+\tret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap + 2, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn !!(data & TB_DP_HDP);\n+}\n+\n+/**\n+ * tb_dp_port_hpd_clear() - Clear HPD from DP IN port\n+ * @port: Port to clear HPD\n+ *\n+ * If the DP IN port has HDP set, this function can be used to clear it.\n+ */\n+int tb_dp_port_hpd_clear(struct tb_port *port)\n+{\n+\tu32 data;\n+\tint ret;\n+\n+\tret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap + 3, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tdata |= TB_DP_HPDC;\n+\treturn tb_port_write(port, &data, TB_CFG_PORT, port->cap_adap + 3, 1);\n+}\n+\n+/**\n+ * tb_dp_port_set_hops() - Set video/aux Hop IDs for DP port\n+ * @port: DP IN/OUT port to set hops\n+ * @video: Video Hop ID\n+ * @aux_tx: AUX TX Hop ID\n+ * @aux_rx: AUX RX Hop ID\n+ *\n+ * Programs specified Hop IDs for DP IN/OUT port.\n+ */\n+int tb_dp_port_set_hops(struct tb_port *port, unsigned int video,\n+\t\t\tunsigned int aux_tx, unsigned int aux_rx)\n+{\n+\tu32 data[2];\n+\tint ret;\n+\n+\tret = tb_port_read(port, data, TB_CFG_PORT, port->cap_adap,\n+\t\t\t   ARRAY_SIZE(data));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tdata[0] &= ~TB_DP_VIDEO_HOPID_MASK;\n+\tdata[1] &= ~(TB_DP_AUX_RX_HOPID_MASK | TB_DP_AUX_TX_HOPID_MASK);\n+\n+\tdata[0] |= (video << TB_DP_VIDEO_HOPID_SHIFT) & TB_DP_VIDEO_HOPID_MASK;\n+\tdata[1] |= aux_tx & TB_DP_AUX_TX_HOPID_MASK;\n+\tdata[1] |= (aux_rx << TB_DP_AUX_RX_HOPID_SHIFT) & TB_DP_AUX_RX_HOPID_MASK;\n+\n+\treturn tb_port_write(port, data, TB_CFG_PORT, port->cap_adap,\n+\t\t\t     ARRAY_SIZE(data));\n+}\n+\n+/**\n+ * tb_dp_port_is_enabled() - Is DP adapter port enabled\n+ * @port: DP adapter port to check\n+ */\n+bool tb_dp_port_is_enabled(struct tb_port *port)\n+{\n+\tu32 data;\n+\n+\tif (tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap, 1))\n+\t\treturn false;\n+\n+\treturn !!(data & (TB_DP_VIDEO_EN | TB_DP_AUX_EN));\n+}\n+\n+/**\n+ * tb_dp_port_enable() - Enables/disables DP paths of a port\n+ * @port: DP IN/OUT port\n+ * @enable: Enable/disable DP path\n+ *\n+ * Once Hop IDs are programmed DP paths can be enabled or disabled by\n+ * calling this function.\n+ */\n+int tb_dp_port_enable(struct tb_port *port, bool enable)\n+{\n+\tu32 data;\n+\tint ret;\n+\n+\tret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (enable)\n+\t\tdata |= TB_DP_VIDEO_EN | TB_DP_AUX_EN;\n+\telse\n+\t\tdata &= ~(TB_DP_VIDEO_EN | TB_DP_AUX_EN);\n+\n+\treturn tb_port_write(port, &data, TB_CFG_PORT, port->cap_adap, 1);\n+}\n+\n /* switch utility functions */\n \n static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)\ndiff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c\nindex 0df1d96e46f5..56bbd1237bd9 100644\n--- a/drivers/thunderbolt/tb.c\n+++ b/drivers/thunderbolt/tb.c\n@@ -28,6 +28,32 @@ struct tb_cm {\n \tbool hotplug_active;\n };\n \n+struct tb_hotplug_event {\n+\tstruct work_struct work;\n+\tstruct tb *tb;\n+\tu64 route;\n+\tu8 port;\n+\tbool unplug;\n+};\n+\n+static void tb_handle_hotplug(struct work_struct *work);\n+\n+static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)\n+{\n+\tstruct tb_hotplug_event *ev;\n+\n+\tev = kmalloc(sizeof(*ev), GFP_KERNEL);\n+\tif (!ev)\n+\t\treturn;\n+\n+\tev->tb = tb;\n+\tev->route = route;\n+\tev->port = port;\n+\tev->unplug = unplug;\n+\tINIT_WORK(&ev->work, tb_handle_hotplug);\n+\tqueue_work(tb->wq, &ev->work);\n+}\n+\n /* enumeration & hot plug handling */\n \n static void tb_discover_tunnels(struct tb_switch *sw)\n@@ -42,6 +68,10 @@ static void tb_discover_tunnels(struct tb_switch *sw)\n \n \t\tport = &sw->ports[i];\n \t\tswitch (port->config.type) {\n+\t\tcase TB_TYPE_DP_HDMI_IN:\n+\t\t\ttunnel = tb_tunnel_discover_dp(tb, port);\n+\t\t\tbreak;\n+\n \t\tcase TB_TYPE_PCIE_DOWN:\n \t\t\ttunnel = tb_tunnel_discover_pci(tb, port);\n \t\t\tbreak;\n@@ -102,6 +132,14 @@ static void tb_scan_port(struct tb_port *port)\n \tstruct tb_switch *sw;\n \tif (tb_is_upstream_port(port))\n \t\treturn;\n+\n+\tif (tb_port_is_dpout(port) && tb_dp_port_hpd_is_active(port) == 1) {\n+\t\ttb_port_dbg(port, \"DP adapter HPD set, queuing hotplug\\n\");\n+\t\ttb_queue_hotplug(port->sw->tb, tb_route(port->sw), port->port,\n+\t\t\t\t false);\n+\t\treturn;\n+\t}\n+\n \tif (port->config.type != TB_TYPE_PORT)\n \t\treturn;\n \tif (port->dual_link_port && port->link_nr)\n@@ -148,6 +186,26 @@ static void tb_scan_port(struct tb_port *port)\n \ttb_scan_switch(sw);\n }\n \n+static int tb_free_tunnel(struct tb *tb, enum tb_tunnel_type type,\n+\t\t\t  struct tb_port *src_port, struct tb_port *dst_port)\n+{\n+\tstruct tb_cm *tcm = tb_priv(tb);\n+\tstruct tb_tunnel *tunnel;\n+\n+\tlist_for_each_entry(tunnel, &tcm->tunnel_list, list) {\n+\t\tif (tunnel->type == type &&\n+\t\t    ((src_port && src_port == tunnel->src_port) ||\n+\t\t     (dst_port && dst_port == tunnel->dst_port))) {\n+\t\t\ttb_tunnel_deactivate(tunnel);\n+\t\t\tlist_del(&tunnel->list);\n+\t\t\ttb_tunnel_free(tunnel);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\treturn -ENODEV;\n+}\n+\n /**\n  * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away\n  */\n@@ -227,6 +285,44 @@ static struct tb_port *tb_find_unused_port(struct tb_switch *sw,\n \treturn NULL;\n }\n \n+static int tb_tunnel_dp(struct tb *tb, struct tb_port *out)\n+{\n+\tstruct tb_cm *tcm = tb_priv(tb);\n+\tstruct tb_switch *sw = out->sw;\n+\tstruct tb_tunnel *tunnel;\n+\tstruct tb_port *in;\n+\n+\tif (tb_port_is_enabled(out))\n+\t\treturn 0;\n+\n+\tdo {\n+\t\tsw = tb_to_switch(sw->dev.parent);\n+\t\tif (!sw)\n+\t\t\treturn 0;\n+\t\tin = tb_find_unused_port(sw, TB_TYPE_DP_HDMI_IN);\n+\t} while (!in);\n+\n+\ttunnel = tb_tunnel_alloc_dp(tb, in, out);\n+\tif (!tunnel) {\n+\t\ttb_port_dbg(out, \"DP tunnel allocation failed\\n\");\n+\t\treturn -EIO;\n+\t}\n+\n+\tif (tb_tunnel_activate(tunnel)) {\n+\t\ttb_port_info(out, \"DP tunnel activation failed, aborting\\n\");\n+\t\ttb_tunnel_free(tunnel);\n+\t\treturn -EIO;\n+\t}\n+\n+\tlist_add_tail(&tunnel->list, &tcm->tunnel_list);\n+\treturn 0;\n+}\n+\n+static void tb_teardown_dp(struct tb *tb, struct tb_port *out)\n+{\n+\ttb_free_tunnel(tb, TB_TUNNEL_DP, NULL, out);\n+}\n+\n static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)\n {\n \tstruct tb_cm *tcm = tb_priv(tb);\n@@ -283,14 +379,6 @@ static int tb_approve_switch(struct tb *tb, struct tb_switch *sw)\n \n /* hotplug handling */\n \n-struct tb_hotplug_event {\n-\tstruct work_struct work;\n-\tstruct tb *tb;\n-\tu64 route;\n-\tu8 port;\n-\tbool unplug;\n-};\n-\n /**\n  * tb_handle_hotplug() - handle hotplug event\n  *\n@@ -335,6 +423,8 @@ static void tb_handle_hotplug(struct work_struct *work)\n \t\t\tcancel_work_sync(&sw->work);\n \t\t\ttb_switch_remove(port->remote->sw);\n \t\t\tport->remote = NULL;\n+\t\t} else if (tb_port_is_dpout(port)) {\n+\t\t\ttb_teardown_dp(tb, port);\n \t\t} else {\n \t\t\ttb_port_info(port,\n \t\t\t\t     \"got unplug event for disconnected port, ignoring\\n\");\n@@ -348,6 +438,8 @@ static void tb_handle_hotplug(struct work_struct *work)\n \t\t\ttb_scan_port(port);\n \t\t\tif (!port->remote)\n \t\t\t\ttb_port_info(port, \"hotplug: no switch found\\n\");\n+\t\t} else if (tb_port_is_dpout(port)) {\n+\t\t\ttb_tunnel_dp(tb, port);\n \t\t}\n \t}\n out:\n@@ -364,7 +456,6 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,\n \t\t\t    const void *buf, size_t size)\n {\n \tconst struct cfg_event_pkg *pkg = buf;\n-\tstruct tb_hotplug_event *ev;\n \tu64 route;\n \n \tif (type != TB_CFG_PKG_EVENT) {\n@@ -380,15 +471,7 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,\n \t\t\tpkg->port);\n \t}\n \n-\tev = kmalloc(sizeof(*ev), GFP_KERNEL);\n-\tif (!ev)\n-\t\treturn;\n-\tINIT_WORK(&ev->work, tb_handle_hotplug);\n-\tev->tb = tb;\n-\tev->route = route;\n-\tev->port = pkg->port;\n-\tev->unplug = pkg->unplug;\n-\tqueue_work(tb->wq, &ev->work);\n+\ttb_queue_hotplug(tb, route, pkg->port, pkg->unplug);\n }\n \n static void tb_stop(struct tb *tb)\ndiff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h\nindex bf5bc0fb0f8e..7b6d1428bfad 100644\n--- a/drivers/thunderbolt/tb.h\n+++ b/drivers/thunderbolt/tb.h\n@@ -292,6 +292,16 @@ static inline bool tb_port_is_null(const struct tb_port *port)\n \treturn port->port && port->config.type == TB_TYPE_PORT;\n }\n \n+static inline bool tb_port_is_dpin(const struct tb_port *port)\n+{\n+\treturn port->config.type == TB_TYPE_DP_HDMI_IN;\n+}\n+\n+static inline bool tb_port_is_dpout(const struct tb_port *port)\n+{\n+\treturn port->config.type == TB_TYPE_DP_HDMI_OUT;\n+}\n+\n static inline int tb_sw_read(struct tb_switch *sw, void *buffer,\n \t\t\t     enum tb_cfg_space space, u32 offset, u32 length)\n {\n@@ -468,6 +478,13 @@ bool tb_port_is_enabled(struct tb_port *port);\n bool tb_pci_port_is_enabled(struct tb_port *port);\n int tb_pci_port_enable(struct tb_port *port, bool enable);\n \n+int tb_dp_port_hpd_is_active(struct tb_port *port);\n+int tb_dp_port_hpd_clear(struct tb_port *port);\n+int tb_dp_port_set_hops(struct tb_port *port, unsigned int video,\n+\t\t\tunsigned int aux_tx, unsigned int aux_rx);\n+bool tb_dp_port_is_enabled(struct tb_port *port);\n+int tb_dp_port_enable(struct tb_port *port, bool enable);\n+\n struct tb_path *tb_path_discover(struct tb_port *port, int start_hopid,\n \t\t\t\t struct tb_port **last);\n struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src,\ndiff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h\nindex 74c0f4a5606d..420d2a623f31 100644\n--- a/drivers/thunderbolt/tb_regs.h\n+++ b/drivers/thunderbolt/tb_regs.h\n@@ -213,6 +213,28 @@ struct tb_regs_port_header {\n \n /* DWORD 4 */\n #define TB_PORT_NFC_CREDITS_MASK\tGENMASK(19, 0)\n+#define TB_PORT_MAX_CREDITS_SHIFT\t20\n+#define TB_PORT_MAX_CREDITS_MASK\tGENMASK(26, 20)\n+\n+/* Display Port adapter registers */\n+\n+/* DWORD 0 */\n+#define TB_DP_VIDEO_EN\t\t\tBIT(31)\n+#define TB_DP_AUX_EN\t\t\tBIT(30)\n+#define TB_DP_VIDEO_HOPID_SHIFT\t\t16\n+#define TB_DP_VIDEO_HOPID_MASK\t\tGENMASK(26, 16)\n+/* DWORD 1 */\n+#define TB_DP_AUX_TX_HOPID_MASK\t\tGENMASK(10, 0)\n+#define TB_DP_AUX_RX_HOPID_SHIFT\t11\n+#define TB_DP_AUX_RX_HOPID_MASK\t\tGENMASK(21, 11)\n+/* DWORD 2 */\n+#define TB_DP_HDP\t\t\tBIT(6)\n+/* DWORD 3 */\n+#define TB_DP_HPDC\t\t\tBIT(9)\n+/* DWORD 4 */\n+#define TB_DP_LOCAL_CAP\t\t\t4\n+/* DWORD 5 */\n+#define TB_DP_REMOTE_CAP\t\t5\n \n /* PCIe adapter registers */\n \ndiff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c\nindex 1a5e2aa395c6..5dd648912032 100644\n--- a/drivers/thunderbolt/tunnel.c\n+++ b/drivers/thunderbolt/tunnel.c\n@@ -18,14 +18,25 @@\n #define TB_PCI_PATH_DOWN\t\t0\n #define TB_PCI_PATH_UP\t\t\t1\n \n+/* DP adapters use hop ID 8 for aux and 9 for video */\n+#define TB_DP_AUX_TX_HOPID\t\t8\n+#define TB_DP_VIDEO_HOPID\t\t9\n+\n+#define TB_DP_VIDEO_PATH_OUT\t\t0\n+#define TB_DP_AUX_PATH_OUT\t\t1\n+#define TB_DP_AUX_PATH_IN\t\t2\n+\n+static const char * const tb_tunnel_names[] = { \"PCI\", \"DP\" };\n+\n #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...)                   \\\n \tdo {                                                            \\\n \t\tstruct tb_tunnel *__tunnel = (tunnel);                  \\\n-\t\tlevel(__tunnel->tb, \"%llx:%x <-> %llx:%x (PCI): \" fmt,  \\\n+\t\tlevel(__tunnel->tb, \"%llx:%x <-> %llx:%x (%s): \" fmt,   \\\n \t\t      tb_route(__tunnel->src_port->sw),                 \\\n \t\t      __tunnel->src_port->port,                         \\\n \t\t      tb_route(__tunnel->dst_port->sw),                 \\\n \t\t      __tunnel->dst_port->port,                         \\\n+\t\t      tb_tunnel_names[__tunnel->type],\t\t\t\\\n \t\t      ## arg);                                          \\\n \t} while (0)\n \n@@ -36,7 +47,8 @@\n #define tb_tunnel_info(tunnel, fmt, arg...) \\\n \t__TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)\n \n-static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths)\n+static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,\n+\t\t\t\t\t enum tb_tunnel_type type)\n {\n \tstruct tb_tunnel *tunnel;\n \n@@ -53,6 +65,7 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths)\n \tINIT_LIST_HEAD(&tunnel->list);\n \ttunnel->tb = tb;\n \ttunnel->npaths = npaths;\n+\ttunnel->type = type;\n \n \treturn tunnel;\n }\n@@ -99,7 +112,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down)\n \tif (!tb_pci_port_is_enabled(down))\n \t\treturn NULL;\n \n-\ttunnel = tb_tunnel_alloc(tb, 2);\n+\ttunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_PCI);\n \tif (!tunnel)\n \t\treturn NULL;\n \n@@ -165,7 +178,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,\n \tstruct tb_tunnel *tunnel;\n \tstruct tb_path *path;\n \n-\ttunnel = tb_tunnel_alloc(tb, 2);\n+\ttunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_PCI);\n \tif (!tunnel)\n \t\treturn NULL;\n \n@@ -192,6 +205,262 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,\n \treturn tunnel;\n }\n \n+static int tb_dp_xchg_caps(struct tb_port *in, struct tb_port *out)\n+{\n+\tu32 in_dp_cap, out_dp_cap;\n+\tint ret;\n+\n+\t/*\n+\t * Copy DP_LOCAL_CAP register to DP_REMOTE_CAP register for\n+\t * newer generation hardware.\n+\t */\n+\tif (in->sw->generation < 2 || out->sw->generation < 2)\n+\t\treturn 0;\n+\n+\t/* Read both DP_LOCAL_CAP registers */\n+\tret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT,\n+\t\t\t   in->cap_adap + TB_DP_LOCAL_CAP, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = tb_port_read(out, &out_dp_cap, TB_CFG_PORT,\n+\t\t\t   out->cap_adap + TB_DP_LOCAL_CAP, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Write them to the opposite adapter port */\n+\tret = tb_port_write(out, &in_dp_cap, TB_CFG_PORT,\n+\t\t\t    out->cap_adap + TB_DP_REMOTE_CAP, 1);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn tb_port_write(in, &out_dp_cap, TB_CFG_PORT,\n+\t\t\t     in->cap_adap + TB_DP_REMOTE_CAP, 1);\n+}\n+\n+static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)\n+{\n+\tint ret;\n+\n+\tif (active) {\n+\t\tstruct tb_path **paths;\n+\t\tint last;\n+\n+\t\tret = tb_dp_xchg_caps(tunnel->src_port, tunnel->dst_port);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tpaths = tunnel->paths;\n+\t\tlast = paths[TB_DP_VIDEO_PATH_OUT]->path_length - 1;\n+\n+\t\ttb_dp_port_set_hops(tunnel->src_port,\n+\t\t\tpaths[TB_DP_VIDEO_PATH_OUT]->hops[0].in_hop_index,\n+\t\t\tpaths[TB_DP_AUX_PATH_OUT]->hops[0].in_hop_index,\n+\t\t\tpaths[TB_DP_AUX_PATH_IN]->hops[last].next_hop_index);\n+\n+\t\ttb_dp_port_set_hops(tunnel->dst_port,\n+\t\t\tpaths[TB_DP_VIDEO_PATH_OUT]->hops[last].next_hop_index,\n+\t\t\tpaths[TB_DP_AUX_PATH_IN]->hops[0].in_hop_index,\n+\t\t\tpaths[TB_DP_AUX_PATH_OUT]->hops[last].next_hop_index);\n+\t} else {\n+\t\ttb_dp_port_hpd_clear(tunnel->src_port);\n+\t}\n+\n+\tret = tb_dp_port_enable(tunnel->src_port, active);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn tb_dp_port_enable(tunnel->dst_port, active);\n+}\n+\n+static void tb_dp_init_aux_path(struct tb_path *path)\n+{\n+\tint i;\n+\n+\tpath->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;\n+\tpath->egress_shared_buffer = TB_PATH_NONE;\n+\tpath->ingress_fc_enable = TB_PATH_ALL;\n+\tpath->ingress_shared_buffer = TB_PATH_NONE;\n+\tpath->priority = 2;\n+\tpath->weight = 1;\n+\n+\tpath->hops[0].initial_credits = 1;\n+\tfor (i = 1; i < path->path_length; i++)\n+\t\tpath->hops[i].initial_credits = 1;\n+}\n+\n+static void tb_dp_init_video_path(struct tb_path *path, bool discover)\n+{\n+\tconst struct tb_port *in = path->hops[0].in_port;\n+\n+\tpath->egress_fc_enable = TB_PATH_NONE;\n+\tpath->egress_shared_buffer = TB_PATH_NONE;\n+\tpath->ingress_fc_enable = TB_PATH_NONE;\n+\tpath->ingress_shared_buffer = TB_PATH_NONE;\n+\tpath->priority = 1;\n+\tpath->weight = 1;\n+\n+\tif (discover) {\n+\t\tpath->nfc_credits =\n+\t\t\tin->config.nfc_credits & TB_PORT_NFC_CREDITS_MASK;\n+\t} else {\n+\t\tu32 max_credits;\n+\n+\t\tmax_credits = in->config.nfc_credits & TB_PORT_MAX_CREDITS_MASK;\n+\t\tmax_credits >>= TB_PORT_MAX_CREDITS_SHIFT;\n+\n+\t\t/* Leave some credits for AUX path */\n+\t\tpath->nfc_credits = min_t(u32, max_credits - 2, 12);\n+\t}\n+}\n+\n+/**\n+ * tb_tunnel_discover_dp() - Discover existing Display Port tunnels\n+ * @tb: Pointer to the domain structure\n+ * @in: DP in adapter\n+ *\n+ * If @in adapter is active, follows the tunnel to the DP out adapter\n+ * and back. Returns the discovered tunnel or %NULL if there was no\n+ * tunnel.\n+ *\n+ * Return: DP tunnel or %NULL if no tunnel found.\n+ */\n+struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in)\n+{\n+\tstruct tb_tunnel *tunnel;\n+\tstruct tb_path *path;\n+\tstruct tb_port *out;\n+\tint ret, hopid;\n+\tu32 data[4];\n+\n+\tret = tb_port_read(in, data, TB_CFG_PORT, in->cap_adap,\n+\t\t\t   ARRAY_SIZE(data));\n+\tif (ret < 0)\n+\t\treturn NULL;\n+\n+\t/* Both needs to be enabled for now */\n+\tif (!(data[0] & TB_DP_VIDEO_EN) || !(data[0] & TB_DP_AUX_EN))\n+\t\treturn NULL;\n+\n+\ttunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP);\n+\tif (!tunnel)\n+\t\treturn NULL;\n+\n+\ttunnel->activate = tb_dp_activate;\n+\ttunnel->src_port = in;\n+\n+\thopid = (data[0] & TB_DP_VIDEO_HOPID_MASK) >> TB_DP_VIDEO_HOPID_SHIFT;\n+\tpath = tb_path_discover(in, hopid, &out);\n+\tif (!path)\n+\t\tgoto err_free;\n+\n+\tif (out->config.type != TB_TYPE_DP_HDMI_OUT) {\n+\t\ttb_port_warn(in, \"path does not end to a DP adapter\\n\");\n+\t\tgoto err_free;\n+\t}\n+\n+\ttunnel->paths[TB_DP_VIDEO_PATH_OUT] = path;\n+\ttunnel->dst_port = out;\n+\n+\thopid = data[1] & TB_DP_AUX_TX_HOPID_MASK;\n+\tpath = tb_path_discover(in, hopid, NULL);\n+\tif (!path)\n+\t\tgoto err_free;\n+\ttunnel->paths[TB_DP_AUX_PATH_OUT] = path;\n+\n+\tret = tb_port_read(out, data, TB_CFG_PORT, out->cap_adap,\n+\t\t\t   ARRAY_SIZE(data));\n+\tif (ret < 0)\n+\t\tgoto err_free;\n+\n+\thopid = data[1] & TB_DP_AUX_TX_HOPID_MASK;\n+\n+\tif (!(data[0] & TB_DP_VIDEO_EN) || !(data[0] & TB_DP_AUX_EN))\n+\t\tgoto err_free;\n+\n+\tpath = tb_path_discover(out, hopid, &in);\n+\tif (!path)\n+\t\tgoto err_free;\n+\n+\ttunnel->paths[TB_DP_AUX_PATH_IN] = path;\n+\n+\tif (in != tunnel->src_port) {\n+\t\ttb_tunnel_warn(tunnel, \"path is not complete, skipping\\n\");\n+\t\tgoto err_free;\n+\t}\n+\n+\t/* Activated by the boot firmware */\n+\ttunnel->paths[TB_DP_VIDEO_PATH_OUT]->activated = true;\n+\ttunnel->paths[TB_DP_AUX_PATH_OUT]->activated = true;\n+\ttunnel->paths[TB_DP_AUX_PATH_IN]->activated = true;\n+\n+\ttb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT], true);\n+\ttb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_OUT]);\n+\ttb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_IN]);\n+\n+\treturn tunnel;\n+\n+err_free:\n+\ttb_tunnel_free(tunnel);\n+\treturn NULL;\n+}\n+\n+/**\n+ * tb_tunnel_alloc_dp() - allocate a Display Port tunnel\n+ * @tb: Pointer to the domain structure\n+ * @in: DP in adapter port\n+ * @out: DP out adapter port\n+ *\n+ * Allocates a tunnel between @in and @out that is capable of tunneling\n+ * Display Port traffic.\n+ *\n+ * Return: Returns a tb_tunnel on success or NULL on failure.\n+ */\n+struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,\n+\t\t\t\t     struct tb_port *out)\n+{\n+\tstruct tb_tunnel *tunnel;\n+\tstruct tb_path **paths;\n+\tstruct tb_path *path;\n+\n+\tif (WARN_ON(in->cap_adap < 0 || out->cap_adap < 0))\n+\t\treturn NULL;\n+\n+\ttunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP);\n+\tif (!tunnel)\n+\t\treturn NULL;\n+\n+\ttunnel->activate = tb_dp_activate;\n+\ttunnel->src_port = in;\n+\ttunnel->dst_port = out;\n+\n+\tpaths = tunnel->paths;\n+\n+\tpath = tb_path_alloc(tb, in, out, TB_DP_VIDEO_HOPID, -1, 1);\n+\tif (!path)\n+\t\tgoto err_free;\n+\ttb_dp_init_video_path(path, false);\n+\tpaths[TB_DP_VIDEO_PATH_OUT] = path;\n+\n+\tpath = tb_path_alloc(tb, in, out, TB_DP_AUX_TX_HOPID, -1, 1);\n+\tif (!path)\n+\t\tgoto err_free;\n+\ttb_dp_init_aux_path(path);\n+\tpaths[TB_DP_AUX_PATH_OUT] = path;\n+\n+\tpath = tb_path_alloc(tb, out, in, TB_DP_AUX_TX_HOPID, -1, 1);\n+\tif (!path)\n+\t\tgoto err_free;\n+\ttb_dp_init_aux_path(path);\n+\tpaths[TB_DP_AUX_PATH_IN] = path;\n+\n+\treturn tunnel;\n+\n+err_free:\n+\ttb_tunnel_free(tunnel);\n+\treturn NULL;\n+}\n+\n /**\n  * tb_tunnel_free() - free a tunnel\n  * @tunnel: Tunnel to be freed\ndiff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h\nindex 7e801a31f9d1..07583f8247c1 100644\n--- a/drivers/thunderbolt/tunnel.h\n+++ b/drivers/thunderbolt/tunnel.h\n@@ -11,6 +11,11 @@\n \n #include \"tb.h\"\n \n+enum tb_tunnel_type {\n+\tTB_TUNNEL_PCI,\n+\tTB_TUNNEL_DP,\n+};\n+\n /**\n  * struct tb_tunnel - Tunnel between two ports\n  * @tb: Pointer to the domain\n@@ -20,6 +25,7 @@\n  * @npaths: Number of paths in @paths\n  * @activate: Optional tunnel specific activation/deactivation\n  * @list: Tunnels are linked using this field\n+ * @type: Type of the tunnel\n  */\n struct tb_tunnel {\n \tstruct tb *tb;\n@@ -29,16 +35,31 @@ struct tb_tunnel {\n \tsize_t npaths;\n \tint (*activate)(struct tb_tunnel *tunnel, bool activate);\n \tstruct list_head list;\n+\tenum tb_tunnel_type type;\n };\n \n struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down);\n struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,\n \t\t\t\t      struct tb_port *down);\n+struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in);\n+struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,\n+\t\t\t\t     struct tb_port *out);\n+\n void tb_tunnel_free(struct tb_tunnel *tunnel);\n int tb_tunnel_activate(struct tb_tunnel *tunnel);\n int tb_tunnel_restart(struct tb_tunnel *tunnel);\n void tb_tunnel_deactivate(struct tb_tunnel *tunnel);\n bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);\n \n+static inline bool tb_tunnel_is_pci(const struct tb_tunnel *tunnel)\n+{\n+\treturn tunnel->type == TB_TUNNEL_PCI;\n+}\n+\n+static inline bool tb_tunnel_is_dp(const struct tb_tunnel *tunnel)\n+{\n+\treturn tunnel->type == TB_TUNNEL_DP;\n+}\n+\n #endif\n \n",
    "prefixes": [
        "21/28"
    ]
}