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