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:
Dave Airlie 2015-06-18 12:53:54 +10:00
commit c861acc4d5
3 changed files with 36 additions and 40 deletions

View 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;

View 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);

View 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)