]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - drivers/gpu/drm/drm_dp_mst_topology.c
Merge tag 'drm-misc-next-2020-06-19' of git://anongit.freedesktop.org/drm/drm-misc...
[thirdparty/linux.git] / drivers / gpu / drm / drm_dp_mst_topology.c
index 1e26b89628f98c7d4e4b3bed44078c6c088165fb..b2f5a84b4cfb61e78d06759fdbd726d6ee910a94 100644 (file)
@@ -88,8 +88,8 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
 static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
                                 u8 *guid);
 
-static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
-static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port);
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port);
 static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
 
 #define DBG_PREFIX "[dp_mst]"
@@ -1178,12 +1178,38 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
                                    struct drm_dp_sideband_msg_tx *txmsg)
 {
        struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+       unsigned long wait_timeout = msecs_to_jiffies(4000);
+       unsigned long wait_expires = jiffies + wait_timeout;
        int ret;
 
-       ret = wait_event_timeout(mgr->tx_waitq,
-                                check_txmsg_state(mgr, txmsg),
-                                (4 * HZ));
-       mutex_lock(&mstb->mgr->qlock);
+       for (;;) {
+               /*
+                * If the driver provides a way for this, change to
+                * poll-waiting for the MST reply interrupt if we didn't receive
+                * it for 50 msec. This would cater for cases where the HPD
+                * pulse signal got lost somewhere, even though the sink raised
+                * the corresponding MST interrupt correctly. One example is the
+                * Club 3D CAC-1557 TypeC -> DP adapter which for some reason
+                * filters out short pulses with a duration less than ~540 usec.
+                *
+                * The poll period is 50 msec to avoid missing an interrupt
+                * after the sink has cleared it (after a 110msec timeout
+                * since it raised the interrupt).
+                */
+               ret = wait_event_timeout(mgr->tx_waitq,
+                                        check_txmsg_state(mgr, txmsg),
+                                        mgr->cbs->poll_hpd_irq ?
+                                               msecs_to_jiffies(50) :
+                                               wait_timeout);
+
+               if (ret || !mgr->cbs->poll_hpd_irq ||
+                   time_after(jiffies, wait_expires))
+                       break;
+
+               mgr->cbs->poll_hpd_irq(mgr);
+       }
+
+       mutex_lock(&mgr->qlock);
        if (ret > 0) {
                if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
                        ret = -EIO;
@@ -1197,7 +1223,8 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
 
                /* remove from q */
                if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
-                   txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND)
+                   txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
+                   txmsg->state == DRM_DP_SIDEBAND_TX_SENT)
                        list_del(&txmsg->next);
        }
 out:
@@ -1603,7 +1630,7 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
        mutex_lock(&mgr->delayed_destroy_lock);
        list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
        mutex_unlock(&mgr->delayed_destroy_lock);
-       schedule_work(&mgr->delayed_destroy_work);
+       queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
 }
 
 /**
@@ -1720,7 +1747,7 @@ static void drm_dp_destroy_port(struct kref *kref)
        mutex_lock(&mgr->delayed_destroy_lock);
        list_add(&port->next, &mgr->destroy_port_list);
        mutex_unlock(&mgr->delayed_destroy_lock);
-       schedule_work(&mgr->delayed_destroy_work);
+       queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
 }
 
 /**
@@ -1966,7 +1993,7 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
                        }
 
                        /* remove i2c over sideband */
-                       drm_dp_mst_unregister_i2c_bus(&port->aux);
+                       drm_dp_mst_unregister_i2c_bus(port);
                } else {
                        mutex_lock(&mgr->lock);
                        drm_dp_mst_topology_put_mstb(port->mstb);
@@ -1981,7 +2008,7 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
        if (port->pdt != DP_PEER_DEVICE_NONE) {
                if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
                        /* add i2c over sideband */
-                       ret = drm_dp_mst_register_i2c_bus(&port->aux);
+                       ret = drm_dp_mst_register_i2c_bus(port);
                } else {
                        lct = drm_dp_calculate_rad(port, rad);
                        mstb = drm_dp_add_mst_branch_device(lct, rad);
@@ -2894,8 +2921,9 @@ out:
        return ret < 0 ? ret : changed;
 }
 
-void drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
-                                       struct drm_dp_mst_branch *mstb)
+static void
+drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_mst_branch *mstb)
 {
        struct drm_dp_sideband_msg_tx *txmsg;
        int ret;
@@ -4641,12 +4669,13 @@ static void drm_dp_tx_work(struct work_struct *work)
 static inline void
 drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
 {
+       drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs);
+
        if (port->connector) {
                drm_connector_unregister(port->connector);
                drm_connector_put(port->connector);
        }
 
-       drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs);
        drm_dp_mst_put_port_malloc(port);
 }
 
@@ -5179,6 +5208,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
        INIT_LIST_HEAD(&mgr->destroy_port_list);
        INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
        INIT_LIST_HEAD(&mgr->up_req_list);
+
+       /*
+        * delayed_destroy_work will be queued on a dedicated WQ, so that any
+        * requeuing will be also flushed when deiniting the topology manager.
+        */
+       mgr->delayed_destroy_wq = alloc_ordered_workqueue("drm_dp_mst_wq", 0);
+       if (mgr->delayed_destroy_wq == NULL)
+               return -ENOMEM;
+
        INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
        INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
        INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
@@ -5223,7 +5261,11 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 {
        drm_dp_mst_topology_mgr_set_mst(mgr, false);
        flush_work(&mgr->work);
-       cancel_work_sync(&mgr->delayed_destroy_work);
+       /* The following will also drain any requeued work on the WQ. */
+       if (mgr->delayed_destroy_wq) {
+               destroy_workqueue(mgr->delayed_destroy_wq);
+               mgr->delayed_destroy_wq = NULL;
+       }
        mutex_lock(&mgr->payload_lock);
        kfree(mgr->payloads);
        mgr->payloads = NULL;
@@ -5346,22 +5388,26 @@ static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
 
 /**
  * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
- * @aux: DisplayPort AUX channel
+ * @port: The port to add the I2C bus on
  *
  * Returns 0 on success or a negative error code on failure.
  */
-static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port)
 {
+       struct drm_dp_aux *aux = &port->aux;
+       struct device *parent_dev = port->mgr->dev->dev;
+
        aux->ddc.algo = &drm_dp_mst_i2c_algo;
        aux->ddc.algo_data = aux;
        aux->ddc.retries = 3;
 
        aux->ddc.class = I2C_CLASS_DDC;
        aux->ddc.owner = THIS_MODULE;
-       aux->ddc.dev.parent = aux->dev;
-       aux->ddc.dev.of_node = aux->dev->of_node;
+       /* FIXME: set the kdev of the port's connector as parent */
+       aux->ddc.dev.parent = parent_dev;
+       aux->ddc.dev.of_node = parent_dev->of_node;
 
-       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(parent_dev),
                sizeof(aux->ddc.name));
 
        return i2c_add_adapter(&aux->ddc);
@@ -5369,11 +5415,11 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
 
 /**
  * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
- * @aux: DisplayPort AUX channel
+ * @port: The port to remove the I2C bus from
  */
-static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port)
 {
-       i2c_del_adapter(&aux->ddc);
+       i2c_del_adapter(&port->aux.ddc);
 }
 
 /**
@@ -5447,7 +5493,7 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
 {
        struct drm_dp_mst_port *immediate_upstream_port;
        struct drm_dp_mst_port *fec_port;
-       struct drm_dp_desc desc = { };
+       struct drm_dp_desc desc = {};
        u8 endpoint_fec;
        u8 endpoint_dsc;