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 | |
36 | bool link_get_hpd_state(struct dc_link *link) |
37 | { |
38 | uint32_t state; |
39 | |
40 | dal_gpio_lock_pin(gpio: link->hpd_gpio); |
41 | dal_gpio_get_value(gpio: link->hpd_gpio, value: &state); |
42 | dal_gpio_unlock_pin(gpio: link->hpd_gpio); |
43 | |
44 | return state; |
45 | } |
46 | |
47 | void 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 | |
55 | void 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 | |
63 | void 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(dcb: link->ctx->dc_bios, link_id: link->link_id, gpio_service: link->ctx->gpio_service); |
74 | |
75 | if (!hpd) |
76 | return; |
77 | |
78 | /* Setup HPD filtering */ |
79 | if (dal_gpio_open(gpio: hpd, mode: 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(irq: hpd, config: &config); |
86 | |
87 | dal_gpio_close(gpio: hpd); |
88 | } else { |
89 | ASSERT_CRITICAL(false); |
90 | } |
91 | /* Release HPD handle */ |
92 | dal_gpio_destroy_irq(ptr: &hpd); |
93 | } |
94 | } |
95 | |
96 | struct 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(service: gpio_service, |
116 | offset: pin_info.offset, |
117 | mask: pin_info.mask); |
118 | } |
119 | |
120 | bool query_hpd_status(struct dc_link *link, uint32_t *is_hpd_high) |
121 | { |
122 | struct gpio *hpd_pin = link_get_hpd_gpio( |
123 | dcb: link->ctx->dc_bios, link_id: link->link_id, |
124 | gpio_service: link->ctx->gpio_service); |
125 | if (!hpd_pin) |
126 | return false; |
127 | |
128 | dal_gpio_open(gpio: hpd_pin, mode: GPIO_MODE_INTERRUPT); |
129 | dal_gpio_get_value(gpio: hpd_pin, value: is_hpd_high); |
130 | dal_gpio_close(gpio: hpd_pin); |
131 | dal_gpio_destroy_irq(ptr: &hpd_pin); |
132 | return true; |
133 | } |
134 | |
135 | enum 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(dcb: link->ctx->dc_bios, link_id: link->link_id, |
143 | gpio_service: link->ctx->gpio_service); |
144 | |
145 | if (hpd) { |
146 | switch (dal_irq_get_source(irq: 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(ptr: &hpd); |
171 | } |
172 | |
173 | return hpd_id; |
174 | } |
175 | |
176 | bool 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(dcb: link->ctx->dc_bios, link_id: link->link_id, |
215 | gpio_service: link->ctx->gpio_service); |
216 | |
217 | if (!hpd) |
218 | return result; |
219 | |
220 | /* Setup HPD filtering */ |
221 | if (dal_gpio_open(gpio: hpd, mode: 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(irq: hpd, config: &config); |
228 | |
229 | dal_gpio_close(gpio: hpd); |
230 | |
231 | result = true; |
232 | } else { |
233 | ASSERT_CRITICAL(false); |
234 | } |
235 | |
236 | /* Release HPD handle */ |
237 | dal_gpio_destroy_irq(ptr: &hpd); |
238 | |
239 | return result; |
240 | } |
241 | |