Files
linux/drivers/media/platform/amd/isp4/isp4_debug.c
Bin Du ec4bec227b media: platform: amd: isp4 debug fs logging and more descriptive errors
Add debug fs for isp4 driver and add more detailed descriptive error info
to some of the log message

Co-developed-by: Sultan Alsawaf <sultan@kerneltoast.com>
Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
Co-developed-by: Svetoslav Stoilov <Svetoslav.Stoilov@amd.com>
Signed-off-by: Svetoslav Stoilov <Svetoslav.Stoilov@amd.com>
Signed-off-by: Bin Du <Bin.Du@amd.com>
Reviewed-by: Sultan Alsawaf <sultan@kerneltoast.com>
Tested-by: Alexey Zagorodnikov <xglooom@gmail.com>
Tested-by: Kate Hsuan <hpa@redhat.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
2026-05-06 13:48:09 +03:00

272 lines
6.4 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2025 Advanced Micro Devices, Inc.
*/
#include "isp4.h"
#include "isp4_debug.h"
#include "isp4_hw_reg.h"
#include "isp4_interface.h"
#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024)
#define ISP4DBG_MACRO_2_STR(X) #X
#define ISP4DBG_ONE_TIME_LOG_LEN 510
#ifdef CONFIG_DEBUG_FS
void isp_debugfs_create(struct isp4_device *isp_dev)
{
isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL);
debugfs_create_bool("fw_log_enable", 0644,
isp_dev->isp_subdev.debugfs_dir,
&isp_dev->isp_subdev.enable_fw_log);
isp_dev->isp_subdev.fw_log_output =
devm_kzalloc(isp_dev->isp_subdev.dev,
ISP4DBG_FW_LOG_RINGBUF_SIZE + 32,
GFP_KERNEL);
}
void isp_debugfs_remove(struct isp4_device *isp_dev)
{
debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir);
isp_dev->isp_subdev.debugfs_dir = NULL;
}
static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size)
{
struct isp4_interface *ispif = &isp->ispif;
char *buf = isp->fw_log_output;
struct device *dev = isp->dev;
u32 rd_ptr, wr_ptr;
u32 total_cnt = 0;
u32 offset = 0;
u32 cnt;
if (!sys || !rb_size)
return 0;
guard(mutex)(&ispif->isp4if_mutex);
rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0);
wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0);
do {
if (wr_ptr > rd_ptr)
cnt = wr_ptr - rd_ptr;
else if (wr_ptr < rd_ptr)
cnt = rb_size - rd_ptr;
else
goto quit;
if (cnt > rb_size) {
dev_err(dev, "fail bad fw log size %u\n", cnt);
goto quit;
}
memcpy(buf + offset, sys + rd_ptr, cnt);
offset += cnt;
total_cnt += cnt;
rd_ptr = (rd_ptr + cnt) % rb_size;
} while (rd_ptr < wr_ptr);
isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr);
quit:
return total_cnt;
}
void isp_fw_log_print(struct isp4_subdev *isp)
{
struct isp4_interface *ispif = &isp->ispif;
char *fw_log_buf = isp->fw_log_output;
u32 cnt;
if (!isp->enable_fw_log || !fw_log_buf)
return;
cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr,
ispif->fw_log_buf->mem_size);
if (cnt) {
char temp_ch;
char *str;
char *end;
/* line end */
char *le;
str = (char *)fw_log_buf;
end = ((char *)fw_log_buf + cnt);
fw_log_buf[cnt] = 0;
while (str < end) {
le = strchr(str, 0x0A);
if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) ||
(!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) {
if (le)
*le = 0;
if (*str != '\0')
dev_dbg(isp->dev, "%s", str);
if (le) {
*le = 0x0A;
str = le + 1;
} else {
break;
}
} else {
u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN;
temp_ch = str[tmp_len];
str[tmp_len] = 0;
dev_dbg(isp->dev, "%s", str);
str[tmp_len] = temp_ch;
str = &str[tmp_len];
}
}
}
}
#endif
char *isp4dbg_get_buf_src_str(u32 src)
{
switch (src) {
case ISP4FW_BUFFER_SOURCE_STREAM:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM);
default:
return "Unknown buf source";
}
}
char *isp4dbg_get_buf_done_str(u32 status)
{
switch (status) {
case ISP4FW_BUFFER_STATUS_INVALID:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID);
case ISP4FW_BUFFER_STATUS_SKIPPED:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED);
case ISP4FW_BUFFER_STATUS_EXIST:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST);
case ISP4FW_BUFFER_STATUS_DONE:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE);
case ISP4FW_BUFFER_STATUS_LACK:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK);
case ISP4FW_BUFFER_STATUS_DIRTY:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY);
case ISP4FW_BUFFER_STATUS_MAX:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX);
default:
return "Unknown Buf Done Status";
}
}
char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */)
{
switch (fmt) {
case ISP4FW_IMAGE_FORMAT_NV12:
return "NV12";
case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED:
return "YUV422INTERLEAVED";
default:
return "unknown fmt";
}
}
void isp4dbg_show_bufmeta_info(struct device *dev, char *pre,
void *in, void *orig_buf)
{
struct isp4fw_buffer_meta_info *p;
struct isp4if_img_buf_info *orig;
if (!in)
return;
if (!pre)
pre = "";
p = in;
orig = orig_buf;
dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre,
isp4dbg_get_img_fmt_str(p->image_prop.image_format),
p->enabled, isp4dbg_get_buf_done_str(p->status), p->status,
isp4dbg_get_buf_src_str(p->source));
dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n",
orig->planes[0].sys_addr, orig->planes[0].mc_addr,
orig->planes[0].len, orig->planes[1].sys_addr,
orig->planes[1].mc_addr, orig->planes[1].len,
orig->planes[2].sys_addr, orig->planes[2].mc_addr,
orig->planes[2].len);
}
char *isp4dbg_get_buf_type(u32 type)
{
/* enum isp4fw_buffer_type */
switch (type) {
case ISP4FW_BUFFER_TYPE_PREVIEW:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW);
case ISP4FW_BUFFER_TYPE_META_INFO:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO);
case ISP4FW_BUFFER_TYPE_MEM_POOL:
return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL);
default:
return "unknown type";
}
}
char *isp4dbg_get_cmd_str(u32 cmd)
{
switch (cmd) {
case ISP4FW_CMD_ID_START_STREAM:
return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM);
case ISP4FW_CMD_ID_STOP_STREAM:
return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM);
case ISP4FW_CMD_ID_SEND_BUFFER:
return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER);
case ISP4FW_CMD_ID_SET_STREAM_CONFIG:
return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG);
case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP:
return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP);
case ISP4FW_CMD_ID_ENABLE_OUT_CHAN:
return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN);
default:
return "unknown cmd";
}
}
char *isp4dbg_get_resp_str(u32 cmd)
{
switch (cmd) {
case ISP4FW_RESP_ID_CMD_DONE:
return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE);
case ISP4FW_RESP_ID_NOTI_FRAME_DONE:
return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE);
default:
return "unknown respid";
}
}
char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */)
{
switch (stream) {
case ISP4IF_STREAM_ID_GLOBAL:
return "STREAM_GLOBAL";
case ISP4IF_STREAM_ID_1:
return "STREAM1";
default:
return "unknown streamID";
}
}
char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */)
{
switch ((enum isp4fw_pipe_out_ch)ch) {
case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW:
return "prev";
default:
return "unknown channel";
}
}