Browse Source

msm: camera: smmu: Add support for non-contiguous mermory region

Add support to discard a memory region inside the full dma map
virtual address space region.

CRs-Fixed: 2580128
Change-Id: I76cc778f2437a01a4efabec836ce92c47d983d61
Signed-off-by: Pavan Kumar Chilamkurthi <[email protected]>
Pavan Kumar Chilamkurthi 5 years ago
parent
commit
efc0528e8a

+ 2 - 0
drivers/cam_icp/fw_inc/hfi_intf.h

@@ -32,6 +32,7 @@ struct hfi_mem {
  * @sec_heap: secondary heap hfi memory for firmware
  * @qdss: qdss mapped memory for fw
  * @io_mem: io memory info
+ * @io_mem2: 2nd io memory info
  * @icp_base: icp base address
  */
 struct hfi_mem_info {
@@ -44,6 +45,7 @@ struct hfi_mem_info {
 	struct hfi_mem shmem;
 	struct hfi_mem qdss;
 	struct hfi_mem io_mem;
+	struct hfi_mem io_mem2;
 	void __iomem *icp_base;
 };
 

+ 2 - 0
drivers/cam_icp/fw_inc/hfi_reg.h

@@ -39,6 +39,8 @@
 #define HFI_REG_QDSS_IOVA_SIZE                  0x70
 #define HFI_REG_IO_REGION_IOVA                  0x74
 #define HFI_REG_IO_REGION_SIZE                  0x78
+#define HFI_REG_IO2_REGION_IOVA                 0x7C
+#define HFI_REG_IO2_REGION_SIZE                 0x80
 
 /* end of ICP CSR registers */
 

+ 17 - 0
drivers/cam_icp/hfi.c

@@ -669,6 +669,15 @@ int cam_hfi_resume(struct hfi_mem_info *hfi_mem,
 	cam_io_w_mb((uint32_t)hfi_mem->io_mem.len,
 		icp_base + HFI_REG_IO_REGION_SIZE);
 
+	cam_io_w_mb((uint32_t)hfi_mem->io_mem2.iova,
+		icp_base + HFI_REG_IO2_REGION_IOVA);
+	cam_io_w_mb((uint32_t)hfi_mem->io_mem2.len,
+		icp_base + HFI_REG_IO2_REGION_SIZE);
+
+	CAM_INFO(CAM_HFI, "Resume IO1 : [0x%x 0x%x] IO2 [0x%x 0x%x]",
+		hfi_mem->io_mem.iova, hfi_mem->io_mem.len,
+		hfi_mem->io_mem2.iova, hfi_mem->io_mem2.len);
+
 	return rc;
 }
 
@@ -859,6 +868,14 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 		icp_base + HFI_REG_IO_REGION_IOVA);
 	cam_io_w_mb((uint32_t)hfi_mem->io_mem.len,
 		icp_base + HFI_REG_IO_REGION_SIZE);
+	cam_io_w_mb((uint32_t)hfi_mem->io_mem2.iova,
+		icp_base + HFI_REG_IO2_REGION_IOVA);
+	cam_io_w_mb((uint32_t)hfi_mem->io_mem2.len,
+		icp_base + HFI_REG_IO2_REGION_SIZE);
+
+	CAM_INFO(CAM_HFI, "Init IO1 : [0x%x 0x%x] IO2 [0x%x 0x%x]",
+		hfi_mem->io_mem.iova, hfi_mem->io_mem.len,
+		hfi_mem->io_mem2.iova, hfi_mem->io_mem2.len);
 
 	hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION);
 

+ 63 - 11
drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c

