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
12static 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
51static 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
74void 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
101static 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
121void 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
152void 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
171void 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

source code of linux/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c