xref: /dragonfly/sys/dev/drm/amd/display/dc/i2caux/i2caux.c (revision 655933d6)
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"
27 
28 /*
29  * Pre-requisites: headers required by header of this unit
30  */
31 #include "include/i2caux_interface.h"
32 #include "dc_bios_types.h"
33 
34 /*
35  * Header of this unit
36  */
37 
38 #include "i2caux.h"
39 
40 /*
41  * Post-requisites: headers required by this unit
42  */
43 
44 #include "engine.h"
45 #include "i2c_engine.h"
46 #include "aux_engine.h"
47 
48 /*
49  * This unit
50  */
51 
52 #include "dce80/i2caux_dce80.h"
53 
54 #include "dce100/i2caux_dce100.h"
55 
56 #include "dce110/i2caux_dce110.h"
57 
58 #include "dce112/i2caux_dce112.h"
59 
60 #include "dce120/i2caux_dce120.h"
61 
62 #if defined(CONFIG_DRM_AMD_DC_DCN1_0)
63 #include "dcn10/i2caux_dcn10.h"
64 #endif
65 
66 #include "diagnostics/i2caux_diag.h"
67 
68 /*
69  * @brief
70  * Plain API, available publicly
71  */
72 
73 struct i2caux *dal_i2caux_create(
74 	struct dc_context *ctx)
75 {
76 	if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) {
77 		return dal_i2caux_diag_fpga_create(ctx);
78 	}
79 
80 	switch (ctx->dce_version) {
81 	case DCE_VERSION_8_0:
82 	case DCE_VERSION_8_1:
83 	case DCE_VERSION_8_3:
84 		return dal_i2caux_dce80_create(ctx);
85 	case DCE_VERSION_11_2:
86 	case DCE_VERSION_11_22:
87 		return dal_i2caux_dce112_create(ctx);
88 	case DCE_VERSION_11_0:
89 		return dal_i2caux_dce110_create(ctx);
90 	case DCE_VERSION_10_0:
91 		return dal_i2caux_dce100_create(ctx);
92 	case DCE_VERSION_12_0:
93 		return dal_i2caux_dce120_create(ctx);
94 #if defined(CONFIG_DRM_AMD_DC_DCN1_0)
95 	case DCN_VERSION_1_0:
96 		return dal_i2caux_dcn10_create(ctx);
97 #endif
98 
99 	default:
100 		BREAK_TO_DEBUGGER();
101 		return NULL;
102 	}
103 }
104 
105 bool dal_i2caux_submit_i2c_command(
106 	struct i2caux *i2caux,
107 	struct ddc *ddc,
108 	struct i2c_command *cmd)
109 {
110 	struct i2c_engine *engine;
111 	uint8_t index_of_payload = 0;
112 	bool result;
113 
114 	if (!ddc) {
115 		BREAK_TO_DEBUGGER();
116 		return false;
117 	}
118 
119 	if (!cmd) {
120 		BREAK_TO_DEBUGGER();
121 		return false;
122 	}
123 
124 	/*
125 	 * default will be SW, however there is a feature flag in adapter
126 	 * service that determines whether SW i2c_engine will be available or
127 	 * not, if sw i2c is not available we will fallback to hw. This feature
128 	 * flag is set to not creating sw i2c engine for every dce except dce80
129 	 * currently
130 	 */
131 	switch (cmd->engine) {
132 	case I2C_COMMAND_ENGINE_DEFAULT:
133 	case I2C_COMMAND_ENGINE_SW:
134 		/* try to acquire SW engine first,
135 		 * acquire HW engine if SW engine not available */
136 		engine = i2caux->funcs->acquire_i2c_sw_engine(i2caux, ddc);
137 
138 		if (!engine)
139 			engine = i2caux->funcs->acquire_i2c_hw_engine(
140 				i2caux, ddc);
141 	break;
142 	case I2C_COMMAND_ENGINE_HW:
143 	default:
144 		/* try to acquire HW engine first,
145 		 * acquire SW engine if HW engine not available */
146 		engine = i2caux->funcs->acquire_i2c_hw_engine(i2caux, ddc);
147 
148 		if (!engine)
149 			engine = i2caux->funcs->acquire_i2c_sw_engine(
150 				i2caux, ddc);
151 	}
152 
153 	if (!engine)
154 		return false;
155 
156 	engine->funcs->set_speed(engine, cmd->speed);
157 
158 	result = true;
159 
160 	while (index_of_payload < cmd->number_of_payloads) {
161 		bool mot = (index_of_payload != cmd->number_of_payloads - 1);
162 
163 		struct i2c_payload *payload = cmd->payloads + index_of_payload;
164 
165 		struct i2caux_transaction_request request = { 0 };
166 
167 		request.operation = payload->write ?
168 			I2CAUX_TRANSACTION_WRITE :
169 			I2CAUX_TRANSACTION_READ;
170 
171 		request.payload.address_space =
172 			I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C;
173 		request.payload.address = (payload->address << 1) |
174 			!payload->write;
175 		request.payload.length = payload->length;
176 		request.payload.data = payload->data;
177 
178 		if (!engine->base.funcs->submit_request(
179 			&engine->base, &request, mot)) {
180 			result = false;
181 			break;
182 		}
183 
184 		++index_of_payload;
185 	}
186 
187 	i2caux->funcs->release_engine(i2caux, &engine->base);
188 
189 	return result;
190 }
191 
192 bool dal_i2caux_submit_aux_command(
193 	struct i2caux *i2caux,
194 	struct ddc *ddc,
195 	struct aux_command *cmd)
196 {
197 	struct aux_engine *engine;
198 	uint8_t index_of_payload = 0;
199 	bool result;
200 	bool mot;
201 
202 	if (!ddc) {
203 		BREAK_TO_DEBUGGER();
204 		return false;
205 	}
206 
207 	if (!cmd) {
208 		BREAK_TO_DEBUGGER();
209 		return false;
210 	}
211 
212 	engine = i2caux->funcs->acquire_aux_engine(i2caux, ddc);
213 
214 	if (!engine)
215 		return false;
216 
217 	engine->delay = cmd->defer_delay;
218 	engine->max_defer_write_retry = cmd->max_defer_write_retry;
219 
220 	result = true;
221 
222 	while (index_of_payload < cmd->number_of_payloads) {
223 		struct aux_payload *payload = cmd->payloads + index_of_payload;
224 		struct i2caux_transaction_request request = { 0 };
225 
226 		if (cmd->mot == I2C_MOT_UNDEF)
227 			mot = (index_of_payload != cmd->number_of_payloads - 1);
228 		else
229 			mot = (cmd->mot == I2C_MOT_TRUE);
230 
231 		request.operation = payload->write ?
232 			I2CAUX_TRANSACTION_WRITE :
233 			I2CAUX_TRANSACTION_READ;
234 
235 		if (payload->i2c_over_aux) {
236 			request.payload.address_space =
237 				I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C;
238 
239 			request.payload.address = (payload->address << 1) |
240 				!payload->write;
241 		} else {
242 			request.payload.address_space =
243 				I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD;
244 
245 			request.payload.address = payload->address;
246 		}
247 
248 		request.payload.length = payload->length;
249 		request.payload.data = payload->data;
250 
251 		if (!engine->base.funcs->submit_request(
252 			&engine->base, &request, mot)) {
253 			result = false;
254 			break;
255 		}
256 
257 		++index_of_payload;
258 	}
259 
260 	i2caux->funcs->release_engine(i2caux, &engine->base);
261 
262 	return result;
263 }
264 
265 static bool get_hw_supported_ddc_line(
266 	struct ddc *ddc,
267 	enum gpio_ddc_line *line)
268 {
269 	enum gpio_ddc_line line_found;
270 
271 	*line = GPIO_DDC_LINE_UNKNOWN;
272 
273 	if (!ddc) {
274 		BREAK_TO_DEBUGGER();
275 		return false;
276 	}
277 
278 	if (!ddc->hw_info.hw_supported)
279 		return false;
280 
281 	line_found = dal_ddc_get_line(ddc);
282 
283 	if (line_found >= GPIO_DDC_LINE_COUNT)
284 		return false;
285 
286 	*line = line_found;
287 
288 	return true;
289 }
290 
291 void dal_i2caux_configure_aux(
292 	struct i2caux *i2caux,
293 	struct ddc *ddc,
294 	union aux_config cfg)
295 {
296 	struct aux_engine *engine =
297 		i2caux->funcs->acquire_aux_engine(i2caux, ddc);
298 
299 	if (!engine)
300 		return;
301 
302 	engine->funcs->configure(engine, cfg);
303 
304 	i2caux->funcs->release_engine(i2caux, &engine->base);
305 }
306 
307 void dal_i2caux_destroy(
308 	struct i2caux **i2caux)
309 {
310 	if (!i2caux || !*i2caux) {
311 		BREAK_TO_DEBUGGER();
312 		return;
313 	}
314 
315 	(*i2caux)->funcs->destroy(i2caux);
316 
317 	*i2caux = NULL;
318 }
319 
320 /*
321  * @brief
322  * An utility function used by 'struct i2caux' and its descendants
323  */
324 
325 uint32_t dal_i2caux_get_reference_clock(
326 		struct dc_bios *bios)
327 {
328 	struct dc_firmware_info info = { { 0 } };
329 
330 	if (bios->funcs->get_firmware_info(bios, &info) != BP_RESULT_OK)
331 		return 0;
332 
333 	return info.pll_info.crystal_frequency;
334 }
335 
336 /*
337  * @brief
338  * i2caux
339  */
340 
341 enum {
342 	/* following are expressed in KHz */
343 	DEFAULT_I2C_SW_SPEED = 50,
344 	DEFAULT_I2C_HW_SPEED = 50,
345 
346 	DEFAULT_I2C_SW_SPEED_100KHZ = 100,
347 	DEFAULT_I2C_HW_SPEED_100KHZ = 100,
348 
349 	/* This is the timeout as defined in DP 1.2a,
350 	 * 2.3.4 "Detailed uPacket TX AUX CH State Description". */
351 	AUX_TIMEOUT_PERIOD = 400,
352 
353 	/* Ideally, the SW timeout should be just above 550usec
354 	 * which is programmed in HW.
355 	 * But the SW timeout of 600usec is not reliable,
356 	 * because on some systems, delay_in_microseconds()
357 	 * returns faster than it should.
358 	 * EPR #379763: by trial-and-error on different systems,
359 	 * 700usec is the minimum reliable SW timeout for polling
360 	 * the AUX_SW_STATUS.AUX_SW_DONE bit.
361 	 * This timeout expires *only* when there is
362 	 * AUX Error or AUX Timeout conditions - not during normal operation.
363 	 * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set
364 	 * at most within ~240usec. That means,
365 	 * increasing this timeout will not affect normal operation,
366 	 * and we'll timeout after
367 	 * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 1600usec.
368 	 * This timeout is especially important for
369 	 * resume from S3 and CTS. */
370 	SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 4
371 };
372 
373 struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine(
374 	struct i2caux *i2caux,
375 	struct ddc *ddc)
376 {
377 	enum gpio_ddc_line line;
378 	struct i2c_engine *engine = NULL;
379 
380 	if (get_hw_supported_ddc_line(ddc, &line))
381 		engine = i2caux->i2c_sw_engines[line];
382 
383 	if (!engine)
384 		engine = i2caux->i2c_generic_sw_engine;
385 
386 	if (!engine)
387 		return NULL;
388 
389 	if (!engine->base.funcs->acquire(&engine->base, ddc))
390 		return NULL;
391 
392 	return engine;
393 }
394 
395 struct aux_engine *dal_i2caux_acquire_aux_engine(
396 	struct i2caux *i2caux,
397 	struct ddc *ddc)
398 {
399 	enum gpio_ddc_line line;
400 	struct aux_engine *engine;
401 
402 	if (!get_hw_supported_ddc_line(ddc, &line))
403 		return NULL;
404 
405 	engine = i2caux->aux_engines[line];
406 
407 	if (!engine)
408 		return NULL;
409 
410 	if (!engine->base.funcs->acquire(&engine->base, ddc))
411 		return NULL;
412 
413 	return engine;
414 }
415 
416 void dal_i2caux_release_engine(
417 	struct i2caux *i2caux,
418 	struct engine *engine)
419 {
420 	engine->funcs->release_engine(engine);
421 
422 	dal_ddc_close(engine->ddc);
423 
424 	engine->ddc = NULL;
425 }
426 
427 void dal_i2caux_construct(
428 	struct i2caux *i2caux,
429 	struct dc_context *ctx)
430 {
431 	uint32_t i = 0;
432 
433 	i2caux->ctx = ctx;
434 	do {
435 		i2caux->i2c_sw_engines[i] = NULL;
436 		i2caux->i2c_hw_engines[i] = NULL;
437 		i2caux->aux_engines[i] = NULL;
438 
439 		++i;
440 	} while (i < GPIO_DDC_LINE_COUNT);
441 
442 	i2caux->i2c_generic_sw_engine = NULL;
443 	i2caux->i2c_generic_hw_engine = NULL;
444 
445 	i2caux->aux_timeout_period =
446 		SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD;
447 
448 	if (ctx->dce_version >= DCE_VERSION_11_2) {
449 		i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED_100KHZ;
450 		i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED_100KHZ;
451 	} else {
452 		i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED;
453 		i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED;
454 	}
455 }
456 
457 void dal_i2caux_destruct(
458 	struct i2caux *i2caux)
459 {
460 	uint32_t i = 0;
461 
462 	if (i2caux->i2c_generic_hw_engine)
463 		i2caux->i2c_generic_hw_engine->funcs->destroy(
464 			&i2caux->i2c_generic_hw_engine);
465 
466 	if (i2caux->i2c_generic_sw_engine)
467 		i2caux->i2c_generic_sw_engine->funcs->destroy(
468 			&i2caux->i2c_generic_sw_engine);
469 
470 	do {
471 		if (i2caux->aux_engines[i])
472 			i2caux->aux_engines[i]->funcs->destroy(
473 				&i2caux->aux_engines[i]);
474 
475 		if (i2caux->i2c_hw_engines[i])
476 			i2caux->i2c_hw_engines[i]->funcs->destroy(
477 				&i2caux->i2c_hw_engines[i]);
478 
479 		if (i2caux->i2c_sw_engines[i])
480 			i2caux->i2c_sw_engines[i]->funcs->destroy(
481 				&i2caux->i2c_sw_engines[i]);
482 
483 		++i;
484 	} while (i < GPIO_DDC_LINE_COUNT);
485 }
486 
487