abda52f09b090439b59d1efc4910fffa751da84c
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / msm / disp / msm_disp_snapshot_util.c
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 <drm/drm_drv.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
85         drm_printf(p, "module: " KBUILD_MODNAME "\n");
86         drm_printf(p, "dpu devcoredump\n");
87         drm_printf(p, "timestamp %lld\n", ktime_to_ns(state->timestamp));
88
89         list_for_each_entry_safe(block, tmp, &state->blocks, node) {
90                 drm_printf(p, "====================%s================\n", block->name);
91                 msm_disp_state_print_regs(&block->state, block->size, block->base_addr, p);
92         }
93
94         drm_printf(p, "===================dpu drm state================\n");
95
96         if (state->atomic_state)
97                 drm_atomic_print_new_state(state->atomic_state, p);
98 }
99
100 static void msm_disp_capture_atomic_state(struct msm_disp_state *disp_state)
101 {
102         struct drm_device *ddev;
103         struct drm_modeset_acquire_ctx ctx;
104         int ret;
105
106         disp_state->timestamp = ktime_get();
107
108         ddev = disp_state->drm_dev;
109
110         DRM_MODESET_LOCK_ALL_BEGIN(ddev, ctx, 0, ret);
111
112         disp_state->atomic_state = drm_atomic_helper_duplicate_state(ddev,
113                         &ctx);
114
115         DRM_MODESET_LOCK_ALL_END(ddev, ctx, ret);
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 }