summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/display/dc/basics/logger.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-10-09 03:21:15 +0200
committerDave Airlie <airlied@redhat.com>2017-10-09 03:21:15 +0200
commitb9e56e41e0c55c2b2ab5919c5e167faa4200b083 (patch)
tree8b580cf1e412a1c7078fe75994d3effeb297bfc3 /drivers/gpu/drm/amd/display/dc/basics/logger.c
parentMerge branch 'drm-next-4.15' of git://people.freedesktop.org/~agd5f/linux int... (diff)
parentamdgpu/dc: use kref for dc_state. (diff)
downloadlinux-b9e56e41e0c55c2b2ab5919c5e167faa4200b083.tar.xz
linux-b9e56e41e0c55c2b2ab5919c5e167faa4200b083.zip
Merge branch 'drm-next-4.15-dc' of git://people.freedesktop.org/~agd5f/linux into drm-next
Initial pull request for DC support. We've completed a substantial amount of the cleanup and restructuring in our TODO. There are a few additional cleanups that we are continuing to work on, but I don't think there are any showstoppers remaining. We've tried to maintain most of the history for bisect purposes. Harry made sure all the commits build. We've enabled DC for vega10 and Raven. Pre-vega10 parts can be enabled via module parameter (amdgpu.dc=1), but are not enabled by default at this point until we get further testing upstream. This code provides atomic modesetting support for DCE8 (CIK), DCE10 (Tonga, Fiji), DCE11 (CZ, ST, Polaris), DCE12 (vega10), and DCN1 (RV) including HDMI and DP audio, DP MST, and many other advanced display features. + Latest cleanups for DC from you and Harry. Note that there is some flickering on some older asics with this branch due to a regression in powerplay that has already been fixed and will be included in my next non-DC pull request next week. * 'drm-next-4.15-dc' of git://people.freedesktop.org/~agd5f/linux: (897 commits) amdgpu/dc: use kref for dc_state. amdgpu/dc: convert dc_sink to kref. amdgpu/dc: convert dc_stream_state to kref. amdgpu/dc: use kref for dc_plane_state. amdgpu/dc: convert dc_gamma to kref reference counting. amdgpu/dc: convert dc_transfer to use a kref. amdgpu/dc: kill a bunch of dead code. amdgpu/dc: set a bunch of functions to static. amdgpu/dc: kill some deadcode in dc core. amdgpu/dc: fix indentation on a couple of returns. amdgpu/dm: don't use after free. amdgpu/dc: kfree already checks for NULL. amdgpu/dc: fix a bunch of misc whitespace. amdgpu/dc: drop hw_sequencer_types.h amdgpu/dc: drop dce110_types.h amdgpu/dc: use kernel ilog2 for log_2. amdgpu/dc: don't memset after kzalloc. amdgpu/dc: inline dal grph object id functions. amdgpu/dc: inline dml_round_to_multiple amdgpu/dc: rename bios get_image symbol to something more searchable. ...
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/basics/logger.c')
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/logger.c431
1 files changed, 431 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..afb6d2d80e0c
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/basics/logger.c
@@ -0,0 +1,431 @@
+/*
+ * 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->write_wrap_count = 0;
+ logger->read_wrap_count = 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*/
+static void flush_to_debug_console(struct dal_logger *logger)
+{
+ int i = logger->buffer_read_offset;
+ char *string_start = &logger->log_buffer[i];
+
+ dm_output_to_console(
+ "---------------- FLUSHING LOG BUFFER ----------------\n");
+ while (i < logger->buffer_write_offset) {
+
+ if (logger->log_buffer[i] == '\0') {
+ dm_output_to_console("%s", string_start);
+ string_start = (char *)logger->log_buffer + i + 1;
+ }
+ i++;
+ }
+ 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 total_free_space = 0;
+ int space_before_wrap = 0;
+
+ if (logger->buffer_write_offset > logger->buffer_read_offset) {
+ total_free_space = logger->log_buffer_size -
+ logger->buffer_write_offset +
+ logger->buffer_read_offset;
+ space_before_wrap = logger->log_buffer_size -
+ logger->buffer_write_offset;
+ } else if (logger->buffer_write_offset <
+ logger->buffer_read_offset) {
+ total_free_space = logger->log_buffer_size -
+ logger->buffer_read_offset +
+ logger->buffer_write_offset;
+ space_before_wrap = total_free_space;
+ } else if (logger->write_wrap_count !=
+ logger->read_wrap_count) {
+ /* Buffer is completely full already */
+ total_free_space = 0;
+ space_before_wrap = 0;
+ } else {
+ /* Buffer is empty, start writing at beginning */
+ total_free_space = logger->log_buffer_size;
+ space_before_wrap = logger->log_buffer_size;
+ logger->buffer_write_offset = 0;
+ logger->buffer_read_offset = 0;
+ }
+
+ if (space_before_wrap > 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 if (total_free_space > size) {
+ /* We have enough room without flushing,
+ * but need to wrap around */
+
+ int space_after_wrap = total_free_space -
+ space_before_wrap;
+
+ memmove(logger->log_buffer +
+ logger->buffer_write_offset,
+ entry->buf, space_before_wrap);
+ memmove(logger->log_buffer, entry->buf +
+ space_before_wrap, space_after_wrap);
+
+ logger->buffer_write_offset = space_after_wrap;
+ logger->write_wrap_count++;
+
+ } else {
+ /* Not enough room remaining, we should flush
+ * existing logs */
+
+ /* Flush existing unread logs to console */
+ flush_to_debug_console(logger);
+
+ /* 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, msg, args);
+
+ entry.buf_offset += size;
+
+ /* --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;
+ }
+}