summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
diff options
context:
space:
mode:
authorAbhinav Kumar <abhinavk@codeaurora.org>2021-04-16 23:57:20 +0300
committerRob Clark <robdclark@chromium.org>2021-06-23 17:32:13 +0300
commit98659487b845c05b6bed85d881713545db674c7c (patch)
tree7f46773099ad01e8d3857c3f6a585fdffa62c029 /drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
parenta4324a7a1c3d57ecfba0fee3e8b2d370eb5597c9 (diff)
downloadlinux-98659487b845c05b6bed85d881713545db674c7c.tar.xz
drm/msm: add support to take dpu snapshot
Add the msm_disp_snapshot module which adds supports to dump dpu registers and capture the drm atomic state which can be used in case of error conditions. changes in v5: - start storing disp_state in msm_kms instead of dpu_kms - get rid of MSM_DISP_SNAPSHOT_IN_* enum by simplifying the functions - move snprintf inside the snapshot core by using varargs - get rid of some stale code comments - allow snapshot module for non-DPU targets Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Link: https://lore.kernel.org/r/1618606645-19695-3-git-send-email-abhinavk@codeaurora.org Signed-off-by: Rob Clark <robdclark@chromium.org>
Diffstat (limited to 'drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c')
-rw-r--r--drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
new file mode 100644
index 000000000000..024ca49cb2e8
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include "msm_disp_snapshot.h"
+
+static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr)
+{
+ u32 len_padded;
+ u32 num_rows;
+ u32 x0, x4, x8, xc;
+ void __iomem *addr;
+ u32 *dump_addr = NULL;
+ void __iomem *end_addr;
+ int i;
+
+ len_padded = aligned_len * REG_DUMP_ALIGN;
+ num_rows = aligned_len / REG_DUMP_ALIGN;
+
+ addr = base_addr;
+ end_addr = base_addr + aligned_len;
+
+ if (!(*reg))
+ *reg = kzalloc(len_padded, GFP_KERNEL);
+
+ if (*reg)
+ dump_addr = *reg;
+
+ for (i = 0; i < num_rows; i++) {
+ x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0;
+ x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0;
+ x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0;
+ xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0;
+
+ if (dump_addr) {
+ dump_addr[i * 4] = x0;
+ dump_addr[i * 4 + 1] = x4;
+ dump_addr[i * 4 + 2] = x8;
+ dump_addr[i * 4 + 3] = xc;
+ }
+
+ addr += REG_DUMP_ALIGN;
+ }
+}
+
+static void msm_disp_state_print_regs(u32 **reg, u32 len, void __iomem *base_addr,
+ struct drm_printer *p)
+{
+ int i;
+ u32 *dump_addr = NULL;
+ void __iomem *addr;
+ u32 num_rows;
+
+ addr = base_addr;
+ num_rows = len / REG_DUMP_ALIGN;
+
+ if (*reg)
+ dump_addr = *reg;
+
+ for (i = 0; i < num_rows; i++) {
+ drm_printf(p, "0x%lx : %08x %08x %08x %08x\n",
+ (unsigned long)(addr - base_addr),
+ dump_addr[i * 4], dump_addr[i * 4 + 1],
+ dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]);
+ addr += REG_DUMP_ALIGN;
+ }
+}
+
+struct msm_disp_state *msm_disp_state_get(struct drm_device *drm)
+{
+ struct msm_drm_private *priv;
+ struct msm_kms *kms;
+
+ priv = drm->dev_private;
+ kms = priv->kms;
+
+ return kms->disp_state;
+}
+
+void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p)
+{
+ struct msm_disp_state_block *block, *tmp;
+
+ if (!p) {
+ DRM_ERROR("invalid drm printer\n");
+ return;
+ }
+
+ drm_printf(p, "---\n");
+
+ drm_printf(p, "module: " KBUILD_MODNAME "\n");
+ drm_printf(p, "dpu devcoredump\n");
+ drm_printf(p, "timestamp %lld\n", ktime_to_ns(state->timestamp));
+
+ list_for_each_entry_safe(block, tmp, &state->blocks, node) {
+ drm_printf(p, "====================%s================\n", block->name);
+ msm_disp_state_print_regs(&block->state, block->size, block->base_addr, p);
+ }
+
+ drm_printf(p, "===================dpu drm state================\n");
+
+ if (state->atomic_state)
+ drm_atomic_print_new_state(state->atomic_state, p);
+}
+
+static void msm_disp_capture_atomic_state(struct msm_disp_state *disp_state)
+{
+ struct drm_device *ddev;
+ struct drm_modeset_acquire_ctx ctx;
+
+ disp_state->timestamp = ktime_get();
+
+ ddev = disp_state->drm_dev;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ while (drm_modeset_lock_all_ctx(ddev, &ctx) != 0)
+ drm_modeset_backoff(&ctx);
+
+ disp_state->atomic_state = drm_atomic_helper_duplicate_state(ddev,
+ &ctx);
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+void msm_disp_snapshot_capture_state(struct msm_disp_state *disp_state)
+{
+ struct msm_drm_private *priv;
+ struct drm_device *drm_dev;
+
+ drm_dev = disp_state->drm_dev;
+ priv = drm_dev->dev_private;
+
+ msm_disp_capture_atomic_state(disp_state);
+}
+
+void msm_disp_state_free(struct msm_disp_state *disp_state)
+{
+ struct msm_disp_state_block *block, *tmp;
+
+ if (disp_state->atomic_state) {
+ drm_atomic_state_put(disp_state->atomic_state);
+ disp_state->atomic_state = NULL;
+ }
+
+ list_for_each_entry_safe(block, tmp, &disp_state->blocks, node) {
+ list_del(&block->node);
+ kfree(block->state);
+ kfree(block);
+ }
+}
+
+void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len,
+ void __iomem *base_addr, const char *fmt, ...)
+{
+ struct msm_disp_state_block *new_blk;
+ struct va_format vaf;
+ va_list va;
+
+ new_blk = kzalloc(sizeof(struct msm_disp_state_block), GFP_KERNEL);
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+ snprintf(new_blk->name, sizeof(new_blk->name), "%pV", &vaf);
+
+ va_end(va);
+
+ INIT_LIST_HEAD(&new_blk->node);
+ new_blk->size = ALIGN(len, REG_DUMP_ALIGN);
+ new_blk->base_addr = base_addr;
+
+ msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr);
+ list_add(&new_blk->node, &disp_state->blocks);
+}