Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a9861ae authored by Gaurav Jindal's avatar Gaurav Jindal
Browse files

msm: camera: tfe: LDAR dump for TFE



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 TFE, CSID registers, LUT tables and context
information, cmd buffers, timestamps information for
submit, apply, RUP, epoch and buffdones of the last 20
requests.

Change-Id: I9a5decdf88d94e5d05b8c5a14e9f301f42adaa20
CRs-Fixed: 2612116
Signed-off-by: default avatarGaurav Jindal <gjindal@codeaurora.org>
parent c7d67b0c
Loading
Loading
Loading
Loading
+166 −7
Original line number Diff line number Diff line
@@ -118,7 +118,9 @@ static int cam_tfe_mgr_regspace_data_cb(uint32_t reg_base_type,

static int cam_tfe_mgr_handle_reg_dump(struct cam_tfe_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 = -EINVAL, i;

@@ -142,8 +144,8 @@ static int cam_tfe_mgr_handle_reg_dump(struct cam_tfe_hw_mgr_ctx *ctx,
				&reg_dump_buf_desc[i],
				ctx->applied_req_id,
				cam_tfe_mgr_regspace_data_cb,
				NULL,
				false);
				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",
@@ -153,7 +155,7 @@ static int cam_tfe_mgr_handle_reg_dump(struct cam_tfe_hw_mgr_ctx *ctx,
		}
	}

	return 0;
	return rc;
}

static int cam_tfe_mgr_get_hw_caps(void *hw_mgr_priv,
@@ -1719,7 +1721,8 @@ void cam_tfe_cam_cdm_callback(uint32_t handle, void *userdata,
			cam_tfe_mgr_handle_reg_dump(ctx,
				hw_update_data->reg_dump_buf_desc,
				hw_update_data->num_reg_dump_buf,
				CAM_ISP_TFE_PACKET_META_REG_DUMP_PER_REQUEST);
				CAM_ISP_TFE_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",
			 handle, userdata, status, cookie, ctx->ctx_index);
@@ -2979,6 +2982,159 @@ static int cam_tfe_mgr_write(void *hw_mgr_priv, void *write_args)
	return -EPERM;
}

static int cam_tfe_mgr_user_dump_hw(
		struct cam_tfe_hw_mgr_ctx *tfe_ctx,
		struct cam_hw_dump_args *dump_args)
{
	int rc = 0;
	struct cam_hw_soc_dump_args soc_dump_args;

	if (!tfe_ctx || !dump_args) {
		CAM_ERR(CAM_ISP, "Invalid parameters %pK %pK",
			tfe_ctx, dump_args);
		return -EINVAL;
	}
	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_tfe_mgr_handle_reg_dump(tfe_ctx,
		tfe_ctx->reg_dump_buf_desc,
		tfe_ctx->num_reg_dump_buf,
		CAM_ISP_PACKET_META_REG_DUMP_ON_ERROR,
		&soc_dump_args,
		true);
	if (rc) {
		CAM_DBG(CAM_ISP,
			"Dump failed req: %lld handle %u offset %u rc %d",
			dump_args->request_id,
			dump_args->buf_handle,
			dump_args->offset,
			rc);
		return rc;
	}
	dump_args->offset = soc_dump_args.offset;
	return rc;
}

