1bc33f5e5SWenjing Liu /*
2bc33f5e5SWenjing Liu  * Copyright 2012-15 Advanced Micro Devices, Inc.
3bc33f5e5SWenjing Liu  *
4bc33f5e5SWenjing Liu  * Permission is hereby granted, free of charge, to any person obtaining a
5bc33f5e5SWenjing Liu  * copy of this software and associated documentation files (the "Software"),
6bc33f5e5SWenjing Liu  * to deal in the Software without restriction, including without limitation
7bc33f5e5SWenjing Liu  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bc33f5e5SWenjing Liu  * and/or sell copies of the Software, and to permit persons to whom the
9bc33f5e5SWenjing Liu  * Software is furnished to do so, subject to the following conditions:
10bc33f5e5SWenjing Liu  *
11bc33f5e5SWenjing Liu  * The above copyright notice and this permission notice shall be included in
12bc33f5e5SWenjing Liu  * all copies or substantial portions of the Software.
13bc33f5e5SWenjing Liu  *
14bc33f5e5SWenjing Liu  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15bc33f5e5SWenjing Liu  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16bc33f5e5SWenjing Liu  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17bc33f5e5SWenjing Liu  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18bc33f5e5SWenjing Liu  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19bc33f5e5SWenjing Liu  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20bc33f5e5SWenjing Liu  * OTHER DEALINGS IN THE SOFTWARE.
21bc33f5e5SWenjing Liu  *
22bc33f5e5SWenjing Liu  * Authors: AMD
23bc33f5e5SWenjing Liu  *
24bc33f5e5SWenjing Liu  */
25bc33f5e5SWenjing Liu 
26bc33f5e5SWenjing Liu /* FILE POLICY AND INTENDED USAGE:
27bc33f5e5SWenjing Liu  *
28bc33f5e5SWenjing Liu  * This file implements generic display communication protocols such as i2c, aux
29bc33f5e5SWenjing Liu  * and scdc. The file should not contain any specific applications of these
30bc33f5e5SWenjing Liu  * protocols such as display capability query, detection, or handshaking such as
31bc33f5e5SWenjing Liu  * link training.
32bc33f5e5SWenjing Liu  */
33bc33f5e5SWenjing Liu #include "link_ddc.h"
34bc33f5e5SWenjing Liu #include "vector.h"
35bc33f5e5SWenjing Liu #include "dce/dce_aux.h"
36bc33f5e5SWenjing Liu #include "dal_asic_id.h"
37bc33f5e5SWenjing Liu #include "link_dpcd.h"
38bc33f5e5SWenjing Liu #include "dm_helpers.h"
39bc33f5e5SWenjing Liu #include "atomfirmware.h"
40bc33f5e5SWenjing Liu 
41bc33f5e5SWenjing Liu #define DC_LOGGER_INIT(logger)
42bc33f5e5SWenjing Liu 
43bc33f5e5SWenjing Liu static const uint8_t DP_VGA_DONGLE_BRANCH_DEV_NAME[] = "DpVga";
44bc33f5e5SWenjing Liu /* DP to Dual link DVI converter */
45bc33f5e5SWenjing Liu static const uint8_t DP_DVI_CONVERTER_ID_4[] = "m2DVIa";
46bc33f5e5SWenjing Liu static const uint8_t DP_DVI_CONVERTER_ID_5[] = "3393N2";
47bc33f5e5SWenjing Liu 
48bc33f5e5SWenjing Liu struct i2c_payloads {
49bc33f5e5SWenjing Liu 	struct vector payloads;
50bc33f5e5SWenjing Liu };
51bc33f5e5SWenjing Liu 
52bc33f5e5SWenjing Liu struct aux_payloads {
53bc33f5e5SWenjing Liu 	struct vector payloads;
54bc33f5e5SWenjing Liu };
55bc33f5e5SWenjing Liu 
i2c_payloads_create(struct dc_context * ctx,struct i2c_payloads * payloads,uint32_t count)56202a3816SWenjing Liu static bool i2c_payloads_create(
57bc33f5e5SWenjing Liu 		struct dc_context *ctx,
58bc33f5e5SWenjing Liu 		struct i2c_payloads *payloads,
59bc33f5e5SWenjing Liu 		uint32_t count)
60bc33f5e5SWenjing Liu {
61bc33f5e5SWenjing Liu 	if (dal_vector_construct(
62bc33f5e5SWenjing Liu 		&payloads->payloads, ctx, count, sizeof(struct i2c_payload)))
63bc33f5e5SWenjing Liu 		return true;
64bc33f5e5SWenjing Liu 
65bc33f5e5SWenjing Liu 	return false;
66bc33f5e5SWenjing Liu }
67bc33f5e5SWenjing Liu 
i2c_payloads_get(struct i2c_payloads * p)68202a3816SWenjing Liu static struct i2c_payload *i2c_payloads_get(struct i2c_payloads *p)
69bc33f5e5SWenjing Liu {
70bc33f5e5SWenjing Liu 	return (struct i2c_payload *)p->payloads.container;
71bc33f5e5SWenjing Liu }
72bc33f5e5SWenjing Liu 
i2c_payloads_get_count(struct i2c_payloads * p)73202a3816SWenjing Liu static uint32_t i2c_payloads_get_count(struct i2c_payloads *p)
74bc33f5e5SWenjing Liu {
75bc33f5e5SWenjing Liu 	return p->payloads.count;
76bc33f5e5SWenjing Liu }
77bc33f5e5SWenjing Liu 
i2c_payloads_destroy(struct i2c_payloads * p)78202a3816SWenjing Liu static void i2c_payloads_destroy(struct i2c_payloads *p)
79202a3816SWenjing Liu {
80202a3816SWenjing Liu 	if (!p)
81202a3816SWenjing Liu 		return;
82202a3816SWenjing Liu 
83202a3816SWenjing Liu 	dal_vector_destruct(&p->payloads);
84202a3816SWenjing Liu }
85202a3816SWenjing Liu 
86bc33f5e5SWenjing Liu #define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b))
87bc33f5e5SWenjing Liu 
i2c_payloads_add(struct i2c_payloads * payloads,uint32_t address,uint32_t len,uint8_t * data,bool write)88bc33f5e5SWenjing Liu static void i2c_payloads_add(
89bc33f5e5SWenjing Liu 	struct i2c_payloads *payloads,
90bc33f5e5SWenjing Liu 	uint32_t address,
91bc33f5e5SWenjing Liu 	uint32_t len,
92bc33f5e5SWenjing Liu 	uint8_t *data,
93bc33f5e5SWenjing Liu 	bool write)
94bc33f5e5SWenjing Liu {
95bc33f5e5SWenjing Liu 	uint32_t payload_size = EDID_SEGMENT_SIZE;
96bc33f5e5SWenjing Liu 	uint32_t pos;
97bc33f5e5SWenjing Liu 
98bc33f5e5SWenjing Liu 	for (pos = 0; pos < len; pos += payload_size) {
99bc33f5e5SWenjing Liu 		struct i2c_payload payload = {
100bc33f5e5SWenjing Liu 			.write = write,
101bc33f5e5SWenjing Liu 			.address = address,
102bc33f5e5SWenjing Liu 			.length = DDC_MIN(payload_size, len - pos),
103bc33f5e5SWenjing Liu 			.data = data + pos };
104bc33f5e5SWenjing Liu 		dal_vector_append(&payloads->payloads, &payload);
105bc33f5e5SWenjing Liu 	}
106bc33f5e5SWenjing Liu 
107bc33f5e5SWenjing Liu }
108bc33f5e5SWenjing Liu 
ddc_service_construct(struct ddc_service * ddc_service,struct ddc_service_init_data * init_data)109bc33f5e5SWenjing Liu static void ddc_service_construct(
110bc33f5e5SWenjing Liu 	struct ddc_service *ddc_service,
111bc33f5e5SWenjing Liu 	struct ddc_service_init_data *init_data)
112bc33f5e5SWenjing Liu {
113bc33f5e5SWenjing Liu 	enum connector_id connector_id =
114bc33f5e5SWenjing Liu 		dal_graphics_object_id_get_connector_id(init_data->id);
115bc33f5e5SWenjing Liu 
116bc33f5e5SWenjing Liu 	struct gpio_service *gpio_service = init_data->ctx->gpio_service;
117bc33f5e5SWenjing Liu 	struct graphics_object_i2c_info i2c_info;
118bc33f5e5SWenjing Liu 	struct gpio_ddc_hw_info hw_info;
119bc33f5e5SWenjing Liu 	struct dc_bios *dcb = init_data->ctx->dc_bios;
120bc33f5e5SWenjing Liu 
121bc33f5e5SWenjing Liu 	ddc_service->link = init_data->link;
122bc33f5e5SWenjing Liu 	ddc_service->ctx = init_data->ctx;
123bc33f5e5SWenjing Liu 
124bc33f5e5SWenjing Liu 	if (init_data->is_dpia_link ||
125bc33f5e5SWenjing Liu 	    dcb->funcs->get_i2c_info(dcb, init_data->id, &i2c_info) != BP_RESULT_OK) {
126bc33f5e5SWenjing Liu 		ddc_service->ddc_pin = NULL;
127bc33f5e5SWenjing Liu 	} else {
128bc33f5e5SWenjing Liu 		DC_LOGGER_INIT(ddc_service->ctx->logger);
129bc33f5e5SWenjing Liu 		DC_LOG_DC("BIOS object table - i2c_line: %d", i2c_info.i2c_line);
130bc33f5e5SWenjing Liu 		DC_LOG_DC("BIOS object table - i2c_engine_id: %d", i2c_info.i2c_engine_id);
131bc33f5e5SWenjing Liu 
132bc33f5e5SWenjing Liu 		hw_info.ddc_channel = i2c_info.i2c_line;
133bc33f5e5SWenjing Liu 		if (ddc_service->link != NULL)
134bc33f5e5SWenjing Liu 			hw_info.hw_supported = i2c_info.i2c_hw_assist;
135bc33f5e5SWenjing Liu 		else
136bc33f5e5SWenjing Liu 			hw_info.hw_supported = false;
137bc33f5e5SWenjing Liu 
138bc33f5e5SWenjing Liu 		ddc_service->ddc_pin = dal_gpio_create_ddc(
139bc33f5e5SWenjing Liu 			gpio_service,
140bc33f5e5SWenjing Liu 			i2c_info.gpio_info.clk_a_register_index,
141bc33f5e5SWenjing Liu 			1 << i2c_info.gpio_info.clk_a_shift,
142bc33f5e5SWenjing Liu 			&hw_info);
143bc33f5e5SWenjing Liu 	}
144bc33f5e5SWenjing Liu 
145bc33f5e5SWenjing Liu 	ddc_service->flags.EDID_QUERY_DONE_ONCE = false;
146bc33f5e5SWenjing Liu 	ddc_service->flags.FORCE_READ_REPEATED_START = false;
147bc33f5e5SWenjing Liu 	ddc_service->flags.EDID_STRESS_READ = false;
148bc33f5e5SWenjing Liu 
149bc33f5e5SWenjing Liu 	ddc_service->flags.IS_INTERNAL_DISPLAY =
150bc33f5e5SWenjing Liu 		connector_id == CONNECTOR_ID_EDP ||
151bc33f5e5SWenjing Liu 		connector_id == CONNECTOR_ID_LVDS;
152bc33f5e5SWenjing Liu 
153bc33f5e5SWenjing Liu 	ddc_service->wa.raw = 0;
154bc33f5e5SWenjing Liu }
155bc33f5e5SWenjing Liu 
link_create_ddc_service(struct ddc_service_init_data * init_data)156bc33f5e5SWenjing Liu struct ddc_service *link_create_ddc_service(
157bc33f5e5SWenjing Liu 	struct ddc_service_init_data *init_data)
158bc33f5e5SWenjing Liu {
159bc33f5e5SWenjing Liu 	struct ddc_service *ddc_service;
160bc33f5e5SWenjing Liu 
161bc33f5e5SWenjing Liu 	ddc_service = kzalloc(sizeof(struct ddc_service), GFP_KERNEL);
162bc33f5e5SWenjing Liu 
163bc33f5e5SWenjing Liu 	if (!ddc_service)
164bc33f5e5SWenjing Liu 		return NULL;
165bc33f5e5SWenjing Liu 
166bc33f5e5SWenjing Liu 	ddc_service_construct(ddc_service, init_data);
167bc33f5e5SWenjing Liu 	return ddc_service;
168bc33f5e5SWenjing Liu }
169bc33f5e5SWenjing Liu 
ddc_service_destruct(struct ddc_service * ddc)170bc33f5e5SWenjing Liu static void ddc_service_destruct(struct ddc_service *ddc)
171bc33f5e5SWenjing Liu {
172bc33f5e5SWenjing Liu 	if (ddc->ddc_pin)
173bc33f5e5SWenjing Liu 		dal_gpio_destroy_ddc(&ddc->ddc_pin);
174bc33f5e5SWenjing Liu }
175bc33f5e5SWenjing Liu 
link_destroy_ddc_service(struct ddc_service ** ddc)176bc33f5e5SWenjing Liu void link_destroy_ddc_service(struct ddc_service **ddc)
177bc33f5e5SWenjing Liu {
178bc33f5e5SWenjing Liu 	if (!ddc || !*ddc) {
179bc33f5e5SWenjing Liu 		BREAK_TO_DEBUGGER();
180bc33f5e5SWenjing Liu 		return;
181bc33f5e5SWenjing Liu 	}
182bc33f5e5SWenjing Liu 	ddc_service_destruct(*ddc);
183bc33f5e5SWenjing Liu 	kfree(*ddc);
184bc33f5e5SWenjing Liu 	*ddc = NULL;
185bc33f5e5SWenjing Liu }
186bc33f5e5SWenjing Liu 
set_ddc_transaction_type(struct ddc_service * ddc,enum ddc_transaction_type type)187bc33f5e5SWenjing Liu void set_ddc_transaction_type(
188bc33f5e5SWenjing Liu 	struct ddc_service *ddc,
189bc33f5e5SWenjing Liu 	enum ddc_transaction_type type)
190bc33f5e5SWenjing Liu {
191bc33f5e5SWenjing Liu 	ddc->transaction_type = type;
192bc33f5e5SWenjing Liu }
193bc33f5e5SWenjing Liu 
link_is_in_aux_transaction_mode(struct ddc_service * ddc)194bc33f5e5SWenjing Liu bool link_is_in_aux_transaction_mode(struct ddc_service *ddc)
195bc33f5e5SWenjing Liu {
196bc33f5e5SWenjing Liu 	switch (ddc->transaction_type) {
197bc33f5e5SWenjing Liu 	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX:
198bc33f5e5SWenjing Liu 	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER:
199bc33f5e5SWenjing Liu 	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER:
200bc33f5e5SWenjing Liu 		return true;
201bc33f5e5SWenjing Liu 	default:
202bc33f5e5SWenjing Liu 		break;
203bc33f5e5SWenjing Liu 	}
204bc33f5e5SWenjing Liu 	return false;
205bc33f5e5SWenjing Liu }
206bc33f5e5SWenjing Liu 
set_dongle_type(struct ddc_service * ddc,enum display_dongle_type dongle_type)207bc33f5e5SWenjing Liu void set_dongle_type(struct ddc_service *ddc,
208bc33f5e5SWenjing Liu 		enum display_dongle_type dongle_type)
209bc33f5e5SWenjing Liu {
210bc33f5e5SWenjing Liu 	ddc->dongle_type = dongle_type;
211bc33f5e5SWenjing Liu }
212bc33f5e5SWenjing Liu 
defer_delay_converter_wa(struct ddc_service * ddc,uint32_t defer_delay)213bc33f5e5SWenjing Liu static uint32_t defer_delay_converter_wa(
214bc33f5e5SWenjing Liu 	struct ddc_service *ddc,
215bc33f5e5SWenjing Liu 	uint32_t defer_delay)
216bc33f5e5SWenjing Liu {
217bc33f5e5SWenjing Liu 	struct dc_link *link = ddc->link;
218bc33f5e5SWenjing Liu 
219bc33f5e5SWenjing Liu 	if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER &&
220bc33f5e5SWenjing Liu 		link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 &&
221bc33f5e5SWenjing Liu 		(link->dpcd_caps.branch_fw_revision[0] < 0x01 ||
222bc33f5e5SWenjing Liu 				(link->dpcd_caps.branch_fw_revision[0] == 0x01 &&
223bc33f5e5SWenjing Liu 				link->dpcd_caps.branch_fw_revision[1] < 0x40)) &&
224bc33f5e5SWenjing Liu 		!memcmp(link->dpcd_caps.branch_dev_name,
225bc33f5e5SWenjing Liu 		    DP_VGA_DONGLE_BRANCH_DEV_NAME,
226bc33f5e5SWenjing Liu 			sizeof(link->dpcd_caps.branch_dev_name)))
227bc33f5e5SWenjing Liu 
228bc33f5e5SWenjing Liu 		return defer_delay > DPVGA_DONGLE_AUX_DEFER_WA_DELAY ?
229bc33f5e5SWenjing Liu 			defer_delay : DPVGA_DONGLE_AUX_DEFER_WA_DELAY;
230bc33f5e5SWenjing Liu 
231bc33f5e5SWenjing Liu 	if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 &&
232bc33f5e5SWenjing Liu 	    !memcmp(link->dpcd_caps.branch_dev_name,
233bc33f5e5SWenjing Liu 		    DP_DVI_CONVERTER_ID_4,
234bc33f5e5SWenjing Liu 		    sizeof(link->dpcd_caps.branch_dev_name)))
235bc33f5e5SWenjing Liu 		return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ?
236bc33f5e5SWenjing Liu 			defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY;
237bc33f5e5SWenjing Liu 	if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_006037 &&
238bc33f5e5SWenjing Liu 	    !memcmp(link->dpcd_caps.branch_dev_name,
239bc33f5e5SWenjing Liu 		    DP_DVI_CONVERTER_ID_5,
240bc33f5e5SWenjing Liu 		    sizeof(link->dpcd_caps.branch_dev_name)))
241bc33f5e5SWenjing Liu 		return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY_1MS ?
242bc33f5e5SWenjing Liu 			I2C_OVER_AUX_DEFER_WA_DELAY_1MS : defer_delay;
243bc33f5e5SWenjing Liu 
244bc33f5e5SWenjing Liu 	return defer_delay;
245bc33f5e5SWenjing Liu }
246bc33f5e5SWenjing Liu 
247bc33f5e5SWenjing Liu #define DP_TRANSLATOR_DELAY 5
248bc33f5e5SWenjing Liu 
link_get_aux_defer_delay(struct ddc_service * ddc)249bc33f5e5SWenjing Liu uint32_t link_get_aux_defer_delay(struct ddc_service *ddc)
250bc33f5e5SWenjing Liu {
251bc33f5e5SWenjing Liu 	uint32_t defer_delay = 0;
252bc33f5e5SWenjing Liu 
253bc33f5e5SWenjing Liu 	switch (ddc->transaction_type) {
254bc33f5e5SWenjing Liu 	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX:
255bc33f5e5SWenjing Liu 		if ((DISPLAY_DONGLE_DP_VGA_CONVERTER == ddc->dongle_type) ||
256bc33f5e5SWenjing Liu 			(DISPLAY_DONGLE_DP_DVI_CONVERTER == ddc->dongle_type) ||
257bc33f5e5SWenjing Liu 			(DISPLAY_DONGLE_DP_HDMI_CONVERTER ==
258bc33f5e5SWenjing Liu 				ddc->dongle_type)) {
259bc33f5e5SWenjing Liu 
260bc33f5e5SWenjing Liu 			defer_delay = DP_TRANSLATOR_DELAY;
261bc33f5e5SWenjing Liu 
262bc33f5e5SWenjing Liu 			defer_delay =
263bc33f5e5SWenjing Liu 				defer_delay_converter_wa(ddc, defer_delay);
264bc33f5e5SWenjing Liu 
265bc33f5e5SWenjing Liu 		} else /*sink has a delay different from an Active Converter*/
266bc33f5e5SWenjing Liu 			defer_delay = 0;
267bc33f5e5SWenjing Liu 		break;
268bc33f5e5SWenjing Liu 	case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER:
269bc33f5e5SWenjing Liu 		defer_delay = DP_TRANSLATOR_DELAY;
270bc33f5e5SWenjing Liu 		break;
271bc33f5e5SWenjing Liu 	default:
272bc33f5e5SWenjing Liu 		break;
273bc33f5e5SWenjing Liu 	}
274bc33f5e5SWenjing Liu 	return defer_delay;
275bc33f5e5SWenjing Liu }
276bc33f5e5SWenjing Liu 
submit_aux_command(struct ddc_service * ddc,struct aux_payload * payload)277bc33f5e5SWenjing Liu static bool submit_aux_command(struct ddc_service *ddc,
278bc33f5e5SWenjing Liu 		struct aux_payload *payload)
279bc33f5e5SWenjing Liu {
280bc33f5e5SWenjing Liu 	uint32_t retrieved = 0;
281bc33f5e5SWenjing Liu 	bool ret = false;
282bc33f5e5SWenjing Liu 
283bc33f5e5SWenjing Liu 	if (!ddc)
284bc33f5e5SWenjing Liu 		return false;
285bc33f5e5SWenjing Liu 
286bc33f5e5SWenjing Liu 	if (!payload)
287bc33f5e5SWenjing Liu 		return false;
288bc33f5e5SWenjing Liu 
289bc33f5e5SWenjing Liu 	do {
290bc33f5e5SWenjing Liu 		struct aux_payload current_payload;
291bc33f5e5SWenjing Liu 		bool is_end_of_payload = (retrieved + DEFAULT_AUX_MAX_DATA_SIZE) >=
292bc33f5e5SWenjing Liu 				payload->length;
293bc33f5e5SWenjing Liu 		uint32_t payload_length = is_end_of_payload ?
294bc33f5e5SWenjing Liu 				payload->length - retrieved : DEFAULT_AUX_MAX_DATA_SIZE;
295bc33f5e5SWenjing Liu 
296bc33f5e5SWenjing Liu 		current_payload.address = payload->address;
297bc33f5e5SWenjing Liu 		current_payload.data = &payload->data[retrieved];
298bc33f5e5SWenjing Liu 		current_payload.defer_delay = payload->defer_delay;
299bc33f5e5SWenjing Liu 		current_payload.i2c_over_aux = payload->i2c_over_aux;
300bc33f5e5SWenjing Liu 		current_payload.length = payload_length;
301bc33f5e5SWenjing Liu 		/* set mot (middle of transaction) to false if it is the last payload */
302bc33f5e5SWenjing Liu 		current_payload.mot = is_end_of_payload ? payload->mot:true;
303bc33f5e5SWenjing Liu 		current_payload.write_status_update = false;
304bc33f5e5SWenjing Liu 		current_payload.reply = payload->reply;
305bc33f5e5SWenjing Liu 		current_payload.write = payload->write;
306bc33f5e5SWenjing Liu 
307bc33f5e5SWenjing Liu 		ret = link_aux_transfer_with_retries_no_mutex(ddc, &current_payload);
308bc33f5e5SWenjing Liu 
309bc33f5e5SWenjing Liu 		retrieved += payload_length;
310bc33f5e5SWenjing Liu 	} while (retrieved < payload->length && ret == true);
311bc33f5e5SWenjing Liu 
312bc33f5e5SWenjing Liu 	return ret;
313bc33f5e5SWenjing Liu }
314bc33f5e5SWenjing Liu 
link_query_ddc_data(struct ddc_service * ddc,uint32_t address,uint8_t * write_buf,uint32_t write_size,uint8_t * read_buf,uint32_t read_size)315bc33f5e5SWenjing Liu bool link_query_ddc_data(
316bc33f5e5SWenjing Liu 	struct ddc_service *ddc,
317bc33f5e5SWenjing Liu 	uint32_t address,
318bc33f5e5SWenjing Liu 	uint8_t *write_buf,
319bc33f5e5SWenjing Liu 	uint32_t write_size,
320bc33f5e5SWenjing Liu 	uint8_t *read_buf,
321bc33f5e5SWenjing Liu 	uint32_t read_size)
322bc33f5e5SWenjing Liu {
323bc33f5e5SWenjing Liu 	bool success = true;
324bc33f5e5SWenjing Liu 	uint32_t payload_size =
325bc33f5e5SWenjing Liu 		link_is_in_aux_transaction_mode(ddc) ?
326bc33f5e5SWenjing Liu 			DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE;
327bc33f5e5SWenjing Liu 
328bc33f5e5SWenjing Liu 	uint32_t write_payloads =
329bc33f5e5SWenjing Liu 		(write_size + payload_size - 1) / payload_size;
330bc33f5e5SWenjing Liu 
331bc33f5e5SWenjing Liu 	uint32_t read_payloads =
332bc33f5e5SWenjing Liu 		(read_size + payload_size - 1) / payload_size;
333bc33f5e5SWenjing Liu 
334bc33f5e5SWenjing Liu 	uint32_t payloads_num = write_payloads + read_payloads;
335bc33f5e5SWenjing Liu 
336bc33f5e5SWenjing Liu 	if (!payloads_num)
337bc33f5e5SWenjing Liu 		return false;
338bc33f5e5SWenjing Liu 
339bc33f5e5SWenjing Liu 	if (link_is_in_aux_transaction_mode(ddc)) {
340bc33f5e5SWenjing Liu 		struct aux_payload payload;
341bc33f5e5SWenjing Liu 
342bc33f5e5SWenjing Liu 		payload.i2c_over_aux = true;
343bc33f5e5SWenjing Liu 		payload.address = address;
344bc33f5e5SWenjing Liu 		payload.reply = NULL;
345bc33f5e5SWenjing Liu 		payload.defer_delay = link_get_aux_defer_delay(ddc);
346bc33f5e5SWenjing Liu 		payload.write_status_update = false;
347bc33f5e5SWenjing Liu 
348bc33f5e5SWenjing Liu 		if (write_size != 0) {
349bc33f5e5SWenjing Liu 			payload.write = true;
350bc33f5e5SWenjing Liu 			/* should not set mot (middle of transaction) to 0
351bc33f5e5SWenjing Liu 			 * if there are pending read payloads
352bc33f5e5SWenjing Liu 			 */
353bc33f5e5SWenjing Liu 			payload.mot = !(read_size == 0);
354bc33f5e5SWenjing Liu 			payload.length = write_size;
355bc33f5e5SWenjing Liu 			payload.data = write_buf;
356bc33f5e5SWenjing Liu 
357bc33f5e5SWenjing Liu 			success = submit_aux_command(ddc, &payload);
358bc33f5e5SWenjing Liu 		}
359bc33f5e5SWenjing Liu 
360bc33f5e5SWenjing Liu 		if (read_size != 0 && success) {
361bc33f5e5SWenjing Liu 			payload.write = false;
362bc33f5e5SWenjing Liu 			/* should set mot (middle of transaction) to 0
363bc33f5e5SWenjing Liu 			 * since it is the last payload to send
364bc33f5e5SWenjing Liu 			 */
365bc33f5e5SWenjing Liu 			payload.mot = false;
366bc33f5e5SWenjing Liu 			payload.length = read_size;
367bc33f5e5SWenjing Liu 			payload.data = read_buf;
368bc33f5e5SWenjing Liu 
369bc33f5e5SWenjing Liu 			success = submit_aux_command(ddc, &payload);
370bc33f5e5SWenjing Liu 		}
371bc33f5e5SWenjing Liu 	} else {
372bc33f5e5SWenjing Liu 		struct i2c_command command = {0};
373bc33f5e5SWenjing Liu 		struct i2c_payloads payloads;
374bc33f5e5SWenjing Liu 
375202a3816SWenjing Liu 		if (!i2c_payloads_create(ddc->ctx, &payloads, payloads_num))
376bc33f5e5SWenjing Liu 			return false;
377bc33f5e5SWenjing Liu 
378202a3816SWenjing Liu 		command.payloads = i2c_payloads_get(&payloads);
379bc33f5e5SWenjing Liu 		command.number_of_payloads = 0;
380bc33f5e5SWenjing Liu 		command.engine = DDC_I2C_COMMAND_ENGINE;
381bc33f5e5SWenjing Liu 		command.speed = ddc->ctx->dc->caps.i2c_speed_in_khz;
382bc33f5e5SWenjing Liu 
383bc33f5e5SWenjing Liu 		i2c_payloads_add(
384bc33f5e5SWenjing Liu 			&payloads, address, write_size, write_buf, true);
385bc33f5e5SWenjing Liu 
386bc33f5e5SWenjing Liu 		i2c_payloads_add(
387bc33f5e5SWenjing Liu 			&payloads, address, read_size, read_buf, false);
388bc33f5e5SWenjing Liu 
389bc33f5e5SWenjing Liu 		command.number_of_payloads =
390202a3816SWenjing Liu 			i2c_payloads_get_count(&payloads);
391bc33f5e5SWenjing Liu 
392bc33f5e5SWenjing Liu 		success = dm_helpers_submit_i2c(
393bc33f5e5SWenjing Liu 				ddc->ctx,
394bc33f5e5SWenjing Liu 				ddc->link,
395bc33f5e5SWenjing Liu 				&command);
396bc33f5e5SWenjing Liu 
397202a3816SWenjing Liu 		i2c_payloads_destroy(&payloads);
398bc33f5e5SWenjing Liu 	}
399bc33f5e5SWenjing Liu 
400bc33f5e5SWenjing Liu 	return success;
401bc33f5e5SWenjing Liu }
402bc33f5e5SWenjing Liu 
link_aux_transfer_raw(struct ddc_service * ddc,struct aux_payload * payload,enum aux_return_code_type * operation_result)403202a3816SWenjing Liu int link_aux_transfer_raw(struct ddc_service *ddc,
404bc33f5e5SWenjing Liu 		struct aux_payload *payload,
405bc33f5e5SWenjing Liu 		enum aux_return_code_type *operation_result)
406bc33f5e5SWenjing Liu {
407bc33f5e5SWenjing Liu 	if (ddc->ctx->dc->debug.enable_dmub_aux_for_legacy_ddc ||
408bc33f5e5SWenjing Liu 	    !ddc->ddc_pin) {
409bc33f5e5SWenjing Liu 		return dce_aux_transfer_dmub_raw(ddc, payload, operation_result);
410bc33f5e5SWenjing Liu 	} else {
411bc33f5e5SWenjing Liu 		return dce_aux_transfer_raw(ddc, payload, operation_result);
412bc33f5e5SWenjing Liu 	}
413bc33f5e5SWenjing Liu }
414bc33f5e5SWenjing Liu 
link_get_fixed_vs_pe_retimer_write_address(struct dc_link * link)415*ad5594adSMichael Strauss uint32_t link_get_fixed_vs_pe_retimer_write_address(struct dc_link *link)
416*ad5594adSMichael Strauss {
417*ad5594adSMichael Strauss 	uint32_t vendor_lttpr_write_address = 0xF004F;
418*ad5594adSMichael Strauss 	uint8_t offset;
419*ad5594adSMichael Strauss 
420*ad5594adSMichael Strauss 	switch (link->dpcd_caps.lttpr_caps.phy_repeater_cnt) {
421*ad5594adSMichael Strauss 	case 0x80: // 1 lttpr repeater
422*ad5594adSMichael Strauss 		offset =  1;
423*ad5594adSMichael Strauss 		break;
424*ad5594adSMichael Strauss 	case 0x40: // 2 lttpr repeaters
425*ad5594adSMichael Strauss 		offset = 2;
426*ad5594adSMichael Strauss 		break;
427*ad5594adSMichael Strauss 	case 0x20: // 3 lttpr repeaters
428*ad5594adSMichael Strauss 		offset = 3;
429*ad5594adSMichael Strauss 		break;
430*ad5594adSMichael Strauss 	case 0x10: // 4 lttpr repeaters
431*ad5594adSMichael Strauss 		offset = 4;
432*ad5594adSMichael Strauss 		break;
433*ad5594adSMichael Strauss 	case 0x08: // 5 lttpr repeaters
434*ad5594adSMichael Strauss 		offset = 5;
435*ad5594adSMichael Strauss 		break;
436*ad5594adSMichael Strauss 	case 0x04: // 6 lttpr repeaters
437*ad5594adSMichael Strauss 		offset = 6;
438*ad5594adSMichael Strauss 		break;
439*ad5594adSMichael Strauss 	case 0x02: // 7 lttpr repeaters
440*ad5594adSMichael Strauss 		offset = 7;
441*ad5594adSMichael Strauss 		break;
442*ad5594adSMichael Strauss 	case 0x01: // 8 lttpr repeaters
443*ad5594adSMichael Strauss 		offset = 8;
444*ad5594adSMichael Strauss 		break;
445*ad5594adSMichael Strauss 	default:
446*ad5594adSMichael Strauss 		offset = 0xFF;
447*ad5594adSMichael Strauss 	}
448*ad5594adSMichael Strauss 
449*ad5594adSMichael Strauss 	if (offset != 0xFF) {
450*ad5594adSMichael Strauss 		vendor_lttpr_write_address +=
451*ad5594adSMichael Strauss 				((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
452*ad5594adSMichael Strauss 	}
453*ad5594adSMichael Strauss 	return vendor_lttpr_write_address;
454*ad5594adSMichael Strauss }
455*ad5594adSMichael Strauss 
link_get_fixed_vs_pe_retimer_read_address(struct dc_link * link)456*ad5594adSMichael Strauss uint32_t link_get_fixed_vs_pe_retimer_read_address(struct dc_link *link)
457*ad5594adSMichael Strauss {
458*ad5594adSMichael Strauss 	return link_get_fixed_vs_pe_retimer_write_address(link) + 4;
459*ad5594adSMichael Strauss }
460*ad5594adSMichael Strauss 
link_configure_fixed_vs_pe_retimer(struct ddc_service * ddc,const uint8_t * data,uint32_t length)461*ad5594adSMichael Strauss bool link_configure_fixed_vs_pe_retimer(struct ddc_service *ddc, const uint8_t *data, uint32_t length)
462*ad5594adSMichael Strauss {
463*ad5594adSMichael Strauss 	struct aux_payload write_payload = {
464*ad5594adSMichael Strauss 		.i2c_over_aux = false,
465*ad5594adSMichael Strauss 		.write = true,
466*ad5594adSMichael Strauss 		.address = link_get_fixed_vs_pe_retimer_write_address(ddc->link),
467*ad5594adSMichael Strauss 		.length = length,
468*ad5594adSMichael Strauss 		.data = (uint8_t *) data,
469*ad5594adSMichael Strauss 		.reply = NULL,
470*ad5594adSMichael Strauss 		.mot = I2C_MOT_UNDEF,
471*ad5594adSMichael Strauss 		.write_status_update = false,
472*ad5594adSMichael Strauss 		.defer_delay = 0,
473*ad5594adSMichael Strauss 	};
474*ad5594adSMichael Strauss 
475*ad5594adSMichael Strauss 	return link_aux_transfer_with_retries_no_mutex(ddc,
476*ad5594adSMichael Strauss 			&write_payload);
477*ad5594adSMichael Strauss }
478*ad5594adSMichael Strauss 
link_query_fixed_vs_pe_retimer(struct ddc_service * ddc,uint8_t * data,uint32_t length)479*ad5594adSMichael Strauss bool link_query_fixed_vs_pe_retimer(struct ddc_service *ddc, uint8_t *data, uint32_t length)
480*ad5594adSMichael Strauss {
481*ad5594adSMichael Strauss 	struct aux_payload read_payload = {
482*ad5594adSMichael Strauss 		.i2c_over_aux = false,
483*ad5594adSMichael Strauss 		.write = false,
484*ad5594adSMichael Strauss 		.address = link_get_fixed_vs_pe_retimer_read_address(ddc->link),
485*ad5594adSMichael Strauss 		.length = length,
486*ad5594adSMichael Strauss 		.data = data,
487*ad5594adSMichael Strauss 		.reply = NULL,
488*ad5594adSMichael Strauss 		.mot = I2C_MOT_UNDEF,
489*ad5594adSMichael Strauss 		.write_status_update = false,
490*ad5594adSMichael Strauss 		.defer_delay = 0,
491*ad5594adSMichael Strauss 	};
492*ad5594adSMichael Strauss 
493*ad5594adSMichael Strauss 	return link_aux_transfer_with_retries_no_mutex(ddc,
494*ad5594adSMichael Strauss 			&read_payload);
495*ad5594adSMichael Strauss }
496*ad5594adSMichael Strauss 
link_aux_transfer_with_retries_no_mutex(struct ddc_service * ddc,struct aux_payload * payload)497bc33f5e5SWenjing Liu bool link_aux_transfer_with_retries_no_mutex(struct ddc_service *ddc,
498bc33f5e5SWenjing Liu 		struct aux_payload *payload)
499bc33f5e5SWenjing Liu {
500bc33f5e5SWenjing Liu 	return dce_aux_transfer_with_retries(ddc, payload);
501bc33f5e5SWenjing Liu }
502bc33f5e5SWenjing Liu 
503bc33f5e5SWenjing Liu 
try_to_configure_aux_timeout(struct ddc_service * ddc,uint32_t timeout)504bc33f5e5SWenjing Liu bool try_to_configure_aux_timeout(struct ddc_service *ddc,
505bc33f5e5SWenjing Liu 		uint32_t timeout)
506bc33f5e5SWenjing Liu {
507bc33f5e5SWenjing Liu 	bool result = false;
508bc33f5e5SWenjing Liu 	struct ddc *ddc_pin = ddc->ddc_pin;
509bc33f5e5SWenjing Liu 
510bc33f5e5SWenjing Liu 	if ((ddc->link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) &&
511bc33f5e5SWenjing Liu 			!ddc->link->dc->debug.disable_fixed_vs_aux_timeout_wa &&
512085f7bd9STaimur Hassan 			ddc->ctx->dce_version == DCN_VERSION_3_1) {
513bc33f5e5SWenjing Liu 		/* Fixed VS workaround for AUX timeout */
514bc33f5e5SWenjing Liu 		const uint32_t fixed_vs_address = 0xF004F;
515bc33f5e5SWenjing Liu 		const uint8_t fixed_vs_data[4] = {0x1, 0x22, 0x63, 0xc};
516bc33f5e5SWenjing Liu 
517bc33f5e5SWenjing Liu 		core_link_write_dpcd(ddc->link,
518bc33f5e5SWenjing Liu 				fixed_vs_address,
519bc33f5e5SWenjing Liu 				fixed_vs_data,
520bc33f5e5SWenjing Liu 				sizeof(fixed_vs_data));
521bc33f5e5SWenjing Liu 
522bc33f5e5SWenjing Liu 		timeout = 3072;
523bc33f5e5SWenjing Liu 	}
524bc33f5e5SWenjing Liu 
525bc33f5e5SWenjing Liu 	/* Do not try to access nonexistent DDC pin. */
526bc33f5e5SWenjing Liu 	if (ddc->link->ep_type != DISPLAY_ENDPOINT_PHY)
527bc33f5e5SWenjing Liu 		return true;
528bc33f5e5SWenjing Liu 
529bc33f5e5SWenjing Liu 	if (ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout) {
530bc33f5e5SWenjing Liu 		ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout(ddc, timeout);
531bc33f5e5SWenjing Liu 		result = true;
532bc33f5e5SWenjing Liu 	}
533bc33f5e5SWenjing Liu 
534bc33f5e5SWenjing Liu 	return result;
535bc33f5e5SWenjing Liu }
536bc33f5e5SWenjing Liu 
get_ddc_pin(struct ddc_service * ddc_service)537bc33f5e5SWenjing Liu struct ddc *get_ddc_pin(struct ddc_service *ddc_service)
538bc33f5e5SWenjing Liu {
539bc33f5e5SWenjing Liu 	return ddc_service->ddc_pin;
540bc33f5e5SWenjing Liu }
541bc33f5e5SWenjing Liu 
write_scdc_data(struct ddc_service * ddc_service,uint32_t pix_clk,bool lte_340_scramble)542bc33f5e5SWenjing Liu void write_scdc_data(struct ddc_service *ddc_service,
543bc33f5e5SWenjing Liu 		uint32_t pix_clk,
544bc33f5e5SWenjing Liu 		bool lte_340_scramble)
545bc33f5e5SWenjing Liu {
546bc33f5e5SWenjing Liu 	bool over_340_mhz = pix_clk > 340000 ? 1 : 0;
547bc33f5e5SWenjing Liu 	uint8_t slave_address = HDMI_SCDC_ADDRESS;
548bc33f5e5SWenjing Liu 	uint8_t offset = HDMI_SCDC_SINK_VERSION;
549bc33f5e5SWenjing Liu 	uint8_t sink_version = 0;
550bc33f5e5SWenjing Liu 	uint8_t write_buffer[2] = {0};
551bc33f5e5SWenjing Liu 	/*Lower than 340 Scramble bit from SCDC caps*/
552bc33f5e5SWenjing Liu 
553bc33f5e5SWenjing Liu 	if (ddc_service->link->local_sink &&
554bc33f5e5SWenjing Liu 		ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite)
555bc33f5e5SWenjing Liu 		return;
556bc33f5e5SWenjing Liu 
557bc33f5e5SWenjing Liu 	link_query_ddc_data(ddc_service, slave_address, &offset,
558bc33f5e5SWenjing Liu 			sizeof(offset), &sink_version, sizeof(sink_version));
559bc33f5e5SWenjing Liu 	if (sink_version == 1) {
560bc33f5e5SWenjing Liu 		/*Source Version = 1*/
561bc33f5e5SWenjing Liu 		write_buffer[0] = HDMI_SCDC_SOURCE_VERSION;
562bc33f5e5SWenjing Liu 		write_buffer[1] = 1;
563bc33f5e5SWenjing Liu 		link_query_ddc_data(ddc_service, slave_address,
564bc33f5e5SWenjing Liu 				write_buffer, sizeof(write_buffer), NULL, 0);
565bc33f5e5SWenjing Liu 		/*Read Request from SCDC caps*/
566bc33f5e5SWenjing Liu 	}
567bc33f5e5SWenjing Liu 	write_buffer[0] = HDMI_SCDC_TMDS_CONFIG;
568bc33f5e5SWenjing Liu 
569bc33f5e5SWenjing Liu 	if (over_340_mhz) {
570bc33f5e5SWenjing Liu 		write_buffer[1] = 3;
571bc33f5e5SWenjing Liu 	} else if (lte_340_scramble) {
572bc33f5e5SWenjing Liu 		write_buffer[1] = 1;
573bc33f5e5SWenjing Liu 	} else {
574bc33f5e5SWenjing Liu 		write_buffer[1] = 0;
575bc33f5e5SWenjing Liu 	}
576bc33f5e5SWenjing Liu 	link_query_ddc_data(ddc_service, slave_address, write_buffer,
577bc33f5e5SWenjing Liu 			sizeof(write_buffer), NULL, 0);
578bc33f5e5SWenjing Liu }
579bc33f5e5SWenjing Liu 
read_scdc_data(struct ddc_service * ddc_service)580bc33f5e5SWenjing Liu void read_scdc_data(struct ddc_service *ddc_service)
581bc33f5e5SWenjing Liu {
582bc33f5e5SWenjing Liu 	uint8_t slave_address = HDMI_SCDC_ADDRESS;
583bc33f5e5SWenjing Liu 	uint8_t offset = HDMI_SCDC_TMDS_CONFIG;
584bc33f5e5SWenjing Liu 	uint8_t tmds_config = 0;
585bc33f5e5SWenjing Liu 
586bc33f5e5SWenjing Liu 	if (ddc_service->link->local_sink &&
587bc33f5e5SWenjing Liu 		ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite)
588bc33f5e5SWenjing Liu 		return;
589bc33f5e5SWenjing Liu 
590bc33f5e5SWenjing Liu 	link_query_ddc_data(ddc_service, slave_address, &offset,
591bc33f5e5SWenjing Liu 			sizeof(offset), &tmds_config, sizeof(tmds_config));
592bc33f5e5SWenjing Liu 	if (tmds_config & 0x1) {
593bc33f5e5SWenjing Liu 		union hdmi_scdc_status_flags_data status_data = {0};
594bc33f5e5SWenjing Liu 		uint8_t scramble_status = 0;
595bc33f5e5SWenjing Liu 
596bc33f5e5SWenjing Liu 		offset = HDMI_SCDC_SCRAMBLER_STATUS;
597bc33f5e5SWenjing Liu 		link_query_ddc_data(ddc_service, slave_address,
598bc33f5e5SWenjing Liu 				&offset, sizeof(offset), &scramble_status,
599bc33f5e5SWenjing Liu 				sizeof(scramble_status));
600bc33f5e5SWenjing Liu 		offset = HDMI_SCDC_STATUS_FLAGS;
601bc33f5e5SWenjing Liu 		link_query_ddc_data(ddc_service, slave_address,
602bc33f5e5SWenjing Liu 				&offset, sizeof(offset), &status_data.byte,
603bc33f5e5SWenjing Liu 				sizeof(status_data.byte));
604bc33f5e5SWenjing Liu 	}
605bc33f5e5SWenjing Liu }
606