]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/gpu/drm/amd/display/dc/basics/logger.c
drm/amd/display: log HUBP using DTN logging
[mirror_ubuntu-bionic-kernel.git] / drivers / gpu / drm / amd / display / dc / basics / logger.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#include "dm_services.h"
26#include "include/logger_interface.h"
27#include "logger.h"
28
29
30#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))
31
32static const struct dc_log_type_info log_type_info_tbl[] = {
33 {LOG_ERROR, "Error"},
34 {LOG_WARNING, "Warning"},
c0bc0bd5 35 {LOG_DEBUG, "Debug"},
4562236b
HW
36 {LOG_DC, "DC_Interface"},
37 {LOG_SURFACE, "Surface"},
38 {LOG_HW_HOTPLUG, "HW_Hotplug"},
39 {LOG_HW_LINK_TRAINING, "HW_LKTN"},
40 {LOG_HW_SET_MODE, "HW_Mode"},
41 {LOG_HW_RESUME_S3, "HW_Resume"},
42 {LOG_HW_AUDIO, "HW_Audio"},
43 {LOG_HW_HPD_IRQ, "HW_HPDIRQ"},
44 {LOG_MST, "MST"},
45 {LOG_SCALER, "Scaler"},
46 {LOG_BIOS, "BIOS"},
47 {LOG_BANDWIDTH_CALCS, "BWCalcs"},
48 {LOG_BANDWIDTH_VALIDATION, "BWValidation"},
49 {LOG_I2C_AUX, "I2C_AUX"},
50 {LOG_SYNC, "Sync"},
51 {LOG_BACKLIGHT, "Backlight"},
52 {LOG_FEATURE_OVERRIDE, "Override"},
53 {LOG_DETECTION_EDID_PARSER, "Edid"},
54 {LOG_DETECTION_DP_CAPS, "DP_Caps"},
55 {LOG_RESOURCE, "Resource"},
56 {LOG_DML, "DML"},
57 {LOG_EVENT_MODE_SET, "Mode"},
58 {LOG_EVENT_DETECTION, "Detect"},
59 {LOG_EVENT_LINK_TRAINING, "LKTN"},
60 {LOG_EVENT_LINK_LOSS, "LinkLoss"},
61 {LOG_EVENT_UNDERFLOW, "Underflow"},
9ff1bb09
CM
62 {LOG_IF_TRACE, "InterfaceTrace"},
63 {LOG_DTN, "DTN"}
4562236b
HW
64};
65
66
67#define DC_DEFAULT_LOG_MASK ((1 << LOG_ERROR) | \
68 (1 << LOG_WARNING) | \
69 (1 << LOG_EVENT_MODE_SET) | \
70 (1 << LOG_EVENT_DETECTION) | \
71 (1 << LOG_EVENT_LINK_TRAINING) | \
72 (1 << LOG_EVENT_LINK_LOSS) | \
73 (1 << LOG_EVENT_UNDERFLOW) | \
74 (1 << LOG_RESOURCE) | \
75 (1 << LOG_FEATURE_OVERRIDE) | \
76 (1 << LOG_DETECTION_EDID_PARSER) | \
77 (1 << LOG_DC) | \
78 (1 << LOG_HW_HOTPLUG) | \
79 (1 << LOG_HW_SET_MODE) | \
80 (1 << LOG_HW_RESUME_S3) | \
81 (1 << LOG_HW_HPD_IRQ) | \
82 (1 << LOG_SYNC) | \
83 (1 << LOG_BANDWIDTH_VALIDATION) | \
84 (1 << LOG_MST) | \
4562236b
HW
85 (1 << LOG_DETECTION_DP_CAPS) | \
86 (1 << LOG_BACKLIGHT)) | \
87 (1 << LOG_I2C_AUX) | \
9ff1bb09
CM
88 (1 << LOG_IF_TRACE) | \
89 (1 << LOG_DTN) /* | \
c0bc0bd5
JL
90 (1 << LOG_DEBUG) | \
91 (1 << LOG_BIOS) | \
4562236b
HW
92 (1 << LOG_SURFACE) | \
93 (1 << LOG_SCALER) | \
94 (1 << LOG_DML) | \
95 (1 << LOG_HW_LINK_TRAINING) | \
96 (1 << LOG_HW_AUDIO)| \
97 (1 << LOG_BANDWIDTH_CALCS)*/
98
99/* ----------- Object init and destruction ----------- */
100static bool construct(struct dc_context *ctx, struct dal_logger *logger)
101{
102 /* malloc buffer and init offsets */
103 logger->log_buffer_size = DAL_LOGGER_BUFFER_MAX_SIZE;
104 logger->log_buffer = (char *)dm_alloc(logger->log_buffer_size *
105 sizeof(char));
106
107 if (!logger->log_buffer)
108 return false;
109
110 /* Initialize both offsets to start of buffer (empty) */
111 logger->buffer_read_offset = 0;
112 logger->buffer_write_offset = 0;
113
114 logger->write_wrap_count = 0;
115 logger->read_wrap_count = 0;
116 logger->open_count = 0;
117
118 logger->flags.bits.ENABLE_CONSOLE = 1;
119 logger->flags.bits.ENABLE_BUFFER = 0;
120
121 logger->ctx = ctx;
122
123 logger->mask = DC_DEFAULT_LOG_MASK;
124
125 return true;
126}
127
128static void destruct(struct dal_logger *logger)
129{
130 if (logger->log_buffer) {
131 dm_free(logger->log_buffer);
132 logger->log_buffer = NULL;
133 }
134}
135
136struct dal_logger *dal_logger_create(struct dc_context *ctx)
137{
138 /* malloc struct */
139 struct dal_logger *logger = dm_alloc(sizeof(struct dal_logger));
140
141 if (!logger)
142 return NULL;
143 if (!construct(ctx, logger)) {
144 dm_free(logger);
145 return NULL;
146 }
147
148 return logger;
149}
150
151uint32_t dal_logger_destroy(struct dal_logger **logger)
152{
153 if (logger == NULL || *logger == NULL)
154 return 1;
155 destruct(*logger);
156 dm_free(*logger);
157 *logger = NULL;
158
159 return 0;
160}
161
162/* ------------------------------------------------------------------------ */
163
164
165static bool dal_logger_should_log(
166 struct dal_logger *logger,
167 enum dc_log_type log_type)
168{
169 if (logger->mask & (1 << log_type))
170 return true;
171
172 return false;
173}
174
175static void log_to_debug_console(struct log_entry *entry)
176{
177 struct dal_logger *logger = entry->logger;
178
179 if (logger->flags.bits.ENABLE_CONSOLE == 0)
180 return;
181
182 if (entry->buf_offset) {
183 switch (entry->type) {
184 case LOG_ERROR:
185 dm_error("%s", entry->buf);
186 break;
187 default:
188 dm_output_to_console("%s", entry->buf);
189 break;
190 }
191 }
192}
193
194/* Print everything unread existing in log_buffer to debug console*/
195static void flush_to_debug_console(struct dal_logger *logger)
196{
197 int i = logger->buffer_read_offset;
198 char *string_start = &logger->log_buffer[i];
199
200 dm_output_to_console(
201 "---------------- FLUSHING LOG BUFFER ----------------\n");
202 while (i < logger->buffer_write_offset) {
203
204 if (logger->log_buffer[i] == '\0') {
205 dm_output_to_console("%s", string_start);
206 string_start = (char *)logger->log_buffer + i + 1;
207 }
208 i++;
209 }
210 dm_output_to_console(
211 "-------------- END FLUSHING LOG BUFFER --------------\n\n");
212}
213
214static void log_to_internal_buffer(struct log_entry *entry)
215{
216
217 uint32_t size = entry->buf_offset;
218 struct dal_logger *logger = entry->logger;
219
220 if (logger->flags.bits.ENABLE_BUFFER == 0)
221 return;
222
223 if (logger->log_buffer == NULL)
224 return;
225
226 if (size > 0 && size < logger->log_buffer_size) {
227
228 int total_free_space = 0;
229 int space_before_wrap = 0;
230
231 if (logger->buffer_write_offset > logger->buffer_read_offset) {
232 total_free_space = logger->log_buffer_size -
233 logger->buffer_write_offset +
234 logger->buffer_read_offset;
235 space_before_wrap = logger->log_buffer_size -
236 logger->buffer_write_offset;
237 } else if (logger->buffer_write_offset <
238 logger->buffer_read_offset) {
239 total_free_space = logger->log_buffer_size -
240 logger->buffer_read_offset +
241 logger->buffer_write_offset;
242 space_before_wrap = total_free_space;
243 } else if (logger->write_wrap_count !=
244 logger->read_wrap_count) {
245 /* Buffer is completely full already */
246 total_free_space = 0;
247 space_before_wrap = 0;
248 } else {
249 /* Buffer is empty, start writing at beginning */
250 total_free_space = logger->log_buffer_size;
251 space_before_wrap = logger->log_buffer_size;
252 logger->buffer_write_offset = 0;
253 logger->buffer_read_offset = 0;
254 }
255
256 if (space_before_wrap > size) {
257 /* No wrap around, copy 'size' bytes
258 * from 'entry->buf' to 'log_buffer'
259 */
260 memmove(logger->log_buffer +
261 logger->buffer_write_offset,
262 entry->buf, size);
263 logger->buffer_write_offset += size;
264
265 } else if (total_free_space > size) {
266 /* We have enough room without flushing,
267 * but need to wrap around */
268
269 int space_after_wrap = total_free_space -
270 space_before_wrap;
271
272 memmove(logger->log_buffer +
273 logger->buffer_write_offset,
274 entry->buf, space_before_wrap);
275 memmove(logger->log_buffer, entry->buf +
276 space_before_wrap, space_after_wrap);
277
278 logger->buffer_write_offset = space_after_wrap;
279 logger->write_wrap_count++;
280
281 } else {
282 /* Not enough room remaining, we should flush
283 * existing logs */
284
285 /* Flush existing unread logs to console */
286 flush_to_debug_console(logger);
287
288 /* Start writing to beginning of buffer */
289 memmove(logger->log_buffer, entry->buf, size);
290 logger->buffer_write_offset = size;
291 logger->buffer_read_offset = 0;
292 }
293
294 }
295}
296
297static void log_heading(struct log_entry *entry)
298{
299 int j;
300
301 for (j = 0; j < NUM_ELEMENTS(log_type_info_tbl); j++) {
302
303 const struct dc_log_type_info *info = &log_type_info_tbl[j];
304
305 if (info->type == entry->type)
306 dm_logger_append(entry, "[%s]\t", info->name);
307 }
308}
309
310static void append_entry(
311 struct log_entry *entry,
312 char *buffer,
313 uint32_t buf_size)
314{
315 if (!entry->buf ||
316 entry->buf_offset + buf_size > entry->max_buf_bytes
317 ) {
318 BREAK_TO_DEBUGGER();
319 return;
320 }
321
322 /* Todo: check if off by 1 byte due to \0 anywhere */
323 memmove(entry->buf + entry->buf_offset, buffer, buf_size);
324 entry->buf_offset += buf_size;
325}
326
327/* ------------------------------------------------------------------------ */
328
329/* Warning: Be careful that 'msg' is null terminated and the total size is
330 * less than DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE (256) including '\0'
331 */
332void dm_logger_write(
333 struct dal_logger *logger,
334 enum dc_log_type log_type,
335 const char *msg,
336 ...)
337{
338 if (logger && dal_logger_should_log(logger, log_type)) {
339 uint32_t size;
340 va_list args;
341 char buffer[LOG_MAX_LINE_SIZE];
342 struct log_entry entry;
343
344 va_start(args, msg);
345
346 entry.logger = logger;
347
348 entry.buf = buffer;
349
350 entry.buf_offset = 0;
351 entry.max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char);
352
353 entry.type = log_type;
354
355 log_heading(&entry);
356
357 size = dm_log_to_buffer(
358 buffer, LOG_MAX_LINE_SIZE, msg, args);
359
360 entry.buf_offset += size;
361
362 /* --Flush log_entry buffer-- */
363 /* print to kernel console */
364 log_to_debug_console(&entry);
365 /* log internally for dsat */
366 log_to_internal_buffer(&entry);
367
368 va_end(args);
369 }
370}
371
372/* Same as dm_logger_write, except without open() and close(), which must
373 * be done separately.
374 */
375void dm_logger_append(
376 struct log_entry *entry,
377 const char *msg,
378 ...)
379{
380 struct dal_logger *logger;
381
382 if (!entry) {
383 BREAK_TO_DEBUGGER();
384 return;
385 }
386
387 logger = entry->logger;
388
389 if (logger && logger->open_count > 0 &&
390 dal_logger_should_log(logger, entry->type)) {
391
392 uint32_t size;
393 va_list args;
394 char buffer[LOG_MAX_LINE_SIZE];
395
396 va_start(args, msg);
397
398 size = dm_log_to_buffer(
399 buffer, LOG_MAX_LINE_SIZE, msg, args);
400
401 if (size < LOG_MAX_LINE_SIZE - 1) {
402 append_entry(entry, buffer, size);
403 } else {
404 append_entry(entry, "LOG_ERROR, line too long\n", 27);
405 }
406
407 va_end(args);
408 }
409}
410
411void dm_logger_open(
412 struct dal_logger *logger,
413 struct log_entry *entry, /* out */
414 enum dc_log_type log_type)
415{
416 if (!entry) {
417 BREAK_TO_DEBUGGER();
418 return;
419 }
420
421 entry->type = log_type;
422 entry->logger = logger;
423
424 entry->buf = dm_alloc(DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char));
425
426 entry->buf_offset = 0;
427 entry->max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char);
428
429 logger->open_count++;
430
2248eb6b 431 log_heading(entry);
4562236b
HW
432}
433
434void dm_logger_close(struct log_entry *entry)
435{
436 struct dal_logger *logger = entry->logger;
437
438 if (logger && logger->open_count > 0) {
439 logger->open_count--;
440 } else {
441 BREAK_TO_DEBUGGER();
442 goto cleanup;
443 }
444
445 /* --Flush log_entry buffer-- */
446 /* print to kernel console */
447 log_to_debug_console(entry);
448 /* log internally for dsat */
449 log_to_internal_buffer(entry);
450
451 /* TODO: Write end heading */
452
453cleanup:
454 if (entry->buf) {
455 dm_free(entry->buf);
456 entry->buf = NULL;
457 entry->buf_offset = 0;
458 entry->max_buf_bytes = 0;
459 }
460}