Browse Source

Merge "msm: camera: common: LDAR dump NRT devices information" into camera-kernel.lnx.4.0

Camera Software Integration 5 years ago
parent
commit
23372b610f

+ 15 - 1
drivers/cam_fd/cam_fd_context.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/module.h>
@@ -117,6 +117,19 @@ static int __cam_fd_ctx_release_dev_in_activated(struct cam_context *ctx,
 	return rc;
 }
 
+static int __cam_fd_ctx_dump_dev_in_activated(
+	struct cam_context *ctx,
+	struct cam_dump_req_cmd *cmd)
+{
+	int rc;
+
+	rc = cam_context_dump_dev_to_hw(ctx, cmd);
+	if (rc)
+		CAM_ERR(CAM_FD, "Failed to dump device, rc=%d", rc);
+
+	return rc;
+}
+
 static int __cam_fd_ctx_flush_dev_in_activated(struct cam_context *ctx,
 	struct cam_flush_dev_cmd *cmd)
 {
@@ -198,6 +211,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_fd_ctx_release_dev_in_activated,
 			.config_dev = __cam_fd_ctx_config_dev_in_activated,
 			.flush_dev = __cam_fd_ctx_flush_dev_in_activated,
+			.dump_dev = __cam_fd_ctx_dump_dev_in_activated,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_fd_ctx_handle_irq_in_activated,

+ 133 - 0
drivers/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c

@@ -883,6 +883,7 @@ static int cam_fd_mgr_util_submit_frame(void *priv, void *data)
 	hw_device->cur_hw_ctx = hw_ctx;
 	hw_device->req_id = frame_req->request_id;
 	mutex_unlock(&hw_device->lock);
+	frame_req->submit_timestamp = ktime_get();
 
 	rc = cam_fd_mgr_util_put_frame_req(
 		&hw_mgr->frame_processing_list, &frame_req);
@@ -1514,6 +1515,137 @@ static int cam_fd_mgr_hw_flush(void *hw_mgr_priv,
 	return rc;
 }
 
+static int cam_fd_mgr_hw_dump(
+	void *hw_mgr_priv,
+	void *hw_dump_args)
+{
+	int                              rc;
+	uint8_t                         *dst;
+	ktime_t                          cur_time;
+	size_t                           remain_len;
+	uint32_t                         min_len;
+	uint64_t                         diff;
+	uint64_t                        *addr, *start;
+	struct timespec64                cur_ts;
+	struct timespec64                req_ts;
+	struct cam_fd_hw_mgr            *hw_mgr;
+	struct cam_hw_dump_args         *dump_args;
+	struct cam_fd_hw_mgr_ctx        *hw_ctx;
+	struct cam_fd_device            *hw_device;
+	struct cam_fd_hw_dump_args       fd_dump_args;
+	struct cam_fd_hw_dump_header    *hdr;
+	struct cam_fd_mgr_frame_request *frame_req, *req_temp;
+
+	hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+	dump_args = (struct cam_hw_dump_args *)hw_dump_args;
+	if (!hw_mgr || !dump_args) {
+		CAM_ERR(CAM_FD, "Invalid args %pK %pK",
+			hw_mgr, dump_args);
+		return -EINVAL;
+	}
+
+	hw_ctx = (struct cam_fd_hw_mgr_ctx *)dump_args->ctxt_to_hw_map;
+
+	if (!hw_ctx) {
+		CAM_ERR(CAM_FD, "Invalid ctx");
+		return -EINVAL;
+	}
+
+	rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+
+	if (rc) {
+		CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+		return rc;
+	}
+
+	list_for_each_entry_safe(frame_req, req_temp,
+		&hw_mgr->frame_processing_list, list) {
+		if (frame_req->request_id == dump_args->request_id)
+			goto hw_dump;
+	}
+
+	CAM_DBG(CAM_FD, "fd dump cannot find req %llu",
+		dump_args->request_id);
+	return rc;
+hw_dump:
+	cur_time = ktime_get();
+	diff = ktime_us_delta(frame_req->submit_timestamp, cur_time);
+	cur_ts = ktime_to_timespec64(cur_time);
+	req_ts = ktime_to_timespec64(frame_req->submit_timestamp);
+	if (diff < CAM_FD_RESPONSE_TIME_THRESHOLD) {
+		CAM_INFO(CAM_FD, "No Error req %lld %ld:%06ld %ld:%06ld",
+			dump_args->request_id,
+			req_ts.tv_sec,
+			req_ts.tv_nsec/NSEC_PER_USEC,
+			cur_ts.tv_sec,
+			cur_ts.tv_nsec/NSEC_PER_USEC);
+		return 0;
+	}
+	CAM_INFO(CAM_FD, "Error req %lld %ld:%06ld %ld:%06ld",
+		dump_args->request_id,
+		req_ts.tv_sec,
+		req_ts.tv_nsec/NSEC_PER_USEC,
+		cur_ts.tv_sec,
+		cur_ts.tv_nsec/NSEC_PER_USEC);
+	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
+		&fd_dump_args.cpu_addr, &fd_dump_args.buf_len);
+	if (rc) {
+		CAM_ERR(CAM_FD, "Invalid handle %u rc %d",
+			dump_args->buf_handle, rc);
+		return rc;
+	}
+	if (fd_dump_args.buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_FD, "dump offset overshoot len %zu offset %zu",
+			fd_dump_args.buf_len, dump_args->offset);
+		return -ENOSPC;
+	}
+	remain_len = fd_dump_args.buf_len - dump_args->offset;
+	min_len =  sizeof(struct cam_fd_hw_dump_header) +
+		(CAM_FD_HW_DUMP_NUM_WORDS * sizeof(uint64_t));
+
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_FD, "dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)fd_dump_args.cpu_addr + dump_args->offset;
+	hdr = (struct cam_fd_hw_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_FD_HW_DUMP_TAG_MAX_LEN,
+		"FD_REQ:");
+	hdr->word_size = sizeof(uint64_t);
+	addr = (uint64_t *)(dst + sizeof(struct cam_fd_hw_dump_header));
+	start = addr;
+	*addr++ = frame_req->request_id;
+	*addr++ = req_ts.tv_sec;
+	*addr++ = req_ts.tv_nsec/NSEC_PER_USEC;
+	*addr++ = cur_ts.tv_sec;
+	*addr++ = cur_ts.tv_nsec/NSEC_PER_USEC;
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += hdr->size +
+		sizeof(struct cam_fd_hw_dump_header);
+
+	fd_dump_args.request_id = dump_args->request_id;
+	fd_dump_args.offset = dump_args->offset;
+	if (hw_device->hw_intf->hw_ops.process_cmd) {
+		rc = hw_device->hw_intf->hw_ops.process_cmd(
+			hw_device->hw_intf->hw_priv,
+			CAM_FD_HW_CMD_HW_DUMP,
+			&fd_dump_args,
+			sizeof(struct
+			cam_fd_hw_dump_args));
+		if (rc) {
+			CAM_ERR(CAM_FD, "Hw Dump cmd fails req %lld rc %d",
+				frame_req->request_id, rc);
+			return rc;
+		}
+	}
+	CAM_DBG(CAM_FD, "Offset before %zu after %zu",
+		dump_args->offset, fd_dump_args.offset);
+	dump_args->offset = fd_dump_args.offset;
+	return rc;
+}
+
 static int cam_fd_mgr_hw_stop(void *hw_mgr_priv, void *mgr_stop_args)
 {
 	struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
@@ -1954,6 +2086,7 @@ int cam_fd_hw_mgr_init(struct device_node *of_node,
 	hw_mgr_intf->hw_write = NULL;
 	hw_mgr_intf->hw_close = NULL;
 	hw_mgr_intf->hw_flush = cam_fd_mgr_hw_flush;
+	hw_mgr_intf->hw_dump = cam_fd_mgr_hw_dump;
 
 	return rc;
 

+ 9 - 1
drivers/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_FD_HW_MGR_H_
@@ -21,6 +21,12 @@
 #define CAM_FD_HW_MAX            1
 #define CAM_FD_WORKQ_NUM_TASK    10
 
+/*
+ * Response time threshold in ms beyond which a request is not expected to be
+ * with FD hw
+ */
+#define CAM_FD_RESPONSE_TIME_THRESHOLD   100000
+
 struct cam_fd_hw_mgr;
 
 /**
@@ -100,6 +106,7 @@ struct cam_fd_device {
  * @hw_update_entries     : HW update entries corresponding to this request
  *                          which needs to be submitted to HW through CDM
  * @num_hw_update_entries : Number of HW update entries
+ * @submit_timestamp      : Time stamp for submit req with hw
  */
 struct cam_fd_mgr_frame_request {
 	struct list_head               list;
@@ -108,6 +115,7 @@ struct cam_fd_mgr_frame_request {
 	struct cam_fd_hw_req_private   hw_req_private;
 	struct cam_hw_update_entry     hw_update_entries[CAM_FD_MAX_HW_ENTRIES];
 	uint32_t                       num_hw_update_entries;
+	ktime_t                        submit_timestamp;
 };
 
 /**

+ 79 - 0
drivers/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c

@@ -516,6 +516,80 @@ static int cam_fd_hw_util_processcmd_frame_done(struct cam_hw_info *fd_hw,
 	return 0;
 }
 
+static int cam_fd_hw_util_processcmd_hw_dump(
+	struct cam_hw_info *fd_hw,
+	void               *args)
+{
+	int                            i, j;
+	uint8_t                       *dst;
+	uint32_t                      *addr, *start;
+	uint32_t                       num_reg, min_len;
+	uint64_t                       remain_len;
+	struct cam_hw_soc_info        *soc_info;
+	struct cam_fd_hw_dump_header  *hdr;
+	struct cam_fd_hw_dump_args    *dump_args;
+
+	if (!fd_hw || !args) {
+		CAM_ERR(CAM_FD, "Invalid args %pK %pK",
+			fd_hw, args);
+		return -EINVAL;
+	}
+
+	mutex_lock(&fd_hw->hw_mutex);
+
+	if (fd_hw->hw_state == CAM_HW_STATE_POWER_DOWN) {
+		CAM_INFO(CAM_FD, "power off state");
+		mutex_unlock(&fd_hw->hw_mutex);
+		return 0;
+	}
+
+	dump_args = (struct cam_fd_hw_dump_args *)args;
+	soc_info = &fd_hw->soc_info;
+
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_FD, "dump offset overshoot len %zu offset %zu",
+			dump_args->buf_len, dump_args->offset);
+		mutex_unlock(&fd_hw->hw_mutex);
+		return -ENOSPC;
+	}
+
+	remain_len = dump_args->buf_len - dump_args->offset;
+	min_len =  sizeof(struct cam_fd_hw_dump_header) +
+		    soc_info->reg_map[0].size + sizeof(uint32_t);
+
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_FD, "dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		mutex_unlock(&fd_hw->hw_mutex);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
+	hdr = (struct cam_fd_hw_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_FD_HW_DUMP_TAG_MAX_LEN,
+		"FD_REG:");
+	hdr->word_size = sizeof(uint32_t);
+	addr = (uint32_t *)(dst + sizeof(struct cam_fd_hw_dump_header));
+	start = addr;
+	*addr++ = soc_info->index;
+
+	for (j = 0; j < soc_info->num_reg_map; j++) {
+		num_reg = soc_info->reg_map[j].size/4;
+		for (i = 0; i < num_reg; i++) {
+			*addr++ = soc_info->mem_block[j]->start + i*4;
+			*addr++ = cam_io_r(soc_info->reg_map[j].mem_base +
+				(i*4));
+		}
+	}
+
+	mutex_unlock(&fd_hw->hw_mutex);
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += hdr->size +
+		sizeof(struct cam_fd_hw_dump_header);
+	CAM_DBG(CAM_FD, "%zu", dump_args->offset);
+	return 0;
+}
+
 irqreturn_t cam_fd_hw_irq(int irq_num, void *data)
 {
 	struct cam_hw_info *fd_hw = (struct cam_hw_info *)data;
@@ -1161,6 +1235,11 @@ int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
 			cmd_frame_results);
 		break;
 	}
+	case CAM_FD_HW_CMD_HW_DUMP: {
+		rc = cam_fd_hw_util_processcmd_hw_dump(fd_hw,
+			cmd_args);
+		break;
+	}
 	default:
 		break;
 	}

+ 33 - 0
drivers/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h

@@ -24,6 +24,8 @@
 
 #define CAM_FD_MAX_IO_BUFFERS        5
 #define CAM_FD_MAX_HW_ENTRIES        5
+#define CAM_FD_HW_DUMP_TAG_MAX_LEN   32
+#define CAM_FD_HW_DUMP_NUM_WORDS     5
 
 /**
  * enum cam_fd_hw_type - Enum for FD HW type
@@ -81,12 +83,14 @@ enum cam_fd_hw_irq_type {
  * @CAM_FD_HW_CMD_UPDATE_SOC        : Command to process soc update
  * @CAM_FD_HW_CMD_REGISTER_CALLBACK : Command to set hw mgr callback
  * @CAM_FD_HW_CMD_MAX               : Indicates max cmd
+ * @CAM_FD_HW_CMD_HW_DUMP           : Command to dump fd hw information
  */
 enum cam_fd_hw_cmd_type {
 	CAM_FD_HW_CMD_PRESTART,
 	CAM_FD_HW_CMD_FRAME_DONE,
 	CAM_FD_HW_CMD_UPDATE_SOC,
 	CAM_FD_HW_CMD_REGISTER_CALLBACK,
+	CAM_FD_HW_CMD_HW_DUMP,
 	CAM_FD_HW_CMD_MAX,
 };
 
@@ -281,6 +285,34 @@ struct cam_fd_hw_cmd_set_irq_cb {
 	void *data;
 };
 
+/**
+ * struct cam_fd_hw_dump_args : Args for dump request
+ *
+ * @request_id   : Issue request id
+ * @offset       : offset of the buffer
+ * @buf_len      : Length of target buffer
+ * @cpu_addr     : start address of the target buffer
+ */
+struct cam_fd_hw_dump_args {
+	uint64_t  request_id;
+	size_t    offset;
+	size_t    buf_len;
+	uintptr_t cpu_addr;
+};
+
+/**
+ * struct cam_fd_hw_dump_header : fd hw dump header
+ *
+ * @tag       : fd hw dump header tag
+ * @size      : Size of data
+ * @word_size : size of each word
+ */
+struct cam_fd_hw_dump_header {
+	uint8_t  tag[CAM_FD_HW_DUMP_TAG_MAX_LEN];
+	uint64_t size;
+	uint32_t word_size;
+};
+
 /**
  * @brief : API to register FD Hw to platform framework.
  * @return struct platform_device pointer on on success, or ERR_PTR() on error.
@@ -293,3 +325,4 @@ int cam_fd_hw_init_module(void);
 void cam_fd_hw_exit_module(void);
 
 #endif /* _CAM_FD_HW_INTF_H_ */
+

+ 16 - 1
drivers/cam_icp/cam_icp_context.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/debugfs.h>
@@ -107,6 +107,19 @@ static int __cam_icp_start_dev_in_acquired(struct cam_context *ctx,
 	return rc;
 }
 
+static int __cam_icp_dump_dev_in_ready(
+	struct cam_context      *ctx,
+	struct cam_dump_req_cmd *cmd)
+{
+	int rc;
+
+	rc = cam_context_dump_dev_to_hw(ctx, cmd);
+	if (rc)
+		CAM_ERR(CAM_ICP, "Failed to dump device");
+
+	return rc;
+}
+
 static int __cam_icp_flush_dev_in_ready(struct cam_context *ctx,
 	struct cam_flush_dev_cmd *cmd)
 {
@@ -230,6 +243,7 @@ static struct cam_ctx_ops
 			.start_dev = __cam_icp_start_dev_in_acquired,
 			.config_dev = __cam_icp_config_dev_in_ready,
 			.flush_dev = __cam_icp_flush_dev_in_ready,
+			.dump_dev = __cam_icp_dump_dev_in_ready,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_icp_handle_buf_done_in_ready,
@@ -242,6 +256,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_icp_release_dev_in_ready,
 			.config_dev = __cam_icp_config_dev_in_ready,
 			.flush_dev = __cam_icp_flush_dev_in_ready,
+			.dump_dev = __cam_icp_dump_dev_in_ready,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_icp_handle_buf_done_in_ready,

+ 54 - 1
drivers/cam_icp/icp_hw/a5_hw/a5_core.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>
@@ -235,6 +235,53 @@ fw_download_failed:
 	return rc;
 }
 
+static int cam_a5_fw_dump(
+	struct cam_icp_hw_dump_args    *dump_args,
+	struct cam_a5_device_core_info *core_info)
+{
+	u8                         *dest;
+	u8                         *src;
+	uint64_t                    size_required;
+	struct cam_icp_dump_header *hdr;
+
+	if (!core_info || !dump_args) {
+		CAM_ERR(CAM_ICP, "invalid params %pK %pK",
+		    core_info, dump_args);
+		return -EINVAL;
+	}
+	if (!core_info->fw_kva_addr || !dump_args->cpu_addr) {
+		CAM_ERR(CAM_ICP, "invalid params %pK, 0x%zx",
+		    core_info->fw_kva_addr, dump_args->cpu_addr);
+		return -EINVAL;
+	}
+
+	size_required = core_info->fw_buf_len +
+		sizeof(struct cam_icp_dump_header);
+
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_ICP, "Dump offset overshoot len %zu offset %zu",
+			dump_args->buf_len, dump_args->offset);
+		return -ENOSPC;
+	}
+
+	if ((dump_args->buf_len - dump_args->offset) < size_required) {
+		CAM_WARN(CAM_ICP, "Dump buffer exhaust required %llu len %llu",
+			size_required, core_info->fw_buf_len);
+		return -ENOSPC;
+	}
+
+	dest = (u8 *)dump_args->cpu_addr + dump_args->offset;
+	hdr = (struct cam_icp_dump_header *)dest;
+	scnprintf(hdr->tag, CAM_ICP_DUMP_TAG_MAX_LEN, "ICP_FW:");
+	hdr->word_size = sizeof(u8);
+	hdr->size = core_info->fw_buf_len;
+	src = (u8 *)core_info->fw_kva_addr;
+	dest = (u8 *)dest + sizeof(struct cam_icp_dump_header);
+	memcpy_fromio(dest, src, core_info->fw_buf_len);
+	dump_args->offset += hdr->size + sizeof(struct cam_icp_dump_header);
+	return 0;
+}
+
 int cam_a5_init_hw(void *device_priv,
 	void *init_hw_args, uint32_t arg_size)
 {
@@ -543,6 +590,12 @@ int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type,
 			core_info->cpas_handle, &ahb_vote);
 		break;
 	}
