]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c
drm/amd/display: Fix i2c write flag.
[mirror_ubuntu-bionic-kernel.git] / drivers / gpu / drm / amd / display / dc / i2caux / i2caux.c
CommitLineData
4562236b
HW
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
2c8ad2d5 60#include "dce120/i2caux_dce120.h"
2c8ad2d5 61
4562236b
HW
62#include "diagnostics/i2caux_diag.h"
63
64/*
65 * @brief
66 * Plain API, available publicly
67 */
68
69struct 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);
2c8ad2d5
AD
85 case DCE_VERSION_12_0:
86 return dal_i2caux_dce120_create(ctx);
4562236b
HW
87 default:
88 BREAK_TO_DEBUGGER();
89 return NULL;
90 }
91}
92
93bool 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
180bool 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
189 if (!ddc) {
190 BREAK_TO_DEBUGGER();
191 return false;
192 }
193
194 if (!cmd) {
195 BREAK_TO_DEBUGGER();
196 return false;
197 }
198
199 engine = i2caux->funcs->acquire_aux_engine(i2caux, ddc);
200
201 if (!engine)
202 return false;
203
204 engine->delay = cmd->defer_delay;
205 engine->max_defer_write_retry = cmd->max_defer_write_retry;
206
207 result = true;
208
209 while (index_of_payload < cmd->number_of_payloads) {
210 bool mot = (index_of_payload != cmd->number_of_payloads - 1);
211
212 struct aux_payload *payload = cmd->payloads + index_of_payload;
213
214 struct i2caux_transaction_request request = { 0 };
215
216 request.operation = payload->write ?
217 I2CAUX_TRANSACTION_WRITE :
218 I2CAUX_TRANSACTION_READ;
219
220 if (payload->i2c_over_aux) {
221 request.payload.address_space =
222 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C;
223
224 request.payload.address = (payload->address << 1) |
225 !payload->write;
226 } else {
227 request.payload.address_space =
228 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD;
229
230 request.payload.address = payload->address;
231 }
232
233 request.payload.length = payload->length;
234 request.payload.data = payload->data;
235
236 if (!engine->base.funcs->submit_request(
237 &engine->base, &request, mot)) {
238 result = false;
239 break;
240 }
241
242 ++index_of_payload;
243 }
244
245 i2caux->funcs->release_engine(i2caux, &engine->base);
246
247 return result;
248}
249
250static bool get_hw_supported_ddc_line(
251 struct ddc *ddc,
252 enum gpio_ddc_line *line)
253{
254 enum gpio_ddc_line line_found;
255
256 if (!ddc) {
257 BREAK_TO_DEBUGGER();
258 return false;
259 }
260
261 if (!ddc->hw_info.hw_supported)
262 return false;
263
264 line_found = dal_ddc_get_line(ddc);
265
266 if (line_found >= GPIO_DDC_LINE_COUNT)
267 return false;
268
269 *line = line_found;
270
271 return true;
272}
273
274void dal_i2caux_configure_aux(
275 struct i2caux *i2caux,
276 struct ddc *ddc,
277 union aux_config cfg)
278{
279 struct aux_engine *engine =
280 i2caux->funcs->acquire_aux_engine(i2caux, ddc);
281
282 if (!engine)
283 return;
284
285 engine->funcs->configure(engine, cfg);
286
287 i2caux->funcs->release_engine(i2caux, &engine->base);
288}
289
290void dal_i2caux_destroy(
291 struct i2caux **i2caux)
292{
293 if (!i2caux || !*i2caux) {
294 BREAK_TO_DEBUGGER();
295 return;
296 }
297
298 (*i2caux)->funcs->destroy(i2caux);
299
300 *i2caux = NULL;
301}
302
303/*
304 * @brief
305 * An utility function used by 'struct i2caux' and its descendants
306 */
307
308uint32_t dal_i2caux_get_reference_clock(
309 struct dc_bios *bios)
310{
311 struct firmware_info info = { { 0 } };
312
313 if (bios->funcs->get_firmware_info(bios, &info) != BP_RESULT_OK)
314 return 0;
315
316 return info.pll_info.crystal_frequency;
317}
318
319/*
320 * @brief
321 * i2caux
322 */
323
324enum {
325 /* following are expressed in KHz */
326 DEFAULT_I2C_SW_SPEED = 50,
327 DEFAULT_I2C_HW_SPEED = 50,
328
329 /* This is the timeout as defined in DP 1.2a,
330 * 2.3.4 "Detailed uPacket TX AUX CH State Description". */
331 AUX_TIMEOUT_PERIOD = 400,
332
333 /* Ideally, the SW timeout should be just above 550usec
334 * which is programmed in HW.
335 * But the SW timeout of 600usec is not reliable,
336 * because on some systems, delay_in_microseconds()
337 * returns faster than it should.
338 * EPR #379763: by trial-and-error on different systems,
339 * 700usec is the minimum reliable SW timeout for polling
340 * the AUX_SW_STATUS.AUX_SW_DONE bit.
341 * This timeout expires *only* when there is
342 * AUX Error or AUX Timeout conditions - not during normal operation.
343 * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set
344 * at most within ~240usec. That means,
345 * increasing this timeout will not affect normal operation,
346 * and we'll timeout after
347 * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 1600usec.
348 * This timeout is especially important for
349 * resume from S3 and CTS. */
350 SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 4
351};
352
353struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine(
354 struct i2caux *i2caux,
355 struct ddc *ddc)
356{
357 enum gpio_ddc_line line;
358 struct i2c_engine *engine = NULL;
359
360 if (get_hw_supported_ddc_line(ddc, &line))
361 engine = i2caux->i2c_sw_engines[line];
362
363 if (!engine)
364 engine = i2caux->i2c_generic_sw_engine;
365
366 if (!engine)
367 return NULL;
368
369 if (!engine->base.funcs->acquire(&engine->base, ddc))
370 return NULL;
371
372 return engine;
373}
374
375struct aux_engine *dal_i2caux_acquire_aux_engine(
376 struct i2caux *i2caux,
377 struct ddc *ddc)
378{
379 enum gpio_ddc_line line;
380 struct aux_engine *engine;
381
382 if (!get_hw_supported_ddc_line(ddc, &line))
383 return NULL;
384
385 engine = i2caux->aux_engines[line];
386
387 if (!engine)
388 return NULL;
389
390 if (!engine->base.funcs->acquire(&engine->base, ddc))
391 return NULL;
392
393 return engine;
394}
395
396void dal_i2caux_release_engine(
397 struct i2caux *i2caux,
398 struct engine *engine)
399{
400 engine->funcs->release_engine(engine);
401
402 dal_ddc_close(engine->ddc);
403
404 engine->ddc = NULL;
405}
406
407bool dal_i2caux_construct(
408 struct i2caux *i2caux,
409 struct dc_context *ctx)
410{
411 uint32_t i = 0;
412
413 i2caux->ctx = ctx;
414 do {
415 i2caux->i2c_sw_engines[i] = NULL;
416 i2caux->i2c_hw_engines[i] = NULL;
417 i2caux->aux_engines[i] = NULL;
418
419 ++i;
420 } while (i < GPIO_DDC_LINE_COUNT);
421
422 i2caux->i2c_generic_sw_engine = NULL;
423 i2caux->i2c_generic_hw_engine = NULL;
424
425 i2caux->aux_timeout_period =
426 SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD;
427
428 i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED;
429 i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED;
430
431 return true;
432}
433
434void dal_i2caux_destruct(
435 struct i2caux *i2caux)
436{
437 uint32_t i = 0;
438
439 if (i2caux->i2c_generic_hw_engine)
440 i2caux->i2c_generic_hw_engine->funcs->destroy(
441 &i2caux->i2c_generic_hw_engine);
442
443 if (i2caux->i2c_generic_sw_engine)
444 i2caux->i2c_generic_sw_engine->funcs->destroy(
445 &i2caux->i2c_generic_sw_engine);
446
447 do {
448 if (i2caux->aux_engines[i])
449 i2caux->aux_engines[i]->funcs->destroy(
450 &i2caux->aux_engines[i]);
451
452 if (i2caux->i2c_hw_engines[i])
453 i2caux->i2c_hw_engines[i]->funcs->destroy(
454 &i2caux->i2c_hw_engines[i]);
455
456 if (i2caux->i2c_sw_engines[i])
457 i2caux->i2c_sw_engines[i]->funcs->destroy(
458 &i2caux->i2c_sw_engines[i]);
459
460 ++i;
461 } while (i < GPIO_DDC_LINE_COUNT);
462}
463