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