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). -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJVeu52AAoJEN0jrNd/PrOhQbEQAIqhhiQdQKBXkgCNZDflqUT9 qN0PkFAlZF+pftDUCqvQg5nN5INWPEqUuzd2UAXPxXBFeRr8YabFN6SgK+/3XDYE +5tFyADFAm9CJhxFWpoIm7uWFEWbbGPKsokNe4WUdmYMIkROuztTodFw/lAkd1rM WNMbCbC9qx8HaHZMA5wnQRhWMlpY+o7TahYYYjroiHlHBYJgcEgigQb7d5pyrbG3 10jJT3xx78+gN04RuMg4z6HJ9SjuuhWgKEoI7fr0EyTfIdQ390MLDh/SEnX4YeRr o3Ww+nkaKG+iENK8GNwJ8w6s7w5X1QiMLB6t0ShU29khUMaCkz9Swr5OsCONUTD7 bEV17B5HNpAgQtWjqiF/YW9b4xe3PQJW0fU6MFkcyo7dZCm1o64tY0u4dCa7WqMb 55NNXkoYpod3VT7S5+qg1ghIEg1NJTxvH41FwkAKZvd4BTO6Jn97GJdGokb+NRsn WkpR/q+kxcHkuFTxK/SRuG7nT7ss6jrZNTDo2aitX8sxs1VW5lgLlMBg1SkafC6S N0t5g+jM1j4j/BETBJjZI+VeyeVZcgQPeO+DaDOEp6TIvMxb3l8ox8LCjtxRbU89 Z+s0y3HL2//vjPh7AMK4Dy5weyfX3LT5c93JJBClhregCYczFG6tcfa1vfiyw9Bo cwI90g4aC4xi41m+INbt =3vlA -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
c861acc4d5
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue