drivers/usb/typec/altmodes/displayport.c

Source file repositories/reference/linux-study-clean/drivers/usb/typec/altmodes/displayport.c

File Facts

System
Linux kernel
Corpus path
drivers/usb/typec/altmodes/displayport.c
Extension
.c
Size
20931 bytes
Lines
859
Domain
Driver Families
Bucket
drivers/usb
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct dp_altmode {
	struct typec_displayport_data data;
	struct typec_displayport_data data_prime;

	enum dp_state state;
	bool hpd;
	bool pending_hpd;
	u32 irq_hpd_count;
	/*
	 * hpd is mandatory for irq_hpd assertion, so irq_hpd also needs its own pending flag if
	 * both hpd and irq_hpd are asserted in the first Status Update before the pin assignment
	 * is configured.
	 */
	bool pending_irq_hpd;

	struct mutex lock; /* device lock */
	struct work_struct work;
	struct typec_altmode *alt;
	const struct typec_altmode *port;
	struct fwnode_handle *connector_fwnode;
	struct typec_altmode *plug_prime;
};

static int dp_altmode_notify(struct dp_altmode *dp)
{
	unsigned long conf;
	u8 state;

	if (dp->data.conf) {
		state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
		conf = TYPEC_MODAL_STATE(state);
	} else {
		conf = TYPEC_STATE_USB;
	}

	return typec_altmode_notify(dp->alt, conf, &dp->data);
}

static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
{
	u8 pin_assign = 0;
	u32 conf;
	u32 signal;

	/* DP Signalling */
	signal = DP_CAP_DP_SIGNALLING(dp->port->vdo) & DP_CAP_DP_SIGNALLING(dp->alt->vdo);
	if (dp->plug_prime)
		signal &= DP_CAP_DP_SIGNALLING(dp->plug_prime->vdo);

	conf = signal << DP_CONF_SIGNALLING_SHIFT;

	switch (con) {
	case DP_STATUS_CON_DISABLED:
		return 0;
	case DP_STATUS_CON_DFP_D:
		conf |= DP_CONF_UFP_U_AS_DFP_D;
		pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
			     DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
		/* Account for active cable capabilities */
		if (dp->plug_prime)
			pin_assign &= DP_CAP_DFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
		break;
	case DP_STATUS_CON_UFP_D:
	case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
		conf |= DP_CONF_UFP_U_AS_UFP_D;
		pin_assign = DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) &
				 DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo);
		/* Account for active cable capabilities */
		if (dp->plug_prime)
			pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
		break;
	default:
		break;
	}

	/* Determining the initial pin assignment. */
	if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
		/* Is USB together with DP preferred */
		if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
		    pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
			pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK;
		else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) {
			pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
			/* Default to pin assign C if available */
			if (pin_assign & BIT(DP_PIN_ASSIGN_C))
				pin_assign = BIT(DP_PIN_ASSIGN_C);
		}

		if (!pin_assign)
			return -EINVAL;

Annotation

Implementation Notes