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 */ 47 static 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 static void reset_bw_alloc_struct(struct dc_link *link) 54 { 55 link->dpia_bw_alloc_config.bw_alloc_enabled = false; 56 link->dpia_bw_alloc_config.sink_verified_bw = 0; 57 link->dpia_bw_alloc_config.sink_max_bw = 0; 58 link->dpia_bw_alloc_config.estimated_bw = 0; 59 link->dpia_bw_alloc_config.bw_granularity = 0; 60 link->dpia_bw_alloc_config.response_ready = false; 61 } 62 static uint8_t get_bw_granularity(struct dc_link *link) 63 { 64 uint8_t bw_granularity = 0; 65 66 core_link_read_dpcd( 67 link, 68 DP_BW_GRANULALITY, 69 &bw_granularity, 70 sizeof(uint8_t)); 71 72 switch (bw_granularity & 0x3) { 73 case 0: 74 bw_granularity = 4; 75 break; 76 case 1: 77 default: 78 bw_granularity = 2; 79 break; 80 } 81 82 return bw_granularity; 83 } 84 static int get_estimated_bw(struct dc_link *link) 85 { 86 uint8_t bw_estimated_bw = 0; 87 88 core_link_read_dpcd( 89 link, 90 ESTIMATED_BW, 91 &bw_estimated_bw, 92 sizeof(uint8_t)); 93 94 return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 95 } 96 static bool allocate_usb4_bw(int *stream_allocated_bw, int bw_needed, struct dc_link *link) 97 { 98 if (bw_needed > 0) 99 *stream_allocated_bw += bw_needed; 100 101 return true; 102 } 103 static bool deallocate_usb4_bw(int *stream_allocated_bw, int bw_to_dealloc, struct dc_link *link) 104 { 105 bool ret = false; 106 107 if (*stream_allocated_bw > 0) { 108 *stream_allocated_bw -= bw_to_dealloc; 109 ret = true; 110 } else { 111 //Do nothing for now 112 ret = true; 113 } 114 115 // Unplug so reset values 116 if (!link->hpd_status) 117 reset_bw_alloc_struct(link); 118 119 return ret; 120 } 121 /* 122 * Read all New BW alloc configuration ex: estimated_bw, allocated_bw, 123 * granuality, Driver_ID, CM_Group, & populate the BW allocation structs 124 * for host router and dpia 125 */ 126 static void init_usb4_bw_struct(struct dc_link *link) 127 { 128 // Init the known values 129 link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link); 130 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); 131 } 132 static uint8_t get_lowest_dpia_index(struct dc_link *link) 133 { 134 const struct dc *dc_struct = link->dc; 135 uint8_t idx = 0xFF; 136 int i; 137 138 for (i = 0; i < MAX_PIPES * 2; ++i) { 139 140 if (!dc_struct->links[i] || 141 dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) 142 continue; 143 144 if (idx > dc_struct->links[i]->link_index) 145 idx = dc_struct->links[i]->link_index; 146 } 147 148 return idx; 149 } 150 /* 151 * Get the Max Available BW or Max Estimated BW for each Host Router 152 * 153 * @link: pointer to the dc_link struct instance 154 * @type: ESTIMATD BW or MAX AVAILABLE BW 155 * 156 * return: response_ready flag from dc_link struct 157 */ 158 static int get_host_router_total_bw(struct dc_link *link, uint8_t type) 159 { 160 const struct dc *dc_struct = link->dc; 161 uint8_t lowest_dpia_index = get_lowest_dpia_index(link); 162 uint8_t idx = (link->link_index - lowest_dpia_index) / 2, idx_temp = 0; 163 struct dc_link *link_temp; 164 int total_bw = 0; 165 int i; 166 167 for (i = 0; i < MAX_PIPES * 2; ++i) { 168 169 if (!dc_struct->links[i] || dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) 170 continue; 171 172 link_temp = dc_struct->links[i]; 173 if (!link_temp || !link_temp->hpd_status) 174 continue; 175 176 idx_temp = (link_temp->link_index - lowest_dpia_index) / 2; 177 178 if (idx_temp == idx) { 179 180 if (type == HOST_ROUTER_BW_ESTIMATED) 181 total_bw += link_temp->dpia_bw_alloc_config.estimated_bw; 182 else if (type == HOST_ROUTER_BW_ALLOCATED) 183 total_bw += link_temp->dpia_bw_alloc_config.sink_allocated_bw; 184 } 185 } 186 187 return total_bw; 188 } 189 /* 190 * Cleanup function for when the dpia is unplugged to reset struct 191 * and perform any required clean up 192 * 193 * @link: pointer to the dc_link struct instance 194 * 195 * return: none 196 */ 197 static bool dpia_bw_alloc_unplug(struct dc_link *link) 198 { 199 if (!link) 200 return true; 201 202 return deallocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, 203 link->dpia_bw_alloc_config.sink_allocated_bw, link); 204 } 205 static void set_usb4_req_bw_req(struct dc_link *link, int req_bw) 206 { 207 uint8_t requested_bw; 208 uint32_t temp; 209 210 // 1. Add check for this corner case #1 211 if (req_bw > link->dpia_bw_alloc_config.estimated_bw) 212 req_bw = link->dpia_bw_alloc_config.estimated_bw; 213 214 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity; 215 requested_bw = temp / Kbps_TO_Gbps; 216 217 // Always make sure to add more to account for floating points 218 if (temp % Kbps_TO_Gbps) 219 ++requested_bw; 220 221 // 2. Add check for this corner case #2 222 req_bw = requested_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 223 if (req_bw == link->dpia_bw_alloc_config.sink_allocated_bw) 224 return; 225 226 if (core_link_write_dpcd( 227 link, 228 REQUESTED_BW, 229 &requested_bw, 230 sizeof(uint8_t)) == DC_OK) 231 link->dpia_bw_alloc_config.response_ready = false; // Reset flag 232 } 233 /* 234 * Return the response_ready flag from dc_link struct 235 * 236 * @link: pointer to the dc_link struct instance 237 * 238 * return: response_ready flag from dc_link struct 239 */ 240 static bool get_cm_response_ready_flag(struct dc_link *link) 241 { 242 return link->dpia_bw_alloc_config.response_ready; 243 } 244 // ------------------------------------------------------------------ 245 // PUBLIC FUNCTIONS 246 // ------------------------------------------------------------------ 247 bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link) 248 { 249 bool ret = false; 250 uint8_t response = 0, 251 bw_support_dpia = 0, 252 bw_support_cm = 0; 253 254 if (!(link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->hpd_status)) 255 goto out; 256 257 if (core_link_read_dpcd( 258 link, 259 DP_TUNNELING_CAPABILITIES, 260 &response, 261 sizeof(uint8_t)) == DC_OK) 262 bw_support_dpia = (response >> 7) & 1; 263 264 if (core_link_read_dpcd( 265 link, 266 USB4_DRIVER_BW_CAPABILITY, 267 &response, 268 sizeof(uint8_t)) == DC_OK) 269 bw_support_cm = (response >> 7) & 1; 270 271 /* Send request acknowledgment to Turn ON DPTX support */ 272 if (bw_support_cm && bw_support_dpia) { 273 274 response = 0x80; 275 if (core_link_write_dpcd( 276 link, 277 DPTX_BW_ALLOCATION_MODE_CONTROL, 278 &response, 279 sizeof(uint8_t)) != DC_OK) { 280 DC_LOG_DEBUG("%s: **** FAILURE Enabling DPtx BW Allocation Mode Support ***\n", 281 __func__); 282 } else { 283 // SUCCESS Enabled DPtx BW Allocation Mode Support 284 link->dpia_bw_alloc_config.bw_alloc_enabled = true; 285 DC_LOG_DEBUG("%s: **** SUCCESS Enabling DPtx BW Allocation Mode Support ***\n", 286 __func__); 287 288 ret = true; 289 init_usb4_bw_struct(link); 290 } 291 } 292 293 out: 294 return ret; 295 } 296 void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result) 297 { 298 int bw_needed = 0; 299 int estimated = 0; 300 int host_router_total_estimated_bw = 0; 301 302 if (!get_bw_alloc_proceed_flag((link))) 303 return; 304 305 switch (result) { 306 307 case DPIA_BW_REQ_FAILED: 308 309 DC_LOG_DEBUG("%s: *** *** BW REQ FAILURE for DP-TX Request *** ***\n", __func__); 310 311 // Update the new Estimated BW value updated by CM 312 link->dpia_bw_alloc_config.estimated_bw = 313 bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 314 315 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw); 316 link->dpia_bw_alloc_config.response_ready = false; 317 318 /* 319 * If FAIL then it is either: 320 * 1. Due to DP-Tx trying to allocate more than available i.e. it failed locally 321 * => get estimated and allocate that 322 * 2. Due to the fact that DP-Tx tried to allocated ESTIMATED BW and failed then 323 * CM will have to update 0xE0023 with new ESTIMATED BW value. 324 */ 325 break; 326 327 case DPIA_BW_REQ_SUCCESS: 328 329 DC_LOG_DEBUG("%s: *** BW REQ SUCCESS for DP-TX Request ***\n", __func__); 330 331 // 1. SUCCESS 1st time before any Pruning is done 332 // 2. SUCCESS after prev. FAIL before any Pruning is done 333 // 3. SUCCESS after Pruning is done but before enabling link 334 335 bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 336 337 // 1. 338 if (!link->dpia_bw_alloc_config.sink_allocated_bw) { 339 340 allocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, bw_needed, link); 341 link->dpia_bw_alloc_config.sink_verified_bw = 342 link->dpia_bw_alloc_config.sink_allocated_bw; 343 344 // SUCCESS from first attempt 345 if (link->dpia_bw_alloc_config.sink_allocated_bw > 346 link->dpia_bw_alloc_config.sink_max_bw) 347 link->dpia_bw_alloc_config.sink_verified_bw = 348 link->dpia_bw_alloc_config.sink_max_bw; 349 } 350 // 3. 351 else if (link->dpia_bw_alloc_config.sink_allocated_bw) { 352 353 // Find out how much do we need to de-alloc 354 if (link->dpia_bw_alloc_config.sink_allocated_bw > bw_needed) 355 deallocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, 356 link->dpia_bw_alloc_config.sink_allocated_bw - bw_needed, link); 357 else 358 allocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, 359 bw_needed - link->dpia_bw_alloc_config.sink_allocated_bw, link); 360 } 361 362 // 4. If this is the 2nd sink then any unused bw will be reallocated to master DPIA 363 // => check if estimated_bw changed 364 365 link->dpia_bw_alloc_config.response_ready = true; 366 break; 367 368 case DPIA_EST_BW_CHANGED: 369 370 DC_LOG_DEBUG("%s: *** ESTIMATED BW CHANGED for DP-TX Request ***\n", __func__); 371 372 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); 373 host_router_total_estimated_bw = get_host_router_total_bw(link, HOST_ROUTER_BW_ESTIMATED); 374 375 // 1. If due to unplug of other sink 376 if (estimated == host_router_total_estimated_bw) { 377 // First update the estimated & max_bw fields 378 if (link->dpia_bw_alloc_config.estimated_bw < estimated) 379 link->dpia_bw_alloc_config.estimated_bw = estimated; 380 } 381 // 2. If due to realloc bw btw 2 dpia due to plug OR realloc unused Bw 382 else { 383 // We lost estimated bw usually due to plug event of other dpia 384 link->dpia_bw_alloc_config.estimated_bw = estimated; 385 } 386 break; 387 388 case DPIA_BW_ALLOC_CAPS_CHANGED: 389 390 DC_LOG_DEBUG("%s: *** BW ALLOC CAPABILITY CHANGED for DP-TX Request ***\n", __func__); 391 link->dpia_bw_alloc_config.bw_alloc_enabled = false; 392 break; 393 } 394 } 395 int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw) 396 { 397 int ret = 0; 398 uint8_t timeout = 10; 399 400 if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type 401 && link->dpia_bw_alloc_config.bw_alloc_enabled)) 402 goto out; 403 404 //1. Hot Plug 405 if (link->hpd_status && peak_bw > 0) { 406 407 // If DP over USB4 then we need to check BW allocation 408 link->dpia_bw_alloc_config.sink_max_bw = peak_bw; 409 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.sink_max_bw); 410 411 do { 412 if (!(timeout > 0)) 413 timeout--; 414 else 415 break; 416 fsleep(10 * 1000); 417 } while (!get_cm_response_ready_flag(link)); 418 419 if (!timeout) 420 ret = 0;// ERROR TIMEOUT waiting for response for allocating bw 421 else if (link->dpia_bw_alloc_config.sink_allocated_bw > 0) 422 ret = get_host_router_total_bw(link, HOST_ROUTER_BW_ALLOCATED); 423 } 424 //2. Cold Unplug 425 else if (!link->hpd_status) 426 dpia_bw_alloc_unplug(link); 427 428 out: 429 return ret; 430 } 431 int link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) 432 { 433 int ret = 0; 434 uint8_t timeout = 10; 435 436 if (!get_bw_alloc_proceed_flag(link)) 437 goto out; 438 439 /* 440 * Sometimes stream uses same timing parameters as the already 441 * allocated max sink bw so no need to re-alloc 442 */ 443 if (req_bw != link->dpia_bw_alloc_config.sink_allocated_bw) { 444 set_usb4_req_bw_req(link, req_bw); 445 do { 446 if (!(timeout > 0)) 447 timeout--; 448 else 449 break; 450 udelay(10 * 1000); 451 } while (!get_cm_response_ready_flag(link)); 452 453 if (!timeout) 454 ret = 0;// ERROR TIMEOUT waiting for response for allocating bw 455 else if (link->dpia_bw_alloc_config.sink_allocated_bw > 0) 456 ret = get_host_router_total_bw(link, HOST_ROUTER_BW_ALLOCATED); 457 } 458 459 out: 460 return ret; 461 } 462 bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias) 463 { 464 bool ret = true; 465 int bw_needed_per_hr[MAX_HR_NUM] = { 0, 0 }; 466 uint8_t lowest_dpia_index = 0, dpia_index = 0; 467 uint8_t i; 468 469 if (!num_dpias || num_dpias > MAX_DPIA_NUM) 470 return ret; 471 472 //Get total Host Router BW & Validate against each Host Router max BW 473 for (i = 0; i < num_dpias; ++i) { 474 475 if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled) 476 continue; 477 478 lowest_dpia_index = get_lowest_dpia_index(link[i]); 479 if (link[i]->link_index < lowest_dpia_index) 480 continue; 481 482 dpia_index = (link[i]->link_index - lowest_dpia_index) / 2; 483 bw_needed_per_hr[dpia_index] += bw_needed_per_dpia[i]; 484 if (bw_needed_per_hr[dpia_index] > get_host_router_total_bw(link[i], HOST_ROUTER_BW_ALLOCATED)) { 485 486 ret = false; 487 break; 488 } 489 } 490 491 return ret; 492 } 493