@@ -2858,18 +2858,21 @@ static int cam_icp_allocate_qdss_mem(void)
 static int cam_icp_get_io_mem_info(void)
 {
 	int rc;
-	size_t len;
-	dma_addr_t iova;
+	size_t len, discard_iova_len;
+	dma_addr_t iova, discard_iova_start;
 
 	rc = cam_smmu_get_io_region_info(icp_hw_mgr.iommu_hdl,
-		&iova, &len);
+		&iova, &len, &discard_iova_start, &discard_iova_len);
 	if (rc)
 		return rc;
 
 	icp_hw_mgr.hfi_mem.io_mem.iova_len = len;
 	icp_hw_mgr.hfi_mem.io_mem.iova_start = iova;
+	icp_hw_mgr.hfi_mem.io_mem.discard_iova_start = discard_iova_start;
+	icp_hw_mgr.hfi_mem.io_mem.discard_iova_len = discard_iova_len;
 
-	CAM_DBG(CAM_ICP, "iova: %llx, len: %zu", iova, len);
+	CAM_DBG(CAM_ICP, "iova: %llx, len: %zu discard iova %llx len %llx",
+		iova, len, discard_iova_start, discard_iova_len);
 
 	return rc;
 }
@@ -3162,12 +3165,38 @@ static int cam_icp_mgr_hfi_resume(struct cam_icp_hw_mgr *hw_mgr)
 	hfi_mem.qdss.iova = icp_hw_mgr.hfi_mem.qdss_buf.iova;
 	hfi_mem.qdss.len = icp_hw_mgr.hfi_mem.qdss_buf.len;
 
