@@ -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
30153020static 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
48694931static 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 ;
0 commit comments