]>
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 | ||
c366be54 | 26 | #include <linux/delay.h> |
4fc4dca8 | 27 | #include <linux/slab.h> |
c366be54 | 28 | |
5c6ac711 | 29 | #include "dm_services.h" |
eae5ffa9 | 30 | #include "core_types.h" |
5c6ac711 BL |
31 | #include "dce_aux.h" |
32 | #include "dce/dce_11_0_sh_mask.h" | |
63b9caff | 33 | #include "dm_event_log.h" |
5c6ac711 BL |
34 | |
35 | #define CTX \ | |
65c78961 | 36 | aux110->base.ctx |
5c6ac711 BL |
37 | #define REG(reg_name)\ |
38 | (aux110->regs->reg_name) | |
39 | ||
40 | #define DC_LOGGER \ | |
65c78961 | 41 | engine->ctx->logger |
5c6ac711 BL |
42 | |
43 | #include "reg_helper.h" | |
44 | ||
8276dd87 | 45 | #undef FN |
46 | #define FN(reg_name, field_name) \ | |
47 | aux110->shift->field_name, aux110->mask->field_name | |
48 | ||
5c6ac711 BL |
49 | #define FROM_AUX_ENGINE(ptr) \ |
50 | container_of((ptr), struct aux_engine_dce110, base) | |
51 | ||
52 | #define FROM_ENGINE(ptr) \ | |
1877ccf6 | 53 | FROM_AUX_ENGINE(container_of((ptr), struct dce_aux, base)) |
5c6ac711 BL |
54 | |
55 | #define FROM_AUX_ENGINE_ENGINE(ptr) \ | |
1877ccf6 | 56 | container_of((ptr), struct dce_aux, base) |
5c6ac711 BL |
57 | enum { |
58 | AUX_INVALID_REPLY_RETRY_COUNTER = 1, | |
59 | AUX_TIMED_OUT_RETRY_COUNTER = 2, | |
60 | AUX_DEFER_RETRY_COUNTER = 6 | |
61 | }; | |
f6040a43 | 62 | |
9bffd080 | 63 | #define TIME_OUT_INCREMENT 1016 |
64 | #define TIME_OUT_MULTIPLIER_8 8 | |
65 | #define TIME_OUT_MULTIPLIER_16 16 | |
66 | #define TIME_OUT_MULTIPLIER_32 32 | |
67 | #define TIME_OUT_MULTIPLIER_64 64 | |
68 | #define MAX_TIMEOUT_LENGTH 127 | |
69 | #define DEFAULT_AUX_ENGINE_MULT 0 | |
70 | #define DEFAULT_AUX_ENGINE_LENGTH 69 | |
f6040a43 | 71 | |
5c6ac711 | 72 | static void release_engine( |
1877ccf6 | 73 | struct dce_aux *engine) |
5c6ac711 | 74 | { |
65c78961 | 75 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); |
5c6ac711 BL |
76 | |
77 | dal_ddc_close(engine->ddc); | |
78 | ||
79 | engine->ddc = NULL; | |
80 | ||
81 | REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1); | |
82 | } | |
83 | ||
84 | #define SW_CAN_ACCESS_AUX 1 | |
85 | #define DMCU_CAN_ACCESS_AUX 2 | |
86 | ||
87 | static bool is_engine_available( | |
1877ccf6 | 88 | struct dce_aux *engine) |
5c6ac711 BL |
89 | { |
90 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
91 | ||
92 | uint32_t value = REG_READ(AUX_ARB_CONTROL); | |
93 | uint32_t field = get_reg_field_value( | |
94 | value, | |
95 | AUX_ARB_CONTROL, | |
96 | AUX_REG_RW_CNTL_STATUS); | |
97 | ||
98 | return (field != DMCU_CAN_ACCESS_AUX); | |
99 | } | |
100 | static bool acquire_engine( | |
1877ccf6 | 101 | struct dce_aux *engine) |
5c6ac711 BL |
102 | { |
103 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
104 | ||
105 | uint32_t value = REG_READ(AUX_ARB_CONTROL); | |
106 | uint32_t field = get_reg_field_value( | |
107 | value, | |
108 | AUX_ARB_CONTROL, | |
109 | AUX_REG_RW_CNTL_STATUS); | |
110 | if (field == DMCU_CAN_ACCESS_AUX) | |
111 | return false; | |
112 | /* enable AUX before request SW to access AUX */ | |
113 | value = REG_READ(AUX_CONTROL); | |
114 | field = get_reg_field_value(value, | |
115 | AUX_CONTROL, | |
116 | AUX_EN); | |
117 | ||
118 | if (field == 0) { | |
119 | set_reg_field_value( | |
120 | value, | |
121 | 1, | |
122 | AUX_CONTROL, | |
123 | AUX_EN); | |
124 | ||
125 | if (REG(AUX_RESET_MASK)) { | |
126 | /*DP_AUX block as part of the enable sequence*/ | |
127 | set_reg_field_value( | |
128 | value, | |
129 | 1, | |
130 | AUX_CONTROL, | |
131 | AUX_RESET); | |
132 | } | |
133 | ||
134 | REG_WRITE(AUX_CONTROL, value); | |
135 | ||
136 | if (REG(AUX_RESET_MASK)) { | |
137 | /*poll HW to make sure reset it done*/ | |
138 | ||
139 | REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1, | |
140 | 1, 11); | |
141 | ||
142 | set_reg_field_value( | |
143 | value, | |
144 | 0, | |
145 | AUX_CONTROL, | |
146 | AUX_RESET); | |
147 | ||
148 | REG_WRITE(AUX_CONTROL, value); | |
149 | ||
150 | REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0, | |
151 | 1, 11); | |
152 | } | |
153 | } /*if (field)*/ | |
154 | ||
155 | /* request SW to access AUX */ | |
156 | REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1); | |
157 | ||
158 | value = REG_READ(AUX_ARB_CONTROL); | |
159 | field = get_reg_field_value( | |
160 | value, | |
161 | AUX_ARB_CONTROL, | |
162 | AUX_REG_RW_CNTL_STATUS); | |
163 | ||
164 | return (field == SW_CAN_ACCESS_AUX); | |
165 | } | |
166 | ||
167 | #define COMPOSE_AUX_SW_DATA_16_20(command, address) \ | |
168 | ((command) | ((0xF0000 & (address)) >> 16)) | |
169 | ||
170 | #define COMPOSE_AUX_SW_DATA_8_15(address) \ | |
171 | ((0xFF00 & (address)) >> 8) | |
172 | ||
173 | #define COMPOSE_AUX_SW_DATA_0_7(address) \ | |
174 | (0xFF & (address)) | |
175 | ||
176 | static void submit_channel_request( | |
1877ccf6 | 177 | struct dce_aux *engine, |
5c6ac711 BL |
178 | struct aux_request_transaction_data *request) |
179 | { | |
180 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
181 | uint32_t value; | |
182 | uint32_t length; | |
183 | ||
184 | bool is_write = | |
185 | ((request->type == AUX_TRANSACTION_TYPE_DP) && | |
186 | (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || | |
187 | ((request->type == AUX_TRANSACTION_TYPE_I2C) && | |
188 | ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || | |
189 | (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); | |
190 | if (REG(AUXN_IMPCAL)) { | |
191 | /* clear_aux_error */ | |
30eb85ff YS |
192 | REG_UPDATE_SEQ_2(AUXN_IMPCAL, |
193 | AUXN_CALOUT_ERROR_AK, 1, | |
194 | AUXN_CALOUT_ERROR_AK, 0); | |
5c6ac711 | 195 | |
30eb85ff YS |
196 | REG_UPDATE_SEQ_2(AUXP_IMPCAL, |
197 | AUXP_CALOUT_ERROR_AK, 1, | |
198 | AUXP_CALOUT_ERROR_AK, 0); | |
5c6ac711 BL |
199 | |
200 | /* force_default_calibrate */ | |
30eb85ff | 201 | REG_UPDATE_SEQ_2(AUXN_IMPCAL, |
5c6ac711 BL |
202 | AUXN_IMPCAL_ENABLE, 1, |
203 | AUXN_IMPCAL_OVERRIDE_ENABLE, 0); | |
204 | ||
205 | /* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */ | |
206 | ||
30eb85ff YS |
207 | REG_UPDATE_SEQ_2(AUXP_IMPCAL, |
208 | AUXP_IMPCAL_OVERRIDE_ENABLE, 1, | |
209 | AUXP_IMPCAL_OVERRIDE_ENABLE, 0); | |
5c6ac711 | 210 | } |
f4bbebf8 ML |
211 | |
212 | REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); | |
213 | ||
214 | REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, | |
f6040a43 | 215 | 10, aux110->polling_timeout_period/10); |
f4bbebf8 | 216 | |
5c6ac711 BL |
217 | /* set the delay and the number of bytes to write */ |
218 | ||
219 | /* The length include | |
220 | * the 4 bit header and the 20 bit address | |
221 | * (that is 3 byte). | |
222 | * If the requested length is non zero this means | |
223 | * an addition byte specifying the length is required. | |
224 | */ | |
225 | ||
226 | length = request->length ? 4 : 3; | |
227 | if (is_write) | |
228 | length += request->length; | |
229 | ||
230 | REG_UPDATE_2(AUX_SW_CONTROL, | |
231 | AUX_SW_START_DELAY, request->delay, | |
232 | AUX_SW_WR_BYTES, length); | |
233 | ||
234 | /* program action and address and payload data (if 'is_write') */ | |
235 | value = REG_UPDATE_4(AUX_SW_DATA, | |
236 | AUX_SW_INDEX, 0, | |
237 | AUX_SW_DATA_RW, 0, | |
238 | AUX_SW_AUTOINCREMENT_DISABLE, 1, | |
239 | AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); | |
240 | ||
241 | value = REG_SET_2(AUX_SW_DATA, value, | |
242 | AUX_SW_AUTOINCREMENT_DISABLE, 0, | |
243 | AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); | |
244 | ||
245 | value = REG_SET(AUX_SW_DATA, value, | |
246 | AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); | |
247 | ||
248 | if (request->length) { | |
249 | value = REG_SET(AUX_SW_DATA, value, | |
250 | AUX_SW_DATA, request->length - 1); | |
251 | } | |
252 | ||
253 | if (is_write) { | |
254 | /* Load the HW buffer with the Data to be sent. | |
255 | * This is relevant for write operation. | |
256 | * For read, the data recived data will be | |
257 | * processed in process_channel_reply(). | |
258 | */ | |
259 | uint32_t i = 0; | |
260 | ||
261 | while (i < request->length) { | |
262 | value = REG_SET(AUX_SW_DATA, value, | |
263 | AUX_SW_DATA, request->data[i]); | |
264 | ||
265 | ++i; | |
266 | } | |
267 | } | |
268 | ||
5c6ac711 | 269 | REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); |
63b9caff CH |
270 | EVENT_LOG_AUX_REQ(engine->ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_NATIVE, |
271 | request->action, request->address, request->length, request->data); | |
5c6ac711 BL |
272 | } |
273 | ||
1877ccf6 | 274 | static int read_channel_reply(struct dce_aux *engine, uint32_t size, |
5c6ac711 BL |
275 | uint8_t *buffer, uint8_t *reply_result, |
276 | uint32_t *sw_status) | |
277 | { | |
278 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
279 | uint32_t bytes_replied; | |
280 | uint32_t reply_result_32; | |
281 | ||
282 | *sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, | |
283 | &bytes_replied); | |
284 | ||
285 | /* In case HPD is LOW, exit AUX transaction */ | |
286 | if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) | |
287 | return -1; | |
288 | ||
289 | /* Need at least the status byte */ | |
290 | if (!bytes_replied) | |
291 | return -1; | |
292 | ||
30eb85ff | 293 | REG_UPDATE_SEQ_3(AUX_SW_DATA, |
5c6ac711 BL |
294 | AUX_SW_INDEX, 0, |
295 | AUX_SW_AUTOINCREMENT_DISABLE, 1, | |
296 | AUX_SW_DATA_RW, 1); | |
297 | ||
298 | REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32); | |
299 | reply_result_32 = reply_result_32 >> 4; | |
ad6756b4 DF |
300 | if (reply_result != NULL) |
301 | *reply_result = (uint8_t)reply_result_32; | |
5c6ac711 BL |
302 | |
303 | if (reply_result_32 == 0) { /* ACK */ | |
304 | uint32_t i = 0; | |
305 | ||
306 | /* First byte was already used to get the command status */ | |
307 | --bytes_replied; | |
308 | ||
309 | /* Do not overflow buffer */ | |
310 | if (bytes_replied > size) | |
311 | return -1; | |
312 | ||
313 | while (i < bytes_replied) { | |
314 | uint32_t aux_sw_data_val; | |
315 | ||
316 | REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val); | |
317 | buffer[i] = aux_sw_data_val; | |
318 | ++i; | |
319 | } | |
320 | ||
321 | return i; | |
322 | } | |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
5c6ac711 | 327 | static enum aux_channel_operation_result get_channel_status( |
1877ccf6 | 328 | struct dce_aux *engine, |
5c6ac711 BL |
329 | uint8_t *returned_bytes) |
330 | { | |
331 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); | |
332 | ||
333 | uint32_t value; | |
334 | ||
335 | if (returned_bytes == NULL) { | |
336 | /*caller pass NULL pointer*/ | |
337 | ASSERT_CRITICAL(false); | |
338 | return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; | |
339 | } | |
340 | *returned_bytes = 0; | |
341 | ||
342 | /* poll to make sure that SW_DONE is asserted */ | |
335d5d7b | 343 | REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, |
f6040a43 | 344 | 10, aux110->polling_timeout_period/10); |
5c6ac711 | 345 | |
335d5d7b | 346 | value = REG_READ(AUX_SW_STATUS); |
5c6ac711 BL |
347 | /* in case HPD is LOW, exit AUX transaction */ |
348 | if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) | |
349 | return AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON; | |
350 | ||
351 | /* Note that the following bits are set in 'status.bits' | |
352 | * during CTS 4.2.1.2 (FW 3.3.1): | |
353 | * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, | |
354 | * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. | |
355 | * | |
356 | * AUX_SW_RX_MIN_COUNT_VIOL is an internal, | |
357 | * HW debugging bit and should be ignored. | |
358 | */ | |
359 | if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { | |
360 | if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || | |
361 | (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) | |
362 | return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; | |
363 | ||
364 | else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || | |
365 | (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || | |
366 | (value & | |
367 | AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || | |
368 | (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) | |
369 | return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; | |
370 | ||
371 | *returned_bytes = get_reg_field_value(value, | |
372 | AUX_SW_STATUS, | |
373 | AUX_SW_REPLY_BYTE_COUNT); | |
374 | ||
375 | if (*returned_bytes == 0) | |
376 | return | |
377 | AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; | |
378 | else { | |
379 | *returned_bytes -= 1; | |
380 | return AUX_CHANNEL_OPERATION_SUCCEEDED; | |
381 | } | |
382 | } else { | |
383 | /*time_elapsed >= aux_engine->timeout_period | |
384 | * AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point | |
385 | */ | |
386 | ASSERT_CRITICAL(false); | |
387 | return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; | |
388 | } | |
389 | } | |
5c6ac711 | 390 | |
5c6ac711 | 391 | enum i2caux_engine_type get_engine_type( |
1877ccf6 | 392 | const struct dce_aux *engine) |
5c6ac711 BL |
393 | { |
394 | return I2CAUX_ENGINE_TYPE_AUX; | |
395 | } | |
396 | ||
65c78961 | 397 | static bool acquire( |
1877ccf6 | 398 | struct dce_aux *engine, |
5c6ac711 BL |
399 | struct ddc *ddc) |
400 | { | |
5c6ac711 BL |
401 | enum gpio_result result; |
402 | ||
2b63d0ec | 403 | if ((engine == NULL) || !is_engine_available(engine)) |
1877ccf6 | 404 | return false; |
5c6ac711 BL |
405 | |
406 | result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, | |
407 | GPIO_DDC_CONFIG_TYPE_MODE_AUX); | |
408 | ||
409 | if (result != GPIO_RESULT_OK) | |
65c78961 | 410 | return false; |
5c6ac711 | 411 | |
1877ccf6 | 412 | if (!acquire_engine(engine)) { |
5c6ac711 | 413 | dal_ddc_close(ddc); |
65c78961 | 414 | return false; |
5c6ac711 BL |
415 | } |
416 | ||
417 | engine->ddc = ddc; | |
418 | ||
65c78961 | 419 | return true; |
5c6ac711 BL |
420 | } |
421 | ||
1877ccf6 | 422 | void dce110_engine_destroy(struct dce_aux **engine) |
5c6ac711 BL |
423 | { |
424 | ||
65c78961 | 425 | struct aux_engine_dce110 *engine110 = FROM_AUX_ENGINE(*engine); |
5c6ac711 BL |
426 | |
427 | kfree(engine110); | |
428 | *engine = NULL; | |
429 | ||
430 | } | |
8276dd87 | 431 | |
9bffd080 | 432 | static uint32_t dce_aux_configure_timeout(struct ddc_service *ddc, |
f6040a43 | 433 | uint32_t timeout_in_us) |
434 | { | |
435 | uint32_t multiplier = 0; | |
436 | uint32_t length = 0; | |
9bffd080 | 437 | uint32_t prev_length = 0; |
438 | uint32_t prev_mult = 0; | |
439 | uint32_t prev_timeout_val = 0; | |
f6040a43 | 440 | struct ddc *ddc_pin = ddc->ddc_pin; |
441 | struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; | |
442 | struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(aux_engine); | |
443 | ||
444 | /* 1-Update polling timeout period */ | |
445 | aux110->polling_timeout_period = timeout_in_us * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER; | |
446 | ||
447 | /* 2-Update aux timeout period length and multiplier */ | |
9bffd080 | 448 | if (timeout_in_us == 0) { |
449 | multiplier = DEFAULT_AUX_ENGINE_MULT; | |
450 | length = DEFAULT_AUX_ENGINE_LENGTH; | |
451 | } else if (timeout_in_us <= TIME_OUT_INCREMENT) { | |
f6040a43 | 452 | multiplier = 0; |
453 | length = timeout_in_us/TIME_OUT_MULTIPLIER_8; | |
454 | if (timeout_in_us % TIME_OUT_MULTIPLIER_8 != 0) | |
455 | length++; | |
f6040a43 | 456 | } else if (timeout_in_us <= 2 * TIME_OUT_INCREMENT) { |
457 | multiplier = 1; | |
458 | length = timeout_in_us/TIME_OUT_MULTIPLIER_16; | |
459 | if (timeout_in_us % TIME_OUT_MULTIPLIER_16 != 0) | |
460 | length++; | |
f6040a43 | 461 | } else if (timeout_in_us <= 4 * TIME_OUT_INCREMENT) { |
462 | multiplier = 2; | |
463 | length = timeout_in_us/TIME_OUT_MULTIPLIER_32; | |
464 | if (timeout_in_us % TIME_OUT_MULTIPLIER_32 != 0) | |
465 | length++; | |
f6040a43 | 466 | } else if (timeout_in_us > 4 * TIME_OUT_INCREMENT) { |
467 | multiplier = 3; | |
468 | length = timeout_in_us/TIME_OUT_MULTIPLIER_64; | |
469 | if (timeout_in_us % TIME_OUT_MULTIPLIER_64 != 0) | |
470 | length++; | |
f6040a43 | 471 | } |
472 | ||
473 | length = (length < MAX_TIMEOUT_LENGTH) ? length : MAX_TIMEOUT_LENGTH; | |
474 | ||
9bffd080 | 475 | REG_GET_2(AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN, &prev_length, AUX_RX_TIMEOUT_LEN_MUL, &prev_mult); |
476 | ||
477 | switch (prev_mult) { | |
478 | case 0: | |
479 | prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_8; | |
480 | break; | |
481 | case 1: | |
482 | prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_16; | |
483 | break; | |
484 | case 2: | |
485 | prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_32; | |
486 | break; | |
487 | case 3: | |
488 | prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_64; | |
489 | break; | |
490 | default: | |
491 | prev_timeout_val = DEFAULT_AUX_ENGINE_LENGTH * TIME_OUT_MULTIPLIER_8; | |
492 | break; | |
493 | } | |
494 | ||
f6040a43 | 495 | REG_UPDATE_SEQ_2(AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN, length, AUX_RX_TIMEOUT_LEN_MUL, multiplier); |
496 | ||
9bffd080 | 497 | return prev_timeout_val; |
f6040a43 | 498 | } |
499 | ||
500 | static struct dce_aux_funcs aux_functions = { | |
501 | .configure_timeout = NULL, | |
502 | .destroy = NULL, | |
503 | }; | |
504 | ||
1877ccf6 | 505 | struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, |
5c6ac711 BL |
506 | struct dc_context *ctx, |
507 | uint32_t inst, | |
508 | uint32_t timeout_period, | |
8276dd87 | 509 | const struct dce110_aux_registers *regs, |
510 | const struct dce110_aux_registers_mask *mask, | |
f6040a43 | 511 | const struct dce110_aux_registers_shift *shift, |
512 | bool is_ext_aux_timeout_configurable) | |
5c6ac711 | 513 | { |
65c78961 BL |
514 | aux_engine110->base.ddc = NULL; |
515 | aux_engine110->base.ctx = ctx; | |
5c6ac711 BL |
516 | aux_engine110->base.delay = 0; |
517 | aux_engine110->base.max_defer_write_retry = 0; | |
65c78961 | 518 | aux_engine110->base.inst = inst; |
f6040a43 | 519 | aux_engine110->polling_timeout_period = timeout_period; |
5c6ac711 BL |
520 | aux_engine110->regs = regs; |
521 | ||
8276dd87 | 522 | aux_engine110->mask = mask; |
523 | aux_engine110->shift = shift; | |
f6040a43 | 524 | aux_engine110->base.funcs = &aux_functions; |
525 | if (is_ext_aux_timeout_configurable) | |
526 | aux_engine110->base.funcs->configure_timeout = &dce_aux_configure_timeout; | |
527 | ||
5c6ac711 BL |
528 | return &aux_engine110->base; |
529 | } | |
530 | ||
eae5ffa9 DF |
531 | static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payload *payload) |
532 | { | |
533 | if (payload->i2c_over_aux) { | |
534 | if (payload->write) { | |
535 | if (payload->mot) | |
536 | return I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; | |
537 | return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; | |
538 | } | |
539 | if (payload->mot) | |
540 | return I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; | |
541 | return I2CAUX_TRANSACTION_ACTION_I2C_READ; | |
542 | } | |
543 | if (payload->write) | |
544 | return I2CAUX_TRANSACTION_ACTION_DP_WRITE; | |
545 | return I2CAUX_TRANSACTION_ACTION_DP_READ; | |
546 | } | |
547 | ||
8ae5b1d7 TL |
548 | int dce_aux_transfer_raw(struct ddc_service *ddc, |
549 | struct aux_payload *payload, | |
550 | enum aux_channel_operation_result *operation_result) | |
eae5ffa9 DF |
551 | { |
552 | struct ddc *ddc_pin = ddc->ddc_pin; | |
1877ccf6 | 553 | struct dce_aux *aux_engine; |
eae5ffa9 DF |
554 | struct aux_request_transaction_data aux_req; |
555 | struct aux_reply_transaction_data aux_rep; | |
556 | uint8_t returned_bytes = 0; | |
557 | int res = -1; | |
558 | uint32_t status; | |
559 | ||
560 | memset(&aux_req, 0, sizeof(aux_req)); | |
561 | memset(&aux_rep, 0, sizeof(aux_rep)); | |
562 | ||
563 | aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; | |
e9c93e5a AK |
564 | if (!acquire(aux_engine, ddc_pin)) { |
565 | *operation_result = AUX_CHANNEL_OPERATION_FAILED_ENGINE_ACQUIRE; | |
dcf1a988 | 566 | return -1; |
e9c93e5a | 567 | } |
eae5ffa9 DF |
568 | |
569 | if (payload->i2c_over_aux) | |
570 | aux_req.type = AUX_TRANSACTION_TYPE_I2C; | |
571 | else | |
572 | aux_req.type = AUX_TRANSACTION_TYPE_DP; | |
573 | ||
574 | aux_req.action = i2caux_action_from_payload(payload); | |
575 | ||
576 | aux_req.address = payload->address; | |
33fd2900 | 577 | aux_req.delay = 0; |
eae5ffa9 DF |
578 | aux_req.length = payload->length; |
579 | aux_req.data = payload->data; | |
580 | ||
1877ccf6 | 581 | submit_channel_request(aux_engine, &aux_req); |
8ae5b1d7 TL |
582 | *operation_result = get_channel_status(aux_engine, &returned_bytes); |
583 | ||
584 | if (*operation_result == AUX_CHANNEL_OPERATION_SUCCEEDED) { | |
63b9caff CH |
585 | int bytes_replied = 0; |
586 | bytes_replied = read_channel_reply(aux_engine, payload->length, | |
8ae5b1d7 TL |
587 | payload->data, payload->reply, |
588 | &status); | |
63b9caff CH |
589 | EVENT_LOG_AUX_REP(aux_engine->ddc->pin_data->en, |
590 | EVENT_LOG_AUX_ORIGIN_NATIVE, *payload->reply, | |
591 | bytes_replied, payload->data); | |
8ae5b1d7 TL |
592 | res = returned_bytes; |
593 | } else { | |
eae5ffa9 | 594 | res = -1; |
eae5ffa9 | 595 | } |
8ae5b1d7 | 596 | |
1877ccf6 | 597 | release_engine(aux_engine); |
eae5ffa9 DF |
598 | return res; |
599 | } | |
600 | ||
8ae5b1d7 TL |
601 | #define AUX_MAX_RETRIES 7 |
602 | #define AUX_MAX_DEFER_RETRIES 7 | |
603 | #define AUX_MAX_I2C_DEFER_RETRIES 7 | |
604 | #define AUX_MAX_INVALID_REPLY_RETRIES 2 | |
605 | #define AUX_MAX_TIMEOUT_RETRIES 3 | |
eae5ffa9 DF |
606 | |
607 | bool dce_aux_transfer_with_retries(struct ddc_service *ddc, | |
608 | struct aux_payload *payload) | |
609 | { | |
610 | int i, ret = 0; | |
611 | uint8_t reply; | |
612 | bool payload_reply = true; | |
8ae5b1d7 | 613 | enum aux_channel_operation_result operation_result; |
772c0b59 GS |
614 | bool retry_on_defer = false; |
615 | ||
8ae5b1d7 TL |
616 | int aux_ack_retries = 0, |
617 | aux_defer_retries = 0, | |
618 | aux_i2c_defer_retries = 0, | |
619 | aux_timeout_retries = 0, | |
620 | aux_invalid_reply_retries = 0; | |
eae5ffa9 DF |
621 | |
622 | if (!payload->reply) { | |
623 | payload_reply = false; | |
624 | payload->reply = &reply; | |
625 | } | |
626 | ||
8ae5b1d7 TL |
627 | for (i = 0; i < AUX_MAX_RETRIES; i++) { |
628 | ret = dce_aux_transfer_raw(ddc, payload, &operation_result); | |
629 | switch (operation_result) { | |
630 | case AUX_CHANNEL_OPERATION_SUCCEEDED: | |
631 | aux_timeout_retries = 0; | |
632 | aux_invalid_reply_retries = 0; | |
633 | ||
634 | switch (*payload->reply) { | |
635 | case AUX_TRANSACTION_REPLY_AUX_ACK: | |
636 | if (!payload->write && payload->length != ret) { | |
637 | if (++aux_ack_retries >= AUX_MAX_RETRIES) | |
638 | goto fail; | |
639 | else | |
640 | udelay(300); | |
641 | } else | |
642 | return true; | |
643 | break; | |
644 | ||
645 | case AUX_TRANSACTION_REPLY_AUX_DEFER: | |
0dcdcc89 | 646 | case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER: |
772c0b59 | 647 | retry_on_defer = true; |
f794913c | 648 | /* fall through */ |
772c0b59 | 649 | case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_NACK: |
33fd2900 | 650 | if (++aux_defer_retries >= AUX_MAX_DEFER_RETRIES) { |
8ae5b1d7 | 651 | goto fail; |
33fd2900 XY |
652 | } else { |
653 | if ((*payload->reply == AUX_TRANSACTION_REPLY_AUX_DEFER) || | |
654 | (*payload->reply == AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER)) { | |
655 | if (payload->defer_delay > 0) | |
656 | msleep(payload->defer_delay); | |
657 | } | |
658 | } | |
8ae5b1d7 TL |
659 | break; |
660 | ||
661 | case AUX_TRANSACTION_REPLY_I2C_DEFER: | |
662 | aux_defer_retries = 0; | |
663 | if (++aux_i2c_defer_retries >= AUX_MAX_I2C_DEFER_RETRIES) | |
664 | goto fail; | |
665 | break; | |
666 | ||
667 | case AUX_TRANSACTION_REPLY_AUX_NACK: | |
668 | case AUX_TRANSACTION_REPLY_HPD_DISCON: | |
669 | default: | |
670 | goto fail; | |
eae5ffa9 | 671 | } |
8ae5b1d7 TL |
672 | break; |
673 | ||
674 | case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: | |
675 | if (++aux_invalid_reply_retries >= AUX_MAX_INVALID_REPLY_RETRIES) | |
676 | goto fail; | |
677 | else | |
678 | udelay(400); | |
679 | break; | |
680 | ||
681 | case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: | |
772c0b59 GS |
682 | // Check whether a DEFER had occurred before the timeout. |
683 | // If so, treat timeout as a DEFER. | |
684 | if (retry_on_defer) { | |
685 | if (++aux_defer_retries >= AUX_MAX_DEFER_RETRIES) | |
686 | goto fail; | |
687 | else if (payload->defer_delay > 0) | |
688 | msleep(payload->defer_delay); | |
689 | } else { | |
690 | if (++aux_timeout_retries >= AUX_MAX_TIMEOUT_RETRIES) | |
691 | goto fail; | |
692 | else { | |
693 | /* | |
694 | * DP 1.4, 2.8.2: AUX Transaction Response/Reply Timeouts | |
695 | * According to the DP spec there should be 3 retries total | |
696 | * with a 400us wait inbetween each. Hardware already waits | |
697 | * for 550us therefore no wait is required here. | |
698 | */ | |
699 | } | |
8ae5b1d7 TL |
700 | } |
701 | break; | |
eae5ffa9 | 702 | |
8ae5b1d7 | 703 | case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: |
e9c93e5a | 704 | case AUX_CHANNEL_OPERATION_FAILED_ENGINE_ACQUIRE: |
8ae5b1d7 TL |
705 | case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: |
706 | default: | |
707 | goto fail; | |
708 | } | |
eae5ffa9 | 709 | } |
8ae5b1d7 TL |
710 | |
711 | fail: | |
712 | if (!payload_reply) | |
713 | payload->reply = NULL; | |
eae5ffa9 DF |
714 | return false; |
715 | } |