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