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 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 }
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 }
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 }
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 }
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  */
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 }
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  */
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  */
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 }
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  */
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 // ------------------------------------------------------------------
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 }
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 }
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 }
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 }
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