Commit e139e8ae authored by Dave Airlie's avatar Dave Airlie

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

drm/tegra: Fixes for v5.6-rc1

These are a couple of quick fixes for regressions that were found during
the first two weeks of the merge window.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200206172753.2185390-1-thierry.reding@gmail.com
parents b45f1b3b 98ae41ad
...@@ -1037,23 +1037,9 @@ void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, ...@@ -1037,23 +1037,9 @@ void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt,
free_pages((unsigned long)virt, get_order(size)); free_pages((unsigned long)virt, get_order(size));
} }
static int host1x_drm_probe(struct host1x_device *dev) static bool host1x_drm_wants_iommu(struct host1x_device *dev)
{ {
struct drm_driver *driver = &tegra_drm_driver;
struct iommu_domain *domain; struct iommu_domain *domain;
struct tegra_drm *tegra;
struct drm_device *drm;
int err;
drm = drm_dev_alloc(driver, &dev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
if (!tegra) {
err = -ENOMEM;
goto put;
}
/* /*
* If the Tegra DRM clients are backed by an IOMMU, push buffers are * If the Tegra DRM clients are backed by an IOMMU, push buffers are
...@@ -1082,9 +1068,38 @@ static int host1x_drm_probe(struct host1x_device *dev) ...@@ -1082,9 +1068,38 @@ static int host1x_drm_probe(struct host1x_device *dev)
* up the device tree appropriately. This is considered an problem * up the device tree appropriately. This is considered an problem
* of integration, so care must be taken for the DT to be consistent. * of integration, so care must be taken for the DT to be consistent.
*/ */
domain = iommu_get_domain_for_dev(drm->dev->parent); domain = iommu_get_domain_for_dev(dev->dev.parent);
/*
* Tegra20 and Tegra30 don't support addressing memory beyond the
* 32-bit boundary, so the regular GATHER opcodes will always be
* sufficient and whether or not the host1x is attached to an IOMMU
* doesn't matter.
*/
if (!domain && dma_get_mask(dev->dev.parent) <= DMA_BIT_MASK(32))
return true;
return domain != NULL;
}
static int host1x_drm_probe(struct host1x_device *dev)
{
struct drm_driver *driver = &tegra_drm_driver;
struct tegra_drm *tegra;
struct drm_device *drm;
int err;
drm = drm_dev_alloc(driver, &dev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
if (!tegra) {
err = -ENOMEM;
goto put;
}
if (domain && iommu_present(&platform_bus_type)) { if (host1x_drm_wants_iommu(dev) && iommu_present(&platform_bus_type)) {
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;
......
...@@ -60,8 +60,16 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo, ...@@ -60,8 +60,16 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
/* /*
* If we've manually mapped the buffer object through the IOMMU, make * If we've manually mapped the buffer object through the IOMMU, make
* sure to return the IOVA address of our mapping. * sure to return the IOVA address of our mapping.
*
* Similarly, for buffers that have been allocated by the DMA API the
* physical address can be used for devices that are not attached to
* an IOMMU. For these devices, callers must pass a valid pointer via
* the @phys argument.
*
* Imported buffers were also already mapped at import time, so the
* existing mapping can be reused.
*/ */
if (phys && obj->mm) { if (phys) {
*phys = obj->iova; *phys = obj->iova;
return NULL; return NULL;
} }
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
* Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
*/ */
#include <linux/iommu.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h> #include <drm/drm_fourcc.h>
...@@ -107,21 +109,27 @@ const struct drm_plane_funcs tegra_plane_funcs = { ...@@ -107,21 +109,27 @@ const struct drm_plane_funcs tegra_plane_funcs = {
static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state) static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
{ {
struct iommu_domain *domain = iommu_get_domain_for_dev(dc->dev);
unsigned int i; unsigned int i;
int err; int err;
for (i = 0; i < state->base.fb->format->num_planes; i++) { for (i = 0; i < state->base.fb->format->num_planes; i++) {
struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i); struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
dma_addr_t phys_addr, *phys;
struct sg_table *sgt;
if (!dc->client.group) { if (!domain || dc->client.group)
struct sg_table *sgt; phys = &phys_addr;
else
phys = NULL;
sgt = host1x_bo_pin(dc->dev, &bo->base, NULL); sgt = host1x_bo_pin(dc->dev, &bo->base, phys);
if (IS_ERR(sgt)) { if (IS_ERR(sgt)) {
err = PTR_ERR(sgt); err = PTR_ERR(sgt);
goto unpin; goto unpin;
} }
if (sgt) {
err = dma_map_sg(dc->dev, sgt->sgl, sgt->nents, err = dma_map_sg(dc->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (err == 0) { if (err == 0) {
...@@ -143,7 +151,7 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state) ...@@ -143,7 +151,7 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
state->iova[i] = sg_dma_address(sgt->sgl); state->iova[i] = sg_dma_address(sgt->sgl);
state->sgt[i] = sgt; state->sgt[i] = sgt;
} else { } else {
state->iova[i] = bo->iova; state->iova[i] = phys_addr;
} }
} }
...@@ -156,9 +164,11 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state) ...@@ -156,9 +164,11 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i); struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
struct sg_table *sgt = state->sgt[i]; struct sg_table *sgt = state->sgt[i];
dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); if (sgt)
host1x_bo_unpin(dc->dev, &bo->base, sgt); dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE);
host1x_bo_unpin(dc->dev, &bo->base, sgt);
state->iova[i] = DMA_MAPPING_ERROR; state->iova[i] = DMA_MAPPING_ERROR;
state->sgt[i] = NULL; state->sgt[i] = NULL;
} }
...@@ -172,17 +182,13 @@ static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state) ...@@ -172,17 +182,13 @@ static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
for (i = 0; i < state->base.fb->format->num_planes; i++) { for (i = 0; i < state->base.fb->format->num_planes; i++) {
struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i); struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
struct sg_table *sgt = state->sgt[i];
if (!dc->client.group) { if (sgt)
struct sg_table *sgt = state->sgt[i]; dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE);
if (sgt) {
dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE);
host1x_bo_unpin(dc->dev, &bo->base, sgt);
}
}
host1x_bo_unpin(dc->dev, &bo->base, sgt);
state->iova[i] = DMA_MAPPING_ERROR; state->iova[i] = DMA_MAPPING_ERROR;
state->sgt[i] = NULL; state->sgt[i] = NULL;
} }
......
...@@ -3915,6 +3915,17 @@ static int tegra_sor_probe(struct platform_device *pdev) ...@@ -3915,6 +3915,17 @@ static int tegra_sor_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sor); platform_set_drvdata(pdev, sor);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
INIT_LIST_HEAD(&sor->client.list);
sor->client.ops = &sor_client_ops;
sor->client.dev = &pdev->dev;
err = host1x_client_register(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
goto rpm_disable;
}
/* /*
* On Tegra210 and earlier, provide our own implementation for the * On Tegra210 and earlier, provide our own implementation for the
* pad output clock. * pad output clock.
...@@ -3922,16 +3933,17 @@ static int tegra_sor_probe(struct platform_device *pdev) ...@@ -3922,16 +3933,17 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (!sor->clk_pad) { if (!sor->clk_pad) {
char *name; char *name;
name = devm_kasprintf(sor->dev, GFP_KERNEL, "sor%u_pad_clkout",
sor->index);
if (!name) {
err = -ENOMEM;
goto unregister;
}
err = host1x_client_resume(&sor->client); err = host1x_client_resume(&sor->client);
if (err < 0) { if (err < 0) {
dev_err(sor->dev, "failed to resume: %d\n", err); dev_err(sor->dev, "failed to resume: %d\n", err);
goto remove; goto unregister;
}
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "sor%u_pad_clkout", sor->index);
if (!name) {
err = -ENOMEM;
goto remove;
} }
sor->clk_pad = tegra_clk_sor_pad_register(sor, name); sor->clk_pad = tegra_clk_sor_pad_register(sor, name);
...@@ -3940,24 +3952,17 @@ static int tegra_sor_probe(struct platform_device *pdev) ...@@ -3940,24 +3952,17 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (IS_ERR(sor->clk_pad)) { if (IS_ERR(sor->clk_pad)) {
err = PTR_ERR(sor->clk_pad); err = PTR_ERR(sor->clk_pad);
dev_err(&pdev->dev, "failed to register SOR pad clock: %d\n", dev_err(sor->dev, "failed to register SOR pad clock: %d\n",
err);
goto remove;
}
INIT_LIST_HEAD(&sor->client.list);
sor->client.ops = &sor_client_ops;
sor->client.dev = &pdev->dev;
err = host1x_client_register(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err); err);
goto remove; goto unregister;
} }
return 0; return 0;
unregister:
host1x_client_unregister(&sor->client);
rpm_disable:
pm_runtime_disable(&pdev->dev);
remove: remove:
if (sor->ops && sor->ops->remove) if (sor->ops && sor->ops->remove)
sor->ops->remove(sor); sor->ops->remove(sor);
...@@ -3971,8 +3976,6 @@ static int tegra_sor_remove(struct platform_device *pdev) ...@@ -3971,8 +3976,6 @@ static int tegra_sor_remove(struct platform_device *pdev)
struct tegra_sor *sor = platform_get_drvdata(pdev); struct tegra_sor *sor = platform_get_drvdata(pdev);
int err; int err;
pm_runtime_disable(&pdev->dev);
err = host1x_client_unregister(&sor->client); err = host1x_client_unregister(&sor->client);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
...@@ -3980,6 +3983,8 @@ static int tegra_sor_remove(struct platform_device *pdev) ...@@ -3980,6 +3983,8 @@ static int tegra_sor_remove(struct platform_device *pdev)
return err; return err;
} }
pm_runtime_disable(&pdev->dev);
if (sor->ops && sor->ops->remove) { if (sor->ops && sor->ops->remove) {
err = sor->ops->remove(sor); err = sor->ops->remove(sor);
if (err < 0) if (err < 0)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/host1x.h> #include <linux/host1x.h>
#include <linux/iommu.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
...@@ -101,9 +102,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -101,9 +102,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
{ {
struct host1x_client *client = job->client; struct host1x_client *client = job->client;
struct device *dev = client->dev; struct device *dev = client->dev;
struct iommu_domain *domain;
unsigned int i; unsigned int i;
int err; int err;
domain = iommu_get_domain_for_dev(dev);
job->num_unpins = 0; job->num_unpins = 0;
for (i = 0; i < job->num_relocs; i++) { for (i = 0; i < job->num_relocs; i++) {
...@@ -117,7 +120,19 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -117,7 +120,19 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin; goto unpin;
} }
if (client->group) /*
* If the client device is not attached to an IOMMU, the
* physical address of the buffer object can be used.
*
* Similarly, when an IOMMU domain is shared between all
* host1x clients, the IOVA is already available, so no
* need to map the buffer object again.
*
* XXX Note that this isn't always safe to do because it
* relies on an assumption that no cache maintenance is
* needed on the buffer objects.
*/
if (!domain || client->group)
phys = &phys_addr; phys = &phys_addr;
else else
phys = NULL; phys = NULL;
...@@ -176,6 +191,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -176,6 +191,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
dma_addr_t phys_addr; dma_addr_t phys_addr;
unsigned long shift; unsigned long shift;
struct iova *alloc; struct iova *alloc;
dma_addr_t *phys;
unsigned int j; unsigned int j;
g->bo = host1x_bo_get(g->bo); g->bo = host1x_bo_get(g->bo);
...@@ -184,7 +200,17 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -184,7 +200,17 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin; goto unpin;
} }
sgt = host1x_bo_pin(host->dev, g->bo, NULL); /**
* If the host1x is not attached to an IOMMU, there is no need
* to map the buffer object for the host1x, since the physical
* address can simply be used.
*/
if (!iommu_get_domain_for_dev(host->dev))
phys = &phys_addr;
else
phys = NULL;
sgt = host1x_bo_pin(host->dev, g->bo, phys);
if (IS_ERR(sgt)) { if (IS_ERR(sgt)) {
err = PTR_ERR(sgt); err = PTR_ERR(sgt);
goto unpin; goto unpin;
...@@ -214,7 +240,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -214,7 +240,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
job->unpins[job->num_unpins].size = gather_size; job->unpins[job->num_unpins].size = gather_size;
phys_addr = iova_dma_addr(&host->iova, alloc); phys_addr = iova_dma_addr(&host->iova, alloc);
} else { } else if (sgt) {
err = dma_map_sg(host->dev, sgt->sgl, sgt->nents, err = dma_map_sg(host->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (!err) { if (!err) {
...@@ -222,6 +248,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -222,6 +248,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin; goto unpin;
} }
job->unpins[job->num_unpins].dir = DMA_TO_DEVICE;
job->unpins[job->num_unpins].dev = host->dev; job->unpins[job->num_unpins].dev = host->dev;
phys_addr = sg_dma_address(sgt->sgl); phys_addr = sg_dma_address(sgt->sgl);
} }
...@@ -229,7 +256,6 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -229,7 +256,6 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
job->addr_phys[job->num_unpins] = phys_addr; job->addr_phys[job->num_unpins] = phys_addr;
job->gather_addr_phys[i] = phys_addr; job->gather_addr_phys[i] = phys_addr;
job->unpins[job->num_unpins].dir = DMA_TO_DEVICE;
job->unpins[job->num_unpins].bo = g->bo; job->unpins[job->num_unpins].bo = g->bo;
job->unpins[job->num_unpins].sgt = sgt; job->unpins[job->num_unpins].sgt = sgt;
job->num_unpins++; job->num_unpins++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment