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
23enum {
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
50enum dp_state {
51	DP_STATE_IDLE,
52	DP_STATE_ENTER,
53	DP_STATE_ENTER_PRIME,
54	DP_STATE_UPDATE,
55	DP_STATE_CONFIGURE,
56	DP_STATE_CONFIGURE_PRIME,
57	DP_STATE_EXIT,
58	DP_STATE_EXIT_PRIME,
59};
60
61struct dp_altmode {
62	struct typec_displayport_data data;
63	struct typec_displayport_data data_prime;
64
65	enum dp_state state;
66	bool hpd;
67	bool pending_hpd;
68
69	struct mutex lock; /* device lock */
70	struct work_struct work;
71	struct typec_altmode *alt;
72	const struct typec_altmode *port;
73	struct fwnode_handle *connector_fwnode;
74	struct typec_altmode *plug_prime;
75};
76
77static int dp_altmode_notify(struct dp_altmode *dp)
78{
79	unsigned long conf;
80	u8 state;
81
82	if (dp->data.conf) {
83		state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
84		conf = TYPEC_MODAL_STATE(state);
85	} else {
86		conf = TYPEC_STATE_USB;
87	}
88
89	return typec_altmode_notify(dp->alt, conf, &dp->data);
90}
91
92static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
93{
94	u8 pin_assign = 0;
95	u32 conf;
96
97	/* DP Signalling */
98	conf = (dp->data.conf & DP_CONF_SIGNALLING_MASK) >> DP_CONF_SIGNALLING_SHIFT;
99
100	switch (con) {
101	case DP_STATUS_CON_DISABLED:
102		return 0;
103	case DP_STATUS_CON_DFP_D:
104		conf |= DP_CONF_UFP_U_AS_DFP_D;
105		pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
106			     DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
107		/* Account for active cable capabilities */
108		if (dp->plug_prime)
109			pin_assign &= DP_CAP_DFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
110		break;
111	case DP_STATUS_CON_UFP_D:
112	case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
113		conf |= DP_CONF_UFP_U_AS_UFP_D;
114		pin_assign = DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) &
115				 DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo);
116		/* Account for active cable capabilities */
117		if (dp->plug_prime)
118			pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
119		break;
120	default:
121		break;
122	}
123
124	/* Determining the initial pin assignment. */
125	if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
126		/* Is USB together with DP preferred */
127		if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
128		    pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
129			pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK;
130		else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) {
131			pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
132			/* Default to pin assign C if available */
133			if (pin_assign & BIT(DP_PIN_ASSIGN_C))
134				pin_assign = BIT(DP_PIN_ASSIGN_C);
135		}
136
137		if (!pin_assign)
138			return -EINVAL;
139
140		conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign);
141	}
142
143	dp->data.conf = conf;
144	if (dp->plug_prime)
145		dp->data_prime.conf = conf;
146
147	return 0;
148}
149
150static int dp_altmode_status_update(struct dp_altmode *dp)
151{
152	bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
153	bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE);
154	u8 con = DP_STATUS_CONNECTION(dp->data.status);
155	int ret = 0;
156
157	if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) {
158		dp->data.conf = 0;
159		dp->data_prime.conf = 0;
160		dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME :
161					     DP_STATE_CONFIGURE;
162	} else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) {
163		dp->state = DP_STATE_EXIT;
164	} else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) {
165		ret = dp_altmode_configure(dp, con);
166		if (!ret) {
167			dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME :
168						     DP_STATE_CONFIGURE;
169			if (dp->hpd != hpd) {
170				dp->hpd = hpd;
171				dp->pending_hpd = true;
172			}
173		}
174	} else {
175		drm_connector_oob_hotplug_event(dp->connector_fwnode,
176						hpd ? connector_status_connected :
177						      connector_status_disconnected);
178		dp->hpd = hpd;
179		sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
180	}
181
182	return ret;
183}
184
185static int dp_altmode_configured(struct dp_altmode *dp)
186{
187	sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
188	sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment");
189	/*
190	 * If the DFP_D/UFP_D sends a change in HPD when first notifying the
191	 * DisplayPort driver that it is connected, then we wait until
192	 * configuration is complete to signal HPD.
193	 */
194	if (dp->pending_hpd) {
195		drm_connector_oob_hotplug_event(dp->connector_fwnode,
196						connector_status_connected);
197		sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
198		dp->pending_hpd = false;
199	}
200
201	return dp_altmode_notify(dp);
202}
203
204static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
205{
206	int svdm_version = typec_altmode_get_svdm_version(dp->alt);
207	u32 header;
208	int ret;
209
210	if (svdm_version < 0)
211		return svdm_version;
212
213	header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE);
214	ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data);
215	if (ret) {
216		dev_err(&dp->alt->dev,
217			"unable to put to connector to safe mode\n");
218		return ret;
219	}
220
221	ret = typec_altmode_vdm(dp->alt, header, &conf, 2);
222	if (ret)
223		dp_altmode_notify(dp);
224
225	return ret;
226}
227
228static int dp_altmode_configure_vdm_cable(struct dp_altmode *dp, u32 conf)
229{
230	int svdm_version = typec_altmode_get_cable_svdm_version(dp->plug_prime);
231	u32 header;
232
233	if (svdm_version < 0)
234		return svdm_version;
235
236	header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE);
237
238	return typec_cable_altmode_vdm(dp->plug_prime, TYPEC_PLUG_SOP_P, header, &conf, 2);
239}
240
241static void dp_altmode_work(struct work_struct *work)
242{
243	struct dp_altmode *dp = container_of(work, struct dp_altmode, work);
244	int svdm_version;
245	u32 header;
246	u32 vdo;
247	int ret;
248
249	mutex_lock(&dp->lock);
250
251	switch (dp->state) {
252	case DP_STATE_ENTER:
253		ret = typec_altmode_enter(dp->alt, NULL);
254		if (ret && ret != -EBUSY)
255			dev_err(&dp->alt->dev, "failed to enter mode\n");
256		break;
257	case DP_STATE_ENTER_PRIME:
258		ret = typec_cable_altmode_enter(dp->alt, TYPEC_PLUG_SOP_P, NULL);
259		/*
260		 * If we fail to enter Alt Mode on SOP', then we should drop the
261		 * plug from the driver and attempt to run the driver without
262		 * it.
263		 */
264		if (ret && ret != -EBUSY) {
265			dev_err(&dp->alt->dev, "plug failed to enter mode\n");
266			dp->state = DP_STATE_ENTER;
267			goto disable_prime;
268		}
269		break;
270	case DP_STATE_UPDATE:
271		svdm_version = typec_altmode_get_svdm_version(dp->alt);
272		if (svdm_version < 0)
273			break;
274		header = DP_HEADER(dp, svdm_version, DP_CMD_STATUS_UPDATE);
275		vdo = 1;
276		ret = typec_altmode_vdm(dp->alt, header, &vdo, 2);
277		if (ret)
278			dev_err(&dp->alt->dev,
279				"unable to send Status Update command (%d)\n",
280				ret);
281		break;
282	case DP_STATE_CONFIGURE:
283		ret = dp_altmode_configure_vdm(dp, dp->data.conf);
284		if (ret)
285			dev_err(&dp->alt->dev,
286				"unable to send Configure command (%d)\n", ret);
287		break;
288	case DP_STATE_CONFIGURE_PRIME:
289		ret = dp_altmode_configure_vdm_cable(dp, dp->data_prime.conf);
290		if (ret) {
291			dev_err(&dp->plug_prime->dev,
292				"unable to send Configure command (%d)\n",
293				ret);
294			dp->state = DP_STATE_CONFIGURE;
295			goto disable_prime;
296		}
297		break;
298	case DP_STATE_EXIT:
299		if (typec_altmode_exit(dp->alt))
300			dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
301		break;
302	case DP_STATE_EXIT_PRIME:
303		if (typec_cable_altmode_exit(dp->plug_prime, TYPEC_PLUG_SOP_P))
304			dev_err(&dp->plug_prime->dev, "Exit Mode Failed!\n");
305		break;
306	default:
307		break;
308	}
309
310	dp->state = DP_STATE_IDLE;
311
312	mutex_unlock(&dp->lock);
313	return;
314
315disable_prime:
316	typec_altmode_put_plug(dp->plug_prime);
317	dp->plug_prime = NULL;
318	schedule_work(&dp->work);
319	mutex_unlock(&dp->lock);
320}
321
322static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo)
323{
324	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
325	u8 old_state;
326
327	mutex_lock(&dp->lock);
328
329	old_state = dp->state;
330	dp->data.status = vdo;
331
332	if (old_state != DP_STATE_IDLE)
333		dev_warn(&alt->dev, "ATTENTION while processing state %d\n",
334			 old_state);
335
336	if (dp_altmode_status_update(dp))
337		dev_warn(&alt->dev, "%s: status update failed\n", __func__);
338
339	if (dp_altmode_notify(dp))
340		dev_err(&alt->dev, "%s: notification failed\n", __func__);
341
342	if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE)
343		schedule_work(&dp->work);
344
345	mutex_unlock(&dp->lock);
346}
347
348static int dp_altmode_vdm(struct typec_altmode *alt,
349			  const u32 hdr, const u32 *vdo, int count)
350{
351	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
352	int cmd_type = PD_VDO_CMDT(hdr);
353	int cmd = PD_VDO_CMD(hdr);
354	int ret = 0;
355
356	mutex_lock(&dp->lock);
357
358	if (dp->state != DP_STATE_IDLE) {
359		ret = -EBUSY;
360		goto err_unlock;
361	}
362
363	switch (cmd_type) {
364	case CMDT_RSP_ACK:
365		switch (cmd) {
366		case CMD_ENTER_MODE:
367			typec_altmode_update_active(alt, true);
368			dp->state = DP_STATE_UPDATE;
369			break;
370		case CMD_EXIT_MODE:
371			typec_altmode_update_active(alt, false);
372			dp->data.status = 0;
373			dp->data.conf = 0;
374			if (dp->hpd) {
375				drm_connector_oob_hotplug_event(dp->connector_fwnode,
376								connector_status_disconnected);
377				dp->hpd = false;
378				sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
379			}
380			if (dp->plug_prime)
381				dp->state = DP_STATE_EXIT_PRIME;
382			break;
383		case DP_CMD_STATUS_UPDATE:
384			dp->data.status = *vdo;
385			ret = dp_altmode_status_update(dp);
386			break;
387		case DP_CMD_CONFIGURE:
388			ret = dp_altmode_configured(dp);
389			break;
390		default:
391			break;
392		}
393		break;
394	case CMDT_RSP_NAK:
395		switch (cmd) {
396		case DP_CMD_CONFIGURE:
397			dp->data.conf = 0;
398			ret = dp_altmode_configured(dp);
399			break;
400		default:
401			break;
402		}
403		break;
404	default:
405		break;
406	}
407
408	if (dp->state != DP_STATE_IDLE)
409		schedule_work(&dp->work);
410
411err_unlock:
412	mutex_unlock(&dp->lock);
413	return ret;
414}
415
416static int dp_cable_altmode_vdm(struct typec_altmode *alt, enum typec_plug_index sop,
417				const u32 hdr, const u32 *vdo, int count)
418{
419	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
420	int cmd_type = PD_VDO_CMDT(hdr);
421	int cmd = PD_VDO_CMD(hdr);
422	int ret = 0;
423
424	mutex_lock(&dp->lock);
425
426	if (dp->state != DP_STATE_IDLE) {
427		ret = -EBUSY;
428		goto err_unlock;
429	}
430
431	switch (cmd_type) {
432	case CMDT_RSP_ACK:
433		switch (cmd) {
434		case CMD_ENTER_MODE:
435			typec_altmode_update_active(dp->plug_prime, true);
436			dp->state = DP_STATE_ENTER;
437			break;
438		case CMD_EXIT_MODE:
439			dp->data_prime.status = 0;
440			dp->data_prime.conf = 0;
441			typec_altmode_update_active(dp->plug_prime, false);
442			break;
443		case DP_CMD_CONFIGURE:
444			dp->state = DP_STATE_CONFIGURE;
445			break;
446		default:
447			break;
448		}
449		break;
450	case CMDT_RSP_NAK:
451		switch (cmd) {
452		case DP_CMD_CONFIGURE:
453			dp->data_prime.conf = 0;
454			/* Attempt to configure on SOP, drop plug */
455			typec_altmode_put_plug(dp->plug_prime);
456			dp->plug_prime = NULL;
457			dp->state = DP_STATE_CONFIGURE;
458			break;
459		default:
460			break;
461		}
462		break;
463	default:
464		break;
465	}
466
467	if (dp->state != DP_STATE_IDLE)
468		schedule_work(&dp->work);
469
470err_unlock:
471	mutex_unlock(&dp->lock);
472	return ret;
473}
474
475static int dp_altmode_activate(struct typec_altmode *alt, int activate)
476{
477	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
478	int ret;
479
480	if (activate) {
481		if (dp->plug_prime) {
482			ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
483			if (ret < 0) {
484				typec_altmode_put_plug(dp->plug_prime);
485				dp->plug_prime = NULL;
486			} else {
487				return ret;
488			}
489		}
490		return typec_altmode_enter(alt, NULL);
491	} else {
492		return typec_altmode_exit(alt);
493	}
494}
495
496static const struct typec_altmode_ops dp_altmode_ops = {
497	.attention = dp_altmode_attention,
498	.vdm = dp_altmode_vdm,
499	.activate = dp_altmode_activate,
500};
501
502static const struct typec_cable_ops dp_cable_ops = {
503	.vdm = dp_cable_altmode_vdm,
504};
505
506static const char * const configurations[] = {
507	[DP_CONF_USB]	= "USB",
508	[DP_CONF_DFP_D]	= "source",
509	[DP_CONF_UFP_D]	= "sink",
510};
511
512static ssize_t
513configuration_store(struct device *dev, struct device_attribute *attr,
514		    const char *buf, size_t size)
515{
516	struct dp_altmode *dp = dev_get_drvdata(dev);
517	u32 conf;
518	u32 cap;
519	int con;
520	int ret = 0;
521
522	con = sysfs_match_string(configurations, buf);
523	if (con < 0)
524		return con;
525
526	mutex_lock(&dp->lock);
527
528	if (dp->state != DP_STATE_IDLE) {
529		ret = -EBUSY;
530		goto err_unlock;
531	}
532
533	cap = DP_CAP_CAPABILITY(dp->alt->vdo);
534
535	if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) ||
536	    (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) {
537		ret = -EINVAL;
538		goto err_unlock;
539	}
540
541	conf = dp->data.conf & ~DP_CONF_DUAL_D;
542	conf |= con;
543
544	if (dp->alt->active) {
545		ret = dp_altmode_configure_vdm(dp, conf);
546		if (ret)
547			goto err_unlock;
548	}
549
550	dp->data.conf = conf;
551
552err_unlock:
553	mutex_unlock(&dp->lock);
554
555	return ret ? ret : size;
556}
557
558static ssize_t configuration_show(struct device *dev,
559				  struct device_attribute *attr, char *buf)
560{
561	struct dp_altmode *dp = dev_get_drvdata(dev);
562	int len;
563	u8 cap;
564	u8 cur;
565	int i;
566
567	mutex_lock(&dp->lock);
568
569	cap = DP_CAP_CAPABILITY(dp->alt->vdo);
570	cur = DP_CONF_CURRENTLY(dp->data.conf);
571
572	len = sprintf(buf, "%s ", cur ? "USB" : "[USB]");
573
574	for (i = 1; i < ARRAY_SIZE(configurations); i++) {
575		if (i == cur)
576			len += sprintf(buf + len, "[%s] ", configurations[i]);
577		else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) ||
578			 (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D))
579			len += sprintf(buf + len, "%s ", configurations[i]);
580	}
581
582	mutex_unlock(&dp->lock);
583
584	buf[len - 1] = '\n';
585	return len;
586}
587static DEVICE_ATTR_RW(configuration);
588
589static const char * const pin_assignments[] = {
590	[DP_PIN_ASSIGN_A] = "A",
591	[DP_PIN_ASSIGN_B] = "B",
592	[DP_PIN_ASSIGN_C] = "C",
593	[DP_PIN_ASSIGN_D] = "D",
594	[DP_PIN_ASSIGN_E] = "E",
595	[DP_PIN_ASSIGN_F] = "F",
596};
597
598/*
599 * Helper function to extract a peripheral's currently supported
600 * Pin Assignments from its DisplayPort alternate mode state.
601 */
602static u8 get_current_pin_assignments(struct dp_altmode *dp)
603{
604	if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_UFP_U_AS_DFP_D)
605		return DP_CAP_PIN_ASSIGN_DFP_D(dp->alt->vdo);
606	else
607		return DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo);
608}
609
610static ssize_t
611pin_assignment_store(struct device *dev, struct device_attribute *attr,
612		     const char *buf, size_t size)
613{
614	struct dp_altmode *dp = dev_get_drvdata(dev);
615	u8 assignments;
616	u32 conf;
617	int ret;
618
619	ret = sysfs_match_string(pin_assignments, buf);
620	if (ret < 0)
621		return ret;
622
623	conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret));
624	ret = 0;
625
626	mutex_lock(&dp->lock);
627
628	if (conf & dp->data.conf)
629		goto out_unlock;
630
631	if (dp->state != DP_STATE_IDLE) {
632		ret = -EBUSY;
633		goto out_unlock;
634	}
635
636	assignments = get_current_pin_assignments(dp);
637
638	if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
639		ret = -EINVAL;
640		goto out_unlock;
641	}
642
643	conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK;
644
645	/* Only send Configure command if a configuration has been set */
646	if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) {
647		/* todo: send manual configure over SOP'*/
648		ret = dp_altmode_configure_vdm(dp, conf);
649		if (ret)
650			goto out_unlock;
651	}
652
653	dp->data.conf = conf;
654
655out_unlock:
656	mutex_unlock(&dp->lock);
657
658	return ret ? ret : size;
659}
660
661static ssize_t pin_assignment_show(struct device *dev,
662				   struct device_attribute *attr, char *buf)
663{
664	struct dp_altmode *dp = dev_get_drvdata(dev);
665	u8 assignments;
666	int len = 0;
667	u8 cur;
668	int i;
669
670	mutex_lock(&dp->lock);
671
672	cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
673
674	assignments = get_current_pin_assignments(dp);
675
676	for (i = 0; assignments; assignments >>= 1, i++) {
677		if (assignments & 1) {
678			if (i == cur)
679				len += sprintf(buf + len, "[%s] ",
680					       pin_assignments[i]);
681			else
682				len += sprintf(buf + len, "%s ",
683					       pin_assignments[i]);
684		}
685	}
686
687	mutex_unlock(&dp->lock);
688
689	/* get_current_pin_assignments can return 0 when no matching pin assignments are found */
690	if (len == 0)
691		len++;
692
693	buf[len - 1] = '\n';
694	return len;
695}
696static DEVICE_ATTR_RW(pin_assignment);
697
698static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
699{
700	struct dp_altmode *dp = dev_get_drvdata(dev);
701
702	return sysfs_emit(buf, "%d\n", dp->hpd);
703}
704static DEVICE_ATTR_RO(hpd);
705
706static struct attribute *displayport_attrs[] = {
707	&dev_attr_configuration.attr,
708	&dev_attr_pin_assignment.attr,
709	&dev_attr_hpd.attr,
710	NULL
711};
712
713static const struct attribute_group displayport_group = {
714	.name = "displayport",
715	.attrs = displayport_attrs,
716};
717
718static const struct attribute_group *displayport_groups[] = {
719	&displayport_group,
720	NULL,
721};
722
723int dp_altmode_probe(struct typec_altmode *alt)
724{
725	const struct typec_altmode *port = typec_altmode_get_partner(alt);
726	struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
727	struct fwnode_handle *fwnode;
728	struct dp_altmode *dp;
729
730	/* FIXME: Port can only be DFP_U. */
731
732	/* Make sure we have compatiple pin configurations */
733	if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
734	      DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
735	    !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
736	      DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
737		return -ENODEV;
738
739	dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
740	if (!dp)
741		return -ENOMEM;
742
743	INIT_WORK(&dp->work, dp_altmode_work);
744	mutex_init(&dp->lock);
745	dp->port = port;
746	dp->alt = alt;
747
748	alt->desc = "DisplayPort";
749	alt->ops = &dp_altmode_ops;
750
751	if (plug) {
752		plug->desc = "Displayport";
753		plug->cable_ops = &dp_cable_ops;
754	}
755
756	dp->plug_prime = plug;
757
758	fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
759	if (fwnode_property_present(fwnode, "displayport"))
760		dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
761	else
762		dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */
763	if (IS_ERR(dp->connector_fwnode))
764		dp->connector_fwnode = NULL;
765
766	typec_altmode_set_drvdata(alt, dp);
767	if (plug)
768		typec_altmode_set_drvdata(plug, dp);
769
770	dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
771	schedule_work(&dp->work);
772
773	return 0;
774}
775EXPORT_SYMBOL_GPL(dp_altmode_probe);
776
777void dp_altmode_remove(struct typec_altmode *alt)
778{
779	struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
780
781	cancel_work_sync(&dp->work);
782	typec_altmode_put_plug(dp->plug_prime);
783
784	if (dp->connector_fwnode) {
785		drm_connector_oob_hotplug_event(dp->connector_fwnode,
786						connector_status_disconnected);
787
788		fwnode_handle_put(dp->connector_fwnode);
789	}
790}
791EXPORT_SYMBOL_GPL(dp_altmode_remove);
792
793static const struct typec_device_id dp_typec_id[] = {
794	{ USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE },
795	{ },
796};
797MODULE_DEVICE_TABLE(typec, dp_typec_id);
798
799static struct typec_altmode_driver dp_altmode_driver = {
800	.id_table = dp_typec_id,
801	.probe = dp_altmode_probe,
802	.remove = dp_altmode_remove,
803	.driver = {
804		.name = "typec_displayport",
805		.owner = THIS_MODULE,
806		.dev_groups = displayport_groups,
807	},
808};
809module_typec_altmode_driver(dp_altmode_driver);
810
811MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
812MODULE_LICENSE("GPL v2");
813MODULE_DESCRIPTION("DisplayPort Alternate Mode");
814