提交 c861acc4 编写于 作者: D Dave Airlie

Merge tag 'drm/tegra/for-4.2-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

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
...@@ -56,15 +56,14 @@ static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work) ...@@ -56,15 +56,14 @@ static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work)
return container_of(work, struct tegra_dpaux, work); return container_of(work, struct tegra_dpaux, work);
} }
static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux, static inline u32 tegra_dpaux_readl(struct tegra_dpaux *dpaux,
unsigned long offset) unsigned long offset)
{ {
return readl(dpaux->regs + (offset << 2)); return readl(dpaux->regs + (offset << 2));
} }
static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux, static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux,
unsigned long value, u32 value, unsigned long offset)
unsigned long offset)
{ {
writel(value, dpaux->regs + (offset << 2)); writel(value, dpaux->regs + (offset << 2));
} }
...@@ -72,34 +71,32 @@ static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux, ...@@ -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, static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer,
size_t size) size_t size)
{ {
unsigned long offset = DPAUX_DP_AUXDATA_WRITE(0);
size_t i, j; size_t i, j;
for (i = 0; i < size; i += 4) { for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
size_t num = min_t(size_t, size - i, 4); size_t num = min_t(size_t, size - i * 4, 4);
unsigned long value = 0; u32 value = 0;
for (j = 0; j < num; j++) 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, static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
size_t size) size_t size)
{ {
unsigned long offset = DPAUX_DP_AUXDATA_READ(0);
size_t i, j; size_t i, j;
for (i = 0; i < size; i += 4) { for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
size_t num = min_t(size_t, size - i, 4); size_t num = min_t(size_t, size - i * 4, 4);
unsigned long value; u32 value;
value = tegra_dpaux_readl(dpaux, offset++); value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i));
for (j = 0; j < num; j++) 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) ...@@ -250,7 +247,7 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data)
{ {
struct tegra_dpaux *dpaux = data; struct tegra_dpaux *dpaux = data;
irqreturn_t ret = IRQ_HANDLED; irqreturn_t ret = IRQ_HANDLED;
unsigned long value; u32 value;
/* clear interrupts */ /* clear interrupts */
value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX); value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
...@@ -273,7 +270,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) ...@@ -273,7 +270,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
{ {
struct tegra_dpaux *dpaux; struct tegra_dpaux *dpaux;
struct resource *regs; struct resource *regs;
unsigned long value; u32 value;
int err; int err;
dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL); dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL);
...@@ -465,7 +462,7 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux) ...@@ -465,7 +462,7 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux)
enum drm_connector_status tegra_dpaux_detect(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); value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
...@@ -477,7 +474,7 @@ enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux) ...@@ -477,7 +474,7 @@ enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux)
int tegra_dpaux_enable(struct tegra_dpaux *dpaux) int tegra_dpaux_enable(struct tegra_dpaux *dpaux)
{ {
unsigned long value; u32 value;
value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
...@@ -495,7 +492,7 @@ int tegra_dpaux_enable(struct tegra_dpaux *dpaux) ...@@ -495,7 +492,7 @@ int tegra_dpaux_enable(struct tegra_dpaux *dpaux)
int tegra_dpaux_disable(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 = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
......
...@@ -124,14 +124,22 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -124,14 +124,22 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
return -ENOMEM; return -ENOMEM;
if (iommu_present(&platform_bus_type)) { if (iommu_present(&platform_bus_type)) {
struct iommu_domain_geometry *geometry;
u64 start, end;
tegra->domain = iommu_domain_alloc(&platform_bus_type); tegra->domain = iommu_domain_alloc(&platform_bus_type);
if (!tegra->domain) { if (!tegra->domain) {
err = -ENOMEM; err = -ENOMEM;
goto free; goto free;
} }
DRM_DEBUG("IOMMU context initialized\n"); geometry = &tegra->domain->geometry;
drm_mm_init(&tegra->mm, 0, SZ_2G); 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); mutex_init(&tegra->clients_lock);
......
...@@ -189,7 +189,6 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo) ...@@ -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) static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
{ {
struct scatterlist *s; struct scatterlist *s;
struct sg_table *sgt;
unsigned int i; unsigned int i;
bo->pages = drm_gem_get_pages(&bo->gem); 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) ...@@ -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; bo->num_pages = bo->gem.size >> PAGE_SHIFT;
sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages);
if (IS_ERR(sgt)) if (IS_ERR(bo->sgt))
goto put_pages; goto put_pages;
/* /*
* Fake up the SG table so that dma_map_sg() can be used to flush the * Fake up the SG table so that dma_sync_sg_for_device() can be used
* pages associated with it. Note that this relies on the fact that * to flush the pages associated with it.
* the DMA API doesn't hook into IOMMU on Tegra, therefore mapping is
* only cache maintenance.
* *
* TODO: Replace this by drm_clflash_sg() once it can be implemented * TODO: Replace this by drm_clflash_sg() once it can be implemented
* without relying on symbols that are not exported. * 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); sg_dma_address(s) = sg_phys(s);
if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) dma_sync_sg_for_device(drm->dev, bo->sgt->sgl, bo->sgt->nents,
goto release_sgt; DMA_TO_DEVICE);
bo->sgt = sgt;
return 0; return 0;
release_sgt:
sg_free_table(sgt);
kfree(sgt);
sgt = ERR_PTR(-ENOMEM);
put_pages: put_pages:
drm_gem_put_pages(&bo->gem, bo->pages, false, false); 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) static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册