Files
linux-nv-oot/drivers/gpu/drm/tegra/uapi/uapi.c
Jon Hunter d3ea27d381 drm/tegra: Update to UAPI v5
Update the tegra-drm driver to the 'Host1x/Tegra UAPI v5' series [0].
This fixes a few minor bugs found in the previous series.

[0] https://patchwork.ozlabs.org/project/linux-tegra/list/?series=223684

Bug 200687525

Change-Id: I270016756b6b689c1fada208896cee81223e7042
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2469984
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
GVS: Gerrit_Virtual_Submit
2023-04-03 09:18:59 +00:00

308 lines
6.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020 NVIDIA Corporation */
#include <linux/host1x-next.h>
#include <linux/iommu.h>
#include <linux/list.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include "../uapi.h"
#include "../drm.h"
struct tegra_drm_channel_ctx *
tegra_drm_channel_ctx_lock(struct tegra_drm_file *file, u32 id)
{
struct tegra_drm_channel_ctx *ctx;
mutex_lock(&file->lock);
ctx = xa_load(&file->contexts, id);
if (!ctx)
mutex_unlock(&file->lock);
return ctx;
}
static void tegra_drm_mapping_release(struct kref *ref)
{
struct tegra_drm_mapping *mapping =
container_of(ref, struct tegra_drm_mapping, ref);
if (mapping->sgt)
dma_unmap_sgtable(mapping->dev, mapping->sgt,
mapping->direction, DMA_ATTR_SKIP_CPU_SYNC);
host1x_bo_unpin(mapping->dev, mapping->bo, mapping->sgt);
host1x_bo_put(mapping->bo);
kfree(mapping);
}
void tegra_drm_mapping_put(struct tegra_drm_mapping *mapping)
{
kref_put(&mapping->ref, tegra_drm_mapping_release);
}
static void tegra_drm_channel_ctx_close(struct tegra_drm_channel_ctx *ctx)
{
unsigned long mapping_id;
struct tegra_drm_mapping *mapping;
xa_for_each(&ctx->mappings, mapping_id, mapping)
tegra_drm_mapping_put(mapping);
xa_destroy(&ctx->mappings);
host1x_channel_put(ctx->channel);
kfree(ctx);
}
int close_channel_ctx(int id, void *p, void *data)
{
struct tegra_drm_channel_ctx *ctx = p;
tegra_drm_channel_ctx_close(ctx);
return 0;
}
void tegra_drm_uapi_close_file(struct tegra_drm_file *file)
{
unsigned long ctx_id;
struct tegra_drm_channel_ctx *ctx;
xa_for_each(&file->contexts, ctx_id, ctx)
tegra_drm_channel_ctx_close(ctx);
xa_destroy(&file->contexts);
}
int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_channel_open *args = data;
struct tegra_drm_client *client = NULL;
struct tegra_drm_channel_ctx *ctx;
int err;
if (args->flags)
return -EINVAL;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
err = -ENODEV;
list_for_each_entry(client, &tegra->clients, list) {
if (client->base.class == args->host1x_class) {
err = 0;
break;
}
}
if (err)
goto free_ctx;
if (client->shared_channel) {
ctx->channel = host1x_channel_get(client->shared_channel);
} else {
ctx->channel = host1x_channel_request(&client->base);
if (!ctx->channel) {
err = -EBUSY;
goto free_ctx;
}
}
err = xa_alloc(&fpriv->contexts, &args->channel_ctx, ctx,
XA_LIMIT(1, U32_MAX), GFP_KERNEL);
if (err < 0)
goto put_channel;
ctx->client = client;
xa_init_flags(&ctx->mappings, XA_FLAGS_ALLOC1);
args->hardware_version = client->version;
return 0;
put_channel:
host1x_channel_put(ctx->channel);
free_ctx:
kfree(ctx);
return err;
}
int tegra_drm_ioctl_channel_close(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_channel_close *args = data;
struct tegra_drm_channel_ctx *ctx;
ctx = tegra_drm_channel_ctx_lock(fpriv, args->channel_ctx);
if (!ctx)
return -EINVAL;
xa_erase(&fpriv->contexts, args->channel_ctx);
mutex_unlock(&fpriv->lock);
tegra_drm_channel_ctx_close(ctx);
return 0;
}
int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_channel_map *args = data;
struct tegra_drm_channel_ctx *ctx;
struct tegra_drm_mapping *mapping;
struct drm_gem_object *gem;
u32 mapping_id;
int err = 0;
if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READWRITE)
return -EINVAL;
ctx = tegra_drm_channel_ctx_lock(fpriv, args->channel_ctx);
if (!ctx)
return -EINVAL;
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
err = -ENOMEM;
goto unlock;
}
kref_init(&mapping->ref);
gem = drm_gem_object_lookup(file, args->handle);
if (!gem) {
err = -EINVAL;
goto unlock;
}
mapping->dev = ctx->client->base.dev;
mapping->bo = &container_of(gem, struct tegra_bo, gem)->base;
if (!iommu_get_domain_for_dev(mapping->dev) ||
ctx->client->base.group) {
host1x_bo_pin(mapping->dev, mapping->bo,
&mapping->iova);
} else {
mapping->direction = DMA_TO_DEVICE;
if (args->flags & DRM_TEGRA_CHANNEL_MAP_READWRITE)
mapping->direction = DMA_BIDIRECTIONAL;
mapping->sgt =
host1x_bo_pin(mapping->dev, mapping->bo, NULL);
if (IS_ERR(mapping->sgt)) {
err = PTR_ERR(mapping->sgt);
goto put_gem;
}
err = dma_map_sgtable(mapping->dev, mapping->sgt,
mapping->direction,
DMA_ATTR_SKIP_CPU_SYNC);
if (err)
goto unpin;
/* TODO only map the requested part */
mapping->iova = sg_dma_address(mapping->sgt->sgl);
}
mapping->iova_end = mapping->iova + gem->size;
mutex_unlock(&fpriv->lock);
err = xa_alloc(&ctx->mappings, &mapping_id, mapping,
XA_LIMIT(1, U32_MAX), GFP_KERNEL);
if (err < 0)
goto unmap;
args->mapping_id = mapping_id;
return 0;
unmap:
if (mapping->sgt) {
dma_unmap_sgtable(mapping->dev, mapping->sgt,
mapping->direction, DMA_ATTR_SKIP_CPU_SYNC);
}
unpin:
host1x_bo_unpin(mapping->dev, mapping->bo, mapping->sgt);
put_gem:
drm_gem_object_put(gem);
kfree(mapping);
unlock:
mutex_unlock(&fpriv->lock);
return err;
}
int tegra_drm_ioctl_channel_unmap(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_channel_unmap *args = data;
struct tegra_drm_channel_ctx *ctx;
struct tegra_drm_mapping *mapping;
ctx = tegra_drm_channel_ctx_lock(fpriv, args->channel_ctx);
if (!ctx)
return -EINVAL;
mapping = xa_erase(&ctx->mappings, args->mapping_id);
mutex_unlock(&fpriv->lock);
if (mapping) {
tegra_drm_mapping_put(mapping);
return 0;
} else {
return -EINVAL;
}
}
int tegra_drm_ioctl_gem_create(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_create *args = data;
struct tegra_bo *bo;
if (args->flags)
return -EINVAL;
bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
&args->handle);
if (IS_ERR(bo))
return PTR_ERR(bo);
return 0;
}
int tegra_drm_ioctl_gem_mmap(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_mmap *args = data;
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(file, args->handle);
if (!gem)
return -EINVAL;
bo = to_tegra_bo(gem);
args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
drm_gem_object_put(gem);
return 0;
}