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
dal_i2caux_create(struct dc_context * ctx)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
dal_i2caux_submit_i2c_command(struct i2caux * i2caux,struct ddc * ddc,struct i2c_command * cmd)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
dal_i2caux_submit_aux_command(struct i2caux * i2caux,struct ddc * ddc,struct aux_command * cmd)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
get_hw_supported_ddc_line(struct ddc * ddc,enum gpio_ddc_line * line)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
dal_i2caux_configure_aux(struct i2caux * i2caux,struct ddc * ddc,union aux_config cfg)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
dal_i2caux_destroy(struct i2caux ** i2caux)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
dal_i2caux_get_reference_clock(struct dc_bios * bios)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
dal_i2caux_acquire_i2c_sw_engine(struct i2caux * i2caux,struct ddc * ddc)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
dal_i2caux_acquire_aux_engine(struct i2caux * i2caux,struct ddc * ddc)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
dal_i2caux_release_engine(struct i2caux * i2caux,struct engine * engine)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
dal_i2caux_construct(struct i2caux * i2caux,struct dc_context * ctx)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
dal_i2caux_destruct(struct i2caux * i2caux)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