Browse Source

msm: camera: isp: LDAR Dump ISP information

When user space detects an error or does not receive
response for a request, Lets do a reset(LDAR) is triggered.
Before LDAR, user space sends flush command to the
kernel space.
In order to debug the cause for this situation and to dump
the information, user space sends a dump command to
kernel space before sending flush.
As a part of this command, it passes the culprit request id
and the buffer into which the information can be dumped.
Kernel space traverses across the drivers and find the culprit hw
and dumps the relevant information in the buffer.
This data is written to a file for offline processing.
This commit dumps the IFE, CSID registers, LUT tables and context
information, cmd buffers, timestamps information for
submit, apply, RUP, epoch and buffdones of the last 20
requests.

CRs-Fixed: 2612116
Change-Id: If83db59458c1e5ad778f3fa90cbc730122491c54
Signed-off-by: Gaurav Jindal <[email protected]>
Gaurav Jindal 5 years ago
parent
commit
e3f5738e43

+ 168 - 0
drivers/cam_cdm/cam_cdm_util.c

@@ -823,3 +823,171 @@ void cam_cdm_util_dump_cmd_buf(
 		}
 	} while (buf_now <= cmd_buf_end);
 }
+
+static uint32_t cam_cdm_util_dump_reg_cont_cmd_v2(
+	uint32_t                         *cmd_buf_addr,
+	struct cam_cdm_cmd_buf_dump_info *dump_info)
+{
+	int                             i;
+	long                            ret;
+	uint8_t                        *dst;
+	size_t                          remain_len;
+	uint32_t                       *temp_ptr = cmd_buf_addr;
+	uint32_t                       *addr, *start;
+	uint32_t                        min_len;
+	struct cdm_regcontinuous_cmd   *p_regcont_cmd;
+	struct cam_cdm_cmd_dump_header *hdr;
+
+	p_regcont_cmd = (struct cdm_regcontinuous_cmd *)temp_ptr;
+	temp_ptr += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT);
+	ret = cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT);
+
+	min_len = (sizeof(uint32_t) * p_regcont_cmd->count) +
+		sizeof(struct cam_cdm_cmd_dump_header) +
+		(2 * sizeof(uint32_t));
+	remain_len = dump_info->dst_max_size - dump_info->dst_offset;
+
+	if (remain_len < min_len) {
+		CAM_WARN_RATE_LIMIT(CAM_CDM,
+			"Dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		return ret;
+	}
+
+	dst = (char *)dump_info->dst_start + dump_info->dst_offset;
+	hdr = (struct cam_cdm_cmd_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_CDM_CMD_TAG_MAX_LEN, "CDM_REG_CONT:");
+	hdr->word_size = sizeof(uint32_t);
+	addr = (uint32_t *)(dst + sizeof(struct cam_cdm_cmd_dump_header));
+	start = addr;
+	*addr++ = p_regcont_cmd->offset;
+	*addr++ = p_regcont_cmd->count;
+	for (i = 0; i < p_regcont_cmd->count; i++) {
+		*addr = *temp_ptr;
+		temp_ptr++;
+		addr++;
+		ret++;
+	}
+	hdr->size = hdr->word_size * (addr - start);
+	dump_info->dst_offset += hdr->size +
+		sizeof(struct cam_cdm_cmd_dump_header);
+
+	return ret;
+}
+
+static uint32_t cam_cdm_util_dump_reg_random_cmd_v2(
+	uint32_t                         *cmd_buf_addr,
+	struct cam_cdm_cmd_buf_dump_info *dump_info)
+{
+	int                             i;
+	long                            ret;
+	uint8_t                        *dst;
+	uint32_t                       *temp_ptr = cmd_buf_addr;
+	uint32_t                       *addr, *start;
+	size_t                          remain_len;
+	uint32_t                        min_len;
+	struct cdm_regrandom_cmd       *p_regrand_cmd;
+	struct cam_cdm_cmd_dump_header *hdr;
+
+	p_regrand_cmd = (struct cdm_regrandom_cmd *)temp_ptr;
+	temp_ptr += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM);
+	ret = cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM);
+
+	min_len = (2 * sizeof(uint32_t) * p_regrand_cmd->count) +
+		sizeof(struct cam_cdm_cmd_dump_header) + sizeof(uint32_t);
+	remain_len = dump_info->dst_max_size - dump_info->dst_offset;
+
+	if (remain_len < min_len) {
+		CAM_WARN_RATE_LIMIT(CAM_CDM,
+			"Dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		return ret;
+	}
+
+	dst = (char *)dump_info->dst_start + dump_info->dst_offset;
+	hdr = (struct cam_cdm_cmd_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_CDM_CMD_TAG_MAX_LEN, "CDM_REG_RANDOM:");
+	hdr->word_size = sizeof(uint32_t);
+	addr = (uint32_t *)(dst + sizeof(struct cam_cdm_cmd_dump_header));
+	start = addr;
+	*addr++ = p_regrand_cmd->count;
+	for (i = 0; i < p_regrand_cmd->count; i++) {
+		addr[0] = temp_ptr[0] & CAM_CDM_REG_OFFSET_MASK;
+		addr[1] = temp_ptr[1];
+		temp_ptr += 2;
+		addr += 2;
+		ret += 2;
+	}
+	hdr->size = hdr->word_size * (addr - start);
+	dump_info->dst_offset += hdr->size +
+		sizeof(struct cam_cdm_cmd_dump_header);
+	return ret;
+}
+
+int cam_cdm_util_dump_cmd_bufs_v2(
+	struct cam_cdm_cmd_buf_dump_info *dump_info)
+{
+	uint32_t  cmd;
+	uint32_t *buf_now;
+	int rc = 0;
+
+	if (!dump_info || !dump_info->src_start || !dump_info->src_end ||
+		!dump_info->dst_start) {
+		CAM_INFO(CAM_CDM, "Invalid args");
+		return -EINVAL;
+	}
+
+	buf_now = dump_info->src_start;
+	do {
+		if (dump_info->dst_offset >= dump_info->dst_max_size) {
+			CAM_WARN(CAM_CDM,
+				"Dump overshoot offset %zu size %zu",
+				dump_info->dst_offset,
+				dump_info->dst_max_size);
+			return -ENOSPC;
+		}
+		cmd = *buf_now;
+		cmd = cmd >> CAM_CDM_COMMAND_OFFSET;
+
+		switch (cmd) {
+		case CAM_CDM_CMD_DMI:
+		case CAM_CDM_CMD_DMI_32:
+		case CAM_CDM_CMD_DMI_64:
+			buf_now += cdm_get_cmd_header_size(CAM_CDM_CMD_DMI);
+			break;
+		case CAM_CDM_CMD_REG_CONT:
+			buf_now += cam_cdm_util_dump_reg_cont_cmd_v2(buf_now,
+				dump_info);
+			break;
+		case CAM_CDM_CMD_REG_RANDOM:
+			buf_now += cam_cdm_util_dump_reg_random_cmd_v2(buf_now,
+				dump_info);
+			break;
+		case CAM_CDM_CMD_BUFF_INDIRECT:
+			buf_now += cdm_get_cmd_header_size(
+				CAM_CDM_CMD_BUFF_INDIRECT);
+			break;
+		case CAM_CDM_CMD_GEN_IRQ:
+			buf_now += cdm_get_cmd_header_size(
+				CAM_CDM_CMD_GEN_IRQ);
+			break;
+		case CAM_CDM_CMD_WAIT_EVENT:
+			buf_now += cdm_get_cmd_header_size(
+				CAM_CDM_CMD_WAIT_EVENT);
+			break;
+		case CAM_CDM_CMD_CHANGE_BASE:
+			buf_now += cdm_get_cmd_header_size(
+				CAM_CDM_CMD_CHANGE_BASE);
+			break;
+		case CAM_CDM_CMD_PERF_CTRL:
+			buf_now += cdm_get_cmd_header_size(
+				CAM_CDM_CMD_PERF_CTRL);
+			break;
+		default:
+			CAM_ERR(CAM_CDM, "Invalid CMD: 0x%x", cmd);
+			buf_now++;
+			break;
+		}
+	} while (buf_now <= dump_info->src_end);
+	return rc;
+}

+ 43 - 0
drivers/cam_cdm/cam_cdm_util.h

@@ -6,6 +6,9 @@
 #ifndef _CAM_CDM_UTIL_H_
 #define _CAM_CDM_UTIL_H_
 
+/* Max len for tag name for header while dumping cmd buffer*/
+#define CAM_CDM_CMD_TAG_MAX_LEN 32
+
 #include <linux/types.h>
 
 enum cam_cdm_command {
@@ -180,6 +183,34 @@ uint32_t *(*cdm_write_wait_prefetch_disable)(
 	uint32_t  mask2);
 };
 
+/**
+ * struct cam_cdm_cmd_buf_dump_info; - Camera CDM dump info
+ * @dst_offset:      dst offset
+ * @dst_max_size     max size of destination buffer
+ * @src_start:       source start address
+ * @src_end:         source end   address
+ * @dst_start:       dst start address
+ */
+struct cam_cdm_cmd_buf_dump_info {
+	size_t    dst_offset;
+	size_t    dst_max_size;
+	uint32_t *src_start;
+	uint32_t *src_end;
+	uintptr_t dst_start;
+};
+
+/**
+ * struct cam_cdm_cmd_dump_header- Camera CDM dump header
+ * @tag:       tag name for header
+ * @size:      size of data
+ * @word_size: size of each word
+ */
+struct cam_cdm_cmd_dump_header {
+	uint8_t   tag[CAM_CDM_CMD_TAG_MAX_LEN];
+	uint64_t  size;
+	uint32_t  word_size;
+};
+
 /**
  * cam_cdm_util_log_cmd_bufs()
  *
@@ -192,6 +223,18 @@ uint32_t *(*cdm_write_wait_prefetch_disable)(
 void cam_cdm_util_dump_cmd_buf(
 	uint32_t *cmd_buffer_start, uint32_t *cmd_buffer_end);
 
+/**
+ * cam_cdm_util_dump_cmd_bufs_v2()
+ *
+ * @brief:        Util function to cdm command buffers
+ *                to a buffer
+ *
+ * @dump_info:    Information about source and destination buffers
+ *
+ * return SUCCESS/FAILURE
+ */
+int cam_cdm_util_dump_cmd_bufs_v2(
+	struct cam_cdm_cmd_buf_dump_info *dump_info);
 
 
 #endif /* _CAM_CDM_UTIL_H_ */

+ 431 - 17
drivers/cam_isp/cam_isp_context.c

@@ -24,9 +24,9 @@ static const char isp_dev_name[] = "cam-isp";
 
 static struct cam_isp_ctx_debug isp_ctx_debug;
 
-#define INC_STATE_MONITOR_HEAD(head, ret) \
+#define INC_HEAD(head, max_entries, ret) \
 	div_u64_rem(atomic64_add_return(1, head),\
-	CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES, (ret))
+	max_entries, (ret))
 
 static int cam_isp_context_dump_active_request(void *data, unsigned long iova,
 	uint32_t buf_info);
