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 <pchilamk@codeaurora.org>
This commit is contained in:
Pavan Kumar Chilamkurthi
2019-12-19 22:26:37 -08:00
parent 1b644126dd
commit efc0528e8a
6 changed files with 226 additions and 18 deletions

View File

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

View File

@@ -39,6 +39,8 @@
#define HFI_REG_QDSS_IOVA_SIZE 0x70 #define HFI_REG_QDSS_IOVA_SIZE 0x70
#define HFI_REG_IO_REGION_IOVA 0x74 #define HFI_REG_IO_REGION_IOVA 0x74
#define HFI_REG_IO_REGION_SIZE 0x78 #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 */ /* end of ICP CSR registers */

View File

@@ -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, cam_io_w_mb((uint32_t)hfi_mem->io_mem.len,
icp_base + HFI_REG_IO_REGION_SIZE); 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; 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); icp_base + HFI_REG_IO_REGION_IOVA);
cam_io_w_mb((uint32_t)hfi_mem->io_mem.len, cam_io_w_mb((uint32_t)hfi_mem->io_mem.len,
icp_base + HFI_REG_IO_REGION_SIZE); 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); hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION);

View File

@@ -2858,18 +2858,21 @@ static int cam_icp_allocate_qdss_mem(void)
static int cam_icp_get_io_mem_info(void) static int cam_icp_get_io_mem_info(void)
{ {
int rc; int rc;
size_t len; size_t len, discard_iova_len;
dma_addr_t iova; dma_addr_t iova, discard_iova_start;
rc = cam_smmu_get_io_region_info(icp_hw_mgr.iommu_hdl, rc = cam_smmu_get_io_region_info(icp_hw_mgr.iommu_hdl,
&iova, &len); &iova, &len, &discard_iova_start, &discard_iova_len);
if (rc) if (rc)
return rc; return rc;
icp_hw_mgr.hfi_mem.io_mem.iova_len = len; 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.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; 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.iova = icp_hw_mgr.hfi_mem.qdss_buf.iova;
hfi_mem.qdss.len = icp_hw_mgr.hfi_mem.qdss_buf.len; 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; if (icp_hw_mgr.hfi_mem.io_mem.discard_iova_start &&
hfi_mem.io_mem.len = icp_hw_mgr.hfi_mem.io_mem.iova_len; 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;
CAM_DBG(CAM_ICP, "IO region IOVA = %X length = %lld", /* IO Region 2 */
hfi_mem.io_mem.iova, hfi_mem.io_mem2.iova =
hfi_mem.io_mem.len); 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;
}
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, return cam_hfi_resume(&hfi_mem,
a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base, 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.iova = icp_hw_mgr.hfi_mem.qdss_buf.iova;
hfi_mem.qdss.len = icp_hw_mgr.hfi_mem.qdss_buf.len; 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; if (icp_hw_mgr.hfi_mem.io_mem.discard_iova_start &&
hfi_mem.io_mem.len = icp_hw_mgr.hfi_mem.io_mem.iova_len; 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, return cam_hfi_init(0, &hfi_mem,
a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base, a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base,

View File

@@ -15,6 +15,8 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/genalloc.h> #include <linux/genalloc.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/dma-iommu.h>
#include <soc/qcom/secure_buffer.h> #include <soc/qcom/secure_buffer.h>
#include <media/cam_req_mgr.h> #include <media/cam_req_mgr.h>
@@ -137,6 +139,10 @@ struct cam_context_bank_info {
bool is_mul_client; bool is_mul_client;
int device_count; int device_count;
int num_shared_hdl; 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 { struct cam_iommu_cb_set {
@@ -1440,11 +1446,13 @@ end:
EXPORT_SYMBOL(cam_smmu_dealloc_qdss); EXPORT_SYMBOL(cam_smmu_dealloc_qdss);
int cam_smmu_get_io_region_info(int32_t smmu_hdl, 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; 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"); CAM_ERR(CAM_SMMU, "Error: Input args are invalid");
return -EINVAL; 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); mutex_lock(&iommu_cb_set.cb_info[idx].lock);
*iova = iommu_cb_set.cb_info[idx].io_info.iova_start; *iova = iommu_cb_set.cb_info[idx].io_info.iova_start;
*len = iommu_cb_set.cb_info[idx].io_info.iova_len; *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, CAM_DBG(CAM_SMMU,
"I/O area for hdl = %x start addr = %pK len = %zu", "I/O area for hdl = %x Region:[%pK %zu] Discard:[%pK %zu]",
smmu_hdl, *iova, *len); smmu_hdl, *iova, *len,
*discard_iova_start, *discard_iova_len);
mutex_unlock(&iommu_cb_set.cb_info[idx].lock); mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
return 0; return 0;
@@ -3339,6 +3352,11 @@ static int cam_smmu_setup_cb(struct cam_context_bank_info *cb,
rc = -ENODEV; rc = -ENODEV;
goto end; 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; cb->state = CAM_SMMU_ATTACH;
} else { } else {
CAM_ERR(CAM_SMMU, "Context bank does not have IO region"); 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; 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, static int cam_smmu_get_memory_regions_info(struct device_node *of_node,
struct cam_context_bank_info *cb) 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_support = 1;
cb->io_info.iova_start = region_start; cb->io_info.iova_start = region_start;
cb->io_info.iova_len = region_len; 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; break;
case CAM_SMMU_REGION_SECHEAP: case CAM_SMMU_REGION_SECHEAP:
cb->secheap_support = 1; 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_len -> %X", region_len);
CAM_DBG(CAM_SMMU, "region_id -> %X", region_id); 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); of_node_put(mem_map_node);
if (!num_regions) { if (!num_regions) {

View File

@@ -60,12 +60,16 @@ typedef void (*cam_smmu_client_page_fault_handler)(struct iommu_domain *domain,
/** /**
* @brief : Structure to store region information * @brief : Structure to store region information
* *
* @param iova_start : Start address of region * @param iova_start : Start address of region
* @param iova_len : length 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 { struct cam_smmu_region_info {
dma_addr_t iova_start; dma_addr_t iova_start;
size_t iova_len; 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 smmu_hdl: SMMU handle identifying the context bank
* @param iova: IOVA address of allocated I/O region * @param iova: IOVA address of allocated I/O region
* @param len: Length of allocated I/O memory * @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. * @return Status of operation. Negative in case of error. Zero otherwise.
*/ */
int cam_smmu_get_io_region_info(int32_t smmu_hdl, 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. * @brief : API to register SMMU hw to platform framework.