Преглед изворни кода

msm: camera: isp: Isp irq injection framework

This change constructs a framework of ISP IRQ injection which
adds support for HW err IRQ simulation.

CRs-Fixed: 3430944
Change-Id: I9552d0aa8b4672bfa04bfc78714d87b72140ad9a
Signed-off-by: Stark Lin <[email protected]>
Stark Lin пре 1 година
родитељ
комит
c0377f2bff
23 измењених фајлова са 1525 додато и 49 уклоњено
  1. 703 0
      drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
  2. 36 0
      drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
  3. 11 3
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid780.h
  4. 7 1
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid880.h
  5. 2 1
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid980.h
  6. 160 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_hw_ver2.c
  7. 2 1
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_hw_ver2.h
  8. 6 3
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite780.h
  9. 8 2
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite880.h
  10. 3 1
      drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite980.h
  11. 62 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
  12. 44 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/cam_sfe780.h
  13. 44 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/cam_sfe880.h
  14. 4 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/cam_sfe_core.c
  15. 82 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_rd.c
  16. 14 1
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_rd.h
  17. 100 20
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_wr.c
  18. 13 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_wr.h
  19. 4 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
  20. 45 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe780.h
  21. 45 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe880.h
  22. 115 16
      drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver3.c
  23. 15 0
      drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver3.h

+ 703 - 0
drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c

@@ -44,6 +44,33 @@
 
 #define MAX_INTERNAL_RECOVERY_ATTEMPTS    1
 
+#define MAX_PARAMS_FOR_IRQ_INJECT     5
+#define IRQ_INJECT_DISPLAY_BUF_LEN    4096
+
+
+typedef int (*cam_isp_irq_inject_cmd_parse_handler)(
+	struct cam_isp_irq_inject_param *irq_inject_param,
+	uint32_t param_index, char *token, bool *is_query);
+
+#define IRQ_INJECT_USAGE_STRING                                                         \
+	"######################################################\n"                          \
+	"Usage:\n"                                                                          \
+	"$INJECT_NODE : /sys/kernel/debug/camera/ife/isp_irq_inject\n\n"                    \
+	"  - cat $INJECT_NODE\n"                                                            \
+	"    print Usage, injected params and current active HW info.\n"                    \
+	"    Also we need to cat the node to get output info after echo params to node.\n\n"\
+	"  - echo ?:?:?:? > $INJECT_NODE\n"                                                 \
+	"    print query info, entering '?' to any param besides req_id to query.\n\n"      \
+	"  - echo hw_type:hw_idx:res_id:irq_mask:req_id > $INJECT_NODE\n"                   \
+	"    hw_type  : Hw to inject IRQ\n"                                                 \
+	"    hw_idx   : Index of the selected hw\n"                                         \
+	"    reg_unit : Register to set irq\n"                                              \
+	"    irq_mask : IRQ to be triggered\n"                                              \
+	"    req_id   : Req to trigger the IRQ, entering 'now' to this param will trigger " \
+	"irq immediately\n\n"                                                               \
+	"Up to 10 sets of inject params are supported.\n"                                   \
+	"######################################################\n"
+
 #define CAM_ISP_NON_RECOVERABLE_CSID_ERRORS          \
 	(CAM_ISP_HW_ERROR_CSID_LANE_FIFO_OVERFLOW    |   \
 	 CAM_ISP_HW_ERROR_CSID_PKT_HDR_CORRUPTED     |   \
@@ -75,6 +102,7 @@ static struct cam_ife_hw_mgr g_ife_hw_mgr;
 static uint32_t g_num_ife_available, g_num_ife_lite_available, g_num_sfe_available;
 static uint32_t g_num_ife_functional, g_num_ife_lite_functional, g_num_sfe_functional;
 static uint32_t max_ife_out_res, max_sfe_out_res;
+static char irq_inject_display_buf[IRQ_INJECT_DISPLAY_BUF_LEN];
 
 static int cam_ife_mgr_find_core_idx(int split_id, struct cam_ife_hw_mgr_ctx *ctx,
 	enum cam_isp_hw_type hw_type, uint32_t *core_idx);
@@ -6644,6 +6672,206 @@ static void cam_ife_mgr_send_frame_event(uint64_t request_id, uint32_t ctx_index
 	}
 }
 
