drivers/usb/typec/ucsi/displayport.c

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

File Facts

System
Linux kernel
Corpus path
drivers/usb/typec/ucsi/displayport.c
Extension
.c
Size
8404 bytes
Lines
350
Domain
Driver Families
Bucket
drivers/usb
Inferred role
Driver Families: implementation source
Status
source 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 ucsi_dp {
	struct typec_displayport_data data;
	struct ucsi_connector *con;
	struct typec_altmode *alt;
	struct work_struct work;
	int offset;

	bool override;
	bool initialized;

	u32 header;
	u32 *vdo_data;
	u8 vdo_size;
};

/*
 * Note. Alternate mode control is optional feature in UCSI. It means that even
 * if the system supports alternate modes, the OS may not be aware of them.
 *
 * In most cases however, the OS will be able to see the supported alternate
 * modes, but it may still not be able to configure them, not even enter or exit
 * them. That is because UCSI defines alt mode details and alt mode "overriding"
 * as separate options.
 *
 * In case alt mode details are supported, but overriding is not, the driver
 * will still display the supported pin assignments and configuration, but any
 * changes the user attempts to do will lead into failure with return value of
 * -EOPNOTSUPP.
 */

static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
{
	struct ucsi_dp *dp = typec_altmode_get_drvdata(alt);
	struct ucsi *ucsi = dp->con->ucsi;
	int svdm_version;
	u64 command;
	u8 cur = 0;
	int ret;

	if (!ucsi_con_mutex_lock(dp->con))
		return -ENOTCONN;

	if (!dp->override && dp->initialized) {
		const struct typec_altmode *p = typec_altmode_get_partner(alt);

		dev_warn(&p->dev,
			 "firmware doesn't support alternate mode overriding\n");
		ret = -EOPNOTSUPP;
		goto err_unlock;
	}

	command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num);
	ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur));
	if (ret < 0) {
		if (ucsi->version > 0x0100)
			goto err_unlock;
		cur = 0xff;
	}

	if (cur != 0xff) {
		ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
		goto err_unlock;
	}

	/*
	 * We can't send the New CAM command yet to the PPM as it needs the
	 * configuration value as well. Pretending that we have now entered the
	 * mode, and letting the alt mode driver continue.
	 */

	svdm_version = typec_altmode_get_svdm_version(alt);
	if (svdm_version < 0) {
		ret = svdm_version;
		goto err_unlock;
	}

	dp->header = VDO(USB_TYPEC_DP_SID, 1, svdm_version, CMD_ENTER_MODE);
	dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE);
	dp->header |= VDO_CMDT(CMDT_RSP_ACK);

	dp->vdo_data = NULL;
	dp->vdo_size = 1;

	schedule_work(&dp->work);
	ret = 0;
err_unlock:
	ucsi_con_mutex_unlock(dp->con);

	return ret;
}

Annotation

Implementation Notes