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

Commit a3770ced authored by Alok Chauhan's avatar Alok Chauhan
Browse files

msm: camera: ope: Add logic to detect hang in CDM



It is possible that cdm clients get callback from CDM
in sometime due to delay in scheduling CDM workqueue.

Add a logic to detect this delay so that client don't
detect false hang.

CRs-Fixed: 2640897
Change-Id: I1a9fdc1ac5d6bcc1869802793632bf6bf8b4c2ca
Signed-off-by: default avatarAlok Chauhan <alokc@codeaurora.org>
parent da36f5f5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -373,6 +373,7 @@ enum cam_cdm_hw_process_intf_cmd {
	CAM_CDM_HW_INTF_CMD_RESET_HW,
	CAM_CDM_HW_INTF_CMD_FLUSH_HW,
	CAM_CDM_HW_INTF_CMD_HANDLE_ERROR,
	CAM_CDM_HW_INTF_CMD_HANG_DETECT,
	CAM_CDM_HW_INTF_CMD_INVALID,
};

@@ -469,6 +470,7 @@ struct cam_cdm_bl_fifo {
	uint8_t bl_tag;
	uint32_t bl_depth;
	uint8_t last_bl_tag_done;
	uint32_t work_record;
};

/**
+35 −0
Original line number Diff line number Diff line
@@ -785,6 +785,41 @@ int cam_cdm_process_cmd(void *hw_priv,
		mutex_unlock(&cdm_hw->hw_mutex);
		break;
	}
	case CAM_CDM_HW_INTF_CMD_HANG_DETECT: {
		uint32_t *handle = cmd_args;
		int idx;
		struct cam_cdm_client *client;

		if (sizeof(uint32_t) != arg_size) {
			CAM_ERR(CAM_CDM,
				"Invalid CDM cmd %d size=%x for handle=%x",
				cmd, arg_size, *handle);
				return -EINVAL;
		}

		idx = CAM_CDM_GET_CLIENT_IDX(*handle);
		mutex_lock(&cdm_hw->hw_mutex);
		client = core->clients[idx];
		if (!client) {
			CAM_ERR(CAM_CDM,
				"Client not present for handle %d",
				*handle);
			mutex_unlock(&cdm_hw->hw_mutex);
			break;
		}

		if (*handle != client->handle) {
			CAM_ERR(CAM_CDM,
				"handle mismatch, client handle %d index %d received handle %d",
				client->handle, idx, *handle);
			mutex_unlock(&cdm_hw->hw_mutex);
			break;
		}

		rc = cam_hw_cdm_hang_detect(cdm_hw, *handle);
		mutex_unlock(&cdm_hw->hw_mutex);
		break;
	}
	default:
		CAM_ERR(CAM_CDM, "CDM HW intf command not valid =%d", cmd);
		break;
+2 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

#ifndef _CAM_CDM_CORE_COMMON_H_
@@ -50,6 +50,7 @@ int cam_hw_cdm_submit_bl(struct cam_hw_info *cdm_hw,
int cam_hw_cdm_reset_hw(struct cam_hw_info *cdm_hw, uint32_t handle);
int cam_hw_cdm_flush_hw(struct cam_hw_info *cdm_hw, uint32_t handle);
int cam_hw_cdm_handle_error(struct cam_hw_info *cdm_hw, uint32_t handle);
int cam_hw_cdm_hang_detect(struct cam_hw_info *cdm_hw, uint32_t handle);
struct cam_cdm_bl_cb_request_entry *cam_cdm_find_request_by_bl_tag(
	uint32_t tag, struct list_head *bl_list);
void cam_cdm_notify_clients(struct cam_hw_info *cdm_hw,
+53 −4
Original line number Diff line number Diff line
@@ -301,7 +301,11 @@ void cam_hw_cdm_dump_core_debug_registers(
	struct cam_cdm *core = (struct cam_cdm *)cdm_hw->core_info;

	cam_cdm_read_hw_reg(cdm_hw, core->offsets->cmn_reg->core_en, &dump_reg);
	CAM_ERR(CAM_CDM, "CDM HW core status=%x", dump_reg);
	CAM_INFO(CAM_CDM, "CDM HW core status=%x", dump_reg);

	cam_cdm_read_hw_reg(cdm_hw, core->offsets->cmn_reg->usr_data,
		&dump_reg);
	CAM_INFO(CAM_CDM, "CDM HW core userdata=0x%x", dump_reg);

	usleep_range(1000, 1010);

@@ -1076,6 +1080,7 @@ static void cam_hw_cdm_reset_cleanup(
		}
		core->bl_fifo[i].bl_tag = 0;
		core->bl_fifo[i].last_bl_tag_done = -1;
		core->bl_fifo[i].work_record = 0;
	}
}

@@ -1102,8 +1107,10 @@ static void cam_hw_cdm_work(struct work_struct *work)
			CAM_CDM_IRQ_STATUS_INLINE_IRQ_MASK) {
			struct cam_cdm_bl_cb_request_entry *node, *tnode;

			CAM_DBG(CAM_CDM, "inline IRQ data=0x%x",
				payload->irq_data);
			CAM_DBG(CAM_CDM, "inline IRQ data=0x%x last tag: 0x%x",
				payload->irq_data,
				core->bl_fifo[payload->fifo_idx]
					.last_bl_tag_done);

			if (payload->irq_data == 0xff) {
				CAM_INFO(CAM_CDM, "Debug genirq received");
@@ -1114,6 +1121,10 @@ static void cam_hw_cdm_work(struct work_struct *work)

			mutex_lock(&core->bl_fifo[payload->fifo_idx]
				.fifo_lock);

			if (core->bl_fifo[payload->fifo_idx].work_record)
				core->bl_fifo[payload->fifo_idx].work_record--;

			if (core->bl_fifo[payload->fifo_idx]
				.last_bl_tag_done !=
				payload->irq_data) {
@@ -1145,6 +1156,10 @@ static void cam_hw_cdm_work(struct work_struct *work)
					kfree(node);
					node = NULL;
				}
			} else {
				CAM_DBG(CAM_CDM,
					"Skip GenIRQ, tag 0x%x fifo %d",
					payload->irq_data, payload->fifo_idx);
			}
			mutex_unlock(&core->bl_fifo[payload->fifo_idx]
				.fifo_lock);
@@ -1291,6 +1306,10 @@ irqreturn_t cam_hw_cdm_irq(int irq_num, void *data)
				CAM_CDM_IRQ_STATUS_USR_DATA_MASK;
		}

		CAM_DBG(CAM_CDM,
			"Rcvd of fifo %d userdata 0x%x tag 0x%x irq_stat 0x%x",
			i, user_data, payload[i]->irq_data, irq_status[i]);

		payload[i]->fifo_idx = i;
		payload[i]->irq_status = irq_status[i];
		payload[i]->hw = cdm_hw;
@@ -1302,6 +1321,7 @@ irqreturn_t cam_hw_cdm_irq(int irq_num, void *data)
			payload[i]->irq_status,
			cdm_hw->soc_info.index);

		cdm_core->bl_fifo[i].work_record++;
		work_status = queue_work(
				cdm_core->bl_fifo[i].work_queue,
				&payload[i]->work);
@@ -1597,6 +1617,33 @@ int cam_hw_cdm_handle_error(
	return rc;
}

int cam_hw_cdm_hang_detect(
	struct cam_hw_info *cdm_hw,
	uint32_t            handle)
{
	struct cam_cdm *cdm_core = NULL;
	int i, rc = -1;

	cdm_core = (struct cam_cdm *)cdm_hw->core_info;

	for (i = 0; i < cdm_core->offsets->reg_data->num_bl_fifo; i++)
		mutex_lock(&cdm_core->bl_fifo[i].fifo_lock);

	for (i = 0; i < cdm_core->offsets->reg_data->num_bl_fifo; i++)
		if (cdm_core->bl_fifo[i].work_record) {
			CAM_WARN(CAM_CDM,
				"workqueue got delayed, work_record :%u",
				cdm_core->bl_fifo[i].work_record);
			rc = 0;
			break;
		}

	for (i = 0; i < cdm_core->offsets->reg_data->num_bl_fifo; i++)
		mutex_unlock(&cdm_core->bl_fifo[i].fifo_lock);

	return rc;
}

int cam_hw_cdm_get_cdm_config(struct cam_hw_info *cdm_hw)
{
	struct cam_hw_soc_info *soc_info = NULL;
@@ -1692,8 +1739,10 @@ int cam_hw_cdm_init(void *hw_priv,
		clear_bit(i, &cdm_core->cdm_status);
		reinit_completion(&cdm_core->bl_fifo[i].bl_complete);
	}
	for (i = 0; i < cdm_core->offsets->reg_data->num_bl_fifo; i++)
	for (i = 0; i < cdm_core->offsets->reg_data->num_bl_fifo; i++) {
		cdm_core->bl_fifo[i].last_bl_tag_done = -1;
		cdm_core->bl_fifo[i].work_record = 0;
	}

	rc = cam_hw_cdm_reset_hw(cdm_hw, reset_hw_hdl);

+28 −1
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

#include <linux/delay.h>
@@ -479,6 +479,33 @@ int cam_cdm_handle_error(uint32_t handle)
}
EXPORT_SYMBOL(cam_cdm_handle_error);

int cam_cdm_detect_hang_error(uint32_t handle)
{
	uint32_t hw_index;
	int rc = -EINVAL;
	struct cam_hw_intf *hw;

	if (get_cdm_mgr_refcount()) {
		CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed");
		rc = -EPERM;
		return rc;
	}

	hw_index = CAM_CDM_GET_HW_IDX(handle);
	if (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM) {
		hw = cdm_mgr.nodes[hw_index].device;
		if (hw && hw->hw_ops.process_cmd)
			rc = hw->hw_ops.process_cmd(hw->hw_priv,
				CAM_CDM_HW_INTF_CMD_HANG_DETECT,
				&handle,
				sizeof(handle));
	}
	put_cdm_mgr_refcount();

	return rc;
}
EXPORT_SYMBOL(cam_cdm_detect_hang_error);

int cam_cdm_intf_register_hw_cdm(struct cam_hw_intf *hw,
	struct cam_cdm_private_dt_data *data, enum cam_cdm_type type,
	uint32_t *index)
Loading