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 */
47static 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
54static 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
75static 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
101static 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
114static 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
127static 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 */
145static 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
163static 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 */
192static 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 */
243static 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
252static 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 && (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 */
293static 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// ------------------------------------------------------------------
301bool 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			 * During DP tunnel creation, CM preallocates BW and reduces estimated BW of other
347			 * DPIA. CM release preallocation only when allocation is complete. Do zero alloc
348			 * to make the CM to release preallocation and update estimated BW correctly for
349			 * all DPIAs per host router
350			 */
351			link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0);
352		}
353	}
354
355out:
356	return ret;
357}
358
359void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result)
360{
361	int bw_needed = 0;
362	int estimated = 0;
363
364	if (!get_bw_alloc_proceed_flag((link)))
365		return;
366
367	switch (result) {
368
369	case DPIA_BW_REQ_FAILED:
370
371		/*
372		 * Ideally, we shouldn't run into this case as we always validate available
373		 * bandwidth and request within that limit
374		 */
375		estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
376
377		DC_LOG_ERROR("%s: BW REQ FAILURE for DP-TX Request for link(%d)\n",
378			__func__, link->link_index);
379		DC_LOG_ERROR("%s: current estimated_bw(%d), new estimated_bw(%d)\n",
380			__func__, link->dpia_bw_alloc_config.estimated_bw, estimated);
381
382		/* Update the new Estimated BW value updated by CM */
383		link->dpia_bw_alloc_config.estimated_bw = estimated;
384
385		/* Allocate the previously requested bandwidth */
386		set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw);
387
388		/*
389		 * If FAIL then it is either:
390		 * 1. Due to DP-Tx trying to allocate more than available i.e. it failed locally
391		 *    => get estimated and allocate that
392		 * 2. Due to the fact that DP-Tx tried to allocated ESTIMATED BW and failed then
393		 *    CM will have to update 0xE0023 with new ESTIMATED BW value.
394		 */
395		break;
396
397	case DPIA_BW_REQ_SUCCESS:
398
399		bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
400
401		DC_LOG_DEBUG("%s: BW REQ SUCCESS for DP-TX Request for link(%d)\n",
402			__func__, link->link_index);
403		DC_LOG_DEBUG("%s: current allocated_bw(%d), new allocated_bw(%d)\n",
404			__func__, link->dpia_bw_alloc_config.allocated_bw, bw_needed);
405
406		link->dpia_bw_alloc_config.allocated_bw = bw_needed;
407
408		link->dpia_bw_alloc_config.response_ready = true;
409		break;
410
411	case DPIA_EST_BW_CHANGED:
412
413		estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
414
415		DC_LOG_DEBUG("%s: ESTIMATED BW CHANGED for link(%d)\n",
416			__func__, link->link_index);
417		DC_LOG_DEBUG("%s: current estimated_bw(%d), new estimated_bw(%d)\n",
418			__func__, link->dpia_bw_alloc_config.estimated_bw, estimated);
419
420		link->dpia_bw_alloc_config.estimated_bw = estimated;
421		break;
422
423	case DPIA_BW_ALLOC_CAPS_CHANGED:
424
425		DC_LOG_ERROR("%s: BW ALLOC CAPABILITY CHANGED to Disabled for link(%d)\n",
426			__func__, link->link_index);
427		link->dpia_bw_alloc_config.bw_alloc_enabled = false;
428		break;
429	}
430}
431int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
432{
433	int ret = 0;
434	uint8_t timeout = 10;
435
436	if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type
437			&& link->dpia_bw_alloc_config.bw_alloc_enabled))
438		goto out;
439
440	//1. Hot Plug
441	if (link->hpd_status && peak_bw > 0) {
442
443		// If DP over USB4 then we need to check BW allocation
444		link->dpia_bw_alloc_config.link_max_bw = peak_bw;
445		set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.link_max_bw);
446
447		do {
448			if (timeout > 0)
449				timeout--;
450			else
451				break;
452			drm_msleep(10);
453		} while (!get_cm_response_ready_flag(link));
454
455		if (!timeout)
456			ret = 0;// ERROR TIMEOUT waiting for response for allocating bw
457		else if (link->dpia_bw_alloc_config.allocated_bw > 0)
458			ret = link->dpia_bw_alloc_config.allocated_bw;
459	}
460	//2. Cold Unplug
461	else if (!link->hpd_status)
462		dpia_bw_alloc_unplug(link);
463
464out:
465	return ret;
466}
467bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
468{
469	bool ret = false;
470	uint8_t timeout = 10;
471
472	DC_LOG_DEBUG("%s: ENTER: link(%d), hpd_status(%d), current allocated_bw(%d), req_bw(%d)\n",
473		__func__, link->link_index, link->hpd_status,
474		link->dpia_bw_alloc_config.allocated_bw, req_bw);
475
476	if (!get_bw_alloc_proceed_flag(link))
477		goto out;
478
479	set_usb4_req_bw_req(link, req_bw);
480	do {
481		if (timeout > 0)
482			timeout--;
483		else
484			break;
485		drm_msleep(10);
486	} while (!get_cm_response_ready_flag(link));
487
488	if (timeout)
489		ret = true;
490
491out:
492	DC_LOG_DEBUG("%s: EXIT: timeout(%d), ret(%d)\n", __func__, timeout, ret);
493	return ret;
494}
495
496bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias)
497{
498	bool ret = true;
499	int bw_needed_per_hr[MAX_HR_NUM] = { 0, 0 }, host_router_total_dp_bw = 0;
500	uint8_t lowest_dpia_index, i, hr_index;
501
502	if (!num_dpias || num_dpias > MAX_DPIA_NUM)
503		return ret;
504
505	lowest_dpia_index = get_lowest_dpia_index(link[0]);
506
507	/* get total Host Router BW with granularity for the given modes */
508	for (i = 0; i < num_dpias; ++i) {
509		int granularity_Gbps = 0;
510		int bw_granularity = 0;
511
512		if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled)
513			continue;
514
515		if (link[i]->link_index < lowest_dpia_index)
516			continue;
517
518		granularity_Gbps = (Kbps_TO_Gbps / link[i]->dpia_bw_alloc_config.bw_granularity);
519		bw_granularity = (bw_needed_per_dpia[i] / granularity_Gbps) * granularity_Gbps +
520				((bw_needed_per_dpia[i] % granularity_Gbps) ? granularity_Gbps : 0);
521
522		hr_index = (link[i]->link_index - lowest_dpia_index) / 2;
523		bw_needed_per_hr[hr_index] += bw_granularity;
524	}
525
526	/* validate against each Host Router max BW */
527	for (hr_index = 0; hr_index < MAX_HR_NUM; ++hr_index) {
528		if (bw_needed_per_hr[hr_index]) {
529			host_router_total_dp_bw = get_host_router_total_dp_tunnel_bw(link[0]->dc, hr_index);
530			if (bw_needed_per_hr[hr_index] > host_router_total_dp_bw) {
531				ret = false;
532				break;
533			}
534		}
535	}
536
537	return ret;
538}
539
540int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link)
541{
542	int dp_overhead = 0, link_mst_overhead = 0;
543
544	if (!get_bw_alloc_proceed_flag((link)))
545		return dp_overhead;
546
547	/* if its mst link, add MTPH overhead */
548	if ((link->type == dc_connection_mst_branch) &&
549		!link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
550		/* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH
551		 * MST overhead is 1/64 of link bandwidth (excluding any overhead)
552		 */
553		const struct dc_link_settings *link_cap =
554			dc_link_get_link_cap(link);
555		uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate *
556					   (uint32_t)link_cap->lane_count *
557					   LINK_RATE_REF_FREQ_IN_KHZ * 8;
558		link_mst_overhead = (link_bw_in_kbps / 64) + ((link_bw_in_kbps % 64) ? 1 : 0);
559	}
560
561	/* add all the overheads */
562	dp_overhead = link_mst_overhead;
563
564	return dp_overhead;
565}
566