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 */
get_bw_alloc_proceed_flag(struct dc_link * tmp)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 }
reset_bw_alloc_struct(struct dc_link * link)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 }
get_bw_granularity(struct dc_link * link)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 }
get_estimated_bw(struct dc_link * link)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 }
allocate_usb4_bw(int * stream_allocated_bw,int bw_needed,struct dc_link * link)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 }
deallocate_usb4_bw(int * stream_allocated_bw,int bw_to_dealloc,struct dc_link * link)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 */
init_usb4_bw_struct(struct dc_link * link)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 }
get_lowest_dpia_index(struct dc_link * link)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 */
get_host_router_total_bw(struct dc_link * link,uint8_t type)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 */
dpia_bw_alloc_unplug(struct dc_link * link)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 }
set_usb4_req_bw_req(struct dc_link * link,int req_bw)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 */
get_cm_response_ready_flag(struct dc_link * link)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 // ------------------------------------------------------------------
link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link * link)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 }
dpia_handle_bw_alloc_response(struct dc_link * link,uint8_t bw,uint8_t result)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 }
dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link * link,int peak_bw)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 }
link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link * link,int req_bw)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 }
dpia_validate_usb4_bw(struct dc_link ** link,int * bw_needed_per_dpia,const unsigned int num_dpias)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