Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 160151f7 authored by Chandan Uddaraju's avatar Chandan Uddaraju Committed by Gerrit - the friendly Code Review server
Browse files

mdss: display-port: add support for USB type-C Alt mode



This change is needed to support display port
over USB type-C port. Add usbpd changes to
enable display port using Alternate mode for
type-C port.

Add support to parse VDO messages and send dp_config
packets when HPD is high.

Change-Id: I3ea5d969a73e3418af23ba4d8b7374fb20d2889f
Signed-off-by: default avatarChandan Uddaraju <chandanu@codeaurora.org>
parent e400fce1
Loading
Loading
Loading
Loading
+199 −7
Original line number Diff line number Diff line
@@ -1186,7 +1186,7 @@ static void mdss_dp_event_work(struct work_struct *work)
	struct mdss_dp_drv_pdata *dp = NULL;
	struct delayed_work *dw = to_delayed_work(work);
	unsigned long flag;
	u32 todo = 0;
	u32 todo = 0, dp_config_pkt[2];

	if (!dw) {
		pr_err("invalid work structure\n");
@@ -1203,24 +1203,47 @@ static void mdss_dp_event_work(struct work_struct *work)
	pr_debug("todo=%x\n", todo);

	switch (todo) {
	case (EV_EDID_READ):
	case EV_EDID_READ:
		mdss_dp_edid_read(dp, 0);
		break;
	case (EV_DPCD_CAP_READ):
	case EV_DPCD_CAP_READ:
		mdss_dp_dpcd_cap_read(dp);
		break;
	case (EV_DPCD_STATUS_READ):
	case EV_DPCD_STATUS_READ:
		mdss_dp_dpcd_status_read(dp);
		break;
	case (EV_LINK_TRAIN):
	case EV_LINK_TRAIN:
		mdss_dp_do_link_train(dp);
		break;
	case (EV_VIDEO_READY):
	case EV_VIDEO_READY:
		mdss_dp_video_ready(dp);
		break;
	case (EV_IDLE_PATTERNS_SENT):
	case EV_IDLE_PATTERNS_SENT:
		mdss_dp_idle_patterns_sent(dp);
		break;
	case EV_USBPD_DISCOVER_MODES:
		usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_DISCOVER_MODES,
			SVDM_CMD_TYPE_INITIATOR, 0x0, 0x0, 0x0);
		break;
	case EV_USBPD_ENTER_MODE:
		usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_ENTER_MODE,
			SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
		break;
	case EV_USBPD_EXIT_MODE:
		usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_EXIT_MODE,
			SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
		break;
	case EV_USBPD_DP_STATUS:
		usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_STATUS,
			SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
		break;
	case EV_USBPD_DP_CONFIGURE:
		dp_config_pkt[0] = SVDM_HDR(USB_C_DP_SID, VDM_VERSION, 0x1,
			SVDM_CMD_TYPE_INITIATOR, DP_VDM_CONFIGURE);
		dp_config_pkt[1] = mdss_dp_usbpd_gen_config_pkt(dp);
		usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_CONFIGURE,
			SVDM_CMD_TYPE_INITIATOR, 0x1, dp_config_pkt, 0x2);
		break;
	default:
		pr_err("Unknown event:%d\n", todo);
	}
@@ -1308,6 +1331,165 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
	return 0;
}

static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr)
{
	struct mdss_dp_drv_pdata *dp_drv;

	dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
	if (!dp_drv->pd) {
		pr_err("get_usbpd phandle failed\n");
		return;
	}

	mutex_lock(&dp_drv->pd_msg_mutex);
	dp_drv->cable_connected = true;
	dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
	mutex_unlock(&dp_drv->pd_msg_mutex);
	pr_debug("discover_mode event sent\n");
}

static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
{
	struct mdss_dp_drv_pdata *dp_drv;

	dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
	if (!dp_drv->pd) {
		pr_err("get_usbpd phandle failed\n");
		return;
	}

	pr_debug("cable disconnected\n");
	mutex_lock(&dp_drv->pd_msg_mutex);
	dp_drv->cable_connected = false;
	mutex_unlock(&dp_drv->pd_msg_mutex);
}