static int cam_tfe_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_tfe_hw_mgr_ctx *tfe_ctx = (struct cam_tfe_hw_mgr_ctx *)
						dump_args->ctxt_to_hw_map;
	int i;
	int rc = 0;

	/* for some targets, information about the TFE 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 dump to this buffer falis due to any reason, fallback to dump
	 * to the LDAR buffer
	 */
	isp_hw_dump_args.is_dump_all = true;
	if (tfe_ctx->num_reg_dump_buf) {
		rc = cam_tfe_mgr_user_dump_hw(tfe_ctx, dump_args);
		if (!rc)
			isp_hw_dump_args.is_dump_all = false;
	}

	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, &tfe_ctx->res_list_tfe_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_TFE_CSID_PATH_RES_RDI_0:
			case CAM_TFE_CSID_PATH_RES_RDI_1:
			case CAM_TFE_CSID_PATH_RES_RDI_2:
				if (tfe_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_TFE_CSID_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, &tfe_ctx->res_list_tfe_in, 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_ISP_HW_TFE_IN_RDI0:
			case CAM_ISP_HW_TFE_IN_RDI1:
			case CAM_ISP_HW_TFE_IN_RDI2:
				if (tfe_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_TFE_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;
	CAM_DBG(CAM_ISP, "offset %u", dump_args->offset);
	return rc;
}

static int cam_tfe_mgr_reset(void *hw_mgr_priv, void *hw_reset_args)
{
	struct cam_tfe_hw_mgr            *hw_mgr = hw_mgr_priv;
@@ -4226,7 +4382,8 @@ static int cam_tfe_mgr_cmd(void *hw_mgr_priv, void *cmd_args)

		rc = cam_tfe_mgr_handle_reg_dump(ctx, ctx->reg_dump_buf_desc,
			ctx->num_reg_dump_buf,
			CAM_ISP_TFE_PACKET_META_REG_DUMP_ON_FLUSH);
			CAM_ISP_TFE_PACKET_META_REG_DUMP_ON_FLUSH,
			NULL, false);
		if (rc) {
			CAM_ERR(CAM_ISP,
				"Reg dump on flush failed req id: %llu num_reg_dump:0x%x rc: %d",
@@ -4242,7 +4399,8 @@ static int cam_tfe_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
		ctx->last_dump_err_req_id = ctx->applied_req_id;
		rc = cam_tfe_mgr_handle_reg_dump(ctx, ctx->reg_dump_buf_desc,
			ctx->num_reg_dump_buf,
			CAM_ISP_TFE_PACKET_META_REG_DUMP_ON_ERROR);
			CAM_ISP_TFE_PACKET_META_REG_DUMP_ON_ERROR,
			NULL, false);
		if (rc) {
			CAM_ERR(CAM_ISP,
				"Reg dump on error failed req id:%llu num_reg_dump:0x%x rc: %d",
@@ -5309,6 +5467,7 @@ int cam_tfe_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, int *iommu_hdl)
	hw_mgr_intf->hw_config = cam_tfe_mgr_config_hw;
	hw_mgr_intf->hw_cmd = cam_tfe_mgr_cmd;
	hw_mgr_intf->hw_reset = cam_tfe_mgr_reset;
	hw_mgr_intf->hw_dump = cam_tfe_mgr_dump;

	if (iommu_hdl)
		*iommu_hdl = g_tfe_hw_mgr.mgr_common.img_iommu_hdl;
+2 −0
Original line number Diff line number Diff line
@@ -267,6 +267,7 @@ struct cam_isp_hw_dual_isp_update_args {
 * @ buf_len:        buf len
 * @ offset:         offset of buffer
 * @ ctxt_to_hw_map: ctx to hw map
 * @ is_dump_all:    flag to indicate if all information or just bw/clk rate
 */
struct cam_isp_hw_dump_args {
	uint64_t                req_id;
@@ -274,6 +275,7 @@ struct cam_isp_hw_dump_args {
	size_t                  buf_len;
	size_t                  offset;
	void                   *ctxt_to_hw_map;
	bool                    is_dump_all;
};

/**
+103 −0
Original line number Diff line number Diff line
@@ -2301,6 +2301,106 @@ static int cam_tfe_csid_get_regdump(struct cam_tfe_csid_hw *csid_hw,
	return 0;
}

static int cam_tfe_csid_dump_hw(
	struct cam_tfe_csid_hw *csid_hw, void *cmd_args)
{
	int                             i;
	uint8_t                        *dst;
	uint32_t                       *addr, *start;
	uint64_t                       *clk_addr, *clk_start;
	uint32_t                        min_len;
	uint32_t                        num_reg;
	uint32_t                        reg_size = 0;
	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;
	}

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

	soc_info = &csid_hw->hw_info->soc_info;
	if (dump_args->is_dump_all)
		reg_size = soc_info->reg_map[0].size;

	min_len = reg_size +
		sizeof(struct cam_isp_hw_dump_header) +
		(sizeof(uint32_t) * CAM_TFE_CSID_DUMP_MISC_NUM_WORDS);
	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;
	}

	mutex_lock(&csid_hw->hw_info->hw_mutex);
	if (csid_hw->hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
		CAM_ERR(CAM_ISP, "CSID:%d Invalid HW State:%d",
			csid_hw->hw_intf->hw_idx,
			csid_hw->hw_info->hw_state);
		mutex_unlock(&csid_hw->hw_info->hw_mutex);
		return -EINVAL;
	}

	if (!dump_args->is_dump_all)
		goto dump_bw;

	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);
dump_bw:
	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, "CSID_CLK_RATE:");
	clk_addr = (uint64_t *)(dst +
		sizeof(struct cam_isp_hw_dump_header));
	clk_start = clk_addr;
	hdr->word_size = sizeof(uint64_t);
	*clk_addr++ = csid_hw->clk_rate;
	hdr->size = hdr->word_size * (clk_addr - clk_start);
	dump_args->offset +=  hdr->size +
		sizeof(struct cam_isp_hw_dump_header);
	CAM_DBG(CAM_ISP, "offset %zu", dump_args->offset);
	mutex_unlock(&csid_hw->hw_info->hw_mutex);
	return 0;
}

static int cam_tfe_csid_process_cmd(void *hw_priv,
	uint32_t cmd_type, void *cmd_args, uint32_t arg_size)
{
@@ -2332,6 +2432,9 @@ static int cam_tfe_csid_process_cmd(void *hw_priv,
	case CAM_TFE_CSID_CMD_GET_REG_DUMP:
		rc = cam_tfe_csid_get_regdump(csid_hw, cmd_args);
		break;
	case CAM_ISP_HW_CMD_DUMP_HW:
		rc = cam_tfe_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);
+6 −0
Original line number Diff line number Diff line
@@ -12,6 +12,12 @@

#define CAM_TFE_CSID_CID_MAX                          4

/* Each word is taken as uint32_t, for dumping uint64_t count as 2 words
 * 1. soc_index
 * 2. clk_rate --> uint64_t -> 2 words
 */
#define CAM_TFE_CSID_DUMP_MISC_NUM_WORDS              3

#define TFE_CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED    BIT(0)
#define TFE_CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED    BIT(1)
#define TFE_CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED    BIT(2)
+232 −0
Original line number Diff line number Diff line
@@ -1490,6 +1490,234 @@ static int cam_tfe_top_get_reg_dump(
	return 0;
}

static int cam_tfe_hw_dump(
	struct cam_tfe_hw_core_info *core_info,
	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                           num_lut_dump_entries = 0;
	uint32_t                           num_reg;
	uint32_t                           lut_word_size, lut_size;
	uint32_t                           lut_bank_sel, lut_dmi_reg;
	uint32_t                           val;
	void __iomem                      *reg_base;
	void __iomem                      *mem_base;
	uint32_t                          *addr, *start;
	uint64_t                          *clk_waddr, *clk_wstart;
	size_t                             remain_len;
	uint32_t                           min_len;
	struct cam_hw_info                *tfe_hw_info;
	struct cam_hw_soc_info            *soc_info;
	struct cam_tfe_top_priv           *top_priv;
	struct cam_tfe_soc_private        *soc_private;
	struct cam_tfe_reg_dump_data      *reg_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 || !core_info) {
		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;
	}

	top_priv = (struct cam_tfe_top_priv  *)core_info->top_priv;
	tfe_hw_info =
		(struct cam_hw_info *)(top_priv->common_data.hw_intf->hw_priv);
	reg_dump_data = top_priv->common_data.reg_dump_data;
	soc_info = top_priv->common_data.soc_info;
	soc_private = top_priv->common_data.soc_info->soc_private;
	mem_base = soc_info->reg_map[TFE_CORE_BASE_IDX].mem_base;

	if (dump_args->is_dump_all) {

		/*Dump registers size*/
		for (i = 0; i < reg_dump_data->num_reg_dump_entries; i++)
			reg_dump_size +=
				(reg_dump_data->reg_entry[i].end_offset -
				reg_dump_data->reg_entry[i].start_offset);

		/*
		 * We dump the offset as well, so the total size dumped becomes
		 * multiplied by 2
		 */
		reg_dump_size *= 2;

		/* LUT dump size */
		for (i = 0; i < reg_dump_data->num_lut_dump_entries; i++)
			lut_dump_size +=
				((reg_dump_data->lut_entry[i].lut_addr_size) *
				(reg_dump_data->lut_entry[i].lut_word_size/8));

		num_lut_dump_entries = reg_dump_data->num_lut_dump_entries;
	}

	/*Minimum len comprises of:
	 * lut_dump_size + reg_dump_size + sizeof dump_header +
	 * (num_lut_dump_entries--> represents number of banks) +
	 *  (misc number of words) * sizeof(uint32_t)
	 */
	min_len = lut_dump_size + reg_dump_size +
		sizeof(struct cam_isp_hw_dump_header) +
		(num_lut_dump_entries * sizeof(uint32_t)) +
		(sizeof(uint32_t) * CAM_TFE_CORE_DUMP_MISC_NUM_WORDS);

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

	mutex_lock(&tfe_hw_info->hw_mutex);
	if (tfe_hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
		CAM_ERR(CAM_ISP, "TFE:%d HW not powered up",
			core_info->core_index);
		mutex_unlock(&tfe_hw_info->hw_mutex);
		return -EPERM;
	}

	if (!dump_args->is_dump_all)
		goto dump_bw;

	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, "TFE_REG:");
	addr = (uint32_t *)(dst + sizeof(struct cam_isp_hw_dump_header));
	start = addr;
	*addr++ = soc_info->index;
	for (i = 0; i < reg_dump_data->num_reg_dump_entries; i++) {
		num_reg  = (reg_dump_data->reg_entry[i].end_offset -
			reg_dump_data->reg_entry[i].start_offset)/4;
		reg_start_offset = reg_dump_data->reg_entry[i].start_offset;
		reg_base = mem_base + reg_start_offset;
		for (j = 0; j < num_reg; j++) {
			addr[0] =
				soc_info->mem_block[TFE_CORE_BASE_IDX]->start +
				reg_start_offset + (j*4);
			addr[1] = cam_io_r(reg_base + (j*4));
			addr += 2;
		}
	}

	/*Dump bus top registers*/
	num_reg  = (reg_dump_data->bus_write_top_end_addr -
			reg_dump_data->bus_start_addr)/4;
	reg_base = mem_base + reg_dump_data->bus_start_addr;
	reg_start_offset = soc_info->mem_block[TFE_CORE_BASE_IDX]->start +
		reg_dump_data->bus_start_addr;
	for (i = 0; i < num_reg; i++) {
		addr[0] = reg_start_offset + (i*4);
		addr[1] = cam_io_r(reg_base + (i*4));
		addr += 2;
	}

	/* Dump bus clients */
	reg_base = mem_base + reg_dump_data->bus_client_start_addr;
	reg_start_offset = soc_info->mem_block[TFE_CORE_BASE_IDX]->start +
		reg_dump_data->bus_client_start_addr;
	for (j = 0; j < reg_dump_data->num_bus_clients; j++) {

		for (i = 0; i <= 0x3c; i += 4) {
			addr[0] = reg_start_offset + i;
			addr[1] = cam_io_r(reg_base + i);
			addr += 2;
		}
		for (i = 0x60; i <= 0x80; i += 4) {
			addr[0] = reg_start_offset + (i*4);
			addr[1] = cam_io_r(reg_base + (i*4));
			addr += 2;
		}
		reg_base += reg_dump_data->bus_client_offset;
		reg_start_offset += reg_dump_data->bus_client_offset;
	}

	hdr->size = hdr->word_size * (addr - start);
	dump_args->offset +=  hdr->size +
		sizeof(struct cam_isp_hw_dump_header);

	/* Dump LUT entries */
	for (i = 0; i < reg_dump_data->num_lut_dump_entries; i++) {

		lut_bank_sel = reg_dump_data->lut_entry[i].lut_bank_sel;
		lut_size = reg_dump_data->lut_entry[i].lut_addr_size;
		lut_word_size = reg_dump_data->lut_entry[i].lut_word_size;
		lut_dmi_reg = reg_dump_data->lut_entry[i].dmi_reg_offset;
		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 = lut_word_size/8;
		addr = (uint32_t *)(dst +
			sizeof(struct cam_isp_hw_dump_header));
		start = addr;
		*addr++ = lut_bank_sel;
		cam_io_w_mb(lut_bank_sel, mem_base + lut_dmi_reg + 4);
		cam_io_w_mb(0, mem_base + 0xC28);
		for (j = 0; j < lut_size; j++) {
			*addr = cam_io_r_mb(mem_base + 0xc30);
			addr++;
		}
		hdr->size = hdr->word_size * (addr - start);
		dump_args->offset +=  hdr->size +
			sizeof(struct cam_isp_hw_dump_header);
	}
	cam_io_w_mb(0, mem_base + 0xC24);
	cam_io_w_mb(0, mem_base + 0xC28);

dump_bw:
	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, "TFE_CLK_RATE_BW:");
	clk_waddr = (uint64_t *)(dst +
		sizeof(struct cam_isp_hw_dump_header));
	clk_wstart = clk_waddr;
	hdr->word_size = sizeof(uint64_t);
	*clk_waddr++ = top_priv->hw_clk_rate;
	*clk_waddr++ = top_priv->total_bw_applied;

	hdr->size = hdr->word_size * (clk_waddr - clk_wstart);
	dump_args->offset +=  hdr->size +
		sizeof(struct cam_isp_hw_dump_header);

	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, "TFE_NIU_MAXWR:");
	addr = (uint32_t *)(dst +
		sizeof(struct cam_isp_hw_dump_header));
	start = addr;
	hdr->word_size = sizeof(uint32_t);
	cam_cpas_reg_read(soc_private->cpas_handle,
		CAM_CPAS_REG_CAMNOC, 0x20, true, &val);
	*addr++ = val;
	hdr->size = hdr->word_size * (addr - start);
	dump_args->offset +=  hdr->size +
		sizeof(struct cam_isp_hw_dump_header);
	mutex_unlock(&tfe_hw_info->hw_mutex);

	CAM_DBG(CAM_ISP, "offset %zu", dump_args->offset);
	return 0;
}

static int cam_tfe_camif_irq_reg_dump(
	struct cam_tfe_hw_core_info    *core_info,
	void *cmd_args, uint32_t arg_size)
@@ -2570,6 +2798,10 @@ int cam_tfe_process_cmd(void *hw_priv, uint32_t cmd_type,
	case CAM_ISP_HW_CMD_QUERY_REGSPACE_DATA:
		*((struct cam_hw_soc_info **)cmd_args) = soc_info;
		break;
	case CAM_ISP_HW_CMD_DUMP_HW:
		rc = cam_tfe_hw_dump(core_info,
			cmd_args, arg_size);
		break;
	case CAM_ISP_HW_CMD_GET_BUF_UPDATE:
	case CAM_ISP_HW_CMD_GET_HFR_UPDATE:
	case CAM_ISP_HW_CMD_STRIPE_UPDATE:
Loading