OSDN Git Service

mdss: display-port: add support for USB type-C Alt mode
authorChandan Uddaraju <chandanu@codeaurora.org>
Tue, 19 Jul 2016 23:11:05 +0000 (16:11 -0700)
committerGerrit - the friendly Code Review server <code-review@localhost>
Fri, 29 Jul 2016 07:16:58 +0000 (00:16 -0700)
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: Chandan Uddaraju <chandanu@codeaurora.org>
drivers/video/fbdev/msm/mdss_dp.c
drivers/video/fbdev/msm/mdss_dp.h
drivers/video/fbdev/msm/mdss_dp_util.c
drivers/video/fbdev/msm/mdss_dp_util.h

index c4b2562..a8a0d4e 100644 (file)
@@ -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:
index 6bf4aae..335047f 100644 (file)
@@ -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;
index c1d2998..323a281 100644 (file)
@@ -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;
+}
index 7394ce1..5d5fca2 100644 (file)
@@ -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__ */