File Index
Files and directories
Sign in
FILE

displayport.c

File preview with syntax highlighting.

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