static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
				enum usbpd_svdm_cmd_type cmd_type,
				const u32 *vdos, int num_vdos)
{
	struct mdss_dp_drv_pdata *dp_drv;

	dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
	if (!dp_drv->pd) {
		pr_err("get_usbpd phandle failed\n");
		return;
	}

	pr_debug("callback -> cmd: 0x%x, *vdos = 0x%x, num_vdos = %d\n",
				cmd, *vdos, num_vdos);

	switch (cmd) {
	case USBPD_SVDM_DISCOVER_MODES:
		if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
			dp_drv->alt_mode.dp_cap.response = *vdos;
			mdss_dp_usbpd_ext_capabilities
					(&dp_drv->alt_mode.dp_cap);
			dp_drv->alt_mode.current_state = DISCOVER_MODES_DONE;
			dp_send_events(dp_drv, EV_USBPD_ENTER_MODE);
		} else {
			pr_err("unknown response: %d for Discover_modes\n",
			       cmd_type);
		}
		break;
	case USBPD_SVDM_ENTER_MODE:
		if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
			dp_drv->alt_mode.current_state = ENTER_MODE_DONE;
			dp_send_events(dp_drv, EV_USBPD_DP_STATUS);
		} else {
			pr_err("unknown response: %d for Enter_mode\n",
			       cmd_type);
		}
		break;
	case USBPD_SVDM_ATTENTION:
		if (cmd_type == SVDM_CMD_TYPE_INITIATOR) {
			pr_debug("Attention. cmd_type=%d\n",
			       cmd_type);
			if (!dp_drv->alt_mode.current_state
						== ENTER_MODE_DONE) {
				pr_debug("sending discover_mode\n");
				dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
				break;
			}
			if (num_vdos == 1) {
				dp_drv->alt_mode.dp_status.response = *vdos;
				mdss_dp_usbpd_ext_dp_status
						(&dp_drv->alt_mode.dp_status);
				if (dp_drv->alt_mode.dp_status.hpd_high) {
					pr_debug("HPD high\n");
					dp_drv->alt_mode.current_state =
							DP_STATUS_DONE;
					dp_send_events
						(dp_drv, EV_USBPD_DP_CONFIGURE);
				}
			}
		} else {
			pr_debug("unknown response: %d for Attention\n",
			       cmd_type);
		}
		break;
	case DP_VDM_STATUS:
		if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
			dp_drv->alt_mode.dp_status.response = *vdos;
			mdss_dp_usbpd_ext_dp_status
					(&dp_drv->alt_mode.dp_status);
			if (dp_drv->alt_mode.dp_status.hpd_high) {
				pr_debug("HDP high\n");
				dp_drv->alt_mode.current_state =
						DP_STATUS_DONE;
				dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE);
			}
		} else {
			pr_err("unknown response: %d for DP_Status\n",
			       cmd_type);
		}
		break;
	case DP_VDM_CONFIGURE:
		if ((dp_drv->cable_connected == true)
				|| (cmd_type == SVDM_CMD_TYPE_RESP_ACK)) {
			dp_drv->alt_mode.current_state = DP_CONFIGURE_DONE;
			pr_debug("config USBPD to DP done\n");
			mdss_dp_host_init(&dp_drv->panel_data);
		} else {
			pr_err("unknown response: %d for DP_Configure\n",
			       cmd_type);
		}
		break;
	default:
		pr_err("unknown cmd: %d\n", cmd);
		break;
	}
}

static int mdss_dp_usbpd_setup(struct mdss_dp_drv_pdata *dp_drv)
{
	int ret = 0;
	const char *pd_phandle = "qcom,dp-usbpd-detection";

	dp_drv->pd = devm_usbpd_get_by_phandle(&dp_drv->pdev->dev,
						    pd_phandle);

	if (IS_ERR(dp_drv->pd)) {
		pr_err("get_usbpd phandle failed (%ld)\n",
				PTR_ERR(dp_drv->pd));
		return PTR_ERR(dp_drv->pd);
	}

	dp_drv->svid_handler.svid = USB_C_DP_SID;
	dp_drv->svid_handler.vdm_received = NULL;
	dp_drv->svid_handler.connect = &usbpd_connect_callback;
	dp_drv->svid_handler.svdm_received = &usbpd_response_callback;
	dp_drv->svid_handler.disconnect = &usbpd_disconnect_callback;

	ret = usbpd_register_svid(dp_drv->pd, &dp_drv->svid_handler);
	if (ret) {
		pr_err("usbpd registration failed\n");
		return -ENODEV;
	}

	return ret;
}

