3 * Copyright 2022 Advanced Micro Devices, Inc.
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
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"
36 #define Kbps_TO_Gbps (1000 * 1000)
38 // ------------------------------------------------------------------
40 // ------------------------------------------------------------------
42 * Always Check the following:
45 * - Is BW Allocation Support Mode enabled on DP-Tx?
47 static bool get_bw_alloc_proceed_flag(struct dc_link *tmp)
49 return (tmp && DISPLAY_ENDPOINT_USB4_DPIA == tmp->ep_type
51 && tmp->dpia_bw_alloc_config.bw_alloc_enabled);
54 static void reset_bw_alloc_struct(struct dc_link *link)
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);
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
75 static uint8_t get_bw_granularity(struct dc_link *link)
77 uint8_t bw_granularity = 0;
85 switch (bw_granularity & 0x3) {
87 bw_granularity = BW_GRANULARITY_0;
90 bw_granularity = BW_GRANULARITY_1;
94 bw_granularity = BW_GRANULARITY_2;
98 return bw_granularity;
101 static int get_estimated_bw(struct dc_link *link)
103 uint8_t bw_estimated_bw = 0;
111 return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
114 static int get_non_reduced_max_link_rate(struct dc_link *link)
116 uint8_t nrd_max_link_rate = 0;
120 DP_TUNNELING_MAX_LINK_RATE,
124 return nrd_max_link_rate;
127 static int get_non_reduced_max_lane_count(struct dc_link *link)
129 uint8_t nrd_max_lane_count = 0;
133 DP_TUNNELING_MAX_LANE_COUNT,
137 return nrd_max_lane_count;
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
145 static void init_usb4_bw_struct(struct dc_link *link)
147 reset_bw_alloc_struct(link);
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);
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);
163 static uint8_t get_lowest_dpia_index(struct dc_link *link)
165 const struct dc *dc_struct = link->dc;
169 for (i = 0; i < MAX_PIPES * 2; ++i) {
171 if (!dc_struct->links[i] ||
172 dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA)
175 if (idx > dc_struct->links[i]->link_index) {
176 idx = dc_struct->links[i]->link_index;
185 * Get the maximum dp tunnel banwidth of host router
187 * @dc: pointer to the dc struct instance
188 * @hr_index: host router index
190 * return: host router maximum dp tunnel bandwidth
192 static int get_host_router_total_dp_tunnel_bw(const struct dc *dc, uint8_t hr_index)
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;
199 for (uint8_t i = 0; i < (MAX_PIPES * 2) - 1; ++i) {
201 if (!dc->links[i] || dc->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA)
204 hr_index_temp = (dc->links[i]->link_index - lowest_dpia_index) / 2;
206 if (hr_index_temp == hr_index) {
207 link_dpia_primary = dc->links[i];
208 link_dpia_secondary = dc->links[i + 1];
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)
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;
236 * Cleanup function for when the dpia is unplugged to reset struct
237 * and perform any required clean up
239 * @link: pointer to the dc_link struct instance
243 static void dpia_bw_alloc_unplug(struct dc_link *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);
252 static void set_usb4_req_bw_req(struct dc_link *link, int req_bw)
254 uint8_t requested_bw;
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;
264 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
265 requested_bw = temp / Kbps_TO_Gbps;
267 /* Always make sure to add more to account for floating points */
268 if (temp % Kbps_TO_Gbps)
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 == 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);
278 link->dpia_bw_alloc_config.response_ready = false; // Reset flag
279 core_link_write_dpcd(
287 * Return the response_ready flag from dc_link struct
289 * @link: pointer to the dc_link struct instance
291 * return: response_ready flag from dc_link struct
293 static bool get_cm_response_ready_flag(struct dc_link *link)
295 return link->dpia_bw_alloc_config.response_ready;
298 // ------------------------------------------------------------------
300 // ------------------------------------------------------------------
301 bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link)
304 uint8_t response = 0,
308 if (!(link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->hpd_status))
311 if (core_link_read_dpcd(
313 DP_TUNNELING_CAPABILITIES,
315 sizeof(uint8_t)) == DC_OK)
316 bw_support_dpia = (response >> 7) & 1;
318 if (core_link_read_dpcd(
320 USB4_DRIVER_BW_CAPABILITY,
322 sizeof(uint8_t)) == DC_OK)
323 bw_support_cm = (response >> 7) & 1;
325 /* Send request acknowledgment to Turn ON DPTX support */
326 if (bw_support_cm && bw_support_dpia) {
329 if (core_link_write_dpcd(
331 DPTX_BW_ALLOCATION_MODE_CONTROL,
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);
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);
342 init_usb4_bw_struct(link);
343 link->dpia_bw_alloc_config.bw_alloc_enabled = true;
351 void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result)
356 if (!get_bw_alloc_proceed_flag((link)))
361 case DPIA_BW_REQ_FAILED:
364 * Ideally, we shouldn't run into this case as we always validate available
365 * bandwidth and request within that limit
367 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
369 DC_LOG_ERROR("%s: BW REQ FAILURE for DP-TX Request for link(%d)\n",
370 __func__, link->link_index);
371 DC_LOG_ERROR("%s: current estimated_bw(%d), new estimated_bw(%d)\n",
372 __func__, link->dpia_bw_alloc_config.estimated_bw, estimated);
374 /* Update the new Estimated BW value updated by CM */
375 link->dpia_bw_alloc_config.estimated_bw = estimated;
377 /* Allocate the previously requested bandwidth */
378 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw);
381 * If FAIL then it is either:
382 * 1. Due to DP-Tx trying to allocate more than available i.e. it failed locally
383 * => get estimated and allocate that
384 * 2. Due to the fact that DP-Tx tried to allocated ESTIMATED BW and failed then
385 * CM will have to update 0xE0023 with new ESTIMATED BW value.
389 case DPIA_BW_REQ_SUCCESS:
391 bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
393 DC_LOG_DEBUG("%s: BW REQ SUCCESS for DP-TX Request for link(%d)\n",
394 __func__, link->link_index);
395 DC_LOG_DEBUG("%s: current allocated_bw(%d), new allocated_bw(%d)\n",
396 __func__, link->dpia_bw_alloc_config.allocated_bw, bw_needed);
398 link->dpia_bw_alloc_config.allocated_bw = bw_needed;
400 link->dpia_bw_alloc_config.response_ready = true;
403 case DPIA_EST_BW_CHANGED:
405 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
407 DC_LOG_DEBUG("%s: ESTIMATED BW CHANGED for link(%d)\n",
408 __func__, link->link_index);
409 DC_LOG_DEBUG("%s: current estimated_bw(%d), new estimated_bw(%d)\n",
410 __func__, link->dpia_bw_alloc_config.estimated_bw, estimated);
412 link->dpia_bw_alloc_config.estimated_bw = estimated;
415 case DPIA_BW_ALLOC_CAPS_CHANGED:
417 DC_LOG_ERROR("%s: BW ALLOC CAPABILITY CHANGED to Disabled for link(%d)\n",
418 __func__, link->link_index);
419 link->dpia_bw_alloc_config.bw_alloc_enabled = false;
423 int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
426 uint8_t timeout = 10;
428 if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type
429 && link->dpia_bw_alloc_config.bw_alloc_enabled))
433 if (link->hpd_status && peak_bw > 0) {
435 // If DP over USB4 then we need to check BW allocation
436 link->dpia_bw_alloc_config.link_max_bw = peak_bw;
437 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.link_max_bw);
445 } while (!get_cm_response_ready_flag(link));
448 ret = 0;// ERROR TIMEOUT waiting for response for allocating bw
449 else if (link->dpia_bw_alloc_config.allocated_bw > 0)
450 ret = link->dpia_bw_alloc_config.allocated_bw;
453 else if (!link->hpd_status)
454 dpia_bw_alloc_unplug(link);
459 bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
462 uint8_t timeout = 10;
464 DC_LOG_DEBUG("%s: ENTER: link(%d), hpd_status(%d), current allocated_bw(%d), req_bw(%d)\n",
465 __func__, link->link_index, link->hpd_status,
466 link->dpia_bw_alloc_config.allocated_bw, req_bw);
468 if (!get_bw_alloc_proceed_flag(link))
471 set_usb4_req_bw_req(link, req_bw);
478 } while (!get_cm_response_ready_flag(link));
484 DC_LOG_DEBUG("%s: EXIT: timeout(%d), ret(%d)\n", __func__, timeout, ret);
488 bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias)
491 int bw_needed_per_hr[MAX_HR_NUM] = { 0, 0 }, host_router_total_dp_bw = 0;
492 uint8_t lowest_dpia_index, i, hr_index;
494 if (!num_dpias || num_dpias > MAX_DPIA_NUM)
497 lowest_dpia_index = get_lowest_dpia_index(link[0]);
499 /* get total Host Router BW with granularity for the given modes */
500 for (i = 0; i < num_dpias; ++i) {
501 int granularity_Gbps = 0;
502 int bw_granularity = 0;
504 if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled)
507 if (link[i]->link_index < lowest_dpia_index)
510 granularity_Gbps = (Kbps_TO_Gbps / link[i]->dpia_bw_alloc_config.bw_granularity);
511 bw_granularity = (bw_needed_per_dpia[i] / granularity_Gbps) * granularity_Gbps +
512 ((bw_needed_per_dpia[i] % granularity_Gbps) ? granularity_Gbps : 0);
514 hr_index = (link[i]->link_index - lowest_dpia_index) / 2;
515 bw_needed_per_hr[hr_index] += bw_granularity;
518 /* validate against each Host Router max BW */
519 for (hr_index = 0; hr_index < MAX_HR_NUM; ++hr_index) {
520 if (bw_needed_per_hr[hr_index]) {
521 host_router_total_dp_bw = get_host_router_total_dp_tunnel_bw(link[0]->dc, hr_index);
522 if (bw_needed_per_hr[hr_index] > host_router_total_dp_bw) {
532 int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link)
534 int dp_overhead = 0, link_mst_overhead = 0;
536 if (!get_bw_alloc_proceed_flag((link)))
539 /* if its mst link, add MTPH overhead */
540 if ((link->type == dc_connection_mst_branch) &&
541 !link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
542 /* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH
543 * MST overhead is 1/64 of link bandwidth (excluding any overhead)
545 const struct dc_link_settings *link_cap =
546 dc_link_get_link_cap(link);
547 uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate *
548 (uint32_t)link_cap->lane_count *
549 LINK_RATE_REF_FREQ_IN_KHZ * 8;
550 link_mst_overhead = (link_bw_in_kbps / 64) + ((link_bw_in_kbps % 64) ? 1 : 0);
553 /* add all the overheads */
554 dp_overhead = link_mst_overhead;