1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2020-2021 NXP
4 */
5
6#include <linux/init.h>
7#include <linux/device.h>
8#include <linux/ioctl.h>
9#include <linux/list.h>
10#include <linux/module.h>
11#include <linux/kernel.h>
12#include <linux/types.h>
13#include <linux/pm_runtime.h>
14#include <media/v4l2-device.h>
15#include <linux/debugfs.h>
16#include "vpu.h"
17#include "vpu_defs.h"
18#include "vpu_core.h"
19#include "vpu_helpers.h"
20#include "vpu_cmds.h"
21#include "vpu_rpc.h"
22#include "vpu_v4l2.h"
23
24struct print_buf_desc {
25 u32 start_h_phy;
26 u32 start_h_vir;
27 u32 start_m;
28 u32 bytes;
29 u32 read;
30 u32 write;
31 char buffer[];
32};
33
34static char *vb2_stat_name[] = {
35 [VB2_BUF_STATE_DEQUEUED] = "dequeued",
36 [VB2_BUF_STATE_IN_REQUEST] = "in_request",
37 [VB2_BUF_STATE_PREPARING] = "preparing",
38 [VB2_BUF_STATE_QUEUED] = "queued",
39 [VB2_BUF_STATE_ACTIVE] = "active",
40 [VB2_BUF_STATE_DONE] = "done",
41 [VB2_BUF_STATE_ERROR] = "error",
42};
43
44static char *vpu_stat_name[] = {
45 [VPU_BUF_STATE_IDLE] = "idle",
46 [VPU_BUF_STATE_INUSE] = "inuse",
47 [VPU_BUF_STATE_DECODED] = "decoded",
48 [VPU_BUF_STATE_READY] = "ready",
49 [VPU_BUF_STATE_SKIP] = "skip",
50 [VPU_BUF_STATE_ERROR] = "error",
51};
52
53static inline const char *to_vpu_stat_name(int state)
54{
55 if (state <= VPU_BUF_STATE_ERROR)
56 return vpu_stat_name[state];
57 return "unknown";
58}
59
60static int vpu_dbg_instance(struct seq_file *s, void *data)
61{
62 struct vpu_inst *inst = s->private;
63 char str[128];
64 int num;
65 struct vb2_queue *vq;
66 int i;
67
68 if (!inst->fh.m2m_ctx)
69 return 0;
70 num = scnprintf(buf: str, size: sizeof(str), fmt: "[%s]\n", vpu_core_type_desc(type: inst->type));
71 if (seq_write(seq: s, data: str, len: num))
72 return 0;
73
74 num = scnprintf(buf: str, size: sizeof(str), fmt: "tgig = %d,pid = %d\n", inst->tgid, inst->pid);
75 if (seq_write(seq: s, data: str, len: num))
76 return 0;
77 num = scnprintf(buf: str, size: sizeof(str), fmt: "state = %s\n", vpu_codec_state_name(state: inst->state));
78 if (seq_write(seq: s, data: str, len: num))
79 return 0;
80 num = scnprintf(buf: str, size: sizeof(str),
81 fmt: "min_buffer_out = %d, min_buffer_cap = %d\n",
82 inst->min_buffer_out, inst->min_buffer_cap);
83 if (seq_write(seq: s, data: str, len: num))
84 return 0;
85
86 vq = v4l2_m2m_get_src_vq(m2m_ctx: inst->fh.m2m_ctx);
87 num = scnprintf(buf: str, size: sizeof(str),
88 fmt: "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",
89 vb2_is_streaming(q: vq),
90 vb2_get_num_buffers(q: vq),
91 inst->out_format.pixfmt,
92 inst->out_format.pixfmt >> 8,
93 inst->out_format.pixfmt >> 16,
94 inst->out_format.pixfmt >> 24,
95 inst->out_format.width,
96 inst->out_format.height,
97 vq->last_buffer_dequeued);
98 if (seq_write(seq: s, data: str, len: num))
99 return 0;
100 for (i = 0; i < inst->out_format.mem_planes; i++) {
101 num = scnprintf(buf: str, size: sizeof(str), fmt: " %d(%d)",
102 vpu_get_fmt_plane_size(fmt: &inst->out_format, plane_no: i),
103 inst->out_format.bytesperline[i]);
104 if (seq_write(seq: s, data: str, len: num))
105 return 0;
106 }
107 if (seq_write(seq: s, data: "\n", len: 1))
108 return 0;
109
110 vq = v4l2_m2m_get_dst_vq(m2m_ctx: inst->fh.m2m_ctx);
111 num = scnprintf(buf: str, size: sizeof(str),
112 fmt: "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",
113 vb2_is_streaming(q: vq),
114 vb2_get_num_buffers(q: vq),
115 inst->cap_format.pixfmt,
116 inst->cap_format.pixfmt >> 8,
117 inst->cap_format.pixfmt >> 16,
118 inst->cap_format.pixfmt >> 24,
119 inst->cap_format.width,
120 inst->cap_format.height,
121 vq->last_buffer_dequeued);
122 if (seq_write(seq: s, data: str, len: num))
123 return 0;
124 for (i = 0; i < inst->cap_format.mem_planes; i++) {
125 num = scnprintf(buf: str, size: sizeof(str), fmt: " %d(%d)",
126 vpu_get_fmt_plane_size(fmt: &inst->cap_format, plane_no: i),
127 inst->cap_format.bytesperline[i]);
128 if (seq_write(seq: s, data: str, len: num))
129 return 0;
130 }
131 if (seq_write(seq: s, data: "\n", len: 1))
132 return 0;
133 num = scnprintf(buf: str, size: sizeof(str), fmt: "crop: (%d, %d) %d x %d\n",
134 inst->crop.left,
135 inst->crop.top,
136 inst->crop.width,
137 inst->crop.height);
138 if (seq_write(seq: s, data: str, len: num))
139 return 0;
140
141 vq = v4l2_m2m_get_src_vq(m2m_ctx: inst->fh.m2m_ctx);
142 for (i = 0; i < vb2_get_num_buffers(q: vq); i++) {
143 struct vb2_buffer *vb;
144 struct vb2_v4l2_buffer *vbuf;
145
146 vb = vb2_get_buffer(q: vq, index: i);
147 if (!vb)
148 continue;
149
150 if (vb->state == VB2_BUF_STATE_DEQUEUED)
151 continue;
152
153 vbuf = to_vb2_v4l2_buffer(vb);
154
155 num = scnprintf(buf: str, size: sizeof(str),
156 fmt: "output [%2d] state = %10s, %8s\n",
157 i, vb2_stat_name[vb->state],
158 to_vpu_stat_name(state: vpu_get_buffer_state(vbuf)));
159 if (seq_write(seq: s, data: str, len: num))
160 return 0;
161 }
162
163 vq = v4l2_m2m_get_dst_vq(m2m_ctx: inst->fh.m2m_ctx);
164 for (i = 0; i < vb2_get_num_buffers(q: vq); i++) {
165 struct vb2_buffer *vb;
166 struct vb2_v4l2_buffer *vbuf;
167
168 vb = vb2_get_buffer(q: vq, index: i);
169 if (!vb)
170 continue;
171
172 if (vb->state == VB2_BUF_STATE_DEQUEUED)
173 continue;
174
175 vbuf = to_vb2_v4l2_buffer(vb);
176
177 num = scnprintf(buf: str, size: sizeof(str),
178 fmt: "capture[%2d] state = %10s, %8s\n",
179 i, vb2_stat_name[vb->state],
180 to_vpu_stat_name(state: vpu_get_buffer_state(vbuf)));
181 if (seq_write(seq: s, data: str, len: num))
182 return 0;
183 }
184
185 num = scnprintf(buf: str, size: sizeof(str), fmt: "sequence = %d\n", inst->sequence);
186 if (seq_write(seq: s, data: str, len: num))
187 return 0;
188
189 if (inst->use_stream_buffer) {
190 num = scnprintf(buf: str, size: sizeof(str), fmt: "stream_buffer = %d / %d, <%pad, 0x%x>\n",
191 vpu_helper_get_used_space(inst),
192 inst->stream_buffer.length,
193 &inst->stream_buffer.phys,
194 inst->stream_buffer.length);
195 if (seq_write(seq: s, data: str, len: num))
196 return 0;
197 }
198 num = scnprintf(buf: str, size: sizeof(str), fmt: "kfifo len = 0x%x\n", kfifo_len(&inst->msg_fifo));
199 if (seq_write(seq: s, data: str, len: num))
200 return 0;
201
202 num = scnprintf(buf: str, size: sizeof(str), fmt: "flow :\n");
203 if (seq_write(seq: s, data: str, len: num))
204 return 0;
205
206 mutex_lock(&inst->core->cmd_lock);
207 for (i = 0; i < ARRAY_SIZE(inst->flows); i++) {
208 u32 idx = (inst->flow_idx + i) % (ARRAY_SIZE(inst->flows));
209
210 if (!inst->flows[idx])
211 continue;
212 num = scnprintf(buf: str, size: sizeof(str), fmt: "\t[%s] %s\n",
213 inst->flows[idx] >= VPU_MSG_ID_NOOP ? "M" : "C",
214 vpu_id_name(id: inst->flows[idx]));
215 if (seq_write(seq: s, data: str, len: num)) {
216 mutex_unlock(lock: &inst->core->cmd_lock);
217 return 0;
218 }
219 }
220 mutex_unlock(lock: &inst->core->cmd_lock);
221
222 i = 0;
223 while (true) {
224 num = call_vop(inst, get_debug_info, str, sizeof(str), i++);
225 if (num <= 0)
226 break;
227 if (seq_write(seq: s, data: str, len: num))
228 return 0;
229 }
230
231 return 0;
232}
233
234static int vpu_dbg_core(struct seq_file *s, void *data)
235{
236 struct vpu_core *core = s->private;
237 struct vpu_shared_addr *iface = core->iface;
238 char str[128];
239 int num;
240
241 num = scnprintf(buf: str, size: sizeof(str), fmt: "[%s]\n", vpu_core_type_desc(type: core->type));
242 if (seq_write(seq: s, data: str, len: num))
243 return 0;
244
245 num = scnprintf(buf: str, size: sizeof(str), fmt: "boot_region = <%pad, 0x%x>\n",
246 &core->fw.phys, core->fw.length);
247 if (seq_write(seq: s, data: str, len: num))
248 return 0;
249 num = scnprintf(buf: str, size: sizeof(str), fmt: "rpc_region = <%pad, 0x%x> used = 0x%x\n",
250 &core->rpc.phys, core->rpc.length, core->rpc.bytesused);
251 if (seq_write(seq: s, data: str, len: num))
252 return 0;
253 num = scnprintf(buf: str, size: sizeof(str), fmt: "fwlog_region = <%pad, 0x%x>\n",
254 &core->log.phys, core->log.length);
255 if (seq_write(seq: s, data: str, len: num))
256 return 0;
257
258 num = scnprintf(buf: str, size: sizeof(str), fmt: "power %s\n",
259 vpu_iface_get_power_state(core) ? "on" : "off");
260 if (seq_write(seq: s, data: str, len: num))
261 return 0;
262 num = scnprintf(buf: str, size: sizeof(str), fmt: "state = %d\n", core->state);
263 if (seq_write(seq: s, data: str, len: num))
264 return 0;
265 if (core->state == VPU_CORE_DEINIT)
266 return 0;
267 num = scnprintf(buf: str, size: sizeof(str), fmt: "fw version = %d.%d.%d\n",
268 (core->fw_version >> 16) & 0xff,
269 (core->fw_version >> 8) & 0xff,
270 core->fw_version & 0xff);
271 if (seq_write(seq: s, data: str, len: num))
272 return 0;
273 num = scnprintf(buf: str, size: sizeof(str), fmt: "instances = %d/%d (0x%02lx), %d\n",
274 hweight32(core->instance_mask),
275 core->supported_instance_count,
276 core->instance_mask,
277 core->request_count);
278 if (seq_write(seq: s, data: str, len: num))
279 return 0;
280 num = scnprintf(buf: str, size: sizeof(str), fmt: "kfifo len = 0x%x\n", kfifo_len(&core->msg_fifo));
281 if (seq_write(seq: s, data: str, len: num))
282 return 0;
283 num = scnprintf(buf: str, size: sizeof(str),
284 fmt: "cmd_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n",
285 iface->cmd_desc->start,
286 iface->cmd_desc->end,
287 iface->cmd_desc->wptr,
288 iface->cmd_desc->rptr);
289 if (seq_write(seq: s, data: str, len: num))
290 return 0;
291 num = scnprintf(buf: str, size: sizeof(str),
292 fmt: "msg_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n",
293 iface->msg_desc->start,
294 iface->msg_desc->end,
295 iface->msg_desc->wptr,
296 iface->msg_desc->rptr);
297 if (seq_write(seq: s, data: str, len: num))
298 return 0;
299
300 return 0;
301}
302
303static int vpu_dbg_fwlog(struct seq_file *s, void *data)
304{
305 struct vpu_core *core = s->private;
306 struct print_buf_desc *print_buf;
307 int length;
308 u32 rptr;
309 u32 wptr;
310 int ret = 0;
311
312 if (!core->log.virt || core->state == VPU_CORE_DEINIT)
313 return 0;
314
315 print_buf = core->log.virt;
316 rptr = print_buf->read;
317 wptr = print_buf->write;
318
319 if (rptr == wptr)
320 return 0;
321 else if (rptr < wptr)
322 length = wptr - rptr;
323 else
324 length = print_buf->bytes + wptr - rptr;
325
326 if (s->count + length >= s->size) {
327 s->count = s->size;
328 return 0;
329 }
330
331 if (rptr + length >= print_buf->bytes) {
332 int num = print_buf->bytes - rptr;
333
334 if (seq_write(seq: s, data: print_buf->buffer + rptr, len: num))
335 ret = -1;
336 length -= num;
337 rptr = 0;
338 }
339
340 if (length) {
341 if (seq_write(seq: s, data: print_buf->buffer + rptr, len: length))
342 ret = -1;
343 rptr += length;
344 }
345 if (!ret)
346 print_buf->read = rptr;
347
348 return 0;
349}
350
351static int vpu_dbg_inst_open(struct inode *inode, struct file *filp)
352{
353 return single_open(filp, vpu_dbg_instance, inode->i_private);
354}
355
356static ssize_t vpu_dbg_inst_write(struct file *file,
357 const char __user *user_buf, size_t size, loff_t *ppos)
358{
359 struct seq_file *s = file->private_data;
360 struct vpu_inst *inst = s->private;
361
362 vpu_session_debug(inst);
363
364 return size;
365}
366
367static ssize_t vpu_dbg_core_write(struct file *file,
368 const char __user *user_buf, size_t size, loff_t *ppos)
369{
370 struct seq_file *s = file->private_data;
371 struct vpu_core *core = s->private;
372
373 pm_runtime_resume_and_get(dev: core->dev);
374 mutex_lock(&core->lock);
375 if (vpu_iface_get_power_state(core) && !core->request_count) {
376 dev_info(core->dev, "reset\n");
377 if (!vpu_core_sw_reset(core)) {
378 vpu_core_set_state(core, state: VPU_CORE_ACTIVE);
379 core->hang_mask = 0;
380 }
381 }
382 mutex_unlock(lock: &core->lock);
383 pm_runtime_put_sync(dev: core->dev);
384
385 return size;
386}
387
388static int vpu_dbg_core_open(struct inode *inode, struct file *filp)
389{
390 return single_open(filp, vpu_dbg_core, inode->i_private);
391}
392
393static int vpu_dbg_fwlog_open(struct inode *inode, struct file *filp)
394{
395 return single_open(filp, vpu_dbg_fwlog, inode->i_private);
396}
397
398static const struct file_operations vpu_dbg_inst_fops = {
399 .owner = THIS_MODULE,
400 .open = vpu_dbg_inst_open,
401 .release = single_release,
402 .read = seq_read,
403 .write = vpu_dbg_inst_write,
404};
405
406static const struct file_operations vpu_dbg_core_fops = {
407 .owner = THIS_MODULE,
408 .open = vpu_dbg_core_open,
409 .release = single_release,
410 .read = seq_read,
411 .write = vpu_dbg_core_write,
412};
413
414static const struct file_operations vpu_dbg_fwlog_fops = {
415 .owner = THIS_MODULE,
416 .open = vpu_dbg_fwlog_open,
417 .release = single_release,
418 .read = seq_read,
419};
420
421int vpu_inst_create_dbgfs_file(struct vpu_inst *inst)
422{
423 struct vpu_dev *vpu;
424 char name[64];
425
426 if (!inst || !inst->core || !inst->core->vpu)
427 return -EINVAL;
428
429 vpu = inst->core->vpu;
430 if (!vpu->debugfs)
431 return -EINVAL;
432
433 if (inst->debugfs)
434 return 0;
435
436 scnprintf(buf: name, size: sizeof(name), fmt: "instance.%d.%d", inst->core->id, inst->id);
437 inst->debugfs = debugfs_create_file(name: (const char *)name,
438 VERIFY_OCTAL_PERMISSIONS(0644),
439 parent: vpu->debugfs,
440 data: inst,
441 fops: &vpu_dbg_inst_fops);
442
443 return 0;
444}
445
446int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst)
447{
448 if (!inst)
449 return 0;
450
451 debugfs_remove(dentry: inst->debugfs);
452 inst->debugfs = NULL;
453
454 return 0;
455}
456
457int vpu_core_create_dbgfs_file(struct vpu_core *core)
458{
459 struct vpu_dev *vpu;
460 char name[64];
461
462 if (!core || !core->vpu)
463 return -EINVAL;
464
465 vpu = core->vpu;
466 if (!vpu->debugfs)
467 return -EINVAL;
468
469 if (!core->debugfs) {
470 scnprintf(buf: name, size: sizeof(name), fmt: "core.%d", core->id);
471 core->debugfs = debugfs_create_file(name: (const char *)name,
472 VERIFY_OCTAL_PERMISSIONS(0644),
473 parent: vpu->debugfs,
474 data: core,
475 fops: &vpu_dbg_core_fops);
476 }
477 if (!core->debugfs_fwlog) {
478 scnprintf(buf: name, size: sizeof(name), fmt: "fwlog.%d", core->id);
479 core->debugfs_fwlog = debugfs_create_file(name: (const char *)name,
480 VERIFY_OCTAL_PERMISSIONS(0444),
481 parent: vpu->debugfs,
482 data: core,
483 fops: &vpu_dbg_fwlog_fops);
484 }
485
486 return 0;
487}
488
489int vpu_core_remove_dbgfs_file(struct vpu_core *core)
490{
491 if (!core)
492 return 0;
493 debugfs_remove(dentry: core->debugfs);
494 core->debugfs = NULL;
495 debugfs_remove(dentry: core->debugfs_fwlog);
496 core->debugfs_fwlog = NULL;
497
498 return 0;
499}
500
501void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow)
502{
503 if (!inst)
504 return;
505
506 inst->flows[inst->flow_idx] = flow;
507 inst->flow_idx = (inst->flow_idx + 1) % (ARRAY_SIZE(inst->flows));
508}
509

source code of linux/drivers/media/platform/amphion/vpu_dbg.c