+static void cam_isp_irq_inject_clear_params(
+	struct cam_isp_irq_inject_param *param)
+{
+	param->hw_type  = -1;
+	param->hw_idx   = -1;
+	param->reg_unit = -1;
+	param->irq_mask = -1;
+	param->req_id   = 0;
+	param->is_valid = false;
+	memset(param->line_buf, '\0', LINE_BUFFER_LEN);
+}
+
+static int cam_ife_hw_mgr_sfe_irq_inject_or_dump_desc(
+	struct cam_ife_hw_mgr *hw_mgr,
+	struct cam_isp_irq_inject_param *params,
+	bool dump_irq_desc)
+{
+	int i, rc = 0, offset = 0;
+	char *line_buf = NULL;
+	struct cam_hw_intf *hw_intf = NULL;
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	for (i = 0; i < CAM_SFE_HW_NUM_MAX; i++) {
+		if ((!hw_mgr->sfe_devices[i]) ||
+			(hw_mgr->sfe_devices[i]->hw_intf->hw_idx != params->hw_idx))
+			continue;
+
+		hw_intf = hw_mgr->sfe_devices[i]->hw_intf;
+
+		if (dump_irq_desc) {
+			rc = hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+				CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION, params,
+				sizeof(struct cam_isp_irq_inject_param));
+			goto clear_param;
+		}
+
+		rc = hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+			CAM_ISP_HW_CMD_IRQ_INJECTION, params,
+			sizeof(struct cam_isp_irq_inject_param));
+		if (rc)
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Injecting IRQ %x failed for SFE at req: %d\n",
+				params->irq_mask, params->req_id);
+		else
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"IRQ %#x injected for SFE at req: %d\n",
+				params->irq_mask, params->req_id);
+		break;
+	}
+
+clear_param:
+	strlcat(irq_inject_display_buf, params->line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	/* Clear the param injected */
+	cam_isp_irq_inject_clear_params(params);
+	kfree(line_buf);
+	return rc;
+}
+
+static int cam_ife_hw_mgr_vfe_irq_inject_or_dump_desc(
+	struct cam_ife_hw_mgr *hw_mgr,
+	struct cam_isp_irq_inject_param *params,
+	bool dump_irq_desc)
+{
+	int i, rc = 0, offset = 0;
+	char *line_buf = NULL;
+	struct cam_hw_intf *hw_intf = NULL;
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) {
+		if ((!hw_mgr->ife_devices[i]) ||
+			(hw_mgr->ife_devices[i]->hw_intf->hw_idx != params->hw_idx))
+			continue;
+
+		hw_intf = hw_mgr->ife_devices[i]->hw_intf;
+
+		if (dump_irq_desc) {
+			rc = hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+				CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION, params,
+				sizeof(struct cam_isp_irq_inject_param));
+			goto clear_param;
+		}
+
+		rc = hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+			CAM_ISP_HW_CMD_IRQ_INJECTION, params,
+			sizeof(struct cam_isp_irq_inject_param));
+		if (rc)
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Injecting IRQ %x failed for IFE at req: %d\n",
+				params->irq_mask, params->req_id);
+		else
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"IRQ %#x injected for IFE at req: %d\n",
+				params->irq_mask, params->req_id);
+		break;
+	}
+
+clear_param:
+	strlcat(irq_inject_display_buf, params->line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	/* Clear the param injected */
+	cam_isp_irq_inject_clear_params(params);
+	kfree(line_buf);
+	return rc;
+}
+
+static int cam_ife_hw_mgr_csid_irq_inject_or_dump_desc(
+	struct cam_ife_hw_mgr *hw_mgr,
+	struct cam_isp_irq_inject_param *params,
+	bool dump_irq_desc)
+{
+	int i, rc = 0, offset = 0;
+	char *line_buf = NULL;
+	struct cam_hw_intf *hw_intf = NULL;
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
+		if ((!hw_mgr->csid_devices[i]) ||
+			(hw_mgr->csid_devices[i]->hw_idx != params->hw_idx))
+			continue;
+
+		hw_intf = hw_mgr->csid_devices[i];
+
+		if (dump_irq_desc) {
+			rc = hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+				CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION, params,
+				sizeof(struct cam_isp_irq_inject_param));
+			goto clear_param;
+		}
+
+		rc = hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+			CAM_ISP_HW_CMD_IRQ_INJECTION, params,
+			sizeof(struct cam_isp_irq_inject_param));
+		if (rc)
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Injecting IRQ %x failed for CSID at req: %d\n",
+				params->irq_mask, params->req_id);
+		else
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"IRQ %#x injected for CSID at req: %d\n",
+				params->irq_mask, params->req_id);
+		break;
+	}
+
+clear_param:
+	strlcat(irq_inject_display_buf, params->line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	/* Clear the param injected */
+	cam_isp_irq_inject_clear_params(params);
+	kfree(line_buf);
+	return rc;
+}
+
+static int cam_ife_hw_mgr_irq_injection(struct cam_ife_hw_mgr *hw_mgr,
+	uint64_t request_id)
+{
+	int i, rc = 0;
+
+	for (i = 0; i < MAX_INJECT_SET; i++) {
+		if ((!hw_mgr->irq_inject_param[i].is_valid) ||
+			((hw_mgr->irq_inject_param[i].req_id != request_id) &&
+			(hw_mgr->irq_inject_param[i].req_id != 0xFFFFFFFF)))
+			continue;
+
+		switch (hw_mgr->irq_inject_param[i].hw_type) {
+		case CAM_ISP_HW_TYPE_CSID:
+			rc = cam_ife_hw_mgr_csid_irq_inject_or_dump_desc(
+				hw_mgr, &hw_mgr->irq_inject_param[i], false);
+			break;
+		case CAM_ISP_HW_TYPE_VFE:
+			rc = cam_ife_hw_mgr_vfe_irq_inject_or_dump_desc(
+				hw_mgr, &hw_mgr->irq_inject_param[i], false);
+			break;
+		case CAM_ISP_HW_TYPE_SFE:
+			rc = cam_ife_hw_mgr_sfe_irq_inject_or_dump_desc(
+				hw_mgr, &hw_mgr->irq_inject_param[i], false);
+			break;
+		default:
+			strlcat(irq_inject_display_buf, "No matched HW_TYPE\n",
+				IRQ_INJECT_DISPLAY_BUF_LEN);
+			rc = -EINVAL;
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
 /* entry function: config_hw */
 static int cam_ife_mgr_config_hw(void *hw_mgr_priv,
 					void *config_hw_args)
@@ -6654,6 +6882,7 @@ static int cam_ife_mgr_config_hw(void *hw_mgr_priv,
 	struct cam_cdm_bl_request *cdm_cmd;
 	struct cam_ife_hw_mgr_ctx *ctx;
 	struct cam_isp_prepare_hw_update_data *hw_update_data;
+	struct cam_ife_hw_mgr *ife_hw_mgr;
 	unsigned long rem_jiffies = 0;
 	bool is_cdm_hung = false;
 
@@ -6663,6 +6892,7 @@ static int cam_ife_mgr_config_hw(void *hw_mgr_priv,
 			hw_mgr_priv, config_hw_args);
 		return -EINVAL;
 	}
+	ife_hw_mgr = (struct cam_ife_hw_mgr *)hw_mgr_priv;
 
 	cfg = config_hw_args;
 	ctx = (struct cam_ife_hw_mgr_ctx *)cfg->ctxt_to_hw_map;
@@ -6699,6 +6929,11 @@ static int cam_ife_mgr_config_hw(void *hw_mgr_priv,
 			ctx, ctx->ctx_index, cfg->request_id);
 	}
 
+	rc = cam_ife_hw_mgr_irq_injection(ife_hw_mgr, cfg->request_id);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Failed to inject IRQ at req %d",
+			cfg->request_id);
+
 	hw_update_data = (struct cam_isp_prepare_hw_update_data  *) cfg->priv;
 	hw_update_data->isp_mgr_ctx = ctx;
 	ctx->cdm_userdata.request_id = cfg->request_id;
@@ -15477,6 +15712,469 @@ DEFINE_DEBUGFS_ATTRIBUTE(cam_ife_csid_testbus_debug,
 	cam_ife_get_csid_testbus_debug,
 	cam_ife_set_csid_testbus_debug, "%16llu");
 
+static int cam_ife_hw_mgr_dump_irq_desc(struct cam_ife_hw_mgr *hw_mgr,
+	struct cam_isp_irq_inject_param *param)
+{
+	int rc = 0;
+
+	switch (param->hw_type) {
+	case CAM_ISP_HW_TYPE_CSID:
+		rc = cam_ife_hw_mgr_csid_irq_inject_or_dump_desc(
+			hw_mgr, param, true);
+		break;
+	case CAM_ISP_HW_TYPE_VFE:
+		rc = cam_ife_hw_mgr_vfe_irq_inject_or_dump_desc(
+			hw_mgr, param, true);
+		break;
+	case CAM_ISP_HW_TYPE_SFE:
+		rc = cam_ife_hw_mgr_sfe_irq_inject_or_dump_desc(
+			hw_mgr, param, true);
+		break;
+	default:
+		strlcat(irq_inject_display_buf, "No matched HW_TYPE\n", IRQ_INJECT_DISPLAY_BUF_LEN);
+		rc = -EINVAL;
+		return rc;
+	}
+
+	return rc;
+}
+
+static void cam_ife_hw_mgr_dump_active_hw(char *buffer, int *offset)
+{
+	uint32_t i;
+	struct cam_ife_hw_mgr_ctx       *ctx;
+	struct cam_isp_hw_mgr_res       *hw_mgr_res;
+	struct cam_isp_hw_mgr_res       *hw_mgr_res_temp;
+	struct cam_ife_hw_mgr_ctx       *ctx_temp;
+
+	mutex_lock(&g_ife_hw_mgr.ctx_mutex);
+	if (list_empty(&g_ife_hw_mgr.used_ctx_list)) {
+		*offset += scnprintf(buffer + *offset, LINE_BUFFER_LEN - *offset,
+			"Currently no ctx in use\n");
+		mutex_unlock(&g_ife_hw_mgr.ctx_mutex);
+		return;
+	}
+
+	list_for_each_entry_safe(ctx, ctx_temp,
+		&g_ife_hw_mgr.used_ctx_list, list) {
+
+		list_for_each_entry_safe(hw_mgr_res, hw_mgr_res_temp,
+			&ctx->res_list_ife_csid, list) {
+			for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+				if (!hw_mgr_res->hw_res[i])
+					continue;
+
+				*offset += scnprintf(buffer + *offset,
+					LINE_BUFFER_LEN - *offset,
+					"hw_type:CSID hw_idx:%d ctx id:%u res: %s\n",
+					hw_mgr_res->hw_res[i]->hw_intf->hw_idx, ctx->ctx_index,
+					hw_mgr_res->hw_res[i]->res_name);
+			}
+		}
+
+		list_for_each_entry_safe(hw_mgr_res, hw_mgr_res_temp,
+			&ctx->res_list_ife_src, list) {
+			for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+				if (!hw_mgr_res->hw_res[i])
+					continue;
+
+				*offset += scnprintf(buffer + *offset,
+					LINE_BUFFER_LEN - *offset,
+					"hw_type:IFE hw_idx:%d ctx id:%u res: %s\n",
+					hw_mgr_res->hw_res[i]->hw_intf->hw_idx, ctx->ctx_index,
+					hw_mgr_res->hw_res[i]->res_name);
+			}
+		}
+
+		list_for_each_entry_safe(hw_mgr_res, hw_mgr_res_temp,
+			&ctx->res_list_sfe_src, list) {
+			for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+				if (!hw_mgr_res->hw_res[i])
+					continue;
+
+				*offset += scnprintf(buffer + *offset,
+					LINE_BUFFER_LEN - *offset,
+					"hw_type:SFE hw_idx:%d ctx id:%u res: %s\n",
+					hw_mgr_res->hw_res[i]->hw_intf->hw_idx, ctx->ctx_index,
+					hw_mgr_res->hw_res[i]->res_name);
+			}
+		}
+	}
+	mutex_unlock(&g_ife_hw_mgr.ctx_mutex);
+}
+
+static inline char *__cam_isp_irq_inject_reg_unit_to_name(int32_t reg_unit)
+{
+	switch (reg_unit) {
+	case CAM_ISP_CSID_TOP_REG:
+		return "CAM_ISP_CSID_TOP_REG";
+	case CAM_ISP_CSID_RX_REG:
+		return "CAM_ISP_CSID_RX_REG";
+	case CAM_ISP_CSID_PATH_IPP_REG:
+		return "CAM_ISP_CSID_PATH_IPP_REG";
+	case CAM_ISP_CSID_PATH_PPP_REG:
+		return "CAM_ISP_CSID_PATH_PPP_REG";
+	case CAM_ISP_CSID_PATH_RDI0_REG:
+		return "CAM_ISP_CSID_PATH_RDI0_REG";
+	case CAM_ISP_CSID_PATH_RDI1_REG:
+		return "CAM_ISP_CSID_PATH_RDI1_REG";
+	case CAM_ISP_CSID_PATH_RDI2_REG:
+		return "CAM_ISP_CSID_PATH_RDI2_REG";
+	case CAM_ISP_CSID_PATH_RDI3_REG:
+		return "CAM_ISP_CSID_PATH_RDI3_REG";
+	case CAM_ISP_CSID_PATH_RDI4_REG:
+		return "CAM_ISP_CSID_PATH_RDI4_REG";
+	case CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG:
+		return "CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG";
+	case CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_1_REG:
+		return "CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_1_REG";
+	case CAM_ISP_SFE_0_BUS_RD_INPUT_IF_IRQ_SET_REG:
+		return "CAM_ISP_SFE_0_BUS_RD_INPUT_IF_IRQ_SET_REG";
+	case CAM_ISP_SFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG:
+		return "CAM_ISP_SFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG";
+	default:
+		return "Invalid reg_unit";
+	}
+}
+
+static inline char *__cam_isp_irq_inject_hw_type_to_name(int32_t hw_type)
+{
+	switch (hw_type) {
+	case CAM_ISP_HW_TYPE_CSID:
+		return "CSID";
+	case CAM_ISP_HW_TYPE_VFE:
+		return "VFE";
+	case CAM_ISP_HW_TYPE_SFE:
+		return "SFE";
+	default:
+		return "Invalid hw_type";
+	}
+}
+
+static inline int cam_isp_irq_inject_get_hw_type(
+	int32_t *hw_type, char *token)
+{
+	if (strcmp(token, "CSID") == 0)
+		*hw_type = CAM_ISP_HW_TYPE_CSID;
+	else if (strcmp(token, "VFE") == 0)
+		*hw_type = CAM_ISP_HW_TYPE_VFE;
+	else if (strcmp(token, "SFE") == 0)
+		*hw_type = CAM_ISP_HW_TYPE_SFE;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cam_isp_irq_inject_parse_common_params(
+	struct cam_isp_irq_inject_param  *irq_inject_param,
+	uint32_t param_index, char *token, bool *is_query)
+{
+	int i, rc = 0, offset = 0;
+	char *line_buf = NULL;
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	switch (param_index) {
+	case HW_TYPE:
+		if (strnstr(token, "?", 1)) {
+			*is_query = true;
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Interruptable HW : CSID | IFE | SFE\n");
+			break;
+		}
+		rc = cam_isp_irq_inject_get_hw_type(&irq_inject_param->hw_type, token);
+		if (rc) {
+			irq_inject_param->hw_type = -1;
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Invalid camera hardware [ %s ]\n", token);
+		}
+		break;
+	case HW_IDX:
+		if (strnstr(token, "?", 1)) {
+			*is_query = true;
+			if (irq_inject_param->hw_type == -1) {
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"HW_IDX : Enter hw_type first\n");
+				break;
+			}
+			switch (irq_inject_param->hw_type) {
+			case CAM_ISP_HW_TYPE_CSID:
+				for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
+					if (!g_ife_hw_mgr.csid_devices[i])
+						break;
+				}
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"Max index of CSID : %d\n", i - 1);
+				break;
+			case CAM_ISP_HW_TYPE_VFE:
+				for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) {
+					if (!g_ife_hw_mgr.ife_devices[i])
+						break;
+				}
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"Max index of VFE : %d\n", i - 1);
+				break;
+			case CAM_ISP_HW_TYPE_SFE:
+				for (i = 0; i < CAM_SFE_HW_NUM_MAX; i++) {
+					if (!g_ife_hw_mgr.sfe_devices[i])
+						break;
+				}
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"Max index of SFE : %d\n", i - 1);
+				break;
+			default:
+				break;
+			}
+		} else if (kstrtou32(token, 0, &irq_inject_param->hw_idx)) {
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Invalid hw index %s\n", token);
+			rc = -EINVAL;
+		}
+		break;
+	case REG_UNIT:
+		if (strnstr(token, "?", 1)) {
+			*is_query = true;
+			if (irq_inject_param->hw_type == -1) {
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"REG_UNIT : Enter hw_type first\n");
+				break;
+			}
+
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Printing available res for hw_type: %s\n",
+				__cam_isp_irq_inject_hw_type_to_name(
+					irq_inject_param->hw_type));
+			for (i = 0; i < CAM_ISP_REG_UNIT_MAX; i++) {
+				if ((irq_inject_param->hw_type == CAM_ISP_HW_TYPE_CSID) &&
+					i > CAM_ISP_CSID_PATH_RDI4_REG)
+					continue;
+				else if ((irq_inject_param->hw_type == CAM_ISP_HW_TYPE_VFE) &&
+					((i < CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG) ||
+					(i > CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_1_REG)))
+					continue;
+				else if ((irq_inject_param->hw_type == CAM_ISP_HW_TYPE_SFE) &&
+					((i < CAM_ISP_SFE_0_BUS_RD_INPUT_IF_IRQ_SET_REG) ||
+					(i > CAM_ISP_SFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG)))
+					continue;
+
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset, "%d : %s\n", i,
+					__cam_isp_irq_inject_reg_unit_to_name(i));
+			}
+
+		} else if (kstrtou32(token, 0, &irq_inject_param->reg_unit)) {
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Invalid register %s\n", token);
+			rc = -EINVAL;
+		}
+		break;
+	case IRQ_MASK:
+		if (strnstr(token, "?", 1)) {
+			*is_query = true;
+			if ((irq_inject_param->hw_type == -1) ||
+				(irq_inject_param->reg_unit == -1)) {
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"IRQ_MASK : Enter hw_type and reg_unit first\n");
+				break;
+			}
+			if (cam_ife_hw_mgr_dump_irq_desc(&g_ife_hw_mgr,
+				irq_inject_param)) {
+				offset += scnprintf(line_buf + offset,
+					LINE_BUFFER_LEN - offset,
+					"Dump irq description failed\n");
+				rc = -EINVAL;
+			}
+		} else if (kstrtou32(token, 0, &irq_inject_param->irq_mask)) {
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Invalid irq mask %s\n", token);
+			rc = -EINVAL;
+		}
+		break;
+	case INJECT_REQ:
+		if (strnstr(token, "now", 3)) {
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Trigger IRQ now\n");
+			irq_inject_param->req_id = 0xFFFFFFFF;
+		} else if (kstrtou64(token, 0, &irq_inject_param->req_id)) {
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Invalid request id %s\n", token);
+			rc = -EINVAL;
+		}
+		break;
+	default:
+		offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+			"Invalid extra parameter: %s\n", token);
+		 rc = -EINVAL;
+	}
+
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	kfree(line_buf);
+	return rc;
+}
+
+static int cam_isp_irq_inject_command_parser(
+	struct cam_isp_irq_inject_param  *irq_inject_param,
+	char **msg, uint32_t max_params,
+	cam_isp_irq_inject_cmd_parse_handler cmd_parse_cb,
+	bool *is_query)
+{
+	char *token = NULL;
+	char *line_buf = NULL;
+	int rc, param_index = 0, offset = 0;
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	token = strsep(msg, ":");
+	while (token != NULL) {
+		rc = cmd_parse_cb(irq_inject_param, param_index, token, is_query);
+		if (rc) {
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Parsed Command failed rc: %d\n", rc);
+			return rc;
+		}
+
+		param_index++;
+		if (param_index == max_params)
+			break;
+		token = strsep(msg, ":");
+	}
+
+	if ((param_index < max_params) && !(*is_query)) {
+		offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+			"Insufficient parameters passed for total parameters: %u\n",
+			param_index);
+		return -EINVAL;
+	}
+
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	kfree(line_buf);
+	return param_index;
+}
+
+static ssize_t cam_isp_irq_injection_read(struct file *file,
+	char __user *ubuf,
+	size_t size, loff_t *ppos)
+{
+	int i, offset = 0;
+	int count = 0;
+	uint32_t hw_type = 0;
+	char *line_buf = NULL;
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	if (!(*ppos) && strlen(irq_inject_display_buf))
+		goto end;
+	else if ((*ppos) && (strlen(irq_inject_display_buf) == 0))
+		return 0;
+
+	strlcat(irq_inject_display_buf, IRQ_INJECT_USAGE_STRING, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	for (i = 0; i < MAX_INJECT_SET; i++) {
+		if (!g_ife_hw_mgr.irq_inject_param[i].is_valid)
+			continue;
+
+		hw_type = g_ife_hw_mgr.irq_inject_param[i].hw_type;
+		offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+			"injected param[%d] : hw_type:%s hw_idx:%d reg_unit:%d irq_mask:%#x req_id:%d\n",
+			i, __cam_isp_irq_inject_hw_type_to_name(hw_type),
+			g_ife_hw_mgr.irq_inject_param[i].hw_idx,
+			g_ife_hw_mgr.irq_inject_param[i].reg_unit,
+			g_ife_hw_mgr.irq_inject_param[i].irq_mask,
+			g_ife_hw_mgr.irq_inject_param[i].req_id);
+	}
+
+	cam_ife_hw_mgr_dump_active_hw(line_buf, &offset);
+
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+end:
+	if (clear_user(ubuf, size))
+		return -EIO;
+	count = simple_read_from_buffer(ubuf, size, ppos, irq_inject_display_buf,
+		strlen(irq_inject_display_buf));
+
+	memset(irq_inject_display_buf, '\0', IRQ_INJECT_DISPLAY_BUF_LEN);
+	kfree(line_buf);
+	return count;
+}
+
+static ssize_t cam_isp_irq_injection_write(struct file *file,
+	const char __user *ubuf, size_t size, loff_t *ppos)
+{
+	bool is_query = false;
+	int i, rc = 0;
+	int offset = 0;
+	uint32_t hw_type = 0;
+	char *msg = NULL;
+	char *line_buf = NULL;
+	char input_buf[LINE_BUFFER_LEN] = {'\0'};
+
+	line_buf = kzalloc(sizeof(char) * LINE_BUFFER_LEN, GFP_KERNEL);
+	if (!line_buf)
+		return -ENOMEM;
+
+	memset(irq_inject_display_buf, '\0', IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	if (copy_from_user(input_buf, ubuf, sizeof(input_buf)))
+		return -EFAULT;
+
+	msg = input_buf;
+
+	for (i = 0; i < MAX_INJECT_SET; i++) {
+		if (g_ife_hw_mgr.irq_inject_param[i].is_valid)
+			continue;
+
+		rc = cam_isp_irq_inject_command_parser(
+			&g_ife_hw_mgr.irq_inject_param[i], &msg,
+			MAX_PARAMS_FOR_IRQ_INJECT,
+			cam_isp_irq_inject_parse_common_params, &is_query);
+		if ((rc != MAX_PARAMS_FOR_IRQ_INJECT) || is_query) {
+			cam_isp_irq_inject_clear_params(&g_ife_hw_mgr.irq_inject_param[i]);
+			if (!is_query)
+				offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+					"Parsed Command failed, param_index = %d\n", rc);
+		} else {
+			g_ife_hw_mgr.irq_inject_param[i].is_valid = true;
+			hw_type = g_ife_hw_mgr.irq_inject_param[i].hw_type;
+			offset += scnprintf(line_buf + offset, LINE_BUFFER_LEN - offset,
+				"Setting param[%d] : hw_type:%s hw_idx:%d reg_unit:%d irq_mask:%#x req_id:%d\n",
+				i, __cam_isp_irq_inject_hw_type_to_name(hw_type),
+				g_ife_hw_mgr.irq_inject_param[i].hw_idx,
+				g_ife_hw_mgr.irq_inject_param[i].reg_unit,
+				g_ife_hw_mgr.irq_inject_param[i].irq_mask,
+				g_ife_hw_mgr.irq_inject_param[i].req_id);
+		}
+		break;
+	}
+
+	strlcat(irq_inject_display_buf, line_buf, IRQ_INJECT_DISPLAY_BUF_LEN);
+
+	kfree(line_buf);
+	return size;
+}
+
+static const struct file_operations cam_isp_irq_injection = {
+	.owner = THIS_MODULE,
+	.open  = simple_open,
+	.read  = cam_isp_irq_injection_read,
+	.write = cam_isp_irq_injection_write,
+};
+
 static int cam_ife_hw_mgr_debug_register(void)
 {
 	int rc = 0;
@@ -15537,6 +16235,8 @@ static int cam_ife_hw_mgr_debug_register(void)
 	debugfs_create_bool("enable_presil_reg_dump", 0644,
 		g_ife_hw_mgr.debug_cfg.dentry,
 		&g_ife_hw_mgr.debug_cfg.enable_presil_reg_dump);
+	debugfs_create_file("isp_irq_inject", 0644,
+		g_ife_hw_mgr.debug_cfg.dentry, NULL, &cam_isp_irq_injection);
 end:
 	g_ife_hw_mgr.debug_cfg.enable_csid_recovery = 1;
 	return rc;
@@ -15808,6 +16508,9 @@ int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, int *iommu_hdl,
 	memset(&g_ife_hw_mgr, 0, sizeof(g_ife_hw_mgr));
 	memset(&path_port_map, 0, sizeof(path_port_map));
 
+	for (i = 0; i < MAX_INJECT_SET; i++)
+		cam_isp_irq_inject_clear_params(&g_ife_hw_mgr.irq_inject_param[i]);
+
 	mutex_init(&g_ife_hw_mgr.ctx_mutex);
 	spin_lock_init(&g_ife_hw_mgr.ctx_lock);
 

+ 36 - 0
drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h

@@ -38,6 +38,9 @@ enum cam_ife_ctx_master_type {
 #define CAM_IFE_CTX_CFG_SW_SYNC_ON        BIT(1)
 #define CAM_IFE_CTX_CFG_DYNAMIC_SWITCH_ON BIT(2)
 
+/* Maximum set for irq injection*/
+#define MAX_INJECT_SET 10
+
 /**
  * struct cam_ife_hw_mgr_debug - contain the debug information
  *
@@ -455,6 +458,36 @@ struct cam_isp_sfe_cache_info {
 	bool     activated[CAM_ISP_EXPOSURE_MAX];
 };
 
+/*
+ * struct cam_isp_irq_inject_irq_desc: Structure to hold IRQ description
+ *
+ * @bitmask : Bitmask of the IRQ
+ * @desc    : String to describe the IRQ bit
+ */
+struct cam_isp_irq_inject_irq_desc {
+	uint32_t    bitmask;
+	char       *desc;
+};
+
+/*
+ * enum cam_isp_irq_inject_common_param_pos - Irq injection param
+ *
+ * HW_TYPE         : hw to inject IRQ
+ * HW_IDX          : index of the selected hw
+ * RES_ID          : register to set irq
+ * IRQ_MASK        : IRQ to be triggered
+ * INJECT_REQ      : req to trigger the IRQ
+ * INJECT_PARAM_MAX: max allowed num of injected param
+ */
+enum cam_isp_irq_inject_common_param_pos {
+	HW_TYPE,
+	HW_IDX,
+	REG_UNIT,
+	IRQ_MASK,
+	INJECT_REQ,
+	INJECT_PARAM_MAX
+};
+
 /**
  * struct cam_ife_hw_mgr - IFE HW Manager
  *
@@ -486,6 +519,7 @@ struct cam_isp_sfe_cache_info {
  * @sfe_cache_info         SFE Cache Info
  * @isp_device_type:       If device supports single-context(ife) or multi-
  *                         context(mc_tfe)
+ * @irq_inject_param       Param for isp irq injection
  */
 struct cam_ife_hw_mgr {
 	struct cam_isp_hw_mgr          mgr_common;
@@ -519,6 +553,8 @@ struct cam_ife_hw_mgr {
 	struct cam_isp_sys_cache_info    sys_cache_info[CAM_LLCC_MAX];
 	struct cam_isp_sfe_cache_info    sfe_cache_info[CAM_SFE_HW_NUM_MAX];
 	uint32_t                         isp_device_type;
+
+	struct cam_isp_irq_inject_param  irq_inject_param[MAX_INJECT_SET];
 };
 
 /**

+ 11 - 3
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid780.h

@@ -122,6 +122,10 @@ static const struct cam_ife_csid_irq_desc cam_ife_csid_780_rx_irq_desc[][32] = {
 	},
 };
 
+static const uint32_t cam_ife_csid_780_num_rx_irq_desc[] = {
+	ARRAY_SIZE(cam_ife_csid_780_rx_irq_desc[0]),
+};
+
 static const struct cam_ife_csid_irq_desc cam_ife_csid_780_path_irq_desc[] = {
 	{
 		.bitmask = BIT(0),
@@ -283,6 +287,10 @@ static const struct cam_ife_csid_top_irq_desc cam_ife_csid_780_top_irq_desc[][32
 	},
 };
 
+static const uint32_t cam_ife_csid_780_num_top_irq_desc[] = {
+	ARRAY_SIZE(cam_ife_csid_780_top_irq_desc[0]),
+};
+
 static struct cam_irq_register_set cam_ife_csid_780_irq_reg_set[9] = {
 	/* Top */
 	{
@@ -354,8 +362,6 @@ static struct cam_irq_controller_reg_info cam_ife_csid_780_top_irq_reg_info[] =
 	},
 };
 
-static uint32_t cam_ife_csid_780_num_top_regs[] = {ARRAY_SIZE(cam_ife_csid_780_top_irq_reg_info),};
-
 static struct cam_irq_controller_reg_info cam_ife_csid_780_rx_irq_reg_info[] = {
 	{
 	.num_registers = 1,
@@ -1458,7 +1464,9 @@ static struct cam_ife_csid_ver2_reg_info cam_ife_csid_780_reg_info = {
 	.rx_irq_desc        = &cam_ife_csid_780_rx_irq_desc,
 	.path_irq_desc      = cam_ife_csid_780_path_irq_desc,
 	.top_irq_desc       = &cam_ife_csid_780_top_irq_desc,
-	.num_top_err_irqs   = cam_ife_csid_780_num_top_regs,
+	.num_top_err_irqs   = cam_ife_csid_780_num_top_irq_desc,
+	.num_rx_err_irqs    = cam_ife_csid_780_num_rx_irq_desc,
+	.num_path_err_irqs  = ARRAY_SIZE(cam_ife_csid_780_path_irq_desc),
 	.num_top_regs       = 1,
 	.num_rx_regs       = 1,
 };

+ 7 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid880.h

@@ -132,6 +132,10 @@ static const struct cam_ife_csid_irq_desc cam_ife_csid_880_rx_irq_desc[][32] = {
 	},
 };
 
+static const uint32_t cam_ife_csid_880_num_rx_irq_desc[] = {
+	ARRAY_SIZE(cam_ife_csid_880_rx_irq_desc[0]),
+};
+
 static const struct cam_ife_csid_irq_desc cam_ife_csid_880_path_irq_desc[] = {
 	{
 		.bitmask = BIT(0),
@@ -1496,10 +1500,12 @@ static struct cam_ife_csid_ver2_reg_info cam_ife_csid_880_reg_info = {
 	},
 	.need_top_cfg = 0x1,
 	.csid_cust_node_map = {0x1, 0x0, 0x2},
+	.top_irq_desc       = &cam_ife_csid_880_top_irq_desc,
 	.rx_irq_desc        = &cam_ife_csid_880_rx_irq_desc,
 	.path_irq_desc      = cam_ife_csid_880_path_irq_desc,
-	.top_irq_desc       = &cam_ife_csid_880_top_irq_desc,
 	.num_top_err_irqs   = cam_ife_csid_880_num_top_irq_desc,
+	.num_rx_err_irqs    = cam_ife_csid_880_num_rx_irq_desc,
+	.num_path_err_irqs  = ARRAY_SIZE(cam_ife_csid_880_path_irq_desc),
 	.num_top_regs       = 1,
 	.num_rx_regs        = 1,
 };

+ 2 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid980.h

@@ -1795,11 +1795,12 @@ static struct cam_ife_csid_ver2_reg_info cam_ife_csid_980_reg_info = {
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_4] = &cam_ife_csid_980_rdi_4_reg_info,
 	.ipp_mc_reg                           = &cam_ife_csid_980_ipp_mc_reg_info,
 	.need_top_cfg = 0x0,
+	.top_irq_desc        = &cam_ife_csid_980_top_irq_desc,
 	.rx_irq_desc         = &cam_ife_csid_980_rx_irq_desc,
 	.path_irq_desc       = cam_ife_csid_980_path_irq_desc,
-	.top_irq_desc        = &cam_ife_csid_980_top_irq_desc,
 	.num_top_err_irqs    = cam_ife_csid_980_num_top_irq_desc,
 	.num_rx_err_irqs     = cam_ife_csid_980_num_rx_irq_desc,
+	.num_path_err_irqs   = ARRAY_SIZE(cam_ife_csid_980_path_irq_desc),
 	.num_top_regs        = 1,
 	.num_rx_regs         = 1,
 };

+ 160 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_hw_ver2.c

@@ -6535,6 +6535,160 @@ end:
 	return 0;
 }
 
+static int cam_ife_csid_ver2_dump_irq_desc(
+	struct cam_ife_csid_ver2_hw  *csid_hw, void *args)
+{
+	int                                     i, offset = 0;
+	struct cam_isp_irq_inject_param         *inject_params = NULL;
+	const struct cam_ife_csid_ver2_reg_info *csid_reg = NULL;
+
+	if (!args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	inject_params = (struct cam_isp_irq_inject_param *)args;
+	csid_reg = (struct cam_ife_csid_ver2_reg_info *)
+			csid_hw->core_info->csid_reg;
+
+	offset += scnprintf(inject_params->line_buf + offset,
+		LINE_BUFFER_LEN - offset,
+		"Printing executable IRQ for hw_type: CSID reg_unit: %d\n",
+		inject_params->reg_unit);
+
+	switch (inject_params->reg_unit) {
+	case CAM_ISP_CSID_TOP_REG:
+		for (i = 0; i < csid_reg->num_top_err_irqs[CAM_IFE_CSID_TOP_IRQ_STATUS_REG0]; i++) {
+			if (!(*csid_reg->top_irq_desc)
+					[CAM_IFE_CSID_TOP_IRQ_STATUS_REG0][i].bitmask)
+				break;
+
+			offset += scnprintf(inject_params->line_buf + offset,
+				LINE_BUFFER_LEN - offset, "%#12x : %s - %s\n",
+				(*csid_reg->top_irq_desc)
+					[CAM_IFE_CSID_TOP_IRQ_STATUS_REG0][i].bitmask,
+				(*csid_reg->top_irq_desc)
+					[CAM_IFE_CSID_TOP_IRQ_STATUS_REG0][i].err_name,
+				(*csid_reg->top_irq_desc)
+					[CAM_IFE_CSID_TOP_IRQ_STATUS_REG0][i].desc);
+		}
+		break;
+	case CAM_ISP_CSID_RX_REG:
+		for (i = 0; i < csid_reg->num_rx_err_irqs[CAM_IFE_CSID_RX_IRQ_STATUS_REG0]; i++) {
+			if (!(*csid_reg->rx_irq_desc)
+					[CAM_IFE_CSID_RX_IRQ_STATUS_REG0][i].bitmask)
+				break;
+
+			offset += scnprintf(inject_params->line_buf + offset,
+				LINE_BUFFER_LEN - offset, "%#12x : %s\n",
+				(*csid_reg->rx_irq_desc)
+					[CAM_IFE_CSID_RX_IRQ_STATUS_REG0][i].bitmask,
+				(*csid_reg->rx_irq_desc)
+					[CAM_IFE_CSID_RX_IRQ_STATUS_REG0][i].desc);
+		}
+		break;
+	case CAM_ISP_CSID_PATH_IPP_REG:
+	case CAM_ISP_CSID_PATH_PPP_REG:
+	case CAM_ISP_CSID_PATH_RDI0_REG:
+	case CAM_ISP_CSID_PATH_RDI1_REG:
+	case CAM_ISP_CSID_PATH_RDI2_REG:
+	case CAM_ISP_CSID_PATH_RDI3_REG:
+	case CAM_ISP_CSID_PATH_RDI4_REG:
+		for (i = 0; i < csid_reg->num_path_err_irqs; i++)
+			offset += scnprintf(inject_params->line_buf + offset,
+				LINE_BUFFER_LEN - offset, "%#12x : %s\n",
+				csid_reg->path_irq_desc[i].bitmask,
+				csid_reg->path_irq_desc[i].desc);
+		break;
+	default:
+		offset += scnprintf(inject_params->line_buf + offset,
+			LINE_BUFFER_LEN - offset,
+			"No matched reg unit for injection\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cam_ife_csid_ver2_irq_inject(
+	struct cam_ife_csid_ver2_hw  *csid_hw, void *args)
+{
+	uint32_t                                irq_set_addr = 0;
+	struct cam_hw_soc_info                  *soc_info;
+	struct cam_isp_irq_inject_param         *inject_params = NULL;
+	const struct cam_ife_csid_ver2_reg_info *csid_reg = NULL;
+	void __iomem *mem_base;
+
+	if (!args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	inject_params = (struct cam_isp_irq_inject_param *)args;
+	csid_reg = (struct cam_ife_csid_ver2_reg_info *)
+			csid_hw->core_info->csid_reg;
+	soc_info = &csid_hw->hw_info->soc_info;
+	mem_base = soc_info->reg_map[CAM_IFE_CSID_CLC_MEM_BASE_ID].mem_base;
+
+	switch (inject_params->reg_unit) {
+	case CAM_ISP_CSID_TOP_REG: {
+		irq_set_addr =
+			csid_reg->cmn_reg->top_irq_set_addr[CAM_IFE_CSID_TOP_IRQ_STATUS_REG0];
+		break;
+	}
+	case CAM_ISP_CSID_RX_REG: {
+		irq_set_addr =
+			csid_reg->csi2_reg->irq_set_addr[CAM_IFE_CSID_RX_IRQ_STATUS_REG0];
+		break;
+	}
+	case CAM_ISP_CSID_PATH_IPP_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_IPP]->irq_set_addr;
+		break;
+	}
+	case CAM_ISP_CSID_PATH_PPP_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_PPP]->irq_set_addr;
+		break;
+	}
+	case CAM_ISP_CSID_PATH_RDI0_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_RDI_0]->irq_set_addr;
+		break;
+	}
+	case CAM_ISP_CSID_PATH_RDI1_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_RDI_1]->irq_set_addr;
+		break;
+	}
+	case CAM_ISP_CSID_PATH_RDI2_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_RDI_2]->irq_set_addr;
+		break;
+	}
+	case CAM_ISP_CSID_PATH_RDI3_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_RDI_3]->irq_set_addr;
+		break;
+	}
+	case CAM_ISP_CSID_PATH_RDI4_REG: {
+		irq_set_addr =
+			csid_reg->path_reg[CAM_IFE_PIX_PATH_RES_RDI_4]->irq_set_addr;
+		break;
+	}
+	default:
+		CAM_INFO(CAM_ISP, "No matched reg unit for injection");
+		return -EINVAL;
+	}
+
+	cam_io_w_mb(inject_params->irq_mask, mem_base + irq_set_addr);
+	cam_io_w_mb(0x10, mem_base + csid_reg->cmn_reg->irq_cmd_addr);
+	CAM_INFO(CAM_ISP, "Injected : irq_mask %#x set_reg_offset %#x",
+		inject_params->irq_mask, irq_set_addr);
+
+	return 0;
+}
+
 static int cam_ife_csid_ver2_reset_out_of_sync_cnt(
 	struct cam_ife_csid_ver2_hw  *csid_hw, void *args)
 {
@@ -6784,6 +6938,12 @@ static int cam_ife_csid_ver2_process_cmd(void *hw_priv,
 	case CAM_ISP_HW_CMD_IRQ_COMP_CFG:
 		rc = cam_ife_csid_ver2_irq_comp_cfg(csid_hw, cmd_args, arg_size);
 		break;
+	case CAM_ISP_HW_CMD_IRQ_INJECTION:
+		rc = cam_ife_csid_ver2_irq_inject(csid_hw, cmd_args);
+		break;
+	case CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION:
+		rc = cam_ife_csid_ver2_dump_irq_desc(csid_hw, cmd_args);
+		break;
 	default:
 		CAM_ERR(CAM_ISP, "CSID:%u unsupported cmd:%d",
 			csid_hw->hw_intf->hw_idx, cmd_type);

+ 2 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_hw_ver2.h

@@ -733,11 +733,12 @@ struct cam_ife_csid_ver2_reg_info {
 		    CAM_IFE_CSID_HW_NUM_MAX];
 	const int                                         input_core_sel[
 		    CAM_IFE_CSID_HW_NUM_MAX][CAM_IFE_CSID_INPUT_CORE_SEL_MAX];
+	const struct cam_ife_csid_top_irq_desc           (*top_irq_desc)[][32];
 	const struct cam_ife_csid_irq_desc               (*rx_irq_desc)[][32];
 	const struct cam_ife_csid_irq_desc               *path_irq_desc;
-	const struct cam_ife_csid_top_irq_desc           (*top_irq_desc)[][32];
 	const uint32_t                                   *num_top_err_irqs;
 	const uint32_t                                   *num_rx_err_irqs;
+	const uint32_t                                    num_path_err_irqs;
 	const uint32_t                                    num_top_regs;
 	const uint32_t                                    num_rx_regs;
 };

+ 6 - 3
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite780.h

@@ -118,8 +118,6 @@ static const struct cam_ife_csid_irq_desc cam_ife_csid_lite_780_rx_irq_desc[][32
 	},
 };
 
