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 10 static 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 49 static 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 72 void 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 98 static 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 118 void 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 145 void 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 164 void 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