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, ¤t_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