From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tony <tonymckahan@gmail.com>
Date: Wed, 3 Mar 2021 07:59:25 +0100
Subject: [ARCHEOLOGY] RK3399 Typec DP (#2676)

> X-Git-Archeology: > recovered message: > * RK3399 NanoPC-T4 Add Type-C alt mode DP
> X-Git-Archeology: > recovered message: > * rk3399 rockpi 4C add mini-DP (WIP)
> X-Git-Archeology: > recovered message: > * [ rockchip64 ] revert rockPi 4C DP patch
> X-Git-Archeology: > recovered message: > Add an extension to disable it, but leave for future work.
> X-Git-Archeology: - Revision 4971535c774a1f49a811baebc083ea028ced0300: https://github.com/armbian/build/commit/4971535c774a1f49a811baebc083ea028ced0300
> X-Git-Archeology:   Date: Wed, 03 Mar 2021 07:59:25 +0100
> X-Git-Archeology:   From: Tony <tonymckahan@gmail.com>
> X-Git-Archeology:   Subject: RK3399 Typec DP (#2676)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 0cdffb29b07305209efb12cf3b5ac6032d3a1153: https://github.com/armbian/build/commit/0cdffb29b07305209efb12cf3b5ac6032d3a1153
> X-Git-Archeology:   Date: Wed, 24 Mar 2021 19:01:53 +0100
> X-Git-Archeology:   From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
> X-Git-Archeology:   Subject: Renaming DEV branch to EDGE (#2704)
> X-Git-Archeology:
> X-Git-Archeology: - Revision ac8fc4385594d59257ee9dffd9efa85e3497fa7d: https://github.com/armbian/build/commit/ac8fc4385594d59257ee9dffd9efa85e3497fa7d
> X-Git-Archeology:   Date: Sat, 26 Feb 2022 07:46:44 +0100
> X-Git-Archeology:   From: Piotr Szczepanik <piter75@gmail.com>
> X-Git-Archeology:   Subject: Switch rockchip64 current to linux 5.15.y (#3489)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 897674aa74bce0326ed7fe06f5336bf4709a8a1f: https://github.com/armbian/build/commit/897674aa74bce0326ed7fe06f5336bf4709a8a1f
> X-Git-Archeology:   Date: Tue, 03 May 2022 08:27:32 +0200
> X-Git-Archeology:   From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
> X-Git-Archeology:   Subject: Bump and freeze kernel at last known working versions (#3736)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 597d2dac11f00d9070a4e49d6bad1b2244e36cb3: https://github.com/armbian/build/commit/597d2dac11f00d9070a4e49d6bad1b2244e36cb3
> X-Git-Archeology:   Date: Sat, 28 May 2022 07:56:22 +0200
> X-Git-Archeology:   From: Jianfeng Liu <liujianfeng1994@gmail.com>
> X-Git-Archeology:   Subject: update rockchip64-edge to 5.18 (#3814)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 8c6641e7b79f0d50acdc306d140e586a4e923cf0: https://github.com/armbian/build/commit/8c6641e7b79f0d50acdc306d140e586a4e923cf0
> X-Git-Archeology:   Date: Wed, 03 Aug 2022 22:22:55 +0200
> X-Git-Archeology:   From: Jianfeng Liu <liujianfeng1994@gmail.com>
> X-Git-Archeology:   Subject: update rockchip64 edge to 5.19 (#4039)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 6765f734cc4a22aeaa9f99a3ad28c8c322de26f6: https://github.com/armbian/build/commit/6765f734cc4a22aeaa9f99a3ad28c8c322de26f6
> X-Git-Archeology:   Date: Tue, 25 Oct 2022 11:26:51 +0200
> X-Git-Archeology:   From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
> X-Git-Archeology:   Subject: Bump rockchip64 edge to 6.0.y (#4337)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 92f1a22d76b987afa7ba555d5b509adc51d689e7: https://github.com/armbian/build/commit/92f1a22d76b987afa7ba555d5b509adc51d689e7
> X-Git-Archeology:   Date: Fri, 16 Dec 2022 13:38:13 +0100
> X-Git-Archeology:   From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
> X-Git-Archeology:   Subject: Re-add rockchip64 6.0 patches (#4575)
> X-Git-Archeology:
> X-Git-Archeology: - Revision 34ae84fac5d0b66a1ab2d1e51534b7beb13ef245: https://github.com/armbian/build/commit/34ae84fac5d0b66a1ab2d1e51534b7beb13ef245
> X-Git-Archeology:   Date: Fri, 05 May 2023 14:22:00 +0200
> X-Git-Archeology:   From: amazingfate <liujianfeng1994@gmail.com>
> X-Git-Archeology:   Subject: bump rockchip64 edge to v6.3
> X-Git-Archeology:
---
 drivers/extcon/Kconfig                  |  10 +
 drivers/extcon/Makefile                 |   1 +
 drivers/extcon/extcon-usbc-virtual-pd.c | 285 ++++++++++
 3 files changed, 296 insertions(+)

diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -214,4 +214,14 @@ config EXTCON_RTK_TYPE_C
 	  The DHC (Digital Home Hub) RTD series SoC contains a type c module.
 	  This driver will detect the status of the type-c port.
 
+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 111111111111..222222222222 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_EXTCON_USB_GPIO)	+= extcon-usb-gpio.o
 obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o
 obj-$(CONFIG_EXTCON_USBC_TUSB320) += extcon-usbc-tusb320.o
 obj-$(CONFIG_EXTCON_RTK_TYPE_C) += extcon-rtk-type-c.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..111111111111
--- /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_ASIS);
+	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 void vpd_extcon_remove(struct platform_device *pdev)
+{
+	struct vpd_extcon *vpd = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&vpd->irq_work);
+
+	return;
+}
+
+#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");
-- 
Armbian