-
-
 static const struct cam_ife_csid_irq_desc cam_ife_csid_lite_780_path_irq_desc[] = {
 	{
 		.bitmask = BIT(0),
@@ -284,6 +282,9 @@ static const struct cam_ife_csid_top_irq_desc cam_ife_csid_lite_780_top_irq_desc
 static const uint32_t cam_ife_csid_lite_780_num_top_irq_desc[] = {
 	ARRAY_SIZE(cam_ife_csid_lite_780_top_irq_desc[0]),
 };
+static const uint32_t cam_ife_csid_lite_780_num_rx_irq_desc[] = {
+	ARRAY_SIZE(cam_ife_csid_lite_780_rx_irq_desc[0]),
+};
 
 static struct cam_irq_register_set cam_ife_csid_lite_780_irq_reg_set[9] = {
 	/* Top */
@@ -1049,10 +1050,12 @@ static struct cam_ife_csid_ver2_reg_info cam_ife_csid_lite_780_reg_info = {
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_2] = &cam_ife_csid_lite_780_rdi_2_reg_info,
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_3] = &cam_ife_csid_lite_780_rdi_3_reg_info,
 	.need_top_cfg = 0,
+	.top_irq_desc       = &cam_ife_csid_lite_780_top_irq_desc,
 	.rx_irq_desc        = &cam_ife_csid_lite_780_rx_irq_desc,
 	.path_irq_desc      = cam_ife_csid_lite_780_path_irq_desc,
-	.top_irq_desc       = &cam_ife_csid_lite_780_top_irq_desc,
 	.num_top_err_irqs   = cam_ife_csid_lite_780_num_top_irq_desc,
+	.num_rx_err_irqs    = cam_ife_csid_lite_780_num_rx_irq_desc,
+	.num_path_err_irqs  = ARRAY_SIZE(cam_ife_csid_lite_780_path_irq_desc),
 	.num_top_regs       = 1,
 	.num_rx_regs        = 1,
 };

+ 8 - 2
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite880.h

@@ -290,7 +290,11 @@ static const struct cam_ife_csid_top_irq_desc cam_ife_csid_lite_880_top_irq_desc
 };
 
 static const uint32_t cam_ife_csid_lite_880_num_top_irq_desc[] = {
-			ARRAY_SIZE(cam_ife_csid_lite_880_top_irq_desc[0])};
+	ARRAY_SIZE(cam_ife_csid_lite_880_top_irq_desc[0]),
+};
+static const uint32_t cam_ife_csid_lite_880_num_rx_irq_desc[] = {
+	ARRAY_SIZE(cam_ife_csid_lite_880_rx_irq_desc[0]),
+};
 
 static struct cam_irq_register_set cam_ife_csid_lite_880_irq_reg_set[9] = {
 	/* Top */
@@ -1055,10 +1059,12 @@ static struct cam_ife_csid_ver2_reg_info cam_ife_csid_lite_880_reg_info = {
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_2] = &cam_ife_csid_lite_880_rdi_2_reg_info,
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_3] = &cam_ife_csid_lite_880_rdi_3_reg_info,
 	.need_top_cfg = 0,
