Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'topic/mst-bw-check-fixes-for-airlied-2020-03-12-2' of git://anongit.freedesktop.org/drm/drm-misc into drm-fixes

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

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Lyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/bf16ee577567beed91c86b7d9cda3ec2e8c50a71.camel@redhat.com

+130 -62
+128 -60
drivers/gpu/drm/drm_dp_mst_topology.c
··· 1935 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 1940 switch (pdt) { 1941 1941 case DP_PEER_DEVICE_DP_LEGACY_CONV: ··· 1965 1965 1966 1966 /* Teardown the old pdt, if there is one */ 1967 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 1970 * If the new PDT would also have an i2c bus, 1971 1971 * don't bother with reregistering it 1972 1972 */ 1973 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 1975 port->pdt = new_pdt; 1976 1976 port->mcs = new_mcs; 1977 1977 return 0; ··· 1991 1991 port->mcs = new_mcs; 1992 1992 1993 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 1995 /* add i2c over sideband */ 1996 1996 ret = drm_dp_mst_register_i2c_bus(&port->aux); 1997 1997 } else { ··· 2172 2172 } 2173 2173 2174 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 2176 port->cached_edid = drm_get_edid(port->connector, 2177 2177 &port->aux.ddc); 2178 2178 drm_connector_set_tile_property(port->connector); ··· 2302 2302 mutex_unlock(&mgr->lock); 2303 2303 } 2304 2304 2305 - if (old_ddps != port->ddps) { 2306 - if (port->ddps) { 2307 - if (!port->input) { 2308 - drm_dp_send_enum_path_resources(mgr, mstb, 2309 - port); 2310 - } 2305 + /* 2306 + * Reprobe PBN caps on both hotplug, and when re-probing the link 2307 + * for our parent mstb 2308 + */ 2309 + if (old_ddps != port->ddps || !created) { 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 2315 } else { 2312 - port->available_pbn = 0; 2316 + port->full_pbn = 0; 2313 2317 } 2314 2318 } 2315 2319 ··· 2405 2401 port->ddps = conn_stat->displayport_device_plug_status; 2406 2402 2407 2403 if (old_ddps != port->ddps) { 2408 - if (port->ddps) { 2409 - dowork = true; 2410 - } else { 2411 - port->available_pbn = 0; 2412 - } 2404 + if (port->ddps && !port->input) 2405 + drm_dp_send_enum_path_resources(mgr, mstb, port); 2406 + else 2407 + port->full_pbn = 0; 2413 2408 } 2414 2409 2415 2410 new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type; ··· 2558 2555 2559 2556 if (port->input || !port->ddps) 2560 2557 continue; 2561 - 2562 - if (!port->available_pbn) { 2563 - drm_modeset_lock(&mgr->base.lock, NULL); 2564 - drm_dp_send_enum_path_resources(mgr, mstb, port); 2565 - drm_modeset_unlock(&mgr->base.lock); 2566 - changed = true; 2567 - } 2568 2558 2569 2559 if (port->mstb) 2570 2560 mstb_child = drm_dp_mst_topology_get_mstb_validated( ··· 2986 2990 2987 2991 ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); 2988 2992 if (ret > 0) { 2993 + ret = 0; 2989 2994 path_res = &txmsg->reply.u.path_resources; 2990 2995 2991 2996 if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) { ··· 2999 3002 path_res->port_number, 3000 3003 path_res->full_payload_bw_number, 3001 3004 path_res->avail_payload_bw_number); 3002 - port->available_pbn = 3003 - path_res->avail_payload_bw_number; 3005 + 3006 + /* 3007 + * If something changed, make sure we send a 3008 + * hotplug 3009 + */ 3010 + if (port->full_pbn != path_res->full_payload_bw_number || 3011 + port->fec_capable != path_res->fec_capable) 3012 + ret = 1; 3013 + 3014 + port->full_pbn = path_res->full_payload_bw_number; 3004 3015 port->fec_capable = path_res->fec_capable; 3005 3016 } 3006 3017 } 3007 3018 3008 3019 kfree(txmsg); 3009 - return 0; 3020 + return ret; 3010 3021 } 3011 3022 3012 3023 static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb) ··· 3601 3596 /* The link address will need to be re-sent on resume */ 3602 3597 mstb->link_address_sent = false; 3603 3598 3604 - list_for_each_entry(port, &mstb->ports, next) { 3605 - /* The PBN for each port will also need to be re-probed */ 3606 - port->available_pbn = 0; 3607 - 3599 + list_for_each_entry(port, &mstb->ports, next) 3608 3600 if (port->mstb) 3609 3601 drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb); 3610 - } 3611 3602 } 3612 3603 3613 3604 /** ··· 4830 4829 return false; 4831 4830 } 4832 4831 4833 - static inline 4834 - int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch, 4835 - struct drm_dp_mst_topology_state *mst_state) 4832 + static int 4833 + drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, 4834 + struct drm_dp_mst_topology_state *state); 4835 + 4836 + static int 4837 + drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb, 4838 + struct drm_dp_mst_topology_state *state) 4836 4839 { 4837 - struct drm_dp_mst_port *port; 4838 4840 struct drm_dp_vcpi_allocation *vcpi; 4839 - int pbn_limit = 0, pbn_used = 0; 4841 + struct drm_dp_mst_port *port; 4842 + int pbn_used = 0, ret; 4843 + bool found = false; 4840 4844 4841 - list_for_each_entry(port, &branch->ports, next) { 4842 - if (port->mstb) 4843 - if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state)) 4844 - return -ENOSPC; 4845 - 4846 - if (port->available_pbn > 0) 4847 - pbn_limit = port->available_pbn; 4848 - } 4849 - DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n", 4850 - branch, pbn_limit); 4851 - 4852 - list_for_each_entry(vcpi, &mst_state->vcpis, next) { 4853 - if (!vcpi->pbn) 4845 + /* Check that we have at least one port in our state that's downstream 4846 + * of this branch, otherwise we can skip this branch 4847 + */ 4848 + list_for_each_entry(vcpi, &state->vcpis, next) { 4849 + if (!vcpi->pbn || 4850 + !drm_dp_mst_port_downstream_of_branch(vcpi->port, mstb)) 4854 4851 continue; 4855 4852 4856 - if (drm_dp_mst_port_downstream_of_branch(vcpi->port, branch)) 4857 - pbn_used += vcpi->pbn; 4853 + found = true; 4854 + break; 4858 4855 } 4859 - DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch used %d PBN\n", 4860 - branch, pbn_used); 4856 + if (!found) 4857 + return 0; 4861 4858 4862 - if (pbn_used > pbn_limit) { 4863 - DRM_DEBUG_ATOMIC("[MST BRANCH:%p] No available bandwidth\n", 4864 - branch); 4859 + if (mstb->port_parent) 4860 + DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] Checking bandwidth limits on [MSTB:%p]\n", 4861 + mstb->port_parent->parent, mstb->port_parent, 4862 + mstb); 4863 + else 4864 + DRM_DEBUG_ATOMIC("[MSTB:%p] Checking bandwidth limits\n", 4865 + mstb); 4866 + 4867 + list_for_each_entry(port, &mstb->ports, next) { 4868 + ret = drm_dp_mst_atomic_check_port_bw_limit(port, state); 4869 + if (ret < 0) 4870 + return ret; 4871 + 4872 + pbn_used += ret; 4873 + } 4874 + 4875 + return pbn_used; 4876 + } 4877 + 4878 + static int 4879 + drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, 4880 + struct drm_dp_mst_topology_state *state) 4881 + { 4882 + struct drm_dp_vcpi_allocation *vcpi; 4883 + int pbn_used = 0; 4884 + 4885 + if (port->pdt == DP_PEER_DEVICE_NONE) 4886 + return 0; 4887 + 4888 + if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) { 4889 + bool found = false; 4890 + 4891 + list_for_each_entry(vcpi, &state->vcpis, next) { 4892 + if (vcpi->port != port) 4893 + continue; 4894 + if (!vcpi->pbn) 4895 + return 0; 4896 + 4897 + found = true; 4898 + break; 4899 + } 4900 + if (!found) 4901 + return 0; 4902 + 4903 + /* This should never happen, as it means we tried to 4904 + * set a mode before querying the full_pbn 4905 + */ 4906 + if (WARN_ON(!port->full_pbn)) 4907 + return -EINVAL; 4908 + 4909 + pbn_used = vcpi->pbn; 4910 + } else { 4911 + pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb, 4912 + state); 4913 + if (pbn_used <= 0) 4914 + return pbn_used; 4915 + } 4916 + 4917 + if (pbn_used > port->full_pbn) { 4918 + DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] required PBN of %d exceeds port limit of %d\n", 4919 + port->parent, port, pbn_used, 4920 + port->full_pbn); 4865 4921 return -ENOSPC; 4866 4922 } 4867 - return 0; 4923 + 4924 + DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] uses %d out of %d PBN\n", 4925 + port->parent, port, pbn_used, port->full_pbn); 4926 + 4927 + return pbn_used; 4868 4928 } 4869 4929 4870 4930 static inline int ··· 5123 5061 ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state); 5124 5062 if (ret) 5125 5063 break; 5126 - ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state); 5127 - if (ret) 5064 + 5065 + mutex_lock(&mgr->lock); 5066 + ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary, 5067 + mst_state); 5068 + mutex_unlock(&mgr->lock); 5069 + if (ret < 0) 5128 5070 break; 5071 + else 5072 + ret = 0; 5129 5073 } 5130 5074 5131 5075 return ret;
+2 -2
include/drm/drm_dp_mst_helper.h
··· 81 81 * &drm_dp_mst_topology_mgr.base.lock. 82 82 * @num_sdp_stream_sinks: Number of stream sinks. Protected by 83 83 * &drm_dp_mst_topology_mgr.base.lock. 84 - * @available_pbn: Available bandwidth for this port. Protected by 84 + * @full_pbn: Max possible bandwidth for this port. Protected by 85 85 * &drm_dp_mst_topology_mgr.base.lock. 86 86 * @next: link to next port on this branch device 87 87 * @aux: i2c aux transport to talk to device connected to this port, protected ··· 126 126 u8 dpcd_rev; 127 127 u8 num_sdp_streams; 128 128 u8 num_sdp_stream_sinks; 129 - uint16_t available_pbn; 129 + uint16_t full_pbn; 130 130 struct list_head next; 131 131 /** 132 132 * @mstb: the branch device connected to this port, if there is one.