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

Commit ce97a916 authored by Lakshmi Narayana Kalavala's avatar Lakshmi Narayana Kalavala
Browse files

msm: camera: support fault handlers for dual vfe usecase



IOMMU fault handler supports only one token per domain.
In Dual vfe use cases, vfe driver registers smmu fault
handler per each device node. Vfe needs to stop if page
fault happens on either vfe device.

Add the support to register fault handlers in the
camera smmu driver per device and trigger the fault handler
for both devices when fault happens on either device.

Change-Id: I7fdf851f595ca968a26cf3f49e490b93bc4535bf
Signed-off-by: default avatarLakshmi Narayana Kalavala <lkalaval@codeaurora.org>
parent 4e44e9c4
Loading
Loading
Loading
Loading
+115 −47
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#define COOKIE_SIZE (BYTE_SIZE*COOKIE_NUM_BYTE)
#define COOKIE_MASK ((1<<COOKIE_SIZE)-1)
#define HANDLE_INIT (-1)
#define CAM_SMMU_CB_MAX 2

#define GET_SMMU_HDL(x, y) (((x) << COOKIE_SIZE) | ((y) & COOKIE_MASK))
#define GET_SMMU_TABLE_IDX(x) (((x) >> COOKIE_SIZE) & COOKIE_MASK)
@@ -80,10 +81,11 @@ struct cam_context_bank_info {
	struct mutex lock;
	int handle;
	enum cam_smmu_ops_param state;
	int (*fault_handler)(struct iommu_domain *,
	int (*handler[CAM_SMMU_CB_MAX])(struct iommu_domain *,
		struct device *, unsigned long,
		int, void*);
	void *token;
	void *token[CAM_SMMU_CB_MAX];
	int cb_count;
};

struct cam_iommu_cb_set {
@@ -200,6 +202,45 @@ static void cam_smmu_print_table(void)
	mutex_unlock(&iommu_table_lock);
}


int cam_smmu_query_vaddr_in_range(int handle,
	unsigned long fault_addr, unsigned long *start_addr,
	unsigned long *end_addr, int *fd)
{
	int idx, rc = -EINVAL;
	struct cam_dma_buff_info *mapping;
	unsigned long sa, ea;

	if (!start_addr || !end_addr || !fd) {
		pr_debug("Invalid params\n");
		return -EINVAL;
	}

	idx = cam_smmu_find_index_by_handle(handle);
	if (idx < 0 || idx >= iommu_cb_set.cb_num) {
		pr_err("Error: index is not valid, index = %d\n", idx);
		return -EINVAL;
	}

	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
	list_for_each_entry(mapping,
		&iommu_cb_set.cb_info[idx].list_head, list) {
		sa = (unsigned long)mapping->paddr;
		ea = (unsigned long)mapping->paddr + mapping->len;

		if (sa <= fault_addr && fault_addr < ea) {
			*start_addr = sa;
			*end_addr = ea;
			*fd = mapping->ion_fd;
			rc = 0;
			break;
		}
	}
	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
	return rc;
}
EXPORT_SYMBOL(cam_smmu_query_vaddr_in_range);

static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
{
	struct cam_dma_buff_info *mapping;
@@ -211,13 +252,14 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
		start_addr = (unsigned long)mapping->paddr;
		end_addr = (unsigned long)mapping->paddr + mapping->len;

		if (start_addr <= current_addr && current_addr <= end_addr) {
			pr_err("Error: vaddr %p is valid: range:%p-%p, ion_fd = %d\n",
		if (start_addr <= current_addr && current_addr < end_addr) {
			pr_err("Error: va %p is valid: range:%p-%p, fd = %d cb: %s\n",
				vaddr, (void *)start_addr, (void *)end_addr,
				mapping->ion_fd);
				mapping->ion_fd,
				iommu_cb_set.cb_info[idx].name);
			return;
		} else {
			pr_err("vaddr %p is not in this range: %p-%p, ion_fd = %d\n",
			CDBG("va %p is not in this range: %p-%p, fd = %d\n",
				vaddr, (void *)start_addr, (void *)end_addr,
				mapping->ion_fd);
		}
@@ -232,7 +274,12 @@ void cam_smmu_reg_client_page_fault_handler(int handle,
		struct device *, unsigned long,
		int, void*), void *token)
{
	int idx;
	int idx, i = 0;

	if (!token) {
		pr_err("Error: token is NULL\n");
		return;
	}

	idx = cam_smmu_find_index_by_handle(handle);
	if (idx < 0 || idx >= iommu_cb_set.cb_num) {
@@ -242,12 +289,33 @@ void cam_smmu_reg_client_page_fault_handler(int handle,
	}

	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
	iommu_cb_set.cb_info[idx].token = token;
	iommu_cb_set.cb_info[idx].fault_handler =
		client_page_fault_handler;
	if (client_page_fault_handler) {
		iommu_cb_set.cb_info[idx].cb_count++;
		if (iommu_cb_set.cb_info[idx].cb_count > CAM_SMMU_CB_MAX) {
			mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
			return;
		}

		for (i = 0; i < iommu_cb_set.cb_info[idx].cb_count; i++) {
			if (iommu_cb_set.cb_info[idx].token[i] == NULL) {
				iommu_cb_set.cb_info[idx].token[i] = token;
				iommu_cb_set.cb_info[idx].handler[i] =
					client_page_fault_handler;
				break;
			}
		}
	} else {
		for (i = 0; i < iommu_cb_set.cb_info[idx].cb_count; i++) {
			if (iommu_cb_set.cb_info[idx].token[i] == token) {
				iommu_cb_set.cb_info[idx].token[i] = NULL;
				iommu_cb_set.cb_info[idx].handler[i] =
					NULL;
				break;
			}
		}
	}
	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
	return;
}

static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,
@@ -255,7 +323,7 @@ static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,
		int flags, void *token)
{
	char *cb_name;
	int i, rc;
	int idx, rc = -ENOSYS, j = 0;

	if (!token) {
		pr_err("Error: token is NULL\n");
@@ -264,27 +332,28 @@ static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,

	cb_name = (char *)token;
	/* check wether it is in the table */
	for (i = 0; i < iommu_cb_set.cb_num; i++) {
		if (!strcmp(iommu_cb_set.cb_info[i].name, cb_name)) {
			mutex_lock(&iommu_cb_set.cb_info[i].lock);
			if (!(iommu_cb_set.cb_info[i].fault_handler)) {
				pr_err("Error: %s: %p has page fault\n",
						(char *)token,
						(void *)iova);
				cam_smmu_check_vaddr_in_range(i,
						(void *)iova);
				rc = -ENOSYS;
			} else {
				rc = iommu_cb_set.cb_info[i].fault_handler(
					domain, dev, iova, flags,
					iommu_cb_set.cb_info[i].token);
	for (idx = 0; idx < iommu_cb_set.cb_num; idx++) {
		if (!strcmp(iommu_cb_set.cb_info[idx].name, cb_name))
			break;
	}
			mutex_unlock(&iommu_cb_set.cb_info[i].lock);

	if (idx < 0 || idx >= iommu_cb_set.cb_num) {
		pr_err("Error: index is not valid, index = %d, token = %s\n",
			idx, cb_name);
		return rc;
	}

	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
	cam_smmu_check_vaddr_in_range(idx, (void *)iova);
	for (j = 0; j < iommu_cb_set.cb_info[idx].cb_count; j++) {
		if ((iommu_cb_set.cb_info[idx].handler[j])) {
			rc = iommu_cb_set.cb_info[idx].handler[j](
					domain, dev, iova, flags,
					iommu_cb_set.cb_info[idx].token[j]);
		}
	pr_err("Error: cb_name %s is not valid.\n", (char *)token);
	return -ENOSYS;
	}
	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
	return rc;
}

static int cam_smmu_translate_dir_to_iommu_dir(
@@ -323,36 +392,33 @@ static enum dma_data_direction cam_smmu_translate_dir(
	return DMA_NONE;
}

void cam_smmu_init_iommu_table(void)
void cam_smmu_reset_iommu_table(void)
{
	unsigned int i;
	mutex_init(&iommu_table_lock);
	int j = 0;
	for (i = 0; i < iommu_cb_set.cb_num; i++) {
		iommu_cb_set.cb_info[i].handle = HANDLE_INIT;
		INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].list_head);
		iommu_cb_set.cb_info[i].state = CAM_SMMU_DETACH;
		iommu_cb_set.cb_info[i].dev = NULL;
		iommu_cb_set.cb_info[i].token = NULL;
		iommu_cb_set.cb_info[i].fault_handler = NULL;
		iommu_cb_set.cb_info[i].cb_count = 0;
		for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
			iommu_cb_set.cb_info[i].token[j] = NULL;
			iommu_cb_set.cb_info[i].handler[j] = NULL;
		}
	}
}

	return;
void cam_smmu_init_iommu_table(void)
{
	mutex_init(&iommu_table_lock);
	cam_smmu_reset_iommu_table();
}

void cam_smmu_deinit_iommu_table(void)
{
	unsigned int i;
	mutex_destroy(&iommu_table_lock);
	for (i = 0; i < iommu_cb_set.cb_num; i++) {
		iommu_cb_set.cb_info[i].handle = HANDLE_INIT;
		INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].list_head);
		iommu_cb_set.cb_info[i].state = CAM_SMMU_DETACH;
		iommu_cb_set.cb_info[i].dev = NULL;
		iommu_cb_set.cb_info[i].token = NULL;
		iommu_cb_set.cb_info[i].fault_handler = NULL;
	}

	return;
	cam_smmu_reset_iommu_table();
}

static int cam_smmu_check_handle_unique(int hdl)
@@ -420,6 +486,7 @@ static int cam_smmu_create_add_handle_in_table(char *name,

			/* put handle in the table */
			iommu_cb_set.cb_info[i].handle = handle;
			iommu_cb_set.cb_info[i].cb_count = 0;
			mutex_init(&iommu_cb_set.cb_info[i].lock);
			*hdl = handle;
			mutex_unlock(&iommu_table_lock);
@@ -1231,6 +1298,7 @@ int cam_smmu_destroy_handle(int handle)
		cam_smmu_print_list(idx);
	}

	iommu_cb_set.cb_info[idx].cb_count = 0;
	mutex_destroy(&iommu_cb_set.cb_info[idx].lock);
	mutex_unlock(&iommu_table_lock);
	return 0;
@@ -1406,7 +1474,7 @@ static int cam_populate_smmu_context_banks(struct device *dev,

	/* set up the iommu mapping for the  context bank */
	ctx = dev;
	pr_info("getting Arm SMMU ctx : %s\n", cb->name);
	CDBG("getting Arm SMMU ctx : %s\n", cb->name);

	rc = cam_smmu_setup_cb(cb, ctx);
	if (rc < 0)
+2 −2
Original line number Diff line number Diff line
@@ -2108,10 +2108,10 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
	/* Unregister page fault handler */
	cam_smmu_reg_client_page_fault_handler(
		vfe_dev->buf_mgr->img_iommu_hdl,
		NULL, NULL);
		NULL, vfe_dev);
	cam_smmu_reg_client_page_fault_handler(
		vfe_dev->buf_mgr->stats_iommu_hdl,
		NULL, NULL);
		NULL, vfe_dev);

	rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
	if (rc <= 0)