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