root/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2023 MediaTek Inc.
 * Author: Yunfei Dong <yunfei.dong@mediatek.com>
 */

#include <linux/debugfs.h>

#include "mtk_vcodec_dbgfs.h"
#include "../decoder/mtk_vcodec_dec_drv.h"
#include "../encoder/mtk_vcodec_enc_drv.h"
#include "mtk_vcodec_util.h"

static void mtk_vdec_dbgfs_get_format_type(struct mtk_vcodec_dec_ctx *ctx, char *buf,
                                           int *used, int total)
{
        int curr_len;

        switch (ctx->current_codec) {
        case V4L2_PIX_FMT_H264_SLICE:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\toutput format: h264 slice\n");
                break;
        case V4L2_PIX_FMT_VP8_FRAME:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\toutput format: vp8 slice\n");
                break;
        case V4L2_PIX_FMT_VP9_FRAME:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\toutput format: vp9 slice\n");
                break;
        default:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\tunsupported output format: 0x%x\n",
                                    ctx->current_codec);
        }
        *used += curr_len;

        switch (ctx->capture_fourcc) {
        case V4L2_PIX_FMT_MM21:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\tcapture format: MM21\n");
                break;
        case V4L2_PIX_FMT_MT21C:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\tcapture format: MT21C\n");
                break;
        default:
                curr_len = snprintf(buf + *used, total - *used,
                                    "\tunsupported capture format: 0x%x\n",
                                    ctx->capture_fourcc);
        }
        *used += curr_len;
}

static void mtk_vdec_dbgfs_get_help(char *buf, int *used, int total)
{
        int curr_len;

        curr_len = snprintf(buf + *used, total - *used,
                            "help: (1: echo -'info' > vdec 2: cat vdec)\n");
        *used += curr_len;

        curr_len = snprintf(buf + *used, total - *used,
                            "\t-picinfo: get resolution\n");
        *used += curr_len;

        curr_len = snprintf(buf + *used, total - *used,
                            "\t-format: get output & capture queue format\n");
        *used += curr_len;
}

static ssize_t mtk_vdec_dbgfs_write(struct file *filp, const char __user *ubuf,
                                    size_t count, loff_t *ppos)
{
        struct mtk_vcodec_dec_dev *vcodec_dev = filp->private_data;
        struct mtk_vcodec_dbgfs *dbgfs = &vcodec_dev->dbgfs;

        mutex_lock(&dbgfs->dbgfs_lock);
        dbgfs->buf_size = simple_write_to_buffer(dbgfs->dbgfs_buf, sizeof(dbgfs->dbgfs_buf),
                                                 ppos, ubuf, count);
        mutex_unlock(&dbgfs->dbgfs_lock);
        if (dbgfs->buf_size > 0)
                return count;

        return dbgfs->buf_size;
}

static ssize_t mtk_vdec_dbgfs_read(struct file *filp, char __user *ubuf,
                                   size_t count, loff_t *ppos)
{
        struct mtk_vcodec_dec_dev *vcodec_dev = filp->private_data;
        struct mtk_vcodec_dbgfs *dbgfs = &vcodec_dev->dbgfs;
        struct mtk_vcodec_dbgfs_inst *dbgfs_inst;
        struct mtk_vcodec_dec_ctx *ctx;
        int total_len = 200 * (dbgfs->inst_count == 0 ? 1 : dbgfs->inst_count);
        int used_len = 0, curr_len, ret;
        bool dbgfs_index[MTK_VDEC_DBGFS_MAX] = {0};
        char *buf = kmalloc(total_len, GFP_KERNEL);

        if (!buf)
                return -ENOMEM;

        if (strstr(dbgfs->dbgfs_buf, "-help") || dbgfs->buf_size == 1) {
                mtk_vdec_dbgfs_get_help(buf, &used_len, total_len);
                goto read_buffer;
        }

        if (strstr(dbgfs->dbgfs_buf, "-picinfo"))
                dbgfs_index[MTK_VDEC_DBGFS_PICINFO] = true;

        if (strstr(dbgfs->dbgfs_buf, "-format"))
                dbgfs_index[MTK_VDEC_DBGFS_FORMAT] = true;

        mutex_lock(&dbgfs->dbgfs_lock);
        list_for_each_entry(dbgfs_inst, &dbgfs->dbgfs_head, node) {
                ctx = dbgfs_inst->vcodec_ctx;

                curr_len = snprintf(buf + used_len, total_len - used_len,
                                    "inst[%d]:\n ", ctx->id);
                used_len += curr_len;

                if (dbgfs_index[MTK_VDEC_DBGFS_PICINFO]) {
                        curr_len = snprintf(buf + used_len, total_len - used_len,
                                            "\treal(%dx%d)=>align(%dx%d)\n",
                                            ctx->picinfo.pic_w, ctx->picinfo.pic_h,
                                            ctx->picinfo.buf_w, ctx->picinfo.buf_h);
                        used_len += curr_len;
                }

                if (dbgfs_index[MTK_VDEC_DBGFS_FORMAT])
                        mtk_vdec_dbgfs_get_format_type(ctx, buf, &used_len, total_len);
        }
        mutex_unlock(&dbgfs->dbgfs_lock);
read_buffer:
        ret = simple_read_from_buffer(ubuf, count, ppos, buf, used_len);
        kfree(buf);
        return ret;
}