@@ -34,6 +34,150 @@ static int cam_isp_context_dump_active_request(void *data, unsigned long iova,
 static int __cam_isp_ctx_start_dev_in_ready(struct cam_context *ctx,
 	struct cam_start_stop_dev_cmd *cmd);
 
+static const char *__cam_isp_evt_val_to_type(
+	uint32_t evt_id)
+{
+	switch (evt_id) {
+	case CAM_ISP_CTX_EVENT_SUBMIT:
+		return "SUBMIT";
+	case CAM_ISP_CTX_EVENT_APPLY:
+		return "APPLY";
+	case CAM_ISP_CTX_EVENT_EPOCH:
+		return "EPOCH";
+	case CAM_ISP_CTX_EVENT_RUP:
+		return "RUP";
+	case CAM_ISP_CTX_EVENT_BUFDONE:
+		return "BUFDONE";
+	default:
+		return "CAM_ISP_EVENT_INVALID";
+	}
+}
+
+static void __cam_isp_ctx_update_event_record(
+	struct cam_isp_context *ctx_isp,
+	enum cam_isp_ctx_event  event,
+	struct cam_ctx_request *req)
+{
+	int                      iterator = 0;
+	ktime_t                  cur_time;
+	struct cam_isp_ctx_req  *req_isp;
+
+	if (!ctx_isp) {
+		CAM_ERR(CAM_ISP, "Invalid Args");
+		return;
+	}
+	switch (event) {
+	case CAM_ISP_CTX_EVENT_EPOCH:
+	case CAM_ISP_CTX_EVENT_RUP:
+	case CAM_ISP_CTX_EVENT_BUFDONE:
+		break;
+	case CAM_ISP_CTX_EVENT_SUBMIT:
+	case CAM_ISP_CTX_EVENT_APPLY:
+		if (!req) {
+			CAM_ERR(CAM_ISP, "Invalid arg for event %d", event);
+			return;
+		}
+		break;
+	default:
+		break;
+	}
+
+	INC_HEAD(&ctx_isp->event_record_head[event],
+		CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES, &iterator);
+	cur_time = ktime_get();
+	if (req) {
+		req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+		ctx_isp->event_record[event][iterator].req_id =
+			req->request_id;
+		req_isp->event_timestamp[event] = cur_time;
+	} else {
+		ctx_isp->event_record[event][iterator].req_id = 0;
+	}
+	ctx_isp->event_record[event][iterator].timestamp  = cur_time;
+}
+
+static int __cam_isp_ctx_dump_event_record(
+	struct cam_isp_context *ctx_isp,
+	uintptr_t               cpu_addr,
+	size_t                  buf_len,
+	size_t                 *offset)
+{
+	int                                  i, j;
+	int                                  index;
+	size_t                               remain_len;
+	uint8_t                             *dst;
+	uint32_t                             oldest_entry, num_entries;
+	uint32_t                             min_len;
+	uint64_t                            *addr, *start;
+	uint64_t                             state_head;
+	struct timespec64                    ts;
+	struct cam_isp_context_dump_header  *hdr;
+	struct cam_isp_context_event_record *record;
+
+	if (!cpu_addr || !buf_len || !offset || !ctx_isp) {
+		CAM_ERR(CAM_ISP, "Invalid args %pK %zu %pK %pK",
+			cpu_addr, buf_len, offset, ctx_isp);
+		return -EINVAL;
+	}
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++) {
+		state_head = atomic64_read(&ctx_isp->event_record_head[i]);
+
+		if (state_head == -1) {
+			return 0;
+		} else if (state_head < CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES) {
+			num_entries = state_head + 1;
+			oldest_entry = 0;
+		} else {
+			num_entries = CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES;
+			div_u64_rem(state_head + 1,
+				CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES,
+				&oldest_entry);
+		}
+		index = oldest_entry;
+
+		if (buf_len <= *offset) {
+			CAM_WARN(CAM_ISP,
+				"Dump buffer overshoot len %zu offset %zu",
+				buf_len, *offset);
+			return -ENOSPC;
+		}
+
+		min_len = sizeof(struct cam_isp_context_dump_header) +
+			((num_entries * CAM_ISP_CTX_DUMP_EVENT_NUM_WORDS) *
+			sizeof(uint64_t));
+		remain_len = buf_len - *offset;
+
+		if (remain_len < min_len) {
+			CAM_WARN(CAM_ISP,
+				"Dump buffer exhaust remain %zu min %u",
+				remain_len, min_len);
+			return -ENOSPC;
+		}
+		dst = (uint8_t *)cpu_addr + *offset;
+		hdr = (struct cam_isp_context_dump_header *)dst;
+		scnprintf(hdr->tag,
+			CAM_ISP_CONTEXT_DUMP_TAG_MAX_LEN, "ISP_EVT_%s:",
+			__cam_isp_evt_val_to_type(i));
+		hdr->word_size = sizeof(uint64_t);
+		addr = (uint64_t *)(dst +
+			sizeof(struct cam_isp_context_dump_header));
+		start = addr;
+		for (j = 0; j <  num_entries; j++) {
+			record  = &ctx_isp->event_record[i][index];
+			ts      = ktime_to_timespec64(record->timestamp);
+			*addr++ = record->req_id;
+			*addr++ = ts.tv_sec;
+			*addr++ = ts.tv_nsec/NSEC_PER_USEC;
+			index = (index + 1) %
+				CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES;
+		}
+		hdr->size = hdr->word_size * (addr - start);
+		*offset += hdr->size +
+			sizeof(struct cam_isp_context_dump_header);
+	}
+	return 0;
+}
+
 static void __cam_isp_ctx_update_state_monitor_array(
 	struct cam_isp_context *ctx_isp,
 	enum cam_isp_state_change_trigger trigger_type,
@@ -41,7 +185,8 @@ static void __cam_isp_ctx_update_state_monitor_array(
 {
 	int iterator;
 
-	INC_STATE_MONITOR_HEAD(&ctx_isp->state_monitor_head, &iterator);
+	INC_HEAD(&ctx_isp->state_monitor_head,
+		CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES, &iterator);
 
 	ctx_isp->cam_isp_ctx_state_monitor[iterator].curr_state =
 		ctx_isp->substate_activated;
@@ -162,13 +307,19 @@ static int cam_isp_context_info_dump(void *context,
 	return 0;
 }
 
-static void cam_isp_ctx_dump_req(struct cam_isp_ctx_req *req_isp)
+static int cam_isp_ctx_dump_req(
+	struct cam_isp_ctx_req  *req_isp,
+	uintptr_t                cpu_addr,
+	size_t                   buf_len,
+	size_t                  *offset,
+	bool                     dump_to_buff)
 {
 	int i = 0, rc = 0;
 	size_t len = 0;
 	uint32_t *buf_addr;
 	uint32_t *buf_start, *buf_end;
 	size_t   remain_len = 0;
+	struct cam_cdm_cmd_buf_dump_info dump_info;
 
 	for (i = 0; i < req_isp->num_cfg; i++) {
 		rc = cam_packet_util_get_cmd_mem_addr(
@@ -182,7 +333,7 @@ static void cam_isp_ctx_dump_req(struct cam_isp_ctx_req *req_isp)
 				CAM_ERR(CAM_ISP,
 					"Invalid offset exp %u actual %u",
 					req_isp->cfg[i].offset, (uint32_t)len);
-				return;
+				return rc;
 			}
 			remain_len = len - req_isp->cfg[i].offset;
 
@@ -192,16 +343,33 @@ static void cam_isp_ctx_dump_req(struct cam_isp_ctx_req *req_isp)
 					"Invalid len exp %u remain_len %u",
 					req_isp->cfg[i].len,
 					(uint32_t)remain_len);
-				return;
+				return rc;
 			}
 
 			buf_start = (uint32_t *)((uint8_t *) buf_addr +
 				req_isp->cfg[i].offset);
 			buf_end = (uint32_t *)((uint8_t *) buf_start +
 				req_isp->cfg[i].len - 1);
-			cam_cdm_util_dump_cmd_buf(buf_start, buf_end);
+			if (dump_to_buff) {
+				if (!cpu_addr || !offset || !buf_len) {
+					CAM_ERR(CAM_ISP, "Invalid args");
+					break;
+				}
+				dump_info.src_start = buf_start;
+				dump_info.src_end =   buf_end;
+				dump_info.dst_start = cpu_addr;
+				dump_info.dst_offset = *offset;
+				dump_info.dst_max_size = buf_len;
+				rc = cam_cdm_util_dump_cmd_bufs_v2(&dump_info);
+				*offset = dump_info.dst_offset;
+				if (rc)
+					return rc;
+			} else {
+				cam_cdm_util_dump_cmd_buf(buf_start, buf_end);
+			}
 		}
 	}
+	return rc;
 }
 
 static int __cam_isp_ctx_enqueue_request_in_order(
@@ -210,6 +378,7 @@ static int __cam_isp_ctx_enqueue_request_in_order(
 	struct cam_ctx_request           *req_current;
 	struct cam_ctx_request           *req_prev;
 	struct list_head                  temp_list;
+	struct cam_isp_context           *ctx_isp;
 
 	INIT_LIST_HEAD(&temp_list);
 	spin_lock_bh(&ctx->lock);
@@ -240,6 +409,9 @@ static int __cam_isp_ctx_enqueue_request_in_order(
 			}
 		}
 	}
+	ctx_isp = (struct cam_isp_context *) ctx->ctx_priv;
+	__cam_isp_ctx_update_event_record(ctx_isp,
+		CAM_ISP_CTX_EVENT_SUBMIT, req);
 	spin_unlock_bh(&ctx->lock);
 	return 0;
 }
@@ -729,6 +901,8 @@ static int __cam_isp_ctx_handle_buf_done_for_request(
 	__cam_isp_ctx_update_state_monitor_array(ctx_isp,
 		CAM_ISP_STATE_CHANGE_TRIGGER_DONE, buf_done_req_id);
 
+	__cam_isp_ctx_update_event_record(ctx_isp,
+		CAM_ISP_CTX_EVENT_BUFDONE, req);
 	return rc;
 }
 
@@ -837,6 +1011,8 @@ static int __cam_isp_ctx_reg_upd_in_applied_state(
 		CAM_DBG(CAM_REQ,
 			"move request %lld to active list(cnt = %d), ctx %u",
 			req->request_id, ctx_isp->active_req_cnt, ctx->ctx_id);
+		__cam_isp_ctx_update_event_record(ctx_isp,
+			CAM_ISP_CTX_EVENT_RUP, req);
 	} else {
 		/* no io config, so the request is completed. */
 		list_add_tail(&req->list, &ctx->free_req_list);
@@ -944,6 +1120,8 @@ static int __cam_isp_ctx_notify_sof_in_activated_state(
 			if (req->request_id > ctx_isp->reported_req_id) {
 				request_id = req->request_id;
 				ctx_isp->reported_req_id = request_id;
+				__cam_isp_ctx_update_event_record(ctx_isp,
+					CAM_ISP_CTX_EVENT_EPOCH, req);
 				break;
 			}
 		}
@@ -1117,7 +1295,8 @@ static int __cam_isp_ctx_epoch_in_applied(struct cam_isp_context *ctx_isp,
 		/* Send SOF event as empty frame*/
 		__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 			CAM_REQ_MGR_SOF_EVENT_SUCCESS);
-
+		__cam_isp_ctx_update_event_record(ctx_isp,
+			CAM_ISP_CTX_EVENT_EPOCH, NULL);
 		goto end;
 	}
 
@@ -1167,7 +1346,8 @@ static int __cam_isp_ctx_epoch_in_applied(struct cam_isp_context *ctx_isp,
 	}
 	__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 		CAM_REQ_MGR_SOF_EVENT_ERROR);
-
+	__cam_isp_ctx_update_event_record(ctx_isp,
+		CAM_ISP_CTX_EVENT_EPOCH, req);
 	ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE;
 	CAM_DBG(CAM_ISP, "next Substate[%s]",
 		__cam_isp_ctx_substate_val_to_type(
@@ -1294,6 +1474,8 @@ static int __cam_isp_ctx_epoch_in_bubble_applied(
 		CAM_ERR(CAM_ISP, "ctx:%d No pending request.", ctx->ctx_id);
 		__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 			CAM_REQ_MGR_SOF_EVENT_SUCCESS);
+		__cam_isp_ctx_update_event_record(ctx_isp,
+			CAM_ISP_CTX_EVENT_EPOCH, NULL);
 
 		ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE;
 		goto end;
@@ -1345,13 +1527,21 @@ static int __cam_isp_ctx_epoch_in_bubble_applied(
 			ctx_isp->reported_req_id = request_id;
 			__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 			CAM_REQ_MGR_SOF_EVENT_ERROR);
-		} else
+
+			__cam_isp_ctx_update_event_record(ctx_isp,
+				CAM_ISP_CTX_EVENT_EPOCH, req);
+		} else {
 			__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 				CAM_REQ_MGR_SOF_EVENT_SUCCESS);
