From 3c1dae0a07c651526f8e878d223a88f82caa5a50 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 11 Jun 2015 18:33:48 +0200 Subject: drm/tegra: dpaux: Fix transfers larger than 4 bytes The DPAUX read/write FIFO registers aren't sequential in the register space, causing transfers larger than 4 bytes to cause accesses to non- existing FIFO registers. Fixes: 6b6b604215c6 ("drm/tegra: Add eDP support") Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dpaux.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index d6b55e3e3716..a43a836e6f88 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -72,34 +72,32 @@ static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux, static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer, size_t size) { - unsigned long offset = DPAUX_DP_AUXDATA_WRITE(0); size_t i, j; - for (i = 0; i < size; i += 4) { - size_t num = min_t(size_t, size - i, 4); + for (i = 0; i < DIV_ROUND_UP(size, 4); i++) { + size_t num = min_t(size_t, size - i * 4, 4); unsigned long value = 0; for (j = 0; j < num; j++) - value |= buffer[i + j] << (j * 8); + value |= buffer[i * 4 + j] << (j * 8); - tegra_dpaux_writel(dpaux, value, offset++); + tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXDATA_WRITE(i)); } } static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer, size_t size) { - unsigned long offset = DPAUX_DP_AUXDATA_READ(0); size_t i, j; - for (i = 0; i < size; i += 4) { - size_t num = min_t(size_t, size - i, 4); + for (i = 0; i < DIV_ROUND_UP(size, 4); i++) { + size_t num = min_t(size_t, size - i * 4, 4); unsigned long value; - value = tegra_dpaux_readl(dpaux, offset++); + value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i)); for (j = 0; j < num; j++) - buffer[i + j] = value >> (j * 8); + buffer[i * 4 + j] = value >> (j * 8); } } -- cgit v1.2.3 From 4553f733c68b66ef49f838aa24470d58caf76ff5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 19 Jan 2015 16:15:04 +0100 Subject: drm/tegra: gem: Take into account IOMMU aperture The IOMMU may not always be able to address 2 GiB of memory. On Tegra20, the GART supports 32 MiB starting at 0x58000000. Also the aperture on Tegra30 and later is in fact the full 4 GiB, rather than just 2 GiB as currently assumed. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 1833abd7d3aa..106208463b37 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -124,14 +124,22 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) return -ENOMEM; if (iommu_present(&platform_bus_type)) { + struct iommu_domain_geometry *geometry; + u64 start, end; + tegra->domain = iommu_domain_alloc(&platform_bus_type); if (!tegra->domain) { err = -ENOMEM; goto free; } - DRM_DEBUG("IOMMU context initialized\n"); - drm_mm_init(&tegra->mm, 0, SZ_2G); + geometry = &tegra->domain->geometry; + start = geometry->aperture_start; + end = geometry->aperture_end; + + DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n", + start, end); + drm_mm_init(&tegra->mm, start, end - start + 1); } mutex_init(&tegra->clients_lock); -- cgit v1.2.3 From fd73caa5e72f0fcf9732b18d123eead96286fd5b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Apr 2015 12:52:36 +0200 Subject: drm/tegra: gem: Flush pages after allocation Pages allocated from shmemfs don't end up being cleared and flushed on ARMv7, so they must be flushed explicitly. Use the DMA mapping API for that purpose, even though it's not used for anything else. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 1217272a51f2..01e16e146bfe 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -189,7 +189,6 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo) static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) { struct scatterlist *s; - struct sg_table *sgt; unsigned int i; bo->pages = drm_gem_get_pages(&bo->gem); @@ -198,36 +197,28 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) bo->num_pages = bo->gem.size >> PAGE_SHIFT; - sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); - if (IS_ERR(sgt)) + bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); + if (IS_ERR(bo->sgt)) goto put_pages; /* - * Fake up the SG table so that dma_map_sg() can be used to flush the - * pages associated with it. Note that this relies on the fact that - * the DMA API doesn't hook into IOMMU on Tegra, therefore mapping is - * only cache maintenance. + * Fake up the SG table so that dma_sync_sg_for_device() can be used + * to flush the pages associated with it. * * TODO: Replace this by drm_clflash_sg() once it can be implemented * without relying on symbols that are not exported. */ - for_each_sg(sgt->sgl, s, sgt->nents, i) + for_each_sg(bo->sgt->sgl, s, bo->sgt->nents, i) sg_dma_address(s) = sg_phys(s); - if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) - goto release_sgt; - - bo->sgt = sgt; + dma_sync_sg_for_device(drm->dev, bo->sgt->sgl, bo->sgt->nents, + DMA_TO_DEVICE); return 0; -release_sgt: - sg_free_table(sgt); - kfree(sgt); - sgt = ERR_PTR(-ENOMEM); put_pages: drm_gem_put_pages(&bo->gem, bo->pages, false, false); - return PTR_ERR(sgt); + return PTR_ERR(bo->sgt); } static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo) -- cgit v1.2.3 From 8a8005e3e19915559b542bf85cc1b17024ee1d31 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 2 Jun 2015 13:13:01 +0200 Subject: drm/tegra: dpaux: Registers are 32-bit Use a sized unsigned 32-bit data type (u32) to store register contents. The DPAUX registers are 32 bits wide irrespective of the architecture's data width. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dpaux.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index a43a836e6f88..07b26972f487 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -56,15 +56,14 @@ static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work) return container_of(work, struct tegra_dpaux, work); } -static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux, - unsigned long offset) +static inline u32 tegra_dpaux_readl(struct tegra_dpaux *dpaux, + unsigned long offset) { return readl(dpaux->regs + (offset << 2)); } static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux, - unsigned long value, - unsigned long offset) + u32 value, unsigned long offset) { writel(value, dpaux->regs + (offset << 2)); } @@ -76,7 +75,7 @@ static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer, for (i = 0; i < DIV_ROUND_UP(size, 4); i++) { size_t num = min_t(size_t, size - i * 4, 4); - unsigned long value = 0; + u32 value = 0; for (j = 0; j < num; j++) value |= buffer[i * 4 + j] << (j * 8); @@ -92,7 +91,7 @@ static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer, for (i = 0; i < DIV_ROUND_UP(size, 4); i++) { size_t num = min_t(size_t, size - i * 4, 4); - unsigned long value; + u32 value; value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i)); @@ -248,7 +247,7 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data) { struct tegra_dpaux *dpaux = data; irqreturn_t ret = IRQ_HANDLED; - unsigned long value; + u32 value; /* clear interrupts */ value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX); @@ -271,7 +270,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) { struct tegra_dpaux *dpaux; struct resource *regs; - unsigned long value; + u32 value; int err; dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL); @@ -463,7 +462,7 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux) enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux) { - unsigned long value; + u32 value; value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT); @@ -475,7 +474,7 @@ enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux) int tegra_dpaux_enable(struct tegra_dpaux *dpaux) { - unsigned long value; + u32 value; value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | @@ -493,7 +492,7 @@ int tegra_dpaux_enable(struct tegra_dpaux *dpaux) int tegra_dpaux_disable(struct tegra_dpaux *dpaux) { - unsigned long value; + u32 value; value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; -- cgit v1.2.3