linux/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
   4 */
   5
   6#define pr_fmt(fmt)     "[drm:%s:%d] " fmt, __func__, __LINE__
   7
   8#include "msm_disp_snapshot.h"
   9
  10static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr)
  11{
  12        u32 len_padded;
  13        u32 num_rows;
  14        u32 x0, x4, x8, xc;
  15        void __iomem *addr;
  16        u32 *dump_addr = NULL;
  17        void __iomem *end_addr;
  18        int i;
  19
  20        len_padded = aligned_len * REG_DUMP_ALIGN;
  21        num_rows = aligned_len / REG_DUMP_ALIGN;
  22
  23        addr = base_addr;
  24        end_addr = base_addr + aligned_len;
  25
  26        if (!(*reg))
  27                *reg = kzalloc(len_padded, GFP_KERNEL);
  28
  29        if (*reg)
  30                dump_addr = *reg;
  31
  32        for (i = 0; i < num_rows; i++) {
  33                x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0;
  34                x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0;
  35                x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0;
  36                xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0;
  37
  38                if (dump_addr) {
  39                        dump_addr[i * 4] = x0;
  40                        dump_addr[i * 4 + 1] = x4;
  41                        dump_addr[i * 4 + 2] = x8;
  42                        dump_addr[i * 4 + 3] = xc;
  43                }
  44
  45                addr += REG_DUMP_ALIGN;
  46        }
  47}
  48
  49static void msm_disp_state_print_regs(u32 **reg, u32 len, void __iomem *base_addr,
  50                struct drm_printer *p)
  51{
  52        int i;
  53        u32 *dump_addr = NULL;
  54        void __iomem *addr;
  55        u32 num_rows;
  56
  57        addr = base_addr;
  58        num_rows = len / REG_DUMP_ALIGN;
  59
  60        if (*reg)
  61                dump_addr = *reg;
  62
  63        for (i = 0; i < num_rows; i++) {
  64                drm_printf(p, "0x%lx : %08x %08x %08x %08x\n",
  65                                (unsigned long)(addr - base_addr),
  66                                dump_addr[i * 4], dump_addr[i * 4 + 1],
  67                                dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]);
  68                addr += REG_DUMP_ALIGN;
  69        }
  70}
  71
  72void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p)
  73{
  74        struct msm_disp_state_block *block, *tmp;
  75
  76        if (!p) {
  77                DRM_ERROR("invalid drm printer\n");
  78                return;
  79        }
  80
  81        drm_printf(p, "---\n");
  82
  83        drm_printf(p, "module: " KBUILD_MODNAME "\n");
  84        drm_printf(p, "dpu devcoredump\n");
  85        drm_printf(p, "timestamp %lld\n", ktime_to_ns(state->timestamp));
  86
  87        list_for_each_entry_safe(block, tmp, &state->blocks, node) {
  88                drm_printf(p, "====================%s================\n", block->name);
  89                msm_disp_state_print_regs(&block->state, block->size, block->base_addr, p);
  90        }
  91
  92        drm_printf(p, "===================dpu drm state================\n");
  93
  94        if (state->atomic_state)
  95                drm_atomic_print_new_state(state->atomic_state, p);
  96}
  97
  98static void msm_disp_capture_atomic_state(struct msm_disp_state *disp_state)
  99{
 100        struct drm_device *ddev;
 101        struct drm_modeset_acquire_ctx ctx;
 102
 103        disp_state->timestamp = ktime_get();
 104
 105        ddev = disp_state->drm_dev;
 106
 107        drm_modeset_acquire_init(&ctx, 0);
 108
 109        while (drm_modeset_lock_all_ctx(ddev, &ctx) != 0)
 110                drm_modeset_backoff(&ctx);
 111
 112        disp_state->atomic_state = drm_atomic_helper_duplicate_state(ddev,
 113                        &ctx);
 114        drm_modeset_drop_locks(&ctx);
 115        drm_modeset_acquire_fini(&ctx);
 116}
 117
 118void msm_disp_snapshot_capture_state(struct msm_disp_state *disp_state)
 119{
 120        struct msm_drm_private *priv;
 121        struct drm_device *drm_dev;
 122        struct msm_kms *kms;
 123        int i;
 124
 125        drm_dev = disp_state->drm_dev;
 126        priv = drm_dev->dev_private;
 127        kms = priv->kms;
 128
 129        if (priv->dp)
 130                msm_dp_snapshot(disp_state, priv->dp);
 131
 132        for (i = 0; i < ARRAY_SIZE(priv->dsi); i++) {
 133                if (!priv->dsi[i])
 134                        continue;
 135
 136                msm_dsi_snapshot(disp_state, priv->dsi[i]);
 137        }
 138
 139        if (kms->funcs->snapshot)
 140                kms->funcs->snapshot(disp_state, kms);
 141
 142        msm_disp_capture_atomic_state(disp_state);
 143}
 144
 145void msm_disp_state_free(void *data)
 146{
 147        struct msm_disp_state *disp_state = data;
 148        struct msm_disp_state_block *block, *tmp;
 149
 150        if (disp_state->atomic_state) {
 151                drm_atomic_state_put(disp_state->atomic_state);
 152                disp_state->atomic_state = NULL;
 153        }
 154
 155        list_for_each_entry_safe(block, tmp, &disp_state->blocks, node) {
 156                list_del(&block->node);
 157                kfree(block->state);
 158                kfree(block);
 159        }
 160
 161        kfree(disp_state);
 162}
 163
 164void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len,
 165                void __iomem *base_addr, const char *fmt, ...)
 166{
 167        struct msm_disp_state_block *new_blk;
 168        struct va_format vaf;
 169        va_list va;
 170
 171        new_blk = kzalloc(sizeof(struct msm_disp_state_block), GFP_KERNEL);
 172
 173        va_start(va, fmt);
 174
 175        vaf.fmt = fmt;
 176        vaf.va = &va;
 177        snprintf(new_blk->name, sizeof(new_blk->name), "%pV", &vaf);
 178
 179        va_end(va);
 180
 181        INIT_LIST_HEAD(&new_blk->node);
 182        new_blk->size = ALIGN(len, REG_DUMP_ALIGN);
 183        new_blk->base_addr = base_addr;
 184
 185        msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr);
 186        list_add(&new_blk->node, &disp_state->blocks);
 187}
 188