diff --git a/MAINTAINERS b/MAINTAINERS
index 68f21d46614c..aeb161b19dae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6466,6 +6466,12 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git
 F:	Documentation/filesystems/ext4/
 F:	fs/ext4/
 
+EXTCON DRIVER FOR TYPE-C VIRTUAL PD
+M:	Jagan Teki <jagan@amarulasolutions.com>
+S:	Maintained
+F:	Documentation/devicetree/bindings/extcon/extcon-usbc-virtual-pd.yaml
+F:	drivers/extcon/extcon-usbc-virtual-pd.c
+
 Extended Verification Module (EVM)
 M:	Mimi Zohar <zohar@linux.ibm.com>
 L:	linux-integrity@vger.kernel.org
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index aac507bff135..edd6c3c52699 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -186,4 +186,14 @@ config EXTCON_USBC_CROS_EC
 	  Say Y here to enable USB Type C cable detection extcon support when
 	  using Chrome OS EC based USB Type-C ports.
 
+config EXTCON_USBC_VIRTUAL_PD
+	tristate "Virtual Type-C PD EXTCON support"
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here to enable Virtual Type-C PD extcon driver support, if
+	  hardware platform designed Type-C modes separately.
+
+	  Example, of designing Display Port separately from Type-C Altmode
+	  instead of accessing Altmode Display Port in Type-C connector.
+
 endif
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 52096fd8a216..c35191eef0e1 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_EXTCON_RT8973A)	+= extcon-rt8973a.o
 obj-$(CONFIG_EXTCON_SM5502)	+= extcon-sm5502.o
 obj-$(CONFIG_EXTCON_USB_GPIO)	+= extcon-usb-gpio.o
 obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o
