Message ID | 20241030142833.v2.5.I142fc0c09df58689b98f0cebf1c5e48b9d4fa800@changeid (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Thunderbolt and DP altmode support for cros-ec-typec | expand |
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
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 >
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
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
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__ */
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