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(size: 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, f: "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, f: "---\n" ); |
84 | drm_printf(p, f: "kernel: " UTS_RELEASE "\n" ); |
85 | drm_printf(p, f: "module: " KBUILD_MODNAME "\n" ); |
86 | drm_printf(p, f: "dpu devcoredump\n" ); |
87 | drm_printf(p, f: "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, f: "====================%s================\n" , block->name); |
92 | msm_disp_state_print_regs(reg: &block->state, len: block->size, base_addr: block->base_addr, p); |
93 | } |
94 | |
95 | drm_printf(p, f: "===================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(tv: &disp_state->time); |
107 | |
108 | ddev = disp_state->drm_dev; |
109 | |
110 | drm_modeset_acquire_init(ctx: &ctx, flags: 0); |
111 | |
112 | while (drm_modeset_lock_all_ctx(dev: ddev, ctx: &ctx) != 0) |
113 | drm_modeset_backoff(ctx: &ctx); |
114 | |
115 | disp_state->atomic_state = drm_atomic_helper_duplicate_state(dev: ddev, |
116 | ctx: &ctx); |
117 | drm_modeset_drop_locks(ctx: &ctx); |
118 | drm_modeset_acquire_fini(ctx: &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(state: 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(entry: &block->node); |
164 | kfree(objp: block->state); |
165 | kfree(objp: block); |
166 | } |
167 | |
168 | kfree(objp: 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(size: 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(buf: new_blk->name, size: sizeof(new_blk->name), fmt: "%pV" , &vaf); |
187 | |
188 | va_end(va); |
189 | |
190 | INIT_LIST_HEAD(list: &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(reg: &new_blk->state, aligned_len: new_blk->size, base_addr); |
195 | list_add_tail(new: &new_blk->node, head: &disp_state->blocks); |
196 | } |
197 | |