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  */
link_dp_is_bw_alloc_available(struct dc_link * link)47 static bool link_dp_is_bw_alloc_available(struct dc_link *link)
48 {
49 	return (link && link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA
50 		&& link->hpd_status
51 		&& link->dpia_bw_alloc_config.bw_alloc_enabled);
52 }
53 
reset_bw_alloc_struct(struct dc_link * link)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.nrd_max_lane_count = 0;
64 	link->dpia_bw_alloc_config.nrd_max_link_rate = 0;
65 	for (int i = 0; i < MAX_SINKS_PER_LINK; i++)
66 		link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0;
67 	DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index);
68 }
69 
70 #define BW_GRANULARITY_0 4 // 0.25 Gbps
71 #define BW_GRANULARITY_1 2 // 0.5 Gbps
72 #define BW_GRANULARITY_2 1 // 1 Gbps
73 
get_bw_granularity(struct dc_link * link)74 static uint8_t get_bw_granularity(struct dc_link *link)
75 {
76 	uint8_t bw_granularity = 0;
77 
78 	core_link_read_dpcd(
79 			link,
80 			DP_BW_GRANULALITY,
81 			&bw_granularity,
82 			sizeof(uint8_t));
83 
84 	switch (bw_granularity & 0x3) {
85 	case 0:
86 		bw_granularity = BW_GRANULARITY_0;
87 		break;
88 	case 1:
89 		bw_granularity = BW_GRANULARITY_1;
90 		break;
91 	case 2:
92 	default:
93 		bw_granularity = BW_GRANULARITY_2;
94 		break;
95 	}
96 
97 	return bw_granularity;
98 }
99 
get_estimated_bw(struct dc_link * link)100 static int get_estimated_bw(struct dc_link *link)
101 {
102 	uint8_t bw_estimated_bw = 0;
103 
104 	core_link_read_dpcd(
105 			link,
106 			ESTIMATED_BW,
107 			&bw_estimated_bw,
108 			sizeof(uint8_t));
109 
110 	return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
111 }
112 
get_non_reduced_max_link_rate(struct dc_link * link)113 static int get_non_reduced_max_link_rate(struct dc_link *link)
114 {
115 	uint8_t nrd_max_link_rate = 0;
116 
117 	core_link_read_dpcd(
118 			link,
119 			DP_TUNNELING_MAX_LINK_RATE,
120 			&nrd_max_link_rate,
121 			sizeof(uint8_t));
122 
123 	return nrd_max_link_rate;
124 }
125 
get_non_reduced_max_lane_count(struct dc_link * link)126 static int get_non_reduced_max_lane_count(struct dc_link *link)
127 {
128 	uint8_t nrd_max_lane_count = 0;
129 
130 	core_link_read_dpcd(
131 			link,
132 			DP_TUNNELING_MAX_LANE_COUNT,
133 			&nrd_max_lane_count,
134 			sizeof(uint8_t));
135 
136 	return nrd_max_lane_count;
137 }
138 
139 /*
140  * Read all New BW alloc configuration ex: estimated_bw, allocated_bw,
141  * granuality, Driver_ID, CM_Group, & populate the BW allocation structs
142  * for host router and dpia
143  */
init_usb4_bw_struct(struct dc_link * link)144 static void init_usb4_bw_struct(struct dc_link *link)
145 {
146 	reset_bw_alloc_struct(link);
147 
148 	/* init the known values */
149 	link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
150 	link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
151 	link->dpia_bw_alloc_config.nrd_max_link_rate = get_non_reduced_max_link_rate(link);
152 	link->dpia_bw_alloc_config.nrd_max_lane_count = get_non_reduced_max_lane_count(link);
153 
154 	DC_LOG_DEBUG("%s: bw_granularity(%d), estimated_bw(%d)\n",
155 		__func__, link->dpia_bw_alloc_config.bw_granularity,
156 		link->dpia_bw_alloc_config.estimated_bw);
157 	DC_LOG_DEBUG("%s: nrd_max_link_rate(%d), nrd_max_lane_count(%d)\n",
158 		__func__, link->dpia_bw_alloc_config.nrd_max_link_rate,
159 		link->dpia_bw_alloc_config.nrd_max_lane_count);
160 }
161 
get_lowest_dpia_index(struct dc_link * link)162 static uint8_t get_lowest_dpia_index(struct dc_link *link)
163 {
164 	const struct dc *dc_struct = link->dc;
165 	uint8_t idx = 0xFF;
166 	int i;
167 
168 	for (i = 0; i < MAX_LINKS; ++i) {
169 
170 		if (!dc_struct->links[i] ||
171 				dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA)
172 			continue;
173 
174 		if (idx > dc_struct->links[i]->link_index) {
175 			idx = dc_struct->links[i]->link_index;
176 			break;
177 		}
178 	}
179 
180 	return idx;
181 }
182 
183 /*
184  * Get the maximum dp tunnel banwidth of host router
185  *
186  * @dc: pointer to the dc struct instance
187  * @hr_index: host router index
188  *
189  * return: host router maximum dp tunnel bandwidth
190  */
get_host_router_total_dp_tunnel_bw(const struct dc * dc,uint8_t hr_index)191 static int get_host_router_total_dp_tunnel_bw(const struct dc *dc, uint8_t hr_index)
192 {
193 	uint8_t lowest_dpia_index = get_lowest_dpia_index(dc->links[0]);
194 	uint8_t hr_index_temp = 0;
195 	struct dc_link *link_dpia_primary, *link_dpia_secondary;
196 	int total_bw = 0;
197 
198 	for (uint8_t i = 0; i < MAX_LINKS - 1; ++i) {
199 
200 		if (!dc->links[i] || dc->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA)
201 			continue;
202 
203 		hr_index_temp = (dc->links[i]->link_index - lowest_dpia_index) / 2;
204 
205 		if (hr_index_temp == hr_index) {
206 			link_dpia_primary = dc->links[i];
207 			link_dpia_secondary = dc->links[i + 1];
208 
209 			/**
210 			 * If BW allocation enabled on both DPIAs, then
211 			 * HR BW = Estimated(dpia_primary) + Allocated(dpia_secondary)
212 			 * otherwise HR BW = Estimated(bw alloc enabled dpia)
213 			 */
214 			if ((link_dpia_primary->hpd_status &&
215 				link_dpia_primary->dpia_bw_alloc_config.bw_alloc_enabled) &&
216 				(link_dpia_secondary->hpd_status &&
217 				link_dpia_secondary->dpia_bw_alloc_config.bw_alloc_enabled)) {
218 					total_bw += link_dpia_primary->dpia_bw_alloc_config.estimated_bw +
219 						link_dpia_secondary->dpia_bw_alloc_config.allocated_bw;
220 			} else if (link_dpia_primary->hpd_status &&
221 					link_dpia_primary->dpia_bw_alloc_config.bw_alloc_enabled) {
222 				total_bw = link_dpia_primary->dpia_bw_alloc_config.estimated_bw;
223 			} else if (link_dpia_secondary->hpd_status &&
224 				link_dpia_secondary->dpia_bw_alloc_config.bw_alloc_enabled) {
225 				total_bw += link_dpia_secondary->dpia_bw_alloc_config.estimated_bw;
226 			}
227 			break;
228 		}
229 	}
230 
231 	return total_bw;
232 }
233 
234 /*
235  * Cleanup function for when the dpia is unplugged to reset struct
236  * and perform any required clean up
237  *
238  * @link: pointer to the dc_link struct instance
239  *
240  * return: none
241  */
dpia_bw_alloc_unplug(struct dc_link * link)242 static void dpia_bw_alloc_unplug(struct dc_link *link)
243 {
244 	if (link) {
245 		DC_LOG_DEBUG("%s: resetting BW alloc config for link(%d)\n",
246 			__func__, link->link_index);
247 		reset_bw_alloc_struct(link);
248 	}
249 }
250 
link_dpia_send_bw_alloc_request(struct dc_link * link,int req_bw)251 static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
252 {
253 	uint8_t requested_bw;
254 	uint32_t temp;
255 
256 	/* Error check whether request bw greater than allocated */
257 	if (req_bw > link->dpia_bw_alloc_config.estimated_bw) {
258 		DC_LOG_ERROR("%s: Request BW greater than estimated BW for link(%d)\n",
259 			__func__, link->link_index);
260 		req_bw = link->dpia_bw_alloc_config.estimated_bw;
261 	}
262 
263 	temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
264 	requested_bw = temp / Kbps_TO_Gbps;
265 
266 	/* Always make sure to add more to account for floating points */
267 	if (temp % Kbps_TO_Gbps)
268 		++requested_bw;
269 
270 	/* Error check whether requested and allocated are equal */
271 	req_bw = requested_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
272 	if (req_bw && (req_bw == link->dpia_bw_alloc_config.allocated_bw)) {
273 		DC_LOG_ERROR("%s: Request BW equals to allocated BW for link(%d)\n",
274 			__func__, link->link_index);
275 	}
276 
277 	core_link_write_dpcd(link, REQUESTED_BW,
278 		&requested_bw,
279 		sizeof(uint8_t));
280 }
281 
282 // ------------------------------------------------------------------
283 // PUBLIC FUNCTIONS
284 // ------------------------------------------------------------------
link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link * link)285 bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link)
286 {
287 	bool ret = false;
288 	uint8_t response = 0,
289 			bw_support_dpia = 0,
290 			bw_support_cm = 0;
291 
292 	if (!(link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->hpd_status))
293 		goto out;
294 
295 	if (core_link_read_dpcd(
296 			link,
297 			DP_TUNNELING_CAPABILITIES,
298 			&response,
299 			sizeof(uint8_t)) == DC_OK)
300 		bw_support_dpia = (response >> 7) & 1;
301 
302 	if (core_link_read_dpcd(
303 		link,
304 		USB4_DRIVER_BW_CAPABILITY,
305 		&response,
306 		sizeof(uint8_t)) == DC_OK)
307 		bw_support_cm = (response >> 7) & 1;
308 
309 	/* Send request acknowledgment to Turn ON DPTX support */
310 	if (bw_support_cm && bw_support_dpia) {
311 
312 		response = 0x80;
313 		if (core_link_write_dpcd(
314 				link,
315 				DPTX_BW_ALLOCATION_MODE_CONTROL,
316 				&response,
317 				sizeof(uint8_t)) != DC_OK) {
318 			DC_LOG_DEBUG("%s: FAILURE Enabling DPtx BW Allocation Mode Support for link(%d)\n",
319 				__func__, link->link_index);
320 		} else {
321 			// SUCCESS Enabled DPtx BW Allocation Mode Support
322 			DC_LOG_DEBUG("%s: SUCCESS Enabling DPtx BW Allocation Mode Support for link(%d)\n",
323 				__func__, link->link_index);
324 
325 			ret = true;
326 			init_usb4_bw_struct(link);
327 			link->dpia_bw_alloc_config.bw_alloc_enabled = true;
328 
329 			/*
330 			 * During DP tunnel creation, CM preallocates BW and reduces estimated BW of other
331 			 * DPIA. CM release preallocation only when allocation is complete. Do zero alloc
332 			 * to make the CM to release preallocation and update estimated BW correctly for
333 			 * all DPIAs per host router
334 			 */
335 			link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0);
336 		}
337 	}
338 
339 out:
340 	return ret;
341 }
342 
343 /*
344  * Handle DP BW allocation status register
345  *
346  * @link: pointer to the dc_link struct instance
347  * @status: content of DP tunneling status DPCD register
348  *
349  * return: none
350  */
link_dp_dpia_handle_bw_alloc_status(struct dc_link * link,uint8_t status)351 void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status)
352 {
353 	if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) {
354 		DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)",
355 				__func__, link->link_index);
356 	} else if (status & DP_TUNNELING_BW_REQUEST_FAILED) {
357 		link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
358 
359 		DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d)  allocated/estimated BW=%d",
360 				__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
361 
362 		link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw);
363 	} else if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) {
364 		link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
365 
366 		DC_LOG_DEBUG("%s: Estimated BW changed on link(%d)  new estimated BW=%d",
367 				__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
368 	}
369 
370 	core_link_write_dpcd(
371 		link, DP_TUNNELING_STATUS,
372 		&status, sizeof(status));
373 }
374 
375 /*
376  * Handle the DP Bandwidth allocation for DPIA
377  *
378  */
dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link * link,int peak_bw)379 void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
380 {
381 	if (link && link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->dpia_bw_alloc_config.bw_alloc_enabled) {
382 		//1. Hot Plug
383 		if (link->hpd_status && peak_bw > 0) {
384 			// If DP over USB4 then we need to check BW allocation
385 			link->dpia_bw_alloc_config.link_max_bw = peak_bw;
386 
387 			link_dpia_send_bw_alloc_request(link, peak_bw);
388 		}
389 		//2. Cold Unplug
390 		else if (!link->hpd_status)
391 			dpia_bw_alloc_unplug(link);
392 	}
393 }
394 
link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link * link,int req_bw)395 void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
396 {
397 	DC_LOG_DEBUG("%s: ENTER: link(%d), hpd_status(%d), current allocated_bw(%d), req_bw(%d)\n",
398 		__func__, link->link_index, link->hpd_status,
399 		link->dpia_bw_alloc_config.allocated_bw, req_bw);
400 
401 	if (link_dp_is_bw_alloc_available(link))
402 		link_dpia_send_bw_alloc_request(link, req_bw);
403 	else
404 		DC_LOG_DEBUG("%s:  Not able to send the BW Allocation request", __func__);
405 }
406 
dpia_validate_usb4_bw(struct dc_link ** link,int * bw_needed_per_dpia,const unsigned int num_dpias)407 bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias)
408 {
409 	bool ret = true;
410 	int bw_needed_per_hr[MAX_HR_NUM] = { 0, 0 }, host_router_total_dp_bw = 0;
411 	uint8_t lowest_dpia_index, i, hr_index;
412 
413 	if (!num_dpias || num_dpias > MAX_DPIA_NUM)
414 		return ret;
415 
416 	lowest_dpia_index = get_lowest_dpia_index(link[0]);
417 
418 	/* get total Host Router BW with granularity for the given modes */
419 	for (i = 0; i < num_dpias; ++i) {
420 		int granularity_Gbps = 0;
421 		int bw_granularity = 0;
422 
423 		if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled)
424 			continue;
425 
426 		if (link[i]->link_index < lowest_dpia_index)
427 			continue;
428 
429 		granularity_Gbps = (Kbps_TO_Gbps / link[i]->dpia_bw_alloc_config.bw_granularity);
430 		bw_granularity = (bw_needed_per_dpia[i] / granularity_Gbps) * granularity_Gbps +
431 				((bw_needed_per_dpia[i] % granularity_Gbps) ? granularity_Gbps : 0);
432 
433 		hr_index = (link[i]->link_index - lowest_dpia_index) / 2;
434 		bw_needed_per_hr[hr_index] += bw_granularity;
435 	}
436 
437 	/* validate against each Host Router max BW */
438 	for (hr_index = 0; hr_index < MAX_HR_NUM; ++hr_index) {
439 		if (bw_needed_per_hr[hr_index]) {
440 			host_router_total_dp_bw = get_host_router_total_dp_tunnel_bw(link[0]->dc, hr_index);
441 			if (bw_needed_per_hr[hr_index] > host_router_total_dp_bw) {
442 				ret = false;
443 				break;
444 			}
445 		}
446 	}
447 
448 	return ret;
449 }
450 
link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link * link)451 int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link)
452 {
453 	int dp_overhead = 0, link_mst_overhead = 0;
454 
455 	if (!link_dp_is_bw_alloc_available(link))
456 		return dp_overhead;
457 
458 	/* if its mst link, add MTPH overhead */
459 	if ((link->type == dc_connection_mst_branch) &&
460 		!link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
461 		/* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH
462 		 * MST overhead is 1/64 of link bandwidth (excluding any overhead)
463 		 */
464 		const struct dc_link_settings *link_cap =
465 			dc_link_get_link_cap(link);
466 		uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate *
467 					   (uint32_t)link_cap->lane_count *
468 					   LINK_RATE_REF_FREQ_IN_KHZ * 8;
469 		link_mst_overhead = (link_bw_in_kbps / 64) + ((link_bw_in_kbps % 64) ? 1 : 0);
470 	}
471 
472 	/* add all the overheads */
473 	dp_overhead = link_mst_overhead;
474 
475 	return dp_overhead;
476 }
477