Merge tag 'v4.3-rc4' into next
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_dp_mst_topology.c
index e23df5fd3836b1b70169a1ee125782c1e754b875..bf27a07dbce36993e7ed668ff567f5565a6e0995 100644 (file)
@@ -53,8 +53,8 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
                                  struct drm_dp_mst_port *port,
                                  int offset, int size, u8 *bytes);
 
-static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
-                                   struct drm_dp_mst_branch *mstb);
+static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                    struct drm_dp_mst_branch *mstb);
 static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
                                           struct drm_dp_mst_branch *mstb,
                                           struct drm_dp_mst_port *port);
@@ -804,8 +804,6 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
        struct drm_dp_mst_port *port, *tmp;
        bool wake_tx = false;
 
-       cancel_work_sync(&mstb->mgr->work);
-
        /*
         * destroy all ports - don't need lock
         * as there are no more references to the mst branch
@@ -863,29 +861,33 @@ static void drm_dp_destroy_port(struct kref *kref)
 {
        struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
        struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+
        if (!port->input) {
                port->vcpi.num_slots = 0;
 
                kfree(port->cached_edid);
 
-               /* we can't destroy the connector here, as
-                  we might be holding the mode_config.mutex
-                  from an EDID retrieval */
+               /*
+                * The only time we don't have a connector
+                * on an output port is if the connector init
+                * fails.
+                */
                if (port->connector) {
+                       /* we can't destroy the connector here, as
+                        * we might be holding the mode_config.mutex
+                        * from an EDID retrieval */
+
                        mutex_lock(&mgr->destroy_connector_lock);
                        list_add(&port->next, &mgr->destroy_connector_list);
                        mutex_unlock(&mgr->destroy_connector_lock);
                        schedule_work(&mgr->destroy_connector_work);
                        return;
                }
+               /* no need to clean up vcpi
+                * as if we have no connector we never setup a vcpi */
                drm_dp_port_teardown_pdt(port, port->pdt);
-
-               if (!port->input && port->vcpi.vcpi > 0)
-                       drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
        }
        kfree(port);
-
-       (*mgr->cbs->hotplug)(mgr);
 }
 
 static void drm_dp_put_port(struct drm_dp_mst_port *port)
@@ -1027,8 +1029,8 @@ 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,
+static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb,
+                               int pnum,
                                char *proppath,
                                size_t proppath_size)
 {
@@ -1041,7 +1043,7 @@ static void build_mst_prop_path(struct drm_dp_mst_port *port,
                snprintf(temp, sizeof(temp), "-%d", port_num);
                strlcat(proppath, temp, proppath_size);
        }
-       snprintf(temp, sizeof(temp), "-%d", port->port_num);
+       snprintf(temp, sizeof(temp), "-%d", pnum);
        strlcat(proppath, temp, proppath_size);
 }
 
@@ -1105,22 +1107,32 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
                drm_dp_port_teardown_pdt(port, old_pdt);
 
                ret = drm_dp_port_setup_pdt(port);
-               if (ret == true) {
+               if (ret == true)
                        drm_dp_send_link_address(mstb->mgr, port->mstb);
-                       port->mstb->link_address_sent = true;
-               }
        }
 
        if (created && !port->input) {
                char proppath[255];
-               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) {
+               build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
+               port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);
+               if (!port->connector) {
+                       /* remove it from the port list */
+                       mutex_lock(&mstb->mgr->lock);
+                       list_del(&port->next);
+                       mutex_unlock(&mstb->mgr->lock);
+                       /* drop port list reference */
+                       drm_dp_put_port(port);
+                       goto out;
+               }
+               if (port->port_num >= DP_MST_LOGICAL_PORT_0) {
                        port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
+                       drm_mode_connector_set_tile_property(port->connector);
                }
+               (*mstb->mgr->cbs->register_connector)(port->connector);
        }
 
+out:
        /* put reference to this port */
        drm_dp_put_port(port);
 }
@@ -1202,10 +1214,9 @@ static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *m
 {
        struct drm_dp_mst_port *port;
        struct drm_dp_mst_branch *mstb_child;
-       if (!mstb->link_address_sent) {
+       if (!mstb->link_address_sent)
                drm_dp_send_link_address(mgr, mstb);
-               mstb->link_address_sent = true;
-       }
+
        list_for_each_entry(port, &mstb->ports, next) {
                if (port->input)
                        continue;
@@ -1458,8 +1469,8 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
        mutex_unlock(&mgr->qlock);
 }
 
-static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
-                                   struct drm_dp_mst_branch *mstb)
+static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                    struct drm_dp_mst_branch *mstb)
 {
        int len;
        struct drm_dp_sideband_msg_tx *txmsg;
@@ -1467,11 +1478,12 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 
        txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
        if (!txmsg)
-               return -ENOMEM;
+               return;
 
        txmsg->dst = mstb;
        len = build_link_address(txmsg);
 
+       mstb->link_address_sent = true;
        drm_dp_queue_down_tx(mgr, txmsg);
 
        ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
@@ -1499,11 +1511,12 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
                        }
                        (*mgr->cbs->hotplug)(mgr);
                }
-       } else
+       } else {
+               mstb->link_address_sent = false;
                DRM_DEBUG_KMS("link address failed %d\n", ret);
+       }
 
        kfree(txmsg);
-       return 0;
 }
 
 static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
@@ -1978,6 +1991,8 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
        drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
                           DP_MST_EN | DP_UPSTREAM_IS_SRC);
        mutex_unlock(&mgr->lock);
+       flush_work(&mgr->work);
+       flush_work(&mgr->destroy_connector_work);
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
 
@@ -2263,10 +2278,10 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
 
        if (port->cached_edid)
                edid = drm_edid_duplicate(port->cached_edid);
-       else
+       else {
                edid = drm_get_edid(connector, &port->aux.ddc);
-
-       drm_mode_connector_set_tile_property(connector);
+               drm_mode_connector_set_tile_property(connector);
+       }
        drm_dp_put_port(port);
        return edid;
 }
@@ -2671,7 +2686,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
 {
        struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work);
        struct drm_dp_mst_port *port;
-
+       bool send_hotplug = false;
        /*
         * Not a regular list traverse as we have to drop the destroy
         * connector lock before destroying the connector, to avoid AB->BA
@@ -2694,7 +2709,10 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
                if (!port->input && port->vcpi.vcpi > 0)
                        drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
                kfree(port);
+               send_hotplug = true;
        }
+       if (send_hotplug)
+               (*mgr->cbs->hotplug)(mgr);
 }
 
 /**
@@ -2747,6 +2765,7 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
  */
 void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 {
+       flush_work(&mgr->work);
        flush_work(&mgr->destroy_connector_work);
        mutex_lock(&mgr->payload_lock);
        kfree(mgr->payloads);