static int mdss_dp_probe(struct platform_device *pdev)
{
	int ret, i;
@@ -1351,8 +1533,16 @@ static int mdss_dp_probe(struct platform_device *pdev)
	dp_drv->mask1 = EDP_INTR_MASK1;
	dp_drv->mask2 = EDP_INTR_MASK2;
	mutex_init(&dp_drv->emutex);
	mutex_init(&dp_drv->pd_msg_mutex);
	spin_lock_init(&dp_drv->lock);

	if (mdss_dp_usbpd_setup(dp_drv)) {
		pr_err("Error usbpd setup!\n");
		devm_kfree(&pdev->dev, dp_drv);
		dp_drv = NULL;
		return -EPROBE_DEFER;
	}

	ret = mdss_retrieve_dp_ctrl_resources(pdev, dp_drv);
	if (ret)
		goto probe_err;
@@ -1424,6 +1614,8 @@ static int mdss_dp_probe(struct platform_device *pdev)

	pr_debug("done\n");

	dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);

	return 0;

probe_err:
+78 −3
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/usb/usbpd.h>

#include "mdss_hdmi_util.h"
#include "video/msm_hdmi_modes.h"
@@ -122,6 +123,10 @@ struct edp_buf {
	char i2c;	/* 1 == i2c cmd, 0 == native cmd */
};

/* USBPD-TypeC specific Macros */
#define VDM_VERSION		0x0
#define USB_C_DP_SID		0xFF01

