xref: /dragonfly/sys/dev/drm/amd/display/dc/dce/dce_aux.c (revision 78973132)
1b843c749SSergey Zigachev /*
2b843c749SSergey Zigachev  * Copyright 2012-15 Advanced Micro Devices, Inc.
3b843c749SSergey Zigachev  *
4b843c749SSergey Zigachev  * Permission is hereby granted, free of charge, to any person obtaining a
5b843c749SSergey Zigachev  * copy of this software and associated documentation files (the "Software"),
6b843c749SSergey Zigachev  * to deal in the Software without restriction, including without limitation
7b843c749SSergey Zigachev  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b843c749SSergey Zigachev  * and/or sell copies of the Software, and to permit persons to whom the
9b843c749SSergey Zigachev  * Software is furnished to do so, subject to the following conditions:
10b843c749SSergey Zigachev  *
11b843c749SSergey Zigachev  * The above copyright notice and this permission notice shall be included in
12b843c749SSergey Zigachev  * all copies or substantial portions of the Software.
13b843c749SSergey Zigachev  *
14b843c749SSergey Zigachev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15b843c749SSergey Zigachev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16b843c749SSergey Zigachev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17b843c749SSergey Zigachev  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18b843c749SSergey Zigachev  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19b843c749SSergey Zigachev  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20b843c749SSergey Zigachev  * OTHER DEALINGS IN THE SOFTWARE.
21b843c749SSergey Zigachev  *
22b843c749SSergey Zigachev  * Authors: AMD
23b843c749SSergey Zigachev  *
24b843c749SSergey Zigachev  */
25b843c749SSergey Zigachev 
26b843c749SSergey Zigachev #include "dm_services.h"
27b843c749SSergey Zigachev #include "dce_aux.h"
28b843c749SSergey Zigachev #include "dce/dce_11_0_sh_mask.h"
29b843c749SSergey Zigachev 
30b843c749SSergey Zigachev #define CTX \
31b843c749SSergey Zigachev 	aux110->base.ctx
32b843c749SSergey Zigachev #define REG(reg_name)\
33b843c749SSergey Zigachev 	(aux110->regs->reg_name)
34b843c749SSergey Zigachev 
35b843c749SSergey Zigachev #define DC_LOGGER \
36b843c749SSergey Zigachev 	engine->ctx->logger
37b843c749SSergey Zigachev 
38b843c749SSergey Zigachev #include "reg_helper.h"
39b843c749SSergey Zigachev 
40b843c749SSergey Zigachev #define FROM_AUX_ENGINE(ptr) \
41b843c749SSergey Zigachev 	container_of((ptr), struct aux_engine_dce110, base)
42b843c749SSergey Zigachev 
43b843c749SSergey Zigachev #define FROM_ENGINE(ptr) \
44b843c749SSergey Zigachev 	FROM_AUX_ENGINE(container_of((ptr), struct aux_engine, base))
45b843c749SSergey Zigachev 
46b843c749SSergey Zigachev #define FROM_AUX_ENGINE_ENGINE(ptr) \
47b843c749SSergey Zigachev 	container_of((ptr), struct aux_engine, base)
48b843c749SSergey Zigachev enum {
49b843c749SSergey Zigachev 	AUX_INVALID_REPLY_RETRY_COUNTER = 1,
50b843c749SSergey Zigachev 	AUX_TIMED_OUT_RETRY_COUNTER = 2,
51b843c749SSergey Zigachev 	AUX_DEFER_RETRY_COUNTER = 6
52b843c749SSergey Zigachev };
release_engine(struct aux_engine * engine)53b843c749SSergey Zigachev static void release_engine(
54b843c749SSergey Zigachev 	struct aux_engine *engine)
55b843c749SSergey Zigachev {
56b843c749SSergey Zigachev 	struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine);
57b843c749SSergey Zigachev 
58b843c749SSergey Zigachev 	dal_ddc_close(engine->ddc);
59b843c749SSergey Zigachev 
60b843c749SSergey Zigachev 	engine->ddc = NULL;
61b843c749SSergey Zigachev 
62b843c749SSergey Zigachev 	REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1);
63b843c749SSergey Zigachev }
64b843c749SSergey Zigachev 
65b843c749SSergey Zigachev #define SW_CAN_ACCESS_AUX 1
66b843c749SSergey Zigachev #define DMCU_CAN_ACCESS_AUX 2
67b843c749SSergey Zigachev 
is_engine_available(struct aux_engine * engine)68b843c749SSergey Zigachev static bool is_engine_available(
69b843c749SSergey Zigachev 	struct aux_engine *engine)
70b843c749SSergey Zigachev {
71b843c749SSergey Zigachev 	struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine);
72b843c749SSergey Zigachev 
73b843c749SSergey Zigachev 	uint32_t value = REG_READ(AUX_ARB_CONTROL);
74b843c749SSergey Zigachev 	uint32_t field = get_reg_field_value(
75b843c749SSergey Zigachev 			value,
76b843c749SSergey Zigachev 			AUX_ARB_CONTROL,
77b843c749SSergey Zigachev 			AUX_REG_RW_CNTL_STATUS);
78b843c749SSergey Zigachev 
79b843c749SSergey Zigachev 	return (field != DMCU_CAN_ACCESS_AUX);
80b843c749SSergey Zigachev }
acquire_engine(struct aux_engine * engine)81b843c749SSergey Zigachev static bool acquire_engine(
82b843c749SSergey Zigachev 	struct aux_engine *engine)
83b843c749SSergey Zigachev {
84b843c749SSergey Zigachev 	struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine);
85b843c749SSergey Zigachev 
86b843c749SSergey Zigachev 	uint32_t value = REG_READ(AUX_ARB_CONTROL);
87b843c749SSergey Zigachev 	uint32_t field = get_reg_field_value(
88b843c749SSergey Zigachev 			value,
89b843c749SSergey Zigachev 			AUX_ARB_CONTROL,
90b843c749SSergey Zigachev 			AUX_REG_RW_CNTL_STATUS);
91b843c749SSergey Zigachev 	if (field == DMCU_CAN_ACCESS_AUX)
92b843c749SSergey Zigachev 		return false;
93b843c749SSergey Zigachev 	/* enable AUX before request SW to access AUX */
94b843c749SSergey Zigachev 	value = REG_READ(AUX_CONTROL);
95b843c749SSergey Zigachev 	field = get_reg_field_value(value,
96b843c749SSergey Zigachev 				AUX_CONTROL,
97b843c749SSergey Zigachev 				AUX_EN);
98b843c749SSergey Zigachev 
99b843c749SSergey Zigachev 	if (field == 0) {
100b843c749SSergey Zigachev 		set_reg_field_value(
101b843c749SSergey Zigachev 				value,
102b843c749SSergey Zigachev 				1,
103b843c749SSergey Zigachev 				AUX_CONTROL,
104b843c749SSergey Zigachev 				AUX_EN);
105b843c749SSergey Zigachev 
106b843c749SSergey Zigachev 		if (REG(AUX_RESET_MASK)) {
107b843c749SSergey Zigachev 			/*DP_AUX block as part of the enable sequence*/
108b843c749SSergey Zigachev 			set_reg_field_value(
109b843c749SSergey Zigachev 				value,
110b843c749SSergey Zigachev 				1,
111b843c749SSergey Zigachev 				AUX_CONTROL,
112b843c749SSergey Zigachev 				AUX_RESET);
113b843c749SSergey Zigachev 		}
114b843c749SSergey Zigachev 
115b843c749SSergey Zigachev 		REG_WRITE(AUX_CONTROL, value);
116b843c749SSergey Zigachev 
117b843c749SSergey Zigachev 		if (REG(AUX_RESET_MASK)) {
118b843c749SSergey Zigachev 			/*poll HW to make sure reset it done*/
119b843c749SSergey Zigachev 
120b843c749SSergey Zigachev 			REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1,
121b843c749SSergey Zigachev 					1, 11);
122b843c749SSergey Zigachev 
123b843c749SSergey Zigachev 			set_reg_field_value(
124b843c749SSergey Zigachev 				value,
125b843c749SSergey Zigachev 				0,
126b843c749SSergey Zigachev 				AUX_CONTROL,
127b843c749SSergey Zigachev 				AUX_RESET);
128b843c749SSergey Zigachev 
129b843c749SSergey Zigachev 			REG_WRITE(AUX_CONTROL, value);
130b843c749SSergey Zigachev 
131b843c749SSergey Zigachev 			REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0,
132b843c749SSergey Zigachev 					1, 11);
133b843c749SSergey Zigachev 		}
134b843c749SSergey Zigachev 	} /*if (field)*/
135b843c749SSergey Zigachev 
136b843c749SSergey Zigachev 	/* request SW to access AUX */
137b843c749SSergey Zigachev 	REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1);
138b843c749SSergey Zigachev 
139b843c749SSergey Zigachev 	value = REG_READ(AUX_ARB_CONTROL);
140b843c749SSergey Zigachev 	field = get_reg_field_value(
141b843c749SSergey Zigachev 			value,
142b843c749SSergey Zigachev 			AUX_ARB_CONTROL,
143b843c749SSergey Zigachev 			AUX_REG_RW_CNTL_STATUS);
144b843c749SSergey Zigachev 
145b843c749SSergey Zigachev 	return (field == SW_CAN_ACCESS_AUX);
146b843c749SSergey Zigachev }
147b843c749SSergey Zigachev 
148b843c749SSergey Zigachev #define COMPOSE_AUX_SW_DATA_16_20(command, address) \
149b843c749SSergey Zigachev 	((command) | ((0xF0000 & (address)) >> 16))
150b843c749SSergey Zigachev 
151b843c749SSergey Zigachev #define COMPOSE_AUX_SW_DATA_8_15(address) \
152b843c749SSergey Zigachev 	((0xFF00 & (address)) >> 8)
153b843c749SSergey Zigachev 
154b843c749SSergey Zigachev #define COMPOSE_AUX_SW_DATA_0_7(address) \
155b843c749SSergey Zigachev 	(0xFF & (address))
156b843c749SSergey Zigachev 
submit_channel_request(struct aux_engine * engine,struct aux_request_transaction_data * request)157b843c749SSergey Zigachev static void submit_channel_request(
158b843c749SSergey Zigachev 	struct aux_engine *engine,
159b843c749SSergey Zigachev 	struct aux_request_transaction_data *request)
160b843c749SSergey Zigachev {
161b843c749SSergey Zigachev 	struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine);
162b843c749SSergey Zigachev 	uint32_t value;
163b843c749SSergey Zigachev 	uint32_t length;
164b843c749SSergey Zigachev 
165b843c749SSergey Zigachev 	bool is_write =
166b843c749SSergey Zigachev 		((request->type == AUX_TRANSACTION_TYPE_DP) &&
167b843c749SSergey Zigachev 		 (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) ||
168b843c749SSergey Zigachev 		((request->type == AUX_TRANSACTION_TYPE_I2C) &&
169b843c749SSergey Zigachev 		((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) ||
170b843c749SSergey Zigachev 		 (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT)));
171b843c749SSergey Zigachev 	if (REG(AUXN_IMPCAL)) {
172b843c749SSergey Zigachev 		/* clear_aux_error */
173b843c749SSergey Zigachev 		REG_UPDATE_SEQ(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK,
174b843c749SSergey Zigachev 				1,
175b843c749SSergey Zigachev 				0);
176b843c749SSergey Zigachev 
177b843c749SSergey Zigachev 		REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK,
178b843c749SSergey Zigachev 				1,
179b843c749SSergey Zigachev 				0);
180b843c749SSergey Zigachev 
181b843c749SSergey Zigachev 		/* force_default_calibrate */
182b843c749SSergey Zigachev 		REG_UPDATE_1BY1_2(AUXN_IMPCAL,
183b843c749SSergey Zigachev 				AUXN_IMPCAL_ENABLE, 1,
184b843c749SSergey Zigachev 				AUXN_IMPCAL_OVERRIDE_ENABLE, 0);
185b843c749SSergey Zigachev 
186b843c749SSergey Zigachev 		/* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */
187b843c749SSergey Zigachev 
188b843c749SSergey Zigachev 		REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE,
189b843c749SSergey Zigachev 				1,
190b843c749SSergey Zigachev 				0);
191b843c749SSergey Zigachev 	}
192b843c749SSergey Zigachev 
193b843c749SSergey Zigachev 	REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1);
194b843c749SSergey Zigachev 
195b843c749SSergey Zigachev 	REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0,
196b843c749SSergey Zigachev 				10, aux110->timeout_period/10);
197b843c749SSergey Zigachev 
198b843c749SSergey Zigachev 	/* set the delay and the number of bytes to write */
199b843c749SSergey Zigachev 
200b843c749SSergey Zigachev 	/* The length include
201b843c749SSergey Zigachev 	 * the 4 bit header and the 20 bit address
202b843c749SSergey Zigachev 	 * (that is 3 byte).
203b843c749SSergey Zigachev 	 * If the requested length is non zero this means
204b843c749SSergey Zigachev 	 * an addition byte specifying the length is required.
205b843c749SSergey Zigachev 	 */
206b843c749SSergey Zigachev 
207b843c749SSergey Zigachev 	length = request->length ? 4 : 3;
208b843c749SSergey Zigachev 	if (is_write)
209b843c749SSergey Zigachev 		length += request->length;
210b843c749SSergey Zigachev 
211b843c749SSergey Zigachev 	REG_UPDATE_2(AUX_SW_CONTROL,
212b843c749SSergey Zigachev 			AUX_SW_START_DELAY, request->delay,
213b843c749SSergey Zigachev 			AUX_SW_WR_BYTES, length);
214b843c749SSergey Zigachev 
215b843c749SSergey Zigachev 	/* program action and address and payload data (if 'is_write') */
216b843c749SSergey Zigachev 	value = REG_UPDATE_4(AUX_SW_DATA,
217b843c749SSergey Zigachev 			AUX_SW_INDEX, 0,
218b843c749SSergey Zigachev 			AUX_SW_DATA_RW, 0,
219b843c749SSergey Zigachev 			AUX_SW_AUTOINCREMENT_DISABLE, 1,
220b843c749SSergey Zigachev 			AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address));
221b843c749SSergey Zigachev 
222b843c749SSergey Zigachev 	value = REG_SET_2(AUX_SW_DATA, value,
223b843c749SSergey Zigachev 			AUX_SW_AUTOINCREMENT_DISABLE, 0,
224b843c749SSergey Zigachev 			AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address));
225b843c749SSergey Zigachev 
226b843c749SSergey Zigachev 	value = REG_SET(AUX_SW_DATA, value,
227b843c749SSergey Zigachev 			AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address));
228b843c749SSergey Zigachev 
229b843c749SSergey Zigachev 	if (request->length) {
230b843c749SSergey Zigachev 		value = REG_SET(AUX_SW_DATA, value,
231b843c749SSergey Zigachev 				AUX_SW_DATA, request->length - 1);
232b843c749SSergey Zigachev 	}
233b843c749SSergey Zigachev 
234b843c749SSergey Zigachev 	if (is_write) {
235b843c749SSergey Zigachev 		/* Load the HW buffer with the Data to be sent.
236b843c749SSergey Zigachev 		 * This is relevant for write operation.
237b843c749SSergey Zigachev 		 * For read, the data recived data will be
238b843c749SSergey Zigachev 		 * processed in process_channel_reply().
239b843c749SSergey Zigachev 		 */
240b843c749SSergey Zigachev 		uint32_t i = 0;
241b843c749SSergey Zigachev 
242b843c749SSergey Zigachev 		while (i < request->length) {
243b843c749SSergey Zigachev 			value = REG_SET(AUX_SW_DATA, value,
244b843c749SSergey Zigachev 					AUX_SW_DATA, request->data[i]);
245b843c749SSergey Zigachev 
246b843c749SSergey Zigachev 			++i;
247b843c749SSergey Zigachev 		}
248b843c749SSergey Zigachev 	}
249b843c749SSergey Zigachev 
250b843c749SSergey Zigachev 	REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1);
251b843c749SSergey Zigachev }
252b843c749SSergey Zigachev 
read_channel_reply(struct aux_engine * engine,uint32_t size,uint8_t * buffer,uint8_t * reply_result,uint32_t * sw_status)253b843c749SSergey Zigachev static int read_channel_reply(struct aux_engine *engine, uint32_t size,
254b843c749SSergey Zigachev 			      uint8_t *buffer, uint8_t *reply_result,
255b843c749SSergey Zigachev 			      uint32_t *sw_status)
256b843c749SSergey Zigachev {
257b843c749SSergey Zigachev 	struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine);
258b843c749SSergey Zigachev 	uint32_t bytes_replied;
259b843c749SSergey Zigachev 	uint32_t reply_result_32;
260b843c749SSergey Zigachev 
261b843c749SSergey Zigachev 	*sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT,
262b843c749SSergey Zigachev 			     &bytes_replied);
263b843c749SSergey Zigachev 
264b843c749SSergey Zigachev 	/* In case HPD is LOW, exit AUX transaction */
265b843c749SSergey Zigachev 	if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK))
266b843c749SSergey Zigachev 		return -1;
267b843c749SSergey Zigachev 
268b843c749SSergey Zigachev 	/* Need at least the status byte */
269b843c749SSergey Zigachev 	if (!bytes_replied)
270b843c749SSergey Zigachev 		return -1;
271b843c749SSergey Zigachev 
272b843c749SSergey Zigachev 	REG_UPDATE_1BY1_3(AUX_SW_DATA,
273b843c749SSergey Zigachev 			  AUX_SW_INDEX, 0,
274b843c749SSergey Zigachev 			  AUX_SW_AUTOINCREMENT_DISABLE, 1,
275b843c749SSergey Zigachev 			  AUX_SW_DATA_RW, 1);
276b843c749SSergey Zigachev 
277b843c749SSergey Zigachev 	REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32);
278b843c749SSergey Zigachev 	reply_result_32 = reply_result_32 >> 4;
279b843c749SSergey Zigachev 	*reply_result = (uint8_t)reply_result_32;
280b843c749SSergey Zigachev 
281b843c749SSergey Zigachev 	if (reply_result_32 == 0) { /* ACK */
282b843c749SSergey Zigachev 		uint32_t i = 0;
283b843c749SSergey Zigachev 
284b843c749SSergey Zigachev 		/* First byte was already used to get the command status */
285b843c749SSergey Zigachev 		--bytes_replied;
286b843c749SSergey Zigachev 
287b843c749SSergey Zigachev 		/* Do not overflow buffer */
288b843c749SSergey Zigachev 		if (bytes_replied > size)
289b843c749SSergey Zigachev 			return -1;
290b843c749SSergey Zigachev 
291b843c749SSergey Zigachev 		while (i < bytes_replied) {
292b843c749SSergey Zigachev 			uint32_t aux_sw_data_val;
293b843c749SSergey Zigachev 
294b843c749SSergey Zigachev 			REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val);
295b843c749SSergey Zigachev 			buffer[i] = aux_sw_data_val;
296b843c749SSergey Zigachev 			++i;
297b843c749SSergey Zigachev 		}
298b843c749SSergey Zigachev 
299b843c749SSergey Zigachev 		return i;
300b843c749SSergey Zigachev 	}
301b843c749SSergey Zigachev 
302b843c749SSergey Zigachev 	return 0;
303b843c749SSergey Zigachev }
304b843c749SSergey Zigachev 
process_channel_reply(struct aux_engine * engine,struct aux_reply_transaction_data * reply)305b843c749SSergey Zigachev static void process_channel_reply(
306b843c749SSergey Zigachev 	struct aux_engine *engine,
307b843c749SSergey Zigachev 	struct aux_reply_transaction_data *reply)
308b843c749SSergey Zigachev {
309b843c749SSergey Zigachev 	int bytes_replied;
310b843c749SSergey Zigachev 	uint8_t reply_result;
311b843c749SSergey Zigachev 	uint32_t sw_status;
312b843c749SSergey Zigachev 
313b843c749SSergey Zigachev 	bytes_replied = read_channel_reply(engine, reply->length, reply->data,
314b843c749SSergey Zigachev 					   &reply_result, &sw_status);
315b843c749SSergey Zigachev 
316b843c749SSergey Zigachev 	/* in case HPD is LOW, exit AUX transaction */
317b843c749SSergey Zigachev 	if ((sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) {
318b843c749SSergey Zigachev 		reply->status = AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON;
319b843c749SSergey Zigachev 		return;
320b843c749SSergey Zigachev 	}
321b843c749SSergey Zigachev 
322b843c749SSergey Zigachev 	if (bytes_replied < 0) {
323b843c749SSergey Zigachev 		/* Need to handle an error case...
324b843c749SSergey Zigachev 		 * Hopefully, upper layer function won't call this function if
325b843c749SSergey Zigachev 		 * the number of bytes in the reply was 0, because there was
326b843c749SSergey Zigachev 		 * surely an error that was asserted that should have been
327b843c749SSergey Zigachev 		 * handled for hot plug case, this could happens
328b843c749SSergey Zigachev 		 */
329b843c749SSergey Zigachev 		if (!(sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) {
330b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_INVALID;
331b843c749SSergey Zigachev 			ASSERT_CRITICAL(false);
332b843c749SSergey Zigachev 			return;
333b843c749SSergey Zigachev 		}
334b843c749SSergey Zigachev 	} else {
335b843c749SSergey Zigachev 
336b843c749SSergey Zigachev 		switch (reply_result) {
337b843c749SSergey Zigachev 		case 0: /* ACK */
338b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_AUX_ACK;
339b843c749SSergey Zigachev 		break;
340b843c749SSergey Zigachev 		case 1: /* NACK */
341b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_AUX_NACK;
342b843c749SSergey Zigachev 		break;
343b843c749SSergey Zigachev 		case 2: /* DEFER */
344b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_AUX_DEFER;
345b843c749SSergey Zigachev 		break;
346b843c749SSergey Zigachev 		case 4: /* AUX ACK / I2C NACK */
347b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_I2C_NACK;
348b843c749SSergey Zigachev 		break;
349b843c749SSergey Zigachev 		case 8: /* AUX ACK / I2C DEFER */
350b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_I2C_DEFER;
351b843c749SSergey Zigachev 		break;
352b843c749SSergey Zigachev 		default:
353b843c749SSergey Zigachev 			reply->status = AUX_TRANSACTION_REPLY_INVALID;
354b843c749SSergey Zigachev 		}
355b843c749SSergey Zigachev 	}
356b843c749SSergey Zigachev }
357b843c749SSergey Zigachev 
get_channel_status(struct aux_engine * engine,uint8_t * returned_bytes)358b843c749SSergey Zigachev static enum aux_channel_operation_result get_channel_status(
359b843c749SSergey Zigachev 	struct aux_engine *engine,
360b843c749SSergey Zigachev 	uint8_t *returned_bytes)
361b843c749SSergey Zigachev {
362b843c749SSergey Zigachev 	struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine);
363b843c749SSergey Zigachev 
364b843c749SSergey Zigachev 	uint32_t value;
365b843c749SSergey Zigachev 
366b843c749SSergey Zigachev 	if (returned_bytes == NULL) {
367b843c749SSergey Zigachev 		/*caller pass NULL pointer*/
368b843c749SSergey Zigachev 		ASSERT_CRITICAL(false);
369b843c749SSergey Zigachev 		return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN;
370b843c749SSergey Zigachev 	}
371b843c749SSergey Zigachev 	*returned_bytes = 0;
372b843c749SSergey Zigachev 
373b843c749SSergey Zigachev 	/* poll to make sure that SW_DONE is asserted */
374b843c749SSergey Zigachev 	value = REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1,
375b843c749SSergey Zigachev 				10, aux110->timeout_period/10);
376b843c749SSergey Zigachev 
377b843c749SSergey Zigachev 	/* in case HPD is LOW, exit AUX transaction */
378b843c749SSergey Zigachev 	if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK))
379b843c749SSergey Zigachev 		return AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON;
380b843c749SSergey Zigachev 
381b843c749SSergey Zigachev 	/* Note that the following bits are set in 'status.bits'
382b843c749SSergey Zigachev 	 * during CTS 4.2.1.2 (FW 3.3.1):
383b843c749SSergey Zigachev 	 * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP,
384b843c749SSergey Zigachev 	 * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H.
385b843c749SSergey Zigachev 	 *
386b843c749SSergey Zigachev 	 * AUX_SW_RX_MIN_COUNT_VIOL is an internal,
387b843c749SSergey Zigachev 	 * HW debugging bit and should be ignored.
388b843c749SSergey Zigachev 	 */
389b843c749SSergey Zigachev 	if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) {
390b843c749SSergey Zigachev 		if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) ||
391b843c749SSergey Zigachev 			(value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK))
392b843c749SSergey Zigachev 			return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT;
393b843c749SSergey Zigachev 
394b843c749SSergey Zigachev 		else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) ||
395b843c749SSergey Zigachev 			(value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) ||
396b843c749SSergey Zigachev 			(value &
397b843c749SSergey Zigachev 				AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) ||
398b843c749SSergey Zigachev 			(value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK))
399b843c749SSergey Zigachev 			return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY;
400b843c749SSergey Zigachev 
401b843c749SSergey Zigachev 		*returned_bytes = get_reg_field_value(value,
402b843c749SSergey Zigachev 				AUX_SW_STATUS,
403b843c749SSergey Zigachev 				AUX_SW_REPLY_BYTE_COUNT);
404b843c749SSergey Zigachev 
405b843c749SSergey Zigachev 		if (*returned_bytes == 0)
406b843c749SSergey Zigachev 			return
407b843c749SSergey Zigachev 			AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY;
408b843c749SSergey Zigachev 		else {
409b843c749SSergey Zigachev 			*returned_bytes -= 1;
410b843c749SSergey Zigachev 			return AUX_CHANNEL_OPERATION_SUCCEEDED;
411b843c749SSergey Zigachev 		}
412b843c749SSergey Zigachev 	} else {
413b843c749SSergey Zigachev 		/*time_elapsed >= aux_engine->timeout_period
414b843c749SSergey Zigachev 		 *  AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point
415b843c749SSergey Zigachev 		 */
416b843c749SSergey Zigachev 		ASSERT_CRITICAL(false);
417b843c749SSergey Zigachev 		return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT;
418b843c749SSergey Zigachev 	}
419b843c749SSergey Zigachev }
process_read_reply(struct aux_engine * engine,struct read_command_context * ctx)420b843c749SSergey Zigachev static void process_read_reply(
421b843c749SSergey Zigachev 	struct aux_engine *engine,
422b843c749SSergey Zigachev 	struct read_command_context *ctx)
423b843c749SSergey Zigachev {
424b843c749SSergey Zigachev 	engine->funcs->process_channel_reply(engine, &ctx->reply);
425b843c749SSergey Zigachev 
426b843c749SSergey Zigachev 	switch (ctx->reply.status) {
427b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_AUX_ACK:
428b843c749SSergey Zigachev 		ctx->defer_retry_aux = 0;
429b843c749SSergey Zigachev 		if (ctx->returned_byte > ctx->current_read_length) {
430b843c749SSergey Zigachev 			ctx->status =
431b843c749SSergey Zigachev 				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
432b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
433b843c749SSergey Zigachev 		} else if (ctx->returned_byte < ctx->current_read_length) {
434b843c749SSergey Zigachev 			ctx->current_read_length -= ctx->returned_byte;
435b843c749SSergey Zigachev 
436b843c749SSergey Zigachev 			ctx->offset += ctx->returned_byte;
437b843c749SSergey Zigachev 
438b843c749SSergey Zigachev 			++ctx->invalid_reply_retry_aux_on_ack;
439b843c749SSergey Zigachev 
440b843c749SSergey Zigachev 			if (ctx->invalid_reply_retry_aux_on_ack >
441b843c749SSergey Zigachev 				AUX_INVALID_REPLY_RETRY_COUNTER) {
442b843c749SSergey Zigachev 				ctx->status =
443b843c749SSergey Zigachev 				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
444b843c749SSergey Zigachev 				ctx->operation_succeeded = false;
445b843c749SSergey Zigachev 			}
446b843c749SSergey Zigachev 		} else {
447b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
448b843c749SSergey Zigachev 			ctx->transaction_complete = true;
449b843c749SSergey Zigachev 			ctx->operation_succeeded = true;
450b843c749SSergey Zigachev 		}
451b843c749SSergey Zigachev 	break;
452b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_AUX_NACK:
453b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
454b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
455b843c749SSergey Zigachev 	break;
456b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_AUX_DEFER:
457b843c749SSergey Zigachev 		++ctx->defer_retry_aux;
458b843c749SSergey Zigachev 
459b843c749SSergey Zigachev 		if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) {
460b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
461b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
462b843c749SSergey Zigachev 		}
463b843c749SSergey Zigachev 	break;
464b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_I2C_DEFER:
465b843c749SSergey Zigachev 		ctx->defer_retry_aux = 0;
466b843c749SSergey Zigachev 
467b843c749SSergey Zigachev 		++ctx->defer_retry_i2c;
468b843c749SSergey Zigachev 
469b843c749SSergey Zigachev 		if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) {
470b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
471b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
472b843c749SSergey Zigachev 		}
473b843c749SSergey Zigachev 	break;
474b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_HPD_DISCON:
475b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
476b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
477b843c749SSergey Zigachev 	break;
478b843c749SSergey Zigachev 	default:
479b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
480b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
481b843c749SSergey Zigachev 	}
482b843c749SSergey Zigachev }
process_read_request(struct aux_engine * engine,struct read_command_context * ctx)483b843c749SSergey Zigachev static void process_read_request(
484b843c749SSergey Zigachev 	struct aux_engine *engine,
485b843c749SSergey Zigachev 	struct read_command_context *ctx)
486b843c749SSergey Zigachev {
487b843c749SSergey Zigachev 	enum aux_channel_operation_result operation_result;
488b843c749SSergey Zigachev 
489b843c749SSergey Zigachev 	engine->funcs->submit_channel_request(engine, &ctx->request);
490b843c749SSergey Zigachev 
491b843c749SSergey Zigachev 	operation_result = engine->funcs->get_channel_status(
492b843c749SSergey Zigachev 		engine, &ctx->returned_byte);
493b843c749SSergey Zigachev 
494b843c749SSergey Zigachev 	switch (operation_result) {
495b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_SUCCEEDED:
496b843c749SSergey Zigachev 		if (ctx->returned_byte > ctx->current_read_length) {
497b843c749SSergey Zigachev 			ctx->status =
498b843c749SSergey Zigachev 				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
499b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
500b843c749SSergey Zigachev 		} else {
501b843c749SSergey Zigachev 			ctx->timed_out_retry_aux = 0;
502b843c749SSergey Zigachev 			ctx->invalid_reply_retry_aux = 0;
503b843c749SSergey Zigachev 
504b843c749SSergey Zigachev 			ctx->reply.length = ctx->returned_byte;
505b843c749SSergey Zigachev 			ctx->reply.data = ctx->buffer;
506b843c749SSergey Zigachev 
507b843c749SSergey Zigachev 			process_read_reply(engine, ctx);
508b843c749SSergey Zigachev 		}
509b843c749SSergey Zigachev 	break;
510b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
511b843c749SSergey Zigachev 		++ctx->invalid_reply_retry_aux;
512b843c749SSergey Zigachev 
513b843c749SSergey Zigachev 		if (ctx->invalid_reply_retry_aux >
514b843c749SSergey Zigachev 			AUX_INVALID_REPLY_RETRY_COUNTER) {
515b843c749SSergey Zigachev 			ctx->status =
516b843c749SSergey Zigachev 				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
517b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
518b843c749SSergey Zigachev 		} else
519b843c749SSergey Zigachev 			udelay(400);
520b843c749SSergey Zigachev 	break;
521b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
522b843c749SSergey Zigachev 		++ctx->timed_out_retry_aux;
523b843c749SSergey Zigachev 
524b843c749SSergey Zigachev 		if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
525b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
526b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
527b843c749SSergey Zigachev 		} else {
528b843c749SSergey Zigachev 			/* DP 1.2a, table 2-58:
529b843c749SSergey Zigachev 			 * "S3: AUX Request CMD PENDING:
530b843c749SSergey Zigachev 			 * retry 3 times, with 400usec wait on each"
531b843c749SSergey Zigachev 			 * The HW timeout is set to 550usec,
532b843c749SSergey Zigachev 			 * so we should not wait here
533b843c749SSergey Zigachev 			 */
534b843c749SSergey Zigachev 		}
535b843c749SSergey Zigachev 	break;
536b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON:
537b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
538b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
539b843c749SSergey Zigachev 	break;
540b843c749SSergey Zigachev 	default:
541b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
542b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
543b843c749SSergey Zigachev 	}
544b843c749SSergey Zigachev }
read_command(struct aux_engine * engine,struct i2caux_transaction_request * request,bool middle_of_transaction)545b843c749SSergey Zigachev static bool read_command(
546b843c749SSergey Zigachev 	struct aux_engine *engine,
547b843c749SSergey Zigachev 	struct i2caux_transaction_request *request,
548b843c749SSergey Zigachev 	bool middle_of_transaction)
549b843c749SSergey Zigachev {
550b843c749SSergey Zigachev 	struct read_command_context ctx;
551b843c749SSergey Zigachev 
552b843c749SSergey Zigachev 	ctx.buffer = request->payload.data;
553b843c749SSergey Zigachev 	ctx.current_read_length = request->payload.length;
554b843c749SSergey Zigachev 	ctx.offset = 0;
555b843c749SSergey Zigachev 	ctx.timed_out_retry_aux = 0;
556b843c749SSergey Zigachev 	ctx.invalid_reply_retry_aux = 0;
557b843c749SSergey Zigachev 	ctx.defer_retry_aux = 0;
558b843c749SSergey Zigachev 	ctx.defer_retry_i2c = 0;
559b843c749SSergey Zigachev 	ctx.invalid_reply_retry_aux_on_ack = 0;
560b843c749SSergey Zigachev 	ctx.transaction_complete = false;
561b843c749SSergey Zigachev 	ctx.operation_succeeded = true;
562b843c749SSergey Zigachev 
563b843c749SSergey Zigachev 	if (request->payload.address_space ==
564b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
565b843c749SSergey Zigachev 		ctx.request.type = AUX_TRANSACTION_TYPE_DP;
566b843c749SSergey Zigachev 		ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ;
567b843c749SSergey Zigachev 		ctx.request.address = request->payload.address;
568b843c749SSergey Zigachev 	} else if (request->payload.address_space ==
569b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
570b843c749SSergey Zigachev 		ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
571b843c749SSergey Zigachev 		ctx.request.action = middle_of_transaction ?
572b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
573b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_READ;
574b843c749SSergey Zigachev 		ctx.request.address = request->payload.address >> 1;
575b843c749SSergey Zigachev 	} else {
576b843c749SSergey Zigachev 		/* in DAL2, there was no return in such case */
577b843c749SSergey Zigachev 		BREAK_TO_DEBUGGER();
578b843c749SSergey Zigachev 		return false;
579b843c749SSergey Zigachev 	}
580b843c749SSergey Zigachev 
581b843c749SSergey Zigachev 	ctx.request.delay = 0;
582b843c749SSergey Zigachev 
583b843c749SSergey Zigachev 	do {
584b843c749SSergey Zigachev 		memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length);
585b843c749SSergey Zigachev 
586b843c749SSergey Zigachev 		ctx.request.data = ctx.buffer + ctx.offset;
587b843c749SSergey Zigachev 		ctx.request.length = ctx.current_read_length;
588b843c749SSergey Zigachev 
589b843c749SSergey Zigachev 		process_read_request(engine, &ctx);
590b843c749SSergey Zigachev 
591b843c749SSergey Zigachev 		request->status = ctx.status;
592b843c749SSergey Zigachev 
593b843c749SSergey Zigachev 		if (ctx.operation_succeeded && !ctx.transaction_complete)
594b843c749SSergey Zigachev 			if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
595b843c749SSergey Zigachev 				msleep(engine->delay);
596b843c749SSergey Zigachev 	} while (ctx.operation_succeeded && !ctx.transaction_complete);
597b843c749SSergey Zigachev 
598b843c749SSergey Zigachev 	if (request->payload.address_space ==
599b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
600b843c749SSergey Zigachev 		DC_LOG_I2C_AUX("READ: addr:0x%x  value:0x%x Result:%d",
601b843c749SSergey Zigachev 				request->payload.address,
602b843c749SSergey Zigachev 				request->payload.data[0],
603b843c749SSergey Zigachev 				ctx.operation_succeeded);
604b843c749SSergey Zigachev 	}
605b843c749SSergey Zigachev 
606b843c749SSergey Zigachev 	return ctx.operation_succeeded;
607b843c749SSergey Zigachev }
608b843c749SSergey Zigachev 
process_write_reply(struct aux_engine * engine,struct write_command_context * ctx)609b843c749SSergey Zigachev static void process_write_reply(
610b843c749SSergey Zigachev 	struct aux_engine *engine,
611b843c749SSergey Zigachev 	struct write_command_context *ctx)
612b843c749SSergey Zigachev {
613b843c749SSergey Zigachev 	engine->funcs->process_channel_reply(engine, &ctx->reply);
614b843c749SSergey Zigachev 
615b843c749SSergey Zigachev 	switch (ctx->reply.status) {
616b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_AUX_ACK:
617b843c749SSergey Zigachev 		ctx->operation_succeeded = true;
618b843c749SSergey Zigachev 
619b843c749SSergey Zigachev 		if (ctx->returned_byte) {
620b843c749SSergey Zigachev 			ctx->request.action = ctx->mot ?
621b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
622b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
623b843c749SSergey Zigachev 
624b843c749SSergey Zigachev 			ctx->current_write_length = 0;
625b843c749SSergey Zigachev 
626b843c749SSergey Zigachev 			++ctx->ack_m_retry;
627b843c749SSergey Zigachev 
628b843c749SSergey Zigachev 			if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) {
629b843c749SSergey Zigachev 				ctx->status =
630b843c749SSergey Zigachev 				I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
631b843c749SSergey Zigachev 				ctx->operation_succeeded = false;
632b843c749SSergey Zigachev 			} else
633b843c749SSergey Zigachev 				udelay(300);
634b843c749SSergey Zigachev 		} else {
635b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
636b843c749SSergey Zigachev 			ctx->defer_retry_aux = 0;
637b843c749SSergey Zigachev 			ctx->ack_m_retry = 0;
638b843c749SSergey Zigachev 			ctx->transaction_complete = true;
639b843c749SSergey Zigachev 		}
640b843c749SSergey Zigachev 	break;
641b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_AUX_NACK:
642b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
643b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
644b843c749SSergey Zigachev 	break;
645b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_AUX_DEFER:
646b843c749SSergey Zigachev 		++ctx->defer_retry_aux;
647b843c749SSergey Zigachev 
648b843c749SSergey Zigachev 		if (ctx->defer_retry_aux > ctx->max_defer_retry) {
649b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
650b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
651b843c749SSergey Zigachev 		}
652b843c749SSergey Zigachev 	break;
653b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_I2C_DEFER:
654b843c749SSergey Zigachev 		ctx->defer_retry_aux = 0;
655b843c749SSergey Zigachev 		ctx->current_write_length = 0;
656b843c749SSergey Zigachev 
657b843c749SSergey Zigachev 		ctx->request.action = ctx->mot ?
658b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
659b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
660b843c749SSergey Zigachev 
661b843c749SSergey Zigachev 		++ctx->defer_retry_i2c;
662b843c749SSergey Zigachev 
663b843c749SSergey Zigachev 		if (ctx->defer_retry_i2c > ctx->max_defer_retry) {
664b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
665b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
666b843c749SSergey Zigachev 		}
667b843c749SSergey Zigachev 	break;
668b843c749SSergey Zigachev 	case AUX_TRANSACTION_REPLY_HPD_DISCON:
669b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
670b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
671b843c749SSergey Zigachev 	break;
672b843c749SSergey Zigachev 	default:
673b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
674b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
675b843c749SSergey Zigachev 	}
676b843c749SSergey Zigachev }
process_write_request(struct aux_engine * engine,struct write_command_context * ctx)677b843c749SSergey Zigachev static void process_write_request(
678b843c749SSergey Zigachev 	struct aux_engine *engine,
679b843c749SSergey Zigachev 	struct write_command_context *ctx)
680b843c749SSergey Zigachev {
681b843c749SSergey Zigachev 	enum aux_channel_operation_result operation_result;
682b843c749SSergey Zigachev 
683b843c749SSergey Zigachev 	engine->funcs->submit_channel_request(engine, &ctx->request);
684b843c749SSergey Zigachev 
685b843c749SSergey Zigachev 	operation_result = engine->funcs->get_channel_status(
686b843c749SSergey Zigachev 		engine, &ctx->returned_byte);
687b843c749SSergey Zigachev 
688b843c749SSergey Zigachev 	switch (operation_result) {
689b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_SUCCEEDED:
690b843c749SSergey Zigachev 		ctx->timed_out_retry_aux = 0;
691b843c749SSergey Zigachev 		ctx->invalid_reply_retry_aux = 0;
692b843c749SSergey Zigachev 
693b843c749SSergey Zigachev 		ctx->reply.length = ctx->returned_byte;
694b843c749SSergey Zigachev 		ctx->reply.data = ctx->reply_data;
695b843c749SSergey Zigachev 
696b843c749SSergey Zigachev 		process_write_reply(engine, ctx);
697b843c749SSergey Zigachev 	break;
698b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
699b843c749SSergey Zigachev 		++ctx->invalid_reply_retry_aux;
700b843c749SSergey Zigachev 
701b843c749SSergey Zigachev 		if (ctx->invalid_reply_retry_aux >
702b843c749SSergey Zigachev 			AUX_INVALID_REPLY_RETRY_COUNTER) {
703b843c749SSergey Zigachev 			ctx->status =
704b843c749SSergey Zigachev 				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
705b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
706b843c749SSergey Zigachev 		} else
707b843c749SSergey Zigachev 			udelay(400);
708b843c749SSergey Zigachev 	break;
709b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
710b843c749SSergey Zigachev 		++ctx->timed_out_retry_aux;
711b843c749SSergey Zigachev 
712b843c749SSergey Zigachev 		if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
713b843c749SSergey Zigachev 			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
714b843c749SSergey Zigachev 			ctx->operation_succeeded = false;
715b843c749SSergey Zigachev 		} else {
716b843c749SSergey Zigachev 			/* DP 1.2a, table 2-58:
717b843c749SSergey Zigachev 			 * "S3: AUX Request CMD PENDING:
718b843c749SSergey Zigachev 			 * retry 3 times, with 400usec wait on each"
719b843c749SSergey Zigachev 			 * The HW timeout is set to 550usec,
720b843c749SSergey Zigachev 			 * so we should not wait here
721b843c749SSergey Zigachev 			 */
722b843c749SSergey Zigachev 		}
723b843c749SSergey Zigachev 	break;
724b843c749SSergey Zigachev 	case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON:
725b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
726b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
727b843c749SSergey Zigachev 	break;
728b843c749SSergey Zigachev 	default:
729b843c749SSergey Zigachev 		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
730b843c749SSergey Zigachev 		ctx->operation_succeeded = false;
731b843c749SSergey Zigachev 	}
732b843c749SSergey Zigachev }
write_command(struct aux_engine * engine,struct i2caux_transaction_request * request,bool middle_of_transaction)733b843c749SSergey Zigachev static bool write_command(
734b843c749SSergey Zigachev 	struct aux_engine *engine,
735b843c749SSergey Zigachev 	struct i2caux_transaction_request *request,
736b843c749SSergey Zigachev 	bool middle_of_transaction)
737b843c749SSergey Zigachev {
738b843c749SSergey Zigachev 	struct write_command_context ctx;
739b843c749SSergey Zigachev 
740b843c749SSergey Zigachev 	ctx.mot = middle_of_transaction;
741b843c749SSergey Zigachev 	ctx.buffer = request->payload.data;
742b843c749SSergey Zigachev 	ctx.current_write_length = request->payload.length;
743b843c749SSergey Zigachev 	ctx.timed_out_retry_aux = 0;
744b843c749SSergey Zigachev 	ctx.invalid_reply_retry_aux = 0;
745b843c749SSergey Zigachev 	ctx.defer_retry_aux = 0;
746b843c749SSergey Zigachev 	ctx.defer_retry_i2c = 0;
747b843c749SSergey Zigachev 	ctx.ack_m_retry = 0;
748b843c749SSergey Zigachev 	ctx.transaction_complete = false;
749b843c749SSergey Zigachev 	ctx.operation_succeeded = true;
750b843c749SSergey Zigachev 
751b843c749SSergey Zigachev 	if (request->payload.address_space ==
752b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
753b843c749SSergey Zigachev 		ctx.request.type = AUX_TRANSACTION_TYPE_DP;
754b843c749SSergey Zigachev 		ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE;
755b843c749SSergey Zigachev 		ctx.request.address = request->payload.address;
756b843c749SSergey Zigachev 	} else if (request->payload.address_space ==
757b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
758b843c749SSergey Zigachev 		ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
759b843c749SSergey Zigachev 		ctx.request.action = middle_of_transaction ?
760b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
761b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
762b843c749SSergey Zigachev 		ctx.request.address = request->payload.address >> 1;
763b843c749SSergey Zigachev 	} else {
764b843c749SSergey Zigachev 		/* in DAL2, there was no return in such case */
765b843c749SSergey Zigachev 		BREAK_TO_DEBUGGER();
766b843c749SSergey Zigachev 		return false;
767b843c749SSergey Zigachev 	}
768b843c749SSergey Zigachev 
769b843c749SSergey Zigachev 	ctx.request.delay = 0;
770b843c749SSergey Zigachev 
771b843c749SSergey Zigachev 	ctx.max_defer_retry =
772b843c749SSergey Zigachev 		(engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ?
773b843c749SSergey Zigachev 			engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER;
774b843c749SSergey Zigachev 
775b843c749SSergey Zigachev 	do {
776b843c749SSergey Zigachev 		ctx.request.data = ctx.buffer;
777b843c749SSergey Zigachev 		ctx.request.length = ctx.current_write_length;
778b843c749SSergey Zigachev 
779b843c749SSergey Zigachev 		process_write_request(engine, &ctx);
780b843c749SSergey Zigachev 
781b843c749SSergey Zigachev 		request->status = ctx.status;
782b843c749SSergey Zigachev 
783b843c749SSergey Zigachev 		if (ctx.operation_succeeded && !ctx.transaction_complete)
784b843c749SSergey Zigachev 			if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
785b843c749SSergey Zigachev 				msleep(engine->delay);
786b843c749SSergey Zigachev 	} while (ctx.operation_succeeded && !ctx.transaction_complete);
787b843c749SSergey Zigachev 
788b843c749SSergey Zigachev 	if (request->payload.address_space ==
789b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
790b843c749SSergey Zigachev 		DC_LOG_I2C_AUX("WRITE: addr:0x%x  value:0x%x Result:%d",
791b843c749SSergey Zigachev 				request->payload.address,
792b843c749SSergey Zigachev 				request->payload.data[0],
793b843c749SSergey Zigachev 				ctx.operation_succeeded);
794b843c749SSergey Zigachev 	}
795b843c749SSergey Zigachev 
796b843c749SSergey Zigachev 	return ctx.operation_succeeded;
797b843c749SSergey Zigachev }
end_of_transaction_command(struct aux_engine * engine,struct i2caux_transaction_request * request)798b843c749SSergey Zigachev static bool end_of_transaction_command(
799b843c749SSergey Zigachev 	struct aux_engine *engine,
800b843c749SSergey Zigachev 	struct i2caux_transaction_request *request)
801b843c749SSergey Zigachev {
802b843c749SSergey Zigachev 	struct i2caux_transaction_request dummy_request;
803b843c749SSergey Zigachev 	uint8_t dummy_data;
804b843c749SSergey Zigachev 
805b843c749SSergey Zigachev 	/* [tcheng] We only need to send the stop (read with MOT = 0)
806b843c749SSergey Zigachev 	 * for I2C-over-Aux, not native AUX
807b843c749SSergey Zigachev 	 */
808b843c749SSergey Zigachev 
809b843c749SSergey Zigachev 	if (request->payload.address_space !=
810b843c749SSergey Zigachev 		I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C)
811b843c749SSergey Zigachev 		return false;
812b843c749SSergey Zigachev 
813b843c749SSergey Zigachev 	dummy_request.operation = request->operation;
814b843c749SSergey Zigachev 	dummy_request.payload.address_space = request->payload.address_space;
815b843c749SSergey Zigachev 	dummy_request.payload.address = request->payload.address;
816b843c749SSergey Zigachev 
817b843c749SSergey Zigachev 	/*
818b843c749SSergey Zigachev 	 * Add a dummy byte due to some receiver quirk
819b843c749SSergey Zigachev 	 * where one byte is sent along with MOT = 0.
820b843c749SSergey Zigachev 	 * Ideally this should be 0.
821b843c749SSergey Zigachev 	 */
822b843c749SSergey Zigachev 
823b843c749SSergey Zigachev 	dummy_request.payload.length = 0;
824b843c749SSergey Zigachev 	dummy_request.payload.data = &dummy_data;
825b843c749SSergey Zigachev 
826b843c749SSergey Zigachev 	if (request->operation == I2CAUX_TRANSACTION_READ)
827b843c749SSergey Zigachev 		return read_command(engine, &dummy_request, false);
828b843c749SSergey Zigachev 	else
829b843c749SSergey Zigachev 		return write_command(engine, &dummy_request, false);
830b843c749SSergey Zigachev 
831b843c749SSergey Zigachev 	/* according Syed, it does not need now DoDummyMOT */
832b843c749SSergey Zigachev }
submit_request(struct aux_engine * engine,struct i2caux_transaction_request * request,bool middle_of_transaction)833b843c749SSergey Zigachev static bool submit_request(
834b843c749SSergey Zigachev 	struct aux_engine *engine,
835b843c749SSergey Zigachev 	struct i2caux_transaction_request *request,
836b843c749SSergey Zigachev 	bool middle_of_transaction)
837b843c749SSergey Zigachev {
838b843c749SSergey Zigachev 
839b843c749SSergey Zigachev 	bool result;
840b843c749SSergey Zigachev 	bool mot_used = true;
841b843c749SSergey Zigachev 
842b843c749SSergey Zigachev 	switch (request->operation) {
843b843c749SSergey Zigachev 	case I2CAUX_TRANSACTION_READ:
844b843c749SSergey Zigachev 		result = read_command(engine, request, mot_used);
845b843c749SSergey Zigachev 	break;
846b843c749SSergey Zigachev 	case I2CAUX_TRANSACTION_WRITE:
847b843c749SSergey Zigachev 		result = write_command(engine, request, mot_used);
848b843c749SSergey Zigachev 	break;
849b843c749SSergey Zigachev 	default:
850b843c749SSergey Zigachev 		result = false;
851b843c749SSergey Zigachev 	}
852b843c749SSergey Zigachev 
853b843c749SSergey Zigachev 	/* [tcheng]
854b843c749SSergey Zigachev 	 * need to send stop for the last transaction to free up the AUX
855b843c749SSergey Zigachev 	 * if the above command fails, this would be the last transaction
856b843c749SSergey Zigachev 	 */
857b843c749SSergey Zigachev 
858b843c749SSergey Zigachev 	if (!middle_of_transaction || !result)
859b843c749SSergey Zigachev 		end_of_transaction_command(engine, request);
860b843c749SSergey Zigachev 
861b843c749SSergey Zigachev 	/* mask AUX interrupt */
862b843c749SSergey Zigachev 
863b843c749SSergey Zigachev 	return result;
864b843c749SSergey Zigachev }
865*78973132SSergey Zigachev 
866*78973132SSergey Zigachev static
get_engine_type(const struct aux_engine * engine)867b843c749SSergey Zigachev enum i2caux_engine_type get_engine_type(
868b843c749SSergey Zigachev 		const struct aux_engine *engine)
869b843c749SSergey Zigachev {
870b843c749SSergey Zigachev 	return I2CAUX_ENGINE_TYPE_AUX;
871b843c749SSergey Zigachev }
872b843c749SSergey Zigachev 
acquire(struct aux_engine * engine,struct ddc * ddc)873b843c749SSergey Zigachev static bool acquire(
874b843c749SSergey Zigachev 	struct aux_engine *engine,
875b843c749SSergey Zigachev 	struct ddc *ddc)
876b843c749SSergey Zigachev {
877b843c749SSergey Zigachev 
878b843c749SSergey Zigachev 	enum gpio_result result;
879b843c749SSergey Zigachev 
880b843c749SSergey Zigachev 	if (engine->funcs->is_engine_available) {
881b843c749SSergey Zigachev 		/*check whether SW could use the engine*/
882b843c749SSergey Zigachev 		if (!engine->funcs->is_engine_available(engine))
883b843c749SSergey Zigachev 			return false;
884b843c749SSergey Zigachev 	}
885b843c749SSergey Zigachev 
886b843c749SSergey Zigachev 	result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
887b843c749SSergey Zigachev 		GPIO_DDC_CONFIG_TYPE_MODE_AUX);
888b843c749SSergey Zigachev 
889b843c749SSergey Zigachev 	if (result != GPIO_RESULT_OK)
890b843c749SSergey Zigachev 		return false;
891b843c749SSergey Zigachev 
892b843c749SSergey Zigachev 	if (!engine->funcs->acquire_engine(engine)) {
893b843c749SSergey Zigachev 		dal_ddc_close(ddc);
894b843c749SSergey Zigachev 		return false;
895b843c749SSergey Zigachev 	}
896b843c749SSergey Zigachev 
897b843c749SSergey Zigachev 	engine->ddc = ddc;
898b843c749SSergey Zigachev 
899b843c749SSergey Zigachev 	return true;
900b843c749SSergey Zigachev }
901b843c749SSergey Zigachev 
902b843c749SSergey Zigachev static const struct aux_engine_funcs aux_engine_funcs = {
903b843c749SSergey Zigachev 	.acquire_engine = acquire_engine,
904b843c749SSergey Zigachev 	.submit_channel_request = submit_channel_request,
905b843c749SSergey Zigachev 	.process_channel_reply = process_channel_reply,
906b843c749SSergey Zigachev 	.read_channel_reply = read_channel_reply,
907b843c749SSergey Zigachev 	.get_channel_status = get_channel_status,
908b843c749SSergey Zigachev 	.is_engine_available = is_engine_available,
909b843c749SSergey Zigachev 	.release_engine = release_engine,
910b843c749SSergey Zigachev 	.destroy_engine = dce110_engine_destroy,
911b843c749SSergey Zigachev 	.submit_request = submit_request,
912b843c749SSergey Zigachev 	.get_engine_type = get_engine_type,
913b843c749SSergey Zigachev 	.acquire = acquire,
914b843c749SSergey Zigachev };
915b843c749SSergey Zigachev 
dce110_engine_destroy(struct aux_engine ** engine)916b843c749SSergey Zigachev void dce110_engine_destroy(struct aux_engine **engine)
917b843c749SSergey Zigachev {
918b843c749SSergey Zigachev 
919b843c749SSergey Zigachev 	struct aux_engine_dce110 *engine110 = FROM_AUX_ENGINE(*engine);
920b843c749SSergey Zigachev 
921b843c749SSergey Zigachev 	kfree(engine110);
922b843c749SSergey Zigachev 	*engine = NULL;
923b843c749SSergey Zigachev 
924b843c749SSergey Zigachev }
dce110_aux_engine_construct(struct aux_engine_dce110 * aux_engine110,struct dc_context * ctx,uint32_t inst,uint32_t timeout_period,const struct dce110_aux_registers * regs)925b843c749SSergey Zigachev struct aux_engine *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110,
926b843c749SSergey Zigachev 		struct dc_context *ctx,
927b843c749SSergey Zigachev 		uint32_t inst,
928b843c749SSergey Zigachev 		uint32_t timeout_period,
929b843c749SSergey Zigachev 		const struct dce110_aux_registers *regs)
930b843c749SSergey Zigachev {
931b843c749SSergey Zigachev 	aux_engine110->base.ddc = NULL;
932b843c749SSergey Zigachev 	aux_engine110->base.ctx = ctx;
933b843c749SSergey Zigachev 	aux_engine110->base.delay = 0;
934b843c749SSergey Zigachev 	aux_engine110->base.max_defer_write_retry = 0;
935b843c749SSergey Zigachev 	aux_engine110->base.funcs = &aux_engine_funcs;
936b843c749SSergey Zigachev 	aux_engine110->base.inst = inst;
937b843c749SSergey Zigachev 	aux_engine110->timeout_period = timeout_period;
938b843c749SSergey Zigachev 	aux_engine110->regs = regs;
939b843c749SSergey Zigachev 
940b843c749SSergey Zigachev 	return &aux_engine110->base;
941b843c749SSergey Zigachev }
942b843c749SSergey Zigachev 
943