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");
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);
}
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;
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;
pr_debug("done\n");
+ dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
+
return 0;
probe_err:
#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"
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,
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)
#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)
u32 aux_native_rx;
};
-
#define DPCD_LINK_VOLTAGE_MAX 4
#define DPCD_LINK_PRE_EMPHASIS_MAX 4
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;
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;
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;
+}