1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * UCSI DisplayPort Alternate Mode Support 4 * 5 * Copyright (C) 2018, Intel Corporation 6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 */ 8 9 #include <linux/usb/typec_dp.h> 10 #include <linux/usb/pd_vdo.h> 11 12 #include "ucsi.h" 13 14 #define UCSI_CMD_SET_NEW_CAM(_con_num_, _enter_, _cam_, _am_) \ 15 (UCSI_SET_NEW_CAM | ((_con_num_) << 16) | ((_enter_) << 23) | \ 16 ((_cam_) << 24) | ((u64)(_am_) << 32)) 17 18 struct ucsi_dp { 19 struct typec_displayport_data data; 20 struct ucsi_connector *con; 21 struct typec_altmode *alt; 22 struct work_struct work; 23 int offset; 24 25 bool override; 26 bool initialized; 27 28 u32 header; 29 u32 *vdo_data; 30 u8 vdo_size; 31 }; 32 33 /* 34 * Note. Alternate mode control is optional feature in UCSI. It means that even 35 * if the system supports alternate modes, the OS may not be aware of them. 36 * 37 * In most cases however, the OS will be able to see the supported alternate 38 * modes, but it may still not be able to configure them, not even enter or exit 39 * them. That is because UCSI defines alt mode details and alt mode "overriding" 40 * as separate options. 41 * 42 * In case alt mode details are supported, but overriding is not, the driver 43 * will still display the supported pin assignments and configuration, but any 44 * changes the user attempts to do will lead into failure with return value of 45 * -EOPNOTSUPP. 46 */ 47 48 static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) 49 { 50 struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); 51 struct ucsi *ucsi = dp->con->ucsi; 52 int svdm_version; 53 u64 command; 54 u8 cur = 0; 55 int ret; 56 57 if (!ucsi_con_mutex_lock(dp->con)) 58 return -ENOTCONN; 59 60 if (!dp->override && dp->initialized) { 61 const struct typec_altmode *p = typec_altmode_get_partner(alt); 62 63 dev_warn(&p->dev, 64 "firmware doesn't support alternate mode overriding\n"); 65 ret = -EOPNOTSUPP; 66 goto err_unlock; 67 } 68 69 command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num); 70 ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); 71 if (ret < 0) { 72 if (ucsi->version > 0x0100) 73 goto err_unlock; 74 cur = 0xff; 75 } 76 77 if (cur != 0xff) { 78 ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; 79 goto err_unlock; 80 } 81 82 /* 83 * We can't send the New CAM command yet to the PPM as it needs the 84 * configuration value as well. Pretending that we have now entered the 85 * mode, and letting the alt mode driver continue. 86 */ 87 88 svdm_version = typec_altmode_get_svdm_version(alt); 89 if (svdm_version < 0) { 90 ret = svdm_version; 91 goto err_unlock; 92 } 93 94 dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, CMD_ENTER_MODE); 95 dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); 96 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 97 98 dp->vdo_data = NULL; 99 dp->vdo_size = 1; 100 101 schedule_work(&dp->work); 102 ret = 0; 103 err_unlock: 104 ucsi_con_mutex_unlock(dp->con); 105 106 return ret; 107 } 108 109 static int ucsi_displayport_exit(struct typec_altmode *alt) 110 { 111 struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); 112 int svdm_version; 113 u64 command; 114 int ret = 0; 115 116 if (!ucsi_con_mutex_lock(dp->con)) 117 return -ENOTCONN; 118 119 if (!dp->override) { 120 const struct typec_altmode *p = typec_altmode_get_partner(alt); 121 122 dev_warn(&p->dev, 123 "firmware doesn't support alternate mode overriding\n"); 124 ret = -EOPNOTSUPP; 125 goto out_unlock; 126 } 127 128 command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); 129 ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0); 130 if (ret < 0) 131 goto out_unlock; 132 133 svdm_version = typec_altmode_get_svdm_version(alt); 134 if (svdm_version < 0) { 135 ret = svdm_version; 136 goto out_unlock; 137 } 138 139 dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, CMD_EXIT_MODE); 140 dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); 141 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 142 143 dp->vdo_data = NULL; 144 dp->vdo_size = 1; 145 146 schedule_work(&dp->work); 147 148 out_unlock: 149 ucsi_con_mutex_unlock(dp->con); 150 151 return ret; 152 } 153 154 /* 155 * We do not actually have access to the Status Update VDO, so we have to guess 156 * things. 157 */ 158 static int ucsi_displayport_status_update(struct ucsi_dp *dp) 159 { 160 u32 cap = dp->alt->vdo; 161 162 dp->data.status = DP_STATUS_ENABLED; 163 164 /* 165 * If pin assignement D is supported, claiming always 166 * that Multi-function is preferred. 167 */ 168 if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { 169 dp->data.status |= DP_STATUS_CON_UFP_D; 170 171 if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) 172 dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; 173 } else { 174 dp->data.status |= DP_STATUS_CON_DFP_D; 175 176 if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) 177 dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; 178 } 179 180 dp->vdo_data = &dp->data.status; 181 dp->vdo_size = 2; 182 183 return 0; 184 } 185 186 static int ucsi_displayport_configure(struct ucsi_dp *dp) 187 { 188 u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); 189 u64 command; 190 191 if (!dp->override) 192 return 0; 193 194 command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); 195 196 return ucsi_send_command(dp->con->ucsi, command, NULL, 0); 197 } 198 199 static int ucsi_displayport_vdm(struct typec_altmode *alt, 200 u32 header, const u32 *data, int count) 201 { 202 struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); 203 int cmd_type = PD_VDO_CMDT(header); 204 int cmd = PD_VDO_CMD(header); 205 int svdm_version; 206 207 if (!ucsi_con_mutex_lock(dp->con)) 208 return -ENOTCONN; 209 210 if (!dp->override && dp->initialized) { 211 const struct typec_altmode *p = typec_altmode_get_partner(alt); 212 213 dev_warn(&p->dev, 214 "firmware doesn't support alternate mode overriding\n"); 215 ucsi_con_mutex_unlock(dp->con); 216 return -EOPNOTSUPP; 217 } 218 219 svdm_version = typec_altmode_get_svdm_version(alt); 220 if (svdm_version < 0) { 221 ucsi_con_mutex_unlock(dp->con); 222 return svdm_version; 223 } 224 225 switch (cmd_type) { 226 case CMDT_INIT: 227 if (PD_VDO_SVDM_VER(header) < svdm_version) { 228 typec_partner_set_svdm_version(dp->con->partner, PD_VDO_SVDM_VER(header)); 229 svdm_version = PD_VDO_SVDM_VER(header); 230 } 231 232 dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, cmd); 233 dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); 234 235 switch (cmd) { 236 case DP_CMD_STATUS_UPDATE: 237 if (ucsi_displayport_status_update(dp)) 238 dp->header |= VDO_CMDT(CMDT_RSP_NAK); 239 else 240 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 241 break; 242 case DP_CMD_CONFIGURE: 243 if (count < 2) { 244 dp->header |= VDO_CMDT(CMDT_RSP_NAK); 245 break; 246 } 247 dp->data.conf = *data; 248 if (ucsi_displayport_configure(dp)) { 249 dp->header |= VDO_CMDT(CMDT_RSP_NAK); 250 } else { 251 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 252 if (dp->initialized) 253 ucsi_altmode_update_active(dp->con); 254 else 255 dp->initialized = true; 256 } 257 break; 258 default: 259 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 260 break; 261 } 262 263 schedule_work(&dp->work); 264 break; 265 default: 266 break; 267 } 268 269 ucsi_con_mutex_unlock(dp->con); 270 271 return 0; 272 } 273 274 static const struct typec_altmode_ops ucsi_displayport_ops = { 275 .enter = ucsi_displayport_enter, 276 .exit = ucsi_displayport_exit, 277 .vdm = ucsi_displayport_vdm, 278 }; 279 280 static void ucsi_displayport_work(struct work_struct *work) 281 { 282 struct ucsi_dp *dp = container_of(work, struct ucsi_dp, work); 283 int ret; 284 285 ret = typec_altmode_vdm(dp->alt, dp->header, 286 dp->vdo_data, dp->vdo_size); 287 if (ret) 288 dev_err(&dp->alt->dev, "VDM 0x%x failed\n", dp->header); 289 290 dp->vdo_data = NULL; 291 dp->vdo_size = 0; 292 dp->header = 0; 293 } 294 295 void ucsi_displayport_remove_partner(struct typec_altmode *alt) 296 { 297 struct ucsi_dp *dp; 298 299 if (!alt) 300 return; 301 302 dp = typec_altmode_get_drvdata(alt); 303 if (!dp) 304 return; 305 306 cancel_work_sync(&dp->work); 307 308 dp->data.conf = 0; 309 dp->data.status = 0; 310 dp->initialized = false; 311 } 312 313 struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con, 314 bool override, int offset, 315 struct typec_altmode_desc *desc) 316 { 317 u8 all_assignments = BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D) | 318 BIT(DP_PIN_ASSIGN_E); 319 struct typec_altmode *alt; 320 struct ucsi_dp *dp; 321 322 /* We can't rely on the firmware with the capabilities. */ 323 desc->vdo |= DP_CAP_DP_SIGNALLING(0) | DP_CAP_RECEPTACLE; 324 325 /* Claiming that we support all pin assignments */ 326 desc->vdo |= all_assignments << 8; 327 desc->vdo |= all_assignments << 16; 328 329 alt = typec_port_register_altmode(con->port, desc); 330 if (IS_ERR(alt)) 331 return alt; 332 333 dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); 334 if (!dp) { 335 typec_unregister_altmode(alt); 336 return ERR_PTR(-ENOMEM); 337 } 338 339 INIT_WORK(&dp->work, ucsi_displayport_work); 340 dp->override = override; 341 dp->offset = offset; 342 dp->con = con; 343 dp->alt = alt; 344 345 typec_altmode_set_ops(alt, &ucsi_displayport_ops); 346 typec_altmode_set_drvdata(alt, dp); 347 348 return alt; 349 } 350