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// ------------------------------------------------------------------ 39// PRIVATE FUNCTIONS 40// ------------------------------------------------------------------ 41/* 42 * Always Check the following: 43 * - Is it USB4 link? 44 * - Is HPD HIGH? 45 * - Is BW Allocation Support Mode enabled on DP-Tx? 46 */ 47static bool get_bw_alloc_proceed_flag(struct dc_link *tmp) 48{ 49 return (tmp && DISPLAY_ENDPOINT_USB4_DPIA == tmp->ep_type 50 && tmp->hpd_status 51 && tmp->dpia_bw_alloc_config.bw_alloc_enabled); 52} 53 54static void reset_bw_alloc_struct(struct dc_link *link) 55{ 56 link->dpia_bw_alloc_config.bw_alloc_enabled = false; 57 link->dpia_bw_alloc_config.link_verified_bw = 0; 58 link->dpia_bw_alloc_config.link_max_bw = 0; 59 link->dpia_bw_alloc_config.allocated_bw = 0; 60 link->dpia_bw_alloc_config.estimated_bw = 0; 61 link->dpia_bw_alloc_config.bw_granularity = 0; 62 link->dpia_bw_alloc_config.dp_overhead = 0; 63 link->dpia_bw_alloc_config.response_ready = false; 64 link->dpia_bw_alloc_config.nrd_max_lane_count = 0; 65 link->dpia_bw_alloc_config.nrd_max_link_rate = 0; 66 for (int i = 0; i < MAX_SINKS_PER_LINK; i++) 67 link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0; 68 DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index); 69} 70 71#define BW_GRANULARITY_0 4 // 0.25 Gbps 72#define BW_GRANULARITY_1 2 // 0.5 Gbps 73#define BW_GRANULARITY_2 1 // 1 Gbps 74 75static uint8_t get_bw_granularity(struct dc_link *link) 76{ 77 uint8_t bw_granularity = 0; 78 79 core_link_read_dpcd( 80 link, 81 DP_BW_GRANULALITY, 82 &bw_granularity, 83 sizeof(uint8_t)); 84 85 switch (bw_granularity & 0x3) { 86 case 0: 87 bw_granularity = BW_GRANULARITY_0; 88 break; 89 case 1: 90 bw_granularity = BW_GRANULARITY_1; 91 break; 92 case 2: 93 default: 94 bw_granularity = BW_GRANULARITY_2; 95 break; 96 } 97 98 return bw_granularity; 99} 100 101static int get_estimated_bw(struct dc_link *link) 102{ 103 uint8_t bw_estimated_bw = 0; 104 105 core_link_read_dpcd( 106 link, 107 ESTIMATED_BW, 108 &bw_estimated_bw, 109 sizeof(uint8_t)); 110 111 return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 112} 113 114static int get_non_reduced_max_link_rate(struct dc_link *link) 115{ 116 uint8_t nrd_max_link_rate = 0; 117 118 core_link_read_dpcd( 119 link, 120 DP_TUNNELING_MAX_LINK_RATE, 121 &nrd_max_link_rate, 122 sizeof(uint8_t)); 123 124 return nrd_max_link_rate; 125} 126 127static int get_non_reduced_max_lane_count(struct dc_link *link) 128{ 129 uint8_t nrd_max_lane_count = 0; 130 131 core_link_read_dpcd( 132 link, 133 DP_TUNNELING_MAX_LANE_COUNT, 134 &nrd_max_lane_count, 135 sizeof(uint8_t)); 136 137 return nrd_max_lane_count; 138} 139 140/* 141 * Read all New BW alloc configuration ex: estimated_bw, allocated_bw, 142 * granuality, Driver_ID, CM_Group, & populate the BW allocation structs 143 * for host router and dpia 144 */ 145static void init_usb4_bw_struct(struct dc_link *link) 146{ 147 reset_bw_alloc_struct(link); 148 149 /* init the known values */ 150 link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link); 151 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); 152 link->dpia_bw_alloc_config.nrd_max_link_rate = get_non_reduced_max_link_rate(link); 153 link->dpia_bw_alloc_config.nrd_max_lane_count = get_non_reduced_max_lane_count(link); 154 155 DC_LOG_DEBUG("%s: bw_granularity(%d), estimated_bw(%d)\n", 156 __func__, link->dpia_bw_alloc_config.bw_granularity, 157 link->dpia_bw_alloc_config.estimated_bw); 158 DC_LOG_DEBUG("%s: nrd_max_link_rate(%d), nrd_max_lane_count(%d)\n", 159 __func__, link->dpia_bw_alloc_config.nrd_max_link_rate, 160 link->dpia_bw_alloc_config.nrd_max_lane_count); 161} 162 163static uint8_t get_lowest_dpia_index(struct dc_link *link) 164{ 165 const struct dc *dc_struct = link->dc; 166 uint8_t idx = 0xFF; 167 int i; 168 169 for (i = 0; i < MAX_PIPES * 2; ++i) { 170 171 if (!dc_struct->links[i] || 172 dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) 173 continue; 174 175 if (idx > dc_struct->links[i]->link_index) { 176 idx = dc_struct->links[i]->link_index; 177 break; 178 } 179 } 180 181 return idx; 182} 183 184/* 185 * Get the maximum dp tunnel banwidth of host router 186 * 187 * @dc: pointer to the dc struct instance 188 * @hr_index: host router index 189 * 190 * return: host router maximum dp tunnel bandwidth 191 */ 192static int get_host_router_total_dp_tunnel_bw(const struct dc *dc, uint8_t hr_index) 193{ 194 uint8_t lowest_dpia_index = get_lowest_dpia_index(dc->links[0]); 195 uint8_t hr_index_temp = 0; 196 struct dc_link *link_dpia_primary, *link_dpia_secondary; 197 int total_bw = 0; 198 199 for (uint8_t i = 0; i < (MAX_PIPES * 2) - 1; ++i) { 200 201 if (!dc->links[i] || dc->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) 202 continue; 203 204 hr_index_temp = (dc->links[i]->link_index - lowest_dpia_index) / 2; 205 206 if (hr_index_temp == hr_index) { 207 link_dpia_primary = dc->links[i]; 208 link_dpia_secondary = dc->links[i + 1]; 209 210 /** 211 * If BW allocation enabled on both DPIAs, then 212 * HR BW = Estimated(dpia_primary) + Allocated(dpia_secondary) 213 * otherwise HR BW = Estimated(bw alloc enabled dpia) 214 */ 215 if ((link_dpia_primary->hpd_status && 216 link_dpia_primary->dpia_bw_alloc_config.bw_alloc_enabled) && 217 (link_dpia_secondary->hpd_status && 218 link_dpia_secondary->dpia_bw_alloc_config.bw_alloc_enabled)) { 219 total_bw += link_dpia_primary->dpia_bw_alloc_config.estimated_bw + 220 link_dpia_secondary->dpia_bw_alloc_config.allocated_bw; 221 } else if (link_dpia_primary->hpd_status && 222 link_dpia_primary->dpia_bw_alloc_config.bw_alloc_enabled) { 223 total_bw = link_dpia_primary->dpia_bw_alloc_config.estimated_bw; 224 } else if (link_dpia_secondary->hpd_status && 225 link_dpia_secondary->dpia_bw_alloc_config.bw_alloc_enabled) { 226 total_bw += link_dpia_secondary->dpia_bw_alloc_config.estimated_bw; 227 } 228 break; 229 } 230 } 231 232 return total_bw; 233} 234 235/* 236 * Cleanup function for when the dpia is unplugged to reset struct 237 * and perform any required clean up 238 * 239 * @link: pointer to the dc_link struct instance 240 * 241 * return: none 242 */ 243static void dpia_bw_alloc_unplug(struct dc_link *link) 244{ 245 if (link) { 246 DC_LOG_DEBUG("%s: resetting bw alloc config for link(%d)\n", 247 __func__, link->link_index); 248 reset_bw_alloc_struct(link); 249 } 250} 251 252static void set_usb4_req_bw_req(struct dc_link *link, int req_bw) 253{ 254 uint8_t requested_bw; 255 uint32_t temp; 256 257 /* Error check whether request bw greater than allocated */ 258 if (req_bw > link->dpia_bw_alloc_config.estimated_bw) { 259 DC_LOG_ERROR("%s: Request bw greater than estimated bw for link(%d)\n", 260 __func__, link->link_index); 261 req_bw = link->dpia_bw_alloc_config.estimated_bw; 262 } 263 264 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity; 265 requested_bw = temp / Kbps_TO_Gbps; 266 267 /* Always make sure to add more to account for floating points */ 268 if (temp % Kbps_TO_Gbps) 269 ++requested_bw; 270 271 /* Error check whether requested and allocated are equal */ 272 req_bw = requested_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 273 if (req_bw && (req_bw == link->dpia_bw_alloc_config.allocated_bw)) { 274 DC_LOG_ERROR("%s: Request bw equals to allocated bw for link(%d)\n", 275 __func__, link->link_index); 276 } 277 278 link->dpia_bw_alloc_config.response_ready = false; // Reset flag 279 core_link_write_dpcd( 280 link, 281 REQUESTED_BW, 282 &requested_bw, 283 sizeof(uint8_t)); 284} 285 286/* 287 * Return the response_ready flag from dc_link struct 288 * 289 * @link: pointer to the dc_link struct instance 290 * 291 * return: response_ready flag from dc_link struct 292 */ 293static bool get_cm_response_ready_flag(struct dc_link *link) 294{ 295 return link->dpia_bw_alloc_config.response_ready; 296} 297 298// ------------------------------------------------------------------ 299// PUBLIC FUNCTIONS 300// ------------------------------------------------------------------ 301bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link) 302{ 303 bool ret = false; 304 uint8_t response = 0, 305 bw_support_dpia = 0, 306 bw_support_cm = 0; 307 308 if (!(link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->hpd_status)) 309 goto out; 310 311 if (core_link_read_dpcd( 312 link, 313 DP_TUNNELING_CAPABILITIES, 314 &response, 315 sizeof(uint8_t)) == DC_OK) 316 bw_support_dpia = (response >> 7) & 1; 317 318 if (core_link_read_dpcd( 319 link, 320 USB4_DRIVER_BW_CAPABILITY, 321 &response, 322 sizeof(uint8_t)) == DC_OK) 323 bw_support_cm = (response >> 7) & 1; 324 325 /* Send request acknowledgment to Turn ON DPTX support */ 326 if (bw_support_cm && bw_support_dpia) { 327 328 response = 0x80; 329 if (core_link_write_dpcd( 330 link, 331 DPTX_BW_ALLOCATION_MODE_CONTROL, 332 &response, 333 sizeof(uint8_t)) != DC_OK) { 334 DC_LOG_DEBUG("%s: FAILURE Enabling DPtx BW Allocation Mode Support for link(%d)\n", 335 __func__, link->link_index); 336 } else { 337 // SUCCESS Enabled DPtx BW Allocation Mode Support 338 DC_LOG_DEBUG("%s: SUCCESS Enabling DPtx BW Allocation Mode Support for link(%d)\n", 339 __func__, link->link_index); 340 341 ret = true; 342 init_usb4_bw_struct(link); 343 link->dpia_bw_alloc_config.bw_alloc_enabled = true; 344 345 /* 346 * During DP tunnel creation, CM preallocates BW and reduces estimated BW of other 347 * DPIA. CM release preallocation only when allocation is complete. Do zero alloc 348 * to make the CM to release preallocation and update estimated BW correctly for 349 * all DPIAs per host router 350 */ 351 link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0); 352 } 353 } 354 355out: 356 return ret; 357} 358 359void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result) 360{ 361 int bw_needed = 0; 362 int estimated = 0; 363 364 if (!get_bw_alloc_proceed_flag((link))) 365 return; 366 367 switch (result) { 368 369 case DPIA_BW_REQ_FAILED: 370 371 /* 372 * Ideally, we shouldn't run into this case as we always validate available 373 * bandwidth and request within that limit 374 */ 375 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 376 377 DC_LOG_ERROR("%s: BW REQ FAILURE for DP-TX Request for link(%d)\n", 378 __func__, link->link_index); 379 DC_LOG_ERROR("%s: current estimated_bw(%d), new estimated_bw(%d)\n", 380 __func__, link->dpia_bw_alloc_config.estimated_bw, estimated); 381 382 /* Update the new Estimated BW value updated by CM */ 383 link->dpia_bw_alloc_config.estimated_bw = estimated; 384 385 /* Allocate the previously requested bandwidth */ 386 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw); 387 388 /* 389 * If FAIL then it is either: 390 * 1. Due to DP-Tx trying to allocate more than available i.e. it failed locally 391 * => get estimated and allocate that 392 * 2. Due to the fact that DP-Tx tried to allocated ESTIMATED BW and failed then 393 * CM will have to update 0xE0023 with new ESTIMATED BW value. 394 */ 395 break; 396 397 case DPIA_BW_REQ_SUCCESS: 398 399 bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 400 401 DC_LOG_DEBUG("%s: BW REQ SUCCESS for DP-TX Request for link(%d)\n", 402 __func__, link->link_index); 403 DC_LOG_DEBUG("%s: current allocated_bw(%d), new allocated_bw(%d)\n", 404 __func__, link->dpia_bw_alloc_config.allocated_bw, bw_needed); 405 406 link->dpia_bw_alloc_config.allocated_bw = bw_needed; 407 408 link->dpia_bw_alloc_config.response_ready = true; 409 break; 410 411 case DPIA_EST_BW_CHANGED: 412 413 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 414 415 DC_LOG_DEBUG("%s: ESTIMATED BW CHANGED for link(%d)\n", 416 __func__, link->link_index); 417 DC_LOG_DEBUG("%s: current estimated_bw(%d), new estimated_bw(%d)\n", 418 __func__, link->dpia_bw_alloc_config.estimated_bw, estimated); 419 420 link->dpia_bw_alloc_config.estimated_bw = estimated; 421 break; 422 423 case DPIA_BW_ALLOC_CAPS_CHANGED: 424 425 DC_LOG_ERROR("%s: BW ALLOC CAPABILITY CHANGED to Disabled for link(%d)\n", 426 __func__, link->link_index); 427 link->dpia_bw_alloc_config.bw_alloc_enabled = false; 428 break; 429 } 430} 431int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw) 432{ 433 int ret = 0; 434 uint8_t timeout = 10; 435 436 if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type 437 && link->dpia_bw_alloc_config.bw_alloc_enabled)) 438 goto out; 439 440 //1. Hot Plug 441 if (link->hpd_status && peak_bw > 0) { 442 443 // If DP over USB4 then we need to check BW allocation 444 link->dpia_bw_alloc_config.link_max_bw = peak_bw; 445 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.link_max_bw); 446 447 do { 448 if (timeout > 0) 449 timeout--; 450 else 451 break; 452 drm_msleep(10); 453 } while (!get_cm_response_ready_flag(link)); 454 455 if (!timeout) 456 ret = 0;// ERROR TIMEOUT waiting for response for allocating bw 457 else if (link->dpia_bw_alloc_config.allocated_bw > 0) 458 ret = link->dpia_bw_alloc_config.allocated_bw; 459 } 460 //2. Cold Unplug 461 else if (!link->hpd_status) 462 dpia_bw_alloc_unplug(link); 463 464out: 465 return ret; 466} 467bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) 468{ 469 bool ret = false; 470 uint8_t timeout = 10; 471 472 DC_LOG_DEBUG("%s: ENTER: link(%d), hpd_status(%d), current allocated_bw(%d), req_bw(%d)\n", 473 __func__, link->link_index, link->hpd_status, 474 link->dpia_bw_alloc_config.allocated_bw, req_bw); 475 476 if (!get_bw_alloc_proceed_flag(link)) 477 goto out; 478 479 set_usb4_req_bw_req(link, req_bw); 480 do { 481 if (timeout > 0) 482 timeout--; 483 else 484 break; 485 drm_msleep(10); 486 } while (!get_cm_response_ready_flag(link)); 487 488 if (timeout) 489 ret = true; 490 491out: 492 DC_LOG_DEBUG("%s: EXIT: timeout(%d), ret(%d)\n", __func__, timeout, ret); 493 return ret; 494} 495 496bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias) 497{ 498 bool ret = true; 499 int bw_needed_per_hr[MAX_HR_NUM] = { 0, 0 }, host_router_total_dp_bw = 0; 500 uint8_t lowest_dpia_index, i, hr_index; 501 502 if (!num_dpias || num_dpias > MAX_DPIA_NUM) 503 return ret; 504 505 lowest_dpia_index = get_lowest_dpia_index(link[0]); 506 507 /* get total Host Router BW with granularity for the given modes */ 508 for (i = 0; i < num_dpias; ++i) { 509 int granularity_Gbps = 0; 510 int bw_granularity = 0; 511 512 if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled) 513 continue; 514 515 if (link[i]->link_index < lowest_dpia_index) 516 continue; 517 518 granularity_Gbps = (Kbps_TO_Gbps / link[i]->dpia_bw_alloc_config.bw_granularity); 519 bw_granularity = (bw_needed_per_dpia[i] / granularity_Gbps) * granularity_Gbps + 520 ((bw_needed_per_dpia[i] % granularity_Gbps) ? granularity_Gbps : 0); 521 522 hr_index = (link[i]->link_index - lowest_dpia_index) / 2; 523 bw_needed_per_hr[hr_index] += bw_granularity; 524 } 525 526 /* validate against each Host Router max BW */ 527 for (hr_index = 0; hr_index < MAX_HR_NUM; ++hr_index) { 528 if (bw_needed_per_hr[hr_index]) { 529 host_router_total_dp_bw = get_host_router_total_dp_tunnel_bw(link[0]->dc, hr_index); 530 if (bw_needed_per_hr[hr_index] > host_router_total_dp_bw) { 531 ret = false; 532 break; 533 } 534 } 535 } 536 537 return ret; 538} 539 540int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link) 541{ 542 int dp_overhead = 0, link_mst_overhead = 0; 543 544 if (!get_bw_alloc_proceed_flag((link))) 545 return dp_overhead; 546 547 /* if its mst link, add MTPH overhead */ 548 if ((link->type == dc_connection_mst_branch) && 549 !link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) { 550 /* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH 551 * MST overhead is 1/64 of link bandwidth (excluding any overhead) 552 */ 553 const struct dc_link_settings *link_cap = 554 dc_link_get_link_cap(link); 555 uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate * 556 (uint32_t)link_cap->lane_count * 557 LINK_RATE_REF_FREQ_IN_KHZ * 8; 558 link_mst_overhead = (link_bw_in_kbps / 64) + ((link_bw_in_kbps % 64) ? 1 : 0); 559 } 560 561 /* add all the overheads */ 562 dp_overhead = link_mst_overhead; 563 564 return dp_overhead; 565} 566