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