-	} else
+			__cam_isp_ctx_update_event_record(ctx_isp,
+				CAM_ISP_CTX_EVENT_EPOCH, NULL);
+		}
+	} else {
 		__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 			CAM_REQ_MGR_SOF_EVENT_SUCCESS);
-
+		__cam_isp_ctx_update_event_record(ctx_isp,
+			CAM_ISP_CTX_EVENT_EPOCH, NULL);
+	}
 	ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE;
 	CAM_DBG(CAM_ISP, "next Substate[%s]",
 		__cam_isp_ctx_substate_val_to_type(
@@ -1441,7 +1631,7 @@ static int __cam_isp_ctx_handle_error(struct cam_isp_context *ctx_isp,
 	req_isp = (struct cam_isp_ctx_req *) req_to_dump->req_priv;
 
 	if (error_event_data->enable_req_dump)
-		cam_isp_ctx_dump_req(req_isp);
+		rc = cam_isp_ctx_dump_req(req_isp, 0, 0, NULL, false);
 
 	__cam_isp_ctx_update_state_monitor_array(ctx_isp,
 		CAM_ISP_STATE_CHANGE_TRIGGER_ERROR, req_to_dump->request_id);
@@ -2109,6 +2299,8 @@ static int __cam_isp_ctx_apply_req_in_activated_state(
 		__cam_isp_ctx_update_state_monitor_array(ctx_isp,
 			CAM_ISP_STATE_CHANGE_TRIGGER_APPLIED,
 			req->request_id);
+		__cam_isp_ctx_update_event_record(ctx_isp,
+			CAM_ISP_CTX_EVENT_APPLY, req);
 	}
 end:
 	return rc;
@@ -2186,6 +2378,200 @@ static int __cam_isp_ctx_apply_req_in_bubble(
 	return rc;
 }
 
+static int __cam_isp_ctx_dump_req_info(
+	struct cam_context     *ctx,
+	struct cam_ctx_request *req,
+	uintptr_t               cpu_addr,
+	size_t                  buf_len,
+	size_t                 *offset)
+{
+	int                                 i, rc;
+	uint8_t                            *dst;
+	int32_t                            *addr, *start;
+	uint32_t                            min_len;
+	size_t                              remain_len;
+	struct cam_isp_ctx_req             *req_isp;
+	struct cam_isp_context             *ctx_isp;
+	struct cam_isp_context_dump_header *hdr;
+
+	if (!req || !ctx || !offset || !cpu_addr || !buf_len) {
+		CAM_ERR(CAM_ISP, "Invalid parameters %pK %pK %pK %zu",
+			req, ctx, offset, buf_len);
+		return -EINVAL;
+	}
+	req_isp = (struct cam_isp_ctx_req *)req->req_priv;
+	ctx_isp = (struct cam_isp_context *)ctx->ctx_priv;
+
+	if (buf_len <= *offset) {
+		CAM_WARN(CAM_ISP, "Dump buffer overshoot len %zu offset %zu",
+			buf_len, *offset);
+		return -ENOSPC;
+	}
+
+	remain_len = buf_len - *offset;
+	min_len = sizeof(struct cam_isp_context_dump_header) +
+		(CAM_ISP_CTX_DUMP_REQUEST_NUM_WORDS *
+		 req_isp->num_fence_map_out *
+		sizeof(int32_t));
+
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_ISP, "Dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)cpu_addr + *offset;
+	hdr = (struct cam_isp_context_dump_header *)dst;
+	hdr->word_size = sizeof(int32_t);
+	scnprintf(hdr->tag, CAM_ISP_CONTEXT_DUMP_TAG_MAX_LEN,
+		"ISP_OUT_FENCE:");
+	addr = (int32_t *)(dst + sizeof(struct cam_isp_context_dump_header));
+	start = addr;
+	for (i = 0; i < req_isp->num_fence_map_out; i++) {
+		*addr++ = req_isp->fence_map_out[i].resource_handle;
+		*addr++ = req_isp->fence_map_out[i].sync_id;
+	}
+	hdr->size = hdr->word_size * (addr - start);
+	*offset += hdr->size + sizeof(struct cam_isp_context_dump_header);
+	rc = cam_isp_ctx_dump_req(req_isp, cpu_addr, buf_len,
+		offset, true);
+	return rc;
+}
+
+static int __cam_isp_ctx_dump_in_top_state(
+	struct cam_context           *ctx,
+	struct cam_req_mgr_dump_info *dump_info)
+{
+	int                                 rc = 0;
+	bool                                dump_only_event_record = false;
+	size_t                              buf_len;
+	size_t                              remain_len;
+	uint8_t                            *dst;
+	ktime_t                             cur_time;
+	uint32_t                            min_len;
+	uint64_t                            diff;
+	uint64_t                           *addr, *start;
+	uintptr_t                           cpu_addr;
+	struct timespec64                   ts;
+	struct cam_isp_context             *ctx_isp;
+	struct cam_ctx_request             *req = NULL;
+	struct cam_isp_ctx_req             *req_isp;
+	struct cam_ctx_request             *req_temp;
+	struct cam_hw_dump_args             dump_args;
+	struct cam_isp_context_dump_header *hdr;
+
+	spin_lock_bh(&ctx->lock);
+	list_for_each_entry_safe(req, req_temp,
+		&ctx->active_req_list, list) {
+		if (req->request_id == dump_info->req_id) {
+			CAM_INFO(CAM_ISP, "isp dump active list req: %lld",
+			    dump_info->req_id);
+			goto hw_dump;
+		}
+	}
+	list_for_each_entry_safe(req, req_temp,
+		&ctx->wait_req_list, list) {
+		if (req->request_id == dump_info->req_id) {
+			CAM_INFO(CAM_ISP, "isp dump wait list req: %lld",
+			    dump_info->req_id);
+			goto hw_dump;
+		}
+	}
+	spin_unlock_bh(&ctx->lock);
+	return rc;
+hw_dump:
+	rc  = cam_mem_get_cpu_buf(dump_info->buf_handle,
+		&cpu_addr, &buf_len);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "Invalid handle %u rc %d",
+			dump_info->buf_handle, rc);
+		spin_unlock_bh(&ctx->lock);
+		return rc;
+	}
+	if (buf_len <= dump_info->offset) {
+		spin_unlock_bh(&ctx->lock);
+		CAM_WARN(CAM_ISP, "Dump buffer overshoot len %zu offset %zu",
+			buf_len, dump_info->offset);
+		return -ENOSPC;
+	}
+
+	remain_len = buf_len - dump_info->offset;
+	min_len = sizeof(struct cam_isp_context_dump_header) +
+		(CAM_ISP_CTX_DUMP_NUM_WORDS * sizeof(uint64_t));
+
+	if (remain_len < min_len) {
+		spin_unlock_bh(&ctx->lock);
+		CAM_WARN(CAM_ISP, "Dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		return -ENOSPC;
+	}
+
+	ctx_isp = (struct cam_isp_context *) ctx->ctx_priv;
+	req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+	cur_time = ktime_get();
+	diff = ktime_us_delta(
+		req_isp->event_timestamp[CAM_ISP_CTX_EVENT_APPLY],
+		cur_time);
+	if (diff < CAM_ISP_CTX_RESPONSE_TIME_THRESHOLD) {
+		CAM_INFO(CAM_ISP, "req %lld found no error",
+			req->request_id);
+		dump_only_event_record = true;
+	}
+	dst = (uint8_t *)cpu_addr + dump_info->offset;
+	hdr = (struct cam_isp_context_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_ISP_CONTEXT_DUMP_TAG_MAX_LEN,
+		"ISP_CTX_DUMP:");
+	hdr->word_size = sizeof(uint64_t);
+	addr = (uint64_t *)(dst +
+		sizeof(struct cam_isp_context_dump_header));
+	start = addr;
+	*addr++ = req->request_id;
+	ts      = ktime_to_timespec64(
+		req_isp->event_timestamp[CAM_ISP_CTX_EVENT_APPLY]);
+	*addr++ = ts.tv_sec;
+	*addr++ = ts.tv_nsec/NSEC_PER_USEC;
+	ts      = ktime_to_timespec64(cur_time);
+	*addr++ = ts.tv_sec;
+	*addr++ = ts.tv_nsec/NSEC_PER_USEC;
+	hdr->size = hdr->word_size * (addr - start);
+	dump_info->offset += hdr->size +
+		sizeof(struct cam_isp_context_dump_header);
+
+	rc = __cam_isp_ctx_dump_event_record(ctx_isp, cpu_addr,
+		buf_len, &dump_info->offset);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "Dump event fail %lld",
+			req->request_id);
+		spin_unlock_bh(&ctx->lock);
+		return rc;
+	}
+	if (dump_only_event_record) {
+		spin_unlock_bh(&ctx->lock);
+		return rc;
+	}
+	rc = __cam_isp_ctx_dump_req_info(ctx, req, cpu_addr,
+		buf_len, &dump_info->offset);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "Dump Req info fail %lld",
+			req->request_id);
+		spin_unlock_bh(&ctx->lock);
+		return rc;
+	}
+	spin_unlock_bh(&ctx->lock);
+
+	if (ctx->hw_mgr_intf->hw_dump) {
+		dump_args.offset = dump_info->offset;
+		dump_args.request_id = dump_info->req_id;
+		dump_args.buf_handle = dump_info->buf_handle;
+		dump_args.ctxt_to_hw_map = ctx_isp->hw_ctx;
+		rc = ctx->hw_mgr_intf->hw_dump(
+			ctx->hw_mgr_intf->hw_mgr_priv,
+			&dump_args);
+		dump_info->offset = dump_args.offset;
+	}
+	return rc;
+}
+
 static int __cam_isp_ctx_flush_req(struct cam_context *ctx,
 	struct list_head *req_list, struct cam_req_mgr_flush_request *flush_req)
 {
@@ -2749,7 +3135,7 @@ static int __cam_isp_ctx_rdi_only_sof_in_bubble_state(
 static int __cam_isp_ctx_rdi_only_reg_upd_in_bubble_applied_state(
 	struct cam_isp_context *ctx_isp, void *evt_data)
 {
-	struct cam_ctx_request  *req;
+	struct cam_ctx_request  *req = NULL;
 	struct cam_context      *ctx = ctx_isp->base;
 	struct cam_isp_ctx_req  *req_isp;
 	struct cam_req_mgr_trigger_notify  notify;
@@ -2809,12 +3195,15 @@ static int __cam_isp_ctx_rdi_only_reg_upd_in_bubble_applied_state(
 	CAM_DBG(CAM_ISP, "next Substate[%s]",
 		__cam_isp_ctx_substate_val_to_type(
 		ctx_isp->substate_activated));
-
+	__cam_isp_ctx_update_event_record(ctx_isp,
+		CAM_ISP_CTX_EVENT_RUP, req);
 	return 0;
 error:
 	/* Send SOF event as idle frame*/
 	__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 		CAM_REQ_MGR_SOF_EVENT_SUCCESS);
+	__cam_isp_ctx_update_event_record(ctx_isp,
+		CAM_ISP_CTX_EVENT_RUP, NULL);
 
 	/*
 	 * There is no request in the pending list, move the sub state machine
@@ -2974,6 +3363,7 @@ static int __cam_isp_ctx_release_hw_in_top_state(struct cam_context *ctx,
 	struct cam_isp_context *ctx_isp =
 		(struct cam_isp_context *) ctx->ctx_priv;
 	struct cam_req_mgr_flush_request flush_req;
+	int i;
 
 	if (ctx_isp->hw_ctx) {
 		rel_arg.ctxt_to_hw_map = ctx_isp->hw_ctx;
@@ -2994,6 +3384,8 @@ static int __cam_isp_ctx_release_hw_in_top_state(struct cam_context *ctx,
 
 	atomic64_set(&ctx_isp->state_monitor_head, -1);
 
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx_isp->event_record_head[i], -1);
 	/*
 	 * Ideally, we should never have any active request here.
 	 * But we still add some sanity check code here to help the debug
@@ -3023,6 +3415,7 @@ static int __cam_isp_ctx_release_dev_in_top_state(struct cam_context *ctx,
 	struct cam_release_dev_cmd *cmd)
 {
 	int rc = 0;
+	int i;
 	struct cam_hw_release_args       rel_arg;
 	struct cam_isp_context *ctx_isp =
 		(struct cam_isp_context *) ctx->ctx_priv;
@@ -3054,7 +3447,8 @@ static int __cam_isp_ctx_release_dev_in_top_state(struct cam_context *ctx,
 	ctx_isp->req_info.last_bufdone_req_id = 0;
 
 	atomic64_set(&ctx_isp->state_monitor_head, -1);
-
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx_isp->event_record_head[i], -1);
 	/*
 	 * Ideally, we should never have any active request here.
 	 * But we still add some sanity check code here to help the debug
@@ -3279,6 +3673,7 @@ static int __cam_isp_ctx_acquire_dev_in_available(struct cam_context *ctx,
 	struct cam_acquire_dev_cmd *cmd)
 {
 	int rc = 0;
+	int i;
 	struct cam_hw_acquire_args       param;
 	struct cam_isp_resource         *isp_res = NULL;
 	struct cam_create_dev_hdl        req_hdl_param;
@@ -3391,6 +3786,8 @@ static int __cam_isp_ctx_acquire_dev_in_available(struct cam_context *ctx,
 	ctx->ctxt_to_hw_map = param.ctxt_to_hw_map;
 
 	atomic64_set(&ctx_isp->state_monitor_head, -1);
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx_isp->event_record_head[i], -1);
 
 	kfree(isp_res);
 	isp_res = NULL;
@@ -3441,6 +3838,7 @@ static int __cam_isp_ctx_acquire_hw_v1(struct cam_context *ctx,
 	void *args)
 {
 	int rc = 0;
+	int i;
 	struct cam_acquire_hw_cmd_v1 *cmd =
 		(struct cam_acquire_hw_cmd_v1 *)args;
 	struct cam_hw_acquire_args       param;
@@ -3546,6 +3944,9 @@ static int __cam_isp_ctx_acquire_hw_v1(struct cam_context *ctx,
 
 	atomic64_set(&ctx_isp->state_monitor_head, -1);
 
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx_isp->event_record_head[i], -1);
+
 	trace_cam_context_state("ISP", ctx);
 	CAM_DBG(CAM_ISP,
 		"Acquire success on session_hdl 0x%xs ctx_type %d ctx_id %u",
@@ -3855,6 +4256,7 @@ static int __cam_isp_ctx_start_dev_in_ready(struct cam_context *ctx,
 	struct cam_start_stop_dev_cmd *cmd)
 {
 	int rc = 0;
+	int i;
 	struct cam_isp_start_args        start_isp;
 	struct cam_ctx_request          *req;
 	struct cam_isp_ctx_req          *req_isp;
@@ -3911,6 +4313,9 @@ static int __cam_isp_ctx_start_dev_in_ready(struct cam_context *ctx,
 
 	atomic64_set(&ctx_isp->state_monitor_head, -1);
 
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx_isp->event_record_head[i], -1);
+
 	/*
 	 * In case of CSID TPG we might receive SOF and RUP IRQs
 	 * before hw_mgr_intf->hw_start has returned. So move
@@ -3941,7 +4346,7 @@ static int __cam_isp_ctx_start_dev_in_ready(struct cam_context *ctx,
 		ctx->state = CAM_CTX_READY;
 		trace_cam_context_state("ISP", ctx);
 		if (rc == -ETIMEDOUT)
-			cam_isp_ctx_dump_req(req_isp);
+			rc = cam_isp_ctx_dump_req(req_isp, 0, 0, NULL, false);
 		list_del_init(&req->list);
 		list_add(&req->list, &ctx->pending_req_list);
 		goto end;
@@ -4070,6 +4475,9 @@ static int __cam_isp_ctx_stop_dev_in_activated_unlock(
 	atomic_set(&ctx_isp->process_bubble, 0);
 	atomic64_set(&ctx_isp->state_monitor_head, -1);
 
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx_isp->event_record_head[i], -1);
+
 	CAM_DBG(CAM_ISP, "Stop device success next state %d on ctx %u",
 		ctx->state, ctx->ctx_id);
 
@@ -4326,6 +4734,7 @@ static struct cam_ctx_ops
 			.unlink = __cam_isp_ctx_unlink_in_acquired,
 			.get_dev_info = __cam_isp_ctx_get_dev_info_in_acquired,
 			.flush_req = __cam_isp_ctx_flush_req_in_top_state,
+			.dump_req = __cam_isp_ctx_dump_in_top_state,
 		},
 		.irq_ops = NULL,
 		.pagefault_ops = cam_isp_context_dump_active_request,
@@ -4342,6 +4751,7 @@ static struct cam_ctx_ops
 		.crm_ops = {
 			.unlink = __cam_isp_ctx_unlink_in_ready,
 			.flush_req = __cam_isp_ctx_flush_req_in_ready,
+			.dump_req = __cam_isp_ctx_dump_in_top_state,
 		},
 		.irq_ops = NULL,
 		.pagefault_ops = cam_isp_context_dump_active_request,
@@ -4376,6 +4786,7 @@ static struct cam_ctx_ops
 			.apply_req = __cam_isp_ctx_apply_req,
 			.flush_req = __cam_isp_ctx_flush_req_in_top_state,
 			.process_evt = __cam_isp_ctx_process_evt,
+			.dump_req = __cam_isp_ctx_dump_in_top_state,
 		},
 		.irq_ops = __cam_isp_ctx_handle_irq_in_activated,
 		.pagefault_ops = cam_isp_context_dump_active_request,
@@ -4559,6 +4970,9 @@ int cam_isp_context_init(struct cam_isp_context *ctx,
 	}
 	atomic64_set(&ctx->state_monitor_head, -1);
 
+	for (i = 0; i < CAM_ISP_CTX_EVENT_MAX; i++)
+		atomic64_set(&ctx->event_record_head[i], -1);
+
 	cam_isp_context_debug_register();
 err:
 	return rc;

+ 71 - 0
drivers/cam_isp/cam_isp_context.h

@@ -33,6 +33,27 @@
  */
 #define CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES   40
 
+/*
+ * Threshold response time in us beyond which a request is not expected
+ * to be with IFE hw
+ */
+#define CAM_ISP_CTX_RESPONSE_TIME_THRESHOLD   100000
+
+/* Number of words for dumping isp context */
+#define CAM_ISP_CTX_DUMP_NUM_WORDS  5
+
+/* Number of words for dumping isp context events*/
+#define CAM_ISP_CTX_DUMP_EVENT_NUM_WORDS  3
+
+/* Number of words for dumping request info*/
+#define CAM_ISP_CTX_DUMP_REQUEST_NUM_WORDS  2
+
+/* Maximum entries in event record */
+#define CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES   20
+
+/* Maximum length of tag while dumping */
+#define CAM_ISP_CONTEXT_DUMP_TAG_MAX_LEN 32
+
 /* forward declaration */
 struct cam_isp_context;
 
@@ -55,6 +76,19 @@ enum cam_isp_ctx_activated_substate {
 	CAM_ISP_CTX_ACTIVATED_MAX,
 };
 
+/**
+ * enum cam_isp_ctx_event_type - events for a request
+ *
+ */
+enum cam_isp_ctx_event {
+	CAM_ISP_CTX_EVENT_SUBMIT,
+	CAM_ISP_CTX_EVENT_APPLY,
+	CAM_ISP_CTX_EVENT_EPOCH,
+	CAM_ISP_CTX_EVENT_RUP,
+	CAM_ISP_CTX_EVENT_BUFDONE,
+	CAM_ISP_CTX_EVENT_MAX
+};
+
 /**
  * enum cam_isp_state_change_trigger - Different types of ISP events
  *
@@ -109,6 +143,7 @@ struct cam_isp_ctx_irq_ops {
  * @bubble_report:         Flag to track if bubble report is active on
  *                         current request
  * @hw_update_data:        HW update data for this request
+ * @event_timestamp:       Timestamp for different stage of request
  * @reapply:               True if reapplying after bubble
  *
  */
@@ -125,6 +160,8 @@ struct cam_isp_ctx_req {
 	uint32_t                              num_acked;
 	int32_t                               bubble_report;
 	struct cam_isp_prepare_hw_update_data hw_update_data;
+	ktime_t                               event_timestamp
+		[CAM_ISP_CTX_EVENT_MAX];
 	bool                                  bubble_detected;
 	bool                                  reapply;
 };
@@ -160,8 +197,23 @@ struct cam_isp_context_state_monitor {
 struct cam_isp_context_req_id_info {
 	int64_t                          last_bufdone_req_id;
 };
+
 /**
  *
+ *
+ * struct cam_isp_context_event_record - Information for last 20 Events
+ *  for a request; Submit, Apply, EPOCH, RUP, Buf done.
+ *
+ * @req_id:    Last applied request id
+ * @timestamp: Timestamp for the event
+ *
+ */
+struct cam_isp_context_event_record {
+	uint64_t                         req_id;
+	ktime_t                          timestamp;
+};
+
+/**
  * struct cam_isp_context   -  ISP context object
  *
  * @base:                      Common context object pointer
@@ -187,6 +239,8 @@ struct cam_isp_context_req_id_info {
  * @state_monitor_head:        Write index to the state monitoring array
  * @req_info                   Request id information about last buf done
  * @cam_isp_ctx_state_monitor: State monitoring array
+ * @event_record_head:         Write index to the state monitoring array
+ * @event_record:              Event record array
  * @rdi_only_context:          Get context type information.
  *                             true, if context is rdi only context
  * @hw_acquired:               Indicate whether HW resources are acquired
@@ -221,6 +275,10 @@ struct cam_isp_context {
 	struct cam_isp_context_state_monitor cam_isp_ctx_state_monitor[
 		CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES];
 	struct cam_isp_context_req_id_info    req_info;
+	atomic64_t                            event_record_head[
+		CAM_ISP_CTX_EVENT_MAX];
+	struct cam_isp_context_event_record   event_record[
+		CAM_ISP_CTX_EVENT_MAX][CAM_ISP_CTX_EVENT_RECORD_MAX_ENTRIES];
 	bool                                  rdi_only_context;
 	bool                                  hw_acquired;
 	bool                                  init_received;
@@ -229,6 +287,19 @@ struct cam_isp_context {
 	uint32_t                              isp_device_type;
 };
 
+/**
+ * struct cam_isp_context_dump_header - ISP context dump header
+ * @tag:       Tag name for the header
+ * @word_size: Size of word
+ * @size:      Size of data
+ *
+ */
+struct cam_isp_context_dump_header {
+	uint8_t   tag[CAM_ISP_CONTEXT_DUMP_TAG_MAX_LEN];
+	uint64_t  size;
+	uint32_t  word_size;
+};
+
 /**
  * cam_isp_context_init()
  *

+ 160 - 5
drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c

@@ -134,7 +134,9 @@ static int cam_ife_mgr_regspace_data_cb(uint32_t reg_base_type,
 
 static int cam_ife_mgr_handle_reg_dump(struct cam_ife_hw_mgr_ctx *ctx,
 	struct cam_cmd_buf_desc *reg_dump_buf_desc, uint32_t num_reg_dump_buf,
-	uint32_t meta_type)
+	uint32_t meta_type,
+	void *soc_dump_args,
+	bool user_triggered_dump)
 {
 	int rc = 0, i;
 
@@ -157,7 +159,9 @@ static int cam_ife_mgr_handle_reg_dump(struct cam_ife_hw_mgr_ctx *ctx,
 			rc = cam_soc_util_reg_dump_to_cmd_buf(ctx,
 				&reg_dump_buf_desc[i],
 				ctx->applied_req_id,
-				cam_ife_mgr_regspace_data_cb);
+				cam_ife_mgr_regspace_data_cb,
+				soc_dump_args,
+				user_triggered_dump);
 			if (rc) {
 				CAM_ERR(CAM_ISP,
 					"Reg dump failed at idx: %d, rc: %d req_id: %llu meta type: %u",
@@ -2427,7 +2431,8 @@ void cam_ife_cam_cdm_callback(uint32_t handle, void *userdata,
 			cam_ife_mgr_handle_reg_dump(ctx,
 				hw_update_data->reg_dump_buf_desc,
 				hw_update_data->num_reg_dump_buf,
-				CAM_ISP_PACKET_META_REG_DUMP_PER_REQUEST);
+				CAM_ISP_PACKET_META_REG_DUMP_PER_REQUEST,
+				NULL, false);
 
 		CAM_DBG(CAM_ISP,
 			"Called by CDM hdl=%x, udata=%pK, status=%d, cookie=%llu ctx_index=%d",
@@ -5943,7 +5948,7 @@ static int cam_ife_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
 		ctx->last_dump_flush_req_id = ctx->applied_req_id;
 		rc = cam_ife_mgr_handle_reg_dump(ctx, ctx->reg_dump_buf_desc,
 			ctx->num_reg_dump_buf,
-			CAM_ISP_PACKET_META_REG_DUMP_ON_FLUSH);
+			CAM_ISP_PACKET_META_REG_DUMP_ON_FLUSH, NULL, false);
 		if (rc) {
 			CAM_ERR(CAM_ISP,
 				"Reg dump on flush failed req id: %llu rc: %d",
@@ -5959,7 +5964,7 @@ static int cam_ife_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
 		ctx->last_dump_err_req_id = ctx->applied_req_id;
 		rc = cam_ife_mgr_handle_reg_dump(ctx, ctx->reg_dump_buf_desc,
 			ctx->num_reg_dump_buf,
-			CAM_ISP_PACKET_META_REG_DUMP_ON_ERROR);
+			CAM_ISP_PACKET_META_REG_DUMP_ON_ERROR, NULL, false);
 		if (rc) {
 			CAM_ERR(CAM_ISP,
 				"Reg dump on error failed req id: %llu rc: %d",
@@ -5978,6 +5983,155 @@ static int cam_ife_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
 	return rc;
 }
 
+static int cam_ife_mgr_user_dump_hw(
+		struct cam_ife_hw_mgr_ctx *ife_ctx,
+		struct cam_hw_dump_args *dump_args)
+{
+	int rc = 0;
+	struct cam_hw_soc_dump_args soc_dump_args;
+
+	if (!ife_ctx || !dump_args) {
+		CAM_ERR(CAM_ISP, "Invalid parameters %pK %pK",
+			ife_ctx, dump_args);
+		rc = -EINVAL;
+		goto end;
+	}
+	soc_dump_args.buf_handle = dump_args->buf_handle;
+	soc_dump_args.request_id = dump_args->request_id;
+	soc_dump_args.offset = dump_args->offset;
+
+	rc = cam_ife_mgr_handle_reg_dump(ife_ctx,
+		ife_ctx->reg_dump_buf_desc,
+		ife_ctx->num_reg_dump_buf,
+		CAM_ISP_PACKET_META_REG_DUMP_ON_ERROR,
+		&soc_dump_args,
+		true);
+	if (rc) {
+		CAM_ERR(CAM_ISP,
+			"Dump failed req: %lld handle %u offset %u",
+			dump_args->request_id,
+			dump_args->buf_handle,
+			dump_args->offset);
+		goto end;
+	}
+	dump_args->offset = soc_dump_args.offset;
+end:
+	return rc;
+}
+
+static int cam_ife_mgr_dump(void *hw_mgr_priv, void *args)
+{
+	struct cam_isp_hw_dump_args isp_hw_dump_args;
+	struct cam_hw_dump_args *dump_args = (struct cam_hw_dump_args *)args;
+	struct cam_isp_hw_mgr_res            *hw_mgr_res;
+	struct cam_hw_intf                   *hw_intf;
+	struct cam_ife_hw_mgr_ctx *ife_ctx = (struct cam_ife_hw_mgr_ctx *)
+						dump_args->ctxt_to_hw_map;
+	int i;
+	int rc = 0;
+
+	/* for some targets, information about the IFE registers to be dumped
+	 * is already submitted with the hw manager. In this case, we
+	 * can dump just the related registers and skip going to core files.
+	 */
+	if (ife_ctx->num_reg_dump_buf) {
+		cam_ife_mgr_user_dump_hw(ife_ctx, dump_args);
+		goto end;
+	}
+
+	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
+		&isp_hw_dump_args.cpu_addr,
+		&isp_hw_dump_args.buf_len);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "Invalid handle %u rc %d",
+			dump_args->buf_handle, rc);
+		return rc;
+	}
+
+	isp_hw_dump_args.offset = dump_args->offset;
+	isp_hw_dump_args.req_id = dump_args->request_id;
+
+	list_for_each_entry(hw_mgr_res, &ife_ctx->res_list_ife_csid, list) {
+		for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+			if (!hw_mgr_res->hw_res[i])
+				continue;
+			hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+			switch (hw_mgr_res->hw_res[i]->res_id) {
+			case CAM_IFE_PIX_PATH_RES_RDI_0:
+			case CAM_IFE_PIX_PATH_RES_RDI_1:
+			case CAM_IFE_PIX_PATH_RES_RDI_2:
+			case CAM_IFE_PIX_PATH_RES_RDI_3:
+				if (ife_ctx->is_rdi_only_context &&
+					hw_intf->hw_ops.process_cmd) {
+					rc = hw_intf->hw_ops.process_cmd(
+						hw_intf->hw_priv,
+						CAM_ISP_HW_CMD_DUMP_HW,
+						&isp_hw_dump_args,
+						sizeof(struct
+						    cam_isp_hw_dump_args));
+				}
+				break;
+			case CAM_IFE_PIX_PATH_RES_IPP:
+				if (hw_intf->hw_ops.process_cmd) {
+					rc = hw_intf->hw_ops.process_cmd(
+						hw_intf->hw_priv,
+						CAM_ISP_HW_CMD_DUMP_HW,
+						&isp_hw_dump_args,
+						sizeof(struct
+						    cam_isp_hw_dump_args));
+				}
+				break;
+			default:
+				CAM_DBG(CAM_ISP, "not a valid res %d",
+				hw_mgr_res->res_id);
+				break;
+			}
+		}
+	}
+
+	list_for_each_entry(hw_mgr_res, &ife_ctx->res_list_ife_src, list) {
+		for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+			if (!hw_mgr_res->hw_res[i])
+				continue;
+			hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+			switch (hw_mgr_res->res_id) {
+			case CAM_ISP_HW_VFE_IN_RDI0:
+			case CAM_ISP_HW_VFE_IN_RDI1:
+			case CAM_ISP_HW_VFE_IN_RDI2:
+			case CAM_ISP_HW_VFE_IN_RDI3:
+				if (ife_ctx->is_rdi_only_context &&
+					hw_intf->hw_ops.process_cmd) {
+					rc = hw_intf->hw_ops.process_cmd(
+						hw_intf->hw_priv,
+						CAM_ISP_HW_CMD_DUMP_HW,
+						&isp_hw_dump_args,
+						sizeof(struct
+						    cam_isp_hw_dump_args));
+				}
+				break;
+			case CAM_ISP_HW_VFE_IN_CAMIF:
+				if (hw_intf->hw_ops.process_cmd) {
+					rc = hw_intf->hw_ops.process_cmd(
+						hw_intf->hw_priv,
+						CAM_ISP_HW_CMD_DUMP_HW,
+						&isp_hw_dump_args,
+						sizeof(struct
+						    cam_isp_hw_dump_args));
+				}
+				break;
+			default:
+				CAM_DBG(CAM_ISP, "not a valid res %d",
+					hw_mgr_res->res_id);
+				break;
+			}
+		}
+	}
+	dump_args->offset = isp_hw_dump_args.offset;
+end:
+	CAM_DBG(CAM_ISP, "offset %u", dump_args->offset);
+	return rc;
+}
+
 static int cam_ife_mgr_cmd_get_sof_timestamp(
 	struct cam_ife_hw_mgr_ctx            *ife_ctx,
 	uint64_t                             *time_stamp,
@@ -6957,6 +7111,7 @@ int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, int *iommu_hdl)
 	hw_mgr_intf->hw_config = cam_ife_mgr_config_hw;
 	hw_mgr_intf->hw_cmd = cam_ife_mgr_cmd;
 	hw_mgr_intf->hw_reset = cam_ife_mgr_reset;
