From ace9429bb58fd418f0c81d4c2835699bddf6bde6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:27:49 +0200 Subject: Adding upstream version 6.6.15. Signed-off-by: Daniel Baumann --- .../display/dc/link/protocols/link_dp_dpia_bw.c | 492 +++++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c (limited to 'drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c') diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c new file mode 100644 index 0000000000..7581023daa --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c @@ -0,0 +1,492 @@ + +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +/*********************************************************************/ +// USB4 DPIA BANDWIDTH ALLOCATION LOGIC +/*********************************************************************/ +#include "link_dp_dpia_bw.h" +#include "link_dpcd.h" +#include "dc_dmub_srv.h" + +#define DC_LOGGER \ + link->ctx->logger + +#define Kbps_TO_Gbps (1000 * 1000) + +// ------------------------------------------------------------------ +// PRIVATE FUNCTIONS +// ------------------------------------------------------------------ +/* + * Always Check the following: + * - Is it USB4 link? + * - Is HPD HIGH? + * - Is BW Allocation Support Mode enabled on DP-Tx? + */ +static bool get_bw_alloc_proceed_flag(struct dc_link *tmp) +{ + return (tmp && DISPLAY_ENDPOINT_USB4_DPIA == tmp->ep_type + && tmp->hpd_status + && tmp->dpia_bw_alloc_config.bw_alloc_enabled); +} +static void reset_bw_alloc_struct(struct dc_link *link) +{ + link->dpia_bw_alloc_config.bw_alloc_enabled = false; + link->dpia_bw_alloc_config.sink_verified_bw = 0; + link->dpia_bw_alloc_config.sink_max_bw = 0; + link->dpia_bw_alloc_config.estimated_bw = 0; + link->dpia_bw_alloc_config.bw_granularity = 0; + link->dpia_bw_alloc_config.response_ready = false; +} +static uint8_t get_bw_granularity(struct dc_link *link) +{ + uint8_t bw_granularity = 0; + + core_link_read_dpcd( + link, + DP_BW_GRANULALITY, + &bw_granularity, + sizeof(uint8_t)); + + switch (bw_granularity & 0x3) { + case 0: + bw_granularity = 4; + break; + case 1: + default: + bw_granularity = 2; + break; + } + + return bw_granularity; +} +static int get_estimated_bw(struct dc_link *link) +{ + uint8_t bw_estimated_bw = 0; + + core_link_read_dpcd( + link, + ESTIMATED_BW, + &bw_estimated_bw, + sizeof(uint8_t)); + + return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); +} +static bool allocate_usb4_bw(int *stream_allocated_bw, int bw_needed, struct dc_link *link) +{ + if (bw_needed > 0) + *stream_allocated_bw += bw_needed; + + return true; +} +static bool deallocate_usb4_bw(int *stream_allocated_bw, int bw_to_dealloc, struct dc_link *link) +{ + bool ret = false; + + if (*stream_allocated_bw > 0) { + *stream_allocated_bw -= bw_to_dealloc; + ret = true; + } else { + //Do nothing for now + ret = true; + } + + // Unplug so reset values + if (!link->hpd_status) + reset_bw_alloc_struct(link); + + return ret; +} +/* + * Read all New BW alloc configuration ex: estimated_bw, allocated_bw, + * granuality, Driver_ID, CM_Group, & populate the BW allocation structs + * for host router and dpia + */ +static void init_usb4_bw_struct(struct dc_link *link) +{ + // Init the known values + link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link); + link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); +} +static uint8_t get_lowest_dpia_index(struct dc_link *link) +{ + const struct dc *dc_struct = link->dc; + uint8_t idx = 0xFF; + int i; + + for (i = 0; i < MAX_PIPES * 2; ++i) { + + if (!dc_struct->links[i] || + dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) + continue; + + if (idx > dc_struct->links[i]->link_index) + idx = dc_struct->links[i]->link_index; + } + + return idx; +} +/* + * Get the Max Available BW or Max Estimated BW for each Host Router + * + * @link: pointer to the dc_link struct instance + * @type: ESTIMATD BW or MAX AVAILABLE BW + * + * return: response_ready flag from dc_link struct + */ +static int get_host_router_total_bw(struct dc_link *link, uint8_t type) +{ + const struct dc *dc_struct = link->dc; + uint8_t lowest_dpia_index = get_lowest_dpia_index(link); + uint8_t idx = (link->link_index - lowest_dpia_index) / 2, idx_temp = 0; + struct dc_link *link_temp; + int total_bw = 0; + int i; + + for (i = 0; i < MAX_PIPES * 2; ++i) { + + if (!dc_struct->links[i] || dc_struct->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) + continue; + + link_temp = dc_struct->links[i]; + if (!link_temp || !link_temp->hpd_status) + continue; + + idx_temp = (link_temp->link_index - lowest_dpia_index) / 2; + + if (idx_temp == idx) { + + if (type == HOST_ROUTER_BW_ESTIMATED) + total_bw += link_temp->dpia_bw_alloc_config.estimated_bw; + else if (type == HOST_ROUTER_BW_ALLOCATED) + total_bw += link_temp->dpia_bw_alloc_config.sink_allocated_bw; + } + } + + return total_bw; +} +/* + * Cleanup function for when the dpia is unplugged to reset struct + * and perform any required clean up + * + * @link: pointer to the dc_link struct instance + * + * return: none + */ +static bool dpia_bw_alloc_unplug(struct dc_link *link) +{ + if (!link) + return true; + + return deallocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, + link->dpia_bw_alloc_config.sink_allocated_bw, link); +} +static void set_usb4_req_bw_req(struct dc_link *link, int req_bw) +{ + uint8_t requested_bw; + uint32_t temp; + + // 1. Add check for this corner case #1 + if (req_bw > link->dpia_bw_alloc_config.estimated_bw) + req_bw = link->dpia_bw_alloc_config.estimated_bw; + + temp = req_bw * link->dpia_bw_alloc_config.bw_granularity; + requested_bw = temp / Kbps_TO_Gbps; + + // Always make sure to add more to account for floating points + if (temp % Kbps_TO_Gbps) + ++requested_bw; + + // 2. Add check for this corner case #2 + req_bw = requested_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); + if (req_bw == link->dpia_bw_alloc_config.sink_allocated_bw) + return; + + if (core_link_write_dpcd( + link, + REQUESTED_BW, + &requested_bw, + sizeof(uint8_t)) == DC_OK) + link->dpia_bw_alloc_config.response_ready = false; // Reset flag +} +/* + * Return the response_ready flag from dc_link struct + * + * @link: pointer to the dc_link struct instance + * + * return: response_ready flag from dc_link struct + */ +static bool get_cm_response_ready_flag(struct dc_link *link) +{ + return link->dpia_bw_alloc_config.response_ready; +} +// ------------------------------------------------------------------ +// PUBLIC FUNCTIONS +// ------------------------------------------------------------------ +bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link) +{ + bool ret = false; + uint8_t response = 0, + bw_support_dpia = 0, + bw_support_cm = 0; + + if (!(link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->hpd_status)) + goto out; + + if (core_link_read_dpcd( + link, + DP_TUNNELING_CAPABILITIES, + &response, + sizeof(uint8_t)) == DC_OK) + bw_support_dpia = (response >> 7) & 1; + + if (core_link_read_dpcd( + link, + USB4_DRIVER_BW_CAPABILITY, + &response, + sizeof(uint8_t)) == DC_OK) + bw_support_cm = (response >> 7) & 1; + + /* Send request acknowledgment to Turn ON DPTX support */ + if (bw_support_cm && bw_support_dpia) { + + response = 0x80; + if (core_link_write_dpcd( + link, + DPTX_BW_ALLOCATION_MODE_CONTROL, + &response, + sizeof(uint8_t)) != DC_OK) { + DC_LOG_DEBUG("%s: **** FAILURE Enabling DPtx BW Allocation Mode Support ***\n", + __func__); + } else { + // SUCCESS Enabled DPtx BW Allocation Mode Support + link->dpia_bw_alloc_config.bw_alloc_enabled = true; + DC_LOG_DEBUG("%s: **** SUCCESS Enabling DPtx BW Allocation Mode Support ***\n", + __func__); + + ret = true; + init_usb4_bw_struct(link); + } + } + +out: + return ret; +} +void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result) +{ + int bw_needed = 0; + int estimated = 0; + int host_router_total_estimated_bw = 0; + + if (!get_bw_alloc_proceed_flag((link))) + return; + + switch (result) { + + case DPIA_BW_REQ_FAILED: + + DC_LOG_DEBUG("%s: *** *** BW REQ FAILURE for DP-TX Request *** ***\n", __func__); + + // Update the new Estimated BW value updated by CM + link->dpia_bw_alloc_config.estimated_bw = + bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); + + set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw); + link->dpia_bw_alloc_config.response_ready = false; + + /* + * If FAIL then it is either: + * 1. Due to DP-Tx trying to allocate more than available i.e. it failed locally + * => get estimated and allocate that + * 2. Due to the fact that DP-Tx tried to allocated ESTIMATED BW and failed then + * CM will have to update 0xE0023 with new ESTIMATED BW value. + */ + break; + + case DPIA_BW_REQ_SUCCESS: + + DC_LOG_DEBUG("%s: *** BW REQ SUCCESS for DP-TX Request ***\n", __func__); + + // 1. SUCCESS 1st time before any Pruning is done + // 2. SUCCESS after prev. FAIL before any Pruning is done + // 3. SUCCESS after Pruning is done but before enabling link + + bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); + + // 1. + if (!link->dpia_bw_alloc_config.sink_allocated_bw) { + + allocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, bw_needed, link); + link->dpia_bw_alloc_config.sink_verified_bw = + link->dpia_bw_alloc_config.sink_allocated_bw; + + // SUCCESS from first attempt + if (link->dpia_bw_alloc_config.sink_allocated_bw > + link->dpia_bw_alloc_config.sink_max_bw) + link->dpia_bw_alloc_config.sink_verified_bw = + link->dpia_bw_alloc_config.sink_max_bw; + } + // 3. + else if (link->dpia_bw_alloc_config.sink_allocated_bw) { + + // Find out how much do we need to de-alloc + if (link->dpia_bw_alloc_config.sink_allocated_bw > bw_needed) + deallocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, + link->dpia_bw_alloc_config.sink_allocated_bw - bw_needed, link); + else + allocate_usb4_bw(&link->dpia_bw_alloc_config.sink_allocated_bw, + bw_needed - link->dpia_bw_alloc_config.sink_allocated_bw, link); + } + + // 4. If this is the 2nd sink then any unused bw will be reallocated to master DPIA + // => check if estimated_bw changed + + link->dpia_bw_alloc_config.response_ready = true; + break; + + case DPIA_EST_BW_CHANGED: + + DC_LOG_DEBUG("%s: *** ESTIMATED BW CHANGED for DP-TX Request ***\n", __func__); + + estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); + host_router_total_estimated_bw = get_host_router_total_bw(link, HOST_ROUTER_BW_ESTIMATED); + + // 1. If due to unplug of other sink + if (estimated == host_router_total_estimated_bw) { + // First update the estimated & max_bw fields + if (link->dpia_bw_alloc_config.estimated_bw < estimated) + link->dpia_bw_alloc_config.estimated_bw = estimated; + } + // 2. If due to realloc bw btw 2 dpia due to plug OR realloc unused Bw + else { + // We lost estimated bw usually due to plug event of other dpia + link->dpia_bw_alloc_config.estimated_bw = estimated; + } + break; + + case DPIA_BW_ALLOC_CAPS_CHANGED: + + DC_LOG_DEBUG("%s: *** BW ALLOC CAPABILITY CHANGED for DP-TX Request ***\n", __func__); + link->dpia_bw_alloc_config.bw_alloc_enabled = false; + break; + } +} +int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw) +{ + int ret = 0; + uint8_t timeout = 10; + + if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type + && link->dpia_bw_alloc_config.bw_alloc_enabled)) + goto out; + + //1. Hot Plug + if (link->hpd_status && peak_bw > 0) { + + // If DP over USB4 then we need to check BW allocation + link->dpia_bw_alloc_config.sink_max_bw = peak_bw; + set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.sink_max_bw); + + do { + if (!(timeout > 0)) + timeout--; + else + break; + fsleep(10 * 1000); + } while (!get_cm_response_ready_flag(link)); + + if (!timeout) + ret = 0;// ERROR TIMEOUT waiting for response for allocating bw + else if (link->dpia_bw_alloc_config.sink_allocated_bw > 0) + ret = get_host_router_total_bw(link, HOST_ROUTER_BW_ALLOCATED); + } + //2. Cold Unplug + else if (!link->hpd_status) + dpia_bw_alloc_unplug(link); + +out: + return ret; +} +int link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) +{ + int ret = 0; + uint8_t timeout = 10; + + if (!get_bw_alloc_proceed_flag(link)) + goto out; + + /* + * Sometimes stream uses same timing parameters as the already + * allocated max sink bw so no need to re-alloc + */ + if (req_bw != link->dpia_bw_alloc_config.sink_allocated_bw) { + set_usb4_req_bw_req(link, req_bw); + do { + if (!(timeout > 0)) + timeout--; + else + break; + udelay(10 * 1000); + } while (!get_cm_response_ready_flag(link)); + + if (!timeout) + ret = 0;// ERROR TIMEOUT waiting for response for allocating bw + else if (link->dpia_bw_alloc_config.sink_allocated_bw > 0) + ret = get_host_router_total_bw(link, HOST_ROUTER_BW_ALLOCATED); + } + +out: + return ret; +} +bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias) +{ + bool ret = true; + int bw_needed_per_hr[MAX_HR_NUM] = { 0, 0 }; + uint8_t lowest_dpia_index = 0, dpia_index = 0; + uint8_t i; + + if (!num_dpias || num_dpias > MAX_DPIA_NUM) + return ret; + + //Get total Host Router BW & Validate against each Host Router max BW + for (i = 0; i < num_dpias; ++i) { + + if (!link[i]->dpia_bw_alloc_config.bw_alloc_enabled) + continue; + + lowest_dpia_index = get_lowest_dpia_index(link[i]); + if (link[i]->link_index < lowest_dpia_index) + continue; + + dpia_index = (link[i]->link_index - lowest_dpia_index) / 2; + bw_needed_per_hr[dpia_index] += bw_needed_per_dpia[i]; + if (bw_needed_per_hr[dpia_index] > get_host_router_total_bw(link[i], HOST_ROUTER_BW_ALLOCATED)) { + + ret = false; + break; + } + } + + return ret; +} -- cgit v1.2.3