1/* 2 * Copyright 2022 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26/* FILE POLICY AND INTENDED USAGE: 27 * 28 * This file implements functions that manage basic HPD components such as gpio. 29 * It also provides wrapper functions to execute HPD related programming. This 30 * file only manages basic HPD functionality. It doesn't manage detection or 31 * feature or signal specific HPD behaviors. 32 */ 33#include "link_hpd.h" 34#include "gpio_service_interface.h" 35 36bool link_get_hpd_state(struct dc_link *link) 37{ 38 uint32_t state; 39 40 dal_gpio_lock_pin(link->hpd_gpio); 41 dal_gpio_get_value(link->hpd_gpio, &state); 42 dal_gpio_unlock_pin(link->hpd_gpio); 43 44 return state; 45} 46 47void link_enable_hpd(const struct dc_link *link) 48{ 49 struct link_encoder *encoder = link->link_enc; 50 51 if (encoder != NULL && encoder->funcs->enable_hpd != NULL) 52 encoder->funcs->enable_hpd(encoder); 53} 54 55void link_disable_hpd(const struct dc_link *link) 56{ 57 struct link_encoder *encoder = link->link_enc; 58 59 if (encoder != NULL && encoder->funcs->enable_hpd != NULL) 60 encoder->funcs->disable_hpd(encoder); 61} 62 63void link_enable_hpd_filter(struct dc_link *link, bool enable) 64{ 65 struct gpio *hpd; 66 67 if (enable) { 68 link->is_hpd_filter_disabled = false; 69 program_hpd_filter(link); 70 } else { 71 link->is_hpd_filter_disabled = true; 72 /* Obtain HPD handle */ 73 hpd = link_get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); 74 75 if (!hpd) 76 return; 77 78 /* Setup HPD filtering */ 79 if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { 80 struct gpio_hpd_config config; 81 82 config.delay_on_connect = 0; 83 config.delay_on_disconnect = 0; 84 85 dal_irq_setup_hpd_filter(hpd, &config); 86 87 dal_gpio_close(hpd); 88 } else { 89 ASSERT_CRITICAL(false); 90 } 91 /* Release HPD handle */ 92 dal_gpio_destroy_irq(&hpd); 93 } 94} 95 96struct gpio *link_get_hpd_gpio(struct dc_bios *dcb, 97 struct graphics_object_id link_id, 98 struct gpio_service *gpio_service) 99{ 100 enum bp_result bp_result; 101 struct graphics_object_hpd_info hpd_info; 102 struct gpio_pin_info pin_info; 103 104 if (dcb->funcs->get_hpd_info(dcb, link_id, &hpd_info) != BP_RESULT_OK) 105 return NULL; 106 107 bp_result = dcb->funcs->get_gpio_pin_info(dcb, 108 hpd_info.hpd_int_gpio_uid, &pin_info); 109 110 if (bp_result != BP_RESULT_OK) { 111 ASSERT(bp_result == BP_RESULT_NORECORD); 112 return NULL; 113 } 114 115 return dal_gpio_service_create_irq(gpio_service, 116 pin_info.offset, 117 pin_info.mask); 118} 119 120bool query_hpd_status(struct dc_link *link, uint32_t *is_hpd_high) 121{ 122 struct gpio *hpd_pin = link_get_hpd_gpio( 123 link->ctx->dc_bios, link->link_id, 124 link->ctx->gpio_service); 125 if (!hpd_pin) 126 return false; 127 128 dal_gpio_open(hpd_pin, GPIO_MODE_INTERRUPT); 129 dal_gpio_get_value(hpd_pin, is_hpd_high); 130 dal_gpio_close(hpd_pin); 131 dal_gpio_destroy_irq(&hpd_pin); 132 return true; 133} 134 135enum hpd_source_id get_hpd_line(struct dc_link *link) 136{ 137 struct gpio *hpd; 138 enum hpd_source_id hpd_id; 139 140 hpd_id = HPD_SOURCEID_UNKNOWN; 141 142 hpd = link_get_hpd_gpio(link->ctx->dc_bios, link->link_id, 143 link->ctx->gpio_service); 144 145 if (hpd) { 146 switch (dal_irq_get_source(hpd)) { 147 case DC_IRQ_SOURCE_HPD1: 148 hpd_id = HPD_SOURCEID1; 149 break; 150 case DC_IRQ_SOURCE_HPD2: 151 hpd_id = HPD_SOURCEID2; 152 break; 153 case DC_IRQ_SOURCE_HPD3: 154 hpd_id = HPD_SOURCEID3; 155 break; 156 case DC_IRQ_SOURCE_HPD4: 157 hpd_id = HPD_SOURCEID4; 158 break; 159 case DC_IRQ_SOURCE_HPD5: 160 hpd_id = HPD_SOURCEID5; 161 break; 162 case DC_IRQ_SOURCE_HPD6: 163 hpd_id = HPD_SOURCEID6; 164 break; 165 default: 166 BREAK_TO_DEBUGGER(); 167 break; 168 } 169 170 dal_gpio_destroy_irq(&hpd); 171 } 172 173 return hpd_id; 174} 175 176bool program_hpd_filter(const struct dc_link *link) 177{ 178 bool result = false; 179 struct gpio *hpd; 180 int delay_on_connect_in_ms = 0; 181 int delay_on_disconnect_in_ms = 0; 182 183 if (link->is_hpd_filter_disabled) 184 return false; 185 /* Verify feature is supported */ 186 switch (link->connector_signal) { 187 case SIGNAL_TYPE_DVI_SINGLE_LINK: 188 case SIGNAL_TYPE_DVI_DUAL_LINK: 189 case SIGNAL_TYPE_HDMI_TYPE_A: 190 /* Program hpd filter */ 191 delay_on_connect_in_ms = 500; 192 delay_on_disconnect_in_ms = 100; 193 break; 194 case SIGNAL_TYPE_DISPLAY_PORT: 195 case SIGNAL_TYPE_DISPLAY_PORT_MST: 196 /* Program hpd filter to allow DP signal to settle */ 197 /* 500: not able to detect MST <-> SST switch as HPD is low for 198 * only 100ms on DELL U2413 199 * 0: some passive dongle still show aux mode instead of i2c 200 * 20-50: not enough to hide bouncing HPD with passive dongle. 201 * also see intermittent i2c read issues. 202 */ 203 delay_on_connect_in_ms = 80; 204 delay_on_disconnect_in_ms = 0; 205 break; 206 case SIGNAL_TYPE_LVDS: 207 case SIGNAL_TYPE_EDP: 208 default: 209 /* Don't program hpd filter */ 210 return false; 211 } 212 213 /* Obtain HPD handle */ 214 hpd = link_get_hpd_gpio(link->ctx->dc_bios, link->link_id, 215 link->ctx->gpio_service); 216 217 if (!hpd) 218 return result; 219 220 /* Setup HPD filtering */ 221 if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { 222 struct gpio_hpd_config config; 223 224 config.delay_on_connect = delay_on_connect_in_ms; 225 config.delay_on_disconnect = delay_on_disconnect_in_ms; 226 227 dal_irq_setup_hpd_filter(hpd, &config); 228 229 dal_gpio_close(hpd); 230 231 result = true; 232 } else { 233 ASSERT_CRITICAL(false); 234 } 235 236 /* Release HPD handle */ 237 dal_gpio_destroy_irq(&hpd); 238 239 return result; 240} 241