xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/rockchip/rockchip_drm_debugfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3  * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
4  * Author: Sandy Huang <hjc@rock-chips.com>
5  */
6 
7 #include <drm/drm_atomic_uapi.h>
8 #include <drm/drm_drv.h>
9 #include <drm/drm_file.h>
10 #include <drm/drm_gem_cma_helper.h>
11 #include <drm/drm_of.h>
12 #include <drm/drm_probe_helper.h>
13 
14 #include <linux/file.h>
15 
16 #include "rockchip_drm_drv.h"
17 #include "rockchip_drm_debugfs.h"
18 #include "rockchip_drm_fb.h"
19 
20 #define DUMP_BUF_PATH		"/data"
21 #define AFBC_HEADER_SIZE		16
22 #define AFBC_HDR_ALIGN			64
23 #define AFBC_SUPERBLK_PIXELS		256
24 #define AFBC_SUPERBLK_ALIGNMENT		128
25 
26 #define to_rockchip_crtc(x) container_of(x, struct rockchip_crtc, crtc)
27 
temp_pow(int sum,int n)28 static int temp_pow(int sum, int n)
29 {
30 	int i;
31 	int temp = sum;
32 
33 	if (n < 1)
34 		return 1;
35 	for (i = 1; i < n ; i++)
36 		sum *= temp;
37 	return sum;
38 }
39 
get_afbc_size(uint32_t width,uint32_t height,uint32_t bpp)40 static int get_afbc_size(uint32_t width, uint32_t height, uint32_t bpp)
41 {
42 	uint32_t h_alignment = 16;
43 	uint32_t n_blocks;
44 	uint32_t hdr_size;
45 	uint32_t size;
46 
47 	height = ALIGN(height, h_alignment);
48 	n_blocks = width * height / AFBC_SUPERBLK_PIXELS;
49 	hdr_size = ALIGN(n_blocks * AFBC_HEADER_SIZE, AFBC_HDR_ALIGN);
50 
51 	size = hdr_size + n_blocks * ALIGN(bpp * AFBC_SUPERBLK_PIXELS / 8, AFBC_SUPERBLK_ALIGNMENT);
52 
53 	return size;
54 }
55 
rockchip_drm_dump_plane_buffer(struct vop_dump_info * dump_info,int frame_count)56 int rockchip_drm_dump_plane_buffer(struct vop_dump_info *dump_info, int frame_count)
57 {
58 	int flags;
59 	int bpp = 32;
60 	const char *ptr;
61 	char file_name[100];
62 	int width;
63 	size_t size, uv_size = 0;
64 	void *kvaddr, *kvaddr_origin;
65 	struct file *file;
66 	loff_t pos = 0;
67 	struct drm_format_name_buf format_name;
68 	char format[8];
69 
70 	drm_get_format_name(dump_info->format->format, &format_name);
71 	strscpy(format, format_name.str, 5);
72 	bpp = rockchip_drm_get_bpp(dump_info->format);
73 
74 	if (dump_info->yuv_format) {
75 		u8 hsub = dump_info->format->hsub;
76 		u8 vsub = dump_info->format->vsub;
77 
78 		width = dump_info->pitches * 8 / bpp;
79 		flags = O_RDWR | O_CREAT | O_APPEND;
80 		uv_size = (width * dump_info->height * bpp >> 3) * 2 / hsub / vsub;
81 		snprintf(file_name, 100, "%s/video%d_%d_%s.%s", DUMP_BUF_PATH,
82 			 width, dump_info->height, format,
83 			 "bin");
84 	} else {
85 		width = dump_info->pitches * 8 / bpp;
86 		flags = O_RDWR | O_CREAT;
87 		snprintf(file_name, 100, "%s/win%d_area%d_%dx%d_%s%s%d.%s",
88 			 DUMP_BUF_PATH, dump_info->win_id,
89 			 dump_info->area_id, width, dump_info->height,
90 			 format, dump_info->AFBC_flag ?
91 			 "_AFBC_" : "_", frame_count, "bin");
92 	}
93 	kvaddr = vmap(dump_info->pages, dump_info->num_pages, VM_MAP,
94 		      pgprot_writecombine(PAGE_KERNEL));
95 	kvaddr_origin = kvaddr;
96 	if (!kvaddr)
97 		DRM_ERROR("failed to vmap() buffer\n");
98 	else
99 		kvaddr += dump_info->offset;
100 
101 	if (dump_info->AFBC_flag)
102 		size = get_afbc_size(width, dump_info->height, bpp);
103 	else
104 		size = (width * dump_info->height * bpp >> 3) + uv_size;
105 
106 	ptr = file_name;
107 	file = filp_open(ptr, flags, 0644);
108 	if (!IS_ERR(file)) {
109 		kernel_write(file, kvaddr, size, &pos);
110 		DRM_INFO("dump file name is:%s\n", file_name);
111 		fput(file);
112 	} else {
113 		DRM_INFO("open %s failed\n", ptr);
114 	}
115 	vunmap(kvaddr_origin);
116 
117 	return 0;
118 }
119 
rockchip_drm_dump_buffer_show(struct seq_file * m,void * data)120 static int rockchip_drm_dump_buffer_show(struct seq_file *m, void *data)
121 {
122 	seq_puts(m, "  echo dump    > dump to dump one frame\n");
123 	seq_puts(m, "  echo dumpon  > dump to start vop keep dumping\n");
124 	seq_puts(m, "  echo dumpoff > dump to stop keep dumping\n");
125 	seq_puts(m, "  echo dumpn   > dump n is the number of dump times\n");
126 	seq_puts(m, "  dump path is /data\n");
127 
128 	return 0;
129 }
130 
rockchip_drm_dump_buffer_open(struct inode * inode,struct file * file)131 static int rockchip_drm_dump_buffer_open(struct inode *inode, struct file *file)
132 {
133 	struct drm_crtc *crtc = inode->i_private;
134 
135 	return single_open(file, rockchip_drm_dump_buffer_show, crtc);
136 }
137 
138 static ssize_t
rockchip_drm_dump_buffer_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)139 rockchip_drm_dump_buffer_write(struct file *file, const char __user *ubuf,
140 			       size_t len, loff_t *offp)
141 {
142 	struct seq_file *m = file->private_data;
143 	struct drm_crtc *crtc = m->private;
144 	char buf[14] = {};
145 	int dump_times = 0;
146 	struct vop_dump_list *pos, *n;
147 	int i = 0;
148 	struct rockchip_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
149 
150 	if (!rockchip_crtc->vop_dump_list_init_flag)
151 		return -EPERM;
152 
153 	if (len > sizeof(buf) - 1)
154 		return -EINVAL;
155 	if (copy_from_user(buf, ubuf, len))
156 		return -EFAULT;
157 	buf[len - 1] = '\0';
158 	if (strncmp(buf, "dumpon", 6) == 0) {
159 		rockchip_crtc->vop_dump_status = DUMP_KEEP;
160 		DRM_INFO("keep dumping\n");
161 	} else if (strncmp(buf, "dumpoff", 7) == 0) {
162 		rockchip_crtc->vop_dump_status = DUMP_DISABLE;
163 		DRM_INFO("close keep dumping\n");
164 	} else if (strncmp(buf, "dump", 4) == 0) {
165 		if (isdigit(buf[4])) {
166 			for (i = 4; i < strlen(buf); i++) {
167 				dump_times += temp_pow(10, (strlen(buf)
168 						       - i - 1))
169 						       * (buf[i] - '0');
170 		}
171 			rockchip_crtc->vop_dump_times = dump_times;
172 		} else {
173 			drm_modeset_lock_all(crtc->dev);
174 			list_for_each_entry_safe(pos, n,
175 						 &rockchip_crtc->vop_dump_list_head,
176 						 entry) {
177 				rockchip_drm_dump_plane_buffer(&pos->dump_info,
178 							       rockchip_crtc->frame_count);
179 		}
180 			drm_modeset_unlock_all(crtc->dev);
181 			rockchip_crtc->frame_count++;
182 		}
183 	} else {
184 		return -EINVAL;
185 	}
186 
187 	return len;
188 }
189 
190 static const struct file_operations rockchip_drm_dump_buffer_fops = {
191 	.owner = THIS_MODULE,
192 	.open = rockchip_drm_dump_buffer_open,
193 	.read = seq_read,
194 	.llseek = seq_lseek,
195 	.release = single_release,
196 	.write = rockchip_drm_dump_buffer_write,
197 };
198 
rockchip_drm_add_dump_buffer(struct drm_crtc * crtc,struct dentry * root)199 int rockchip_drm_add_dump_buffer(struct drm_crtc *crtc, struct dentry *root)
200 {
201 	struct dentry *vop_dump_root;
202 	struct dentry *ent;
203 	struct rockchip_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
204 
205 	vop_dump_root = debugfs_create_dir("vop_dump", root);
206 	rockchip_crtc->vop_dump_status = DUMP_DISABLE;
207 	rockchip_crtc->vop_dump_list_init_flag = false;
208 	rockchip_crtc->vop_dump_times = 0;
209 	rockchip_crtc->frame_count = 0;
210 	ent = debugfs_create_file("dump", 0644, vop_dump_root,
211 				  crtc, &rockchip_drm_dump_buffer_fops);
212 	if (!ent) {
213 		DRM_ERROR("create vop_plane_dump err\n");
214 		debugfs_remove_recursive(vop_dump_root);
215 	}
216 
217 	return 0;
218 }
219 
rockchip_drm_debugfs_color_bar_show(struct seq_file * s,void * data)220 static int rockchip_drm_debugfs_color_bar_show(struct seq_file *s, void *data)
221 {
222 	seq_puts(s, "  Enable horizontal color bar:\n");
223 	seq_puts(s, "      echo 1 > /sys/kernel/debug/dri/0/video_port0/color_bar\n");
224 	seq_puts(s, "  Enable vertical color bar:\n");
225 	seq_puts(s, "      echo 2 > /sys/kernel/debug/dri/0/video_port0/color_bar\n");
226 	seq_puts(s, "  Disable color bar:\n");
227 	seq_puts(s, "      echo 0 > /sys/kernel/debug/dri/0/video_port0/color_bar\n");
228 
229 	return 0;
230 }
231 
rockchip_drm_debugfs_color_bar_open(struct inode * inode,struct file * file)232 static int rockchip_drm_debugfs_color_bar_open(struct inode *inode, struct file *file)
233 {
234 	struct drm_crtc *crtc = inode->i_private;
235 
236 	return single_open(file, rockchip_drm_debugfs_color_bar_show, crtc);
237 }
238 
rockchip_drm_debugfs_color_bar_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)239 static ssize_t rockchip_drm_debugfs_color_bar_write(struct file *file, const char __user *ubuf,
240 						    size_t len, loff_t *offp)
241 {
242 	struct seq_file *s = file->private_data;
243 	struct drm_crtc *crtc = s->private;
244 	struct rockchip_drm_private *priv = crtc->dev->dev_private;
245 	int pipe = drm_crtc_index(crtc);
246 	u8 mode;
247 
248 	if (len != 2) {
249 		DRM_INFO("Unsupported color bar mode\n");
250 		return -EINVAL;
251 	}
252 
253 	if (kstrtou8_from_user(ubuf, len, 0, &mode))
254 		return -EFAULT;
255 
256 	if (priv->crtc_funcs[pipe]->crtc_set_color_bar) {
257 		if (priv->crtc_funcs[pipe]->crtc_set_color_bar(crtc, mode))
258 			return -EINVAL;
259 	}
260 
261 	return len;
262 }
263 
264 static const struct file_operations rockchip_drm_debugfs_color_bar_fops = {
265 	.owner = THIS_MODULE,
266 	.open = rockchip_drm_debugfs_color_bar_open,
267 	.read = seq_read,
268 	.llseek = seq_lseek,
269 	.release = single_release,
270 	.write = rockchip_drm_debugfs_color_bar_write,
271 };
272 
rockchip_drm_debugfs_add_color_bar(struct drm_crtc * crtc,struct dentry * root)273 int rockchip_drm_debugfs_add_color_bar(struct drm_crtc *crtc, struct dentry *root)
274 {
275 	struct dentry *ent;
276 
277 	ent = debugfs_create_file("color_bar", 0644, root, crtc,
278 				  &rockchip_drm_debugfs_color_bar_fops);
279 	if (!ent)
280 		DRM_ERROR("Failed to add color_bar for debugfs\n");
281 
282 	return 0;
283 }
284