+	case CAM_ICP_A5_CMD_HW_DUMP: {
+		struct cam_icp_hw_dump_args *dump_args = cmd_args;
+
+		rc = cam_a5_fw_dump(dump_args, core_info);
+		break;
+	}
 	default:
 		break;
 	}

+ 111 - 0
drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c

@@ -3930,6 +3930,7 @@ static int cam_icp_mgr_config_hw(void *hw_mgr_priv, void *config_hw_args)
 
 	cam_icp_mgr_ipe_bps_clk_update(hw_mgr, ctx_data, idx);
 	ctx_data->hfi_frame_process.fw_process_flag[idx] = true;
+	ctx_data->hfi_frame_process.submit_timestamp[idx] = ktime_get();
 
 	CAM_DBG(CAM_ICP, "req_id %llu, io config %llu", req_id,
 		frame_info->io_config);
@@ -5038,6 +5039,115 @@ static int cam_icp_mgr_enqueue_abort(
 	return 0;
 }
 
+static int cam_icp_mgr_hw_dump(void *hw_priv, void *hw_dump_args)
+{
+	int                              rc;
+	int                              i;
+	size_t                           remain_len;
+	uint8_t                         *dst;
+	uint32_t                         min_len;
+	uint64_t                         diff;
+	uint64_t                        *addr, *start;
+	struct timespec64                cur_ts;
+	struct timespec64                req_ts;
+	ktime_t                          cur_time;
+	struct cam_hw_intf              *a5_dev_intf;
+	struct cam_icp_hw_mgr           *hw_mgr;
+	struct cam_hw_dump_args         *dump_args;
+	struct cam_icp_hw_ctx_data      *ctx_data;
+	struct cam_icp_dump_header      *hdr;
+	struct cam_icp_hw_dump_args      icp_dump_args;
+	struct hfi_frame_process_info   *frm_process;
+
+	if ((!hw_priv) || (!hw_dump_args)) {
+		CAM_ERR(CAM_ICP, "Invalid params %pK %pK",
+			hw_priv, hw_dump_args);
+		return -EINVAL;
+	}
+
+	dump_args = (struct cam_hw_dump_args *)hw_dump_args;
+	hw_mgr = hw_priv;
+	ctx_data = dump_args->ctxt_to_hw_map;
+	CAM_DBG(CAM_ICP, "Req %lld", dump_args->request_id);
+	frm_process = &ctx_data->hfi_frame_process;
+	for (i = 0; i < CAM_FRAME_CMD_MAX; i++) {
+		if ((frm_process->request_id[i] ==
+			dump_args->request_id) &&
+			frm_process->fw_process_flag[i])
+			goto hw_dump;
+	}
+	return 0;
+hw_dump:
+	cur_time = ktime_get();
+	diff = ktime_us_delta(frm_process->submit_timestamp[i], cur_time);
+	cur_ts = ktime_to_timespec64(cur_time);
+	req_ts = ktime_to_timespec64(frm_process->submit_timestamp[i]);
+
+	if (diff < CAM_ICP_CTX_RESPONSE_TIME_THRESHOLD) {
+		CAM_INFO(CAM_ICP, "No Error req %lld %ld:%06ld %ld:%06ld",
+			dump_args->request_id,
+			req_ts.tv_sec,
+			req_ts.tv_nsec/NSEC_PER_USEC,
+			cur_ts.tv_sec,
+			cur_ts.tv_nsec/NSEC_PER_USEC);
+		return 0;
+	}
+
+	CAM_INFO(CAM_ICP, "Error req %lld %ld:%06ld %ld:%06ld",
+		dump_args->request_id,
+		req_ts.tv_sec,
+		req_ts.tv_nsec/NSEC_PER_USEC,
+		cur_ts.tv_sec,
+		cur_ts.tv_nsec/NSEC_PER_USEC);
+	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
+		&icp_dump_args.cpu_addr, &icp_dump_args.buf_len);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "Invalid addr %u rc %d",
+			dump_args->buf_handle, rc);
+		return rc;
+	}
+	if (icp_dump_args.buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_ICP, "dump buffer overshoot len %zu offset %zu",
+			icp_dump_args.buf_len, dump_args->offset);
+		return -ENOSPC;
+	}
+
+	remain_len = icp_dump_args.buf_len - dump_args->offset;
+	min_len = sizeof(struct cam_icp_dump_header) +
+			(CAM_ICP_DUMP_NUM_WORDS * sizeof(uint64_t));
+
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_ICP, "dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)icp_dump_args.cpu_addr + dump_args->offset;
+	hdr = (struct cam_icp_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_ICP_DUMP_TAG_MAX_LEN, "ICP_REQ:");
+	hdr->word_size = sizeof(uint64_t);
+	addr = (uint64_t *)(dst + sizeof(struct cam_icp_dump_header));
+	start = addr;
+	*addr++ = frm_process->request_id[i];
+	*addr++ = req_ts.tv_sec;
+	*addr++ = req_ts.tv_nsec/NSEC_PER_USEC;
+	*addr++ = cur_ts.tv_sec;
+	*addr++ = cur_ts.tv_nsec/NSEC_PER_USEC;
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += (hdr->size + sizeof(struct cam_icp_dump_header));
+	/* Dumping the fw image*/
+	icp_dump_args.offset = dump_args->offset;
+	a5_dev_intf = hw_mgr->a5_dev_intf;
+	rc = a5_dev_intf->hw_ops.process_cmd(
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_CMD_HW_DUMP, &icp_dump_args,
+		sizeof(struct cam_icp_hw_dump_args));
+	CAM_DBG(CAM_ICP, "Offset before %zu after %zu",
+		dump_args->offset, icp_dump_args.offset);
+	dump_args->offset = icp_dump_args.offset;
+	return rc;
+}
+
 static int cam_icp_mgr_hw_flush(void *hw_priv, void *hw_flush_args)
 {
 	struct cam_hw_flush_args *flush_args = hw_flush_args;
@@ -5850,6 +5960,7 @@ int cam_icp_hw_mgr_init(struct device_node *of_node, uint64_t *hw_mgr_hdl,
 	hw_mgr_intf->hw_close = cam_icp_mgr_hw_close_u;
 	hw_mgr_intf->hw_flush = cam_icp_mgr_hw_flush;
 	hw_mgr_intf->hw_cmd = cam_icp_mgr_cmd;
+	hw_mgr_intf->hw_dump = cam_icp_mgr_hw_dump;
 
 	icp_hw_mgr.secure_mode = CAM_SECURE_MODE_NON_SECURE;
 	mutex_init(&icp_hw_mgr.hw_mgr_mutex);

+ 9 - 1
drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.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_ICP_HW_MGR_H
@@ -67,6 +67,12 @@
 /* Current appliacble vote paths, based on number of UAPI definitions */
 #define CAM_ICP_MAX_PER_PATH_VOTES 6
 
+/*
+ * Response time threshold in ms beyond which a request is not expected
+ * to be with ICP hw
+ */
+#define CAM_ICP_CTX_RESPONSE_TIME_THRESHOLD   300000
+
 /**
  * struct icp_hfi_mem_info
  * @qtbl: Memory info of queue table
@@ -171,6 +177,7 @@ struct cam_icp_clk_bw_req_internal_v2 {
  * @clk_info: Clock information for a request
  * @clk_info_v2: Clock info for AXI bw voting v2
  * @frame_info: information needed to process request
+ * @submit_timestamp: Submit timestamp to hw
  */
 struct hfi_frame_process_info {
 	struct hfi_cmd_ipebps_async hfi_frame_cmd[CAM_FRAME_CMD_MAX];
@@ -186,6 +193,7 @@ struct hfi_frame_process_info {
 	struct cam_icp_clk_bw_request clk_info[CAM_FRAME_CMD_MAX];
 	struct cam_icp_clk_bw_req_internal_v2 clk_info_v2[CAM_FRAME_CMD_MAX];
 	struct icp_frame_info frame_info[CAM_FRAME_CMD_MAX];
+	ktime_t submit_timestamp[CAM_FRAME_CMD_MAX];
 };
 
 /**

+ 2 - 1
drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.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_A5_HW_INTF_H
@@ -27,6 +27,7 @@ enum cam_icp_a5_cmd_type {
 	CAM_ICP_A5_CMD_UBWC_CFG,
 	CAM_ICP_A5_CMD_PC_PREP,
 	CAM_ICP_A5_CMD_CLK_UPDATE,
+	CAM_ICP_A5_CMD_HW_DUMP,
 	CAM_ICP_A5_CMD_MAX,
 };
 

+ 28 - 1
drivers/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.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_ICP_HW_MGR_INTF_H
@@ -27,6 +27,9 @@
 #define CAM_ICP_DEFAULT_AXI_PATH    CAM_AXI_PATH_DATA_ALL
 #define CAM_ICP_DEFAULT_AXI_TRANSAC CAM_AXI_TRANSACTION_READ
 
+#define CAM_ICP_DUMP_TAG_MAX_LEN 32
+#define CAM_ICP_DUMP_NUM_WORDS   5
+
 int cam_icp_hw_mgr_init(struct device_node *of_node,
 	uint64_t *hw_mgr_hdl, int *iommu_hdl);
 
@@ -44,4 +47,28 @@ struct cam_icp_cpas_vote {
 	uint32_t axi_vote_valid;
 };
 
+/**
+ * struct cam_icp_hw_dump_args
+ * @cpu_addr: kernel vaddr
+ * @buf_len:  buffer length
+ * @offset:   offset
+ */
+struct cam_icp_hw_dump_args {
+	uintptr_t  cpu_addr;
+	size_t     buf_len;
+	size_t     offset;
+};
+
+/**
+ * struct cam_icp_dump_header
+ * @tag:        tag of the packet
+ * @size:       size of data in packet
+ * @word_size:  size of each word in packet
+ */
+struct cam_icp_dump_header {
+	uint8_t    tag[CAM_ICP_DUMP_TAG_MAX_LEN];
+	uint64_t   size;
+	int32_t    word_size;
+};
+
 #endif /* CAM_ICP_HW_MGR_INTF_H */

+ 15 - 1
drivers/cam_jpeg/cam_jpeg_context.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/debugfs.h>
@@ -83,6 +83,19 @@ static int __cam_jpeg_ctx_release_dev_in_acquired(struct cam_context *ctx,
 	return rc;
 }
 
+static int __cam_jpeg_ctx_dump_dev_in_acquired(
+	struct cam_context      *ctx,
+	struct cam_dump_req_cmd *cmd)
+{
+	int rc;
+
+	rc = cam_context_dump_dev_to_hw(ctx, cmd);
+	if (rc)
+		CAM_ERR(CAM_JPEG, "Failed to dump device, rc=%d", rc);
+
+	return rc;
+}
+
 static int __cam_jpeg_ctx_flush_dev_in_acquired(struct cam_context *ctx,
 	struct cam_flush_dev_cmd *cmd)
 {
@@ -145,6 +158,7 @@ static struct cam_ctx_ops
 			.config_dev = __cam_jpeg_ctx_config_dev_in_acquired,
 			.stop_dev = __cam_jpeg_ctx_stop_dev_in_acquired,
 			.flush_dev = __cam_jpeg_ctx_flush_dev_in_acquired,
+			.dump_dev = __cam_jpeg_ctx_dump_dev_in_acquired,
 		},
 		.crm_ops = { },
 		.irq_ops = __cam_jpeg_ctx_handle_buf_done_in_acquired,

+ 139 - 0
drivers/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c

@@ -488,6 +488,7 @@ static int cam_jpeg_mgr_process_cmd(void *priv, void *data)
 			rc);
 		goto end_callcb;
 	}
+	p_cfg_req->submit_timestamp = ktime_get();
 
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 	return rc;
@@ -1491,6 +1492,143 @@ num_dev_failed:
 	return rc;
 }
 
