| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* Copyright (c) 2007-2020, The Linux Foundation. All rights reserved. */ |
| |
| #ifndef MDSS_SMMU_H |
| #define MDSS_SMMU_H |
| |
| #include <linux/ion.h> |
| #include <linux/msm_mdp.h> |
| #include <linux/mdss_io_util.h> |
| |
| #include "mdss.h" |
| #include "mdss_mdp.h" |
| #include "mdss_debug.h" |
| |
| #define MDSS_SMMU_COMPATIBLE "qcom,smmu" |
| #define MDSS_SMMU_COMPAT_STR_LEN 10 |
| #define SMMU_CBN_FSYNR1 0x6c |
| |
| struct mdss_iommu_map_type { |
| char *client_name; |
| char *ctx_name; |
| unsigned long start; |
| unsigned long size; |
| }; |
| |
| struct mdss_smmu_domain { |
| char *ctx_name; |
| int domain; |
| unsigned long start; |
| unsigned long size; |
| }; |
| |
| struct mdss_smmu_private { |
| struct device_node *pdev; |
| struct list_head smmu_device_list; |
| struct list_head user_list; |
| struct mutex smmu_reg_lock; |
| }; |
| |
| void mdss_smmu_register(struct device *dev); |
| int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev); |
| int mdss_smmu_set_attribute(int domain, int flag, int val); |
| |
| enum smmu_attributes { |
| EARLY_MAP |
| }; |
| |
| static inline int mdss_smmu_dma_data_direction(int dir) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| return (mdss_has_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR)) ? |
| DMA_BIDIRECTIONAL : dir; |
| } |
| |
| static inline bool is_mdss_smmu_compatible_device(const char *str) |
| { |
| /* check the prefix */ |
| return (!strcmp(str, MDSS_SMMU_COMPATIBLE)) ? true : false; |
| } |
| |
| /* |
| * mdss_smmu_is_valid_domain_type() |
| * |
| * Used to check if rotator smmu domain is defined or not by checking if |
| * vbif base is defined. As those are associated. |
| */ |
| static inline bool mdss_smmu_is_valid_domain_type(struct mdss_data_type *mdata, |
| int domain_type) |
| { |
| if ((domain_type == MDSS_IOMMU_DOMAIN_ROT_UNSECURE || |
| domain_type == MDSS_IOMMU_DOMAIN_ROT_SECURE) && |
| !mdss_mdp_is_nrt_vbif_base_defined(mdata)) |
| return false; |
| return true; |
| } |
| |
| static inline bool mdss_smmu_is_valid_domain_condition( |
| struct mdss_data_type *mdata, |
| int domain_type, |
| bool is_attach) |
| { |
| if (is_attach) { |
| if (test_bit(MDSS_CAPS_SEC_DETACH_SMMU, |
| mdata->mdss_caps_map) && |
| (mdata->sec_disp_en || |
| (mdata->sec_cam_en && |
| domain_type == MDSS_IOMMU_DOMAIN_SECURE))) { |
| pr_debug("SMMU attach not attempted, sd:%d, sc:%d\n", |
| mdata->sec_disp_en, mdata->sec_cam_en); |
| return false; |
| } else { |
| return true; |
| } |
| } else { |
| if (test_bit(MDSS_CAPS_SEC_DETACH_SMMU, |
| mdata->mdss_caps_map) && |
| (mdata->sec_disp_en || |
| (mdata->sec_cam_en && |
| domain_type == MDSS_IOMMU_DOMAIN_SECURE))) { |
| pr_debug("SMMU detach attempted, sd:%d, sc:%d\n", |
| mdata->sec_disp_en, mdata->sec_cam_en); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| } |
| |
| static inline struct mdss_smmu_client *mdss_smmu_get_cb(u32 domain) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdss_smmu_is_valid_domain_type(mdata, domain)) |
| return NULL; |
| |
| return (domain >= MDSS_IOMMU_MAX_DOMAIN) ? NULL : |
| &mdata->mdss_smmu[domain]; |
| } |
| |
| static inline int is_mdss_iommu_attached(void) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| return mdata ? mdata->iommu_attached : false; |
| } |
| |
| static inline int mdss_smmu_get_domain_type(u64 flags, bool rotator) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| int type; |
| |
| if (flags & MDP_SECURE_OVERLAY_SESSION) { |
| type = (rotator && |
| mdata->mdss_smmu[MDSS_IOMMU_DOMAIN_ROT_SECURE].base.dev) ? |
| MDSS_IOMMU_DOMAIN_ROT_SECURE : MDSS_IOMMU_DOMAIN_SECURE; |
| } else { |
| type = (rotator && |
| mdata->mdss_smmu[MDSS_IOMMU_DOMAIN_ROT_UNSECURE].base.dev) ? |
| MDSS_IOMMU_DOMAIN_ROT_UNSECURE : MDSS_IOMMU_DOMAIN_UNSECURE; |
| } |
| return type; |
| } |
| |
| static inline int mdss_smmu_attach(struct mdss_data_type *mdata) |
| { |
| int rc; |
| |
| mdata->mdss_util->iommu_lock(); |
| MDSS_XLOG(mdata->iommu_attached); |
| |
| if (mdata->iommu_attached) { |
| pr_debug("mdp iommu already attached\n"); |
| rc = 0; |
| goto end; |
| } |
| |
| if (!mdata->smmu_ops.smmu_attach) { |
| rc = -ENODEV; |
| goto end; |
| } |
| |
| rc = mdata->smmu_ops.smmu_attach(mdata); |
| if (!rc) |
| mdata->iommu_attached = true; |
| |
| end: |
| mdata->mdss_util->iommu_unlock(); |
| return rc; |
| } |
| |
| static inline int mdss_smmu_detach(struct mdss_data_type *mdata) |
| { |
| int rc; |
| |
| mdata->mdss_util->iommu_lock(); |
| MDSS_XLOG(mdata->iommu_attached); |
| |
| if (!mdata->iommu_attached) { |
| pr_debug("mdp iommu already dettached\n"); |
| rc = 0; |
| goto end; |
| } |
| |
| if (!mdata->smmu_ops.smmu_detach) { |
| rc = -ENODEV; |
| goto end; |
| } |
| |
| rc = mdata->smmu_ops.smmu_detach(mdata); |
| if (!rc) |
| mdata->iommu_attached = false; |
| |
| end: |
| mdata->mdss_util->iommu_unlock(); |
| return rc; |
| } |
| |
| static inline int mdss_smmu_get_domain_id(u32 type) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdss_smmu_is_valid_domain_type(mdata, type)) |
| return -ENODEV; |
| |
| if (!mdata || !mdata->smmu_ops.smmu_get_domain_id |
| || type >= MDSS_IOMMU_MAX_DOMAIN) |
| return -ENODEV; |
| |
| return mdata->smmu_ops.smmu_get_domain_id(type); |
| } |
| |
| static inline struct dma_buf_attachment *mdss_smmu_dma_buf_attach( |
| struct dma_buf *dma_buf, struct device *dev, int domain) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdata || !mdata->smmu_ops.smmu_dma_buf_attach) |
| return NULL; |
| |
| return mdata->smmu_ops.smmu_dma_buf_attach(dma_buf, dev, domain); |
| } |
| |
| static inline int mdss_smmu_map_dma_buf(struct dma_buf *dma_buf, |
| struct sg_table *table, int domain, dma_addr_t *iova, |
| unsigned long *size, int dir) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdata->smmu_ops.smmu_map_dma_buf) |
| return -ENODEV; |
| |
| return mdata->smmu_ops.smmu_map_dma_buf(dma_buf, table, |
| domain, iova, size, |
| mdss_smmu_dma_data_direction(dir)); |
| } |
| |
| static inline void mdss_smmu_unmap_dma_buf(struct sg_table *table, int domain, |
| int dir, struct dma_buf *dma_buf) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (mdata->smmu_ops.smmu_unmap_dma_buf) |
| mdata->smmu_ops.smmu_unmap_dma_buf(table, domain, |
| mdss_smmu_dma_data_direction(dir), dma_buf); |
| } |
| |
| static inline int mdss_smmu_dma_alloc_coherent(struct device *dev, size_t size, |
| dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr, |
| gfp_t gfp, int domain) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdata || !mdata->smmu_ops.smmu_dma_alloc_coherent) |
| return -ENODEV; |
| |
| return mdata->smmu_ops.smmu_dma_alloc_coherent(dev, size, |
| phys, iova, cpu_addr, gfp, domain); |
| } |
| |
| static inline void mdss_smmu_dma_free_coherent(struct device *dev, size_t size, |
| void *cpu_addr, dma_addr_t phys, dma_addr_t iova, int domain) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (mdata && mdata->smmu_ops.smmu_dma_free_coherent) |
| mdata->smmu_ops.smmu_dma_free_coherent(dev, size, cpu_addr, |
| phys, iova, domain); |
| } |
| |
| static inline int mdss_smmu_map(int domain, phys_addr_t iova, phys_addr_t phys, |
| int gfp_order, int prot) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdata->smmu_ops.smmu_map) |
| return -ENODEV; |
| |
| return mdata->smmu_ops.smmu_map(domain, iova, phys, gfp_order, prot); |
| } |
| |
| static inline void mdss_smmu_unmap(int domain, unsigned long iova, |
| int gfp_order) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (mdata->smmu_ops.smmu_unmap) |
| mdata->smmu_ops.smmu_unmap(domain, iova, gfp_order); |
| } |
| |
| static inline char *mdss_smmu_dsi_alloc_buf(struct device *dev, int size, |
| dma_addr_t *dmap, gfp_t gfp) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdata->smmu_ops.smmu_dsi_alloc_buf) |
| return NULL; |
| |
| return mdata->smmu_ops.smmu_dsi_alloc_buf(dev, size, dmap, gfp); |
| } |
| |
| static inline int mdss_smmu_dsi_map_buffer(phys_addr_t phys, |
| unsigned int domain, unsigned long size, dma_addr_t *dma_addr, |
| void *cpu_addr, int dir) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (!mdata->smmu_ops.smmu_dsi_map_buffer) |
| return -ENODEV; |
| |
| return mdata->smmu_ops.smmu_dsi_map_buffer(phys, domain, size, |
| dma_addr, cpu_addr, |
| mdss_smmu_dma_data_direction(dir)); |
| } |
| |
| static inline void mdss_smmu_dsi_unmap_buffer(dma_addr_t dma_addr, int domain, |
| unsigned long size, int dir) |
| { |
| struct mdss_data_type *mdata = mdss_mdp_get_mdata(); |
| |
| if (mdata->smmu_ops.smmu_dsi_unmap_buffer) |
| mdata->smmu_ops.smmu_dsi_unmap_buffer(dma_addr, domain, |
| size, mdss_smmu_dma_data_direction(dir)); |
| } |
| |
| static inline void mdss_smmu_deinit(struct mdss_data_type *mdata) |
| { |
| if (mdata->smmu_ops.smmu_deinit) |
| mdata->smmu_ops.smmu_deinit(mdata); |
| } |
| |
| #endif /* MDSS_SMMU_H */ |