+obj-$(CONFIG_EXTCON_USBC_VIRTUAL_PD) += extcon-usbc-virtual-pd.o
diff --git a/drivers/extcon/extcon-usbc-virtual-pd.c b/drivers/extcon/extcon-usbc-virtual-pd.c
new file mode 100644
index 000000000000..e0713670e33d
--- /dev/null
+++ b/drivers/extcon/extcon-usbc-virtual-pd.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Type-C Virtual PD Extcon driver
+ *
+ * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (c) 2019 Radxa Limited
+ * Copyright (c) 2019 Amarula Solutions(India)
+ */
+
+#include <linux/extcon-provider.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static const unsigned int vpd_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_DISP_DP,
+	EXTCON_NONE,
+};
+
+enum vpd_data_role {
+	DR_NONE,
+	DR_HOST,
+	DR_DEVICE,
+	DR_DISPLAY_PORT,
+};
+
+enum vpd_polarity {
+	POLARITY_NORMAL,
+	POLARITY_FLIP,
+};
+
+enum vpd_usb_ss {
+	USB_SS_USB2,
+	USB_SS_USB3,
+};
+
+struct vpd_extcon {
+	struct device *dev;
+	struct extcon_dev *extcon;
+	struct gpio_desc *det_gpio;
+
+	u8 polarity;
+	u8 usb_ss;
+	enum vpd_data_role data_role;
+
+	int irq;
+	bool enable_irq;
+	struct work_struct work;
+	struct delayed_work irq_work;
+};
+
+static void vpd_extcon_irq_work(struct work_struct *work)
+{
+	struct vpd_extcon *vpd = container_of(work, struct vpd_extcon, irq_work.work);
+	bool host_connected = false, device_connected = false, dp_connected = false;
+	union extcon_property_value property;
+	int det;
+
+	det = vpd->det_gpio ? gpiod_get_raw_value(vpd->det_gpio) : 0;
+	if (det) {
+		device_connected = (vpd->data_role == DR_DEVICE) ? true : false;
+		host_connected = (vpd->data_role == DR_HOST) ? true : false;
+		dp_connected = (vpd->data_role == DR_DISPLAY_PORT) ? true : false;
+	}
+
+	extcon_set_state(vpd->extcon, EXTCON_USB, host_connected);
+	extcon_set_state(vpd->extcon, EXTCON_USB_HOST, device_connected);
+	extcon_set_state(vpd->extcon, EXTCON_DISP_DP, dp_connected);
+
+	property.intval = vpd->polarity;
+	extcon_set_property(vpd->extcon, EXTCON_USB,
+			    EXTCON_PROP_USB_TYPEC_POLARITY, property);
+	extcon_set_property(vpd->extcon, EXTCON_USB_HOST,
+			    EXTCON_PROP_USB_TYPEC_POLARITY, property);
+	extcon_set_property(vpd->extcon, EXTCON_DISP_DP,
+			    EXTCON_PROP_USB_TYPEC_POLARITY, property);
+
+	property.intval = vpd->usb_ss;
+	extcon_set_property(vpd->extcon, EXTCON_USB,
+			    EXTCON_PROP_USB_SS, property);
+	extcon_set_property(vpd->extcon, EXTCON_USB_HOST,
+			    EXTCON_PROP_USB_SS, property);
+	extcon_set_property(vpd->extcon, EXTCON_DISP_DP,
+			    EXTCON_PROP_USB_SS, property);
+
+	extcon_sync(vpd->extcon, EXTCON_USB);
+	extcon_sync(vpd->extcon, EXTCON_USB_HOST);
+	extcon_sync(vpd->extcon, EXTCON_DISP_DP);
+}
+
+static irqreturn_t vpd_extcon_irq_handler(int irq, void *dev_id)
+{
+	struct vpd_extcon *vpd = dev_id;
+
+	schedule_delayed_work(&vpd->irq_work, msecs_to_jiffies(10));
+
+	return IRQ_HANDLED;
+}
+
+static enum vpd_data_role vpd_extcon_data_role(struct vpd_extcon *vpd)
+{
+	const char *const data_roles[] = {
+		[DR_NONE]		= "NONE",
+		[DR_HOST]		= "host",
+		[DR_DEVICE]		= "device",
+		[DR_DISPLAY_PORT]	= "display-port",
+	};
+	struct device *dev = vpd->dev;
+	int ret;
+	const char *dr;
+
+	ret = device_property_read_string(dev, "vpd-data-role", &dr);
+	if (ret < 0)
+		return DR_NONE;
+
+	ret = match_string(data_roles, ARRAY_SIZE(data_roles), dr);
+
+	return (ret < 0) ? DR_NONE : ret;
+}
+
+static int vpd_extcon_parse_dts(struct vpd_extcon *vpd)
+{
+	struct device *dev = vpd->dev;
+	bool val = false;
+	int ret;
+
+	val = device_property_read_bool(dev, "vpd-polarity");
+	if (val)
+		vpd->polarity = POLARITY_FLIP;
+	else
+		vpd->polarity = POLARITY_NORMAL;
+
+	val = device_property_read_bool(dev, "vpd-super-speed");
+	if (val)
+		vpd->usb_ss = USB_SS_USB3;
+	else
+		vpd->usb_ss = USB_SS_USB2;
+
+	vpd->data_role = vpd_extcon_data_role(vpd);
+
+	vpd->det_gpio = devm_gpiod_get_optional(dev, "det", GPIOD_OUT_LOW);
+	if (IS_ERR(vpd->det_gpio)) {
+		ret = PTR_ERR(vpd->det_gpio);
+		dev_warn(dev, "failed to get det gpio: %d\n", ret);
+		return ret;
+	}
+
+	vpd->irq = gpiod_to_irq(vpd->det_gpio);
+	if (vpd->irq < 0) {
+		dev_err(dev, "failed to get irq for gpio: %d\n", vpd->irq);
+		return vpd->irq;
+	}
+
+	ret = devm_request_threaded_irq(dev, vpd->irq, NULL,
+					vpd_extcon_irq_handler,
+					IRQF_TRIGGER_FALLING |
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					NULL, vpd);
+	if (ret)
+		dev_err(dev, "failed to request gpio irq\n");
+
+	return ret;
+}
+
+static int vpd_extcon_probe(struct platform_device *pdev)
+{
+	struct vpd_extcon *vpd;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	vpd = devm_kzalloc(dev, sizeof(*vpd), GFP_KERNEL);
+	if (!vpd)
+		return -ENOMEM;
+
+	vpd->dev = dev;
+	ret = vpd_extcon_parse_dts(vpd);
+	if (ret)
+		return ret;
+
+	INIT_DELAYED_WORK(&vpd->irq_work, vpd_extcon_irq_work);
+
+	vpd->extcon = devm_extcon_dev_allocate(dev, vpd_cable);
+	if (IS_ERR(vpd->extcon)) {
+		dev_err(dev, "allocat extcon failed\n");
+		return PTR_ERR(vpd->extcon);
+	}
+
+	ret = devm_extcon_dev_register(dev, vpd->extcon);
+	if (ret) {
+		dev_err(dev, "register extcon failed: %d\n", ret);
+		return ret;
+	}
+
+	extcon_set_property_capability(vpd->extcon, EXTCON_USB,
+				       EXTCON_PROP_USB_VBUS);
+	extcon_set_property_capability(vpd->extcon, EXTCON_USB_HOST,
+				       EXTCON_PROP_USB_VBUS);
+
+	extcon_set_property_capability(vpd->extcon, EXTCON_USB,
+				       EXTCON_PROP_USB_TYPEC_POLARITY);
+	extcon_set_property_capability(vpd->extcon, EXTCON_USB_HOST,
+				       EXTCON_PROP_USB_TYPEC_POLARITY);
+	extcon_set_property_capability(vpd->extcon, EXTCON_USB,
+				       EXTCON_PROP_USB_SS);
+	extcon_set_property_capability(vpd->extcon, EXTCON_USB_HOST,
+				       EXTCON_PROP_USB_SS);
+
+	extcon_set_property_capability(vpd->extcon, EXTCON_DISP_DP,
+				       EXTCON_PROP_USB_SS);
+	extcon_set_property_capability(vpd->extcon, EXTCON_DISP_DP,
+				       EXTCON_PROP_USB_TYPEC_POLARITY);
+
+	platform_set_drvdata(pdev, vpd);
+
+	vpd_extcon_irq_work(&vpd->irq_work.work);
+
+	return 0;
+}
+
+static int vpd_extcon_remove(struct platform_device *pdev)
+{
+	struct vpd_extcon *vpd = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&vpd->irq_work);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vpd_extcon_suspend(struct device *dev)
+{
+	struct vpd_extcon *vpd = dev_get_drvdata(dev);
+
+	if (!vpd->enable_irq) {
+		disable_irq_nosync(vpd->irq);
+		vpd->enable_irq = true;
+	}
+
+	return 0;
+}
+
+static int vpd_extcon_resume(struct device *dev)
+{
+	struct vpd_extcon *vpd = dev_get_drvdata(dev);
+
+	if (vpd->enable_irq) {
+		enable_irq(vpd->irq);
+		vpd->enable_irq = false;
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vpd_extcon_pm_ops,
+			 vpd_extcon_suspend, vpd_extcon_resume);
+
+static const struct of_device_id vpd_extcon_dt_match[] = {
+	{ .compatible = "linux,extcon-usbc-virtual-pd", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver vpd_extcon_driver = {
+	.probe		= vpd_extcon_probe,
+	.remove		= vpd_extcon_remove,
+	.driver		= {
+		.name	= "extcon-usbc-virtual-pd",
+		.pm	= &vpd_extcon_pm_ops,
+		.of_match_table = vpd_extcon_dt_match,
+	},
+};
+
+module_platform_driver(vpd_extcon_driver);
+
+MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
+MODULE_DESCRIPTION("Type-C Virtual PD extcon driver");
+MODULE_LICENSE("GPL v2");
