summaryrefslogtreecommitdiff
path: root/drivers/media/platform/amd/isp4/isp4_debug.c
blob: 2fc00fc9a1940332eeacecfec73b746d287aa764 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// 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";
	}
}