+static int cam_jpeg_mgr_hw_dump(void *hw_mgr_priv, void *dump_hw_args)
+{
+	int                             rc;
+	uint8_t                        *dst;
+	ktime_t                         cur_time;
+	size_t                          remain_len;
+	uint32_t                        min_len;
+	uint32_t                        dev_type;
+	uint64_t                        diff;
+	uint64_t                       *addr, *start;
+	struct timespec64               cur_ts;
+	struct timespec64               req_ts;
+	struct cam_jpeg_hw_mgr         *hw_mgr;
+	struct cam_hw_dump_args        *dump_args;
+	struct cam_jpeg_hw_cfg_req     *p_cfg_req;
+	struct cam_jpeg_hw_ctx_data    *ctx_data;
+	struct cam_jpeg_hw_dump_args    jpeg_dump_args;
+	struct cam_jpeg_hw_dump_header *hdr;
+
+	if (!hw_mgr_priv || !dump_hw_args) {
+		CAM_ERR(CAM_JPEG, "Invalid args %pK %pK",
+			hw_mgr_priv, dump_hw_args);
+		return -EINVAL;
+	}
+
+	hw_mgr = hw_mgr_priv;
+	dump_args = (struct cam_hw_dump_args *)dump_hw_args;
+	ctx_data = (struct cam_jpeg_hw_ctx_data *)dump_args->ctxt_to_hw_map;
+
+	if (!ctx_data) {
+		CAM_ERR(CAM_JPEG, "Invalid context");
+		return -EINVAL;
+	}
+
+	mutex_lock(&hw_mgr->hw_mgr_mutex);
+
+	if (!ctx_data->in_use) {
+		CAM_ERR(CAM_JPEG, "ctx is not in use");
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return -EINVAL;
+	}
+
+	dev_type = ctx_data->jpeg_dev_acquire_info.dev_type;
+
+	if (true == hw_mgr->device_in_use[dev_type][0]) {
+		p_cfg_req = hw_mgr->dev_hw_cfg_args[dev_type][0];
+		if (p_cfg_req  && p_cfg_req->req_id ==
+			    (uintptr_t)dump_args->request_id)
+			goto hw_dump;
+	}
+
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
+	return 0;
+
+hw_dump:
+	cur_time = ktime_get();
+	diff = ktime_us_delta(p_cfg_req->submit_timestamp, cur_time);
+	cur_ts = ktime_to_timespec64(cur_time);
+	req_ts = ktime_to_timespec64(p_cfg_req->submit_timestamp);
+
+	if (diff < CAM_JPEG_RESPONSE_TIME_THRESHOLD) {
+		CAM_INFO(CAM_JPEG,
+			"No error req %lld %ld:%06ld %ld:%06ld",
+			dump_args->request_id,
+			req_ts.tv_sec,
+			req_ts.tv_nsec/NSEC_PER_USEC,
+			cur_ts.tv_sec,
+			cur_ts.tv_nsec/NSEC_PER_USEC);
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return 0;
+	}
+
+	CAM_INFO(CAM_JPEG,
+		"Error req %lld %ld:%06ld %ld:%06ld",
+		dump_args->request_id,
+		req_ts.tv_sec,
+		req_ts.tv_nsec/NSEC_PER_USEC,
+		cur_ts.tv_sec,
+		cur_ts.tv_nsec/NSEC_PER_USEC);
+	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
+		&jpeg_dump_args.cpu_addr, &jpeg_dump_args.buf_len);
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "Invalid handle %u rc %d",
+			dump_args->buf_handle, rc);
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return -rc;
+	}
+
+	if (jpeg_dump_args.buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_JPEG, "dump offset overshoot len %zu offset %zu",
+			jpeg_dump_args.buf_len, dump_args->offset);
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return -ENOSPC;
+	}
+
+	remain_len = jpeg_dump_args.buf_len - dump_args->offset;
+	min_len = sizeof(struct cam_jpeg_hw_dump_header) +
+			(CAM_JPEG_HW_DUMP_NUM_WORDS * sizeof(uint64_t));
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_JPEG, "dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)jpeg_dump_args.cpu_addr + dump_args->offset;
+	hdr = (struct cam_jpeg_hw_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_JPEG_HW_DUMP_TAG_MAX_LEN,
+		"JPEG_REQ:");
+	hdr->word_size = sizeof(uint64_t);
+	addr = (uint64_t *)(dst + sizeof(struct cam_jpeg_hw_dump_header));
+	start = addr;
+	*addr++ = dump_args->request_id;
+	*addr++ = req_ts.tv_sec;
+	*addr++ = req_ts.tv_nsec/NSEC_PER_USEC;
+	*addr++ = cur_ts.tv_sec;
+	*addr++ = cur_ts.tv_nsec/NSEC_PER_USEC;
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += hdr->size +
+		sizeof(struct cam_jpeg_hw_dump_header);
+	jpeg_dump_args.request_id = dump_args->request_id;
+	jpeg_dump_args.offset = dump_args->offset;
+
+	if (hw_mgr->devices[dev_type][0]->hw_ops.process_cmd) {
+		rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd(
+			hw_mgr->devices[dev_type][0]->hw_priv,
+			CAM_JPEG_CMD_HW_DUMP,
+			&jpeg_dump_args, sizeof(jpeg_dump_args));
+	}
+
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
+	CAM_DBG(CAM_JPEG, "Offset before %u after %u",
+		dump_args->offset, jpeg_dump_args.offset);
+	dump_args->offset = jpeg_dump_args.offset;
+	return rc;
+}
+
 static int cam_jpeg_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
 {
 	int rc = 0;
@@ -1544,6 +1682,7 @@ int cam_jpeg_hw_mgr_init(struct device_node *of_node, uint64_t *hw_mgr_hdl,
 	hw_mgr_intf->hw_flush = cam_jpeg_mgr_hw_flush;
 	hw_mgr_intf->hw_stop = cam_jpeg_mgr_hw_stop;
 	hw_mgr_intf->hw_cmd = cam_jpeg_mgr_cmd;
+	hw_mgr_intf->hw_dump = cam_jpeg_mgr_hw_dump;
 
 	mutex_init(&g_jpeg_hw_mgr.hw_mgr_mutex);
 	spin_lock_init(&g_jpeg_hw_mgr.hw_mgr_lock);

+ 9 - 1
drivers/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef CAM_JPEG_HW_MGR_H
@@ -21,6 +21,12 @@
 #define CAM_JPEG_WORKQ_TASK_MSG_TYPE 2
 #define CAM_JPEG_HW_CFG_Q_MAX        50
 
+/*
+ * Response time threshold in ms beyond which a request is not expected
+ * to be with JPEG hw
+ */
+#define CAM_JPEG_RESPONSE_TIME_THRESHOLD   100000
+
 /**
  * struct cam_jpeg_process_frame_work_data_t
  *
@@ -69,12 +75,14 @@ struct cam_jpeg_hw_cdm_info_t {
  * @hw_cfg_args: Hw config args
  * @dev_type: Dev type for cfg request
  * @req_id: Request Id
+ * @submit_timestamp: Timestamp of submitting request
  */
 struct cam_jpeg_hw_cfg_req {
 	struct list_head list;
 	struct cam_hw_config_args hw_cfg_args;
 	uint32_t dev_type;
 	uintptr_t req_id;
+	ktime_t    submit_timestamp;
 };
 
 /**

+ 18 - 1
drivers/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.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_JPEG_HW_INTF_H
@@ -15,6 +15,9 @@
 
 #define JPEG_VOTE                     640000000
 
+#define CAM_JPEG_HW_DUMP_TAG_MAX_LEN 32
+#define CAM_JPEG_HW_DUMP_NUM_WORDS   5
+
 enum cam_jpeg_hw_type {
 	CAM_JPEG_DEV_ENC,
 	CAM_JPEG_DEV_DMA,
@@ -27,9 +30,23 @@ struct cam_jpeg_set_irq_cb {
 	uint32_t b_set_cb;
 };
 
+struct cam_jpeg_hw_dump_args {
+	uint64_t  request_id;
+	uintptr_t cpu_addr;
+	size_t    offset;
+	size_t    buf_len;
+};
+
+struct cam_jpeg_hw_dump_header {
+	uint8_t     tag[CAM_JPEG_HW_DUMP_TAG_MAX_LEN];
+	uint64_t    size;
+	uint32_t    word_size;
+};
+
 enum cam_jpeg_cmd_type {
 	CAM_JPEG_CMD_CDM_CFG,
 	CAM_JPEG_CMD_SET_IRQ_CB,
+	CAM_JPEG_CMD_HW_DUMP,
 	CAM_JPEG_CMD_MAX,
 };
 

+ 5 - 1
drivers/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.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_JPEG_ENC_HW_INFO_TITAN170_H
@@ -66,6 +66,10 @@ static struct cam_jpeg_enc_device_hw_info cam_jpeg_enc_hw_info = {
 		.resetdone = CAM_JPEG_HW_MASK_COMP_RESET_ACK,
 		.iserror = CAM_JPEG_HW_MASK_COMP_ERR,
 		.stopdone = CAM_JPEG_HW_IRQ_STATUS_STOP_DONE_MASK,
+	},
+	.reg_dump = {
+		.start_offset = 0x0,
+		.end_offset = 0x33C,
 	}
 };
 

+ 82 - 1
drivers/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.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/of.h>
@@ -378,6 +378,81 @@ int cam_jpeg_enc_stop_hw(void *data,
 	return 0;
 }
 
+int cam_jpeg_enc_hw_dump(
+	struct cam_hw_info           *jpeg_enc_dev,
+	struct cam_jpeg_hw_dump_args *dump_args)
+{
+
+	int                                   i;
+	uint8_t                              *dst;
+	uint32_t                             *addr, *start;
+	uint32_t                              num_reg, min_len;
+	uint32_t                              reg_start_offset;
+	size_t                                remain_len;
+	struct cam_hw_soc_info               *soc_info;
+	struct cam_jpeg_hw_dump_header       *hdr;
+	struct cam_jpeg_enc_device_hw_info   *hw_info;
+	struct cam_jpeg_enc_device_core_info *core_info;
+
+	soc_info = &jpeg_enc_dev->soc_info;
+	core_info = (struct cam_jpeg_enc_device_core_info *)
+		jpeg_enc_dev->core_info;
+	hw_info = core_info->jpeg_enc_hw_info;
+	mutex_lock(&core_info->core_mutex);
+	spin_lock(&jpeg_enc_dev->hw_lock);
+
+	if (jpeg_enc_dev->hw_state == CAM_HW_STATE_POWER_DOWN) {
+		CAM_ERR(CAM_JPEG, "JPEG HW is in off state");
+		spin_unlock(&jpeg_enc_dev->hw_lock);
+		mutex_unlock(&core_info->core_mutex);
+		return -EINVAL;
+	}
+
+	spin_unlock(&jpeg_enc_dev->hw_lock);
+
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_JPEG, "dump buffer overshoot %zu %zu",
+			dump_args->buf_len, dump_args->offset);
+		mutex_unlock(&core_info->core_mutex);
+		return -ENOSPC;
+	}
+
+	remain_len = dump_args->buf_len - dump_args->offset;
+	min_len =  sizeof(struct cam_jpeg_hw_dump_header) +
+		    soc_info->reg_map[0].size + sizeof(uint32_t);
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_JPEG, "dump buffer exhaust %zu %u",
+			remain_len, min_len);
+		mutex_unlock(&core_info->core_mutex);
+		return -ENOSPC;
+	}
+
+	dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
+	hdr = (struct cam_jpeg_hw_dump_header *)dst;
+	snprintf(hdr->tag, CAM_JPEG_HW_DUMP_TAG_MAX_LEN,
+		"JPEG_REG:");
+	hdr->word_size = sizeof(uint32_t);
+	addr = (uint32_t *)(dst + sizeof(struct cam_jpeg_hw_dump_header));
+	start = addr;
+	*addr++ = soc_info->index;
+	num_reg = (hw_info->reg_dump.end_offset -
+		hw_info->reg_dump.start_offset)/4;
+	reg_start_offset = hw_info->reg_dump.start_offset;
+	for (i = 0; i < num_reg; i++) {
+		*addr++ = soc_info->mem_block[0]->start +
+			reg_start_offset + i*4;
+		*addr++ = cam_io_r(soc_info->reg_map[0].mem_base + (i*4));
+	}
+
+	mutex_unlock(&core_info->core_mutex);
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += hdr->size +
+		sizeof(struct cam_jpeg_hw_dump_header);
+	CAM_DBG(CAM_JPEG, "offset %zu", dump_args->offset);
+
+	return 0;
+}
+
 int cam_jpeg_enc_process_cmd(void *device_priv, uint32_t cmd_type,
 	void *cmd_args, uint32_t arg_size)
 {
@@ -418,6 +493,12 @@ int cam_jpeg_enc_process_cmd(void *device_priv, uint32_t cmd_type,
 		rc = 0;
 		break;
 	}
+	case CAM_JPEG_CMD_HW_DUMP:
+	{
+		rc = cam_jpeg_enc_hw_dump(jpeg_enc_dev,
+			cmd_args);
+		break;
+	}
 	default:
 		rc = -EINVAL;
 		break;

+ 7 - 1
drivers/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.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_JPEG_ENC_CORE_H
@@ -39,10 +39,16 @@ struct cam_jpeg_enc_int_status {
 	uint32_t stopdone;
 };
 
+struct cam_jpeg_enc_reg_dump {
+	uint32_t start_offset;
+	uint32_t end_offset;
+};
+
 struct cam_jpeg_enc_device_hw_info {
 	struct cam_jpeg_enc_reg_offsets reg_offset;
 	struct cam_jpeg_enc_regval reg_val;
 	struct cam_jpeg_enc_int_status int_status;
+	struct cam_jpeg_enc_reg_dump reg_dump;
 };
 
 enum cam_jpeg_enc_core_state {

+ 17 - 1
drivers/cam_lrme/cam_lrme_context.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/module.h>
@@ -86,6 +86,21 @@ static int __cam_lrme_ctx_config_dev_in_activated(struct cam_context *ctx,
 	return rc;
 }
 
+static int __cam_lrme_ctx_dump_dev_in_activated(
+	struct cam_context      *ctx,
+	struct cam_dump_req_cmd *cmd)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter ctx %d", ctx->ctx_id);
+
+	rc = cam_context_dump_dev_to_hw(ctx, cmd);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Failed to dump device");
+
+	return rc;
+}
+
 static int __cam_lrme_ctx_flush_dev_in_activated(struct cam_context *ctx,
 	struct cam_flush_dev_cmd *cmd)
 {
@@ -199,6 +214,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_lrme_ctx_release_dev_in_activated,
 			.stop_dev = __cam_lrme_ctx_stop_dev_in_activated,
 			.flush_dev = __cam_lrme_ctx_flush_dev_in_activated,
+			.dump_dev = __cam_lrme_ctx_dump_dev_in_activated,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_lrme_ctx_handle_irq_in_activated,

+ 46 - 1
drivers/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.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/module.h>
@@ -649,6 +649,50 @@ static int cam_lrme_mgr_hw_release(void *hw_mgr_priv, void *hw_release_args)
 	return rc;
 }
 
+static int cam_lrme_mgr_hw_dump(void *hw_mgr_priv, void *hw_dump_args)
+{
+	struct cam_hw_dump_args *dump_args = hw_dump_args;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_lrme_device *hw_device;
+	int rc = 0;
+	uint32_t device_index;
+	struct cam_lrme_hw_dump_args lrme_dump_args;
+
+	device_index = CAM_LRME_DECODE_DEVICE_INDEX(dump_args->ctxt_to_hw_map);
+	if (device_index >= hw_mgr->device_count) {
+		CAM_ERR(CAM_LRME, "Invalid device index %d", device_index);
+		return -EPERM;
+	}
+
+	CAM_DBG(CAM_LRME, "Start device index %d", device_index);
+
+	rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to get hw device");
+		return rc;
+	}
+	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
+		&lrme_dump_args.cpu_addr,
+		&lrme_dump_args.buf_len);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Invalid handle %u rc %d",
+			dump_args->buf_handle, rc);
+		return rc;
+	}
+	lrme_dump_args.offset =  dump_args->offset;
+	lrme_dump_args.request_id = dump_args->request_id;
+
+	rc = hw_device->hw_intf.hw_ops.process_cmd(
+		hw_device->hw_intf.hw_priv,
+		CAM_LRME_HW_CMD_DUMP,
+		&lrme_dump_args,
+		sizeof(struct cam_lrme_hw_dump_args));
+	CAM_DBG(CAM_LRME, "Offset before %zu after %zu",
+		dump_args->offset, lrme_dump_args.offset);
+	dump_args->offset = lrme_dump_args.offset;
+	return rc;
+}
+
 static int cam_lrme_mgr_hw_flush(void *hw_mgr_priv, void *hw_flush_args)
 {	int rc = 0, i;
 	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
@@ -1147,6 +1191,7 @@ int cam_lrme_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf,
 	hw_mgr_intf->hw_flush = cam_lrme_mgr_hw_flush;
 
 	g_lrme_hw_mgr.event_cb = cam_lrme_dev_buf_done_cb;
+	hw_mgr_intf->hw_dump = cam_lrme_mgr_hw_dump;
 
 	cam_lrme_mgr_create_debugfs_entry();
 

+ 162 - 0
drivers/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c

@@ -3,6 +3,7 @@
  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
+#include <linux/timer.h>
 #include "cam_lrme_hw_core.h"
 #include "cam_lrme_hw_soc.h"
 #include "cam_smmu_api.h"
@@ -21,6 +22,159 @@ static void cam_lrme_dump_registers(void __iomem *base)
 	cam_io_dump(base, 0x900, (0x928 - 0x900) / 0x4);
 }
 
+static int  cam_lrme_dump_regs_to_buf(
+	struct cam_lrme_frame_request *req,
+	struct cam_hw_info *lrme_hw,
+	struct cam_lrme_hw_dump_args *dump_args)
+{
+	int                             i;
+	uint8_t                        *dst;
+	uint32_t                       *addr, *start;
+	uint32_t                        num_reg, min_len;
+	size_t                          remain_len;
+	struct cam_hw_soc_info         *soc_info;
+	struct cam_lrme_hw_dump_header *hdr;
+
+	if (!lrme_hw || !req || !dump_args) {
+		CAM_ERR(CAM_LRME, "Invalid params %pK, %pK, %pK",
+			lrme_hw, req, dump_args);
+		return -EINVAL;
+	}
+	soc_info = &lrme_hw->soc_info;
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_LRME, "dump buffer overshoot len %zu offset %zu",
+			dump_args->buf_len, dump_args->offset);
+		return -ENOSPC;
+	}
+	remain_len = dump_args->buf_len - dump_args->offset;
+	min_len =  sizeof(struct cam_lrme_hw_dump_header) +
+		    soc_info->reg_map[0].size + sizeof(uint32_t);
+
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_LRME, "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_lrme_hw_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_LRME_HW_DUMP_TAG_MAX_LEN,
+		"LRME_REG:");
+	hdr->word_size = sizeof(uint32_t);
+	addr = (uint32_t *)(dst + sizeof(struct cam_lrme_hw_dump_header));
+	start = addr;
+	*addr++ = soc_info->index;
+	num_reg = soc_info->reg_map[0].size/4;
+	for (i = 0; i < num_reg; i++) {
+		*addr++ = soc_info->mem_block[0]->start + (i*4);
+		*addr++ = cam_io_r(soc_info->reg_map[0].mem_base + (i*4));
+	}
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += hdr->size +
+		sizeof(struct cam_lrme_hw_dump_header);
+	CAM_DBG(CAM_LRME, "offset %zu", dump_args->offset);
+	return 0;
+}
+
+static int cam_lrme_hw_dump(
+	struct cam_hw_info           *lrme_hw,
+	struct cam_lrme_hw_dump_args *dump_args)
+{
+	uint8_t                        *dst;
+	ktime_t                         cur_time;
+	size_t                          remain_len;
+	uint32_t                        min_len;
+	uint64_t                        diff;
+	uint64_t                       *addr, *start;
+	struct timespec64               cur_ts;
+	struct timespec64               req_ts;
+	struct cam_lrme_core           *lrme_core;
+	struct cam_lrme_frame_request  *req = NULL;
+	struct cam_lrme_hw_dump_header *hdr;
+
+	mutex_lock(&lrme_hw->hw_mutex);
+	if (lrme_hw->hw_state == CAM_HW_STATE_POWER_DOWN) {
+		CAM_DBG(CAM_LRME, "LRME HW is in off state");
+		mutex_unlock(&lrme_hw->hw_mutex);
+		return 0;
+	}
+
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+
+	if (lrme_core->req_submit &&
+		lrme_core->req_submit->req_id == dump_args->request_id)
+		req = lrme_core->req_submit;
+	else if (lrme_core->req_proc &&
+		lrme_core->req_proc->req_id == dump_args->request_id)
+		req = lrme_core->req_proc;
+
+	if (!req) {
+		CAM_DBG(CAM_LRME, "LRME req %lld not with hw",
+			dump_args->request_id);
+		mutex_unlock(&lrme_hw->hw_mutex);
+		return 0;
+	}
+
+	cur_time = ktime_get();
+	diff = ktime_us_delta(req->submit_timestamp, cur_time);
+	cur_ts = ktime_to_timespec64(cur_time);
+	req_ts = ktime_to_timespec64(req->submit_timestamp);
+
+	if (diff < CAM_LRME_RESPONSE_TIME_THRESHOLD) {
+		CAM_INFO(CAM_LRME, "No error req %lld %ld:%06ld %ld:%06ld",
+			dump_args->request_id,
+			req_ts.tv_sec,
+			req_ts.tv_nsec/NSEC_PER_USEC,
+			cur_ts.tv_sec,
+			cur_ts.tv_nsec/NSEC_PER_USEC);
+		mutex_unlock(&lrme_hw->hw_mutex);
+		return 0;
+	}
+
+	CAM_INFO(CAM_LRME, "Error req %lld %ld:%06ld %ld:%06ld",
+		dump_args->request_id,
+		req_ts.tv_sec,
+		req_ts.tv_nsec/NSEC_PER_USEC,
+		cur_ts.tv_sec,
+		cur_ts.tv_nsec/NSEC_PER_USEC);
+
+	if (dump_args->buf_len <= dump_args->offset) {
+		CAM_WARN(CAM_LRME, "dump buffer overshoot len %zu offset %zu",
+			dump_args->buf_len, dump_args->offset);
+		mutex_unlock(&lrme_hw->hw_mutex);
+		return 0;
+	}
+
+	remain_len = dump_args->buf_len - dump_args->offset;
+	min_len =  sizeof(struct cam_lrme_hw_dump_header) +
+		    (CAM_LRME_HW_DUMP_NUM_WORDS * sizeof(uint64_t));
+
+	if (remain_len < min_len) {
+		CAM_WARN(CAM_LRME, "dump buffer exhaust remain %zu min %u",
+			remain_len, min_len);
+		mutex_unlock(&lrme_hw->hw_mutex);
+		return 0;
+	}
+
+	dst = (uint8_t *)dump_args->cpu_addr + dump_args->offset;
+	hdr = (struct cam_lrme_hw_dump_header *)dst;
+	scnprintf(hdr->tag, CAM_LRME_HW_DUMP_TAG_MAX_LEN,
+		"LRME_REQ:");
+	hdr->word_size = sizeof(uint64_t);
+	addr = (uint64_t *)(dst + sizeof(struct cam_lrme_hw_dump_header));
+	start = addr;
+	*addr++ = req->req_id;
+	*addr++ = req_ts.tv_sec;
+	*addr++ = req_ts.tv_nsec/NSEC_PER_USEC;
+	*addr++ = cur_ts.tv_sec;
+	*addr++ = cur_ts.tv_nsec/NSEC_PER_USEC;
+	hdr->size = hdr->word_size * (addr - start);
+	dump_args->offset += hdr->size +
+		sizeof(struct cam_lrme_hw_dump_header);
+	cam_lrme_dump_regs_to_buf(req, lrme_hw, dump_args);
+	mutex_unlock(&lrme_hw->hw_mutex);
+	return 0;
+}
+
 static void cam_lrme_cdm_write_reg_val_pair(uint32_t *buffer,
 	uint32_t *index, uint32_t reg_offset, uint32_t reg_value)
 {
@@ -959,6 +1113,8 @@ int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args,
 		goto error;
 	}
 
+	frame_req->submit_timestamp = ktime_get();
+
 	switch (lrme_core->state) {
 	case CAM_LRME_CORE_STATE_PROCESSING:
 		lrme_core->state = CAM_LRME_CORE_STATE_REQ_PROC_PEND;
@@ -1268,6 +1424,12 @@ int cam_lrme_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
 		break;
 	}
 
+	case CAM_LRME_HW_CMD_DUMP: {
+		struct cam_lrme_hw_dump_args *dump_args =
+		   (struct cam_lrme_hw_dump_args *)cmd_args;
+		rc = cam_lrme_hw_dump(lrme_hw, dump_args);
+		break;
+		}
 	default:
 		break;
 	}

+ 19 - 1
drivers/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_LRME_HW_CORE_H_
@@ -35,6 +35,10 @@
 
 #define CAM_LRME_MAX_REG_PAIR_NUM 60
 
+#define CAM_LRME_RESPONSE_TIME_THRESHOLD 100000
+#define CAM_LRME_HW_DUMP_TAG_MAX_LEN     32
+#define CAM_LRME_HW_DUMP_NUM_WORDS       5
+
 /**
  * enum cam_lrme_irq_set
  *
@@ -432,6 +436,20 @@ struct cam_lrme_hw_info {
 	struct cam_lrme_titan_reg titan_reg;
 };
 
+/**
+ * struct cam_lrme_hw_dump_header : LRME hw dump header
+ *
+ * @tag       : LRME hw dump header tag
+ * @size      : Size of data
+ * @word_size : size of each word
+ */
+
+struct cam_lrme_hw_dump_header {
+	uint8_t     tag[CAM_LRME_HW_DUMP_TAG_MAX_LEN];
+	uint64_t    size;
+	uint32_t    word_size;
+};
+
 int cam_lrme_hw_process_irq(void *priv, void *data);
 int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args,
 	uint32_t arg_size);

+ 22 - 1
drivers/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.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_LRME_HW_INTF_H_
@@ -59,12 +59,14 @@ enum cam_lrme_cb_type {
  * @CAM_LRME_HW_CMD_REGISTER_CB       : register HW manager callback
  * @CAM_LRME_HW_CMD_SUBMIT            : Submit frame to HW
  * @CAM_LRME_HW_CMD_DUMP_REGISTER     : dump register values
+ * @CAM_LRME_HW_CMD_DUMP              : dump register values to buffer
  */
 enum cam_lrme_hw_cmd_type {
 	CAM_LRME_HW_CMD_PREPARE_HW_UPDATE,
 	CAM_LRME_HW_CMD_REGISTER_CB,
 	CAM_LRME_HW_CMD_SUBMIT,
 	CAM_LRME_HW_CMD_DUMP_REGISTER,
+	CAM_LRME_HW_CMD_DUMP,
 };
 
 /**
@@ -87,6 +89,7 @@ enum cam_lrme_hw_reset_type {
  * @hw_device             : Pointer to HW device
  * @hw_update_entries     : List of hw_update_entries
  * @num_hw_update_entries : number of hw_update_entries
+ * @submit_timestamp      : timestamp of submitting request with hw
  */
 struct cam_lrme_frame_request {
 	struct list_head           frame_list;
@@ -95,6 +98,7 @@ struct cam_lrme_frame_request {
 	struct cam_lrme_device    *hw_device;
 	struct cam_hw_update_entry hw_update_entries[CAM_LRME_MAX_HW_ENTRIES];
 	uint32_t                   num_hw_update_entries;
+	ktime_t                    submit_timestamp;
 };
 
 /**
@@ -192,6 +196,21 @@ struct cam_lrme_hw_submit_args {
 	struct cam_lrme_frame_request *frame_req;
 };
 
+/**
+ * struct cam_lrme_hw_dump_args : Args for dump request
+ *
+ * @request_id   : Issue request id
+ * @cpu_addr     : start address of the target buffer
+ * @offset       : offset of the buffer
+ * @buf_len      : Length of target buffer
+ */
+struct cam_lrme_hw_dump_args {
+	uint64_t  request_id;
+	uintptr_t cpu_addr;
+	size_t    offset;
+	size_t    buf_len;
+};
+
 /**
  * @brief : API to register LRME hw to platform framework.
  * @return struct platform_device pointer on on success, or ERR_PTR() on error.
@@ -202,4 +221,6 @@ int cam_lrme_hw_init_module(void);
  * @brief : API to remove LRME Hw from platform framework.
  */
 void cam_lrme_hw_exit_module(void);
+
 #endif /* _CAM_LRME_HW_INTF_H_ */
+