-	hfi_mem.io_mem.iova = icp_hw_mgr.hfi_mem.io_mem.iova_start;
-	hfi_mem.io_mem.len = icp_hw_mgr.hfi_mem.io_mem.iova_len;
+	if (icp_hw_mgr.hfi_mem.io_mem.discard_iova_start &&
+		icp_hw_mgr.hfi_mem.io_mem.discard_iova_len) {
+		/* IO Region 1 */
+		hfi_mem.io_mem.iova = icp_hw_mgr.hfi_mem.io_mem.iova_start;
+		hfi_mem.io_mem.len =
+			icp_hw_mgr.hfi_mem.io_mem.discard_iova_start -
+			icp_hw_mgr.hfi_mem.io_mem.iova_start;
+
+		/* IO Region 2 */
+		hfi_mem.io_mem2.iova =
+			icp_hw_mgr.hfi_mem.io_mem.discard_iova_start +
+			icp_hw_mgr.hfi_mem.io_mem.discard_iova_len;
+		hfi_mem.io_mem2.len =
+			icp_hw_mgr.hfi_mem.io_mem.iova_start +
+			icp_hw_mgr.hfi_mem.io_mem.iova_len   -
+			hfi_mem.io_mem2.iova;
+	} else {
+		/* IO Region 1 */
+		hfi_mem.io_mem.iova = icp_hw_mgr.hfi_mem.io_mem.iova_start;
+		hfi_mem.io_mem.len = icp_hw_mgr.hfi_mem.io_mem.iova_len;
 
-	CAM_DBG(CAM_ICP, "IO region IOVA = %X length = %lld",
-			hfi_mem.io_mem.iova,
-			hfi_mem.io_mem.len);
+		/* IO Region 2 */
+		hfi_mem.io_mem2.iova = 0x0;
+		hfi_mem.io_mem2.len = 0x0;
+	}
+
+	CAM_DBG(CAM_ICP,
+		"IO region1 IOVA = %X length = %lld, IO region2 IOVA = %X length = %lld",
+		hfi_mem.io_mem.iova,
+		hfi_mem.io_mem.len,
+		hfi_mem.io_mem2.iova,
+		hfi_mem.io_mem2.len);
 
 	return cam_hfi_resume(&hfi_mem,
 		a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base,
@@ -3637,8 +3666,31 @@ static int cam_icp_mgr_hfi_init(struct cam_icp_hw_mgr *hw_mgr)
 	hfi_mem.qdss.iova = icp_hw_mgr.hfi_mem.qdss_buf.iova;
 	hfi_mem.qdss.len = icp_hw_mgr.hfi_mem.qdss_buf.len;
 
-	hfi_mem.io_mem.iova = icp_hw_mgr.hfi_mem.io_mem.iova_start;
-	hfi_mem.io_mem.len = icp_hw_mgr.hfi_mem.io_mem.iova_len;
+	if (icp_hw_mgr.hfi_mem.io_mem.discard_iova_start &&
+		icp_hw_mgr.hfi_mem.io_mem.discard_iova_len) {
+		/* IO Region 1 */
+		hfi_mem.io_mem.iova = icp_hw_mgr.hfi_mem.io_mem.iova_start;
+		hfi_mem.io_mem.len =
+			icp_hw_mgr.hfi_mem.io_mem.discard_iova_start -
+			icp_hw_mgr.hfi_mem.io_mem.iova_start;
+
+		/* IO Region 2 */
+		hfi_mem.io_mem2.iova =
+			icp_hw_mgr.hfi_mem.io_mem.discard_iova_start +
+			icp_hw_mgr.hfi_mem.io_mem.discard_iova_len;
+		hfi_mem.io_mem2.len =
+			icp_hw_mgr.hfi_mem.io_mem.iova_start +
+			icp_hw_mgr.hfi_mem.io_mem.iova_len   -
+			hfi_mem.io_mem2.iova;
+	} else {
+		/* IO Region 1 */
+		hfi_mem.io_mem.iova = icp_hw_mgr.hfi_mem.io_mem.iova_start;
+		hfi_mem.io_mem.len = icp_hw_mgr.hfi_mem.io_mem.iova_len;
+
+		/* IO Region 2 */
+		hfi_mem.io_mem2.iova = 0x0;
+		hfi_mem.io_mem2.len = 0x0;
+	}
 
 	return cam_hfi_init(0, &hfi_mem,
 		a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base,

+ 132 - 4
drivers/cam_smmu/cam_smmu_api.c

@@ -15,6 +15,8 @@
 #include <linux/workqueue.h>
 #include <linux/genalloc.h>
 #include <linux/debugfs.h>
+#include <linux/dma-iommu.h>
+
 #include <soc/qcom/secure_buffer.h>
 
 #include <media/cam_req_mgr.h>
@@ -137,6 +139,10 @@ struct cam_context_bank_info {
 	bool is_mul_client;
 	int device_count;
 	int num_shared_hdl;
+
+	/* discard iova - non-zero values are valid */
+	dma_addr_t discard_iova_start;
+	size_t discard_iova_len;
 };
 
 struct cam_iommu_cb_set {
@@ -1440,11 +1446,13 @@ end:
 EXPORT_SYMBOL(cam_smmu_dealloc_qdss);
 
 int cam_smmu_get_io_region_info(int32_t smmu_hdl,
-	dma_addr_t *iova, size_t *len)
+	dma_addr_t *iova, size_t *len,
+	dma_addr_t *discard_iova_start, size_t *discard_iova_len)
 {
 	int32_t idx;
 
-	if (!iova || !len || (smmu_hdl == HANDLE_INIT)) {
+	if (!iova || !len || !discard_iova_start || !discard_iova_len ||
+		(smmu_hdl == HANDLE_INIT)) {
 		CAM_ERR(CAM_SMMU, "Error: Input args are invalid");
 		return -EINVAL;
 	}
@@ -1466,10 +1474,15 @@ int cam_smmu_get_io_region_info(int32_t smmu_hdl,
 	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
 	*iova = iommu_cb_set.cb_info[idx].io_info.iova_start;
 	*len = iommu_cb_set.cb_info[idx].io_info.iova_len;
+	*discard_iova_start =
+		iommu_cb_set.cb_info[idx].io_info.discard_iova_start;
+	*discard_iova_len =
+		iommu_cb_set.cb_info[idx].io_info.discard_iova_len;
 
 	CAM_DBG(CAM_SMMU,
-		"I/O area for hdl = %x start addr = %pK len = %zu",
-		smmu_hdl, *iova, *len);
+		"I/O area for hdl = %x Region:[%pK %zu] Discard:[%pK %zu]",
+		smmu_hdl, *iova, *len,
+		*discard_iova_start, *discard_iova_len);
 	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
 
 	return 0;
@@ -3339,6 +3352,11 @@ static int cam_smmu_setup_cb(struct cam_context_bank_info *cb,
 			rc = -ENODEV;
 			goto end;
 		}
+
+		if (cb->discard_iova_start)
+			iommu_dma_reserve_iova(dev, cb->discard_iova_start,
+				cb->discard_iova_len);
+
 		cb->state = CAM_SMMU_ATTACH;
 	} else {
 		CAM_ERR(CAM_SMMU, "Context bank does not have IO region");
@@ -3405,6 +3423,52 @@ static int cam_alloc_smmu_context_banks(struct device *dev)
 	return 0;
 }
 
+static int cam_smmu_get_discard_memory_regions(struct device_node *of_node,
+	dma_addr_t *discard_iova_start, size_t *discard_iova_len)
+{
+	uint32_t discard_iova[2] = { 0 };
+	int num_values = 0;
+	int rc = 0;
+
+	if (!discard_iova_start || !discard_iova_len)
+		return -EINVAL;
+
+	*discard_iova_start = 0;
+	*discard_iova_len = 0;
+
+	num_values = of_property_count_u32_elems(of_node,
+		"iova-region-discard");
+	if (num_values <= 0) {
+		CAM_DBG(CAM_UTIL, "No discard region specified");
+		return 0;
+	} else if (num_values != 2) {
+		CAM_ERR(CAM_UTIL, "Invalid discard region specified %d",
+			num_values);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_array(of_node,
+		"iova-region-discard",
+		discard_iova, num_values);
+	if (rc) {
+		CAM_ERR(CAM_UTIL, "Can not read discard region %d", num_values);
+		return rc;
+	} else if (!discard_iova[0] || !discard_iova[1]) {
+		CAM_ERR(CAM_UTIL,
+			"Incorrect Discard region specified [0x%x 0x%x]",
+			discard_iova[0], discard_iova[1]);
+		return -EINVAL;
+	}
+
+	CAM_DBG(CAM_UTIL, "Discard region [0x%x 0x%x]",
+		discard_iova[0], discard_iova[0] + discard_iova[1]);
+
+	*discard_iova_start = discard_iova[0];
+	*discard_iova_len = discard_iova[1];
+
+	return 0;
+}
+
 static int cam_smmu_get_memory_regions_info(struct device_node *of_node,
 	struct cam_context_bank_info *cb)
 {
@@ -3503,6 +3567,16 @@ static int cam_smmu_get_memory_regions_info(struct device_node *of_node,
 			cb->io_support = 1;
 			cb->io_info.iova_start = region_start;
 			cb->io_info.iova_len = region_len;
+			rc = cam_smmu_get_discard_memory_regions(child_node,
+				&cb->io_info.discard_iova_start,
+				&cb->io_info.discard_iova_len);
+			if (rc) {
+				CAM_ERR(CAM_SMMU,
+					"Invalid Discard region specified in IO region, rc=%d",
+					rc);
+				of_node_put(mem_map_node);
+				return -EINVAL;
+			}
 			break;
 		case CAM_SMMU_REGION_SECHEAP:
 			cb->secheap_support = 1;
@@ -3527,6 +3601,60 @@ static int cam_smmu_get_memory_regions_info(struct device_node *of_node,
 		CAM_DBG(CAM_SMMU, "region_len -> %X", region_len);
 		CAM_DBG(CAM_SMMU, "region_id -> %X", region_id);
 	}
+
+	if (cb->io_support) {
+		rc = cam_smmu_get_discard_memory_regions(of_node,
+			&cb->discard_iova_start,
+			&cb->discard_iova_len);
+		if (rc) {
+			CAM_ERR(CAM_SMMU,
+				"Invalid Discard region specified in CB, rc=%d",
+				rc);
+			of_node_put(mem_map_node);
+			return -EINVAL;
+		}
+
+		/* Make sure Discard region is properly specified */
+		if ((cb->discard_iova_start !=
+			cb->io_info.discard_iova_start) ||
+			(cb->discard_iova_len !=
+			cb->io_info.discard_iova_len)) {
+			CAM_ERR(CAM_SMMU,
+				"Mismatch Discard region specified, [0x%x 0x%x] [0x%x 0x%x]",
+				cb->discard_iova_start,
+				cb->discard_iova_len,
+				cb->io_info.discard_iova_start,
+				cb->io_info.discard_iova_len);
+			of_node_put(mem_map_node);
+			return -EINVAL;
+		} else if (cb->discard_iova_start && cb->discard_iova_len) {
+			if ((cb->discard_iova_start <=
+			cb->io_info.iova_start) ||
+			(cb->discard_iova_start >=
+			cb->io_info.iova_start + cb->io_info.iova_len) ||
+			(cb->discard_iova_start + cb->discard_iova_len >=
+			cb->io_info.iova_start + cb->io_info.iova_len)) {
+				CAM_ERR(CAM_SMMU,
+				"[%s] : Incorrect Discard region specified [0x%x 0x%x] in [0x%x 0x%x]",
+				cb->name,
+				cb->discard_iova_start,
+				cb->discard_iova_start + cb->discard_iova_len,
+				cb->io_info.iova_start,
+				cb->io_info.iova_start + cb->io_info.iova_len);
+				of_node_put(mem_map_node);
+				return -EINVAL;
+			}
+
+			CAM_INFO(CAM_SMMU,
+				"[%s] : Discard region specified [0x%x 0x%x] in [0x%x 0x%x]",
+				cb->name,
+				cb->discard_iova_start,
+				cb->discard_iova_start + cb->discard_iova_len,
+				cb->io_info.iova_start,
+				cb->io_info.iova_start + cb->io_info.iova_len);
+		}
+	}
+
 	of_node_put(mem_map_node);
 
 	if (!num_regions) {

+ 10 - 3
drivers/cam_smmu/cam_smmu_api.h

@@ -60,12 +60,16 @@ typedef void (*cam_smmu_client_page_fault_handler)(struct iommu_domain *domain,
 /**
  * @brief            : Structure to store region information
  *
- * @param iova_start : Start address of region
- * @param iova_len   : length of region
+ * @param iova_start         : Start address of region
+ * @param iova_len           : length of region
+ * @param discard_iova_start : iova addr start from where should not be used
+ * @param discard_iova_len   : length of discard iova region
  */
 struct cam_smmu_region_info {
 	dma_addr_t iova_start;
 	size_t iova_len;
+	dma_addr_t discard_iova_start;
+	size_t discard_iova_len;
 };
 
 /**
@@ -387,11 +391,14 @@ int cam_smmu_dealloc_qdss(int32_t smmu_hdl);
  * @param smmu_hdl: SMMU handle identifying the context bank
  * @param iova: IOVA address of allocated I/O region
  * @param len: Length of allocated I/O memory
+ * @param discard_iova_start: Start address of io space to discard
+ * @param discard_iova_len: Length of io space to discard
  *
  * @return Status of operation. Negative in case of error. Zero otherwise.
  */
 int cam_smmu_get_io_region_info(int32_t smmu_hdl,
-	dma_addr_t *iova, size_t *len);
+	dma_addr_t *iova, size_t *len,
+	dma_addr_t *discard_iova_start, size_t *discard_iova_len);
 
 /**
  * @brief : API to register SMMU hw to platform framework.