]>
Commit | Line | Data |
---|---|---|
5c6ac711 BL |
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 | #include "dm_services.h" | |
eae5ffa9 | 27 | #include "core_types.h" |
5c6ac711 BL |
28 | #include "dce_aux.h" |
29 | #include "dce/dce_11_0_sh_mask.h" | |
30 | ||
31 | #define CTX \ | |
65c78961 | 32 | aux110->base.ctx |
5c6ac711 BL |
33 | #define REG(reg_name)\ |
34 | (aux110->regs->reg_name) | |
35 | ||
36 | #define DC_LOGGER \ | |
65c78961 | 37 | engine->ctx->logger |
5c6ac711 BL |
38 | |
39 | #include "reg_helper.h" | |
40 | ||
41 | #define FROM_AUX_ENGINE(ptr) \ | |
42 | container_of((ptr), struct aux_engine_dce110, base) | |
43 | ||
44 | #define FROM_ENGINE(ptr) \ | |
1877ccf6 | 45 | FROM_AUX_ENGINE(container_of((ptr), struct dce_aux, base)) |
5c6ac711 BL |
46 | |
47 | #define FROM_AUX_ENGINE_ENGINE(ptr) \ | |
1877ccf6 | 48 | container_of((ptr), struct dce_aux, base) |
5c6ac711 BL |
49 | enum { |
50 | AUX_INVALID_REPLY_RETRY_COUNTER = 1, | |
51 | AUX_TIMED_OUT_RETRY_COUNTER = 2, | |
52 | AUX_DEFER_RETRY_COUNTER = 6 | |
53 | }; | |
54 | static void release_engine( | |
1877ccf6 | 55 | struct dce_aux *engine) |
5c6ac711 | 56 | { |
65c78961 | 57 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); |
5c6ac711 BL |
58 | |
59 | dal_ddc_close(engine->ddc); | |
60 | ||
61 | engine->ddc = NULL; | |
62 | ||
63 | REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1); | |
64 | } | |
65 | ||
66 | #define SW_CAN_ACCESS_AUX 1 | |
67 | #define DMCU_CAN_ACCESS_AUX 2 | |
68 | ||
69 | static bool is_engine_available( | |
1877ccf6 | 70 | struct dce_aux *engine) |
5c6ac711 BL |
71 | { |
72 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
73 | ||
74 | uint32_t value = REG_READ(AUX_ARB_CONTROL); | |
75 | uint32_t field = get_reg_field_value( | |
76 | value, | |
77 | AUX_ARB_CONTROL, | |
78 | AUX_REG_RW_CNTL_STATUS); | |
79 | ||
80 | return (field != DMCU_CAN_ACCESS_AUX); | |
81 | } | |
82 | static bool acquire_engine( | |
1877ccf6 | 83 | struct dce_aux *engine) |
5c6ac711 BL |
84 | { |
85 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
86 | ||
87 | uint32_t value = REG_READ(AUX_ARB_CONTROL); | |
88 | uint32_t field = get_reg_field_value( | |
89 | value, | |
90 | AUX_ARB_CONTROL, | |
91 | AUX_REG_RW_CNTL_STATUS); | |
92 | if (field == DMCU_CAN_ACCESS_AUX) | |
93 | return false; | |
94 | /* enable AUX before request SW to access AUX */ | |
95 | value = REG_READ(AUX_CONTROL); | |
96 | field = get_reg_field_value(value, | |
97 | AUX_CONTROL, | |
98 | AUX_EN); | |
99 | ||
100 | if (field == 0) { | |
101 | set_reg_field_value( | |
102 | value, | |
103 | 1, | |
104 | AUX_CONTROL, | |
105 | AUX_EN); | |
106 | ||
107 | if (REG(AUX_RESET_MASK)) { | |
108 | /*DP_AUX block as part of the enable sequence*/ | |
109 | set_reg_field_value( | |
110 | value, | |
111 | 1, | |
112 | AUX_CONTROL, | |
113 | AUX_RESET); | |
114 | } | |
115 | ||
116 | REG_WRITE(AUX_CONTROL, value); | |
117 | ||
118 | if (REG(AUX_RESET_MASK)) { | |
119 | /*poll HW to make sure reset it done*/ | |
120 | ||
121 | REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1, | |
122 | 1, 11); | |
123 | ||
124 | set_reg_field_value( | |
125 | value, | |
126 | 0, | |
127 | AUX_CONTROL, | |
128 | AUX_RESET); | |
129 | ||
130 | REG_WRITE(AUX_CONTROL, value); | |
131 | ||
132 | REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0, | |
133 | 1, 11); | |
134 | } | |
135 | } /*if (field)*/ | |
136 | ||
137 | /* request SW to access AUX */ | |
138 | REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1); | |
139 | ||
140 | value = REG_READ(AUX_ARB_CONTROL); | |
141 | field = get_reg_field_value( | |
142 | value, | |
143 | AUX_ARB_CONTROL, | |
144 | AUX_REG_RW_CNTL_STATUS); | |
145 | ||
146 | return (field == SW_CAN_ACCESS_AUX); | |
147 | } | |
148 | ||
149 | #define COMPOSE_AUX_SW_DATA_16_20(command, address) \ | |
150 | ((command) | ((0xF0000 & (address)) >> 16)) | |
151 | ||
152 | #define COMPOSE_AUX_SW_DATA_8_15(address) \ | |
153 | ((0xFF00 & (address)) >> 8) | |
154 | ||
155 | #define COMPOSE_AUX_SW_DATA_0_7(address) \ | |
156 | (0xFF & (address)) | |
157 | ||
158 | static void submit_channel_request( | |
1877ccf6 | 159 | struct dce_aux *engine, |
5c6ac711 BL |
160 | struct aux_request_transaction_data *request) |
161 | { | |
162 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
163 | uint32_t value; | |
164 | uint32_t length; | |
165 | ||
166 | bool is_write = | |
167 | ((request->type == AUX_TRANSACTION_TYPE_DP) && | |
168 | (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || | |
169 | ((request->type == AUX_TRANSACTION_TYPE_I2C) && | |
170 | ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || | |
171 | (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); | |
172 | if (REG(AUXN_IMPCAL)) { | |
173 | /* clear_aux_error */ | |
174 | REG_UPDATE_SEQ(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, | |
175 | 1, | |
176 | 0); | |
177 | ||
178 | REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, | |
179 | 1, | |
180 | 0); | |
181 | ||
182 | /* force_default_calibrate */ | |
183 | REG_UPDATE_1BY1_2(AUXN_IMPCAL, | |
184 | AUXN_IMPCAL_ENABLE, 1, | |
185 | AUXN_IMPCAL_OVERRIDE_ENABLE, 0); | |
186 | ||
187 | /* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */ | |
188 | ||
189 | REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, | |
190 | 1, | |
191 | 0); | |
192 | } | |
193 | /* set the delay and the number of bytes to write */ | |
194 | ||
195 | /* The length include | |
196 | * the 4 bit header and the 20 bit address | |
197 | * (that is 3 byte). | |
198 | * If the requested length is non zero this means | |
199 | * an addition byte specifying the length is required. | |
200 | */ | |
201 | ||
202 | length = request->length ? 4 : 3; | |
203 | if (is_write) | |
204 | length += request->length; | |
205 | ||
206 | REG_UPDATE_2(AUX_SW_CONTROL, | |
207 | AUX_SW_START_DELAY, request->delay, | |
208 | AUX_SW_WR_BYTES, length); | |
209 | ||
210 | /* program action and address and payload data (if 'is_write') */ | |
211 | value = REG_UPDATE_4(AUX_SW_DATA, | |
212 | AUX_SW_INDEX, 0, | |
213 | AUX_SW_DATA_RW, 0, | |
214 | AUX_SW_AUTOINCREMENT_DISABLE, 1, | |
215 | AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); | |
216 | ||
217 | value = REG_SET_2(AUX_SW_DATA, value, | |
218 | AUX_SW_AUTOINCREMENT_DISABLE, 0, | |
219 | AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); | |
220 | ||
221 | value = REG_SET(AUX_SW_DATA, value, | |
222 | AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); | |
223 | ||
224 | if (request->length) { | |
225 | value = REG_SET(AUX_SW_DATA, value, | |
226 | AUX_SW_DATA, request->length - 1); | |
227 | } | |
228 | ||
229 | if (is_write) { | |
230 | /* Load the HW buffer with the Data to be sent. | |
231 | * This is relevant for write operation. | |
232 | * For read, the data recived data will be | |
233 | * processed in process_channel_reply(). | |
234 | */ | |
235 | uint32_t i = 0; | |
236 | ||
237 | while (i < request->length) { | |
238 | value = REG_SET(AUX_SW_DATA, value, | |
239 | AUX_SW_DATA, request->data[i]); | |
240 | ||
241 | ++i; | |
242 | } | |
243 | } | |
244 | ||
245 | REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); | |
246 | REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, | |
247 | 10, aux110->timeout_period/10); | |
248 | REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); | |
249 | } | |
250 | ||
1877ccf6 | 251 | static int read_channel_reply(struct dce_aux *engine, uint32_t size, |
5c6ac711 BL |
252 | uint8_t *buffer, uint8_t *reply_result, |
253 | uint32_t *sw_status) | |
254 | { | |
255 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
256 | uint32_t bytes_replied; | |
257 | uint32_t reply_result_32; | |
258 | ||
259 | *sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, | |
260 | &bytes_replied); | |
261 | ||
262 | /* In case HPD is LOW, exit AUX transaction */ | |
263 | if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) | |
264 | return -1; | |
265 | ||
266 | /* Need at least the status byte */ | |
267 | if (!bytes_replied) | |
268 | return -1; | |
269 | ||
270 | REG_UPDATE_1BY1_3(AUX_SW_DATA, | |
271 | AUX_SW_INDEX, 0, | |
272 | AUX_SW_AUTOINCREMENT_DISABLE, 1, | |
273 | AUX_SW_DATA_RW, 1); | |
274 | ||
275 | REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32); | |
276 | reply_result_32 = reply_result_32 >> 4; | |
ad6756b4 DF |
277 | if (reply_result != NULL) |
278 | *reply_result = (uint8_t)reply_result_32; | |
5c6ac711 BL |
279 | |
280 | if (reply_result_32 == 0) { /* ACK */ | |
281 | uint32_t i = 0; | |
282 | ||
283 | /* First byte was already used to get the command status */ | |
284 | --bytes_replied; | |
285 | ||
286 | /* Do not overflow buffer */ | |
287 | if (bytes_replied > size) | |
288 | return -1; | |
289 | ||
290 | while (i < bytes_replied) { | |
291 | uint32_t aux_sw_data_val; | |
292 | ||
293 | REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val); | |
294 | buffer[i] = aux_sw_data_val; | |
295 | ++i; | |
296 | } | |
297 | ||
298 | return i; | |
299 | } | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
5c6ac711 | 304 | static enum aux_channel_operation_result get_channel_status( |
1877ccf6 | 305 | struct dce_aux *engine, |
5c6ac711 BL |
306 | uint8_t *returned_bytes) |
307 | { | |
308 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
309 | ||
310 | uint32_t value; | |
311 | ||
312 | if (returned_bytes == NULL) { | |
313 | /*caller pass NULL pointer*/ | |
314 | ASSERT_CRITICAL(false); | |
315 | return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; | |
316 | } | |
317 | *returned_bytes = 0; | |
318 | ||
319 | /* poll to make sure that SW_DONE is asserted */ | |
320 | value = REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, | |
321 | 10, aux110->timeout_period/10); | |
322 | ||
323 | /* in case HPD is LOW, exit AUX transaction */ | |
324 | if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) | |
325 | return AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON; | |
326 | ||
327 | /* Note that the following bits are set in 'status.bits' | |
328 | * during CTS 4.2.1.2 (FW 3.3.1): | |
329 | * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, | |
330 | * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. | |
331 | * | |
332 | * AUX_SW_RX_MIN_COUNT_VIOL is an internal, | |
333 | * HW debugging bit and should be ignored. | |
334 | */ | |
335 | if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { | |
336 | if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || | |
337 | (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) | |
338 | return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; | |
339 | ||
340 | else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || | |
341 | (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || | |
342 | (value & | |
343 | AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || | |
344 | (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) | |
345 | return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; | |
346 | ||
347 | *returned_bytes = get_reg_field_value(value, | |
348 | AUX_SW_STATUS, | |
349 | AUX_SW_REPLY_BYTE_COUNT); | |
350 | ||
351 | if (*returned_bytes == 0) | |
352 | return | |
353 | AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; | |
354 | else { | |
355 | *returned_bytes -= 1; | |
356 | return AUX_CHANNEL_OPERATION_SUCCEEDED; | |
357 | } | |
358 | } else { | |
359 | /*time_elapsed >= aux_engine->timeout_period | |
360 | * AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point | |
361 | */ | |
362 | ASSERT_CRITICAL(false); | |
363 | return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; | |
364 | } | |
365 | } | |
5c6ac711 | 366 | |
5c6ac711 | 367 | enum i2caux_engine_type get_engine_type( |
1877ccf6 | 368 | const struct dce_aux *engine) |
5c6ac711 BL |
369 | { |
370 | return I2CAUX_ENGINE_TYPE_AUX; | |
371 | } | |
372 | ||
65c78961 | 373 | static bool acquire( |
1877ccf6 | 374 | struct dce_aux *engine, |
5c6ac711 BL |
375 | struct ddc *ddc) |
376 | { | |
65c78961 | 377 | |
5c6ac711 BL |
378 | enum gpio_result result; |
379 | ||
1877ccf6 DF |
380 | if (!is_engine_available(engine)) |
381 | return false; | |
5c6ac711 BL |
382 | |
383 | result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, | |
384 | GPIO_DDC_CONFIG_TYPE_MODE_AUX); | |
385 | ||
386 | if (result != GPIO_RESULT_OK) | |
65c78961 | 387 | return false; |
5c6ac711 | 388 | |
1877ccf6 | 389 | if (!acquire_engine(engine)) { |
5c6ac711 | 390 | dal_ddc_close(ddc); |
65c78961 | 391 | return false; |
5c6ac711 BL |
392 | } |
393 | ||
394 | engine->ddc = ddc; | |
395 | ||
65c78961 | 396 | return true; |
5c6ac711 BL |
397 | } |
398 | ||
1877ccf6 | 399 | void dce110_engine_destroy(struct dce_aux **engine) |
5c6ac711 BL |
400 | { |
401 | ||
65c78961 | 402 | struct aux_engine_dce110 *engine110 = FROM_AUX_ENGINE(*engine); |
5c6ac711 BL |
403 | |
404 | kfree(engine110); | |
405 | *engine = NULL; | |
406 | ||
407 | } | |
1877ccf6 | 408 | struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, |
5c6ac711 BL |
409 | struct dc_context *ctx, |
410 | uint32_t inst, | |
411 | uint32_t timeout_period, | |
412 | const struct dce110_aux_registers *regs) | |
413 | { | |
65c78961 BL |
414 | aux_engine110->base.ddc = NULL; |
415 | aux_engine110->base.ctx = ctx; | |
5c6ac711 BL |
416 | aux_engine110->base.delay = 0; |
417 | aux_engine110->base.max_defer_write_retry = 0; | |
65c78961 | 418 | aux_engine110->base.inst = inst; |
5c6ac711 BL |
419 | aux_engine110->timeout_period = timeout_period; |
420 | aux_engine110->regs = regs; | |
421 | ||
422 | return &aux_engine110->base; | |
423 | } | |
424 | ||
eae5ffa9 DF |
425 | static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payload *payload) |
426 | { | |
427 | if (payload->i2c_over_aux) { | |
428 | if (payload->write) { | |
429 | if (payload->mot) | |
430 | return I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; | |
431 | return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; | |
432 | } | |
433 | if (payload->mot) | |
434 | return I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; | |
435 | return I2CAUX_TRANSACTION_ACTION_I2C_READ; | |
436 | } | |
437 | if (payload->write) | |
438 | return I2CAUX_TRANSACTION_ACTION_DP_WRITE; | |
439 | return I2CAUX_TRANSACTION_ACTION_DP_READ; | |
440 | } | |
441 | ||
442 | int dce_aux_transfer(struct ddc_service *ddc, | |
443 | struct aux_payload *payload) | |
444 | { | |
445 | struct ddc *ddc_pin = ddc->ddc_pin; | |
1877ccf6 | 446 | struct dce_aux *aux_engine; |
eae5ffa9 DF |
447 | enum aux_channel_operation_result operation_result; |
448 | struct aux_request_transaction_data aux_req; | |
449 | struct aux_reply_transaction_data aux_rep; | |
450 | uint8_t returned_bytes = 0; | |
451 | int res = -1; | |
452 | uint32_t status; | |
453 | ||
454 | memset(&aux_req, 0, sizeof(aux_req)); | |
455 | memset(&aux_rep, 0, sizeof(aux_rep)); | |
456 | ||
457 | aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; | |
1877ccf6 | 458 | acquire(aux_engine, ddc_pin); |
eae5ffa9 DF |
459 | |
460 | if (payload->i2c_over_aux) | |
461 | aux_req.type = AUX_TRANSACTION_TYPE_I2C; | |
462 | else | |
463 | aux_req.type = AUX_TRANSACTION_TYPE_DP; | |
464 | ||
465 | aux_req.action = i2caux_action_from_payload(payload); | |
466 | ||
467 | aux_req.address = payload->address; | |
468 | aux_req.delay = payload->defer_delay * 10; | |
469 | aux_req.length = payload->length; | |
470 | aux_req.data = payload->data; | |
471 | ||
1877ccf6 DF |
472 | submit_channel_request(aux_engine, &aux_req); |
473 | operation_result = get_channel_status(aux_engine, &returned_bytes); | |
eae5ffa9 DF |
474 | |
475 | switch (operation_result) { | |
476 | case AUX_CHANNEL_OPERATION_SUCCEEDED: | |
1877ccf6 | 477 | res = read_channel_reply(aux_engine, payload->length, |
eae5ffa9 DF |
478 | payload->data, payload->reply, |
479 | &status); | |
480 | break; | |
481 | case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: | |
482 | res = 0; | |
483 | break; | |
484 | case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: | |
485 | case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: | |
486 | case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: | |
487 | res = -1; | |
488 | break; | |
489 | } | |
1877ccf6 | 490 | release_engine(aux_engine); |
eae5ffa9 DF |
491 | return res; |
492 | } | |
493 | ||
494 | #define AUX_RETRY_MAX 7 | |
495 | ||
496 | bool dce_aux_transfer_with_retries(struct ddc_service *ddc, | |
497 | struct aux_payload *payload) | |
498 | { | |
499 | int i, ret = 0; | |
500 | uint8_t reply; | |
501 | bool payload_reply = true; | |
502 | ||
503 | if (!payload->reply) { | |
504 | payload_reply = false; | |
505 | payload->reply = &reply; | |
506 | } | |
507 | ||
508 | for (i = 0; i < AUX_RETRY_MAX; i++) { | |
509 | ret = dce_aux_transfer(ddc, payload); | |
510 | ||
511 | if (ret >= 0) { | |
512 | if (*payload->reply == 0) { | |
513 | if (!payload_reply) | |
514 | payload->reply = NULL; | |
515 | return true; | |
516 | } | |
517 | } | |
518 | ||
9ca08992 | 519 | udelay(1000); |
eae5ffa9 DF |
520 | } |
521 | return false; | |
522 | } |