diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 5879f67bc88c..4e4b98c448cb 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -9,6 +9,7 @@ config DRM_MSM
 	depends on QCOM_OCMEM || QCOM_OCMEM=n
 	depends on QCOM_LLCC || QCOM_LLCC=n
 	depends on QCOM_COMMAND_DB || QCOM_COMMAND_DB=n
+	depends on TYPEC || TYPEC=n
 	select IOMMU_IO_PGTABLE
 	select QCOM_MDT_LOADER if ARCH_QCOM
 	select REGULATOR
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 56a79aeffed4..e863f537047a 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -85,6 +85,8 @@ struct dp_display_private {
 	bool hpd_irq_on;
 	bool audio_supported;
 
+	bool use_hw_hpd;
+
 	struct platform_device *pdev;
 	struct dentry *root;
 
@@ -466,11 +468,10 @@ static int dp_display_handle_irq_hpd(struct dp_display_private *dp)
 	return 0;
 }
 
-static int dp_display_usbpd_attention_cb(struct device *dev)
+static int dp_display_usbpd_attention(struct dp_display_private *dp)
 {
 	int rc = 0;
 	u32 sink_request;
-	struct dp_display_private *dp = dev_get_dp_display_private(dev);
 
 	/* check for any test request issued by sink */
 	rc = dp_link_process_request(dp->link);
@@ -690,7 +691,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
 		return 0;
 	}
 
-	ret = dp_display_usbpd_attention_cb(&dp->pdev->dev);
+	ret = dp_display_usbpd_attention(dp);
 	if (ret == -ECONNRESET) { /* cable unplugged */
 		dp->core_initialized = false;
 	}
@@ -709,6 +710,13 @@ static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
 	dp_audio_put(dp->audio);
 }
 
+static int dp_display_usbpd_attention_cb(struct device *dev)
+{
+	struct dp_display_private *dp = dev_get_dp_display_private(dev);
+
+	return dp_irq_hpd_handle(dp, 0);
+}
+
 static int dp_init_sub_modules(struct dp_display_private *dp)
 {
 	int rc = 0;
@@ -731,6 +739,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 		goto error;
 	}
 
+	dp->use_hw_hpd = !of_property_read_bool(dev->of_node, "mode-switch");
+
 	dp->parser = dp_parser_get(dp->pdev);
 	if (IS_ERR(dp->parser)) {
 		rc = PTR_ERR(dp->parser);
@@ -1135,27 +1145,29 @@ static irqreturn_t dp_display_irq_handler(int irq, void *dev_id)
 		return IRQ_NONE;
 	}
 
-	hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog);
+	if (dp->use_hw_hpd) {
+		hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog);
 
-	DRM_DEBUG_DP("hpd isr status=%#x\n", hpd_isr_status);
-	if (hpd_isr_status & 0x0F) {
-		/* hpd related interrupts */
-		if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK)
-			dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0);
+		DRM_DEBUG_DP("hpd isr status=%#x\n", hpd_isr_status);
+		if (hpd_isr_status & 0x0F) {
+			/* hpd related interrupts */
+			if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK)
+				dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0);
 
-		if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
-			/* stop sentinel connect pending checking */
-			dp_del_event(dp, EV_CONNECT_PENDING_TIMEOUT);
-			dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0);
-		}
+			if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
+				/* stop sentinel connect pending checking */
+				dp_del_event(dp, EV_CONNECT_PENDING_TIMEOUT);
+				dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0);
+			}
 
-		if (hpd_isr_status & DP_DP_HPD_REPLUG_INT_MASK) {
-			dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0);
-			dp_add_event(dp, EV_HPD_PLUG_INT, 0, 3);
-		}
+			if (hpd_isr_status & DP_DP_HPD_REPLUG_INT_MASK) {
+				dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0);
+				dp_add_event(dp, EV_HPD_PLUG_INT, 0, 3);
+			}
 
-		if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK)
-			dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0);
+			if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK)
+				dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0);
+		}
 	}
 
 	/* DP controller isr */
diff --git a/drivers/gpu/drm/msm/dp/dp_hpd.c b/drivers/gpu/drm/msm/dp/dp_hpd.c
index e1c90fa47411..2a7ed9b8354e 100644
--- a/drivers/gpu/drm/msm/dp/dp_hpd.c
+++ b/drivers/gpu/drm/msm/dp/dp_hpd.c
@@ -7,6 +7,9 @@
 
 #include <linux/slab.h>
 #include <linux/device.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
 
 #include "dp_hpd.h"
 
@@ -22,6 +25,8 @@ struct dp_hpd_private {
 	struct device *dev;
 	struct dp_usbpd_cb *dp_cb;
 	struct dp_usbpd dp_usbpd;
+	struct typec_mux *mux;
+	bool connected;
 };
 
 int dp_hpd_connect(struct dp_usbpd *dp_usbpd, bool hpd)
@@ -47,9 +52,45 @@ int dp_hpd_connect(struct dp_usbpd *dp_usbpd, bool hpd)
 	return rc;
 }
 
+static int dp_hpd_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+{
+	struct dp_hpd_private *dp_hpd = typec_mux_get_drvdata(mux);
+	struct dp_usbpd *usbpd = &dp_hpd->dp_usbpd;
+	struct typec_displayport_data *dp_data = state->data;
+	int pin_assign = 0;
+
+	if (dp_data) {
+		pin_assign = DP_CONF_GET_PIN_ASSIGN(dp_data->conf);
+		usbpd->hpd_high = !!(dp_data->status & DP_STATUS_HPD_STATE);
+		usbpd->hpd_irq = !!(dp_data->status & DP_STATUS_IRQ_HPD);
+		usbpd->multi_func = pin_assign == DP_PIN_ASSIGN_C || DP_PIN_ASSIGN_E;
+	}
+
+	if (!pin_assign) {
+		if (dp_hpd->connected) {
+			dp_hpd->connected = false;
+			dp_hpd->dp_cb->disconnect(dp_hpd->dev);
+		}
+	} else if (!dp_hpd->connected) {
+		dp_hpd->connected = true;
+		dp_hpd->dp_cb->configure(dp_hpd->dev);
+	} else {
+		dp_hpd->dp_cb->attention(dp_hpd->dev);
+	}
+
+	return 0;
+}
+
+static void dp_hpd_unregister_typec_mux(void *data)
+{
+	typec_mux_unregister(data);
+}
+
 struct dp_usbpd *dp_hpd_get(struct device *dev, struct dp_usbpd_cb *cb)
 {
+	struct typec_mux_desc mux_desc = {};
 	struct dp_hpd_private *dp_hpd;
+	int rc;
 
 	if (!cb) {
 		pr_err("invalid cb data\n");
@@ -65,5 +106,18 @@ struct dp_usbpd *dp_hpd_get(struct device *dev, struct dp_usbpd_cb *cb)
 
 	dp_hpd->dp_usbpd.connect = dp_hpd_connect;
 
+	mux_desc.fwnode = dev->fwnode;
+	mux_desc.set = dp_hpd_mux_set;
+	mux_desc.drvdata = dp_hpd;
+	dp_hpd->mux = typec_mux_register(dev, &mux_desc);
+	if (IS_ERR(dp_hpd->mux)) {
+		dev_err(dev, "unable to register typec mux\n");
+		return ERR_CAST(dp_hpd->mux);
+	}
+
+	rc = devm_add_action_or_reset(dev, dp_hpd_unregister_typec_mux, dp_hpd->mux);
+	if (rc)
+		return ERR_PTR(rc);
+
 	return &dp_hpd->dp_usbpd;
 }
