diff options
Diffstat (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c')
-rw-r--r-- | drivers/gpu/drm/drm_dp_mst_topology.c | 68 |
1 files changed, 60 insertions, 8 deletions
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 132581ca4ad8..b0487c9f018c 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c | |||
@@ -867,8 +867,16 @@ static void drm_dp_destroy_port(struct kref *kref) | |||
867 | port->vcpi.num_slots = 0; | 867 | port->vcpi.num_slots = 0; |
868 | 868 | ||
869 | kfree(port->cached_edid); | 869 | kfree(port->cached_edid); |
870 | if (port->connector) | 870 | |
871 | (*port->mgr->cbs->destroy_connector)(mgr, port->connector); | 871 | /* we can't destroy the connector here, as |
872 | we might be holding the mode_config.mutex | ||
873 | from an EDID retrieval */ | ||
874 | if (port->connector) { | ||
875 | mutex_lock(&mgr->destroy_connector_lock); | ||
876 | list_add(&port->connector->destroy_list, &mgr->destroy_connector_list); | ||
877 | mutex_unlock(&mgr->destroy_connector_lock); | ||
878 | schedule_work(&mgr->destroy_connector_work); | ||
879 | } | ||
872 | drm_dp_port_teardown_pdt(port, port->pdt); | 880 | drm_dp_port_teardown_pdt(port, port->pdt); |
873 | 881 | ||
874 | if (!port->input && port->vcpi.vcpi > 0) | 882 | if (!port->input && port->vcpi.vcpi > 0) |
@@ -1163,6 +1171,8 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_ | |||
1163 | struct drm_dp_mst_port *port; | 1171 | struct drm_dp_mst_port *port; |
1164 | int i; | 1172 | int i; |
1165 | /* find the port by iterating down */ | 1173 | /* find the port by iterating down */ |
1174 | |||
1175 | mutex_lock(&mgr->lock); | ||
1166 | mstb = mgr->mst_primary; | 1176 | mstb = mgr->mst_primary; |
1167 | 1177 | ||
1168 | for (i = 0; i < lct - 1; i++) { | 1178 | for (i = 0; i < lct - 1; i++) { |
@@ -1182,6 +1192,7 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_ | |||
1182 | } | 1192 | } |
1183 | } | 1193 | } |
1184 | kref_get(&mstb->kref); | 1194 | kref_get(&mstb->kref); |
1195 | mutex_unlock(&mgr->lock); | ||
1185 | return mstb; | 1196 | return mstb; |
1186 | } | 1197 | } |
1187 | 1198 | ||
@@ -1189,7 +1200,7 @@ static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *m | |||
1189 | struct drm_dp_mst_branch *mstb) | 1200 | struct drm_dp_mst_branch *mstb) |
1190 | { | 1201 | { |
1191 | struct drm_dp_mst_port *port; | 1202 | struct drm_dp_mst_port *port; |
1192 | 1203 | struct drm_dp_mst_branch *mstb_child; | |
1193 | if (!mstb->link_address_sent) { | 1204 | if (!mstb->link_address_sent) { |
1194 | drm_dp_send_link_address(mgr, mstb); | 1205 | drm_dp_send_link_address(mgr, mstb); |
1195 | mstb->link_address_sent = true; | 1206 | mstb->link_address_sent = true; |
@@ -1204,17 +1215,31 @@ static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *m | |||
1204 | if (!port->available_pbn) | 1215 | if (!port->available_pbn) |
1205 | drm_dp_send_enum_path_resources(mgr, mstb, port); | 1216 | drm_dp_send_enum_path_resources(mgr, mstb, port); |
1206 | 1217 | ||
1207 | if (port->mstb) | 1218 | if (port->mstb) { |
1208 | drm_dp_check_and_send_link_address(mgr, port->mstb); | 1219 | mstb_child = drm_dp_get_validated_mstb_ref(mgr, port->mstb); |
1220 | if (mstb_child) { | ||
1221 | drm_dp_check_and_send_link_address(mgr, mstb_child); | ||
1222 | drm_dp_put_mst_branch_device(mstb_child); | ||
1223 | } | ||
1224 | } | ||
1209 | } | 1225 | } |
1210 | } | 1226 | } |
1211 | 1227 | ||
1212 | static void drm_dp_mst_link_probe_work(struct work_struct *work) | 1228 | static void drm_dp_mst_link_probe_work(struct work_struct *work) |
1213 | { | 1229 | { |
1214 | struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); | 1230 | struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); |
1231 | struct drm_dp_mst_branch *mstb; | ||
1215 | 1232 | ||
1216 | drm_dp_check_and_send_link_address(mgr, mgr->mst_primary); | 1233 | mutex_lock(&mgr->lock); |
1217 | 1234 | mstb = mgr->mst_primary; | |
1235 | if (mstb) { | ||
1236 | kref_get(&mstb->kref); | ||
1237 | } | ||
1238 | mutex_unlock(&mgr->lock); | ||
1239 | if (mstb) { | ||
1240 | drm_dp_check_and_send_link_address(mgr, mstb); | ||
1241 | drm_dp_put_mst_branch_device(mstb); | ||
1242 | } | ||
1218 | } | 1243 | } |
1219 | 1244 | ||
1220 | static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, | 1245 | static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, |
@@ -1269,7 +1294,6 @@ retry: | |||
1269 | goto retry; | 1294 | goto retry; |
1270 | } | 1295 | } |
1271 | DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret); | 1296 | DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret); |
1272 | WARN(1, "fail\n"); | ||
1273 | 1297 | ||
1274 | return -EIO; | 1298 | return -EIO; |
1275 | } | 1299 | } |
@@ -2632,6 +2656,30 @@ static void drm_dp_tx_work(struct work_struct *work) | |||
2632 | mutex_unlock(&mgr->qlock); | 2656 | mutex_unlock(&mgr->qlock); |
2633 | } | 2657 | } |
2634 | 2658 | ||
2659 | static void drm_dp_destroy_connector_work(struct work_struct *work) | ||
2660 | { | ||
2661 | struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work); | ||
2662 | struct drm_connector *connector; | ||
2663 | |||
2664 | /* | ||
2665 | * Not a regular list traverse as we have to drop the destroy | ||
2666 | * connector lock before destroying the connector, to avoid AB->BA | ||
2667 | * ordering between this lock and the config mutex. | ||
2668 | */ | ||
2669 | for (;;) { | ||
2670 | mutex_lock(&mgr->destroy_connector_lock); | ||
2671 | connector = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_connector, destroy_list); | ||
2672 | if (!connector) { | ||
2673 | mutex_unlock(&mgr->destroy_connector_lock); | ||
2674 | break; | ||
2675 | } | ||
2676 | list_del(&connector->destroy_list); | ||
2677 | mutex_unlock(&mgr->destroy_connector_lock); | ||
2678 | |||
2679 | mgr->cbs->destroy_connector(mgr, connector); | ||
2680 | } | ||
2681 | } | ||
2682 | |||
2635 | /** | 2683 | /** |
2636 | * drm_dp_mst_topology_mgr_init - initialise a topology manager | 2684 | * drm_dp_mst_topology_mgr_init - initialise a topology manager |
2637 | * @mgr: manager struct to initialise | 2685 | * @mgr: manager struct to initialise |
@@ -2651,10 +2699,13 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, | |||
2651 | mutex_init(&mgr->lock); | 2699 | mutex_init(&mgr->lock); |
2652 | mutex_init(&mgr->qlock); | 2700 | mutex_init(&mgr->qlock); |
2653 | mutex_init(&mgr->payload_lock); | 2701 | mutex_init(&mgr->payload_lock); |
2702 | mutex_init(&mgr->destroy_connector_lock); | ||
2654 | INIT_LIST_HEAD(&mgr->tx_msg_upq); | 2703 | INIT_LIST_HEAD(&mgr->tx_msg_upq); |
2655 | INIT_LIST_HEAD(&mgr->tx_msg_downq); | 2704 | INIT_LIST_HEAD(&mgr->tx_msg_downq); |
2705 | INIT_LIST_HEAD(&mgr->destroy_connector_list); | ||
2656 | INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work); | 2706 | INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work); |
2657 | INIT_WORK(&mgr->tx_work, drm_dp_tx_work); | 2707 | INIT_WORK(&mgr->tx_work, drm_dp_tx_work); |
2708 | INIT_WORK(&mgr->destroy_connector_work, drm_dp_destroy_connector_work); | ||
2658 | init_waitqueue_head(&mgr->tx_waitq); | 2709 | init_waitqueue_head(&mgr->tx_waitq); |
2659 | mgr->dev = dev; | 2710 | mgr->dev = dev; |
2660 | mgr->aux = aux; | 2711 | mgr->aux = aux; |
@@ -2679,6 +2730,7 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init); | |||
2679 | */ | 2730 | */ |
2680 | void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) | 2731 | void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) |
2681 | { | 2732 | { |
2733 | flush_work(&mgr->destroy_connector_work); | ||
2682 | mutex_lock(&mgr->payload_lock); | 2734 | mutex_lock(&mgr->payload_lock); |
2683 | kfree(mgr->payloads); | 2735 | kfree(mgr->payloads); |
2684 | mgr->payloads = NULL; | 2736 | mgr->payloads = NULL; |