+	.top_irq_desc       = &cam_ife_csid_lite_880_top_irq_desc,
 	.rx_irq_desc        = &cam_ife_csid_lite_880_rx_irq_desc,
 	.path_irq_desc      = cam_ife_csid_lite_880_path_irq_desc,
-	.top_irq_desc       = &cam_ife_csid_lite_880_top_irq_desc,
 	.num_top_err_irqs   = cam_ife_csid_lite_880_num_top_irq_desc,
+	.num_rx_err_irqs    = cam_ife_csid_lite_880_num_rx_irq_desc,
+	.num_path_err_irqs  = ARRAY_SIZE(cam_ife_csid_lite_880_path_irq_desc),
 	.num_top_regs       = 1,
 	.num_rx_regs        = 1,
 };

+ 3 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite980.h

@@ -29,10 +29,12 @@ static struct cam_ife_csid_ver2_reg_info cam_ife_csid_lite_980_reg_info = {
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_2] = &cam_ife_csid_lite_880_rdi_2_reg_info,
 	.path_reg[CAM_IFE_PIX_PATH_RES_RDI_3] = &cam_ife_csid_lite_880_rdi_3_reg_info,
 	.need_top_cfg = 0,
+	.top_irq_desc       = &cam_ife_csid_lite_880_top_irq_desc,
 	.rx_irq_desc        = &cam_ife_csid_lite_880_rx_irq_desc,
 	.path_irq_desc      = cam_ife_csid_lite_880_path_irq_desc,
