FILE
link_dp_dpia_bw.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
1 2/* 3 * Copyright 2022 Advanced Micro Devices, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: AMD 24 * 25 */ 26/*********************************************************************/ 27// USB4 DPIA BANDWIDTH ALLOCATION LOGIC 28/*********************************************************************/ 29#include "link_dp_dpia_bw.h" 30#include "link_dpcd.h" 31#include "dc_dmub_srv.h" 32 33#define DC_LOGGER \ 34 link->ctx->logger 35 36#define Kbps_TO_Gbps (1000 * 1000) 37 38#define MST_TIME_SLOT_COUNT 64 39 40// ------------------------------------------------------------------ 41// PRIVATE FUNCTIONS 42// ------------------------------------------------------------------ 43/* 44 * Always Check the following: 45 * - Is it USB4 link? 46 * - Is HPD HIGH? 47 * - Is BW Allocation Support Mode enabled on DP-Tx? 48 */ 49static bool link_dp_is_bw_alloc_available(struct dc_link *link) 50{ 51 return (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling 52 && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc 53 && link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support); 54} 55 56static void reset_bw_alloc_struct(struct dc_link *link) 57{ 58 link->dpia_bw_alloc_config.bw_alloc_enabled = false; 59 link->dpia_bw_alloc_config.link_verified_bw = 0; 60 link->dpia_bw_alloc_config.link_max_bw = 0; 61 link->dpia_bw_alloc_config.allocated_bw = 0; 62 link->dpia_bw_alloc_config.estimated_bw = 0; 63 link->dpia_bw_alloc_config.bw_granularity = 0; 64 link->dpia_bw_alloc_config.dp_overhead = 0; 65 link->dpia_bw_alloc_config.nrd_max_lane_count = 0; 66 link->dpia_bw_alloc_config.nrd_max_link_rate = 0; 67 for (int i = 0; i < MAX_SINKS_PER_LINK; i++) 68 link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0; 69 DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index); 70} 71 72#define BW_GRANULARITY_0 4 // 0.25 Gbps 73#define BW_GRANULARITY_1 2 // 0.5 Gbps 74#define BW_GRANULARITY_2 1 // 1 Gbps 75 76static uint8_t get_bw_granularity(struct dc_link *link) 77{ 78 uint8_t bw_granularity = 0; 79 80 core_link_read_dpcd( 81 link, 82 DP_BW_GRANULALITY, 83 &bw_granularity, 84 sizeof(uint8_t)); 85 86 switch (bw_granularity & 0x3) { 87 case 0: 88 bw_granularity = BW_GRANULARITY_0; 89 break; 90 case 1: 91 bw_granularity = BW_GRANULARITY_1; 92 break; 93 case 2: 94 default: 95 bw_granularity = BW_GRANULARITY_2; 96 break; 97 } 98 99 return bw_granularity; 100} 101 102static int get_estimated_bw(struct dc_link *link) 103{ 104 uint8_t bw_estimated_bw = 0; 105 106 core_link_read_dpcd( 107 link, 108 ESTIMATED_BW, 109 &bw_estimated_bw, 110 sizeof(uint8_t)); 111 112 return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 113} 114 115static int get_non_reduced_max_link_rate(struct dc_link *link) 116{ 117 uint8_t nrd_max_link_rate = 0; 118 119 core_link_read_dpcd( 120 link, 121 DP_TUNNELING_MAX_LINK_RATE, 122 &nrd_max_link_rate, 123 sizeof(uint8_t)); 124 125 return nrd_max_link_rate; 126} 127 128static int get_non_reduced_max_lane_count(struct dc_link *link) 129{ 130 uint8_t nrd_max_lane_count = 0; 131 132 core_link_read_dpcd( 133 link, 134 DP_TUNNELING_MAX_LANE_COUNT, 135 &nrd_max_lane_count, 136 sizeof(uint8_t)); 137 138 return nrd_max_lane_count; 139} 140 141/* 142 * Read all New BW alloc configuration ex: estimated_bw, allocated_bw, 143 * granuality, Driver_ID, CM_Group, & populate the BW allocation structs 144 * for host router and dpia 145 */ 146static void retrieve_usb4_dp_bw_allocation_info(struct dc_link *link) 147{ 148 reset_bw_alloc_struct(link); 149 150 /* init the known values */ 151 link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link); 152 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); 153 link->dpia_bw_alloc_config.nrd_max_link_rate = get_non_reduced_max_link_rate(link); 154 link->dpia_bw_alloc_config.nrd_max_lane_count = get_non_reduced_max_lane_count(link); 155 156 DC_LOG_DEBUG("%s: bw_granularity(%d), estimated_bw(%d)\n", 157 __func__, link->dpia_bw_alloc_config.bw_granularity, 158 link->dpia_bw_alloc_config.estimated_bw); 159 DC_LOG_DEBUG("%s: nrd_max_link_rate(%d), nrd_max_lane_count(%d)\n", 160 __func__, link->dpia_bw_alloc_config.nrd_max_link_rate, 161 link->dpia_bw_alloc_config.nrd_max_lane_count); 162} 163 164/* 165 * Cleanup function for when the dpia is unplugged to reset struct 166 * and perform any required clean up 167 * 168 * @link: pointer to the dc_link struct instance 169 * 170 * return: none 171 */ 172static void dpia_bw_alloc_unplug(struct dc_link *link) 173{ 174 if (link) { 175 DC_LOG_DEBUG("%s: resetting BW alloc config for link(%d)\n", 176 __func__, link->link_index); 177 reset_bw_alloc_struct(link); 178 } 179} 180 181static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw) 182{ 183 uint8_t request_reg_val; 184 uint32_t temp, request_bw; 185 186 if (link->dpia_bw_alloc_config.bw_granularity == 0) { 187 DC_LOG_ERROR("%s: Link[%d]: bw_granularity is zero!", __func__, link->link_index); 188 return; 189 } 190 191 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity; 192 request_reg_val = temp / Kbps_TO_Gbps; 193 194 /* Always make sure to add more to account for floating points */ 195 if (temp % Kbps_TO_Gbps) 196 ++request_reg_val; 197 198 request_bw = request_reg_val * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 199 200 if (request_bw > link->dpia_bw_alloc_config.estimated_bw) { 201 DC_LOG_ERROR("%s: Link[%d]: Request BW (%d --> %d) > Estimated BW (%d)... Set to Estimated BW!", 202 __func__, link->link_index, 203 req_bw, request_bw, link->dpia_bw_alloc_config.estimated_bw); 204 req_bw = link->dpia_bw_alloc_config.estimated_bw; 205 206 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity; 207 request_reg_val = temp / Kbps_TO_Gbps; 208 if (temp % Kbps_TO_Gbps) 209 ++request_reg_val; 210 } 211 212 link->dpia_bw_alloc_config.allocated_bw = request_bw; 213 DC_LOG_DC("%s: Link[%d]: Request BW: %d", __func__, link->link_index, request_bw); 214 215 core_link_write_dpcd(link, REQUESTED_BW, 216 &request_reg_val, 217 sizeof(uint8_t)); 218} 219 220// ------------------------------------------------------------------ 221// PUBLIC FUNCTIONS 222// ------------------------------------------------------------------ 223bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link) 224{ 225 bool ret = false; 226 uint8_t val; 227 228 val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ; 229 230 if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) { 231 DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode enabled", __func__, link->link_index); 232 233 retrieve_usb4_dp_bw_allocation_info(link); 234 235 if ( 236 link->dpia_bw_alloc_config.nrd_max_link_rate 237 && link->dpia_bw_alloc_config.nrd_max_lane_count) { 238 link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate; 239 link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count; 240 } 241 242 link->dpia_bw_alloc_config.bw_alloc_enabled = true; 243 ret = true; 244 245 if (link->dc->debug.dpia_debug.bits.enable_usb4_bw_zero_alloc_patch) { 246 /* 247 * During DP tunnel creation, the CM preallocates BW 248 * and reduces the estimated BW of other DPIAs. 249 * The CM releases the preallocation only when the allocation is complete. 250 * Perform a zero allocation to make the CM release the preallocation 251 * and correctly update the estimated BW for all DPIAs per host router. 252 */ 253 link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0); 254 } 255 } else 256 DC_LOG_DEBUG("%s: link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index); 257 258 return ret; 259} 260 261/* 262 * Handle DP BW allocation status register 263 * 264 * @link: pointer to the dc_link struct instance 265 * @status: content of DP tunneling status DPCD register 266 * 267 * return: none 268 */ 269void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status) 270{ 271 if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) { 272 DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)", 273 __func__, link->link_index); 274 } 275 276 if (status & DP_TUNNELING_BW_REQUEST_FAILED) { 277 DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d) allocated/estimated BW=%d", 278 __func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw); 279 280 link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw); 281 } 282 283 if (status & DP_TUNNELING_BW_ALLOC_CAP_CHANGED) { 284 link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link); 285 286 DC_LOG_DEBUG("%s: Granularity changed on link(%d) new granularity=%d", 287 __func__, link->link_index, link->dpia_bw_alloc_config.bw_granularity); 288 } 289 290 if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) { 291 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); 292 293 DC_LOG_DEBUG("%s: Estimated BW changed on link(%d) new estimated BW=%d", 294 __func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw); 295 } 296 297 core_link_write_dpcd( 298 link, DP_TUNNELING_STATUS, 299 &status, sizeof(status)); 300} 301 302/* 303 * Handle the DP Bandwidth allocation for DPIA 304 * 305 */ 306void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw) 307{ 308 if (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling 309 && link->dpia_bw_alloc_config.bw_alloc_enabled) { 310 if (peak_bw > 0) { 311 // If DP over USB4 then we need to check BW allocation 312 link->dpia_bw_alloc_config.link_max_bw = peak_bw; 313 314 link_dpia_send_bw_alloc_request(link, peak_bw); 315 } else 316 dpia_bw_alloc_unplug(link); 317 } 318} 319 320void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) 321{ 322 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); 323 324 DC_LOG_DEBUG("%s: ENTER: link[%d] hpd(%d) Allocated_BW: %d Estimated_BW: %d Req_BW: %d", 325 __func__, link->link_index, link->hpd_status, 326 link->dpia_bw_alloc_config.allocated_bw, 327 link->dpia_bw_alloc_config.estimated_bw, 328 req_bw); 329 330 if (link_dp_is_bw_alloc_available(link)) 331 link_dpia_send_bw_alloc_request(link, req_bw); 332 else 333 DC_LOG_DEBUG("%s: BW Allocation mode not available", __func__); 334} 335 336uint32_t link_dpia_get_dp_overhead(const struct dc_link *link) 337{ 338 uint32_t link_dp_overhead = 0; 339 340 if ((link->type == dc_connection_mst_branch) && 341 !link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) { 342 /* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH 343 * MST overhead is 1/64 of link bandwidth (excluding any overhead) 344 */ 345 const struct dc_link_settings *link_cap = dc_link_get_link_cap(link); 346 347 if (link_cap) { 348 uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate * 349 (uint32_t)link_cap->lane_count * 350 LINK_RATE_REF_FREQ_IN_KHZ * 8; 351 link_dp_overhead = (link_bw_in_kbps / MST_TIME_SLOT_COUNT) 352 + ((link_bw_in_kbps % MST_TIME_SLOT_COUNT) ? 1 : 0); 353 } 354 } 355 356 return link_dp_overhead; 357} 358 359/* 360 * Aggregates the DPIA bandwidth usage for the respective USB4 Router. 361 * And then validate if the required bandwidth is within the router's capacity. 362 * 363 * @dc_validation_dpia_set: pointer to the dc_validation_dpia_set 364 * @count: number of DPIA validation sets 365 * 366 * return: true if validation is succeeded 367 */ 368bool link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set *dpia_link_sets, uint8_t count) 369{ 370 uint32_t granularity_Gbps; 371 const struct dc_link *link; 372 uint32_t link_bw_granularity; 373 uint32_t link_required_bw; 374 struct usb4_router_validation_set router_sets[MAX_HOST_ROUTERS_NUM] = { 0 }; 375 uint8_t i; 376 bool is_success = true; 377 uint8_t router_count = 0; 378 379 if ((dpia_link_sets == NULL) || (count == 0)) 380 return is_success; 381 382 // Iterate through each DP tunneling link (DPIA). 383 // Aggregate its bandwidth requirements onto the respective USB4 router. 384 for (i = 0; i < count; i++) { 385 link = dpia_link_sets[i].link; 386 link_required_bw = dpia_link_sets[i].required_bw; 387 const struct dc_tunnel_settings *dp_tunnel_settings = dpia_link_sets[i].tunnel_settings; 388 389 if ((link == NULL) || (dp_tunnel_settings == NULL) || dp_tunnel_settings->bw_granularity == 0) 390 break; 391 392 if (link->type == dc_connection_mst_branch) 393 link_required_bw += link_dpia_get_dp_overhead(link); 394 395 granularity_Gbps = (Kbps_TO_Gbps / dp_tunnel_settings->bw_granularity); 396 link_bw_granularity = (link_required_bw / granularity_Gbps) * granularity_Gbps + 397 ((link_required_bw % granularity_Gbps) ? granularity_Gbps : 0); 398 399 // Find or add the USB4 router associated with the current DPIA link 400 for (uint8_t j = 0; j < MAX_HOST_ROUTERS_NUM; j++) { 401 if (router_sets[j].is_valid == false) { 402 router_sets[j].is_valid = true; 403 router_sets[j].cm_id = dp_tunnel_settings->cm_id; 404 router_count++; 405 } 406 407 if (router_sets[j].cm_id == dp_tunnel_settings->cm_id) { 408 uint32_t remaining_bw = 409 dp_tunnel_settings->estimated_bw - dp_tunnel_settings->allocated_bw; 410 411 router_sets[j].allocated_bw += dp_tunnel_settings->allocated_bw; 412 413 if (remaining_bw > router_sets[j].remaining_bw) 414 router_sets[j].remaining_bw = remaining_bw; 415 416 // Get the max estimated BW within the same CM_ID 417 if (dp_tunnel_settings->estimated_bw > router_sets[j].estimated_bw) 418 router_sets[j].estimated_bw = dp_tunnel_settings->estimated_bw; 419 420 router_sets[j].required_bw += link_bw_granularity; 421 router_sets[j].dpia_count++; 422 break; 423 } 424 } 425 } 426 427 // Validate bandwidth for each unique router found. 428 for (i = 0; i < router_count; i++) { 429 uint32_t total_bw = 0; 430 431 if (router_sets[i].is_valid == false) 432 break; 433 434 // Determine the total available bandwidth for the current router based on aggregated data 435 if ((router_sets[i].dpia_count == 1) || (router_sets[i].allocated_bw == 0)) 436 total_bw = router_sets[i].estimated_bw; 437 else 438 total_bw = router_sets[i].allocated_bw + router_sets[i].remaining_bw; 439 440 if (router_sets[i].required_bw > total_bw) { 441 is_success = false; 442 break; 443 } 444 } 445 446 return is_success; 447} 448
~/
Read-only file preview.