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