aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2022-07-12 15:50:41 +1000
committerDave Airlie <airlied@redhat.com>2022-07-12 16:50:05 +1000
commit8daecf611258d252b3107132d0143752b2e186fc (patch)
tree7e10777180fdb971aaaa94893648e4c123ad8981 /drivers/gpu/drm
parentMerge tag 'drm-misc-next-2022-07-07' of git://anongit.freedesktop.org/drm/drm-misc into drm-next (diff)
parentdrm/tegra: vic: Use devm_platform_ioremap_resource() (diff)
downloadlinux-dev-8daecf611258d252b3107132d0143752b2e186fc.tar.xz
linux-dev-8daecf611258d252b3107132d0143752b2e186fc.zip
Merge tag 'drm/tegra/for-5.20-rc1' of https://gitlab.freedesktop.org/drm/tegra into drm-next
drm/tegra: Changes for v5.20-rc1 The bulk of these changes adds support for context isolation for the various supported host1x engines, as well as support for the hardware found on the new Tegra234 SoC generation. There's also a couple of fixes and cleanups. To round things off, the device tree bindings are converted to the new json-schema format that allows DTBs to be validated. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Thierry Reding <thierry.reding@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/20220708181136.673789-1-thierry.reding@gmail.com
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/tegra/dc.c1
-rw-r--r--drivers/gpu/drm/tegra/drm.c1
-rw-r--r--drivers/gpu/drm/tegra/drm.h11
-rw-r--r--drivers/gpu/drm/tegra/falcon.c8
-rw-r--r--drivers/gpu/drm/tegra/falcon.h1
-rw-r--r--drivers/gpu/drm/tegra/gem.c11
-rw-r--r--drivers/gpu/drm/tegra/hub.c1
-rw-r--r--drivers/gpu/drm/tegra/nvdec.c14
-rw-r--r--drivers/gpu/drm/tegra/plane.c1
-rw-r--r--drivers/gpu/drm/tegra/submit.c48
-rw-r--r--drivers/gpu/drm/tegra/uapi.c43
-rw-r--r--drivers/gpu/drm/tegra/vic.c92
12 files changed, 209 insertions, 23 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index a2a731e8a8a3..747abafb6a5c 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/iommu.h>
#include <linux/interconnect.h>
#include <linux/module.h>
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 4cdc8faf798f..6748ec1e0005 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1381,6 +1381,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra194-sor", },
{ .compatible = "nvidia,tegra194-vic", },
{ .compatible = "nvidia,tegra194-nvdec", },
+ { .compatible = "nvidia,tegra234-vic", },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index fc0a19554eac..845e60f144c7 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -80,6 +80,7 @@ struct tegra_drm_context {
/* Only used by new UAPI. */
struct xarray mappings;
+ struct host1x_memory_context *memory_context;
};
struct tegra_drm_client_ops {
@@ -91,12 +92,22 @@ struct tegra_drm_client_ops {
int (*submit)(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
+ int (*get_streamid_offset)(struct tegra_drm_client *client, u32 *offset);
+ int (*can_use_memory_ctx)(struct tegra_drm_client *client, bool *supported);
};
int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
+static inline int
+tegra_drm_get_streamid_offset_thi(struct tegra_drm_client *client, u32 *offset)
+{
+ *offset = 0x30;
+
+ return 0;
+}
+
struct tegra_drm_client {
struct host1x_client base;
struct list_head list;
diff --git a/drivers/gpu/drm/tegra/falcon.c b/drivers/gpu/drm/tegra/falcon.c
index 3762d87759d9..c0d85463eb1a 100644
--- a/drivers/gpu/drm/tegra/falcon.c
+++ b/drivers/gpu/drm/tegra/falcon.c
@@ -48,6 +48,14 @@ static int falcon_copy_chunk(struct falcon *falcon,
if (target == FALCON_MEMORY_IMEM)
cmd |= FALCON_DMATRFCMD_IMEM;
+ /*
+ * Use second DMA context (i.e. the one for firmware). Strictly
+ * speaking, at this point both DMA contexts point to the firmware
+ * stream ID, but this register's value will be reused by the firmware
+ * for later DMA transactions, so we need to use the correct value.
+ */
+ cmd |= FALCON_DMATRFCMD_DMACTX(1);
+
falcon_writel(falcon, offset, FALCON_DMATRFMOFFS);
falcon_writel(falcon, base, FALCON_DMATRFFBOFFS);
falcon_writel(falcon, cmd, FALCON_DMATRFCMD);
diff --git a/drivers/gpu/drm/tegra/falcon.h b/drivers/gpu/drm/tegra/falcon.h
index c56ee32d92ee..1955cf11a8a6 100644
--- a/drivers/gpu/drm/tegra/falcon.h
+++ b/drivers/gpu/drm/tegra/falcon.h
@@ -50,6 +50,7 @@
#define FALCON_DMATRFCMD_IDLE (1 << 1)
#define FALCON_DMATRFCMD_IMEM (1 << 4)
#define FALCON_DMATRFCMD_SIZE_256B (6 << 8)
+#define FALCON_DMATRFCMD_DMACTX(v) (((v) & 0x7) << 12)
#define FALCON_DMATRFFBOFFS 0x0000111c
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index 7c7dd84e6db8..81991090adcc 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -704,14 +704,23 @@ static int tegra_gem_prime_vmap(struct dma_buf *buf, struct iosys_map *map)
{
struct drm_gem_object *gem = buf->priv;
struct tegra_bo *bo = to_tegra_bo(gem);
+ void *vaddr;
- iosys_map_set_vaddr(map, bo->vaddr);
+ vaddr = tegra_bo_mmap(&bo->base);
+ if (IS_ERR(vaddr))
+ return PTR_ERR(vaddr);
+
+ iosys_map_set_vaddr(map, vaddr);
return 0;
}
static void tegra_gem_prime_vunmap(struct dma_buf *buf, struct iosys_map *map)
{
+ struct drm_gem_object *gem = buf->priv;
+ struct tegra_bo *bo = to_tegra_bo(gem);
+
+ tegra_bo_munmap(&bo->base, map->vaddr);
}
static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = {
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
index 61729ea9f0af..b872527a123c 100644
--- a/drivers/gpu/drm/tegra/hub.c
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -5,6 +5,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/host1x.h>
#include <linux/module.h>
#include <linux/of.h>
diff --git a/drivers/gpu/drm/tegra/nvdec.c b/drivers/gpu/drm/tegra/nvdec.c
index 79e1e88203cf..276fe0472730 100644
--- a/drivers/gpu/drm/tegra/nvdec.c
+++ b/drivers/gpu/drm/tegra/nvdec.c
@@ -5,6 +5,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/host1x.h>
#include <linux/iommu.h>
#include <linux/module.h>
@@ -21,6 +22,8 @@
#include "falcon.h"
#include "vic.h"
+#define NVDEC_TFBIF_TRANSCFG 0x2c44
+
struct nvdec_config {
const char *firmware;
unsigned int version;
@@ -63,7 +66,7 @@ static int nvdec_boot(struct nvdec *nvdec)
u32 value;
value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | TRANSCFG_ATT(0, TRANSCFG_SID_HW);
- nvdec_writel(nvdec, value, VIC_TFBIF_TRANSCFG);
+ nvdec_writel(nvdec, value, NVDEC_TFBIF_TRANSCFG);
if (spec->num_ids > 0) {
value = spec->ids[0] & 0xffff;
@@ -304,10 +307,19 @@ static void nvdec_close_channel(struct tegra_drm_context *context)
host1x_channel_put(context->channel);
}
+static int nvdec_can_use_memory_ctx(struct tegra_drm_client *client, bool *supported)
+{
+ *supported = true;
+
+ return 0;
+}
+
static const struct tegra_drm_client_ops nvdec_ops = {
.open_channel = nvdec_open_channel,
.close_channel = nvdec_close_channel,
.submit = tegra_drm_submit,
+ .get_streamid_offset = tegra_drm_get_streamid_offset_thi,
+ .can_use_memory_ctx = nvdec_can_use_memory_ctx,
};
#define NVIDIA_TEGRA_210_NVDEC_FIRMWARE "nvidia/tegra210/nvdec.bin"
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c
index d049b211c9b3..ca9f03e3675b 100644
--- a/drivers/gpu/drm/tegra/plane.c
+++ b/drivers/gpu/drm/tegra/plane.c
@@ -3,6 +3,7 @@
* Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
*/
+#include <linux/dma-mapping.h>
#include <linux/iommu.h>
#include <linux/interconnect.h>
diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c
index 6d6dd8c35475..b24738bdf3df 100644
--- a/drivers/gpu/drm/tegra/submit.c
+++ b/drivers/gpu/drm/tegra/submit.c
@@ -498,6 +498,9 @@ static void release_job(struct host1x_job *job)
struct tegra_drm_submit_data *job_data = job->user_data;
u32 i;
+ if (job->memory_context)
+ host1x_memory_context_put(job->memory_context);
+
for (i = 0; i < job_data->num_used_mappings; i++)
tegra_drm_mapping_put(job_data->used_mappings[i].mapping);
@@ -588,11 +591,51 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
goto put_job;
}
+ if (context->client->ops->get_streamid_offset) {
+ err = context->client->ops->get_streamid_offset(
+ context->client, &job->engine_streamid_offset);
+ if (err) {
+ SUBMIT_ERR(context, "failed to get streamid offset: %d", err);
+ goto unpin_job;
+ }
+ }
+
+ if (context->memory_context && context->client->ops->can_use_memory_ctx) {
+ bool supported;
+
+ err = context->client->ops->can_use_memory_ctx(context->client, &supported);
+ if (err) {
+ SUBMIT_ERR(context, "failed to detect if engine can use memory context: %d", err);
+ goto unpin_job;
+ }
+
+ if (supported) {
+ job->memory_context = context->memory_context;
+ host1x_memory_context_get(job->memory_context);
+ }
+ } else if (context->client->ops->get_streamid_offset) {
+#ifdef CONFIG_IOMMU_API
+ struct iommu_fwspec *spec;
+
+ /*
+ * Job submission will need to temporarily change stream ID,
+ * so need to tell it what to change it back to.
+ */
+ spec = dev_iommu_fwspec_get(context->client->base.dev);
+ if (spec && spec->num_ids > 0)
+ job->engine_fallback_streamid = spec->ids[0] & 0xffff;
+ else
+ job->engine_fallback_streamid = 0x7f;
+#else
+ job->engine_fallback_streamid = 0x7f;
+#endif
+ }
+
/* Boot engine. */
err = pm_runtime_resume_and_get(context->client->base.dev);
if (err < 0) {
SUBMIT_ERR(context, "could not power up engine: %d", err);
- goto unpin_job;
+ goto put_memory_context;
}
job->user_data = job_data;
@@ -627,6 +670,9 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
goto put_job;
+put_memory_context:
+ if (job->memory_context)
+ host1x_memory_context_put(job->memory_context);
unpin_job:
host1x_job_unpin(job);
put_job:
diff --git a/drivers/gpu/drm/tegra/uapi.c b/drivers/gpu/drm/tegra/uapi.c
index 9ab9179d2026..a98239cb0e29 100644
--- a/drivers/gpu/drm/tegra/uapi.c
+++ b/drivers/gpu/drm/tegra/uapi.c
@@ -33,6 +33,9 @@ static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
struct tegra_drm_mapping *mapping;
unsigned long id;
+ if (context->memory_context)
+ host1x_memory_context_put(context->memory_context);
+
xa_for_each(&context->mappings, id, mapping)
tegra_drm_mapping_put(mapping);
@@ -72,6 +75,7 @@ static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u
int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
{
+ struct host1x *host = tegra_drm_to_host1x(drm->dev_private);
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_channel_open *args = data;
@@ -102,10 +106,36 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_
}
}
+ /* Only allocate context if the engine supports context isolation. */
+ if (device_iommu_mapped(client->base.dev) && client->ops->can_use_memory_ctx) {
+ bool supported;
+
+ err = client->ops->can_use_memory_ctx(client, &supported);
+ if (err)
+ goto put_channel;
+
+ if (supported)
+ context->memory_context = host1x_memory_context_alloc(
+ host, get_task_pid(current, PIDTYPE_TGID));
+
+ if (IS_ERR(context->memory_context)) {
+ if (PTR_ERR(context->memory_context) != -EOPNOTSUPP) {
+ err = PTR_ERR(context->memory_context);
+ goto put_channel;
+ } else {
+ /*
+ * OK, HW does not support contexts or contexts
+ * are disabled.
+ */
+ context->memory_context = NULL;
+ }
+ }
+ }
+
err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
GFP_KERNEL);
if (err < 0)
- goto put_channel;
+ goto put_memctx;
context->client = client;
xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
@@ -118,6 +148,9 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_
return 0;
+put_memctx:
+ if (context->memory_context)
+ host1x_memory_context_put(context->memory_context);
put_channel:
host1x_channel_put(context->channel);
free:
@@ -156,6 +189,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
struct tegra_drm_mapping *mapping;
struct tegra_drm_context *context;
enum dma_data_direction direction;
+ struct device *mapping_dev;
int err = 0;
if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
@@ -177,6 +211,11 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
kref_init(&mapping->ref);
+ if (context->memory_context)
+ mapping_dev = &context->memory_context->dev;
+ else
+ mapping_dev = context->client->base.dev;
+
mapping->bo = tegra_gem_lookup(file, args->handle);
if (!mapping->bo) {
err = -EINVAL;
@@ -201,7 +240,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
goto put_gem;
}
- mapping->map = host1x_bo_pin(context->client->base.dev, mapping->bo, direction, NULL);
+ mapping->map = host1x_bo_pin(mapping_dev, mapping->bo, direction, NULL);
if (IS_ERR(mapping->map)) {
err = PTR_ERR(mapping->map);
goto put_gem;
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
index f56f5921a8c2..7382ee132eb7 100644
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -38,6 +38,8 @@ struct vic {
struct clk *clk;
struct reset_control *rst;
+ bool can_use_context;
+
/* Platform configuration */
const struct vic_config *config;
};
@@ -229,28 +231,38 @@ static int vic_load_firmware(struct vic *vic)
{
struct host1x_client *client = &vic->client.base;
struct tegra_drm *tegra = vic->client.drm;
+ static DEFINE_MUTEX(lock);
+ u32 fce_bin_data_offset;
dma_addr_t iova;
size_t size;
void *virt;
int err;
- if (vic->falcon.firmware.virt)
- return 0;
+ mutex_lock(&lock);
+
+ if (vic->falcon.firmware.virt) {
+ err = 0;
+ goto unlock;
+ }
err = falcon_read_firmware(&vic->falcon, vic->config->firmware);
if (err < 0)
- return err;
+ goto unlock;
size = vic->falcon.firmware.size;
if (!client->group) {
virt = dma_alloc_coherent(vic->dev, size, &iova, GFP_KERNEL);
- if (!virt)
- return -ENOMEM;
+ if (!virt) {
+ err = -ENOMEM;
+ goto unlock;
+ }
} else {
virt = tegra_drm_alloc(tegra, size, &iova);
- if (IS_ERR(virt))
- return PTR_ERR(virt);
+ if (IS_ERR(virt)) {
+ err = PTR_ERR(virt);
+ goto unlock;
+ }
}
vic->falcon.firmware.virt = virt;
@@ -277,7 +289,28 @@ static int vic_load_firmware(struct vic *vic)
vic->falcon.firmware.phys = phys;
}
- return 0;
+ /*
+ * Check if firmware is new enough to not require mapping firmware
+ * to data buffer domains.
+ */
+ fce_bin_data_offset = *(u32 *)(virt + VIC_UCODE_FCE_DATA_OFFSET);
+
+ if (!vic->config->supports_sid) {
+ vic->can_use_context = false;
+ } else if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
+ /*
+ * Firmware will access FCE through STREAMID0, so context
+ * isolation cannot be used.
+ */
+ vic->can_use_context = false;
+ dev_warn_once(vic->dev, "context isolation disabled due to old firmware\n");
+ } else {
+ vic->can_use_context = true;
+ }
+
+unlock:
+ mutex_unlock(&lock);
+ return err;
cleanup:
if (!client->group)
@@ -285,11 +318,12 @@ cleanup:
else
tegra_drm_free(tegra, size, virt, iova);
+ mutex_unlock(&lock);
return err;
}
-static int vic_runtime_resume(struct device *dev)
+static int __maybe_unused vic_runtime_resume(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
int err;
@@ -323,7 +357,7 @@ disable:
return err;
}
-static int vic_runtime_suspend(struct device *dev)
+static int __maybe_unused vic_runtime_suspend(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
int err;
@@ -358,10 +392,27 @@ static void vic_close_channel(struct tegra_drm_context *context)
host1x_channel_put(context->channel);
}
+static int vic_can_use_memory_ctx(struct tegra_drm_client *client, bool *supported)
+{
+ struct vic *vic = to_vic(client);
+ int err;
+
+ /* This doesn't access HW so it's safe to call without powering up. */
+ err = vic_load_firmware(vic);
+ if (err < 0)
+ return err;
+
+ *supported = vic->can_use_context;
+
+ return 0;
+}
+
static const struct tegra_drm_client_ops vic_ops = {
.open_channel = vic_open_channel,
.close_channel = vic_close_channel,
.submit = tegra_drm_submit,
+ .get_streamid_offset = tegra_drm_get_streamid_offset_thi,
+ .can_use_memory_ctx = vic_can_use_memory_ctx,
};
#define NVIDIA_TEGRA_124_VIC_FIRMWARE "nvidia/tegra124/vic03_ucode.bin"
@@ -396,11 +447,20 @@ static const struct vic_config vic_t194_config = {
.supports_sid = true,
};
+#define NVIDIA_TEGRA_234_VIC_FIRMWARE "nvidia/tegra234/vic.bin"
+
+static const struct vic_config vic_t234_config = {
+ .firmware = NVIDIA_TEGRA_234_VIC_FIRMWARE,
+ .version = 0x23,
+ .supports_sid = true,
+};
+
static const struct of_device_id tegra_vic_of_match[] = {
{ .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config },
{ .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config },
{ .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
{ .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
+ { .compatible = "nvidia,tegra234-vic", .data = &vic_t234_config },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
@@ -409,7 +469,6 @@ static int vic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct host1x_syncpt **syncpts;
- struct resource *regs;
struct vic *vic;
int err;
@@ -430,13 +489,7 @@ static int vic_probe(struct platform_device *pdev)
if (!syncpts)
return -ENOMEM;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs) {
- dev_err(&pdev->dev, "failed to get registers\n");
- return -ENXIO;
- }
-
- vic->regs = devm_ioremap_resource(dev, regs);
+ vic->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(vic->regs))
return PTR_ERR(vic->regs);
@@ -539,3 +592,6 @@ MODULE_FIRMWARE(NVIDIA_TEGRA_186_VIC_FIRMWARE);
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE);
#endif
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
+MODULE_FIRMWARE(NVIDIA_TEGRA_234_VIC_FIRMWARE);
+#endif