25.03.2021 16:03, Thierry Reding пишет: > From: Thierry Reding <treding@xxxxxxxxxx> > > Hi, > > this is a set of patches that is the result of earlier discussions > regarding early identity mappings that are needed to avoid SMMU faults > during early boot. > > The goal here is to avoid early identity mappings altogether and instead > postpone the need for the identity mappings to when devices are attached > to the SMMU. This works by making the SMMU driver coordinate with the > memory controller driver on when to start enforcing SMMU translations. > This makes Tegra behave in a more standard way and pushes the code to > deal with the Tegra-specific programming into the NVIDIA SMMU > implementation. It is an interesting idea which inspired me to try to apply a somewhat similar thing to Tegra SMMU driver by holding the SMMU ASID enable-bit until display driver allows to toggle it. This means that we will need an extra small tegra-specific SMMU API function, but it should be okay. I typed a patch and seems it's working good, I'll prepare a proper patch if you like it. What do you think about this: diff --git a/drivers/gpu/drm/grate/dc.c b/drivers/gpu/drm/grate/dc.c index 45a41586f153..8874cfba40a1 100644 --- a/drivers/gpu/drm/grate/dc.c +++ b/drivers/gpu/drm/grate/dc.c @@ -17,6 +17,7 @@ #include <linux/reset.h> #include <soc/tegra/common.h> +#include <soc/tegra/mc.h> #include <soc/tegra/pmc.h> #include <drm/drm_atomic.h> @@ -2640,6 +2641,11 @@ static int tegra_dc_init(struct host1x_client *client) return err; } + if (dc->soc->sync_smmu) { + struct iommu_domain *domain = iommu_get_domain_for_dev(dc->dev); + tegra_smmu_sync_domain(domain, dc->dev); + } + if (dc->soc->wgrps) primary = tegra_dc_add_shared_planes(drm, dc); else @@ -2824,6 +2830,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_win_b_vfilter_mem_client = true, .has_win_c_without_vert_filter = true, .plane_tiled_memory_bandwidth_x2 = false, + .sync_smmu = false, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -2846,6 +2853,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .has_win_b_vfilter_mem_client = true, .has_win_c_without_vert_filter = false, .plane_tiled_memory_bandwidth_x2 = true, + .sync_smmu = true, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -2868,6 +2876,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .has_win_b_vfilter_mem_client = false, .has_win_c_without_vert_filter = false, .plane_tiled_memory_bandwidth_x2 = true, + .sync_smmu = true, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -2890,6 +2899,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .has_win_b_vfilter_mem_client = false, .has_win_c_without_vert_filter = false, .plane_tiled_memory_bandwidth_x2 = false, + .sync_smmu = true, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -2912,6 +2922,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .has_win_b_vfilter_mem_client = false, .has_win_c_without_vert_filter = false, .plane_tiled_memory_bandwidth_x2 = false, + .sync_smmu = true, }; static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { @@ -2961,6 +2972,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = { .wgrps = tegra186_dc_wgrps, .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), .plane_tiled_memory_bandwidth_x2 = false, + .sync_smmu = false, }; static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { @@ -3010,6 +3022,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = { .wgrps = tegra194_dc_wgrps, .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), .plane_tiled_memory_bandwidth_x2 = false, + .sync_smmu = false, }; static const struct of_device_id tegra_dc_of_match[] = { diff --git a/drivers/gpu/drm/grate/dc.h b/drivers/gpu/drm/grate/dc.h index 316a56131cf1..e0057bf7be99 100644 --- a/drivers/gpu/drm/grate/dc.h +++ b/drivers/gpu/drm/grate/dc.h @@ -91,6 +91,7 @@ struct tegra_dc_soc_info { bool has_win_b_vfilter_mem_client; bool has_win_c_without_vert_filter; bool plane_tiled_memory_bandwidth_x2; + bool sync_smmu; }; struct tegra_dc { diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 602aab98c079..e750b1844a88 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -47,6 +47,9 @@ struct tegra_smmu { struct dentry *debugfs; struct iommu_device iommu; /* IOMMU Core code handle */ + + bool display_synced[2]; + bool display_enabled[2]; }; struct tegra_smmu_as { @@ -78,6 +81,10 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) return readl(smmu->regs + offset); } +/* all Tegra SoCs use the same group IDs for displays */ +#define SMMU_SWGROUP_DC 1 +#define SMMU_SWGROUP_DCB 2 + #define SMMU_CONFIG 0x010 #define SMMU_CONFIG_ENABLE (1 << 0) @@ -253,6 +260,20 @@ static inline void smmu_flush(struct tegra_smmu *smmu) smmu_readl(smmu, SMMU_PTB_ASID); } +static int smmu_swgroup_to_display_id(unsigned int swgroup) +{ + switch (swgroup) { + case SMMU_SWGROUP_DC: + return 0; + + case SMMU_SWGROUP_DCB: + return 1; + + default: + return -1; + } +} + static int tegra_smmu_alloc_asid(struct tegra_smmu *smmu, unsigned int *idp) { unsigned long id; @@ -352,10 +373,21 @@ tegra_smmu_find_swgroup(struct tegra_smmu *smmu, unsigned int swgroup) static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup, unsigned int asid) { + const int disp_id = smmu_swgroup_to_display_id(swgroup); const struct tegra_smmu_swgroup *group; unsigned int i; u32 value; + if (disp_id >= 0) { + smmu->display_enabled[disp_id] = true; + + if (!smmu->display_synced[disp_id]) { + pr_debug("%s deferred for swgroup %u\n", + __func__, swgroup); + return; + } + } + group = tegra_smmu_find_swgroup(smmu, swgroup); if (group) { value = smmu_readl(smmu, group->reg); @@ -385,10 +417,14 @@ static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup, static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup, unsigned int asid) { + const int disp_id = smmu_swgroup_to_display_id(swgroup); const struct tegra_smmu_swgroup *group; unsigned int i; u32 value; + if (disp_id >= 0) + smmu->display_enabled[disp_id] = false; + group = tegra_smmu_find_swgroup(smmu, swgroup); if (group) { value = smmu_readl(smmu, group->reg); @@ -410,6 +446,32 @@ static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup, } } +void tegra_smmu_sync_domain(struct iommu_domain *domain, struct device *dev) +{ + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct tegra_smmu *smmu = dev_iommu_priv_get(dev); + unsigned int index; + + if (!fwspec || !domain) + return; + + for (index = 0; index < fwspec->num_ids; index++) { + const unsigned int swgroup = fwspec->ids[index]; + const int disp_id = smmu_swgroup_to_display_id(swgroup); + + if (disp_id < 0 || smmu->display_synced[disp_id]) + continue; + + smmu->display_synced[disp_id] = true; + + if (!smmu->display_enabled[disp_id]) + continue; + + tegra_smmu_enable(smmu, swgroup, to_smmu_as(domain)->id); + } +} +EXPORT_SYMBOL_GPL(tegra_smmu_sync_domain); + static int tegra_smmu_as_prepare(struct tegra_smmu *smmu, struct tegra_smmu_as *as) { diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index cfd3b35e23e5..ac1f3226b2ac 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -15,6 +15,7 @@ struct clk; struct device; +struct iommu_domain; struct page; struct tegra_smmu_enable { @@ -88,6 +89,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc, struct tegra_mc *mc); void tegra_smmu_remove(struct tegra_smmu *smmu); +void tegra_smmu_sync_domain(struct iommu_domain *domain, struct device *dev); #else static inline struct tegra_smmu * tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc, @@ -99,6 +101,11 @@ tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc, static inline void tegra_smmu_remove(struct tegra_smmu *smmu) { } + +static inline void tegra_smmu_sync_domain(struct iommu_domain *domain, + struct device *dev) +{ +} #endif #ifdef CONFIG_TEGRA_IOMMU_GART