xref: /linux/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c (revision 546b928da0427b0d6c663cbb992bd7bfa9ac7971)
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 #define MST_TIME_SLOT_COUNT 64
39 
40 // ------------------------------------------------------------------
41 // PRIVATE FUNCTIONS
42 // ------------------------------------------------------------------
43 /*
44  * Always Check the following:
45  *  - Is it USB4 link?
46  *  - Is HPD HIGH?
47  *  - Is BW Allocation Support Mode enabled on DP-Tx?
48  */
link_dp_is_bw_alloc_available(struct dc_link * link)49 static bool link_dp_is_bw_alloc_available(struct dc_link *link)
50 {
51 	return (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
52 		&& link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc
53 		&& link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support);
54 }
55 
reset_bw_alloc_struct(struct dc_link * link)56 static void reset_bw_alloc_struct(struct dc_link *link)
57 {
58 	link->dpia_bw_alloc_config.bw_alloc_enabled = false;
59 	link->dpia_bw_alloc_config.link_verified_bw = 0;
60 	link->dpia_bw_alloc_config.link_max_bw = 0;
61 	link->dpia_bw_alloc_config.allocated_bw = 0;
62 	link->dpia_bw_alloc_config.estimated_bw = 0;
63 	link->dpia_bw_alloc_config.bw_granularity = 0;
64 	link->dpia_bw_alloc_config.dp_overhead = 0;
65 	link->dpia_bw_alloc_config.nrd_max_lane_count = 0;
66 	link->dpia_bw_alloc_config.nrd_max_link_rate = 0;
67 	for (int i = 0; i < MAX_SINKS_PER_LINK; i++)
68 		link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0;
69 	DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index);
70 }
71 
72 #define BW_GRANULARITY_0 4 // 0.25 Gbps
73 #define BW_GRANULARITY_1 2 // 0.5 Gbps
74 #define BW_GRANULARITY_2 1 // 1 Gbps
75 
get_bw_granularity(struct dc_link * link)76 static uint8_t get_bw_granularity(struct dc_link *link)
77 {
78 	uint8_t bw_granularity = 0;
79 
80 	core_link_read_dpcd(
81 			link,
82 			DP_BW_GRANULALITY,
83 			&bw_granularity,
84 			sizeof(uint8_t));
85 
86 	switch (bw_granularity & 0x3) {
87 	case 0:
88 		bw_granularity = BW_GRANULARITY_0;
89 		break;
90 	case 1:
91 		bw_granularity = BW_GRANULARITY_1;
92 		break;
93 	case 2:
94 	default:
95 		bw_granularity = BW_GRANULARITY_2;
96 		break;
97 	}
98 
99 	return bw_granularity;
100 }
101 
get_estimated_bw(struct dc_link * link)102 static int get_estimated_bw(struct dc_link *link)
103 {
104 	uint8_t bw_estimated_bw = 0;
105 
106 	if (link->dpia_bw_alloc_config.bw_granularity == 0) {
107 		DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__);
108 		return 0;
109 	}
110 
111 	core_link_read_dpcd(
112 			link,
113 			ESTIMATED_BW,
114 			&bw_estimated_bw,
115 			sizeof(uint8_t));
116 
117 	return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
118 }
119 
get_non_reduced_max_link_rate(struct dc_link * link)120 static uint8_t get_non_reduced_max_link_rate(struct dc_link *link)
121 {
122 	uint8_t nrd_max_link_rate = 0;
123 
124 	core_link_read_dpcd(
125 			link,
126 			DP_TUNNELING_MAX_LINK_RATE,
127 			&nrd_max_link_rate,
128 			sizeof(uint8_t));
129 
130 	return nrd_max_link_rate;
131 }
132 
get_non_reduced_max_lane_count(struct dc_link * link)133 static uint8_t get_non_reduced_max_lane_count(struct dc_link *link)
134 {
135 	uint8_t nrd_max_lane_count = 0;
136 
137 	core_link_read_dpcd(
138 			link,
139 			DP_TUNNELING_MAX_LANE_COUNT,
140 			&nrd_max_lane_count,
141 			sizeof(uint8_t));
142 
143 	return nrd_max_lane_count;
144 }
145 
146 /*
147  * Read all New BW alloc configuration ex: estimated_bw, allocated_bw,
148  * granuality, Driver_ID, CM_Group, & populate the BW allocation structs
149  * for host router and dpia
150  */
retrieve_usb4_dp_bw_allocation_info(struct dc_link * link)151 static void retrieve_usb4_dp_bw_allocation_info(struct dc_link *link)
152 {
153 	reset_bw_alloc_struct(link);
154 
155 	/* init the known values */
156 	link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
157 	link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
158 	link->dpia_bw_alloc_config.nrd_max_link_rate = get_non_reduced_max_link_rate(link);
159 	link->dpia_bw_alloc_config.nrd_max_lane_count = get_non_reduced_max_lane_count(link);
160 
161 	DC_LOG_DEBUG("%s: bw_granularity(%d), estimated_bw(%d)\n",
162 		__func__, link->dpia_bw_alloc_config.bw_granularity,
163 		link->dpia_bw_alloc_config.estimated_bw);
164 	DC_LOG_DEBUG("%s: nrd_max_link_rate(%d), nrd_max_lane_count(%d)\n",
165 		__func__, link->dpia_bw_alloc_config.nrd_max_link_rate,
166 		link->dpia_bw_alloc_config.nrd_max_lane_count);
167 }
168 
169 /*
170  * Cleanup function for when the dpia is unplugged to reset struct
171  * and perform any required clean up
172  *
173  * @link: pointer to the dc_link struct instance
174  *
175  * return: none
176  */
dpia_bw_alloc_unplug(struct dc_link * link)177 static void dpia_bw_alloc_unplug(struct dc_link *link)
178 {
179 	if (link) {
180 		DC_LOG_DEBUG("%s: resetting BW alloc config for link(%d)\n",
181 			__func__, link->link_index);
182 		reset_bw_alloc_struct(link);
183 	}
184 }
185 
link_dpia_send_bw_alloc_request(struct dc_link * link,int req_bw)186 static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
187 {
188 	uint32_t request_reg_val;
189 	uint32_t temp, request_bw;
190 
191 	if (link->dpia_bw_alloc_config.bw_granularity == 0) {
192 		DC_LOG_ERROR("%s:  Link[%d]:  bw_granularity is zero!", __func__, link->link_index);
193 		return;
194 	}
195 
196 	temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
197 	request_reg_val = temp / Kbps_TO_Gbps;
198 
199 	/* Always make sure to add more to account for floating points */
200 	if (temp % Kbps_TO_Gbps)
201 		++request_reg_val;
202 
203 	request_bw = request_reg_val * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
204 
205 	if (request_bw > (uint32_t)link->dpia_bw_alloc_config.estimated_bw) {
206 		DC_LOG_ERROR("%s:  Link[%d]:  Request BW (%d --> %d) > Estimated BW (%d)... Set to Estimated BW!",
207 				__func__, link->link_index,
208 				req_bw, request_bw, link->dpia_bw_alloc_config.estimated_bw);
209 		req_bw = link->dpia_bw_alloc_config.estimated_bw;
210 
211 		temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
212 		request_reg_val = temp / Kbps_TO_Gbps;
213 		if (temp % Kbps_TO_Gbps)
214 			++request_reg_val;
215 	}
216 
217 	link->dpia_bw_alloc_config.allocated_bw = request_bw;
218 	DC_LOG_DC("%s:  Link[%d]:  Request BW:  %d", __func__, link->link_index, request_bw);
219 
220 	uint8_t requested_bw_dpcd = (uint8_t)request_reg_val;
221 	core_link_write_dpcd(link, REQUESTED_BW, &requested_bw_dpcd,
222 		sizeof(uint8_t));
223 }
224 
225 // ------------------------------------------------------------------
226 // PUBLIC FUNCTIONS
227 // ------------------------------------------------------------------
link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link * link)228 bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link)
229 {
230 	bool ret = false;
231 	uint8_t val;
232 
233 	val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ;
234 
235 	if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) {
236 		DC_LOG_DEBUG("%s:  link[%d] DPTX BW allocation mode enabled", __func__, link->link_index);
237 
238 		retrieve_usb4_dp_bw_allocation_info(link);
239 
240 		if (
241 				link->dpia_bw_alloc_config.nrd_max_link_rate
242 				&& link->dpia_bw_alloc_config.nrd_max_lane_count) {
243 			link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate;
244 			link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count;
245 		}
246 
247 		link->dpia_bw_alloc_config.bw_alloc_enabled = true;
248 		ret = true;
249 
250 		if (link->dc->debug.dpia_debug.bits.enable_usb4_bw_zero_alloc_patch) {
251 			/*
252 			 * During DP tunnel creation, the CM preallocates BW
253 			 * and reduces the estimated BW of other DPIAs.
254 			 * The CM releases the preallocation only when the allocation is complete.
255 			 * Perform a zero allocation to make the CM release the preallocation
256 			 * and correctly update the estimated BW for all DPIAs per host router.
257 			 */
258 			link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0);
259 		}
260 	} else
261 		DC_LOG_DEBUG("%s:  link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index);
262 
263 	return ret;
264 }
265 
266 /*
267  * Handle DP BW allocation status register
268  *
269  * @link: pointer to the dc_link struct instance
270  * @status: content of DP tunneling status DPCD register
271  *
272  * return: none
273  */
link_dp_dpia_handle_bw_alloc_status(struct dc_link * link,uint8_t status)274 void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status)
275 {
276 	if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) {
277 		DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)",
278 				__func__, link->link_index);
279 	}
280 
281 	if (status & DP_TUNNELING_BW_REQUEST_FAILED) {
282 		DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d)  allocated/estimated BW=%d",
283 				__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
284 
285 		link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw);
286 	}
287 
288 	if (status & DP_TUNNELING_BW_ALLOC_CAP_CHANGED) {
289 		link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
290 
291 		DC_LOG_DEBUG("%s: Granularity changed on link(%d)  new granularity=%d",
292 				__func__, link->link_index, link->dpia_bw_alloc_config.bw_granularity);
293 	}
294 
295 	if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) {
296 		link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
297 
298 		DC_LOG_DEBUG("%s: Estimated BW changed on link(%d)  new estimated BW=%d",
299 				__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
300 	}
301 
302 	core_link_write_dpcd(
303 		link, DP_TUNNELING_STATUS,
304 		&status, sizeof(status));
305 }
306 
307 /*
308  * Handle the DP Bandwidth allocation for DPIA
309  *
310  */
dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link * link,int peak_bw)311 void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
312 {
313 	if (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
314 			&& link->dpia_bw_alloc_config.bw_alloc_enabled) {
315 		if (peak_bw > 0) {
316 			// If DP over USB4 then we need to check BW allocation
317 			link->dpia_bw_alloc_config.link_max_bw = peak_bw;
318 
319 			link_dpia_send_bw_alloc_request(link, peak_bw);
320 		} else
321 			dpia_bw_alloc_unplug(link);
322 	}
323 }
324 
link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link * link,int req_bw)325 void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
326 {
327 	link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
328 
329 	DC_LOG_DEBUG("%s: ENTER: link[%d] hpd(%d)  Allocated_BW: %d  Estimated_BW: %d  Req_BW: %d",
330 		__func__, link->link_index, link->hpd_status,
331 		link->dpia_bw_alloc_config.allocated_bw,
332 		link->dpia_bw_alloc_config.estimated_bw,
333 		req_bw);
334 
335 	if (link_dp_is_bw_alloc_available(link))
336 		link_dpia_send_bw_alloc_request(link, req_bw);
337 	else
338 		DC_LOG_DEBUG("%s:  BW Allocation mode not available", __func__);
339 }
340 
link_dpia_get_dp_overhead(const struct dc_link * link)341 uint32_t link_dpia_get_dp_overhead(const struct dc_link *link)
342 {
343 	uint32_t link_dp_overhead = 0;
344 
345 	if ((link->type == dc_connection_mst_branch) &&
346 				!link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
347 		/* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH
348 		 * MST overhead is 1/64 of link bandwidth (excluding any overhead)
349 		 */
350 		const struct dc_link_settings *link_cap = dc_link_get_link_cap(link);
351 
352 		if (link_cap) {
353 			uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate *
354 					   (uint32_t)link_cap->lane_count *
355 					   LINK_RATE_REF_FREQ_IN_KHZ * 8;
356 			link_dp_overhead = (link_bw_in_kbps / MST_TIME_SLOT_COUNT)
357 						+ ((link_bw_in_kbps % MST_TIME_SLOT_COUNT) ? 1 : 0);
358 		}
359 	}
360 
361 	return link_dp_overhead;
362 }
363 
364 /*
365  * Aggregates the DPIA bandwidth usage for the respective USB4 Router.
366  * And then validate if the required bandwidth is within the router's capacity.
367  *
368  * @dc_validation_dpia_set: pointer to the dc_validation_dpia_set
369  * @count: number of DPIA validation sets
370  *
371  * return: true if validation is succeeded
372  */
link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set * dpia_link_sets,uint8_t count)373 bool link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set *dpia_link_sets, uint8_t count)
374 {
375 	uint32_t granularity_Gbps;
376 	const struct dc_link *link;
377 	uint32_t link_bw_granularity;
378 	uint32_t link_required_bw;
379 	struct usb4_router_validation_set router_sets[MAX_HOST_ROUTERS_NUM] = { 0 };
380 	uint8_t i;
381 	bool is_success = true;
382 	uint8_t router_count = 0;
383 
384 	if ((dpia_link_sets == NULL) || (count == 0))
385 		return is_success;
386 
387 	// Iterate through each DP tunneling link (DPIA).
388 	// Aggregate its bandwidth requirements onto the respective USB4 router.
389 	for (i = 0; i < count; i++) {
390 		link = dpia_link_sets[i].link;
391 		link_required_bw = dpia_link_sets[i].required_bw;
392 		const struct dc_tunnel_settings *dp_tunnel_settings = dpia_link_sets[i].tunnel_settings;
393 
394 		if ((link == NULL) || (dp_tunnel_settings == NULL) || dp_tunnel_settings->bw_granularity == 0)
395 			break;
396 
397 		if (link->type == dc_connection_mst_branch)
398 			link_required_bw += link_dpia_get_dp_overhead(link);
399 
400 		granularity_Gbps = (Kbps_TO_Gbps / dp_tunnel_settings->bw_granularity);
401 		link_bw_granularity = (link_required_bw / granularity_Gbps) * granularity_Gbps +
402 				((link_required_bw % granularity_Gbps) ? granularity_Gbps : 0);
403 
404 		// Find or add the USB4 router associated with the current DPIA link
405 		for (uint8_t j = 0; j < MAX_HOST_ROUTERS_NUM; j++) {
406 			if (router_sets[j].is_valid == false) {
407 				router_sets[j].is_valid = true;
408 				router_sets[j].cm_id = dp_tunnel_settings->cm_id;
409 				router_count++;
410 			}
411 
412 			if (router_sets[j].cm_id == dp_tunnel_settings->cm_id) {
413 				uint32_t remaining_bw =
414 					dp_tunnel_settings->estimated_bw - dp_tunnel_settings->allocated_bw;
415 
416 				router_sets[j].allocated_bw += dp_tunnel_settings->allocated_bw;
417 
418 				if (remaining_bw > router_sets[j].remaining_bw)
419 					router_sets[j].remaining_bw = remaining_bw;
420 
421 				// Get the max estimated BW within the same CM_ID
422 				if (dp_tunnel_settings->estimated_bw > router_sets[j].estimated_bw)
423 					router_sets[j].estimated_bw = dp_tunnel_settings->estimated_bw;
424 
425 				router_sets[j].required_bw += link_bw_granularity;
426 				router_sets[j].dpia_count++;
427 				break;
428 			}
429 		}
430 	}
431 
432 	// Validate bandwidth for each unique router found.
433 	for (i = 0; i < router_count; i++) {
434 		uint32_t total_bw = 0;
435 
436 		if (router_sets[i].is_valid == false)
437 			break;
438 
439 		// Determine the total available bandwidth for the current router based on aggregated data
440 		if ((router_sets[i].dpia_count == 1) || (router_sets[i].allocated_bw == 0))
441 			total_bw = router_sets[i].estimated_bw;
442 		else
443 			total_bw = router_sets[i].allocated_bw + router_sets[i].remaining_bw;
444 
445 		if (router_sets[i].required_bw > total_bw) {
446 			is_success = false;
447 			break;
448 		}
449 	}
450 
451 	return is_success;
452 }
453 
454