Add support for entering and exiting displayport alt-mode on systems
using AP driven alt-mode.
Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
---
Changes in v2:
- Refactored displayport into cros_typec_altmode.c to extract common
implementation between altmodes
MAINTAINERS | 3 +
drivers/platform/chrome/Makefile | 3 +-
drivers/platform/chrome/cros_ec_typec.c | 13 +-
drivers/platform/chrome/cros_ec_typec.h | 1 +
drivers/platform/chrome/cros_typec_altmode.c | 277 +++++++++++++++++++
drivers/platform/chrome/cros_typec_altmode.h | 34 +++
6 files changed, 329 insertions(+), 2 deletions(-)
create mode 100644 drivers/platform/chrome/cros_typec_altmode.c
create mode 100644 drivers/platform/chrome/cros_typec_altmode.h
diff --git a/MAINTAINERS b/MAINTAINERS
index e9659a5a7fb3..de99bcbda7d4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5369,9 +5369,12 @@ F: include/linux/platform_data/cros_usbpd_notify.h
CHROMEOS EC USB TYPE-C DRIVER
M: Prashant Malani <pmalani@chromium.org>
+M: Benson Leung <bleung@chromium.org>
+M: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
L: chrome-platform@lists.linux.dev
S: Maintained
F: drivers/platform/chrome/cros_ec_typec.*
+F: drivers/platform/chrome/cros_typec_altmode.*
F: drivers/platform/chrome/cros_typec_switch.c
F: drivers/platform/chrome/cros_typec_vdm.*
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 2dcc6ccc2302..8b007404c024 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -17,8 +17,9 @@ obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
obj-$(CONFIG_CROS_EC_UART) += cros_ec_uart.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
-cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o
+cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o cros_typec_altmode.o
obj-$(CONFIG_CROS_EC_TYPEC) += cros-ec-typec.o
+
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o
obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 0c8db11bd8a4..7997e7136c4c 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -18,6 +18,7 @@
#include "cros_ec_typec.h"
#include "cros_typec_vdm.h"
+#include "cros_typec_altmode.h"
#define DRV_NAME "cros-ec-typec"
@@ -293,12 +294,16 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
desc.svid = USB_TYPEC_DP_SID;
desc.mode = USB_TYPEC_DP_MODE;
desc.vdo = DP_PORT_VDO;
- amode = typec_port_register_altmode(port->port, &desc);
+ amode = cros_typec_register_displayport(port, &desc,
+ typec->ap_driven_altmode);
if (IS_ERR(amode))
return PTR_ERR(amode);
port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
+
+#if !IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
typec_altmode_set_drvdata(amode, port);
amode->ops = &port_amode_ops;
+#endif
/*
* Register TBT compatibility alt mode. The EC will not enter the mode
@@ -575,6 +580,10 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
if (!ret)
ret = typec_mux_set(port->mux, &port->state);
+ if (!ret)
+ cros_typec_displayport_status_update(port->state.alt,
+ port->state.data);
+
return ret;
}
@@ -1254,6 +1263,8 @@ static int cros_typec_probe(struct platform_device *pdev)
typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD);
typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);
+ typec->ap_driven_altmode = cros_ec_check_features(
+ ec_dev, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY);
ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
&resp, sizeof(resp));
diff --git a/drivers/platform/chrome/cros_ec_typec.h b/drivers/platform/chrome/cros_ec_typec.h
index deda180a646f..9fd5342bb0ad 100644
--- a/drivers/platform/chrome/cros_ec_typec.h
+++ b/drivers/platform/chrome/cros_ec_typec.h
@@ -39,6 +39,7 @@ struct cros_typec_data {
struct work_struct port_work;
bool typec_cmd_supported;
bool needs_mux_ack;
+ bool ap_driven_altmode;
};
/* Per port data. */
diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
new file mode 100644
index 000000000000..af2f077674f1
--- /dev/null
+++ b/drivers/platform/chrome/cros_typec_altmode.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Alt-mode implementation on ChromeOS EC.
+ *
+ * Copyright 2024 Google LLC
+ * Author: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
+ */
+#include "cros_ec_typec.h"
+
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/pd_vdo.h>
+
+#include "cros_typec_altmode.h"
+
+struct cros_typec_dp_data {
+ struct typec_displayport_data data;
+
+ bool configured;
+ bool pending_status_update;
+};
+
+struct cros_typec_altmode_data {
+ struct work_struct work;
+ struct cros_typec_port *port;
+ struct typec_altmode *alt;
+ bool ap_mode_entry;
+
+ u32 header;
+ u32 *vdo_data;
+ u8 vdo_size;
+
+ u16 sid;
+ u8 mode;
+
+ union am_specific {
+ struct cros_typec_dp_data dp;
+ } am_data;
+};
+
+static void cros_typec_altmode_work(struct work_struct *work)
+{
+ struct cros_typec_altmode_data *data =
+ container_of(work, struct cros_typec_altmode_data, work);
+
+ if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
+ data->vdo_size))
+ dev_err(&data->alt->dev, "VDM 0x%x failed", data->header);
+
+ data->header = 0;
+ data->vdo_data = NULL;
+ data->vdo_size = 0;
+}
+
+static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
+{
+ struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
+ struct ec_params_typec_control req = {
+ .port = data->port->port_num,
+ .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
+ };
+ int svdm_version;
+ int ret;
+
+ if (!data->ap_mode_entry) {
+ const struct typec_altmode *partner =
+ typec_altmode_get_partner(alt);
+ dev_warn(&partner->dev,
+ "EC does not support ap driven mode entry\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (data->sid == USB_TYPEC_DP_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_DP;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ data->header = VDO(data->sid, 1, svdm_version, CMD_ENTER_MODE);
+ data->header |= VDO_OPOS(data->mode);
+ data->header |= VDO_CMDT(CMDT_RSP_ACK);
+
+ data->vdo_data = NULL;
+ data->vdo_size = 1;
+
+ schedule_work(&data->work);
+
+ return ret;
+}
+
+static int cros_typec_altmode_exit(struct typec_altmode *alt)
+{
+ struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
+ struct ec_params_typec_control req = {
+ .port = data->port->port_num,
+ .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
+ };
+ int svdm_version;
+ int ret;
+
+ if (!data->ap_mode_entry) {
+ const struct typec_altmode *partner =
+ typec_altmode_get_partner(alt);
+ dev_warn(&partner->dev,
+ "EC does not support ap driven mode entry\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+
+ if (ret < 0)
+ return ret;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ data->header = VDO(data->sid, 1, svdm_version, CMD_EXIT_MODE);
+ data->header |= VDO_OPOS(data->mode);
+ data->header |= VDO_CMDT(CMDT_RSP_ACK);
+
+ data->vdo_data = NULL;
+ data->vdo_size = 1;
+
+ schedule_work(&data->work);
+
+ return ret;
+}
+
+static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
+ const u32 *data, int count)
+{
+ struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
+
+ int cmd_type = PD_VDO_CMDT(header);
+ int cmd = PD_VDO_CMD(header);
+ int svdm_version;
+
+ if (!adata->ap_mode_entry) {
+ const struct typec_altmode *partner =
+ typec_altmode_get_partner(alt);
+ dev_warn(&partner->dev,
+ "EC does not support ap driven mode entry\n");
+ return -EOPNOTSUPP;
+ }
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ if (PD_VDO_SVDM_VER(header) < svdm_version) {
+ typec_partner_set_svdm_version(adata->port->partner,
+ PD_VDO_SVDM_VER(header));
+ svdm_version = PD_VDO_SVDM_VER(header);
+ }
+
+ adata->header = VDO(adata->sid, 1, svdm_version, cmd);
+ adata->header |= VDO_OPOS(adata->mode);
+
+ /*
+ * DP_CMD_CONFIGURE: We can't actually do anything with the
+ * provided VDO yet so just send back an ACK.
+ *
+ * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
+ * DPStatus Acks.
+ */
+ switch (cmd) {
+ case DP_CMD_CONFIGURE:
+ adata->am_data.dp.data.conf = *data;
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ adata->am_data.dp.configured = true;
+ schedule_work(&adata->work);
+ break;
+ case DP_CMD_STATUS_UPDATE:
+ adata->am_data.dp.pending_status_update = true;
+ break;
+ default:
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ schedule_work(&adata->work);
+ break;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
+ const u32 *data, int count)
+{
+ struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ return cros_typec_displayport_vdm(alt, header, data, count);
+
+ return -EINVAL;
+}
+
+static const struct typec_altmode_ops cros_typec_altmode_ops = {
+ .enter = cros_typec_altmode_enter,
+ .exit = cros_typec_altmode_exit,
+ .vdm = cros_typec_altmode_vdm,
+};
+
+#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
+int cros_typec_displayport_status_update(struct typec_altmode *altmode,
+ struct typec_displayport_data *data)
+{
+ struct cros_typec_altmode_data *adata =
+ typec_altmode_get_drvdata(altmode);
+
+ if (!adata->am_data.dp.pending_status_update) {
+ dev_dbg(&altmode->dev,
+ "Got DPStatus without a pending request");
+ return 0;
+ }
+
+ if (adata->am_data.dp.configured && adata->am_data.dp.data.conf != data->conf)
+ dev_dbg(&altmode->dev,
+ "DP Conf doesn't match. Requested 0x%04x, Actual 0x%04x",
+ adata->am_data.dp.data.conf, data->conf);
+
+ adata->am_data.dp.data = *data;
+ adata->am_data.dp.pending_status_update = false;
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ adata->vdo_data = &adata->am_data.dp.data.status;
+ adata->vdo_size = 2;
+
+ schedule_work(&adata->work);
+ return 0;
+}
+
+struct typec_altmode *
+cros_typec_register_displayport(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc,
+ bool ap_mode_entry)
+{
+ struct typec_altmode *alt;
+ struct cros_typec_altmode_data *data;
+
+ alt = typec_port_register_altmode(port->port, desc);
+ if (IS_ERR(alt))
+ return alt;
+
+ data = devm_kzalloc(&alt->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ typec_unregister_altmode(alt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ INIT_WORK(&data->work, cros_typec_altmode_work);
+ data->alt = alt;
+ data->port = port;
+ data->ap_mode_entry = ap_mode_entry;
+ data->sid = USB_TYPEC_DP_SID;
+ data->mode = USB_TYPEC_DP_MODE;
+
+ data->am_data.dp.configured = false;
+ typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
+ typec_altmode_set_drvdata(alt, data);
+
+ return alt;
+}
+#endif
diff --git a/drivers/platform/chrome/cros_typec_altmode.h b/drivers/platform/chrome/cros_typec_altmode.h
new file mode 100644
index 000000000000..c6f8fb02c99c
--- /dev/null
+++ b/drivers/platform/chrome/cros_typec_altmode.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __CROS_TYPEC_ALTMODE_H__
+#define __CROS_TYPEC_ALTMODE_H__
+
+struct cros_typec_port;
+struct typec_altmode;
+struct typec_altmode_desc;
+struct typec_displayport_data;
+
+#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
+struct typec_altmode *
+cros_typec_register_displayport(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc,
+ bool ap_mode_entry);
+
+int cros_typec_displayport_status_update(struct typec_altmode *altmode,
+ struct typec_displayport_data *data);
+#else
+static inline struct typec_altmode *
+cros_typec_register_displayport(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc,
+ bool ap_mode_entry)
+{
+ return typec_port_register_altmode(port->port, desc);
+}
+
+static inline int cros_typec_displayport_status_update(struct typec_altmode *altmode,
+ struct typec_displayport_data *data)
+{
+ return 0;
+}
+#endif
+#endif /* __CROS_TYPEC_ALTMODE_H__ */
--
2.47.0.163.g1226f6d8fa-goog
Hi Abhishek,
kernel test robot noticed the following build warnings:
[auto build test WARNING on chrome-platform/for-next]
[also build test WARNING on chrome-platform/for-firmware-next usb/usb-testing usb/usb-next usb/usb-linus westeri-thunderbolt/next linus/master v6.12-rc5 next-20241031]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Abhishek-Pandit-Subedi/usb-typec-Add-driver-for-Thunderbolt-3-Alternate-Mode/20241031-053304
base: https://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git for-next
patch link: https://lore.kernel.org/r/20241030142833.v2.5.I142fc0c09df58689b98f0cebf1c5e48b9d4fa800%40changeid
patch subject: [PATCH v2 5/7] platform/chrome: cros_ec_typec: Displayport support
config: arm-multi_v7_defconfig (https://download.01.org/0day-ci/archive/20241101/202411010346.AqbfMqLc-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 13.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241101/202411010346.AqbfMqLc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411010346.AqbfMqLc-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/platform/chrome/cros_typec_altmode.c:212:39: warning: 'cros_typec_altmode_ops' defined but not used [-Wunused-const-variable=]
212 | static const struct typec_altmode_ops cros_typec_altmode_ops = {
| ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/platform/chrome/cros_typec_altmode.c:40:13: warning: 'cros_typec_altmode_work' defined but not used [-Wunused-function]
40 | static void cros_typec_altmode_work(struct work_struct *work)
| ^~~~~~~~~~~~~~~~~~~~~~~
vim +/cros_typec_altmode_ops +212 drivers/platform/chrome/cros_typec_altmode.c
39
> 40 static void cros_typec_altmode_work(struct work_struct *work)
41 {
42 struct cros_typec_altmode_data *data =
43 container_of(work, struct cros_typec_altmode_data, work);
44
45 if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
46 data->vdo_size))
47 dev_err(&data->alt->dev, "VDM 0x%x failed", data->header);
48
49 data->header = 0;
50 data->vdo_data = NULL;
51 data->vdo_size = 0;
52 }
53
54 static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
55 {
56 struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
57 struct ec_params_typec_control req = {
58 .port = data->port->port_num,
59 .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
60 };
61 int svdm_version;
62 int ret;
63
64 if (!data->ap_mode_entry) {
65 const struct typec_altmode *partner =
66 typec_altmode_get_partner(alt);
67 dev_warn(&partner->dev,
68 "EC does not support ap driven mode entry\n");
69 return -EOPNOTSUPP;
70 }
71
72 if (data->sid == USB_TYPEC_DP_SID)
73 req.mode_to_enter = CROS_EC_ALTMODE_DP;
74 else
75 return -EOPNOTSUPP;
76
77 ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
78 &req, sizeof(req), NULL, 0);
79 if (ret < 0)
80 return ret;
81
82 svdm_version = typec_altmode_get_svdm_version(alt);
83 if (svdm_version < 0)
84 return svdm_version;
85
86 data->header = VDO(data->sid, 1, svdm_version, CMD_ENTER_MODE);
87 data->header |= VDO_OPOS(data->mode);
88 data->header |= VDO_CMDT(CMDT_RSP_ACK);
89
90 data->vdo_data = NULL;
91 data->vdo_size = 1;
92
93 schedule_work(&data->work);
94
95 return ret;
96 }
97
98 static int cros_typec_altmode_exit(struct typec_altmode *alt)
99 {
100 struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
101 struct ec_params_typec_control req = {
102 .port = data->port->port_num,
103 .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
104 };
105 int svdm_version;
106 int ret;
107
108 if (!data->ap_mode_entry) {
109 const struct typec_altmode *partner =
110 typec_altmode_get_partner(alt);
111 dev_warn(&partner->dev,
112 "EC does not support ap driven mode entry\n");
113 return -EOPNOTSUPP;
114 }
115
116 ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
117 &req, sizeof(req), NULL, 0);
118
119 if (ret < 0)
120 return ret;
121
122 svdm_version = typec_altmode_get_svdm_version(alt);
123 if (svdm_version < 0)
124 return svdm_version;
125
126 data->header = VDO(data->sid, 1, svdm_version, CMD_EXIT_MODE);
127 data->header |= VDO_OPOS(data->mode);
128 data->header |= VDO_CMDT(CMDT_RSP_ACK);
129
130 data->vdo_data = NULL;
131 data->vdo_size = 1;
132
133 schedule_work(&data->work);
134
135 return ret;
136 }
137
138 static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
139 const u32 *data, int count)
140 {
141 struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
142
143 int cmd_type = PD_VDO_CMDT(header);
144 int cmd = PD_VDO_CMD(header);
145 int svdm_version;
146
147 if (!adata->ap_mode_entry) {
148 const struct typec_altmode *partner =
149 typec_altmode_get_partner(alt);
150 dev_warn(&partner->dev,
151 "EC does not support ap driven mode entry\n");
152 return -EOPNOTSUPP;
153 }
154
155 svdm_version = typec_altmode_get_svdm_version(alt);
156 if (svdm_version < 0)
157 return svdm_version;
158
159 switch (cmd_type) {
160 case CMDT_INIT:
161 if (PD_VDO_SVDM_VER(header) < svdm_version) {
162 typec_partner_set_svdm_version(adata->port->partner,
163 PD_VDO_SVDM_VER(header));
164 svdm_version = PD_VDO_SVDM_VER(header);
165 }
166
167 adata->header = VDO(adata->sid, 1, svdm_version, cmd);
168 adata->header |= VDO_OPOS(adata->mode);
169
170 /*
171 * DP_CMD_CONFIGURE: We can't actually do anything with the
172 * provided VDO yet so just send back an ACK.
173 *
174 * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
175 * DPStatus Acks.
176 */
177 switch (cmd) {
178 case DP_CMD_CONFIGURE:
179 adata->am_data.dp.data.conf = *data;
180 adata->header |= VDO_CMDT(CMDT_RSP_ACK);
181 adata->am_data.dp.configured = true;
182 schedule_work(&adata->work);
183 break;
184 case DP_CMD_STATUS_UPDATE:
185 adata->am_data.dp.pending_status_update = true;
186 break;
187 default:
188 adata->header |= VDO_CMDT(CMDT_RSP_ACK);
189 schedule_work(&adata->work);
190 break;
191 }
192
193 break;
194 default:
195 break;
196 }
197
198 return 0;
199 }
200
201 static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
202 const u32 *data, int count)
203 {
204 struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
205
206 if (adata->sid == USB_TYPEC_DP_SID)
207 return cros_typec_displayport_vdm(alt, header, data, count);
208
209 return -EINVAL;
210 }
211
> 212 static const struct typec_altmode_ops cros_typec_altmode_ops = {
213 .enter = cros_typec_altmode_enter,
214 .exit = cros_typec_altmode_exit,
215 .vdm = cros_typec_altmode_vdm,
216 };
217
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Wed, Oct 30, 2024 at 02:28:36PM -0700, Abhishek Pandit-Subedi wrote:
> Add support for entering and exiting displayport alt-mode on systems
> using AP driven alt-mode.
>
> Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> ---
>
> Changes in v2:
> - Refactored displayport into cros_typec_altmode.c to extract common
> implementation between altmodes
Thanks!
>
> MAINTAINERS | 3 +
> drivers/platform/chrome/Makefile | 3 +-
> drivers/platform/chrome/cros_ec_typec.c | 13 +-
> drivers/platform/chrome/cros_ec_typec.h | 1 +
> drivers/platform/chrome/cros_typec_altmode.c | 277 +++++++++++++++++++
> drivers/platform/chrome/cros_typec_altmode.h | 34 +++
> 6 files changed, 329 insertions(+), 2 deletions(-)
> create mode 100644 drivers/platform/chrome/cros_typec_altmode.c
> create mode 100644 drivers/platform/chrome/cros_typec_altmode.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e9659a5a7fb3..de99bcbda7d4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5369,9 +5369,12 @@ F: include/linux/platform_data/cros_usbpd_notify.h
>
> CHROMEOS EC USB TYPE-C DRIVER
> M: Prashant Malani <pmalani@chromium.org>
> +M: Benson Leung <bleung@chromium.org>
> +M: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> L: chrome-platform@lists.linux.dev
> S: Maintained
> F: drivers/platform/chrome/cros_ec_typec.*
> +F: drivers/platform/chrome/cros_typec_altmode.*
> F: drivers/platform/chrome/cros_typec_switch.c
> F: drivers/platform/chrome/cros_typec_vdm.*
>
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index 2dcc6ccc2302..8b007404c024 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -17,8 +17,9 @@ obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
> obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
> obj-$(CONFIG_CROS_EC_UART) += cros_ec_uart.o
> cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
> -cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o
> +cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o cros_typec_altmode.o
> obj-$(CONFIG_CROS_EC_TYPEC) += cros-ec-typec.o
> +
> obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
> obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o
> obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
> diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
> index 0c8db11bd8a4..7997e7136c4c 100644
> --- a/drivers/platform/chrome/cros_ec_typec.c
> +++ b/drivers/platform/chrome/cros_ec_typec.c
> @@ -18,6 +18,7 @@
>
> #include "cros_ec_typec.h"
> #include "cros_typec_vdm.h"
> +#include "cros_typec_altmode.h"
>
> #define DRV_NAME "cros-ec-typec"
>
> @@ -293,12 +294,16 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
> desc.svid = USB_TYPEC_DP_SID;
> desc.mode = USB_TYPEC_DP_MODE;
> desc.vdo = DP_PORT_VDO;
> - amode = typec_port_register_altmode(port->port, &desc);
> + amode = cros_typec_register_displayport(port, &desc,
> + typec->ap_driven_altmode);
> if (IS_ERR(amode))
> return PTR_ERR(amode);
> port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
> +
> +#if !IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
> typec_altmode_set_drvdata(amode, port);
> amode->ops = &port_amode_ops;
> +#endif
>
> /*
> * Register TBT compatibility alt mode. The EC will not enter the mode
> @@ -575,6 +580,10 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
> if (!ret)
> ret = typec_mux_set(port->mux, &port->state);
>
> + if (!ret)
> + cros_typec_displayport_status_update(port->state.alt,
> + port->state.data);
> +
> return ret;
> }
>
> @@ -1254,6 +1263,8 @@ static int cros_typec_probe(struct platform_device *pdev)
>
> typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD);
> typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);
> + typec->ap_driven_altmode = cros_ec_check_features(
> + ec_dev, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY);
>
> ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
> &resp, sizeof(resp));
> diff --git a/drivers/platform/chrome/cros_ec_typec.h b/drivers/platform/chrome/cros_ec_typec.h
> index deda180a646f..9fd5342bb0ad 100644
> --- a/drivers/platform/chrome/cros_ec_typec.h
> +++ b/drivers/platform/chrome/cros_ec_typec.h
> @@ -39,6 +39,7 @@ struct cros_typec_data {
> struct work_struct port_work;
> bool typec_cmd_supported;
> bool needs_mux_ack;
> + bool ap_driven_altmode;
> };
>
> /* Per port data. */
> diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
> new file mode 100644
> index 000000000000..af2f077674f1
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_typec_altmode.c
> @@ -0,0 +1,277 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Alt-mode implementation on ChromeOS EC.
> + *
> + * Copyright 2024 Google LLC
> + * Author: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> + */
> +#include "cros_ec_typec.h"
> +
> +#include <linux/usb/typec_dp.h>
> +#include <linux/usb/pd_vdo.h>
> +
> +#include "cros_typec_altmode.h"
> +
> +struct cros_typec_dp_data {
> + struct typec_displayport_data data;
> +
> + bool configured;
> + bool pending_status_update;
> +};
> +
> +struct cros_typec_altmode_data {
> + struct work_struct work;
> + struct cros_typec_port *port;
> + struct typec_altmode *alt;
> + bool ap_mode_entry;
> +
> + u32 header;
> + u32 *vdo_data;
> + u8 vdo_size;
> +
> + u16 sid;
> + u8 mode;
> +
> + union am_specific {
> + struct cros_typec_dp_data dp;
> + } am_data;
Can this be done other way around?
struct cros_typec_dp_altmode_data {
struct cros_typec_altmode_data base;
struct cros_typec_dp_data dp;
};
> +};
> +
> +static void cros_typec_altmode_work(struct work_struct *work)
> +{
> + struct cros_typec_altmode_data *data =
> + container_of(work, struct cros_typec_altmode_data, work);
> +
> + if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
> + data->vdo_size))
> + dev_err(&data->alt->dev, "VDM 0x%x failed", data->header);
> +
> + data->header = 0;
> + data->vdo_data = NULL;
> + data->vdo_size = 0;
> +}
> +
> +static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
> +{
> + struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
> + struct ec_params_typec_control req = {
> + .port = data->port->port_num,
> + .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
> + };
> + int svdm_version;
> + int ret;
> +
> + if (!data->ap_mode_entry) {
> + const struct typec_altmode *partner =
> + typec_altmode_get_partner(alt);
> + dev_warn(&partner->dev,
> + "EC does not support ap driven mode entry\n");
> + return -EOPNOTSUPP;
> + }
> +
> + if (data->sid == USB_TYPEC_DP_SID)
> + req.mode_to_enter = CROS_EC_ALTMODE_DP;
> + else
> + return -EOPNOTSUPP;
> +
> + ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
> + &req, sizeof(req), NULL, 0);
> + if (ret < 0)
> + return ret;
> +
> + svdm_version = typec_altmode_get_svdm_version(alt);
> + if (svdm_version < 0)
> + return svdm_version;
> +
> + data->header = VDO(data->sid, 1, svdm_version, CMD_ENTER_MODE);
> + data->header |= VDO_OPOS(data->mode);
> + data->header |= VDO_CMDT(CMDT_RSP_ACK);
> +
> + data->vdo_data = NULL;
> + data->vdo_size = 1;
> +
> + schedule_work(&data->work);
> +
> + return ret;
> +}
> +
> +static int cros_typec_altmode_exit(struct typec_altmode *alt)
> +{
> + struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
> + struct ec_params_typec_control req = {
> + .port = data->port->port_num,
> + .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
> + };
> + int svdm_version;
> + int ret;
> +
> + if (!data->ap_mode_entry) {
> + const struct typec_altmode *partner =
> + typec_altmode_get_partner(alt);
> + dev_warn(&partner->dev,
> + "EC does not support ap driven mode entry\n");
> + return -EOPNOTSUPP;
> + }
> +
> + ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
> + &req, sizeof(req), NULL, 0);
> +
> + if (ret < 0)
> + return ret;
> +
> + svdm_version = typec_altmode_get_svdm_version(alt);
> + if (svdm_version < 0)
> + return svdm_version;
> +
> + data->header = VDO(data->sid, 1, svdm_version, CMD_EXIT_MODE);
> + data->header |= VDO_OPOS(data->mode);
> + data->header |= VDO_CMDT(CMDT_RSP_ACK);
> +
> + data->vdo_data = NULL;
> + data->vdo_size = 1;
> +
> + schedule_work(&data->work);
> +
> + return ret;
> +}
> +
> +static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
> + const u32 *data, int count)
> +{
> + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
> +
> + int cmd_type = PD_VDO_CMDT(header);
> + int cmd = PD_VDO_CMD(header);
> + int svdm_version;
> +
> + if (!adata->ap_mode_entry) {
> + const struct typec_altmode *partner =
> + typec_altmode_get_partner(alt);
> + dev_warn(&partner->dev,
> + "EC does not support ap driven mode entry\n");
> + return -EOPNOTSUPP;
> + }
> +
> + svdm_version = typec_altmode_get_svdm_version(alt);
> + if (svdm_version < 0)
> + return svdm_version;
> +
> + switch (cmd_type) {
> + case CMDT_INIT:
> + if (PD_VDO_SVDM_VER(header) < svdm_version) {
> + typec_partner_set_svdm_version(adata->port->partner,
> + PD_VDO_SVDM_VER(header));
> + svdm_version = PD_VDO_SVDM_VER(header);
> + }
> +
> + adata->header = VDO(adata->sid, 1, svdm_version, cmd);
> + adata->header |= VDO_OPOS(adata->mode);
> +
> + /*
> + * DP_CMD_CONFIGURE: We can't actually do anything with the
> + * provided VDO yet so just send back an ACK.
> + *
> + * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
> + * DPStatus Acks.
> + */
> + switch (cmd) {
> + case DP_CMD_CONFIGURE:
> + adata->am_data.dp.data.conf = *data;
> + adata->header |= VDO_CMDT(CMDT_RSP_ACK);
> + adata->am_data.dp.configured = true;
> + schedule_work(&adata->work);
> + break;
> + case DP_CMD_STATUS_UPDATE:
> + adata->am_data.dp.pending_status_update = true;
> + break;
> + default:
> + adata->header |= VDO_CMDT(CMDT_RSP_ACK);
> + schedule_work(&adata->work);
> + break;
> + }
> +
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
> + const u32 *data, int count)
> +{
> + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
> +
> + if (adata->sid == USB_TYPEC_DP_SID)
> + return cros_typec_displayport_vdm(alt, header, data, count);
> +
> + return -EINVAL;
> +}
> +
> +static const struct typec_altmode_ops cros_typec_altmode_ops = {
> + .enter = cros_typec_altmode_enter,
> + .exit = cros_typec_altmode_exit,
> + .vdm = cros_typec_altmode_vdm,
> +};
> +
> +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
> +int cros_typec_displayport_status_update(struct typec_altmode *altmode,
> + struct typec_displayport_data *data)
> +{
> + struct cros_typec_altmode_data *adata =
> + typec_altmode_get_drvdata(altmode);
> +
> + if (!adata->am_data.dp.pending_status_update) {
> + dev_dbg(&altmode->dev,
> + "Got DPStatus without a pending request");
> + return 0;
> + }
> +
> + if (adata->am_data.dp.configured && adata->am_data.dp.data.conf != data->conf)
> + dev_dbg(&altmode->dev,
> + "DP Conf doesn't match. Requested 0x%04x, Actual 0x%04x",
> + adata->am_data.dp.data.conf, data->conf);
> +
> + adata->am_data.dp.data = *data;
> + adata->am_data.dp.pending_status_update = false;
> + adata->header |= VDO_CMDT(CMDT_RSP_ACK);
> + adata->vdo_data = &adata->am_data.dp.data.status;
> + adata->vdo_size = 2;
> +
> + schedule_work(&adata->work);
> + return 0;
> +}
> +
> +struct typec_altmode *
> +cros_typec_register_displayport(struct cros_typec_port *port,
> + struct typec_altmode_desc *desc,
> + bool ap_mode_entry)
> +{
> + struct typec_altmode *alt;
> + struct cros_typec_altmode_data *data;
> +
> + alt = typec_port_register_altmode(port->port, desc);
> + if (IS_ERR(alt))
> + return alt;
> +
> + data = devm_kzalloc(&alt->dev, sizeof(*data), GFP_KERNEL);
> + if (!data) {
> + typec_unregister_altmode(alt);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + INIT_WORK(&data->work, cros_typec_altmode_work);
> + data->alt = alt;
> + data->port = port;
> + data->ap_mode_entry = ap_mode_entry;
> + data->sid = USB_TYPEC_DP_SID;
> + data->mode = USB_TYPEC_DP_MODE;
> +
> + data->am_data.dp.configured = false;
> + typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
> + typec_altmode_set_drvdata(alt, data);
> +
> + return alt;
> +}
> +#endif
> diff --git a/drivers/platform/chrome/cros_typec_altmode.h b/drivers/platform/chrome/cros_typec_altmode.h
> new file mode 100644
> index 000000000000..c6f8fb02c99c
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_typec_altmode.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifndef __CROS_TYPEC_ALTMODE_H__
> +#define __CROS_TYPEC_ALTMODE_H__
> +
> +struct cros_typec_port;
> +struct typec_altmode;
> +struct typec_altmode_desc;
> +struct typec_displayport_data;
> +
> +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
> +struct typec_altmode *
> +cros_typec_register_displayport(struct cros_typec_port *port,
> + struct typec_altmode_desc *desc,
> + bool ap_mode_entry);
> +
> +int cros_typec_displayport_status_update(struct typec_altmode *altmode,
> + struct typec_displayport_data *data);
> +#else
> +static inline struct typec_altmode *
> +cros_typec_register_displayport(struct cros_typec_port *port,
> + struct typec_altmode_desc *desc,
> + bool ap_mode_entry)
> +{
> + return typec_port_register_altmode(port->port, desc);
> +}
> +
> +static inline int cros_typec_displayport_status_update(struct typec_altmode *altmode,
> + struct typec_displayport_data *data)
> +{
> + return 0;
> +}
> +#endif
> +#endif /* __CROS_TYPEC_ALTMODE_H__ */
> --
> 2.47.0.163.g1226f6d8fa-goog
>
--
With best wishes
Dmitry
On Thu, Oct 31, 2024 at 11:54 AM Dmitry Baryshkov
<dmitry.baryshkov@linaro.org> wrote:
>
> On Wed, Oct 30, 2024 at 02:28:36PM -0700, Abhishek Pandit-Subedi wrote:
> > Add support for entering and exiting displayport alt-mode on systems
> > using AP driven alt-mode.
> >
> > Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> > ---
> >
> > Changes in v2:
> > - Refactored displayport into cros_typec_altmode.c to extract common
> > implementation between altmodes
>
> Thanks!
>
> >
> > MAINTAINERS | 3 +
> > drivers/platform/chrome/Makefile | 3 +-
> > drivers/platform/chrome/cros_ec_typec.c | 13 +-
> > drivers/platform/chrome/cros_ec_typec.h | 1 +
> > drivers/platform/chrome/cros_typec_altmode.c | 277 +++++++++++++++++++
> > drivers/platform/chrome/cros_typec_altmode.h | 34 +++
> > 6 files changed, 329 insertions(+), 2 deletions(-)
> > create mode 100644 drivers/platform/chrome/cros_typec_altmode.c
> > create mode 100644 drivers/platform/chrome/cros_typec_altmode.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index e9659a5a7fb3..de99bcbda7d4 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -5369,9 +5369,12 @@ F: include/linux/platform_data/cros_usbpd_notify.h
> >
> > CHROMEOS EC USB TYPE-C DRIVER
> > M: Prashant Malani <pmalani@chromium.org>
> > +M: Benson Leung <bleung@chromium.org>
> > +M: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> > L: chrome-platform@lists.linux.dev
> > S: Maintained
> > F: drivers/platform/chrome/cros_ec_typec.*
> > +F: drivers/platform/chrome/cros_typec_altmode.*
> > F: drivers/platform/chrome/cros_typec_switch.c
> > F: drivers/platform/chrome/cros_typec_vdm.*
> >
> > diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> > index 2dcc6ccc2302..8b007404c024 100644
> > --- a/drivers/platform/chrome/Makefile
> > +++ b/drivers/platform/chrome/Makefile
> > @@ -17,8 +17,9 @@ obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
> > obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
> > obj-$(CONFIG_CROS_EC_UART) += cros_ec_uart.o
> > cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
> > -cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o
> > +cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o cros_typec_altmode.o
> > obj-$(CONFIG_CROS_EC_TYPEC) += cros-ec-typec.o
> > +
> > obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
> > obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o
> > obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
> > diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
> > index 0c8db11bd8a4..7997e7136c4c 100644
> > --- a/drivers/platform/chrome/cros_ec_typec.c
> > +++ b/drivers/platform/chrome/cros_ec_typec.c
> > @@ -18,6 +18,7 @@
> >
> > #include "cros_ec_typec.h"
> > #include "cros_typec_vdm.h"
> > +#include "cros_typec_altmode.h"
> >
> > #define DRV_NAME "cros-ec-typec"
> >
> > @@ -293,12 +294,16 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
> > desc.svid = USB_TYPEC_DP_SID;
> > desc.mode = USB_TYPEC_DP_MODE;
> > desc.vdo = DP_PORT_VDO;
> > - amode = typec_port_register_altmode(port->port, &desc);
> > + amode = cros_typec_register_displayport(port, &desc,
> > + typec->ap_driven_altmode);
> > if (IS_ERR(amode))
> > return PTR_ERR(amode);
> > port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
> > +
> > +#if !IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
> > typec_altmode_set_drvdata(amode, port);
> > amode->ops = &port_amode_ops;
> > +#endif
> >
> > /*
> > * Register TBT compatibility alt mode. The EC will not enter the mode
> > @@ -575,6 +580,10 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
> > if (!ret)
> > ret = typec_mux_set(port->mux, &port->state);
> >
> > + if (!ret)
> > + cros_typec_displayport_status_update(port->state.alt,
> > + port->state.data);
> > +
> > return ret;
> > }
> >
> > @@ -1254,6 +1263,8 @@ static int cros_typec_probe(struct platform_device *pdev)
> >
> > typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD);
> > typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);
> > + typec->ap_driven_altmode = cros_ec_check_features(
> > + ec_dev, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY);
> >
> > ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
> > &resp, sizeof(resp));
> > diff --git a/drivers/platform/chrome/cros_ec_typec.h b/drivers/platform/chrome/cros_ec_typec.h
> > index deda180a646f..9fd5342bb0ad 100644
> > --- a/drivers/platform/chrome/cros_ec_typec.h
> > +++ b/drivers/platform/chrome/cros_ec_typec.h
> > @@ -39,6 +39,7 @@ struct cros_typec_data {
> > struct work_struct port_work;
> > bool typec_cmd_supported;
> > bool needs_mux_ack;
> > + bool ap_driven_altmode;
> > };
> >
> > /* Per port data. */
> > diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
> > new file mode 100644
> > index 000000000000..af2f077674f1
> > --- /dev/null
> > +++ b/drivers/platform/chrome/cros_typec_altmode.c
> > @@ -0,0 +1,277 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Alt-mode implementation on ChromeOS EC.
> > + *
> > + * Copyright 2024 Google LLC
> > + * Author: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> > + */
> > +#include "cros_ec_typec.h"
> > +
> > +#include <linux/usb/typec_dp.h>
> > +#include <linux/usb/pd_vdo.h>
> > +
> > +#include "cros_typec_altmode.h"
> > +
> > +struct cros_typec_dp_data {
> > + struct typec_displayport_data data;
> > +
> > + bool configured;
> > + bool pending_status_update;
> > +};
> > +
> > +struct cros_typec_altmode_data {
> > + struct work_struct work;
> > + struct cros_typec_port *port;
> > + struct typec_altmode *alt;
> > + bool ap_mode_entry;
> > +
> > + u32 header;
> > + u32 *vdo_data;
> > + u8 vdo_size;
> > +
> > + u16 sid;
> > + u8 mode;
> > +
> > + union am_specific {
> > + struct cros_typec_dp_data dp;
> > + } am_data;
>
> Can this be done other way around?
>
> struct cros_typec_dp_altmode_data {
> struct cros_typec_altmode_data base;
> struct cros_typec_dp_data dp;
> };
Ack -- expect in the next patch series.
>
> > +};
> > +
> > +static void cros_typec_altmode_work(struct work_struct *work)
> > +{
> > + struct cros_typec_altmode_data *data =
> > + container_of(work, struct cros_typec_altmode_data, work);
> > +
> > + if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
> > + data->vdo_size))
> > + dev_err(&data->alt->dev, "VDM 0x%x failed", data->header);
> > +
> > + data->header = 0;
> > + data->vdo_data = NULL;
> > + data->vdo_size = 0;
> > +}
> > +
> > +static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
> > +{
> > + struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
> > + struct ec_params_typec_control req = {
> > + .port = data->port->port_num,
> > + .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
> > + };
> > + int svdm_version;
> > + int ret;
> > +
> > + if (!data->ap_mode_entry) {
> > + const struct typec_altmode *partner =
> > + typec_altmode_get_partner(alt);
> > + dev_warn(&partner->dev,
> > + "EC does not support ap driven mode entry\n");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + if (data->sid == USB_TYPEC_DP_SID)
> > + req.mode_to_enter = CROS_EC_ALTMODE_DP;
> > + else
> > + return -EOPNOTSUPP;
> > +
> > + ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
> > + &req, sizeof(req), NULL, 0);
> > + if (ret < 0)
> > + return ret;
> > +
> > + svdm_version = typec_altmode_get_svdm_version(alt);
> > + if (svdm_version < 0)
> > + return svdm_version;
> > +
> > + data->header = VDO(data->sid, 1, svdm_version, CMD_ENTER_MODE);
> > + data->header |= VDO_OPOS(data->mode);
> > + data->header |= VDO_CMDT(CMDT_RSP_ACK);
> > +
> > + data->vdo_data = NULL;
> > + data->vdo_size = 1;
> > +
> > + schedule_work(&data->work);
> > +
> > + return ret;
> > +}
> > +
> > +static int cros_typec_altmode_exit(struct typec_altmode *alt)
> > +{
> > + struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
> > + struct ec_params_typec_control req = {
> > + .port = data->port->port_num,
> > + .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
> > + };
> > + int svdm_version;
> > + int ret;
> > +
> > + if (!data->ap_mode_entry) {
> > + const struct typec_altmode *partner =
> > + typec_altmode_get_partner(alt);
> > + dev_warn(&partner->dev,
> > + "EC does not support ap driven mode entry\n");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
> > + &req, sizeof(req), NULL, 0);
> > +
> > + if (ret < 0)
> > + return ret;
> > +
> > + svdm_version = typec_altmode_get_svdm_version(alt);
> > + if (svdm_version < 0)
> > + return svdm_version;
> > +
> > + data->header = VDO(data->sid, 1, svdm_version, CMD_EXIT_MODE);
> > + data->header |= VDO_OPOS(data->mode);
> > + data->header |= VDO_CMDT(CMDT_RSP_ACK);
> > +
> > + data->vdo_data = NULL;
> > + data->vdo_size = 1;
> > +
> > + schedule_work(&data->work);
> > +
> > + return ret;
> > +}
> > +
> > +static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
> > + const u32 *data, int count)
> > +{
> > + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
> > +
> > + int cmd_type = PD_VDO_CMDT(header);
> > + int cmd = PD_VDO_CMD(header);
> > + int svdm_version;
> > +
> > + if (!adata->ap_mode_entry) {
> > + const struct typec_altmode *partner =
> > + typec_altmode_get_partner(alt);
> > + dev_warn(&partner->dev,
> > + "EC does not support ap driven mode entry\n");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + svdm_version = typec_altmode_get_svdm_version(alt);
> > + if (svdm_version < 0)
> > + return svdm_version;
> > +
> > + switch (cmd_type) {
> > + case CMDT_INIT:
> > + if (PD_VDO_SVDM_VER(header) < svdm_version) {
> > + typec_partner_set_svdm_version(adata->port->partner,
> > + PD_VDO_SVDM_VER(header));
> > + svdm_version = PD_VDO_SVDM_VER(header);
> > + }
> > +
> > + adata->header = VDO(adata->sid, 1, svdm_version, cmd);
> > + adata->header |= VDO_OPOS(adata->mode);
> > +
> > + /*
> > + * DP_CMD_CONFIGURE: We can't actually do anything with the
> > + * provided VDO yet so just send back an ACK.
> > + *
> > + * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
> > + * DPStatus Acks.
> > + */
> > + switch (cmd) {
> > + case DP_CMD_CONFIGURE:
> > + adata->am_data.dp.data.conf = *data;
> > + adata->header |= VDO_CMDT(CMDT_RSP_ACK);
> > + adata->am_data.dp.configured = true;
> > + schedule_work(&adata->work);
> > + break;
> > + case DP_CMD_STATUS_UPDATE:
> > + adata->am_data.dp.pending_status_update = true;
> > + break;
> > + default:
> > + adata->header |= VDO_CMDT(CMDT_RSP_ACK);
> > + schedule_work(&adata->work);
> > + break;
> > + }
> > +
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
> > + const u32 *data, int count)
> > +{
> > + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
> > +
> > + if (adata->sid == USB_TYPEC_DP_SID)
> > + return cros_typec_displayport_vdm(alt, header, data, count);
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static const struct typec_altmode_ops cros_typec_altmode_ops = {
> > + .enter = cros_typec_altmode_enter,
> > + .exit = cros_typec_altmode_exit,
> > + .vdm = cros_typec_altmode_vdm,
> > +};
> > +
> > +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
> > +int cros_typec_displayport_status_update(struct typec_altmode *altmode,
> > + struct typec_displayport_data *data)
> > +{
> > + struct cros_typec_altmode_data *adata =
> > + typec_altmode_get_drvdata(altmode);
> > +
> > + if (!adata->am_data.dp.pending_status_update) {
> > + dev_dbg(&altmode->dev,
> > + "Got DPStatus without a pending request");
> > + return 0;
> > + }
> > +
> > + if (adata->am_data.dp.configured && adata->am_data.dp.data.conf != data->conf)
> > + dev_dbg(&altmode->dev,
> > + "DP Conf doesn't match. Requested 0x%04x, Actual 0x%04x",
> > + adata->am_data.dp.data.conf, data->conf);
> > +
> > + adata->am_data.dp.data = *data;
> > + adata->am_data.dp.pending_status_update = false;
> > + adata->header |= VDO_CMDT(CMDT_RSP_ACK);
> > + adata->vdo_data = &adata->am_data.dp.data.status;
> > + adata->vdo_size = 2;
> > +
> > + schedule_work(&adata->work);
> > + return 0;
> > +}
> > +
> > +struct typec_altmode *
> > +cros_typec_register_displayport(struct cros_typec_port *port,
> > + struct typec_altmode_desc *desc,
> > + bool ap_mode_entry)
> > +{
> > + struct typec_altmode *alt;
> > + struct cros_typec_altmode_data *data;
> > +
> > + alt = typec_port_register_altmode(port->port, desc);
> > + if (IS_ERR(alt))
> > + return alt;
> > +
> > + data = devm_kzalloc(&alt->dev, sizeof(*data), GFP_KERNEL);
> > + if (!data) {
> > + typec_unregister_altmode(alt);
> > + return ERR_PTR(-ENOMEM);
> > + }
> > +
> > + INIT_WORK(&data->work, cros_typec_altmode_work);
> > + data->alt = alt;
> > + data->port = port;
> > + data->ap_mode_entry = ap_mode_entry;
> > + data->sid = USB_TYPEC_DP_SID;
> > + data->mode = USB_TYPEC_DP_MODE;
> > +
> > + data->am_data.dp.configured = false;
> > + typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
> > + typec_altmode_set_drvdata(alt, data);
> > +
> > + return alt;
> > +}
> > +#endif
> > diff --git a/drivers/platform/chrome/cros_typec_altmode.h b/drivers/platform/chrome/cros_typec_altmode.h
> > new file mode 100644
> > index 000000000000..c6f8fb02c99c
> > --- /dev/null
> > +++ b/drivers/platform/chrome/cros_typec_altmode.h
> > @@ -0,0 +1,34 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +
> > +#ifndef __CROS_TYPEC_ALTMODE_H__
> > +#define __CROS_TYPEC_ALTMODE_H__
> > +
> > +struct cros_typec_port;
> > +struct typec_altmode;
> > +struct typec_altmode_desc;
> > +struct typec_displayport_data;
> > +
> > +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
> > +struct typec_altmode *
> > +cros_typec_register_displayport(struct cros_typec_port *port,
> > + struct typec_altmode_desc *desc,
> > + bool ap_mode_entry);
> > +
> > +int cros_typec_displayport_status_update(struct typec_altmode *altmode,
> > + struct typec_displayport_data *data);
> > +#else
> > +static inline struct typec_altmode *
> > +cros_typec_register_displayport(struct cros_typec_port *port,
> > + struct typec_altmode_desc *desc,
> > + bool ap_mode_entry)
> > +{
> > + return typec_port_register_altmode(port->port, desc);
> > +}
> > +
> > +static inline int cros_typec_displayport_status_update(struct typec_altmode *altmode,
> > + struct typec_displayport_data *data)
> > +{
> > + return 0;
> > +}
> > +#endif
> > +#endif /* __CROS_TYPEC_ALTMODE_H__ */
> > --
> > 2.47.0.163.g1226f6d8fa-goog
> >
>
> --
> With best wishes
> Dmitry
Hi Abhishek,
kernel test robot noticed the following build warnings:
[auto build test WARNING on chrome-platform/for-next]
[also build test WARNING on chrome-platform/for-firmware-next usb/usb-testing usb/usb-next usb/usb-linus westeri-thunderbolt/next linus/master v6.12-rc5 next-20241031]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Abhishek-Pandit-Subedi/usb-typec-Add-driver-for-Thunderbolt-3-Alternate-Mode/20241031-053304
base: https://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git for-next
patch link: https://lore.kernel.org/r/20241030142833.v2.5.I142fc0c09df58689b98f0cebf1c5e48b9d4fa800%40changeid
patch subject: [PATCH v2 5/7] platform/chrome: cros_ec_typec: Displayport support
config: arm-defconfig (https://download.01.org/0day-ci/archive/20241101/202411010134.yJIy0Z3P-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241101/202411010134.yJIy0Z3P-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411010134.yJIy0Z3P-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/platform/chrome/cros_typec_altmode.c:40:13: warning: unused function 'cros_typec_altmode_work' [-Wunused-function]
static void cros_typec_altmode_work(struct work_struct *work)
^
>> drivers/platform/chrome/cros_typec_altmode.c:212:39: warning: unused variable 'cros_typec_altmode_ops' [-Wunused-const-variable]
static const struct typec_altmode_ops cros_typec_altmode_ops = {
^
2 warnings generated.
vim +/cros_typec_altmode_ops +212 drivers/platform/chrome/cros_typec_altmode.c
39
> 40 static void cros_typec_altmode_work(struct work_struct *work)
41 {
42 struct cros_typec_altmode_data *data =
43 container_of(work, struct cros_typec_altmode_data, work);
44
45 if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
46 data->vdo_size))
47 dev_err(&data->alt->dev, "VDM 0x%x failed", data->header);
48
49 data->header = 0;
50 data->vdo_data = NULL;
51 data->vdo_size = 0;
52 }
53
54 static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
55 {
56 struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
57 struct ec_params_typec_control req = {
58 .port = data->port->port_num,
59 .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
60 };
61 int svdm_version;
62 int ret;
63
64 if (!data->ap_mode_entry) {
65 const struct typec_altmode *partner =
66 typec_altmode_get_partner(alt);
67 dev_warn(&partner->dev,
68 "EC does not support ap driven mode entry\n");
69 return -EOPNOTSUPP;
70 }
71
72 if (data->sid == USB_TYPEC_DP_SID)
73 req.mode_to_enter = CROS_EC_ALTMODE_DP;
74 else
75 return -EOPNOTSUPP;
76
77 ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
78 &req, sizeof(req), NULL, 0);
79 if (ret < 0)
80 return ret;
81
82 svdm_version = typec_altmode_get_svdm_version(alt);
83 if (svdm_version < 0)
84 return svdm_version;
85
86 data->header = VDO(data->sid, 1, svdm_version, CMD_ENTER_MODE);
87 data->header |= VDO_OPOS(data->mode);
88 data->header |= VDO_CMDT(CMDT_RSP_ACK);
89
90 data->vdo_data = NULL;
91 data->vdo_size = 1;
92
93 schedule_work(&data->work);
94
95 return ret;
96 }
97
98 static int cros_typec_altmode_exit(struct typec_altmode *alt)
99 {
100 struct cros_typec_altmode_data *data = typec_altmode_get_drvdata(alt);
101 struct ec_params_typec_control req = {
102 .port = data->port->port_num,
103 .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
104 };
105 int svdm_version;
106 int ret;
107
108 if (!data->ap_mode_entry) {
109 const struct typec_altmode *partner =
110 typec_altmode_get_partner(alt);
111 dev_warn(&partner->dev,
112 "EC does not support ap driven mode entry\n");
113 return -EOPNOTSUPP;
114 }
115
116 ret = cros_ec_cmd(data->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
117 &req, sizeof(req), NULL, 0);
118
119 if (ret < 0)
120 return ret;
121
122 svdm_version = typec_altmode_get_svdm_version(alt);
123 if (svdm_version < 0)
124 return svdm_version;
125
126 data->header = VDO(data->sid, 1, svdm_version, CMD_EXIT_MODE);
127 data->header |= VDO_OPOS(data->mode);
128 data->header |= VDO_CMDT(CMDT_RSP_ACK);
129
130 data->vdo_data = NULL;
131 data->vdo_size = 1;
132
133 schedule_work(&data->work);
134
135 return ret;
136 }
137
138 static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
139 const u32 *data, int count)
140 {
141 struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
142
143 int cmd_type = PD_VDO_CMDT(header);
144 int cmd = PD_VDO_CMD(header);
145 int svdm_version;
146
147 if (!adata->ap_mode_entry) {
148 const struct typec_altmode *partner =
149 typec_altmode_get_partner(alt);
150 dev_warn(&partner->dev,
151 "EC does not support ap driven mode entry\n");
152 return -EOPNOTSUPP;
153 }
154
155 svdm_version = typec_altmode_get_svdm_version(alt);
156 if (svdm_version < 0)
157 return svdm_version;
158
159 switch (cmd_type) {
160 case CMDT_INIT:
161 if (PD_VDO_SVDM_VER(header) < svdm_version) {
162 typec_partner_set_svdm_version(adata->port->partner,
163 PD_VDO_SVDM_VER(header));
164 svdm_version = PD_VDO_SVDM_VER(header);
165 }
166
167 adata->header = VDO(adata->sid, 1, svdm_version, cmd);
168 adata->header |= VDO_OPOS(adata->mode);
169
170 /*
171 * DP_CMD_CONFIGURE: We can't actually do anything with the
172 * provided VDO yet so just send back an ACK.
173 *
174 * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
175 * DPStatus Acks.
176 */
177 switch (cmd) {
178 case DP_CMD_CONFIGURE:
179 adata->am_data.dp.data.conf = *data;
180 adata->header |= VDO_CMDT(CMDT_RSP_ACK);
181 adata->am_data.dp.configured = true;
182 schedule_work(&adata->work);
183 break;
184 case DP_CMD_STATUS_UPDATE:
185 adata->am_data.dp.pending_status_update = true;
186 break;
187 default:
188 adata->header |= VDO_CMDT(CMDT_RSP_ACK);
189 schedule_work(&adata->work);
190 break;
191 }
192
193 break;
194 default:
195 break;
196 }
197
198 return 0;
199 }
200
201 static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
202 const u32 *data, int count)
203 {
204 struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
205
206 if (adata->sid == USB_TYPEC_DP_SID)
207 return cros_typec_displayport_vdm(alt, header, data, count);
208
209 return -EINVAL;
210 }
211
> 212 static const struct typec_altmode_ops cros_typec_altmode_ops = {
213 .enter = cros_typec_altmode_enter,
214 .exit = cros_typec_altmode_exit,
215 .vdm = cros_typec_altmode_vdm,
216 };
217
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.