Skip to content

Commit 16b78f0

Browse files
committed
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 cd82d82 ("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 <[email protected]> From: Lyude Paul <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
2 parents f31d83f + 047d4cd commit 16b78f0

File tree

2 files changed

+128
-60
lines changed

2 files changed

+128
-60
lines changed

drivers/gpu/drm/drm_dp_mst_topology.c

Lines changed: 126 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,7 +1935,7 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
19351935
return parent_lct + 1;
19361936
}
19371937

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)
19391939
{
19401940
switch (pdt) {
19411941
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,
19651965

19661966
/* Teardown the old pdt, if there is one */
19671967
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)) {
19691969
/*
19701970
* If the new PDT would also have an i2c bus,
19711971
* don't bother with reregistering it
19721972
*/
19731973
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)) {
19751975
port->pdt = new_pdt;
19761976
port->mcs = new_mcs;
19771977
return 0;
@@ -1991,7 +1991,7 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
19911991
port->mcs = new_mcs;
19921992

19931993
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)) {
19951995
/* add i2c over sideband */
19961996
ret = drm_dp_mst_register_i2c_bus(&port->aux);
19971997
} else {
@@ -2172,7 +2172,7 @@ drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
21722172
}
21732173

21742174
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)) {
21762176
port->cached_edid = drm_get_edid(port->connector,
21772177
&port->aux.ddc);
21782178
drm_connector_set_tile_property(port->connector);
@@ -2302,14 +2302,18 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
23022302
mutex_unlock(&mgr->lock);
23032303
}
23042304

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;
23112315
} else {
2312-
port->available_pbn = 0;
2316+
port->full_pbn = 0;
23132317
}
23142318
}
23152319

@@ -2401,11 +2405,10 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
24012405
port->ddps = conn_stat->displayport_device_plug_status;
24022406

24032407
if (old_ddps != port->ddps) {
2404-
if (port->ddps) {
2405-
dowork = true;
2406-
} else {
2407-
port->available_pbn = 0;
2408-
}
2408+
if (port->ddps && !port->input)
2409+
drm_dp_send_enum_path_resources(mgr, mstb, port);
2410+
else
2411+
port->full_pbn = 0;
24092412
}
24102413

24112414
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
25562559
if (port->input || !port->ddps)
25572560
continue;
25582561

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-
25662562
if (port->mstb)
25672563
mstb_child = drm_dp_mst_topology_get_mstb_validated(
25682564
mgr, port->mstb);
@@ -2990,6 +2986,7 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
29902986

29912987
ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
29922988
if (ret > 0) {
2989+
ret = 0;
29932990
path_res = &txmsg->reply.u.path_resources;
29942991

29952992
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,
30022999
path_res->port_number,
30033000
path_res->full_payload_bw_number,
30043001
path_res->avail_payload_bw_number);
3005-
port->available_pbn =
3006-
path_res->avail_payload_bw_number;
3002+
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;
30073012
port->fec_capable = path_res->fec_capable;
30083013
}
30093014
}
30103015

30113016
kfree(txmsg);
3012-
return 0;
3017+
return ret;
30133018
}
30143019

30153020
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)
35963601
/* The link address will need to be re-sent on resume */
35973602
mstb->link_address_sent = false;
35983603

3599-
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-
3604+
list_for_each_entry(port, &mstb->ports, next)
36033605
if (port->mstb)
36043606
drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb);
3605-
}
36063607
}
36073608

36083609
/**
@@ -4829,41 +4830,102 @@ static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
48294830
return false;
48304831
}
48314832

4832-
static inline
4833-
int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch,
4834-
struct drm_dp_mst_topology_state *mst_state)
4833+
static int
4834+
drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
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)
48354840
{
4836-
struct drm_dp_mst_port *port;
48374841
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;
48394845

4840-
list_for_each_entry(port, &branch->ports, next) {
4841-
if (port->mstb)
4842-
if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state))
4843-
return -ENOSPC;
4846+
/* Check that we have at least one port in our state that's downstream
4847+
* of this branch, otherwise we can skip this branch
4848+
*/
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;
48444853

4845-
if (port->available_pbn > 0)
4846-
pbn_limit = port->available_pbn;
4854+
found = true;
4855+
break;
48474856
}
4848-
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n",
4849-
branch, pbn_limit);
4857+
if (!found)
4858+
return 0;
48504859

4851-
list_for_each_entry(vcpi, &mst_state->vcpis, next) {
4852-
if (!vcpi->pbn)
4853-
continue;
4860+
if (mstb->port_parent)
4861+
DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] Checking bandwidth limits on [MSTB:%p]\n",
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;
48544872

4855-
if (drm_dp_mst_port_downstream_of_branch(vcpi->port, branch))
4856-
pbn_used += vcpi->pbn;
4873+
pbn_used += ret;
48574874
}
4858-
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch used %d PBN\n",
4859-
branch, pbn_used);
48604875

4861-
if (pbn_used > pbn_limit) {
4862-
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] No available bandwidth\n",
4863-
branch);
4876+
return pbn_used;
4877+
}
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);
48644922
return -ENOSPC;
48654923
}
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;
48674929
}
48684930

48694931
static inline int
@@ -5061,9 +5123,15 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
50615123
ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state);
50625124
if (ret)
50635125
break;
5064-
ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state);
5065-
if (ret)
5126+
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)
50665132
break;
5133+
else
5134+
ret = 0;
50675135
}
50685136

50695137
return ret;

include/drm/drm_dp_mst_helper.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ struct drm_dp_vcpi {
8181
* &drm_dp_mst_topology_mgr.base.lock.
8282
* @num_sdp_stream_sinks: Number of stream sinks. Protected by
8383
* &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
8585
* &drm_dp_mst_topology_mgr.base.lock.
8686
* @next: link to next port on this branch device
8787
* @aux: i2c aux transport to talk to device connected to this port, protected
@@ -126,7 +126,7 @@ struct drm_dp_mst_port {
126126
u8 dpcd_rev;
127127
u8 num_sdp_streams;
128128
u8 num_sdp_stream_sinks;
129-
uint16_t available_pbn;
129+
uint16_t full_pbn;
130130
struct list_head next;
131131
/**
132132
* @mstb: the branch device connected to this port, if there is one.

0 commit comments

Comments
 (0)