1 // SPDX-License-Identifier: GPL-2.0
3 * USB Typec-C DisplayPort Alternate Mode driver
5 * Copyright (C) 2018 Intel Corporation
6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
8 * DisplayPort is trademark of VESA (www.vesa.org)
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"
20 #define DP_HEADER(_dp, ver, cmd) (VDO((_dp)->alt->svid, 1, ver, cmd) \
21 | VDO_OPOS(USB_TYPEC_DP_MODE))
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) | \
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) | \
40 /* DP only pin assignments */
41 #define DP_PIN_ASSIGN_DP_ONLY_MASK (BIT(DP_PIN_ASSIGN_A) | \
42 BIT(DP_PIN_ASSIGN_C) | \
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) | \
59 struct typec_displayport_data data
;
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
;
72 static int dp_altmode_notify(struct dp_altmode
*dp
)
78 state
= get_count_order(DP_CONF_GET_PIN_ASSIGN(dp
->data
.conf
));
79 conf
= TYPEC_MODAL_STATE(state
);
81 conf
= TYPEC_STATE_USB
;
84 return typec_altmode_notify(dp
->alt
, conf
, &dp
->data
);
87 static int dp_altmode_configure(struct dp_altmode
*dp
, u8 con
)
93 conf
= (dp
->data
.conf
& DP_CONF_SIGNALLING_MASK
) >> DP_CONF_SIGNALLING_SHIFT
;
96 case DP_STATUS_CON_DISABLED
:
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
);
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
);
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
);
129 conf
|= DP_CONF_SET_PIN_ASSIGN(pin_assign
);
132 dp
->data
.conf
= conf
;
137 static int dp_altmode_status_update(struct dp_altmode
*dp
)
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
);
144 if (configured
&& (dp
->data
.status
& DP_STATUS_SWITCH_TO_USB
)) {
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
);
152 dp
->state
= DP_STATE_CONFIGURE
;
153 if (dp
->hpd
!= hpd
) {
155 dp
->pending_hpd
= true;
159 drm_connector_oob_hotplug_event(dp
->connector_fwnode
,
160 hpd
? connector_status_connected
:
161 connector_status_disconnected
);
163 sysfs_notify(&dp
->alt
->dev
.kobj
, "displayport", "hpd");
169 static int dp_altmode_configured(struct dp_altmode
*dp
)
171 sysfs_notify(&dp
->alt
->dev
.kobj
, "displayport", "configuration");
172 sysfs_notify(&dp
->alt
->dev
.kobj
, "displayport", "pin_assignment");
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.
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;
185 return dp_altmode_notify(dp
);
188 static int dp_altmode_configure_vdm(struct dp_altmode
*dp
, u32 conf
)
190 int svdm_version
= typec_altmode_get_svdm_version(dp
->alt
);
194 if (svdm_version
< 0)
197 header
= DP_HEADER(dp
, svdm_version
, DP_CMD_CONFIGURE
);
198 ret
= typec_altmode_notify(dp
->alt
, TYPEC_STATE_SAFE
, &dp
->data
);
200 dev_err(&dp
->alt
->dev
,
201 "unable to put to connector to safe mode\n");
205 ret
= typec_altmode_vdm(dp
->alt
, header
, &conf
, 2);
207 dp_altmode_notify(dp
);
212 static void dp_altmode_work(struct work_struct
*work
)
214 struct dp_altmode
*dp
= container_of(work
, struct dp_altmode
, work
);
220 mutex_lock(&dp
->lock
);
224 ret
= typec_altmode_enter(dp
->alt
, NULL
);
225 if (ret
&& ret
!= -EBUSY
)
226 dev_err(&dp
->alt
->dev
, "failed to enter mode\n");
228 case DP_STATE_UPDATE
:
229 svdm_version
= typec_altmode_get_svdm_version(dp
->alt
);
230 if (svdm_version
< 0)
232 header
= DP_HEADER(dp
, svdm_version
, DP_CMD_STATUS_UPDATE
);
234 ret
= typec_altmode_vdm(dp
->alt
, header
, &vdo
, 2);
236 dev_err(&dp
->alt
->dev
,
237 "unable to send Status Update command (%d)\n",
240 case DP_STATE_CONFIGURE
:
241 ret
= dp_altmode_configure_vdm(dp
, dp
->data
.conf
);
243 dev_err(&dp
->alt
->dev
,
244 "unable to send Configure command (%d)\n", ret
);
247 if (typec_altmode_exit(dp
->alt
))
248 dev_err(&dp
->alt
->dev
, "Exit Mode Failed!\n");
254 dp
->state
= DP_STATE_IDLE
;
256 mutex_unlock(&dp
->lock
);
259 static void dp_altmode_attention(struct typec_altmode
*alt
, const u32 vdo
)
261 struct dp_altmode
*dp
= typec_altmode_get_drvdata(alt
);
264 mutex_lock(&dp
->lock
);
266 old_state
= dp
->state
;
267 dp
->data
.status
= vdo
;
269 if (old_state
!= DP_STATE_IDLE
)
270 dev_warn(&alt
->dev
, "ATTENTION while processing state %d\n",
273 if (dp_altmode_status_update(dp
))
274 dev_warn(&alt
->dev
, "%s: status update failed\n", __func__
);
276 if (dp_altmode_notify(dp
))
277 dev_err(&alt
->dev
, "%s: notification failed\n", __func__
);
279 if (old_state
== DP_STATE_IDLE
&& dp
->state
!= DP_STATE_IDLE
)
280 schedule_work(&dp
->work
);
282 mutex_unlock(&dp
->lock
);
285 static int dp_altmode_vdm(struct typec_altmode
*alt
,
286 const u32 hdr
, const u32
*vdo
, int count
)
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
);
293 mutex_lock(&dp
->lock
);
295 if (dp
->state
!= DP_STATE_IDLE
) {
304 typec_altmode_update_active(alt
, true);
305 dp
->state
= DP_STATE_UPDATE
;
308 typec_altmode_update_active(alt
, false);
312 drm_connector_oob_hotplug_event(dp
->connector_fwnode
,
313 connector_status_disconnected
);
315 sysfs_notify(&dp
->alt
->dev
.kobj
, "displayport", "hpd");
318 case DP_CMD_STATUS_UPDATE
:
319 dp
->data
.status
= *vdo
;
320 ret
= dp_altmode_status_update(dp
);
322 case DP_CMD_CONFIGURE
:
323 ret
= dp_altmode_configured(dp
);
331 case DP_CMD_CONFIGURE
:
333 ret
= dp_altmode_configured(dp
);
343 if (dp
->state
!= DP_STATE_IDLE
)
344 schedule_work(&dp
->work
);
347 mutex_unlock(&dp
->lock
);
351 static int dp_altmode_activate(struct typec_altmode
*alt
, int activate
)
353 return activate
? typec_altmode_enter(alt
, NULL
) :
354 typec_altmode_exit(alt
);
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
,
363 static const char * const configurations
[] = {
364 [DP_CONF_USB
] = "USB",
365 [DP_CONF_DFP_D
] = "source",
366 [DP_CONF_UFP_D
] = "sink",
370 configuration_store(struct device
*dev
, struct device_attribute
*attr
,
371 const char *buf
, size_t size
)
373 struct dp_altmode
*dp
= dev_get_drvdata(dev
);
379 con
= sysfs_match_string(configurations
, buf
);
383 mutex_lock(&dp
->lock
);
385 if (dp
->state
!= DP_STATE_IDLE
) {
390 cap
= DP_CAP_CAPABILITY(dp
->alt
->vdo
);
392 if ((con
== DP_CONF_DFP_D
&& !(cap
& DP_CAP_DFP_D
)) ||
393 (con
== DP_CONF_UFP_D
&& !(cap
& DP_CAP_UFP_D
))) {
398 conf
= dp
->data
.conf
& ~DP_CONF_DUAL_D
;
401 if (dp
->alt
->active
) {
402 ret
= dp_altmode_configure_vdm(dp
, conf
);
407 dp
->data
.conf
= conf
;
410 mutex_unlock(&dp
->lock
);
412 return ret
? ret
: size
;
415 static ssize_t
configuration_show(struct device
*dev
,
416 struct device_attribute
*attr
, char *buf
)
418 struct dp_altmode
*dp
= dev_get_drvdata(dev
);
424 mutex_lock(&dp
->lock
);
426 cap
= DP_CAP_CAPABILITY(dp
->alt
->vdo
);
427 cur
= DP_CONF_CURRENTLY(dp
->data
.conf
);
429 len
= sprintf(buf
, "%s ", cur
? "USB" : "[USB]");
431 for (i
= 1; i
< ARRAY_SIZE(configurations
); i
++) {
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
]);
439 mutex_unlock(&dp
->lock
);
444 static DEVICE_ATTR_RW(configuration
);
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",
456 * Helper function to extract a peripheral's currently supported
457 * Pin Assignments from its DisplayPort alternate mode state.
459 static u8
get_current_pin_assignments(struct dp_altmode
*dp
)
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
);
464 return DP_CAP_PIN_ASSIGN_UFP_D(dp
->alt
->vdo
);
468 pin_assignment_store(struct device
*dev
, struct device_attribute
*attr
,
469 const char *buf
, size_t size
)
471 struct dp_altmode
*dp
= dev_get_drvdata(dev
);
476 ret
= sysfs_match_string(pin_assignments
, buf
);
480 conf
= DP_CONF_SET_PIN_ASSIGN(BIT(ret
));
483 mutex_lock(&dp
->lock
);
485 if (conf
& dp
->data
.conf
)
488 if (dp
->state
!= DP_STATE_IDLE
) {
493 assignments
= get_current_pin_assignments(dp
);
495 if (!(DP_CONF_GET_PIN_ASSIGN(conf
) & assignments
)) {
500 conf
|= dp
->data
.conf
& ~DP_CONF_PIN_ASSIGNEMENT_MASK
;
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
);
509 dp
->data
.conf
= conf
;
512 mutex_unlock(&dp
->lock
);
514 return ret
? ret
: size
;
517 static ssize_t
pin_assignment_show(struct device
*dev
,
518 struct device_attribute
*attr
, char *buf
)
520 struct dp_altmode
*dp
= dev_get_drvdata(dev
);
526 mutex_lock(&dp
->lock
);
528 cur
= get_count_order(DP_CONF_GET_PIN_ASSIGN(dp
->data
.conf
));
530 assignments
= get_current_pin_assignments(dp
);
532 for (i
= 0; assignments
; assignments
>>= 1, i
++) {
533 if (assignments
& 1) {
535 len
+= sprintf(buf
+ len
, "[%s] ",
538 len
+= sprintf(buf
+ len
, "%s ",
543 mutex_unlock(&dp
->lock
);
545 /* get_current_pin_assignments can return 0 when no matching pin assignments are found */
552 static DEVICE_ATTR_RW(pin_assignment
);
554 static ssize_t
hpd_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
556 struct dp_altmode
*dp
= dev_get_drvdata(dev
);
558 return sysfs_emit(buf
, "%d\n", dp
->hpd
);
560 static DEVICE_ATTR_RO(hpd
);
562 static struct attribute
*displayport_attrs
[] = {
563 &dev_attr_configuration
.attr
,
564 &dev_attr_pin_assignment
.attr
,
569 static const struct attribute_group displayport_group
= {
570 .name
= "displayport",
571 .attrs
= displayport_attrs
,
574 static const struct attribute_group
*displayport_groups
[] = {
579 int dp_altmode_probe(struct typec_altmode
*alt
)
581 const struct typec_altmode
*port
= typec_altmode_get_partner(alt
);
582 struct fwnode_handle
*fwnode
;
583 struct dp_altmode
*dp
;
585 /* FIXME: Port can only be DFP_U. */
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
)))
594 dp
= devm_kzalloc(&alt
->dev
, sizeof(*dp
), GFP_KERNEL
);
598 INIT_WORK(&dp
->work
, dp_altmode_work
);
599 mutex_init(&dp
->lock
);
603 alt
->desc
= "DisplayPort";
604 alt
->ops
= &dp_altmode_ops
;
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);
610 dp
->connector_fwnode
= fwnode_handle_get(fwnode
); /* embedded DP */
611 if (IS_ERR(dp
->connector_fwnode
))
612 dp
->connector_fwnode
= NULL
;
614 typec_altmode_set_drvdata(alt
, dp
);
616 dp
->state
= DP_STATE_ENTER
;
617 schedule_work(&dp
->work
);
621 EXPORT_SYMBOL_GPL(dp_altmode_probe
);
623 void dp_altmode_remove(struct typec_altmode
*alt
)
625 struct dp_altmode
*dp
= typec_altmode_get_drvdata(alt
);
627 cancel_work_sync(&dp
->work
);
629 if (dp
->connector_fwnode
) {
630 drm_connector_oob_hotplug_event(dp
->connector_fwnode
,
631 connector_status_disconnected
);
633 fwnode_handle_put(dp
->connector_fwnode
);
636 EXPORT_SYMBOL_GPL(dp_altmode_remove
);
638 static const struct typec_device_id dp_typec_id
[] = {
639 { USB_TYPEC_DP_SID
, USB_TYPEC_DP_MODE
},
642 MODULE_DEVICE_TABLE(typec
, dp_typec_id
);
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
,
649 .name
= "typec_displayport",
650 .owner
= THIS_MODULE
,
651 .dev_groups
= displayport_groups
,
654 module_typec_altmode_driver(dp_altmode_driver
);
656 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
657 MODULE_LICENSE("GPL v2");
658 MODULE_DESCRIPTION("DisplayPort Alternate Mode");