// 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"; }}
// 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";
}
}