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