1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * USB Typec-C DisplayPort Alternate Mode driver
4  *
5  * Copyright (C) 2018 Intel Corporation
6  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7  *
8  * DisplayPort is trademark of VESA (www.vesa.org)
9  */
10 
11 #include <linux/delay.h>
12 #include <linux/mutex.h>
13 #include <linux/module.h>
14 #include <linux/property.h>
15 #include <linux/usb/pd_vdo.h>
16 #include <linux/usb/typec_dp.h>
17 #include <drm/drm_connector.h>
18 #include "displayport.h"
19 
20 #define DP_HEADER(_dp, ver, cmd)	(VDO((_dp)->alt->svid, 1, ver, cmd)	\
21 					 | VDO_OPOS(USB_TYPEC_DP_MODE))
22 
23 enum {
24 	DP_CONF_USB,
25 	DP_CONF_DFP_D,
26 	DP_CONF_UFP_D,
27 	DP_CONF_DUAL_D,
28 };
29 
30 /* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */
31 #define DP_PIN_ASSIGN_GEN2_BR_MASK	(BIT(DP_PIN_ASSIGN_A) | \
32 					 BIT(DP_PIN_ASSIGN_B))
33 
34 /* Pin assignments that use DP v1.3 signaling to carry DP protocol */
35 #define DP_PIN_ASSIGN_DP_BR_MASK	(BIT(DP_PIN_ASSIGN_C) | \
36 					 BIT(DP_PIN_ASSIGN_D) | \
37 					 BIT(DP_PIN_ASSIGN_E) | \
38 					 BIT(DP_PIN_ASSIGN_F))
39 
40 /* DP only pin assignments */
41 #define DP_PIN_ASSIGN_DP_ONLY_MASK	(BIT(DP_PIN_ASSIGN_A) | \
42 					 BIT(DP_PIN_ASSIGN_C) | \
43 					 BIT(DP_PIN_ASSIGN_E))
44 
45 /* Pin assignments where one channel is for USB */
46 #define DP_PIN_ASSIGN_MULTI_FUNC_MASK	(BIT(DP_PIN_ASSIGN_B) | \
47 					 BIT(DP_PIN_ASSIGN_D) | \
48 					 BIT(DP_PIN_ASSIGN_F))
49 
50 enum dp_state {
51 	DP_STATE_IDLE,
52 	DP_STATE_ENTER,
53 	DP_STATE_UPDATE,
54 	DP_STATE_CONFIGURE,
55 	DP_STATE_EXIT,
56 };
57 
58 struct dp_altmode {
59 	struct typec_displayport_data data;
60 
61 	enum dp_state state;
62 	bool hpd;
63 	bool pending_hpd;
64 
65 	struct mutex lock; /* device lock */
66 	struct work_struct work;
67 	struct typec_altmode *alt;
68 	const struct typec_altmode *port;
69 	struct fwnode_handle *connector_fwnode;
70 };
71 
dp_altmode_notify(struct dp_altmode * dp)72 static int dp_altmode_notify(struct dp_altmode *dp)
73 {
74 	unsigned long conf;
75 	u8 state;
76 
77 	if (dp->data.conf) {
78 		state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
79 		conf = TYPEC_MODAL_STATE(state);
80 	} else {
81 		conf = TYPEC_STATE_USB;
82 	}
83 
84 	return typec_altmode_notify(dp->alt, conf, &dp->data);
85 }
86 
dp_altmode_configure(struct dp_altmode * dp,u8 con)87 static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
88 {
89 	u8 pin_assign = 0;
90 	u32 conf;
91 
92 	/* DP Signalling */
93 	conf = (dp->data.conf & DP_CONF_SIGNALLING_MASK) >> DP_CONF_SIGNALLING_SHIFT;
94 
95 	switch (con) {
96 	case DP_STATUS_CON_DISABLED:
97 		return 0;
98 	case DP_STATUS_CON_DFP_D:
99 		conf |= DP_CONF_UFP_U_AS_DFP_D;
100 		pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
101 			     DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
102 		break;
103 	case DP_STATUS_CON_UFP_D:
104 	case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
105 		conf |= DP_CONF_UFP_U_AS_UFP_D;
106 		pin_assign = DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) &
107 				 DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo);
108 		break;
109 	default:
110 		break;
111 	}
112 
113 	/* Determining the initial pin assignment. */
114 	if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
115 		/* Is USB together with DP preferred */
116 		if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
117 		    pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
118 			pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK;
119 		else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) {
120 			pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
121 			/* Default to pin assign C if available */
122 			if (pin_assign & BIT(DP_PIN_ASSIGN_C))
123 				pin_assign = BIT(DP_PIN_ASSIGN_C);
124 		}
125 
126 		if (!pin_assign)
127 			return -EINVAL;
128 
129 		conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign);
130 	}
131 
132 	dp->data.conf = conf;
133 
134 	return 0;
135 }
136 
dp_altmode_status_update(struct dp_altmode * dp)137 static int dp_altmode_status_update(struct dp_altmode *dp)
138 {
139 	bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
140 	bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE);
141 	u8 con = DP_STATUS_CONNECTION(dp->data.status);
142 	int ret = 0;
143 
144 	if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) {
145 		dp->data.conf = 0;
146 		dp->state = DP_STATE_CONFIGURE;
147 	} else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) {
148 		dp->state = DP_STATE_EXIT;
149 	} else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) {
150 		ret = dp_altmode_configure(dp, con);
151 		if (!ret) {
152 			dp->state = DP_STATE_CONFIGURE;
153 			if (dp->hpd != hpd) {
154 				dp->hpd = hpd;
155 				dp->pending_hpd = true;
156 			}
157 		}
158 	} else {
159 		drm_connector_oob_hotplug_event(dp->connector_fwnode,
160 						hpd ? connector_status_connected :
161 						      connector_status_disconnected);
162 		dp->hpd = hpd;
163 		sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
164 	}
165 
166 	return ret;
167 }
168 
dp_altmode_configured(struct dp_altmode * dp)169 static int dp_altmode_configured(struct dp_altmode *dp)
170 {
171 	sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
172 	sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment");
173 	/*
174 	 * If the DFP_D/UFP_D sends a change in HPD when first notifying the
175 	 * DisplayPort driver that it is connected, then we wait until
176 	 * configuration is complete to signal HPD.
177 	 */
178 	if (dp->pending_hpd) {
179 		drm_connector_oob_hotplug_event(dp->connector_fwnode,
180 						connector_status_connected);
181 		sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
182 		dp->pending_hpd = false;
183 	}
184 
185 	return dp_altmode_notify(dp);
186 }
187 
dp_altmode_configure_vdm(struct dp_altmode * dp,u32 conf)188 static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
189 {
190 	int svdm_version = typec_altmode_get_svdm_version(dp->alt);
191 	u32 header;
192 	int ret;
193 
194 	if (svdm_version < 0)
195 		return svdm_version;
196 
197 	header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE);
198 	ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data);
199 	if (ret) {
200 		dev_err(&dp->alt->dev,
201 			"unable to put to connector to safe mode\n");
202 		return ret;
203 	}
204 
205 	ret = typec_altmode_vdm(dp->alt, header, &conf, 2);
206 	if (ret)
207 		dp_altmode_notify(dp);
208 
209 	return ret;
210 }
211 
dp_altmode_work(struct work_struct * work)212 static void dp_altmode_work(struct work_struct *work)
213 {
214 	struct dp_altmode *dp = container_of(work, struct dp_altmode, work);
215 	int svdm_version;
216 	u32 header;
217 	u32 vdo;
218 	int ret;
219 
220 	mutex_lock(&dp->lock);
221 
222 	switch (dp->state) {
223 	case DP_STATE_ENTER:
224 		ret = typec_altmode_enter(dp->alt, NULL);
225 		if (ret && ret != -EBUSY)
226 			dev_err(&dp->alt->dev, "failed to enter mode\n");
227 		break;
228 	case DP_STATE_UPDATE:
229 		svdm_version = typec_altmode_get_svdm_version(dp->alt);
230 		if (svdm_version < 0)
231 			break;
232 		header = DP_HEADER(dp, svdm_version, DP_CMD_STATUS_UPDATE);
233 		vdo = 1;
234 		ret = typec_altmode_vdm(dp->alt, header, &vdo, 2);
235 		if (ret)
236 			dev_err(&dp->alt->dev,
237 				"unable to send Status Update command (%d)\n",
238 				ret);
239 		break;
240 	case DP_STATE_CONFIGURE:
241 		ret = dp_altmode_configure_vdm(dp, dp->data.conf);
242 		if (ret)
243 			dev_err(&dp->alt->dev,
244 				"unable to send Configure command (%d)\n", ret);
245 		break;
246 	case DP_STATE_EXIT:
247 		if (typec_altmode_exit(dp->alt))
248 			dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
249 		break;
250 	default:
251 		break;
252 	}
253 
254 	dp->state = DP_STATE_IDLE;
255 
256 	mutex_unlock(&dp->lock);
257 }
258 
dp_altmode_attention(struct typec_altmode * alt,const u32 vdo)259 static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo)
260 {
261 	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
262 	u8 old_state;
263 
264 	mutex_lock(&dp->lock);
265 
266 	old_state = dp->state;
267 	dp->data.status = vdo;
268 
269 	if (old_state != DP_STATE_IDLE)
270 		dev_warn(&alt->dev, "ATTENTION while processing state %d\n",
271 			 old_state);
272 
273 	if (dp_altmode_status_update(dp))
274 		dev_warn(&alt->dev, "%s: status update failed\n", __func__);
275 
276 	if (dp_altmode_notify(dp))
277 		dev_err(&alt->dev, "%s: notification failed\n", __func__);
278 
279 	if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE)
280 		schedule_work(&dp->work);
281 
282 	mutex_unlock(&dp->lock);
283 }
284 
dp_altmode_vdm(struct typec_altmode * alt,const u32 hdr,const u32 * vdo,int count)285 static int dp_altmode_vdm(struct typec_altmode *alt,
286 			  const u32 hdr, const u32 *vdo, int count)
287 {
288 	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
289 	int cmd_type = PD_VDO_CMDT(hdr);
290 	int cmd = PD_VDO_CMD(hdr);
291 	int ret = 0;
292 
293 	mutex_lock(&dp->lock);
294 
295 	if (dp->state != DP_STATE_IDLE) {
296 		ret = -EBUSY;
297 		goto err_unlock;
298 	}
299 
300 	switch (cmd_type) {
301 	case CMDT_RSP_ACK:
302 		switch (cmd) {
303 		case CMD_ENTER_MODE:
304 			typec_altmode_update_active(alt, true);
305 			dp->state = DP_STATE_UPDATE;
306 			break;
307 		case CMD_EXIT_MODE:
308 			typec_altmode_update_active(alt, false);
309 			dp->data.status = 0;
310 			dp->data.conf = 0;
311 			if (dp->hpd) {
312 				drm_connector_oob_hotplug_event(dp->connector_fwnode,
313 								connector_status_disconnected);
314 				dp->hpd = false;
315 				sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
316 			}
317 			break;
318 		case DP_CMD_STATUS_UPDATE:
319 			dp->data.status = *vdo;
320 			ret = dp_altmode_status_update(dp);
321 			break;
322 		case DP_CMD_CONFIGURE:
323 			ret = dp_altmode_configured(dp);
324 			break;
325 		default:
326 			break;
327 		}
328 		break;
329 	case CMDT_RSP_NAK:
330 		switch (cmd) {
331 		case DP_CMD_CONFIGURE:
332 			dp->data.conf = 0;
333 			ret = dp_altmode_configured(dp);
334 			break;
335 		default:
336 			break;
337 		}
338 		break;
339 	default:
340 		break;
341 	}
342 
343 	if (dp->state != DP_STATE_IDLE)
344 		schedule_work(&dp->work);
345 
346 err_unlock:
347 	mutex_unlock(&dp->lock);
348 	return ret;
349 }
350 
dp_altmode_activate(struct typec_altmode * alt,int activate)351 static int dp_altmode_activate(struct typec_altmode *alt, int activate)
352 {
353 	return activate ? typec_altmode_enter(alt, NULL) :
354 			  typec_altmode_exit(alt);
355 }
356 
357 static const struct typec_altmode_ops dp_altmode_ops = {
358 	.attention = dp_altmode_attention,
359 	.vdm = dp_altmode_vdm,
360 	.activate = dp_altmode_activate,
361 };
362 
363 static const char * const configurations[] = {
364 	[DP_CONF_USB]	= "USB",
365 	[DP_CONF_DFP_D]	= "source",
366 	[DP_CONF_UFP_D]	= "sink",
367 };
368 
369 static ssize_t
configuration_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)370 configuration_store(struct device *dev, struct device_attribute *attr,
371 		    const char *buf, size_t size)
372 {
373 	struct dp_altmode *dp = dev_get_drvdata(dev);
374 	u32 conf;
375 	u32 cap;
376 	int con;
377 	int ret = 0;
378 
379 	con = sysfs_match_string(configurations, buf);
380 	if (con < 0)
381 		return con;
382 
383 	mutex_lock(&dp->lock);
384 
385 	if (dp->state != DP_STATE_IDLE) {
386 		ret = -EBUSY;
387 		goto err_unlock;
388 	}
389 
390 	cap = DP_CAP_CAPABILITY(dp->alt->vdo);
391 
392 	if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) ||
393 	    (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) {
394 		ret = -EINVAL;
395 		goto err_unlock;
396 	}
397 
398 	conf = dp->data.conf & ~DP_CONF_DUAL_D;
399 	conf |= con;
400 
401 	if (dp->alt->active) {
402 		ret = dp_altmode_configure_vdm(dp, conf);
403 		if (ret)
404 			goto err_unlock;
405 	}
406 
407 	dp->data.conf = conf;
408 
409 err_unlock:
410 	mutex_unlock(&dp->lock);
411 
412 	return ret ? ret : size;
413 }
414 
configuration_show(struct device * dev,struct device_attribute * attr,char * buf)415 static ssize_t configuration_show(struct device *dev,
416 				  struct device_attribute *attr, char *buf)
417 {
418 	struct dp_altmode *dp = dev_get_drvdata(dev);
419 	int len;
420 	u8 cap;
421 	u8 cur;
422 	int i;
423 
424 	mutex_lock(&dp->lock);
425 
426 	cap = DP_CAP_CAPABILITY(dp->alt->vdo);
427 	cur = DP_CONF_CURRENTLY(dp->data.conf);
428 
429 	len = sprintf(buf, "%s ", cur ? "USB" : "[USB]");
430 
431 	for (i = 1; i < ARRAY_SIZE(configurations); i++) {
432 		if (i == cur)
433 			len += sprintf(buf + len, "[%s] ", configurations[i]);
434 		else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) ||
435 			 (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D))
436 			len += sprintf(buf + len, "%s ", configurations[i]);
437 	}
438 
439 	mutex_unlock(&dp->lock);
440 
441 	buf[len - 1] = '\n';
442 	return len;
443 }
444 static DEVICE_ATTR_RW(configuration);
445 
446 static const char * const pin_assignments[] = {
447 	[DP_PIN_ASSIGN_A] = "A",
448 	[DP_PIN_ASSIGN_B] = "B",
449 	[DP_PIN_ASSIGN_C] = "C",
450 	[DP_PIN_ASSIGN_D] = "D",
451 	[DP_PIN_ASSIGN_E] = "E",
452 	[DP_PIN_ASSIGN_F] = "F",
453 };
454 
455 /*
456  * Helper function to extract a peripheral's currently supported
457  * Pin Assignments from its DisplayPort alternate mode state.
458  */
get_current_pin_assignments(struct dp_altmode * dp)459 static u8 get_current_pin_assignments(struct dp_altmode *dp)
460 {
461 	if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_UFP_U_AS_DFP_D)
462 		return DP_CAP_PIN_ASSIGN_DFP_D(dp->alt->vdo);
463 	else
464 		return DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo);
465 }
466 
467 static ssize_t
pin_assignment_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)468 pin_assignment_store(struct device *dev, struct device_attribute *attr,
469 		     const char *buf, size_t size)
470 {
471 	struct dp_altmode *dp = dev_get_drvdata(dev);
472 	u8 assignments;
473 	u32 conf;
474 	int ret;
475 
476 	ret = sysfs_match_string(pin_assignments, buf);
477 	if (ret < 0)
478 		return ret;
479 
480 	conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret));
481 	ret = 0;
482 
483 	mutex_lock(&dp->lock);
484 
485 	if (conf & dp->data.conf)
486 		goto out_unlock;
487 
488 	if (dp->state != DP_STATE_IDLE) {
489 		ret = -EBUSY;
490 		goto out_unlock;
491 	}
492 
493 	assignments = get_current_pin_assignments(dp);
494 
495 	if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
496 		ret = -EINVAL;
497 		goto out_unlock;
498 	}
499 
500 	conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK;
501 
502 	/* Only send Configure command if a configuration has been set */
503 	if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) {
504 		ret = dp_altmode_configure_vdm(dp, conf);
505 		if (ret)
506 			goto out_unlock;
507 	}
508 
509 	dp->data.conf = conf;
510 
511 out_unlock:
512 	mutex_unlock(&dp->lock);
513 
514 	return ret ? ret : size;
515 }
516 
pin_assignment_show(struct device * dev,struct device_attribute * attr,char * buf)517 static ssize_t pin_assignment_show(struct device *dev,
518 				   struct device_attribute *attr, char *buf)
519 {
520 	struct dp_altmode *dp = dev_get_drvdata(dev);
521 	u8 assignments;
522 	int len = 0;
523 	u8 cur;
524 	int i;
525 
526 	mutex_lock(&dp->lock);
527 
528 	cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
529 
530 	assignments = get_current_pin_assignments(dp);
531 
532 	for (i = 0; assignments; assignments >>= 1, i++) {
533 		if (assignments & 1) {
534 			if (i == cur)
535 				len += sprintf(buf + len, "[%s] ",
536 					       pin_assignments[i]);
537 			else
538 				len += sprintf(buf + len, "%s ",
539 					       pin_assignments[i]);
540 		}
541 	}
542 
543 	mutex_unlock(&dp->lock);
544 
545 	/* get_current_pin_assignments can return 0 when no matching pin assignments are found */
546 	if (len == 0)
547 		len++;
548 
549 	buf[len - 1] = '\n';
550 	return len;
551 }
552 static DEVICE_ATTR_RW(pin_assignment);
553 
hpd_show(struct device * dev,struct device_attribute * attr,char * buf)554 static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
555 {
556 	struct dp_altmode *dp = dev_get_drvdata(dev);
557 
558 	return sysfs_emit(buf, "%d\n", dp->hpd);
559 }
560 static DEVICE_ATTR_RO(hpd);
561 
562 static struct attribute *displayport_attrs[] = {
563 	&dev_attr_configuration.attr,
564 	&dev_attr_pin_assignment.attr,
565 	&dev_attr_hpd.attr,
566 	NULL
567 };
568 
569 static const struct attribute_group displayport_group = {
570 	.name = "displayport",
571 	.attrs = displayport_attrs,
572 };
573 
574 static const struct attribute_group *displayport_groups[] = {
575 	&displayport_group,
576 	NULL,
577 };
578 
dp_altmode_probe(struct typec_altmode * alt)579 int dp_altmode_probe(struct typec_altmode *alt)
580 {
581 	const struct typec_altmode *port = typec_altmode_get_partner(alt);
582 	struct fwnode_handle *fwnode;
583 	struct dp_altmode *dp;
584 
585 	/* FIXME: Port can only be DFP_U. */
586 
587 	/* Make sure we have compatiple pin configurations */
588 	if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
589 	      DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
590 	    !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
591 	      DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
592 		return -ENODEV;
593 
594 	dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
595 	if (!dp)
596 		return -ENOMEM;
597 
598 	INIT_WORK(&dp->work, dp_altmode_work);
599 	mutex_init(&dp->lock);
600 	dp->port = port;
601 	dp->alt = alt;
602 
603 	alt->desc = "DisplayPort";
604 	alt->ops = &dp_altmode_ops;
605 
606 	fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
607 	if (fwnode_property_present(fwnode, "displayport"))
608 		dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
609 	else
610 		dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */
611 	if (IS_ERR(dp->connector_fwnode))
612 		dp->connector_fwnode = NULL;
613 
614 	typec_altmode_set_drvdata(alt, dp);
615 
616 	dp->state = DP_STATE_ENTER;
617 	schedule_work(&dp->work);
618 
619 	return 0;
620 }
621 EXPORT_SYMBOL_GPL(dp_altmode_probe);
622 
dp_altmode_remove(struct typec_altmode * alt)623 void dp_altmode_remove(struct typec_altmode *alt)
624 {
625 	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
626 
627 	cancel_work_sync(&dp->work);
628 
629 	if (dp->connector_fwnode) {
630 		drm_connector_oob_hotplug_event(dp->connector_fwnode,
631 						connector_status_disconnected);
632 
633 		fwnode_handle_put(dp->connector_fwnode);
634 	}
635 }
636 EXPORT_SYMBOL_GPL(dp_altmode_remove);
637 
638 static const struct typec_device_id dp_typec_id[] = {
639 	{ USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE },
640 	{ },
641 };
642 MODULE_DEVICE_TABLE(typec, dp_typec_id);
643 
644 static struct typec_altmode_driver dp_altmode_driver = {
645 	.id_table = dp_typec_id,
646 	.probe = dp_altmode_probe,
647 	.remove = dp_altmode_remove,
648 	.driver = {
649 		.name = "typec_displayport",
650 		.owner = THIS_MODULE,
651 		.dev_groups = displayport_groups,
652 	},
653 };
654 module_typec_altmode_driver(dp_altmode_driver);
655 
656 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
657 MODULE_LICENSE("GPL v2");
658 MODULE_DESCRIPTION("DisplayPort Alternate Mode");
659