Merge tag 'kvmarm-fixes-6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmar...
[linux-2.6-block.git] / 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 // ------------------------------------------------------------------
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
54 static 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
75 static 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
101 static 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
114 static 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
127 static 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  */
145 static 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
163 static 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  */
192 static 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  */
243 static 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
252 static 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 == 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  */
293 static 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 // ------------------------------------------------------------------
301 bool 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
347 out:
348         return ret;
349 }
350
351 void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result)
352 {
353         int bw_needed = 0;
354         int estimated = 0;
355
356         if (!get_bw_alloc_proceed_flag((link)))
357                 return;
358
359         switch (result) {
360
361         case DPIA_BW_REQ_FAILED:
362
363                 /*
364                  * Ideally, we shouldn't run into this case as we always validate available
365                  * bandwidth and request within that limit
366                  */
367                 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
368
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);
373
374                 /* Update the new Estimated BW value updated by CM */
375                 link->dpia_bw_alloc_config.estimated_bw = estimated;
376
377                 /* Allocate the previously requested bandwidth */
378                 set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw);
379
380                 /*
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.
386                  */
387                 break;
388
389         case DPIA_BW_REQ_SUCCESS:
390
391                 bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
392
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);
397
398                 link->dpia_bw_alloc_config.allocated_bw = bw_needed;
399
400                 link->dpia_bw_alloc_config.response_ready = true;
401                 break;
402
403         case DPIA_EST_BW_CHANGED:
404
405                 estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
406
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);
411
412                 link->dpia_bw_alloc_config.estimated_bw = estimated;
413                 break;
414
415         case DPIA_BW_ALLOC_CAPS_CHANGED:
416
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;
420                 break;
421         }
422 }
423 int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
424 {
425         int ret = 0;
426         uint8_t timeout = 10;
427
428         if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type
429                         && link->dpia_bw_alloc_config.bw_alloc_enabled))
430                 goto out;
431
432         //1. Hot Plug
433         if (link->hpd_status && peak_bw > 0) {
434
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);
438
439                 do {
440                         if (timeout > 0)
441                                 timeout--;
442                         else
443                                 break;
444                         msleep(10);
445                 } while (!get_cm_response_ready_flag(link));
446
447                 if (!timeout)
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;
451         }
452         //2. Cold Unplug
453         else if (!link->hpd_status)
454                 dpia_bw_alloc_unplug(link);
455
456 out:
457         return ret;
458 }
459 bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
460 {
461         bool ret = false;
462         uint8_t timeout = 10;
463
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);
467
468         if (!get_bw_alloc_proceed_flag(link))
469                 goto out;
470
471         set_usb4_req_bw_req(link, req_bw);
472         do {
473                 if (timeout > 0)
474                         timeout--;
475                 else
476                         break;
477                 msleep(10);
478         } while (!get_cm_response_ready_flag(link));
479
480         if (timeout)
481                 ret = true;
482
483 out:
484         DC_LOG_DEBUG("%s: EXIT: timeout(%d), ret(%d)\n", __func__, timeout, ret);
485         return ret;
486 }
487
488 bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias)
489 {
490         bool ret = true;
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;
493
494         if (!num_dpias || num_dpias > MAX_DPIA_NUM)
495                 return ret;
496
497         lowest_dpia_index = get_lowest_dpia_index(link[0]);
498
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;
503
504                 if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled)
505                         continue;
506
507                 if (link[i]->link_index < lowest_dpia_index)
508                         continue;
509
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);
513
514                 hr_index = (link[i]->link_index - lowest_dpia_index) / 2;
515                 bw_needed_per_hr[hr_index] += bw_granularity;
516         }
517
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) {
523                                 ret = false;
524                                 break;
525                         }
526                 }
527         }
528
529         return ret;
530 }
531
532 int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link)
533 {
534         int dp_overhead = 0, link_mst_overhead = 0;
535
536         if (!get_bw_alloc_proceed_flag((link)))
537                 return dp_overhead;
538
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)
544                  */
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);
551         }
552
553         /* add all the overheads */
554         dp_overhead = link_mst_overhead;
555
556         return dp_overhead;
557 }