509 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2020-2021 NXP4 */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(str, sizeof(str), "[%s]\n", vpu_core_type_desc(inst->type));71 if (seq_write(s, str, num))72 return 0;73 74 num = scnprintf(str, sizeof(str), "tgig = %d,pid = %d\n", inst->tgid, inst->pid);75 if (seq_write(s, str, num))76 return 0;77 num = scnprintf(str, sizeof(str), "state = %s\n", vpu_codec_state_name(inst->state));78 if (seq_write(s, str, num))79 return 0;80 num = scnprintf(str, sizeof(str),81 "min_buffer_out = %d, min_buffer_cap = %d\n",82 inst->min_buffer_out, inst->min_buffer_cap);83 if (seq_write(s, str, num))84 return 0;85 86 vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);87 num = scnprintf(str, sizeof(str),88 "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",89 vb2_is_streaming(vq),90 vb2_get_num_buffers(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(s, str, num))99 return 0;100 for (i = 0; i < inst->out_format.mem_planes; i++) {101 num = scnprintf(str, sizeof(str), " %d(%d)",102 vpu_get_fmt_plane_size(&inst->out_format, i),103 inst->out_format.bytesperline[i]);104 if (seq_write(s, str, num))105 return 0;106 }107 if (seq_write(s, "\n", 1))108 return 0;109 110 vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);111 num = scnprintf(str, sizeof(str),112 "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",113 vb2_is_streaming(vq),114 vb2_get_num_buffers(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(s, str, num))123 return 0;124 for (i = 0; i < inst->cap_format.mem_planes; i++) {125 num = scnprintf(str, sizeof(str), " %d(%d)",126 vpu_get_fmt_plane_size(&inst->cap_format, i),127 inst->cap_format.bytesperline[i]);128 if (seq_write(s, str, num))129 return 0;130 }131 if (seq_write(s, "\n", 1))132 return 0;133 num = scnprintf(str, sizeof(str), "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(s, str, num))139 return 0;140 141 vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);142 for (i = 0; i < vb2_get_num_buffers(vq); i++) {143 struct vb2_buffer *vb;144 struct vb2_v4l2_buffer *vbuf;145 146 vb = vb2_get_buffer(vq, 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(str, sizeof(str),156 "output [%2d] state = %10s, %8s\n",157 i, vb2_stat_name[vb->state],158 to_vpu_stat_name(vpu_get_buffer_state(vbuf)));159 if (seq_write(s, str, num))160 return 0;161 }162 163 vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);164 for (i = 0; i < vb2_get_num_buffers(vq); i++) {165 struct vb2_buffer *vb;166 struct vb2_v4l2_buffer *vbuf;167 168 vb = vb2_get_buffer(vq, 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(str, sizeof(str),178 "capture[%2d] state = %10s, %8s\n",179 i, vb2_stat_name[vb->state],180 to_vpu_stat_name(vpu_get_buffer_state(vbuf)));181 if (seq_write(s, str, num))182 return 0;183 }184 185 num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence);186 if (seq_write(s, str, num))187 return 0;188 189 if (inst->use_stream_buffer) {190 num = scnprintf(str, sizeof(str), "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(s, str, num))196 return 0;197 }198 num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&inst->msg_fifo));199 if (seq_write(s, str, num))200 return 0;201 202 num = scnprintf(str, sizeof(str), "flow :\n");203 if (seq_write(s, str, 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(str, sizeof(str), "\t[%s] %s\n",213 inst->flows[idx] >= VPU_MSG_ID_NOOP ? "M" : "C",214 vpu_id_name(inst->flows[idx]));215 if (seq_write(s, str, num)) {216 mutex_unlock(&inst->core->cmd_lock);217 return 0;218 }219 }220 mutex_unlock(&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(s, str, 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(str, sizeof(str), "[%s]\n", vpu_core_type_desc(core->type));242 if (seq_write(s, str, num))243 return 0;244 245 num = scnprintf(str, sizeof(str), "boot_region = <%pad, 0x%x>\n",246 &core->fw.phys, core->fw.length);247 if (seq_write(s, str, num))248 return 0;249 num = scnprintf(str, sizeof(str), "rpc_region = <%pad, 0x%x> used = 0x%x\n",250 &core->rpc.phys, core->rpc.length, core->rpc.bytesused);251 if (seq_write(s, str, num))252 return 0;253 num = scnprintf(str, sizeof(str), "fwlog_region = <%pad, 0x%x>\n",254 &core->log.phys, core->log.length);255 if (seq_write(s, str, num))256 return 0;257 258 num = scnprintf(str, sizeof(str), "power %s\n",259 vpu_iface_get_power_state(core) ? "on" : "off");260 if (seq_write(s, str, num))261 return 0;262 num = scnprintf(str, sizeof(str), "state = %d\n", core->state);263 if (seq_write(s, str, num))264 return 0;265 if (core->state == VPU_CORE_DEINIT)266 return 0;267 num = scnprintf(str, sizeof(str), "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(s, str, num))272 return 0;273 num = scnprintf(str, sizeof(str), "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(s, str, num))279 return 0;280 num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&core->msg_fifo));281 if (seq_write(s, str, num))282 return 0;283 num = scnprintf(str, sizeof(str),284 "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(s, str, num))290 return 0;291 num = scnprintf(str, sizeof(str),292 "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(s, str, 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 else324 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(s, print_buf->buffer + rptr, num))335 ret = -1;336 length -= num;337 rptr = 0;338 }339 340 if (length) {341 if (seq_write(s, print_buf->buffer + rptr, 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(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, VPU_CORE_ACTIVE);379 core->hang_mask = 0;380 }381 }382 mutex_unlock(&core->lock);383 pm_runtime_put_sync(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(name, sizeof(name), "instance.%d.%d", inst->core->id, inst->id);437 inst->debugfs = debugfs_create_file((const char *)name,438 VERIFY_OCTAL_PERMISSIONS(0644),439 vpu->debugfs,440 inst,441 &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(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(name, sizeof(name), "core.%d", core->id);471 core->debugfs = debugfs_create_file((const char *)name,472 VERIFY_OCTAL_PERMISSIONS(0644),473 vpu->debugfs,474 core,475 &vpu_dbg_core_fops);476 }477 if (!core->debugfs_fwlog) {478 scnprintf(name, sizeof(name), "fwlog.%d", core->id);479 core->debugfs_fwlog = debugfs_create_file((const char *)name,480 VERIFY_OCTAL_PERMISSIONS(0444),481 vpu->debugfs,482 core,483 &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(core->debugfs);494 core->debugfs = NULL;495 debugfs_remove(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