-	.top_irq_desc       = &cam_ife_csid_lite_880_top_irq_desc,
 	.num_top_err_irqs   = cam_ife_csid_lite_880_num_top_irq_desc,
+	.num_rx_err_irqs    = cam_ife_csid_lite_880_num_rx_irq_desc,
+	.num_path_err_irqs  = ARRAY_SIZE(cam_ife_csid_lite_880_path_irq_desc),
 	.num_top_regs       = 1,
 	.num_rx_regs        = 1,
 };

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

@@ -28,6 +28,11 @@
  */
 #define CAM_ISP_RES_NAME_LEN      16
 
+/*
+ * MAX len of line_buffer
+ */
+#define LINE_BUFFER_LEN      1500
+
 /* Access core_info of isp resource node */
 #define cam_isp_res_core_info(res) (((struct cam_hw_info *)res->hw_intf->hw_priv)->core_info)
 
@@ -233,6 +238,8 @@ enum cam_isp_hw_cmd_type {
 	CAM_ISP_HW_CMD_CSID_DUMP_CROP_REG,
 	CAM_ISP_HW_CMD_MC_CTXT_SEL,
 	CAM_ISP_HW_CMD_IRQ_COMP_CFG,
+	CAM_ISP_HW_CMD_IRQ_INJECTION,
+	CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION,
 	CAM_ISP_HW_CMD_MAX,
 };
 
@@ -580,4 +587,59 @@ struct cam_isp_hw_overflow_info {
 	bool                    is_bus_overflow;
 };
 
+enum cam_isp_irq_inject_reg_unit_type {
+	CAM_ISP_CSID_TOP_REG,
+	CAM_ISP_CSID_RX_REG,
+	CAM_ISP_CSID_PATH_IPP_REG,
+	CAM_ISP_CSID_PATH_PPP_REG,
+	CAM_ISP_CSID_PATH_RDI0_REG,
+	CAM_ISP_CSID_PATH_RDI1_REG,
+	CAM_ISP_CSID_PATH_RDI2_REG,
+	CAM_ISP_CSID_PATH_RDI3_REG,
+	CAM_ISP_CSID_PATH_RDI4_REG,
+	CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG,
+	CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_1_REG,
+	CAM_ISP_SFE_0_BUS_RD_INPUT_IF_IRQ_SET_REG,
+	CAM_ISP_SFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG,
+	CAM_ISP_REG_UNIT_MAX
+};
+
+/*
+ * struct cam_isp_irq_inject_param:
+ *
+ * @Brief: Params for isp irq injection
+ *
+ * @hw_type  :  Hw to inject IRQ
+ * @hw_idx   :  Index of the selected hw
+ * @reg_unit :  Register to set irq
+ * @irq_mask :  IRQ to be triggered
+ * @req_id   :  Req to trigger the IRQ
+ * @is_valid :  Flag to indicate current set of params is valid or not
+ * @line_buf :  Buffer to temporarily keep log
+ */
+struct cam_isp_irq_inject_param {
+	int32_t  hw_type;
+	int32_t  hw_idx;
+	int32_t  reg_unit;
+	int32_t  irq_mask;
+	uint64_t req_id;
+	bool     is_valid;
+	char     line_buf[LINE_BUFFER_LEN];
+};
+
+/*
+ * struct cam_isp_params_injection:
+ *
+ * @Brief: args for irq injection or irq desc dump
+ *
+ * @param        :  Params for isp irq injection
+ * @vfe_hw_info  :  Vfe hw info
+ * @sfe_hw_info  :  Sfe hw info
+ */
+struct cam_isp_params_injection {
+	struct cam_isp_irq_inject_param *param;
+	void                            *vfe_hw_info;
+	void                            *sfe_hw_info;
+};
+
 #endif /* _CAM_ISP_HW_H_ */

+ 44 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/cam_sfe780.h

@@ -691,6 +691,18 @@ static struct cam_irq_register_set sfe780_bus_rd_irq_reg[1] = {
 	},
 };
 
