History

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

Dave Airlie committed 6 years ago

Commit 16b78f02 parents f31d83f 047d4cd2 files · +128 −60Browse files

Unified · Split

2 files
  1. drivers/gpu/drm/drm_dp_mst_topology.c +126 −58
  2. include/drm/drm_dp_mst_helper.h +2 −2
drivers/gpu/drm/drm_dp_mst_topology.c+126−58
@@ -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
1938static bool drm_dp_mst_is_dp_mst_end_device(u8 pdt, bool mcs)1938static 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 it1971 * 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
3015static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb)3020static 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
4832static inline4833static int
4833int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch,4834drm_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
4837static int
4838drm_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
4879static int
4880drm_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
4869static inline int4931static 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;
include/drm/drm_dp_mst_helper.h+2−2
@@ -81,7 +81,7 @@ struct drm_dp_vcpi { · 80 unmodified lines
81 * &drm_dp_mst_topology_mgr.base.lock.81 * &drm_dp_mst_topology_mgr.base.lock.
82 * @num_sdp_stream_sinks: Number of stream sinks. Protected by82 * @num_sdp_stream_sinks: Number of stream sinks. Protected by
83 * &drm_dp_mst_topology_mgr.base.lock.83 * &drm_dp_mst_topology_mgr.base.lock.
84 * @available_pbn: Available bandwidth for this port. Protected by84 * @full_pbn: Max possible bandwidth for this port. Protected by
85 * &drm_dp_mst_topology_mgr.base.lock.85 * &drm_dp_mst_topology_mgr.base.lock.
86 * @next: link to next port on this branch device86 * @next: link to next port on this branch device
87 * @aux: i2c aux transport to talk to device connected to this port, protected87 * @aux: i2c aux transport to talk to device connected to this port, protected
@@ -126,7 +126,7 @@ struct drm_dp_mst_port { · 38 unmodified lines
126 u8 dpcd_rev;126 u8 dpcd_rev;
127 u8 num_sdp_streams;127 u8 num_sdp_streams;
128 u8 num_sdp_stream_sinks;128 u8 num_sdp_stream_sinks;
129 uint16_t available_pbn;129 uint16_t full_pbn;
130 struct list_head next;130 struct list_head next;
131 /**131 /**
132 * @mstb: the branch device connected to this port, if there is one.132 * @mstb: the branch device connected to this port, if there is one.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t