enum dp_pm_type {
	DP_CORE_PM,
	DP_CTRL_PM,
@@ -129,6 +134,64 @@ enum dp_pm_type {
	DP_MAX_PM
};

#define PIN_ASSIGN_A		BIT(0)
#define PIN_ASSIGN_B		BIT(1)
#define PIN_ASSIGN_C		BIT(2)
#define PIN_ASSIGN_D		BIT(3)
#define PIN_ASSIGN_E		BIT(4)
#define PIN_ASSIGN_F		BIT(5)

#define SVDM_HDR(svid, ver, mode, cmd_type, cmd) \
	(((svid) << 16) | (1 << 15) | ((ver) <<  13) \
	| ((mode) << 8) | ((cmd_type) << 6) | (cmd))

/* DP specific VDM commands */
#define DP_VDM_STATUS		0x10
#define DP_VDM_CONFIGURE	0x11

enum dp_port_cap {
	PORT_NONE = 0,
	PORT_UFP_D,
	PORT_DFP_D,
	PORT_D_UFP_D,
};

struct usbpd_dp_capabilities {
	u32 response;
	enum dp_port_cap s_port;
	bool receptacle_state;
	u8 ulink_pin_config;
	u8 dlink_pin_config;
};

struct usbpd_dp_status {
	u32 response;
	enum dp_port_cap c_port;
	bool low_pow_st;
	bool adaptor_dp_en;
	bool multi_func;
	bool switch_to_usb_config;
	bool exit_dp_mode;
	bool hpd_high;
	bool hpd_irq;
};

enum dp_alt_mode_state {
	ALT_MODE_INIT_STATE = 0,
	DISCOVER_MODES_DONE,
	ENTER_MODE_DONE,
	DP_STATUS_DONE,
	DP_CONFIGURE_DONE,
	UNKNOWN_STATE,
};

struct dp_alt_mode {
	struct usbpd_dp_capabilities dp_cap;
	struct usbpd_dp_status dp_status;
	u32 usbpd_dp_config;
	enum dp_alt_mode_state current_state;
};

#define DPCD_ENHANCED_FRAME	BIT(0)
#define DPCD_TPS3	BIT(1)
#define DPCD_MAX_DOWNSPREAD_0_5	BIT(2)
@@ -141,8 +204,15 @@ enum dp_pm_type {
#define EV_DPCD_CAP_READ		BIT(2)
#define EV_DPCD_STATUS_READ		BIT(3)
#define EV_LINK_TRAIN			BIT(4)
#define EV_IDLE_PATTERNS_SENT		BIT(30)
#define EV_VIDEO_READY			BIT(31)
#define EV_IDLE_PATTERNS_SENT		BIT(5)
#define EV_VIDEO_READY			BIT(6)

#define EV_USBPD_DISCOVER_MODES		BIT(7)
#define EV_USBPD_ENTER_MODE		BIT(8)
#define EV_USBPD_DP_STATUS		BIT(9)
#define EV_USBPD_DP_CONFIGURE		BIT(10)
#define EV_USBPD_CC_PIN_POLARITY	BIT(11)
#define EV_USBPD_EXIT_MODE		BIT(12)

/* dp state ctrl */
#define ST_TRAIN_PATTERN_1		BIT(0)
@@ -253,7 +323,6 @@ struct dp_statistic {
	u32 aux_native_rx;
};


#define DPCD_LINK_VOLTAGE_MAX	4
#define DPCD_LINK_PRE_EMPHASIS_MAX	4

@@ -272,6 +341,10 @@ struct mdss_dp_drv_pdata {
	int (*off) (struct mdss_panel_data *pdata);
	struct platform_device *pdev;

	struct usbpd *pd;
	struct usbpd_svid_handler svid_handler;
	struct dp_alt_mode alt_mode;

	struct mutex emutex;
	int clk_cnt;
	int cont_splash;
@@ -327,6 +400,8 @@ struct mdss_dp_drv_pdata {
	struct completion video_comp;
	struct mutex aux_mutex;
	struct mutex train_mutex;
	struct mutex pd_msg_mutex;
	bool cable_connected;
	u32 aux_cmd_busy;
	u32 aux_cmd_i2c;
	int aux_trans_num;
+78 −0
Original line number Diff line number Diff line
@@ -282,3 +282,81 @@ void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv)

	dp_drv->mdss_util->disable_irq(&mdss_dp_hw);
}

static void mdss_dp_initialize_s_port(enum dp_port_cap *s_port, int port)
{
	switch (port) {
	case 0:
		*s_port = PORT_NONE;
		break;
	case 1:
		*s_port = PORT_UFP_D;
		break;
	case 2:
		*s_port = PORT_DFP_D;
		break;
	case 3:
		*s_port = PORT_D_UFP_D;
		break;
	default:
		*s_port = PORT_NONE;
	}
}

void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap)
{
	u32 buf = dp_cap->response;
	int port = buf & 0x3;

	dp_cap->receptacle_state =
			(buf & BIT(6)) ? true : false;

	dp_cap->dlink_pin_config =
			(buf >> 8) & 0xff;

	dp_cap->ulink_pin_config =
			(buf >> 16) & 0xff;

	mdss_dp_initialize_s_port(&dp_cap->s_port, port);
}

void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status)
{
	u32 buf = dp_status->response;
	int port = buf & 0x3;

	dp_status->low_pow_st =
			(buf & BIT(2)) ? true : false;

	dp_status->adaptor_dp_en =
			(buf & BIT(3)) ? true : false;

	dp_status->multi_func =
			(buf & BIT(4)) ? true : false;

	dp_status->switch_to_usb_config =
			(buf & BIT(5)) ? true : false;

	dp_status->exit_dp_mode =
			(buf & BIT(6)) ? true : false;

	dp_status->hpd_high =
			(buf & BIT(7)) ? true : false;

	dp_status->hpd_irq =
			(buf & BIT(8)) ? true : false;

	mdss_dp_initialize_s_port(&dp_status->c_port, port);
}

u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp)
{
	u32 config = 0;

	config |= (dp->alt_mode.dp_cap.dlink_pin_config << 8);
	config |= (0x1 << 2); /* configure for DPv1.3 */
	config |= 0x2; /* Configuring for UFP_D */

	pr_debug("DP config = 0x%x\n", config);
	return config;
}
+3 −0
Original line number Diff line number Diff line
@@ -114,5 +114,8 @@ int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io);
void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap);
void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status);
u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp);

#endif /* __DP_UTIL_H__ */