UAPI Changes: None Cross-subsystem Changes: None Core Changes: Fixed regressions introduced by commit cd82d82cbc04 ("drm/dp_mst: Add branch bandwidth validation to MST atomic check"), which would cause us to: * Calculate the available bandwidth on an MST topology incorrectly, and as a result reject most display configurations that would try to enable more then one sink on a topology * Occasionally expose MST connectors to userspace before finishing probing their PBN capabilities, resulting in us rejecting display configurations because we assumed briefly that no bandwidth was available Driver Changes: None
Showing the changes to drivers/gpu (1 of 2 files). Show all
Unified · Split
| @@ -1935,7 +1935,7 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port, · 1934 unmodified lines | |||
| 1935 | return parent_lct + 1; | 1935 | return parent_lct + 1; |
| 1936 | } | 1936 | } |
| 1937 | 1937 | ||
| 1938 | static bool drm_dp_mst_is_dp_mst_end_device(u8 pdt, bool mcs) | 1938 | static bool drm_dp_mst_is_end_device(u8 pdt, bool mcs) |
| 1939 | { | 1939 | { |
| 1940 | switch (pdt) { | 1940 | switch (pdt) { |
| 1941 | case DP_PEER_DEVICE_DP_LEGACY_CONV: | 1941 | case DP_PEER_DEVICE_DP_LEGACY_CONV: |
| @@ -1965,13 +1965,13 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt, · 23 unmodified lines | |||
| 1965 | 1965 | ||
| 1966 | /* Teardown the old pdt, if there is one */ | 1966 | /* Teardown the old pdt, if there is one */ |
| 1967 | if (port->pdt != DP_PEER_DEVICE_NONE) { | 1967 | if (port->pdt != DP_PEER_DEVICE_NONE) { |
| 1968 | if (drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) { | 1968 | if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) { |
| 1969 | /* | 1969 | /* |
| 1970 | * If the new PDT would also have an i2c bus, | 1970 | * If the new PDT would also have an i2c bus, |
| 1971 | * don't bother with reregistering it | 1971 | * don't bother with reregistering it |
| 1972 | */ | 1972 | */ |
| 1973 | if (new_pdt != DP_PEER_DEVICE_NONE && | 1973 | if (new_pdt != DP_PEER_DEVICE_NONE && |
| 1974 | drm_dp_mst_is_dp_mst_end_device(new_pdt, new_mcs)) { | 1974 | drm_dp_mst_is_end_device(new_pdt, new_mcs)) { |
| 1975 | port->pdt = new_pdt; | 1975 | port->pdt = new_pdt; |
| 1976 | port->mcs = new_mcs; | 1976 | port->mcs = new_mcs; |
| 1977 | return 0; | 1977 | return 0; |
| @@ -1991,7 +1991,7 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt, · 13 unmodified lines | |||
| 1991 | port->mcs = new_mcs; | 1991 | port->mcs = new_mcs; |
| 1992 | 1992 | ||
| 1993 | if (port->pdt != DP_PEER_DEVICE_NONE) { | 1993 | if (port->pdt != DP_PEER_DEVICE_NONE) { |
| 1994 | if (drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) { | 1994 | if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) { |
| 1995 | /* add i2c over sideband */ | 1995 | /* add i2c over sideband */ |
| 1996 | ret = drm_dp_mst_register_i2c_bus(&port->aux); | 1996 | ret = drm_dp_mst_register_i2c_bus(&port->aux); |
| 1997 | } else { | 1997 | } else { |
| @@ -2172,7 +2172,7 @@ drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb, · 174 unmodified lines | |||
| 2172 | } | 2172 | } |
| 2173 | 2173 | ||
| 2174 | if (port->pdt != DP_PEER_DEVICE_NONE && | 2174 | if (port->pdt != DP_PEER_DEVICE_NONE && |
| 2175 | drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) { | 2175 | drm_dp_mst_is_end_device(port->pdt, port->mcs)) { |
| 2176 | port->cached_edid = drm_get_edid(port->connector, | 2176 | port->cached_edid = drm_get_edid(port->connector, |
| 2177 | &port->aux.ddc); | 2177 | &port->aux.ddc); |
| 2178 | drm_connector_set_tile_property(port->connector); | 2178 | drm_connector_set_tile_property(port->connector); |
| @@ -2302,14 +2302,18 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb, · 123 unmodified lines | |||
| 2302 | mutex_unlock(&mgr->lock); | 2302 | mutex_unlock(&mgr->lock); |
| 2303 | } | 2303 | } |
| 2304 | 2304 | ||
| 2305 | if (old_ddps != port->ddps) { | 2305 | /* |
| 2306 | if (port->ddps) { | 2306 | * Reprobe PBN caps on both hotplug, and when re-probing the link |
| 2307 | if (!port->input) { | 2307 | * for our parent mstb |
| 2308 | drm_dp_send_enum_path_resources(mgr, mstb, | 2308 | */ |
| 2309 | port); | 2309 | if (old_ddps != port->ddps || !created) { |
| 2310 | } | 2310 | if (port->ddps && !port->input) { |
| 2311 | ret = drm_dp_send_enum_path_resources(mgr, mstb, | ||
| 2312 | port); | ||
| 2313 | if (ret == 1) | ||
| 2314 | changed = true; | ||
| 2311 | } else { | 2315 | } else { |
| 2312 | port->available_pbn = 0; | 2316 | port->full_pbn = 0; |
| 2313 | } | 2317 | } |
| 2314 | } | 2318 | } |
| 2315 | 2319 | ||
| @@ -2401,11 +2405,10 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb, · 85 unmodified lines | |||
| 2401 | port->ddps = conn_stat->displayport_device_plug_status; | 2405 | port->ddps = conn_stat->displayport_device_plug_status; |
| 2402 | 2406 | ||
| 2403 | if (old_ddps != port->ddps) { | 2407 | if (old_ddps != port->ddps) { |
| 2404 | if (port->ddps) { | 2408 | if (port->ddps && !port->input) |
| 2405 | dowork = true; | 2409 | drm_dp_send_enum_path_resources(mgr, mstb, port); |
| 2406 | } else { | 2410 | else |
| 2407 | port->available_pbn = 0; | 2411 | port->full_pbn = 0; |
| 2408 | } | ||
| 2409 | } | 2412 | } |
| 2410 | 2413 | ||
| 2411 | new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type; | 2414 | new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type; |
| @@ -2556,13 +2559,6 @@ static int drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mg · 144 unmodified lines | |||
| 2556 | if (port->input || !port->ddps) | 2559 | if (port->input || !port->ddps) |
| 2557 | continue; | 2560 | continue; |
| 2558 | 2561 | ||
| 2559 | if (!port->available_pbn) { | ||
| 2560 | drm_modeset_lock(&mgr->base.lock, NULL); | ||
| 2561 | drm_dp_send_enum_path_resources(mgr, mstb, port); | ||
| 2562 | drm_modeset_unlock(&mgr->base.lock); | ||
| 2563 | changed = true; | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | if (port->mstb) | 2562 | if (port->mstb) |
| 2567 | mstb_child = drm_dp_mst_topology_get_mstb_validated( | 2563 | mstb_child = drm_dp_mst_topology_get_mstb_validated( |
| 2568 | mgr, port->mstb); | 2564 | mgr, port->mstb); |
| @@ -2990,6 +2986,7 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, · 421 unmodified lines | |||
| 2990 | 2986 | ||
| 2991 | ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); | 2987 | ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); |
| 2992 | if (ret > 0) { | 2988 | if (ret > 0) { |
| 2989 | ret = 0; | ||
| 2993 | path_res = &txmsg->reply.u.path_resources; | 2990 | path_res = &txmsg->reply.u.path_resources; |
| 2994 | 2991 | ||
| 2995 | if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) { | 2992 | if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) { |
| @@ -3002,14 +2999,22 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, · 6 unmodified lines | |||
| 3002 | path_res->port_number, | 2999 | path_res->port_number, |
| 3003 | path_res->full_payload_bw_number, | 3000 | path_res->full_payload_bw_number, |
| 3004 | path_res->avail_payload_bw_number); | 3001 | path_res->avail_payload_bw_number); |
| 3005 | port->available_pbn = | 3002 | |
| 3006 | path_res->avail_payload_bw_number; | 3003 | /* |
| 3004 | * If something changed, make sure we send a | ||
| 3005 | * hotplug | ||
| 3006 | */ | ||
| 3007 | if (port->full_pbn != path_res->full_payload_bw_number || | ||
| 3008 | port->fec_capable != path_res->fec_capable) | ||
| 3009 | ret = 1; | ||
| 3010 | |||
| 3011 | port->full_pbn = path_res->full_payload_bw_number; | ||
| 3007 | port->fec_capable = path_res->fec_capable; | 3012 | port->fec_capable = path_res->fec_capable; |
| 3008 | } | 3013 | } |
| 3009 | } | 3014 | } |
| 3010 | 3015 | ||
| 3011 | kfree(txmsg); | 3016 | kfree(txmsg); |
| 3012 | return 0; | 3017 | return ret; |
| 3013 | } | 3018 | } |
| 3014 | 3019 | ||
| 3015 | static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb) | 3020 | static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb) |
| @@ -3596,13 +3601,9 @@ drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb) · 580 unmodified lines | |||
| 3596 | /* The link address will need to be re-sent on resume */ | 3601 | /* The link address will need to be re-sent on resume */ |
| 3597 | mstb->link_address_sent = false; | 3602 | mstb->link_address_sent = false; |
| 3598 | 3603 | ||
| 3599 | list_for_each_entry(port, &mstb->ports, next) { | 3604 | list_for_each_entry(port, &mstb->ports, next) |
| 3600 | /* The PBN for each port will also need to be re-probed */ | ||
| 3601 | port->available_pbn = 0; | ||
| 3602 | |||
| 3603 | if (port->mstb) | 3605 | if (port->mstb) |
| 3604 | drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb); | 3606 | drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb); |
| 3605 | } | ||
| 3606 | } | 3607 | } |
| 3607 | 3608 | ||
| 3608 | /** | 3609 | /** |
| @@ -4829,41 +4830,102 @@ static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port, · 1220 unmodified lines | |||
| 4829 | return false; | 4830 | return false; |
| 4830 | } | 4831 | } |
| 4831 | 4832 | ||
| 4832 | static inline | 4833 | static int |
| 4833 | int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch, | 4834 | drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, |
| 4834 | struct drm_dp_mst_topology_state *mst_state) | 4835 | struct drm_dp_mst_topology_state *state); |
| 4836 | |||
| 4837 | static int | ||
| 4838 | drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb, | ||
| 4839 | struct drm_dp_mst_topology_state *state) | ||
| 4835 | { | 4840 | { |
| 4836 | struct drm_dp_mst_port *port; | ||
| 4837 | struct drm_dp_vcpi_allocation *vcpi; | 4841 | struct drm_dp_vcpi_allocation *vcpi; |
| 4838 | int pbn_limit = 0, pbn_used = 0; | 4842 | struct drm_dp_mst_port *port; |
| 4843 | int pbn_used = 0, ret; | ||
| 4844 | bool found = false; | ||
| 4839 | 4845 | ||
| 4840 | list_for_each_entry(port, &branch->ports, next) { | 4846 | /* Check that we have at least one port in our state that's downstream |
| 4841 | if (port->mstb) | 4847 | * of this branch, otherwise we can skip this branch |
| 4842 | if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state)) | 4848 | */ |
| 4843 | return -ENOSPC; | 4849 | list_for_each_entry(vcpi, &state->vcpis, next) { |
| 4850 | if (!vcpi->pbn || | ||
| 4851 | !drm_dp_mst_port_downstream_of_branch(vcpi->port, mstb)) | ||
| 4852 | continue; | ||
| 4844 | 4853 | ||
| 4845 | if (port->available_pbn > 0) | 4854 | found = true; |
| 4846 | pbn_limit = port->available_pbn; | 4855 | break; |
| 4847 | } | 4856 | } |
| 4848 | DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n", | 4857 | if (!found) |
| 4849 | branch, pbn_limit); | 4858 | return 0; |
| 4850 | 4859 | ||
| 4851 | list_for_each_entry(vcpi, &mst_state->vcpis, next) { | 4860 | if (mstb->port_parent) |
| 4852 | if (!vcpi->pbn) | 4861 | DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] Checking bandwidth limits on [MSTB:%p]\n", |
| 4853 | continue; | 4862 | mstb->port_parent->parent, mstb->port_parent, |
| 4863 | mstb); | ||
| 4864 | else | ||
| 4865 | DRM_DEBUG_ATOMIC("[MSTB:%p] Checking bandwidth limits\n", | ||
| 4866 | mstb); | ||
| 4867 | |||
| 4868 | list_for_each_entry(port, &mstb->ports, next) { | ||
| 4869 | ret = drm_dp_mst_atomic_check_port_bw_limit(port, state); | ||
| 4870 | if (ret < 0) | ||
| 4871 | return ret; | ||
| 4854 | 4872 | ||
| 4855 | if (drm_dp_mst_port_downstream_of_branch(vcpi->port, branch)) | 4873 | pbn_used += ret; |
| 4856 | pbn_used += vcpi->pbn; | ||
| 4857 | } | 4874 | } |
| 4858 | DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch used %d PBN\n", | ||
| 4859 | branch, pbn_used); | ||
| 4860 | 4875 | ||
| 4861 | if (pbn_used > pbn_limit) { | 4876 | return pbn_used; |
| 4862 | DRM_DEBUG_ATOMIC("[MST BRANCH:%p] No available bandwidth\n", | 4877 | } |
| 4863 | branch); | 4878 | |
| 4879 | static int | ||
| 4880 | drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, | ||
| 4881 | struct drm_dp_mst_topology_state *state) | ||
| 4882 | { | ||
| 4883 | struct drm_dp_vcpi_allocation *vcpi; | ||
| 4884 | int pbn_used = 0; | ||
| 4885 | |||
| 4886 | if (port->pdt == DP_PEER_DEVICE_NONE) | ||
| 4887 | return 0; | ||
| 4888 | |||
| 4889 | if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) { | ||
| 4890 | bool found = false; | ||
| 4891 | |||
| 4892 | list_for_each_entry(vcpi, &state->vcpis, next) { | ||
| 4893 | if (vcpi->port != port) | ||
| 4894 | continue; | ||
| 4895 | if (!vcpi->pbn) | ||
| 4896 | return 0; | ||
| 4897 | |||
| 4898 | found = true; | ||
| 4899 | break; | ||
| 4900 | } | ||
| 4901 | if (!found) | ||
| 4902 | return 0; | ||
| 4903 | |||
| 4904 | /* This should never happen, as it means we tried to | ||
| 4905 | * set a mode before querying the full_pbn | ||
| 4906 | */ | ||
| 4907 | if (WARN_ON(!port->full_pbn)) | ||
| 4908 | return -EINVAL; | ||
| 4909 | |||
| 4910 | pbn_used = vcpi->pbn; | ||
| 4911 | } else { | ||
| 4912 | pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb, | ||
| 4913 | state); | ||
| 4914 | if (pbn_used <= 0) | ||
| 4915 | return pbn_used; | ||
| 4916 | } | ||
| 4917 | |||
| 4918 | if (pbn_used > port->full_pbn) { | ||
| 4919 | DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] required PBN of %d exceeds port limit of %d\n", | ||
| 4920 | port->parent, port, pbn_used, | ||
| 4921 | port->full_pbn); | ||
| 4864 | return -ENOSPC; | 4922 | return -ENOSPC; |
| 4865 | } | 4923 | } |
| 4866 | return 0; | 4924 | |
| 4925 | DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] uses %d out of %d PBN\n", | ||
| 4926 | port->parent, port, pbn_used, port->full_pbn); | ||
| 4927 | |||
| 4928 | return pbn_used; | ||
| 4867 | } | 4929 | } |
| 4868 | 4930 | ||
| 4869 | static inline int | 4931 | static inline int |
| @@ -5061,9 +5123,15 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) · 191 unmodified lines | |||
| 5061 | ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state); | 5123 | ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state); |
| 5062 | if (ret) | 5124 | if (ret) |
| 5063 | break; | 5125 | break; |
| 5064 | ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state); | 5126 | |
| 5065 | if (ret) | 5127 | mutex_lock(&mgr->lock); |
| 5128 | ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary, | ||
| 5129 | mst_state); | ||
| 5130 | mutex_unlock(&mgr->lock); | ||
| 5131 | if (ret < 0) | ||
| 5066 | break; | 5132 | break; |
| 5133 | else | ||
| 5134 | ret = 0; | ||
| 5067 | } | 5135 | } |
| 5068 | 5136 | ||
| 5069 | return ret; | 5137 | return ret; |