static const struct file_operations vdec_fops = {
        .open = simple_open,
        .write = mtk_vdec_dbgfs_write,
        .read = mtk_vdec_dbgfs_read,
};

void mtk_vcodec_dbgfs_create(struct mtk_vcodec_dec_ctx *ctx)
{
        struct mtk_vcodec_dbgfs_inst *dbgfs_inst;
        struct mtk_vcodec_dec_dev *vcodec_dev = ctx->dev;

        dbgfs_inst = kzalloc_obj(*dbgfs_inst);
        if (!dbgfs_inst)
                return;

        list_add_tail(&dbgfs_inst->node, &vcodec_dev->dbgfs.dbgfs_head);

        vcodec_dev->dbgfs.inst_count++;

        dbgfs_inst->inst_id = ctx->id;
        dbgfs_inst->vcodec_ctx = ctx;
}
EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_create);

void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dec_dev *vcodec_dev, int ctx_id)
{
        struct mtk_vcodec_dbgfs_inst *dbgfs_inst;

        list_for_each_entry(dbgfs_inst, &vcodec_dev->dbgfs.dbgfs_head, node) {
                if (dbgfs_inst->inst_id == ctx_id) {
                        vcodec_dev->dbgfs.inst_count--;
                        list_del(&dbgfs_inst->node);
                        kfree(dbgfs_inst);
                        return;
                }
        }
}
EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_remove);

static void mtk_vcodec_dbgfs_vdec_init(struct mtk_vcodec_dec_dev *vcodec_dev)
{
        struct dentry *vcodec_root;

        vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-dec", NULL);
        if (IS_ERR(vcodec_dev->dbgfs.vcodec_root))
                dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%pe\n",
                        vcodec_dev->dbgfs.vcodec_root);

        vcodec_root = vcodec_dev->dbgfs.vcodec_root;
        debugfs_create_x32("mtk_v4l2_dbg_level", 0644, vcodec_root, &mtk_v4l2_dbg_level);
        debugfs_create_x32("mtk_vcodec_dbg", 0644, vcodec_root, &mtk_vcodec_dbg);

        vcodec_dev->dbgfs.inst_count = 0;
        INIT_LIST_HEAD(&vcodec_dev->dbgfs.dbgfs_head);
        debugfs_create_file("vdec", 0200, vcodec_root, vcodec_dev, &vdec_fops);
        mutex_init(&vcodec_dev->dbgfs.dbgfs_lock);
}

static void mtk_vcodec_dbgfs_venc_init(struct mtk_vcodec_enc_dev *vcodec_dev)
{
        struct dentry *vcodec_root;

        vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-enc", NULL);
        if (IS_ERR(vcodec_dev->dbgfs.vcodec_root))
                dev_err(&vcodec_dev->plat_dev->dev, "create venc dir err:%d\n",
                        IS_ERR(vcodec_dev->dbgfs.vcodec_root));

        vcodec_root = vcodec_dev->dbgfs.vcodec_root;
        debugfs_create_x32("mtk_v4l2_dbg_level", 0644, vcodec_root, &mtk_v4l2_dbg_level);
        debugfs_create_x32("mtk_vcodec_dbg", 0644, vcodec_root, &mtk_vcodec_dbg);

        vcodec_dev->dbgfs.inst_count = 0;
}

void mtk_vcodec_dbgfs_init(void *vcodec_dev, bool is_encode)
{
        if (is_encode)
                mtk_vcodec_dbgfs_venc_init(vcodec_dev);
        else
                mtk_vcodec_dbgfs_vdec_init(vcodec_dev);
}
EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_init);

void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dbgfs *dbgfs)
{
        debugfs_remove_recursive(dbgfs->vcodec_root);
}
EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_deinit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Mediatek video codec driver");