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