Merge tag 'drm/tegra/for-4.2-rc1' of git://anongit.freedesktop.org/tegra/linux into...
authorDave Airlie <airlied@redhat.com>
Thu, 18 Jun 2015 02:53:54 +0000 (12:53 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 18 Jun 2015 02:53:54 +0000 (12:53 +1000)
drm/tegra: Changes for v4.2-rc1

This contains a couple of mostly fixes for issues that have crept up in
recent versions of linux-next. One issue is that DP AUX transactions of
more than 4 bytes will access the wrong FIFO registers and hence become
corrupt. Another fix is required to restore functionality of Tegra20 if
using the GART. The current code expects the IOMMU aperture to be the
complete 4 GiB address space, whereas the GART on Tegra20 only provides
a 128 MiB aperture. One more issue with IOMMU support is that on 64-bit
ARM, swiotlb is the default IOMMU implementation backing the DMA API. A
side-effect of that is that when dma_map_sg() is called to flush caches
(yes, this is a bit of a hack, but ARM does not provide a better API),
swiotlb will immediately run out of memory because its bounce buffer is
too small to make a framebuffer.

Finally I've included a mostly cosmetic fix that stores register values
in u32 rather than unsigned long to avoid sign-extension issues on 64-
bit ARM. This is only a precaution since it hasn't caused any issues
(yet).

* tag 'drm/tegra/for-4.2-rc1' of git://anongit.freedesktop.org/tegra/linux:
  drm/tegra: dpaux: Registers are 32-bit
  drm/tegra: gem: Flush pages after allocation
  drm/tegra: gem: Take into account IOMMU aperture
  drm/tegra: dpaux: Fix transfers larger than 4 bytes

drivers/gpu/drm/tegra/dpaux.c
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/gem.c

index d6b55e3e3716c8bcaf9e10d2f6625fbfee981797..07b26972f487967cb1b4203488f0387c9e0736fc 100644 (file)
@@ -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));
 }
@@ -72,34 +71,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);
-               unsigned long value = 0;
+       for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
+               size_t num = min_t(size_t, size - i * 4, 4);
+               u32 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);
-               unsigned long value;
+       for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
+               size_t num = min_t(size_t, size - i * 4, 4);
+               u32 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);
        }
 }
 
@@ -250,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);
@@ -273,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);
@@ -465,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);
 
@@ -477,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) |
@@ -495,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;
index bfad15a913a023b0f0f3272118fe822fc9097d0c..427f50c6803cba7d3ed5424547ac7c812d6bb536 100644 (file)
@@ -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);
index 1217272a51f2cf64173f632690123a9920233097..01e16e146bfeffa2b441e6e3037c8f6624fa42d1 100644 (file)
@@ -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)