+static struct cam_sfe_bus_rd_err_irq_desc sfe780_bus_rd_irq_err_desc[] = {
+	{
+		.bitmask = BIT(0),
+		.err_name = "INFO_CONS_VIOLATION",
+		.desc = "Clients have illegal programming, check CONS_VIOLATION_STATUS",
+	},
+	{
+		.bitmask = BIT(31),
+		.err_name = "INFO_CCIF_VIOLATION",
+	},
+};
+
 static struct cam_sfe_bus_rd_constraint_error_desc
 	sfe780_bus_rd_cons_error_desc[CAM_SFE_BUS_RD_CONS_ERR_MAX] = {
 	{
@@ -820,6 +832,8 @@ static struct cam_sfe_bus_rd_hw_info sfe780_bus_rd_hw_info = {
 			.max_height    = -1,
 		},
 	},
+	.num_bus_rd_errors     = ARRAY_SIZE(sfe780_bus_rd_irq_err_desc),
+	.bus_rd_err_desc       = sfe780_bus_rd_irq_err_desc,
 	.top_irq_shift          = 0x1,
 	.latency_buf_allocation = 2048,
 	.sys_cache_default_val  = 0x20,
@@ -828,6 +842,34 @@ static struct cam_sfe_bus_rd_hw_info sfe780_bus_rd_hw_info = {
 	.constraint_error_info  = &sfe780_bus_rd_constraint_error_info,
 };
 
+static struct cam_sfe_bus_wr_err_irq_desc sfe780_bus_wr_irq_err_desc[] = {
+	{
+		.bitmask = BIT(26),
+		.err_name = "IPCC_FENCE_DATA_ERR",
+		.desc = "IPCC or FENCE Data was not available in the Input Fifo",
+	},
+	{
+		.bitmask = BIT(27),
+		.err_name = "IPCC_FENCE_ADDR_ERR",
+		.desc = "IPCC or FENCE address fifo was empty and read was attempted",
+	},
+	{
+		.bitmask = BIT(28),
+		.err_name = "CONS_VIOLATION",
+		.desc = "Programming of software registers violated the constraints",
+	},
+	{
+		.bitmask = BIT(30),
+		.err_name = "VIOLATION",
+		.desc = "Client has a violation in ccif protocol at input",
+	},
+	{
+		.bitmask = BIT(31),
+		.err_name = "IMAGE_SIZE_VIOLATION",
+		.desc = "Programmed image size is not same as image size from the CCIF",
+	},
+};
+
 static struct cam_sfe_bus_wr_constraint_error_desc
 	sfe780_bus_wr_cons_error_desc[CAM_SFE_BUS_CONS_ERR_MAX] = {
 	{
@@ -1674,6 +1716,8 @@ static struct cam_sfe_bus_wr_hw_info sfe780_bus_wr_hw_info = {
 		},
 	},
 	.constraint_error_info = &sfe780_bus_wr_constraint_error_info,
+	.num_bus_wr_errors     = ARRAY_SIZE(sfe780_bus_wr_irq_err_desc),
+	.bus_wr_err_desc       = sfe780_bus_wr_irq_err_desc,
 	.comp_done_mask = {
 		BIT(17), BIT(18), BIT(19), BIT(20), BIT(21), BIT(22), BIT(23),
 		BIT(24), BIT(25), BIT(26),

+ 44 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/cam_sfe880.h

@@ -728,6 +728,18 @@ static struct cam_irq_register_set sfe880_bus_rd_irq_reg[1] = {
 	},
 };
 
+static struct cam_sfe_bus_rd_err_irq_desc sfe880_bus_rd_irq_err_desc[] = {
+	{
+		.bitmask = BIT(0),
+		.err_name = "INFO_CONS_VIOLATION",
+		.desc = "Clients have illegal programming, check CONS_VIOLATION_STATUS",
+	},
+	{
+		.bitmask = BIT(31),
+		.err_name = "INFO_CCIF_VIOLATION",
+	},
+};
+
 static struct cam_sfe_bus_rd_constraint_error_desc
 	sfe880_bus_rd_cons_error_desc[CAM_SFE_BUS_RD_CONS_ERR_MAX] = {
 	{
@@ -859,6 +871,8 @@ static struct cam_sfe_bus_rd_hw_info sfe880_bus_rd_hw_info = {
 			.max_height    = -1,
 		},
 	},
+	.num_bus_rd_errors     = ARRAY_SIZE(sfe880_bus_rd_irq_err_desc),
+	.bus_rd_err_desc       = sfe880_bus_rd_irq_err_desc,
 	.top_irq_shift          = 0x1,
 	.latency_buf_allocation = 2048,
 	.sys_cache_default_val  = 0x20,
@@ -867,6 +881,34 @@ static struct cam_sfe_bus_rd_hw_info sfe880_bus_rd_hw_info = {
 	.constraint_error_info  = &sfe880_bus_rd_constraint_error_info,
 };
 
+static struct cam_sfe_bus_wr_err_irq_desc sfe880_bus_wr_irq_err_desc[] = {
+	{
+		.bitmask = BIT(26),
+		.err_name = "IPCC_FENCE_DATA_ERR",
+		.desc = "IPCC or FENCE Data was not available in the Input Fifo",
+	},
+	{
+		.bitmask = BIT(27),
+		.err_name = "IPCC_FENCE_ADDR_ERR",
+		.desc = "IPCC or FENCE address fifo was empty and read was attempted",
+	},
+	{
+		.bitmask = BIT(28),
+		.err_name = "CONS_VIOLATION",
+		.desc = "Programming of software registers violated the constraints",
+	},
+	{
+		.bitmask = BIT(30),
+		.err_name = "VIOLATION",
+		.desc = "Client has a violation in ccif protocol at input",
+	},
+	{
+		.bitmask = BIT(31),
+		.err_name = "IMAGE_SIZE_VIOLATION",
+		.desc = "Programmed image size is not same as image size from the CCIF",
+	},
+};
+
 static struct cam_sfe_bus_wr_constraint_error_desc
 	sfe880_bus_wr_cons_error_desc[CAM_SFE_BUS_CONS_ERR_MAX] = {
 	{
@@ -1767,6 +1809,8 @@ static struct cam_sfe_bus_wr_hw_info sfe880_bus_wr_hw_info = {
 		},
 	},
 	.constraint_error_info = &sfe880_bus_wr_constraint_error_info,
+	.num_bus_wr_errors     = ARRAY_SIZE(sfe880_bus_wr_irq_err_desc),
+	.bus_wr_err_desc       = sfe880_bus_wr_irq_err_desc,
 	.comp_done_mask = {
 		BIT(17), BIT(18), BIT(19), BIT(20), BIT(21),  BIT(23),
 		BIT(24), BIT(25), BIT(26), BIT(27), BIT(22),

+ 4 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/cam_sfe_core.c

@@ -377,6 +377,7 @@ int cam_sfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 	struct cam_hw_info                *sfe_hw = hw_priv;
 	struct cam_hw_soc_info            *soc_info = NULL;
 	struct cam_sfe_hw_core_info       *core_info = NULL;
+	struct cam_sfe_hw_info            *hw_info = NULL;
 	int rc = 0;
 
 	if (!hw_priv) {
@@ -386,6 +387,7 @@ int cam_sfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 
 	soc_info = &sfe_hw->soc_info;
 	core_info = (struct cam_sfe_hw_core_info *)sfe_hw->core_info;
+	hw_info = core_info->sfe_hw_info;
 
 	switch (cmd_type) {
 	case CAM_ISP_HW_CMD_GET_CHANGE_BASE:
@@ -435,6 +437,8 @@ int cam_sfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 		fallthrough;
 	case CAM_ISP_HW_CMD_GET_RES_FOR_MID:
 	case CAM_ISP_HW_CMD_DUMP_BUS_INFO:
+	case CAM_ISP_HW_CMD_IRQ_INJECTION:
+	case CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION:
 		/* propagate to SFE bus wr */
 		core_info->sfe_bus_wr->hw_ops.process_cmd(
 			core_info->sfe_bus_wr->bus_priv, cmd_type,

+ 82 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_rd.c

@@ -60,6 +60,7 @@ struct cam_sfe_bus_rd_common_data {
 	void __iomem                               *mem_base;
 	struct cam_hw_intf                         *hw_intf;
 	struct cam_sfe_bus_rd_reg_offset_common    *common_reg;
+	struct cam_hw_soc_info                     *soc_info;
 	uint32_t                                    io_buf_update[
 		MAX_REG_VAL_PAIR_SIZE];
 	void                                       *bus_irq_controller;
@@ -1796,6 +1797,80 @@ end:
 	return 0;
 }
 
+static int cam_sfe_bus_rd_irq_inject(
+	void *priv, void *cmd_args, uint32_t arg_size)
+{
+	struct cam_sfe_bus_rd_priv          *bus_priv = NULL;
+	struct cam_hw_soc_info              *soc_info = NULL;
+	struct cam_sfe_bus_rd_hw_info       *bus_rd_hw_info = NULL;
+	struct cam_irq_controller_reg_info  *irq_reg_info = NULL;
+	struct cam_irq_register_set         *inject_reg = NULL;
+	struct cam_isp_irq_inject_param     *inject_params = NULL;
+
+	if (!cmd_args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	bus_priv = (struct cam_sfe_bus_rd_priv *)priv;
+	soc_info = bus_priv->common_data.soc_info;
+	bus_rd_hw_info = (struct cam_sfe_bus_rd_hw_info *)bus_priv->bus_rd_hw_info;
+	irq_reg_info = &bus_rd_hw_info->common_reg.irq_reg_info;
+	inject_reg = irq_reg_info->irq_reg_set;
+	inject_params = (struct cam_isp_irq_inject_param *)cmd_args;
+
+	if (!inject_reg) {
+		CAM_INFO(CAM_SFE, "Invalid inject_reg");
+		return -EINVAL;
+	}
+
+	if (inject_params->reg_unit ==
+		CAM_ISP_SFE_0_BUS_RD_INPUT_IF_IRQ_SET_REG) {
+		cam_io_w_mb(inject_params->irq_mask,
+			soc_info->reg_map[SFE_CORE_BASE_IDX].mem_base +
+			inject_reg->set_reg_offset);
+		cam_io_w_mb(0x10, soc_info->reg_map[SFE_CORE_BASE_IDX].mem_base +
+			irq_reg_info->global_irq_cmd_offset);
+		CAM_INFO(CAM_SFE, "Injected : irq_mask %#x set_reg_offset %#x",
+			inject_params->irq_mask, inject_reg->set_reg_offset);
+	}
+	return 0;
+}
+
+static int cam_sfe_bus_rd_dump_irq_desc(
+	void *priv, void *cmd_args, uint32_t arg_size)
+{
+	int                               i, offset = 0;
+	struct cam_sfe_bus_rd_priv       *bus_priv = NULL;
+	struct cam_sfe_bus_rd_hw_info    *bus_rd_hw_info = NULL;
+	struct cam_isp_irq_inject_param  *inject_params = NULL;
+
+	if (!cmd_args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	bus_priv = (struct cam_sfe_bus_rd_priv *)priv;
+	bus_rd_hw_info = (struct cam_sfe_bus_rd_hw_info *)bus_priv->bus_rd_hw_info;
+	inject_params = (struct cam_isp_irq_inject_param *)cmd_args;
+
+	if (inject_params->reg_unit ==
+			CAM_ISP_SFE_0_BUS_RD_INPUT_IF_IRQ_SET_REG) {
+		offset += scnprintf(inject_params->line_buf + offset,
+			LINE_BUFFER_LEN - offset,
+			"Printing executable IRQ for hw_type: SFE reg_unit: %d\n",
+			inject_params->reg_unit);
+
+		for (i = 0; i < bus_rd_hw_info->num_bus_rd_errors; i++)
+			offset += scnprintf(inject_params->line_buf + offset,
+				LINE_BUFFER_LEN - offset, "%#12x : %s - %s\n",
+				bus_rd_hw_info->bus_rd_err_desc[i].bitmask,
+				bus_rd_hw_info->bus_rd_err_desc[i].err_name,
+				bus_rd_hw_info->bus_rd_err_desc[i].desc);
+	}
+	return 0;
+}
+
 static int cam_sfe_bus_init_hw(void *hw_priv,
 	void *init_hw_args, uint32_t arg_size)
 {
@@ -1974,6 +2049,12 @@ static int cam_sfe_bus_rd_process_cmd(
 			(struct cam_sfe_bus_rd_priv  *)priv);
 		break;
 	}
+	case CAM_ISP_HW_CMD_IRQ_INJECTION:
+		rc = cam_sfe_bus_rd_irq_inject(priv, cmd_args, arg_size);
+		break;
+	case CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION:
+		rc = cam_sfe_bus_rd_dump_irq_desc(priv, cmd_args, arg_size);
+		break;
 	default:
 		CAM_ERR_RATE_LIMIT(CAM_SFE,
 			"Invalid SFE BUS RD command type: %d",
@@ -2032,6 +2113,7 @@ int cam_sfe_bus_rd_init(
 	bus_priv->common_data.irq_err_mask      = bus_rd_hw_info->irq_err_mask;
 	bus_priv->common_data.cons_chk_en_avail =
 		bus_rd_hw_info->constraint_error_info->cons_chk_en_avail;
+	bus_priv->common_data.soc_info         = soc_info;
 	bus_priv->top_irq_shift                 = bus_rd_hw_info->top_irq_shift;
 	bus_priv->latency_buf_allocation        = bus_rd_hw_info->latency_buf_allocation;
 	bus_priv->sys_cache_default_cfg         = bus_rd_hw_info->sys_cache_default_val;

+ 14 - 1
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_rd.h

@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _CAM_SFE_BUS_RD_H_
@@ -31,6 +31,17 @@ enum cam_sfe_bus_rd_type {
 	CAM_SFE_BUS_RD_MAX,
 };
 
+/*
+ * struct cam_sfe_bus_rd_err_irq_desc:
+ *
+ * @Brief:        Bus rd error irq description
+ */
+struct cam_sfe_bus_rd_err_irq_desc {
+	uint32_t  bitmask;
+	char     *err_name;
+	char     *desc;
+};
+
 /*
  * struct cam_sfe_bus_rd_constraint_error_desc:
  *
@@ -133,6 +144,8 @@ struct cam_sfe_bus_rd_hw_info {
 	uint32_t num_bus_rd_resc;
 	struct cam_sfe_bus_rd_info
 		sfe_bus_rd_info[CAM_SFE_BUS_RD_MAX];
+	uint32_t num_bus_rd_errors;
+	struct cam_sfe_bus_rd_err_irq_desc *bus_rd_err_desc;
 	uint32_t top_irq_shift;
 	uint32_t latency_buf_allocation;
 	uint32_t sys_cache_default_val;

+ 100 - 20
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_wr.c

@@ -94,6 +94,7 @@ struct cam_sfe_bus_wr_common_data {
 	uint32_t                                    sys_cache_default_cfg;
 	uint32_t                                    sfe_debug_cfg;
 	struct cam_sfe_bus_cache_dbg_cfg            cache_dbg_cfg;
+	struct cam_hw_soc_info                     *soc_info;
 };
 
 struct cam_sfe_wr_scratch_buf_info {
@@ -195,7 +196,8 @@ struct cam_sfe_bus_wr_priv {
 	int                                 error_irq_handle;
 	void                               *tasklet_info;
 	struct cam_sfe_bus_wr_constraint_error_info *constraint_error_info;
-	struct cam_sfe_bus_sfe_out_hw_info   *sfe_out_hw_info;
+	struct cam_sfe_bus_sfe_out_hw_info *sfe_out_hw_info;
+	struct cam_sfe_bus_wr_hw_info      *bus_wr_hw_info;
 };
 
 static int cam_sfe_bus_subscribe_error_irq(
@@ -3166,6 +3168,76 @@ end:
 	return 0;
 }
 
+static int cam_sfe_bus_wr_irq_inject(
+	void *priv, void *cmd_args, uint32_t arg_size)
+{
+	struct cam_sfe_bus_wr_priv          *bus_priv = NULL;
+	struct cam_hw_soc_info              *soc_info = NULL;
+	struct cam_sfe_bus_wr_hw_info       *bus_wr_hw_info = NULL;
+	struct cam_irq_controller_reg_info  *irq_reg_info = NULL;
+	struct cam_irq_register_set         *inject_reg = NULL;
+	struct cam_isp_irq_inject_param     *inject_params = NULL;
+
+	bus_priv = (struct cam_sfe_bus_wr_priv *)priv;
+	soc_info = bus_priv->common_data.soc_info;
+	bus_wr_hw_info = (struct cam_sfe_bus_wr_hw_info *)bus_priv->bus_wr_hw_info;
+	irq_reg_info = &bus_wr_hw_info->common_reg.irq_reg_info;
+	inject_reg = irq_reg_info->irq_reg_set;
+	inject_params = (struct cam_isp_irq_inject_param *)cmd_args;
+
+	if (!inject_params || !inject_reg) {
+		CAM_INFO(CAM_SFE, "Invalid inject_params or inject_reg");
+		return -EINVAL;
+	}
+
+	if (inject_params->reg_unit ==
+		CAM_ISP_SFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG) {
+
+		cam_io_w_mb(inject_params->irq_mask,
+			soc_info->reg_map[SFE_CORE_BASE_IDX].mem_base +
+			inject_reg->set_reg_offset);
+		cam_io_w_mb(0x10, soc_info->reg_map[SFE_CORE_BASE_IDX].mem_base +
+			irq_reg_info->global_irq_cmd_offset);
+		CAM_INFO(CAM_SFE, "Injected : irq_mask %#x set_reg_offset %#x",
+			inject_params->irq_mask, inject_reg->set_reg_offset);
+	}
+	return 0;
+}
+
+static int cam_sfe_bus_wr_dump_irq_desc(
+	void *priv, void *cmd_args, uint32_t arg_size)
+{
+	int                                          i, offset = 0;
+	struct cam_sfe_bus_wr_priv                  *bus_priv = NULL;
+	struct cam_sfe_bus_wr_hw_info               *bus_wr_hw_info = NULL;
+	struct cam_isp_irq_inject_param             *inject_params = NULL;
+
+	if (!cmd_args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	bus_priv = (struct cam_sfe_bus_wr_priv *)priv;
+	bus_wr_hw_info = (struct cam_sfe_bus_wr_hw_info *)bus_priv->bus_wr_hw_info;
+	inject_params = (struct cam_isp_irq_inject_param *)cmd_args;
+
+	if (inject_params->reg_unit ==
+			CAM_ISP_SFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG) {
+		offset += scnprintf(inject_params->line_buf + offset,
+			LINE_BUFFER_LEN - offset,
+			"Printing executable IRQ for hw_type: SFE reg_unit: %d\n",
+			inject_params->reg_unit);
+
+		for (i = 0; i < bus_wr_hw_info->num_bus_wr_errors; i++)
+			offset += scnprintf(inject_params->line_buf + offset,
+				LINE_BUFFER_LEN - offset, "%#12x : %s - %s\n",
+				bus_wr_hw_info->bus_wr_err_desc[i].bitmask,
+				bus_wr_hw_info->bus_wr_err_desc[i].err_name,
+				bus_wr_hw_info->bus_wr_err_desc[i].desc);
+	}
+	return 0;
+}
+
 static int cam_sfe_bus_wr_start_hw(void *hw_priv,
 	void *start_hw_args, uint32_t arg_size)
 {
@@ -3353,6 +3425,12 @@ static int cam_sfe_bus_wr_process_cmd(
 	case CAM_ISP_HW_CMD_GET_RES_FOR_MID:
 		rc = cam_sfe_bus_wr_get_res_for_mid(priv, cmd_args, arg_size);
 		break;
+	case CAM_ISP_HW_CMD_IRQ_INJECTION:
+		rc = cam_sfe_bus_wr_irq_inject(priv, cmd_args, arg_size);
+		break;
+	case CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION:
+		rc = cam_sfe_bus_wr_dump_irq_desc(priv, cmd_args, arg_size);
+		break;
 	default:
 		CAM_ERR_RATE_LIMIT(CAM_SFE, "Invalid HW command type:%d",
 			cmd_type);
@@ -3400,27 +3478,29 @@ int cam_sfe_bus_wr_init(
 	}
 	sfe_bus_local->bus_priv = bus_priv;
 
-	bus_priv->num_client                       = hw_info->num_client;
-	bus_priv->num_out                          = hw_info->num_out;
-	bus_priv->max_out_res                      = hw_info->max_out_res;
-	bus_priv->num_comp_grp                     = hw_info->num_comp_grp;
-	bus_priv->top_irq_shift                    = hw_info->top_irq_shift;
-	bus_priv->common_data.num_sec_out          = 0;
-	bus_priv->common_data.secure_mode          = CAM_SECURE_MODE_NON_SECURE;
-	bus_priv->common_data.core_index           = soc_info->index;
-	bus_priv->common_data.mem_base             =
+	bus_priv->num_client                        = hw_info->num_client;
+	bus_priv->num_out                           = hw_info->num_out;
+	bus_priv->max_out_res                       = hw_info->max_out_res;
+	bus_priv->num_comp_grp                      = hw_info->num_comp_grp;
+	bus_priv->top_irq_shift                     = hw_info->top_irq_shift;
+	bus_priv->common_data.num_sec_out           = 0;
+	bus_priv->common_data.secure_mode           = CAM_SECURE_MODE_NON_SECURE;
+	bus_priv->common_data.core_index            = soc_info->index;
+	bus_priv->common_data.mem_base              =
 		CAM_SOC_GET_REG_MAP_START(soc_info, SFE_CORE_BASE_IDX);
-	bus_priv->common_data.hw_intf              = hw_intf;
-	bus_priv->common_data.common_reg           = &hw_info->common_reg;
-	bus_priv->common_data.line_done_cfg        = hw_info->line_done_cfg;
-	bus_priv->common_data.pack_align_shift     = hw_info->pack_align_shift;
-	bus_priv->common_data.max_bw_counter_limit = hw_info->max_bw_counter_limit;
-	bus_priv->common_data.err_irq_subscribe    = false;
-	bus_priv->common_data.sfe_irq_controller   = sfe_irq_controller;
-	bus_priv->common_data.irq_err_mask         = hw_info->irq_err_mask;
+	bus_priv->common_data.hw_intf               = hw_intf;
+	bus_priv->common_data.common_reg            = &hw_info->common_reg;
+	bus_priv->common_data.line_done_cfg         = hw_info->line_done_cfg;
+	bus_priv->common_data.pack_align_shift      = hw_info->pack_align_shift;
+	bus_priv->common_data.max_bw_counter_limit  = hw_info->max_bw_counter_limit;
+	bus_priv->common_data.err_irq_subscribe     = false;
+	bus_priv->common_data.sfe_irq_controller    = sfe_irq_controller;
+	bus_priv->common_data.irq_err_mask          = hw_info->irq_err_mask;
 	bus_priv->common_data.sys_cache_default_cfg = hw_info->sys_cache_default_val;
-	bus_priv->constraint_error_info            = hw_info->constraint_error_info;
-	bus_priv->sfe_out_hw_info                  = hw_info->sfe_out_hw_info;
+	bus_priv->common_data.soc_info             = soc_info;
+	bus_priv->constraint_error_info             = hw_info->constraint_error_info;
+	bus_priv->sfe_out_hw_info                   = hw_info->sfe_out_hw_info;
+	bus_priv->bus_wr_hw_info                    = hw_info;
 	rc = cam_cpas_get_cpas_hw_version(&bus_priv->common_data.hw_version);
 	if (rc) {
 		CAM_ERR(CAM_SFE, "Failed to get hw_version rc:%d", rc);

+ 13 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/sfe_hw/sfe_bus/cam_sfe_bus_wr.h

@@ -65,6 +65,17 @@ enum cam_sfe_bus_sfe_out_type {
 	CAM_SFE_BUS_SFE_OUT_MAX,
 };
 
+/*
+ * struct cam_sfe_bus_wr_err_irq_desc:
+ *
+ * @Brief:        Bus wr error irq description
+ */
+struct cam_sfe_bus_wr_err_irq_desc {
+	uint32_t  bitmask;
+	char     *err_name;
+	char     *desc;
+};
+
 /*
  * struct cam_sfe_constraint_error_desc:
  *
@@ -198,6 +209,8 @@ struct cam_sfe_bus_wr_hw_info {
 		sfe_out_hw_info[CAM_SFE_BUS_SFE_OUT_MAX];
 	struct cam_sfe_bus_wr_constraint_error_info
 		*constraint_error_info;
+	uint32_t num_bus_wr_errors;
+	struct cam_sfe_bus_wr_err_irq_desc *bus_wr_err_desc;
 	uint32_t comp_done_mask[CAM_SFE_BUS_WR_COMP_GRP_MAX];
 	uint32_t num_comp_grp;
 	uint32_t line_done_cfg;

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

@@ -486,6 +486,7 @@ int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 	struct cam_hw_info                *vfe_hw = hw_priv;
 	struct cam_hw_soc_info            *soc_info = NULL;
 	struct cam_vfe_hw_core_info       *core_info = NULL;
+	struct cam_vfe_hw_info            *hw_info = NULL;
 	int rc = 0;
 
 	if (!hw_priv) {
@@ -495,6 +496,7 @@ int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 
 	soc_info = &vfe_hw->soc_info;
 	core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+	hw_info = core_info->vfe_hw_info;
 
 	switch (cmd_type) {
 	case CAM_ISP_HW_CMD_GET_CHANGE_BASE:
@@ -535,6 +537,8 @@ int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type,
 	case CAM_ISP_HW_CMD_BUF_UPDATE:
 	case CAM_ISP_HW_USER_DUMP:
 	case CAM_ISP_HW_CMD_MC_CTXT_SEL:
+	case CAM_ISP_HW_CMD_IRQ_INJECTION:
+	case CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION:
 		rc = core_info->vfe_bus->hw_ops.process_cmd(
 			core_info->vfe_bus->bus_priv, cmd_type, cmd_args,
 			arg_size);

+ 45 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe780.h

@@ -963,6 +963,47 @@ static uint32_t vfe780_out_port_mid[][4] = {
 	{22, 0, 0, 0},
 };
 
+static struct cam_vfe_bus_ver3_err_irq_desc vfe780_bus_irq_err_desc_0[] = {
+	{
+		.bitmask = BIT(26),
+		.err_name = "IPCC_FENCE_DATA_ERR",
+		.desc = "IPCC or FENCE Data was not available in the Input Fifo",
+	},
+	{
+		.bitmask = BIT(27),
+		.err_name = "IPCC_FENCE_ADDR_ERR",
+		.desc = "IPCC or FENCE address fifo was empty and read was attempted",
+	},
+	{
+		.bitmask = BIT(28),
+		.err_name = "CONS_VIOLATION",
+		.desc = "Programming of software registers violated the constraints",
+	},
+	{
+		.bitmask = BIT(30),
+		.err_name = "VIOLATION",
+		.desc = "Client has a violation in ccif protocol at input",
+	},
+	{
+		.bitmask = BIT(31),
+		.err_name = "IMAGE_SIZE_VIOLATION",
+		.desc = "Programmed image size is not same as image size from the CCIF",
+	},
+};
+
+static struct cam_vfe_bus_ver3_err_irq_desc vfe780_bus_irq_err_desc_1[] = {
+	{
+		.bitmask = BIT(28),
+		.err_name = "EARLY_DONE",
+		.desc = "Buf done for each client. Early done irq for clients STATS_BAF",
+	},
+	{
+		.bitmask = BIT(29),
+		.err_name = "EARLY_DONE",
+		.desc = "Buf done for each client. Early done irq for clients STATS_BAF",
+	},
+};
+
 static struct cam_vfe_bus_ver3_hw_info vfe780_bus_hw_info = {
 	.common_reg = {
 		.hw_version                       = 0x00000C00,
@@ -2312,6 +2353,10 @@ static struct cam_vfe_bus_ver3_hw_info vfe780_bus_hw_info = {
 			.error_description = "Meta Stride unalign"
 		},
 	},
+	.num_bus_errors_0      = ARRAY_SIZE(vfe780_bus_irq_err_desc_0),
+	.num_bus_errors_1      = ARRAY_SIZE(vfe780_bus_irq_err_desc_1),
+	.bus_err_desc_0        = vfe780_bus_irq_err_desc_0,
+	.bus_err_desc_1        = vfe780_bus_irq_err_desc_1,
 	.num_comp_grp          = 15,
 	.support_consumed_addr = true,
 	.comp_done_mask = {

+ 45 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe17x/cam_vfe880.h

@@ -1008,6 +1008,47 @@ static uint32_t vfe880_out_port_mid[][4] = {
 	{30, 0, 0, 0},
 };
 
+static struct cam_vfe_bus_ver3_err_irq_desc vfe880_bus_irq_err_desc_0[] = {
+	{
+		.bitmask = BIT(26),
+		.err_name = "IPCC_FENCE_DATA_ERR",
+		.desc = "IPCC or FENCE Data was not available in the Input Fifo",
+	},
+	{
+		.bitmask = BIT(27),
+		.err_name = "IPCC_FENCE_ADDR_ERR",
+		.desc = "IPCC or FENCE address fifo was empty and read was attempted",
+	},
+	{
+		.bitmask = BIT(28),
+		.err_name = "CONS_VIOLATION",
+		.desc = "Programming of software registers violated the constraints",
+	},
+	{
+		.bitmask = BIT(30),
+		.err_name = "VIOLATION",
+		.desc = "Client has a violation in ccif protocol at input",
+	},
+	{
+		.bitmask = BIT(31),
+		.err_name = "IMAGE_SIZE_VIOLATION",
+		.desc = "Programmed image size is not same as image size from the CCIF",
+	},
+};
+
+static struct cam_vfe_bus_ver3_err_irq_desc vfe880_bus_irq_err_desc_1[] = {
+	{
+		.bitmask = BIT(28),
+		.err_name = "EARLY_DONE",
+		.desc = "Buf done for each client. Early done irq for clients STATS_BAF",
+	},
+	{
+		.bitmask = BIT(29),
+		.err_name = "EARLY_DONE",
+		.desc = "Buf done for each client. Early done irq for clients STATS_BAF",
+	},
+};
+
 static struct cam_vfe_bus_ver3_hw_info vfe880_bus_hw_info = {
 	.common_reg = {
 		.hw_version                       = 0x00000C00,
@@ -2404,6 +2445,10 @@ static struct cam_vfe_bus_ver3_hw_info vfe880_bus_hw_info = {
 			.error_description = "Meta Stride unalign"
 		},
 	},
+	.num_bus_errors_0      = ARRAY_SIZE(vfe880_bus_irq_err_desc_0),
+	.num_bus_errors_1      = ARRAY_SIZE(vfe880_bus_irq_err_desc_1),
+	.bus_err_desc_0        = vfe880_bus_irq_err_desc_0,
+	.bus_err_desc_1        = vfe880_bus_irq_err_desc_1,
 	.num_comp_grp          = 16,
 	.support_consumed_addr = true,
 	.comp_done_mask = {

+ 115 - 16
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver3.c

@@ -88,6 +88,7 @@ struct cam_vfe_bus_ver3_common_data {
 	void                                       *vfe_irq_controller;
 	void                                       *buf_done_controller;
 	void                                       *priv;
+	struct cam_hw_soc_info                     *soc_info;
 	struct cam_vfe_bus_ver3_reg_offset_common  *common_reg;
 	struct cam_cdm_utils_ops                   *cdm_util_ops;
 	uint32_t                                    io_buf_update[
@@ -209,24 +210,25 @@ struct cam_vfe_bus_ver3_vfe_out_data {
 };
 
 struct cam_vfe_bus_ver3_priv {
-	struct cam_vfe_bus_ver3_common_data common_data;
-	uint32_t                            num_client;
-	uint32_t                            num_out;
-	uint32_t                            num_comp_grp;
-	uint32_t                            top_irq_shift;
-
-	struct cam_isp_resource_node       *bus_client;
-	struct cam_isp_resource_node       *comp_grp;
-	struct cam_isp_resource_node       *vfe_out;
+	struct cam_vfe_bus_ver3_common_data    common_data;
+	uint32_t                               num_client;
+	uint32_t                               num_out;
+	uint32_t                               num_comp_grp;
+	uint32_t                               top_irq_shift;
+
+	struct cam_isp_resource_node          *bus_client;
+	struct cam_isp_resource_node          *comp_grp;
+	struct cam_isp_resource_node          *vfe_out;
 	uint32_t  vfe_out_map_outtype[CAM_VFE_BUS_VER3_VFE_OUT_MAX];
 
-	int                                 bus_irq_handle;
-	int                                 rup_irq_handle;
-	int                                 error_irq_handle;
-	void                               *tasklet_info;
-	uint32_t                            max_out_res;
-	uint32_t                            num_cons_err;
-	struct cam_vfe_constraint_error_info      *constraint_error_list;
+	int                                    bus_irq_handle;
+	int                                    rup_irq_handle;
+	int                                    error_irq_handle;
+	void                                  *tasklet_info;
+	uint32_t                               max_out_res;
+	uint32_t                               num_cons_err;
+	struct cam_vfe_constraint_error_info  *constraint_error_list;
+	struct cam_vfe_bus_ver3_hw_info       *bus_hw_info;
 };
 
 static void cam_vfe_bus_ver3_unsubscribe_init_irq(
@@ -4179,6 +4181,95 @@ static int cam_vfe_bus_ver3_mc_ctxt_sel(
 	return 0;
 }
 
+static int cam_vfe_bus_ver3_irq_inject(
+	void *priv, void *cmd_args, uint32_t arg_size)
+{
+	struct cam_vfe_bus_ver3_priv      *bus_priv = NULL;
+	struct cam_hw_soc_info            *soc_info = NULL;
+	struct cam_vfe_bus_ver3_hw_info   *bus_hw_info = NULL;
+	struct cam_isp_irq_inject_param   *inject_params = NULL;
+	struct cam_irq_register_set       *inject_reg = NULL;
+
+	if (!cmd_args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	bus_priv = (struct cam_vfe_bus_ver3_priv  *)priv;
+	soc_info = bus_priv->common_data.soc_info;
+	bus_hw_info = (struct cam_vfe_bus_ver3_hw_info *)bus_priv->bus_hw_info;
+	inject_params = (struct cam_isp_irq_inject_param *)cmd_args;
+
+	if (inject_params->reg_unit ==
+		CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG)
+		inject_reg = &bus_hw_info->common_reg.irq_reg_info.irq_reg_set[0];
+	else if (inject_params->reg_unit ==
+		CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_1_REG)
+		inject_reg = &bus_hw_info->common_reg.irq_reg_info.irq_reg_set[1];
+	else
+		return -EINVAL;
+
+	if (!inject_reg) {
+		CAM_INFO(CAM_ISP, "Invalid inject_reg");
+		return -EINVAL;
+	}
+
+	cam_io_w_mb(inject_params->irq_mask,
+		soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base +
+		inject_reg->set_reg_offset);
+	cam_io_w_mb(0x10, soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base +
+		bus_hw_info->common_reg.irq_reg_info.global_irq_cmd_offset);
+	CAM_INFO(CAM_ISP, "Injected : irq_mask %#x set_reg_offset %#x",
+		inject_params->irq_mask, inject_reg->set_reg_offset);
+
+	return 0;
+}
+
+static int cam_vfe_bus_ver3_dump_irq_desc(
+	void *priv, void *cmd_args, uint32_t arg_size)
+{
+	int                                   i, offset = 0;
+	int                                   num_irq_desc = 0;
+	struct cam_vfe_bus_ver3_priv         *bus_priv = NULL;
+	struct cam_vfe_bus_ver3_hw_info      *bus_hw_info = NULL;
+	struct cam_isp_irq_inject_param      *inject_params = NULL;
+	struct cam_vfe_bus_ver3_err_irq_desc *err_irq_desc = NULL;
+
+	if (!cmd_args) {
+		CAM_ERR(CAM_ISP, "Invalid params");
+		return -EINVAL;
+	}
+
+	bus_priv = (struct cam_vfe_bus_ver3_priv *)priv;
+	bus_hw_info = (struct cam_vfe_bus_ver3_hw_info *)bus_priv->bus_hw_info;
+	inject_params = (struct cam_isp_irq_inject_param *)cmd_args;
+
+	if (inject_params->reg_unit ==
+			CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_0_REG) {
+		err_irq_desc = bus_hw_info->bus_err_desc_0;
+		num_irq_desc = bus_hw_info->num_bus_errors_0;
+	} else if (inject_params->reg_unit ==
+			CAM_ISP_IFE_0_BUS_WR_INPUT_IF_IRQ_SET_1_REG) {
+		err_irq_desc = bus_hw_info->bus_err_desc_1;
+		num_irq_desc = bus_hw_info->num_bus_errors_1;
+	} else
+		return -EINVAL;
+
+	offset += scnprintf(inject_params->line_buf + offset,
+		LINE_BUFFER_LEN - offset,
+		"Printing executable IRQ for hw_type: VFE reg_unit: %d\n",
+		inject_params->reg_unit);
+
+	for (i = 0; i < num_irq_desc; i++)
+		offset += scnprintf(inject_params->line_buf + offset,
+			LINE_BUFFER_LEN - offset, "%#12x : %s - %s\n",
+			err_irq_desc[i].bitmask,
+			err_irq_desc[i].err_name,
+			err_irq_desc[i].desc);
+
+	return 0;
+}
+
 static int cam_vfe_bus_ver3_start_hw(void *hw_priv,
 	void *start_hw_args, uint32_t arg_size)
 {
@@ -4412,6 +4503,12 @@ static int cam_vfe_bus_ver3_process_cmd(
 		rc = cam_vfe_bus_ver3_mc_ctxt_sel(priv, cmd_args, arg_size);
 		break;
 
+	case CAM_ISP_HW_CMD_IRQ_INJECTION:
+		rc = cam_vfe_bus_ver3_irq_inject(priv, cmd_args, arg_size);
+		break;
+	case CAM_ISP_HW_CMD_DUMP_IRQ_DESCRIPTION:
+		rc = cam_vfe_bus_ver3_dump_irq_desc(priv, cmd_args, arg_size);
+		break;
 	default:
 		CAM_ERR_RATE_LIMIT(CAM_ISP, "VFE:%u Invalid camif process command:%d",
 			priv->hw_intf->hw_idx, cmd_type);
@@ -4498,6 +4595,8 @@ int cam_vfe_bus_ver3_init(
 		ver3_hw_info->max_bw_counter_limit;
 	bus_priv->num_cons_err = ver3_hw_info->num_cons_err;
 	bus_priv->constraint_error_list = ver3_hw_info->constraint_error_list;
+	bus_priv->common_data.soc_info = soc_info;
+	bus_priv->bus_hw_info = ver3_hw_info;
 
 	if (bus_priv->num_out >= CAM_VFE_BUS_VER3_VFE_OUT_MAX) {
 		CAM_ERR(CAM_ISP, "VFE:%u number of vfe out:%d more than max value:%d ",

+ 15 - 0
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver3.h

@@ -99,6 +99,17 @@ enum cam_vfe_bus_ver3_vfe_out_type {
 	CAM_VFE_BUS_VER3_VFE_OUT_MAX,
 };
 
+/*
+ * struct cam_vfe_bus_ver3_err_irq_desc:
+ *
+ * @Brief:        Bus error irq description
+ */
+struct cam_vfe_bus_ver3_err_irq_desc {
+	uint32_t  bitmask;
+	char     *err_name;
+	char     *desc;
+};
+
 /*
  * struct cam_vfe_constraint_error_info:
  *
@@ -249,6 +260,10 @@ struct cam_vfe_bus_ver3_hw_info {
 	uint32_t num_cons_err;
 	struct cam_vfe_constraint_error_info
 		constraint_error_list[CAM_VFE_BUS_VER3_CONS_ERR_MAX];
+	uint32_t num_bus_errors_0;
+	uint32_t num_bus_errors_1;
+	struct cam_vfe_bus_ver3_err_irq_desc *bus_err_desc_0;
+	struct cam_vfe_bus_ver3_err_irq_desc *bus_err_desc_1;
 	uint32_t num_comp_grp;
 	uint32_t comp_done_mask[CAM_VFE_BUS_VER3_COMP_GRP_MAX];
 	uint32_t top_irq_shift;