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 mutex_lock(&dp->con->lock); 58 59 if (!dp->override && dp->initialized) { 60 const struct typec_altmode *p = typec_altmode_get_partner(alt); 61 62 dev_warn(&p->dev, 63 "firmware doesn't support alternate mode overriding\n"); 64 ret = -EOPNOTSUPP; 65 goto err_unlock; 66 } 67 68 command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num); 69 ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); 70 if (ret < 0) { 71 if (ucsi->version > 0x0100) 72 goto err_unlock; 73 cur = 0xff; 74 } 75 76 if (cur != 0xff) { 77 ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; 78 goto err_unlock; 79 } 80 81 /* 82 * We can't send the New CAM command yet to the PPM as it needs the 83 * configuration value as well. Pretending that we have now entered the 84 * mode, and letting the alt mode driver continue. 85 */ 86 87 svdm_version = typec_altmode_get_svdm_version(alt); 88 if (svdm_version < 0) { 89 ret = svdm_version; 90 goto err_unlock; 91 } 92 93 dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, CMD_ENTER_MODE); 94 dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); 95 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 96 97 dp->vdo_data = NULL; 98 dp->vdo_size = 1; 99 100 schedule_work(&dp->work); 101 ret = 0; 102 err_unlock: 103 mutex_unlock(&dp->con->lock); 104 105 return ret; 106 } 107 108 static int ucsi_displayport_exit(struct typec_altmode *alt) 109 { 110 struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); 111 int svdm_version; 112 u64 command; 113 int ret = 0; 114 115 mutex_lock(&dp->con->lock); 116 117 if (!dp->override) { 118 const struct typec_altmode *p = typec_altmode_get_partner(alt); 119 120 dev_warn(&p->dev, 121 "firmware doesn't support alternate mode overriding\n"); 122 ret = -EOPNOTSUPP; 123 goto out_unlock; 124 } 125 126 command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); 127 ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0); 128 if (ret < 0) 129 goto out_unlock; 130 131 svdm_version = typec_altmode_get_svdm_version(alt); 132 if (svdm_version < 0) { 133 ret = svdm_version; 134 goto out_unlock; 135 } 136 137 dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, CMD_EXIT_MODE); 138 dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); 139 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 140 141 dp->vdo_data = NULL; 142 dp->vdo_size = 1; 143 144 schedule_work(&dp->work); 145 146 out_unlock: 147 mutex_unlock(&dp->con->lock); 148 149 return ret; 150 } 151 152 /* 153 * We do not actually have access to the Status Update VDO, so we have to guess 154 * things. 155 */ 156 static int ucsi_displayport_status_update(struct ucsi_dp *dp) 157 { 158 u32 cap = dp->alt->vdo; 159 160 dp->data.status = DP_STATUS_ENABLED; 161 162 /* 163 * If pin assignement D is supported, claiming always 164 * that Multi-function is preferred. 165 */ 166 if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { 167 dp->data.status |= DP_STATUS_CON_UFP_D; 168 169 if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) 170 dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; 171 } else { 172 dp->data.status |= DP_STATUS_CON_DFP_D; 173 174 if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) 175 dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; 176 } 177 178 dp->vdo_data = &dp->data.status; 179 dp->vdo_size = 2; 180 181 return 0; 182 } 183 184 static int ucsi_displayport_configure(struct ucsi_dp *dp) 185 { 186 u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); 187 u64 command; 188 189 if (!dp->override) 190 return 0; 191 192 command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); 193 194 return ucsi_send_command(dp->con->ucsi, command, NULL, 0); 195 } 196 197 static int ucsi_displayport_vdm(struct typec_altmode *alt, 198 u32 header, const u32 *data, int count) 199 { 200 struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); 201 int cmd_type = PD_VDO_CMDT(header); 202 int cmd = PD_VDO_CMD(header); 203 int svdm_version; 204 205 mutex_lock(&dp->con->lock); 206 207 if (!dp->override && dp->initialized) { 208 const struct typec_altmode *p = typec_altmode_get_partner(alt); 209 210 dev_warn(&p->dev, 211 "firmware doesn't support alternate mode overriding\n"); 212 mutex_unlock(&dp->con->lock); 213 return -EOPNOTSUPP; 214 } 215 216 svdm_version = typec_altmode_get_svdm_version(alt); 217 if (svdm_version < 0) { 218 mutex_unlock(&dp->con->lock); 219 return svdm_version; 220 } 221 222 switch (cmd_type) { 223 case CMDT_INIT: 224 if (PD_VDO_SVDM_VER(header) < svdm_version) { 225 typec_partner_set_svdm_version(dp->con->partner, PD_VDO_SVDM_VER(header)); 226 svdm_version = PD_VDO_SVDM_VER(header); 227 } 228 229 dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, cmd); 230 dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); 231 232 switch (cmd) { 233 case DP_CMD_STATUS_UPDATE: 234 if (ucsi_displayport_status_update(dp)) 235 dp->header |= VDO_CMDT(CMDT_RSP_NAK); 236 else 237 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 238 break; 239 case DP_CMD_CONFIGURE: 240 dp->data.conf = *data; 241 if (ucsi_displayport_configure(dp)) { 242 dp->header |= VDO_CMDT(CMDT_RSP_NAK); 243 } else { 244 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 245 if (dp->initialized) 246 ucsi_altmode_update_active(dp->con); 247 else 248 dp->initialized = true; 249 } 250 break; 251 default: 252 dp->header |= VDO_CMDT(CMDT_RSP_ACK); 253 break; 254 } 255 256 schedule_work(&dp->work); 257 break; 258 default: 259 break; 260 } 261 262 mutex_unlock(&dp->con->lock); 263 264 return 0; 265 } 266 267 static const struct typec_altmode_ops ucsi_displayport_ops = { 268 .enter = ucsi_displayport_enter, 269 .exit = ucsi_displayport_exit, 270 .vdm = ucsi_displayport_vdm, 271 }; 272 273 static void ucsi_displayport_work(struct work_struct *work) 274 { 275 struct ucsi_dp *dp = container_of(work, struct ucsi_dp, work); 276 int ret; 277 278 mutex_lock(&dp->con->lock); 279 280 ret = typec_altmode_vdm(dp->alt, dp->header, 281 dp->vdo_data, dp->vdo_size); 282 if (ret) 283 dev_err(&dp->alt->dev, "VDM 0x%x failed\n", dp->header); 284 285 dp->vdo_data = NULL; 286 dp->vdo_size = 0; 287 dp->header = 0; 288 289 mutex_unlock(&dp->con->lock); 290 } 291 292 void ucsi_displayport_remove_partner(struct typec_altmode *alt) 293 { 294 struct ucsi_dp *dp; 295 296 if (!alt) 297 return; 298 299 dp = typec_altmode_get_drvdata(alt); 300 if (!dp) 301 return; 302 303 dp->data.conf = 0; 304 dp->data.status = 0; 305 dp->initialized = false; 306 } 307 308 struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con, 309 bool override, int offset, 310 struct typec_altmode_desc *desc) 311 { 312 u8 all_assignments = BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D) | 313 BIT(DP_PIN_ASSIGN_E); 314 struct typec_altmode *alt; 315 struct ucsi_dp *dp; 316 317 /* We can't rely on the firmware with the capabilities. */ 318 desc->vdo |= DP_CAP_DP_SIGNALING | DP_CAP_RECEPTACLE; 319 320 /* Claiming that we support all pin assignments */ 321 desc->vdo |= all_assignments << 8; 322 desc->vdo |= all_assignments << 16; 323 324 alt = typec_port_register_altmode(con->port, desc); 325 if (IS_ERR(alt)) 326 return alt; 327 328 dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); 329 if (!dp) { 330 typec_unregister_altmode(alt); 331 return ERR_PTR(-ENOMEM); 332 } 333 334 INIT_WORK(&dp->work, ucsi_displayport_work); 335 dp->override = override; 336 dp->offset = offset; 337 dp->con = con; 338 dp->alt = alt; 339 340 alt->ops = &ucsi_displayport_ops; 341 typec_altmode_set_drvdata(alt, dp); 342 343 return alt; 344 } 345