diff options
author | Mark Brown <broonie@kernel.org> | 2018-01-08 15:54:50 +0000 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2018-01-08 15:54:50 +0000 |
commit | 498495dba268b20e8eadd7fe93c140c68b6cc9d2 (patch) | |
tree | 00d1562049d8bc2194fddd9ba0cbbe0812ad6f68 /drivers/gpu/drm/amd/display/dc/basics/logger.c | |
parent | d5cc0a1fcbb5ddbef9fdd4c4a978da3254ddbf37 (diff) | |
parent | 5c256045b87b8aa8e5bc9d2e2fdc0802351c1f99 (diff) | |
download | linux-stable-498495dba268b20e8eadd7fe93c140c68b6cc9d2.tar.gz linux-stable-498495dba268b20e8eadd7fe93c140c68b6cc9d2.tar.bz2 linux-stable-498495dba268b20e8eadd7fe93c140c68b6cc9d2.zip |
Merge branch 'fix/intel' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-intel
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/basics/logger.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/basics/logger.c | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/basics/logger.c b/drivers/gpu/drm/amd/display/dc/basics/logger.c new file mode 100644 index 000000000000..e04e8ecd4874 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/basics/logger.c @@ -0,0 +1,397 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dm_services.h" +#include "include/logger_interface.h" +#include "logger.h" + + +#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) + +static const struct dc_log_type_info log_type_info_tbl[] = { + {LOG_ERROR, "Error"}, + {LOG_WARNING, "Warning"}, + {LOG_DEBUG, "Debug"}, + {LOG_DC, "DC_Interface"}, + {LOG_SURFACE, "Surface"}, + {LOG_HW_HOTPLUG, "HW_Hotplug"}, + {LOG_HW_LINK_TRAINING, "HW_LKTN"}, + {LOG_HW_SET_MODE, "HW_Mode"}, + {LOG_HW_RESUME_S3, "HW_Resume"}, + {LOG_HW_AUDIO, "HW_Audio"}, + {LOG_HW_HPD_IRQ, "HW_HPDIRQ"}, + {LOG_MST, "MST"}, + {LOG_SCALER, "Scaler"}, + {LOG_BIOS, "BIOS"}, + {LOG_BANDWIDTH_CALCS, "BWCalcs"}, + {LOG_BANDWIDTH_VALIDATION, "BWValidation"}, + {LOG_I2C_AUX, "I2C_AUX"}, + {LOG_SYNC, "Sync"}, + {LOG_BACKLIGHT, "Backlight"}, + {LOG_FEATURE_OVERRIDE, "Override"}, + {LOG_DETECTION_EDID_PARSER, "Edid"}, + {LOG_DETECTION_DP_CAPS, "DP_Caps"}, + {LOG_RESOURCE, "Resource"}, + {LOG_DML, "DML"}, + {LOG_EVENT_MODE_SET, "Mode"}, + {LOG_EVENT_DETECTION, "Detect"}, + {LOG_EVENT_LINK_TRAINING, "LKTN"}, + {LOG_EVENT_LINK_LOSS, "LinkLoss"}, + {LOG_EVENT_UNDERFLOW, "Underflow"}, + {LOG_IF_TRACE, "InterfaceTrace"}, + {LOG_DTN, "DTN"} +}; + + +/* ----------- Object init and destruction ----------- */ +static bool construct(struct dc_context *ctx, struct dal_logger *logger, + uint32_t log_mask) +{ + /* malloc buffer and init offsets */ + logger->log_buffer_size = DAL_LOGGER_BUFFER_MAX_SIZE; + logger->log_buffer = (char *)kzalloc(logger->log_buffer_size * sizeof(char), + GFP_KERNEL); + + if (!logger->log_buffer) + return false; + + /* Initialize both offsets to start of buffer (empty) */ + logger->buffer_read_offset = 0; + logger->buffer_write_offset = 0; + + logger->open_count = 0; + + logger->flags.bits.ENABLE_CONSOLE = 1; + logger->flags.bits.ENABLE_BUFFER = 0; + + logger->ctx = ctx; + + logger->mask = log_mask; + + return true; +} + +static void destruct(struct dal_logger *logger) +{ + if (logger->log_buffer) { + kfree(logger->log_buffer); + logger->log_buffer = NULL; + } +} + +struct dal_logger *dal_logger_create(struct dc_context *ctx, uint32_t log_mask) +{ + /* malloc struct */ + struct dal_logger *logger = kzalloc(sizeof(struct dal_logger), + GFP_KERNEL); + + if (!logger) + return NULL; + if (!construct(ctx, logger, log_mask)) { + kfree(logger); + return NULL; + } + + return logger; +} + +uint32_t dal_logger_destroy(struct dal_logger **logger) +{ + if (logger == NULL || *logger == NULL) + return 1; + destruct(*logger); + kfree(*logger); + *logger = NULL; + + return 0; +} + +/* ------------------------------------------------------------------------ */ + + +static bool dal_logger_should_log( + struct dal_logger *logger, + enum dc_log_type log_type) +{ + if (logger->mask & (1 << log_type)) + return true; + + return false; +} + +static void log_to_debug_console(struct log_entry *entry) +{ + struct dal_logger *logger = entry->logger; + + if (logger->flags.bits.ENABLE_CONSOLE == 0) + return; + + if (entry->buf_offset) { + switch (entry->type) { + case LOG_ERROR: + dm_error("%s", entry->buf); + break; + default: + dm_output_to_console("%s", entry->buf); + break; + } + } +} + +/* Print everything unread existing in log_buffer to debug console*/ +void dm_logger_flush_buffer(struct dal_logger *logger, bool should_warn) +{ + char *string_start = &logger->log_buffer[logger->buffer_read_offset]; + + if (should_warn) + dm_output_to_console( + "---------------- FLUSHING LOG BUFFER ----------------\n"); + while (logger->buffer_read_offset < logger->buffer_write_offset) { + + if (logger->log_buffer[logger->buffer_read_offset] == '\0') { + dm_output_to_console("%s", string_start); + string_start = logger->log_buffer + logger->buffer_read_offset + 1; + } + logger->buffer_read_offset++; + } + if (should_warn) + dm_output_to_console( + "-------------- END FLUSHING LOG BUFFER --------------\n\n"); +} + +static void log_to_internal_buffer(struct log_entry *entry) +{ + + uint32_t size = entry->buf_offset; + struct dal_logger *logger = entry->logger; + + if (logger->flags.bits.ENABLE_BUFFER == 0) + return; + + if (logger->log_buffer == NULL) + return; + + if (size > 0 && size < logger->log_buffer_size) { + + int buffer_space = logger->log_buffer_size - + logger->buffer_write_offset; + + if (logger->buffer_write_offset == logger->buffer_read_offset) { + /* Buffer is empty, start writing at beginning */ + buffer_space = logger->log_buffer_size; + logger->buffer_write_offset = 0; + logger->buffer_read_offset = 0; + } + + if (buffer_space > size) { + /* No wrap around, copy 'size' bytes + * from 'entry->buf' to 'log_buffer' + */ + memmove(logger->log_buffer + + logger->buffer_write_offset, + entry->buf, size); + logger->buffer_write_offset += size; + + } else { + /* Not enough room remaining, we should flush + * existing logs */ + + /* Flush existing unread logs to console */ + dm_logger_flush_buffer(logger, true); + + /* Start writing to beginning of buffer */ + memmove(logger->log_buffer, entry->buf, size); + logger->buffer_write_offset = size; + logger->buffer_read_offset = 0; + } + + } +} + +static void log_heading(struct log_entry *entry) +{ + int j; + + for (j = 0; j < NUM_ELEMENTS(log_type_info_tbl); j++) { + + const struct dc_log_type_info *info = &log_type_info_tbl[j]; + + if (info->type == entry->type) + dm_logger_append(entry, "[%s]\t", info->name); + } +} + +static void append_entry( + struct log_entry *entry, + char *buffer, + uint32_t buf_size) +{ + if (!entry->buf || + entry->buf_offset + buf_size > entry->max_buf_bytes + ) { + BREAK_TO_DEBUGGER(); + return; + } + + /* Todo: check if off by 1 byte due to \0 anywhere */ + memmove(entry->buf + entry->buf_offset, buffer, buf_size); + entry->buf_offset += buf_size; +} + +/* ------------------------------------------------------------------------ */ + +/* Warning: Be careful that 'msg' is null terminated and the total size is + * less than DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE (256) including '\0' + */ +void dm_logger_write( + struct dal_logger *logger, + enum dc_log_type log_type, + const char *msg, + ...) +{ + if (logger && dal_logger_should_log(logger, log_type)) { + uint32_t size; + va_list args; + char buffer[LOG_MAX_LINE_SIZE]; + struct log_entry entry; + + va_start(args, msg); + + entry.logger = logger; + + entry.buf = buffer; + + entry.buf_offset = 0; + entry.max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char); + + entry.type = log_type; + + log_heading(&entry); + + size = dm_log_to_buffer( + buffer, LOG_MAX_LINE_SIZE - 1, msg, args); + + buffer[entry.buf_offset + size] = '\0'; + entry.buf_offset += size + 1; + + /* --Flush log_entry buffer-- */ + /* print to kernel console */ + log_to_debug_console(&entry); + /* log internally for dsat */ + log_to_internal_buffer(&entry); + + va_end(args); + } +} + +/* Same as dm_logger_write, except without open() and close(), which must + * be done separately. + */ +void dm_logger_append( + struct log_entry *entry, + const char *msg, + ...) +{ + struct dal_logger *logger; + + if (!entry) { + BREAK_TO_DEBUGGER(); + return; + } + + logger = entry->logger; + + if (logger && logger->open_count > 0 && + dal_logger_should_log(logger, entry->type)) { + + uint32_t size; + va_list args; + char buffer[LOG_MAX_LINE_SIZE]; + + va_start(args, msg); + + size = dm_log_to_buffer( + buffer, LOG_MAX_LINE_SIZE, msg, args); + + if (size < LOG_MAX_LINE_SIZE - 1) { + append_entry(entry, buffer, size); + } else { + append_entry(entry, "LOG_ERROR, line too long\n", 27); + } + + va_end(args); + } +} + +void dm_logger_open( + struct dal_logger *logger, + struct log_entry *entry, /* out */ + enum dc_log_type log_type) +{ + if (!entry) { + BREAK_TO_DEBUGGER(); + return; + } + + entry->type = log_type; + entry->logger = logger; + + entry->buf = kzalloc(DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char), + GFP_KERNEL); + + entry->buf_offset = 0; + entry->max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char); + + logger->open_count++; + + log_heading(entry); +} + +void dm_logger_close(struct log_entry *entry) +{ + struct dal_logger *logger = entry->logger; + + if (logger && logger->open_count > 0) { + logger->open_count--; + } else { + BREAK_TO_DEBUGGER(); + goto cleanup; + } + + /* --Flush log_entry buffer-- */ + /* print to kernel console */ + log_to_debug_console(entry); + /* log internally for dsat */ + log_to_internal_buffer(entry); + + /* TODO: Write end heading */ + +cleanup: + if (entry->buf) { + kfree(entry->buf); + entry->buf = NULL; + entry->buf_offset = 0; + entry->max_buf_bytes = 0; + } +} |