+	hw_mgr_intf->hw_dump = cam_ife_mgr_dump;
 
 	if (iommu_hdl)
 		*iommu_hdl = g_ife_hw_mgr.mgr_common.img_iommu_hdl;

+ 3 - 1
drivers/cam_isp/isp_hw_mgr/cam_tfe_hw_mgr.c

@@ -140,7 +140,9 @@ static int cam_tfe_mgr_handle_reg_dump(struct cam_tfe_hw_mgr_ctx *ctx,
 			rc = cam_soc_util_reg_dump_to_cmd_buf(ctx,
 				&reg_dump_buf_desc[i],
 				ctx->applied_req_id,
-				cam_tfe_mgr_regspace_data_cb);
+				cam_tfe_mgr_regspace_data_cb,
+				NULL,
+				false);
 			if (rc) {
 				CAM_ERR(CAM_ISP,
 					"Reg dump failed at idx: %d, rc: %d req_id: %llu meta type: %u",

+ 66 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c

@@ -3808,7 +3808,70 @@ static int cam_ife_csid_set_epd_config(
 
 	csid_hw->epd_supported = epd_update->epd_supported;
 	CAM_DBG(CAM_ISP, "CSID EPD supported %d", csid_hw->epd_supported);
+	return 0;
+}
 
+static int cam_ife_csid_dump_hw(
+	struct cam_ife_csid_hw *csid_hw, void *cmd_args)
+{
+	int                             i;
+	uint8_t                        *dst;
+	uint32_t                       *addr, *start;
+	uint32_t                        min_len;
+	uint32_t                        num_reg;
+	size_t                          remain_len;
+	struct cam_isp_hw_dump_header  *hdr;
+	struct cam_isp_hw_dump_args    *dump_args =
+		(struct cam_isp_hw_dump_args *)cmd_args;
+	struct cam_hw_soc_info         *soc_info;
+
+	if (!dump_args) {
+		CAM_ERR(CAM_ISP, "Invalid args");
+		return -EINVAL;
+	}
+	if (!dump_args->cpu_addr || !dump_args->buf_len) {
+		CAM_ERR(CAM_ISP,
+			"Invalid params %pK %zu",
+			(void *)dump_args->cpu_addr,
+			dump_args->buf_len);
+		return -EINVAL;
+	}
+	soc_info = &csid_hw->hw_info->soc_info;
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_ISP,
+			"Dump offset overshoot offset %zu buf_len %zu",
+			dump_args->offset, dump_args->buf_len);
+		return -ENOSPC;
+	}
+	min_len = soc_info->reg_map[0].size +
+		sizeof(struct cam_isp_hw_dump_header) +
+		sizeof(uint32_t);
+	remain_len = dump_args->buf_len - dump_args->offset;
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_ISP, "Dump buffer exhaust remain %zu, min %u",
+			remain_len, min_len);
+		return -ENOSPC;
+	}
+	dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
+	hdr = (struct cam_isp_hw_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_ISP_HW_DUMP_TAG_MAX_LEN, "CSID_REG:");
+	addr = (uint32_t *)(dst + sizeof(struct cam_isp_hw_dump_header));
+
+	start = addr;
+	num_reg = soc_info->reg_map[0].size/4;
+	hdr->word_size = sizeof(uint32_t);
+	*addr = soc_info->index;
+	addr++;
+	for (i = 0; i < num_reg; i++) {
+		addr[0] = soc_info->mem_block[0]->start + (i*4);
+		addr[1] = cam_io_r(soc_info->reg_map[0].mem_base
+			+ (i*4));
+		addr += 2;
+	}
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset +=  hdr->size +
+		sizeof(struct cam_isp_hw_dump_header);
+	CAM_DBG(CAM_ISP, "offset %zu", dump_args->offset);
 	return 0;
 }
 
