drm/mst: cached EDID for logical ports (v2)
[deliverable/linux.git] / drivers / gpu / drm / drm_dp_mst_topology.c
index 070f913d2dba40eede7075f6e4b4b652e18dbdfa..0a9d3aad3cbae2d97be0d86f5f652513c5ad900c 100644 (file)
@@ -839,6 +839,8 @@ static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
 
 static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
 {
+       struct drm_dp_mst_branch *mstb;
+
        switch (old_pdt) {
        case DP_PEER_DEVICE_DP_LEGACY_CONV:
        case DP_PEER_DEVICE_SST_SINK:
@@ -846,8 +848,9 @@ static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
                drm_dp_mst_unregister_i2c_bus(&port->aux);
                break;
        case DP_PEER_DEVICE_MST_BRANCHING:
-               drm_dp_put_mst_branch_device(port->mstb);
+               mstb = port->mstb;
                port->mstb = NULL;
+               drm_dp_put_mst_branch_device(mstb);
                break;
        }
 }
@@ -858,6 +861,8 @@ static void drm_dp_destroy_port(struct kref *kref)
        struct drm_dp_mst_topology_mgr *mgr = port->mgr;
        if (!port->input) {
                port->vcpi.num_slots = 0;
+
+               kfree(port->cached_edid);
                if (port->connector)
                        (*port->mgr->cbs->destroy_connector)(mgr, port->connector);
                drm_dp_port_teardown_pdt(port, port->pdt);
@@ -1011,19 +1016,20 @@ static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb,
 
 static void build_mst_prop_path(struct drm_dp_mst_port *port,
                                struct drm_dp_mst_branch *mstb,
-                               char *proppath)
+                               char *proppath,
+                               size_t proppath_size)
 {
        int i;
        char temp[8];
-       snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id);
+       snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id);
        for (i = 0; i < (mstb->lct - 1); i++) {
                int shift = (i % 2) ? 0 : 4;
                int port_num = mstb->rad[i / 2] >> shift;
-               snprintf(temp, 8, "-%d", port_num);
-               strncat(proppath, temp, 255);
+               snprintf(temp, sizeof(temp), "-%d", port_num);
+               strlcat(proppath, temp, proppath_size);
        }
-       snprintf(temp, 8, "-%d", port->port_num);
-       strncat(proppath, temp, 255);
+       snprintf(temp, sizeof(temp), "-%d", port->port_num);
+       strlcat(proppath, temp, proppath_size);
 }
 
 static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
@@ -1094,8 +1100,12 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 
        if (created && !port->input) {
                char proppath[255];
-               build_mst_prop_path(port, mstb, proppath);
+               build_mst_prop_path(port, mstb, proppath, sizeof(proppath));
                port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);
+
+               if (port->port_num >= 8) {
+                       port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
+               }
        }
 
        /* put reference to this port */
@@ -1798,17 +1808,27 @@ static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
        return 0;
 }
 
-static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count)
+static bool drm_dp_get_vc_payload_bw(int dp_link_bw,
+                                    int dp_link_count,
+                                    int *out)
 {
        switch (dp_link_bw) {
+       default:
+               DRM_DEBUG_KMS("invalid link bandwidth in DPCD: %x (link count: %d)\n",
+                             dp_link_bw, dp_link_count);
+               return false;
+
        case DP_LINK_BW_1_62:
-               return 3 * dp_link_count;
+               *out = 3 * dp_link_count;
+               break;
        case DP_LINK_BW_2_7:
-               return 5 * dp_link_count;
+               *out = 5 * dp_link_count;
+               break;
        case DP_LINK_BW_5_4:
-               return 10 * dp_link_count;
+               *out = 10 * dp_link_count;
+               break;
        }
-       BUG();
+       return true;
 }
 
 /**
@@ -1840,7 +1860,13 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
                        goto out_unlock;
                }
 
-               mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK);
+               if (!drm_dp_get_vc_payload_bw(mgr->dpcd[1],
+                                             mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK,
+                                             &mgr->pbn_div)) {
+                       ret = -EINVAL;
+                       goto out_unlock;
+               }
+
                mgr->total_pbn = 2560;
                mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div);
                mgr->avail_slots = mgr->total_slots;
@@ -2150,7 +2176,8 @@ EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
  * This returns the current connection state for a port. It validates the
  * port pointer still exists so the caller doesn't require a reference
  */
-enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector,
+                                                struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
 {
        enum drm_connector_status status = connector_status_disconnected;
 
@@ -2169,6 +2196,10 @@ enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr
 
        case DP_PEER_DEVICE_SST_SINK:
                status = connector_status_connected;
+               /* for logical ports - cache the EDID */
+               if (port->port_num >= 8 && !port->cached_edid) {
+                       port->cached_edid = drm_get_edid(connector, &port->aux.ddc);
+               }
                break;
        case DP_PEER_DEVICE_DP_LEGACY_CONV:
                if (port->ldps)
@@ -2200,7 +2231,11 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
        if (!port)
                return NULL;
 
-       edid = drm_get_edid(connector, &port->aux.ddc);
+       if (port->cached_edid)
+               edid = drm_edid_duplicate(port->cached_edid);
+       else
+               edid = drm_get_edid(connector, &port->aux.ddc);
+
        drm_dp_put_port(port);
        return edid;
 }
This page took 0.070326 seconds and 5 git commands to generate.