@@ -3852,6 +3915,9 @@ static int cam_ife_csid_process_cmd(void *hw_priv,
 	case CAM_IFE_CSID_SET_CONFIG:
 		rc = cam_ife_csid_set_epd_config(csid_hw, cmd_args);
 		break;
+	case CAM_ISP_HW_CMD_DUMP_HW:
+		rc = cam_ife_csid_dump_hw(csid_hw, cmd_args);
+		break;
 	default:
 		CAM_ERR(CAM_ISP, "CSID:%d unsupported cmd:%d",
 			csid_hw->hw_intf->hw_idx, cmd_type);

+ 39 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h

@@ -13,6 +13,8 @@
 #include "cam_irq_controller.h"
 #include "cam_hw_intf.h"
 
+/* Maximum length of tag while dumping */
+#define CAM_ISP_HW_DUMP_TAG_MAX_LEN 32
 /*
  * struct cam_isp_timestamp:
  *
@@ -111,6 +113,7 @@ enum cam_isp_hw_cmd_type {
 	CAM_ISP_HW_CMD_QUERY_REGSPACE_DATA,
 	CAM_ISP_HW_CMD_TPG_PHY_CLOCK_UPDATE,
 	CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP,
+	CAM_ISP_HW_CMD_DUMP_HW,
 	CAM_ISP_HW_CMD_MAX,
 };
 
@@ -253,4 +256,40 @@ struct cam_isp_hw_dual_isp_update_args {
 	struct cam_isp_resource_node    *res;
 	struct cam_isp_dual_config      *dual_cfg;
 };
+
+/*
+ * struct cam_isp_hw_dump_args:
+ *
+ * @Brief:        isp hw dump args
+ *
+ * @ req_id:         request id
+ * @ cpu_addr:       cpu address
+ * @ buf_len:        buf len
+ * @ offset:         offset of buffer
+ * @ ctxt_to_hw_map: ctx to hw map
+ */
+struct cam_isp_hw_dump_args {
+	uint64_t                req_id;
+	uintptr_t               cpu_addr;
+	size_t                  buf_len;
+	size_t                  offset;
+	void                   *ctxt_to_hw_map;
+};
+
+/**
+ * struct cam_isp_hw_dump_header - ISP context dump header
+ *
+ * @Brief:        isp hw dump header
+ *
+ * @tag:       Tag name for the header
+ * @word_size: Size of word
+ * @size:      Size of data
+ *
+ */
+struct cam_isp_hw_dump_header {
+	uint8_t   tag[CAM_ISP_HW_DUMP_TAG_MAX_LEN];
+	uint64_t  size;
+	uint32_t  word_size;
+};
+
 #endif /* _CAM_ISP_HW_H_ */

+ 1 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c

@@ -595,6 +595,7 @@ int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 	case CAM_ISP_HW_CMD_BW_CONTROL:
 	case CAM_ISP_HW_CMD_CORE_CONFIG:
 	case CAM_ISP_HW_CMD_BW_UPDATE_V2:
+	case CAM_ISP_HW_CMD_DUMP_HW:
 		rc = core_info->vfe_top->hw_ops.process_cmd(
 			core_info->vfe_top->top_priv, cmd_type, cmd_args,
 			arg_size);

+ 28 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe170.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_VFE170_H_
@@ -144,6 +144,32 @@ static struct cam_vfe_rdi_reg_data  vfe_170_rdi_2_data = {
 	.reg_update_irq_mask      = 0x80,
 };
 
+struct cam_vfe_top_dump_data vfe170_dump_data = {
+	.num_reg_dump_entries  =  2,
+	.num_lut_dump_entries  =  1,
+	.dmi_cfg               =  0xc24,
+	.dmi_addr              =  0xc28,
+	.dmi_data_path_hi      =  0xc2C,
+	.dmi_data_path_lo      =  0xc30,
+	.reg_entry = {
+		{
+			.reg_dump_start = 0x0,
+			.reg_dump_end   = 0x1164,
+		},
+		{
+			.reg_dump_start = 0x2000,
+			.reg_dump_end   = 0x397C,
+		},
+	},
+	.lut_entry = {
+		{
+			.lut_word_size = 64,
+			.lut_bank_sel  = 0x40,
+			.lut_addr_size = 180,
+		},
+	},
+};
+
 static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = {
 	.common_reg = &vfe170_top_common_reg,
 	.camif_hw_info = {
@@ -173,6 +199,7 @@ static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = {
 		CAM_VFE_RDI_VER_1_0,
 		CAM_VFE_RDI_VER_1_0,
 	},
+	.dump_data = &vfe170_dump_data,
 };
 
 static struct cam_irq_register_set vfe170_bus_irq_reg[3] = {

+ 28 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe175.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_VFE175_H_
@@ -178,6 +178,32 @@ static struct cam_vfe_rdi_reg_data  vfe_175_rdi_2_data = {
 	.reg_update_irq_mask      = 0x80,
 };
 
+struct cam_vfe_top_dump_data vfe175_dump_data = {
+	.num_reg_dump_entries  =  2,
+	.num_lut_dump_entries  =  1,
+	.dmi_cfg               =  0xc24,
+	.dmi_addr              =  0xc28,
+	.dmi_data_path_hi      =  0xc2C,
+	.dmi_data_path_lo      =  0xc30,
+	.reg_entry = {
+		{
+			.reg_dump_start = 0x0,
+			.reg_dump_end   = 0x1164,
+		},
+		{
+			.reg_dump_start = 0x2000,
+			.reg_dump_end   = 0x397C,
+		},
+	},
+	.lut_entry = {
+		{
+			.lut_word_size = 64,
+			.lut_bank_sel  = 0x40,
+			.lut_addr_size = 180,
+		},
+	},
+};
+
 static struct cam_vfe_top_ver2_hw_info vfe175_top_hw_info = {
 	.common_reg = &vfe175_top_common_reg,
 	.camif_hw_info = {
@@ -209,6 +235,7 @@ static struct cam_vfe_top_ver2_hw_info vfe175_top_hw_info = {
 		CAM_VFE_RDI_VER_1_0,
 		CAM_VFE_CAMIF_LITE_VER_2_0,
 	},
+	.dump_data = &vfe175_dump_data,
 };
 
 static struct cam_irq_register_set vfe175_bus_irq_reg[3] = {

+ 28 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe175_130.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_VFE175_130_H_
@@ -226,6 +226,32 @@ static struct cam_vfe_rdi_reg_data  vfe_175_130_rdi_2_data = {
 	.reg_update_irq_mask      = 0x80,
 };
 
+struct cam_vfe_top_dump_data vfe175_130_dump_data = {
+	.num_reg_dump_entries  =  2,
+	.num_lut_dump_entries  =  1,
+	.dmi_cfg               =  0xc24,
+	.dmi_addr              =  0xc28,
+	.dmi_data_path_hi      =  0xc2C,
+	.dmi_data_path_lo      =  0xc30,
+	.reg_entry = {
+		{
+			.reg_dump_start = 0x0,
+			.reg_dump_end   = 0x1164,
+		},
+		{
+			.reg_dump_start = 0x2000,
+			.reg_dump_end   = 0x397C,
+		},
+	},
+	.lut_entry = {
+		{
+			.lut_word_size = 64,
+			.lut_bank_sel  = 0x40,
+			.lut_addr_size = 180,
+		},
+	},
+};
+
 static struct cam_vfe_top_ver2_hw_info vfe175_130_top_hw_info = {
 	.common_reg = &vfe175_130_top_common_reg,
 	.camif_hw_info = {
@@ -263,6 +289,7 @@ static struct cam_vfe_top_ver2_hw_info vfe175_130_top_hw_info = {
 		CAM_VFE_CAMIF_LITE_VER_2_0,
 		CAM_VFE_IN_RD_VER_1_0,
 	},
+	.dump_data = &vfe175_130_dump_data,
 };
 
 static struct cam_irq_register_set vfe175_130_bus_rd_irq_reg[1] = {

+ 29 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_common.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_VFE_TOP_COMMON_H_
@@ -13,6 +13,10 @@
 #include "cam_vfe_hw_intf.h"
 #include "cam_vfe_soc.h"
 
+#define CAM_VFE_TOP_MAX_REG_DUMP_ENTRIES 70
+
+#define CAM_VFE_TOP_MAX_LUT_DUMP_ENTRIES 6
+
 struct cam_vfe_top_priv_common {
 	struct cam_isp_resource_node    mux_rsrc[CAM_VFE_TOP_MUX_MAX];
 	uint32_t                        num_mux;
@@ -26,6 +30,30 @@ struct cam_vfe_top_priv_common {
 	enum cam_vfe_bw_control_action  axi_vote_control[CAM_VFE_TOP_MUX_MAX];
 };
 
+struct cam_vfe_top_reg_dump_entry {
+	uint32_t reg_dump_start;
+	uint32_t reg_dump_end;
+};
+
+struct cam_vfe_top_lut_dump_entry {
+	uint32_t lut_word_size;
+	uint32_t lut_bank_sel;
+	uint32_t lut_addr_size;
+};
+
+struct cam_vfe_top_dump_data {
+	uint32_t num_reg_dump_entries;
+	uint32_t num_lut_dump_entries;
+	uint32_t dmi_cfg;
+	uint32_t dmi_addr;
+	uint32_t dmi_data_path_hi;
+	uint32_t dmi_data_path_lo;
+	struct cam_vfe_top_reg_dump_entry
+		reg_entry[CAM_VFE_TOP_MAX_REG_DUMP_ENTRIES];
+	struct cam_vfe_top_lut_dump_entry
+		lut_entry[CAM_VFE_TOP_MAX_LUT_DUMP_ENTRIES];
+};
+
 int cam_vfe_top_set_axi_bw_vote(struct cam_vfe_soc_private *soc_private,
 	struct cam_vfe_top_priv_common *top_common, bool start_stop);
 

+ 141 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/slab.h>
@@ -19,6 +19,7 @@ struct cam_vfe_top_ver2_common_data {
 	struct cam_hw_soc_info                     *soc_info;
 	struct cam_hw_intf                         *hw_intf;
 	struct cam_vfe_top_ver2_reg_offset_common  *common_reg;
+	struct cam_vfe_top_dump_data               *dump_data;
 };
 
 struct cam_vfe_top_ver2_priv {
@@ -184,6 +185,140 @@ int cam_vfe_top_get_hw_caps(void *device_priv,
 	return -EPERM;
 }
 
+static int cam_vfe_hw_dump(
+	struct cam_vfe_top_ver2_priv *top_priv,
+	void *cmd_args,
+	uint32_t arg_size)
+{
+	int                                i, j;
+	uint8_t                           *dst;
+	uint32_t                           reg_start_offset;
+	uint32_t                           reg_dump_size = 0;
+	uint32_t                           lut_dump_size = 0;
+	uint32_t                           val;
+	uint32_t                           num_reg;
+	void __iomem                      *reg_base;
+	uint32_t                          *addr, *start;
+	size_t                             remain_len;
+	uint32_t                           min_len;
+	struct cam_hw_soc_info            *soc_info;
+	struct cam_vfe_top_dump_data      *dump_data;
+	struct cam_isp_hw_dump_header     *hdr;
+	struct cam_isp_hw_dump_args       *dump_args =
+		(struct cam_isp_hw_dump_args *)cmd_args;
+
+	if (!dump_args) {
+		CAM_ERR(CAM_ISP, "Invalid args");
+		return -EINVAL;
+	}
+	if (!dump_args->cpu_addr || !dump_args->buf_len) {
+		CAM_ERR(CAM_ISP,
+			"Invalid params %pK %zu",
+			(void *)dump_args->cpu_addr,
+			dump_args->buf_len);
+		return -EINVAL;
+	}
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_ISP,
+			"Dump offset overshoot offset %zu buf_len %zu",
+			dump_args->offset, dump_args->buf_len);
+		return -ENOSPC;
+	}
+	dump_data = top_priv->common_data.dump_data;
+	soc_info = top_priv->common_data.soc_info;
+
+	/*Dump registers */
+	for (i = 0; i < dump_data->num_reg_dump_entries; i++)
+		reg_dump_size += (dump_data->reg_entry[i].reg_dump_end -
+			dump_data->reg_entry[i].reg_dump_start);
+	/*
+	 * We dump the offset as well, so the total size dumped becomes
+	 * multiplied by 2
+	 */
+	reg_dump_size *= 2;
+	for (i = 0; i < dump_data->num_lut_dump_entries; i++)
+		lut_dump_size += ((dump_data->lut_entry[i].lut_addr_size) *
+			(dump_data->lut_entry[i].lut_word_size/8));
+
+	/*Minimum len comprises of:
+	 * soc_index
+	 * lut_dump_size + reg_dump_size + sizeof dump_header +
+	 * (num_lut_dump_entries--> represents number of banks)
+	 */
+	min_len = sizeof(uint32_t) + lut_dump_size + reg_dump_size +
+		sizeof(struct cam_isp_hw_dump_header) +
+		(dump_data->num_lut_dump_entries * sizeof(uint32_t));
+	remain_len = dump_args->buf_len - dump_args->offset;
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_ISP, "Dump buffer exhaust remain %zu, min %u",
+			remain_len, min_len);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
+	hdr = (struct cam_isp_hw_dump_header *)dst;
+	hdr->word_size = sizeof(uint32_t);
+	scnprintf(hdr->tag, CAM_ISP_HW_DUMP_TAG_MAX_LEN, "VFE_REG:");
+	addr = (uint32_t *)(dst + sizeof(struct cam_isp_hw_dump_header));
+	start = addr;
+	*addr++ = soc_info->index;
+	for (i = 0; i < dump_data->num_reg_dump_entries; i++) {
+		num_reg  = (dump_data->reg_entry[i].reg_dump_end -
+			dump_data->reg_entry[i].reg_dump_start)/4;
+		reg_start_offset = dump_data->reg_entry[i].reg_dump_start;
+		reg_base = soc_info->reg_map[0].mem_base + reg_start_offset;
+		for (j = 0; j < num_reg; j++) {
+			addr[0] = soc_info->mem_block[0]->start +
+				reg_start_offset + (j*4);
+			addr[1] = cam_io_r(reg_base + (j*4));
+			addr += 2;
+		}
+	}
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset +=  hdr->size +
+		sizeof(struct cam_isp_hw_dump_header);
+
+	/*dump LUT*/
+	for (i = 0; i < dump_data->num_lut_dump_entries; i++) {
+
+		dst = (char *)dump_args->cpu_addr + dump_args->offset;
+		hdr = (struct cam_isp_hw_dump_header *)dst;
+		scnprintf(hdr->tag, CAM_ISP_HW_DUMP_TAG_MAX_LEN, "LUT_REG:");
+		hdr->word_size = dump_data->lut_entry[i].lut_word_size/8;
+		addr = (uint32_t *)(dst +
+			sizeof(struct cam_isp_hw_dump_header));
+		start = addr;
+		*addr++ = dump_data->lut_entry[i].lut_bank_sel;
+		val = 0x100 |  dump_data->lut_entry[i].lut_bank_sel;
+		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
+			dump_data->dmi_cfg);
+		cam_io_w_mb(0, soc_info->reg_map[0].mem_base +
+			dump_data->dmi_addr);
+		for (j = 0; j < dump_data->lut_entry[i].lut_addr_size;
+			j++) {
+			if (dump_data->lut_entry[i].lut_word_size == 64) {
+				addr[0] = cam_io_r(
+					soc_info->reg_map[0].mem_base +
+					dump_data->dmi_data_path_lo);
+				addr[1] = cam_io_r(
+					soc_info->reg_map[0].mem_base +
+					dump_data->dmi_data_path_hi);
+				addr += 2;
+			} else {
+				*addr = cam_io_r(
+					soc_info->reg_map[0].mem_base +
+					dump_data->dmi_data_path_lo);
+				addr++;
+			}
+		}
+		hdr->size = hdr->word_size * (addr - start);
+		dump_args->offset +=  hdr->size +
+			sizeof(struct cam_isp_hw_dump_header);
+	}
+	CAM_DBG(CAM_ISP, "offset %zu", dump_args->offset);
+	return 0;
+}
+
 int cam_vfe_top_init_hw(void *device_priv,
 	void *init_hw_args, uint32_t arg_size)
 {
@@ -505,6 +640,10 @@ int cam_vfe_top_process_cmd(void *device_priv, uint32_t cmd_type,
 		rc = cam_vfe_top_bw_control(soc_private, &top_priv->top_common,
 			cmd_args, arg_size);
 		break;
+	case CAM_ISP_HW_CMD_DUMP_HW:
+		rc = cam_vfe_hw_dump(top_priv,
+			cmd_args, arg_size);
+		break;
 	default:
 		rc = -EINVAL;
 		CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type);
@@ -627,6 +766,7 @@ int cam_vfe_top_ver2_init(
 	top_priv->common_data.hw_intf      = hw_intf;
 	top_priv->top_common.hw_idx        = hw_intf->hw_idx;
 	top_priv->common_data.common_reg   = ver2_hw_info->common_reg;
+	top_priv->common_data.dump_data    = ver2_hw_info->dump_data;
 
 	return rc;
 

+ 2 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_VFE_TOP_VER2_H_
@@ -49,6 +49,7 @@ struct cam_vfe_top_ver2_hw_info {
 	struct cam_vfe_camif_lite_ver2_hw_info      camif_lite_hw_info;
 	struct cam_vfe_rdi_ver2_hw_info             rdi_hw_info;
 	struct cam_vfe_fe_ver1_hw_info              fe_hw_info;
+	struct cam_vfe_top_dump_data               *dump_data;
 	uint32_t                                    num_mux;
 	uint32_t mux_type[CAM_VFE_TOP_MUX_MAX];
 };

+ 285 - 7
drivers/cam_utils/cam_soc_util.c

@@ -2002,9 +2002,268 @@ end:
 	return rc;
 }
 
+static int cam_soc_util_dump_dmi_reg_range_user_buf(
+	struct cam_hw_soc_info *soc_info,
+	struct cam_dmi_read_desc *dmi_read, uint32_t base_idx,
+	struct cam_hw_soc_dump_args *dump_args)
+{
+	int                            i;
+	int                            rc;
+	size_t                         buf_len = 0;
+	uint8_t                       *dst;
+	size_t                         remain_len;
+	uint32_t                       min_len;
+	uint32_t                      *waddr, *start;
+	uintptr_t                      cpu_addr;
+	struct cam_hw_soc_dump_header *hdr;
+
+	if (!soc_info || !dump_args || !dmi_read) {
+		CAM_ERR(CAM_UTIL,
+			"Invalid input args soc_info: %pK, dump_args: %pK",
+			soc_info, dump_args);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (dmi_read->num_pre_writes > CAM_REG_DUMP_DMI_CONFIG_MAX ||
+		dmi_read->num_post_writes > CAM_REG_DUMP_DMI_CONFIG_MAX) {
+		CAM_ERR(CAM_UTIL,
+			"Invalid number of requested writes, pre: %d post: %d",
+			dmi_read->num_pre_writes, dmi_read->num_post_writes);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	rc = cam_mem_get_cpu_buf(dump_args->buf_handle, &cpu_addr, &buf_len);
+	if (rc) {
+		CAM_ERR(CAM_UTIL, "Invalid handle %u rc %d",
+			dump_args->buf_handle, rc);
+		goto end;
+	}
+
+	if (buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_UTIL, "Dump offset overshoot offset %zu len %zu",
+			dump_args->offset, buf_len);
+		rc = -ENOSPC;
+		goto end;
+	}
+	remain_len = buf_len - dump_args->offset;
+	min_len = (dmi_read->num_pre_writes * 2 * sizeof(uint32_t)) +
+		(dmi_read->dmi_data_read.num_values * 2 * sizeof(uint32_t)) +
+		sizeof(uint32_t);
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_UTIL,
+			"Dump Buffer exhaust read %d write %d remain %zu min %u",
+			dmi_read->dmi_data_read.num_values,
+			dmi_read->num_pre_writes, remain_len,
+			min_len);
+		rc = -ENOSPC;
+		goto end;
+	}
+
+	dst = (uint8_t *)cpu_addr + dump_args->offset;
+	hdr = (struct cam_hw_soc_dump_header *)dst;
+	memset(hdr, 0, sizeof(struct cam_hw_soc_dump_header));
+	scnprintf(hdr->tag, CAM_SOC_HW_DUMP_TAG_MAX_LEN,
+		"DMI_DUMP:");
+	waddr = (uint32_t *)(dst + sizeof(struct cam_hw_soc_dump_header));
+	start = waddr;
+	hdr->word_size = sizeof(uint32_t);
+	*waddr = soc_info->index;
+	waddr++;
+	for (i = 0; i < dmi_read->num_pre_writes; i++) {
+		if (dmi_read->pre_read_config[i].offset >
+			(uint32_t)soc_info->reg_map[base_idx].size) {
+			CAM_ERR(CAM_UTIL,
+				"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
+				dmi_read->pre_read_config[i].offset,
+				(uint32_t)soc_info->reg_map[base_idx].size);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		cam_soc_util_w_mb(soc_info, base_idx,
+			dmi_read->pre_read_config[i].offset,
+			dmi_read->pre_read_config[i].value);
+		*waddr++ = dmi_read->pre_read_config[i].offset;
+		*waddr++ = dmi_read->pre_read_config[i].value;
+	}
+
+	if (dmi_read->dmi_data_read.offset >
+		(uint32_t)soc_info->reg_map[base_idx].size) {
+		CAM_ERR(CAM_UTIL,
+			"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
+			dmi_read->dmi_data_read.offset,
+			(uint32_t)soc_info->reg_map[base_idx].size);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	for (i = 0; i < dmi_read->dmi_data_read.num_values; i++) {
+		*waddr++ = dmi_read->dmi_data_read.offset;
+		*waddr++ = cam_soc_util_r_mb(soc_info, base_idx,
+			dmi_read->dmi_data_read.offset);
+	}
+
+	for (i = 0; i < dmi_read->num_post_writes; i++) {
+		if (dmi_read->post_read_config[i].offset >
+			(uint32_t)soc_info->reg_map[base_idx].size) {
+			CAM_ERR(CAM_UTIL,
+				"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
+				dmi_read->post_read_config[i].offset,
+				(uint32_t)soc_info->reg_map[base_idx].size);
+			rc = -EINVAL;
+			goto end;
+		}
+		cam_soc_util_w_mb(soc_info, base_idx,
+			dmi_read->post_read_config[i].offset,
+			dmi_read->post_read_config[i].value);
+	}
+	hdr->size = (waddr - start) * hdr->word_size;
+	dump_args->offset +=  hdr->size +
+		sizeof(struct cam_hw_soc_dump_header);
+
+end:
+	return rc;
+}
+
+static int cam_soc_util_dump_cont_reg_range_user_buf(
+	struct cam_hw_soc_info *soc_info,
+	struct cam_reg_range_read_desc *reg_read,
+	uint32_t base_idx,
+	struct cam_hw_soc_dump_args *dump_args)
+{
+	int                            i;
+	int                            rc = 0;
+	size_t                         buf_len;
+	uint8_t                       *dst;
+	size_t                         remain_len;
+	uint32_t                       min_len;
+	uint32_t                      *waddr, *start;
+	uintptr_t                      cpu_addr;
+	struct cam_hw_soc_dump_header  *hdr;
+
+	if (!soc_info || !dump_args || !reg_read) {
+		CAM_ERR(CAM_UTIL,
+			"Invalid input args soc_info: %pK, dump_out_buffer: %pK reg_read: %pK",
+			soc_info, dump_args, reg_read);
+		rc = -EINVAL;
+		goto end;
+	}
+	rc = cam_mem_get_cpu_buf(dump_args->buf_handle, &cpu_addr, &buf_len);
+	if (rc) {
+		CAM_ERR(CAM_UTIL, "Invalid handle %u rc %d",
+			dump_args->buf_handle, rc);
+		goto end;
+	}
+	if (buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_UTIL, "Dump offset overshoot %zu %zu",
+			dump_args->offset, buf_len);
+		rc = -ENOSPC;
+		goto end;
+	}
+	remain_len = buf_len - dump_args->offset;
+	min_len = (reg_read->num_values * 2 * sizeof(uint32_t)) +
+		sizeof(struct cam_hw_soc_dump_header) + sizeof(uint32_t);
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_UTIL,
+			"Dump Buffer exhaust read_values %d remain %zu min %u",
+			reg_read->num_values,
+			remain_len,
+			min_len);
+		rc = -ENOSPC;
+		goto end;
+	}
+	dst = (uint8_t *)cpu_addr + dump_args->offset;
+	hdr = (struct cam_hw_soc_dump_header *)dst;
+	memset(hdr, 0, sizeof(struct cam_hw_soc_dump_header));
+	scnprintf(hdr->tag, CAM_SOC_HW_DUMP_TAG_MAX_LEN, "%s_REG:",
+		soc_info->dev_name);
+	waddr = (uint32_t *)(dst + sizeof(struct cam_hw_soc_dump_header));
+	start = waddr;
+	hdr->word_size = sizeof(uint32_t);
+	*waddr = soc_info->index;
+	waddr++;
+	for (i = 0; i < reg_read->num_values; i++) {
+		if ((reg_read->offset + (i * sizeof(uint32_t))) >
+			(uint32_t)soc_info->reg_map[base_idx].size) {
+			CAM_ERR(CAM_UTIL,
+				"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
+				(reg_read->offset + (i * sizeof(uint32_t))),
+				(uint32_t)soc_info->reg_map[base_idx].size);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		waddr[0] = reg_read->offset + (i * sizeof(uint32_t));
+		waddr[1] = cam_soc_util_r(soc_info, base_idx,
+			(reg_read->offset + (i * sizeof(uint32_t))));
+		waddr += 2;
+	}
+	hdr->size = (waddr - start) * hdr->word_size;
+	dump_args->offset +=  hdr->size +
+		sizeof(struct cam_hw_soc_dump_header);
+end:
+	return rc;
+}
+
+static int cam_soc_util_user_reg_dump(
+	struct cam_reg_dump_desc *reg_dump_desc,
+	struct cam_hw_soc_dump_args *dump_args,
+	struct cam_hw_soc_info *soc_info,
+	uint32_t reg_base_idx)
+{
+	int rc = 0;
+	int i;
+	struct cam_reg_read_info  *reg_read_info = NULL;
+
+	if (!dump_args || !reg_dump_desc || !soc_info) {
+		CAM_ERR(CAM_UTIL,
+			"Invalid input parameters %pK %pK %pK",
+			dump_args, reg_dump_desc, soc_info);
+		return -EINVAL;
+	}
+	for (i = 0; i < reg_dump_desc->num_read_range; i++) {
+
+		reg_read_info = &reg_dump_desc->read_range[i];
+		if (reg_read_info->type ==
+				CAM_REG_DUMP_READ_TYPE_CONT_RANGE) {
+			rc = cam_soc_util_dump_cont_reg_range_user_buf(
+				soc_info,
+				&reg_read_info->reg_read,
+				reg_base_idx,
+				dump_args);
+		} else if (reg_read_info->type ==
+				CAM_REG_DUMP_READ_TYPE_DMI) {
+			rc = cam_soc_util_dump_dmi_reg_range_user_buf(
+				soc_info,
+				&reg_read_info->dmi_read,
+				reg_base_idx,
+				dump_args);
+		} else {
+			CAM_ERR(CAM_UTIL,
+					"Invalid Reg dump read type: %d",
+					reg_read_info->type);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		if (rc) {
+			CAM_ERR(CAM_UTIL,
+				"Reg range read failed rc: %d reg_base_idx: %d",
+				rc, reg_base_idx);
+			goto end;
+		}
+	}
+end:
+	return rc;
+}
+
 int cam_soc_util_reg_dump_to_cmd_buf(void *ctx,
 	struct cam_cmd_buf_desc *cmd_desc, uint64_t req_id,
-	cam_soc_util_regspace_data_cb reg_data_cb)
+	cam_soc_util_regspace_data_cb reg_data_cb,
+	struct cam_hw_soc_dump_args *soc_dump_args,
+	bool user_triggered_dump)
 {
 	int                               rc = 0, i, j;
 	uintptr_t                         cpu_addr = 0;
@@ -2148,12 +2407,6 @@ int cam_soc_util_reg_dump_to_cmd_buf(void *ctx,
 			goto end;
 		}
 
-		dump_out_buf = (struct cam_reg_dump_out_buffer *)
-			(cmd_buf_start +
-			(uintptr_t)reg_dump_desc->dump_buffer_offset);
-		dump_out_buf->req_id = req_id;
-		dump_out_buf->bytes_written = 0;
-
 		reg_base_type = reg_dump_desc->reg_base_type;
 		if (reg_base_type == 0 || reg_base_type >
 			CAM_REG_DUMP_BASE_TYPE_CAMNOC) {
@@ -2185,6 +2438,31 @@ int cam_soc_util_reg_dump_to_cmd_buf(void *ctx,
 			"Reg data callback success req_id: %llu base_type: %d base_idx: %d num_read_range: %d",
 			req_id, reg_base_type, reg_base_idx,
 			reg_dump_desc->num_read_range);
+
+		/* If the dump request is triggered by user space
+		 * buffer will be different from the buffer which is received
+		 * in init packet. In this case, dump the data to the
+		 * user provided buffer and exit.
+		 */
+		if (user_triggered_dump) {
+			rc = cam_soc_util_user_reg_dump(reg_dump_desc,
+				soc_dump_args, soc_info, reg_base_idx);
+			CAM_INFO(CAM_UTIL,
+				"%s reg_base_idx %d dumped offset %u",
+				soc_info->dev_name, reg_base_idx,
+				soc_dump_args->offset);
+			goto end;
+		}
+
+		/* Below code is executed when data is dumped to the
+		 * out buffer received in init packet
+		 */
+		dump_out_buf = (struct cam_reg_dump_out_buffer *)
+			(cmd_buf_start +
+			(uintptr_t)reg_dump_desc->dump_buffer_offset);
+		dump_out_buf->req_id = req_id;
+		dump_out_buf->bytes_written = 0;
+
 		for (j = 0; j < reg_dump_desc->num_read_range; j++) {
 			CAM_DBG(CAM_UTIL,
 				"Number of bytes written to cmd buffer: %u req_id: %llu",

+ 46 - 11
drivers/cam_utils/cam_soc_util.h

@@ -41,6 +41,9 @@
 #define DDR_TYPE_LPDDR5        8
 #define DDR_TYPE_LPDDR5X       9
 
+/* Maximum length of tag while dumping */
+#define CAM_SOC_HW_DUMP_TAG_MAX_LEN 32
+
 /**
  * enum cam_vote_level - Enum for voting level
  *
@@ -218,6 +221,34 @@ struct cam_hw_soc_info {
 	void                           *soc_private;
 };
 
+/**
+ * struct cam_hw_soc_dump_header - SOC dump header
+ *
+ * @Brief:        soc hw dump header
+ *
+ * @tag:          Tag name for the header
+ * @word_size:    Size of each word
+ * @size:         Total size of dumped data
+ */
+struct cam_hw_soc_dump_header {
+	uint8_t   tag[CAM_SOC_HW_DUMP_TAG_MAX_LEN];
+	uint64_t  size;
+	uint32_t  word_size;
+};
+
+/**
+ * struct cam_hw_soc_dump_args:   SOC Dump args
+ *
+ * @request_id:          Issue request id
+ * @offset:              Buffer offset, updated as the informaton is dumped
+ * @buf_handle:          Buffer handle of the out buffer
+ */
+struct cam_hw_soc_dump_args {
+	uint64_t             request_id;
+	size_t               offset;
+	uint32_t             buf_handle;
+};
+
 /*
  * CAM_SOC_GET_REG_MAP_START
  *
@@ -636,19 +667,23 @@ typedef int (*cam_soc_util_regspace_data_cb)(uint32_t reg_base_type,
 /**
  * cam_soc_util_reg_dump_to_cmd_buf()
  *
- * @brief:              Camera SOC util for dumping sets of register ranges to
- *                      to command buffer
- *
- * @ctx:                Context info from specific hardware manager
- * @cmd_desc:           Command buffer descriptor
- * @req_id:             Last applied req id for which reg dump is required
- * @reg_data_cb:        Callback function to get reg space info based on type
- *                      in command buffer
- *
- * @return:             Success or Failure
+ * @brief:                 Camera SOC util for dumping sets of register ranges
+ *                         command buffer
+ *
+ * @ctx:                   Context info from specific hardware manager
+ * @cmd_desc:              Command buffer descriptor
+ * @req_id:                Last applied req id for which reg dump is required
+ * @reg_data_cb:           Callback function to get reg space info based on type
+ *                         in command buffer
+ * @soc_dump_args:         Dump buffer args to dump the soc information.
+ * @user_triggered_dump:   Flag to indicate if the dump request is issued by
+ *                         user.
+ * @return:                Success or Failure
  */
 int cam_soc_util_reg_dump_to_cmd_buf(void *ctx,
 	struct cam_cmd_buf_desc *cmd_desc, uint64_t req_id,
-	cam_soc_util_regspace_data_cb reg_data_cb);
+	cam_soc_util_regspace_data_cb reg_data_cb,
+	struct cam_hw_soc_dump_args *soc_dump_args,
+	bool user_triggered_dump);
 
 #endif /* _CAM_SOC_UTIL_H_ */