linux/drivers/gpu/drm/tegra/uapi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright (c) 2020 NVIDIA Corporation */
   3
   4#include <linux/host1x.h>
   5#include <linux/iommu.h>
   6#include <linux/list.h>
   7
   8#include <drm/drm_drv.h>
   9#include <drm/drm_file.h>
  10#include <drm/drm_utils.h>
  11
  12#include "drm.h"
  13#include "uapi.h"
  14
  15static void tegra_drm_mapping_release(struct kref *ref)
  16{
  17        struct tegra_drm_mapping *mapping =
  18                container_of(ref, struct tegra_drm_mapping, ref);
  19
  20        host1x_bo_unpin(mapping->map);
  21        host1x_bo_put(mapping->bo);
  22
  23        kfree(mapping);
  24}
  25
  26void tegra_drm_mapping_put(struct tegra_drm_mapping *mapping)
  27{
  28        kref_put(&mapping->ref, tegra_drm_mapping_release);
  29}
  30
  31static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
  32{
  33        struct tegra_drm_mapping *mapping;
  34        unsigned long id;
  35
  36        xa_for_each(&context->mappings, id, mapping)
  37                tegra_drm_mapping_put(mapping);
  38
  39        xa_destroy(&context->mappings);
  40
  41        host1x_channel_put(context->channel);
  42
  43        kfree(context);
  44}
  45
  46void tegra_drm_uapi_close_file(struct tegra_drm_file *file)
  47{
  48        struct tegra_drm_context *context;
  49        struct host1x_syncpt *sp;
  50        unsigned long id;
  51
  52        xa_for_each(&file->contexts, id, context)
  53                tegra_drm_channel_context_close(context);
  54
  55        xa_for_each(&file->syncpoints, id, sp)
  56                host1x_syncpt_put(sp);
  57
  58        xa_destroy(&file->contexts);
  59        xa_destroy(&file->syncpoints);
  60}
  61
  62static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u32 class)
  63{
  64        struct tegra_drm_client *client;
  65
  66        list_for_each_entry(client, &tegra->clients, list)
  67                if (client->base.class == class)
  68                        return client;
  69
  70        return NULL;
  71}
  72
  73int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
  74{
  75        struct tegra_drm_file *fpriv = file->driver_priv;
  76        struct tegra_drm *tegra = drm->dev_private;
  77        struct drm_tegra_channel_open *args = data;
  78        struct tegra_drm_client *client = NULL;
  79        struct tegra_drm_context *context;
  80        int err;
  81
  82        if (args->flags)
  83                return -EINVAL;
  84
  85        context = kzalloc(sizeof(*context), GFP_KERNEL);
  86        if (!context)
  87                return -ENOMEM;
  88
  89        client = tegra_drm_find_client(tegra, args->host1x_class);
  90        if (!client) {
  91                err = -ENODEV;
  92                goto free;
  93        }
  94
  95        if (client->shared_channel) {
  96                context->channel = host1x_channel_get(client->shared_channel);
  97        } else {
  98                context->channel = host1x_channel_request(&client->base);
  99                if (!context->channel) {
 100                        err = -EBUSY;
 101                        goto free;
 102                }
 103        }
 104
 105        err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
 106                       GFP_KERNEL);
 107        if (err < 0)
 108                goto put_channel;
 109
 110        context->client = client;
 111        xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
 112
 113        args->version = client->version;
 114        args->capabilities = 0;
 115
 116        if (device_get_dma_attr(client->base.dev) == DEV_DMA_COHERENT)
 117                args->capabilities |= DRM_TEGRA_CHANNEL_CAP_CACHE_COHERENT;
 118
 119        return 0;
 120
 121put_channel:
 122        host1x_channel_put(context->channel);
 123free:
 124        kfree(context);
 125
 126        return err;
 127}
 128
 129int tegra_drm_ioctl_channel_close(struct drm_device *drm, void *data, struct drm_file *file)
 130{
 131        struct tegra_drm_file *fpriv = file->driver_priv;
 132        struct drm_tegra_channel_close *args = data;
 133        struct tegra_drm_context *context;
 134
 135        mutex_lock(&fpriv->lock);
 136
 137        context = xa_load(&fpriv->contexts, args->context);
 138        if (!context) {
 139                mutex_unlock(&fpriv->lock);
 140                return -EINVAL;
 141        }
 142
 143        xa_erase(&fpriv->contexts, args->context);
 144
 145        mutex_unlock(&fpriv->lock);
 146
 147        tegra_drm_channel_context_close(context);
 148
 149        return 0;
 150}
 151
 152int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_file *file)
 153{
 154        struct tegra_drm_file *fpriv = file->driver_priv;
 155        struct drm_tegra_channel_map *args = data;
 156        struct tegra_drm_mapping *mapping;
 157        struct tegra_drm_context *context;
 158        enum dma_data_direction direction;
 159        int err = 0;
 160
 161        if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
 162                return -EINVAL;
 163
 164        mutex_lock(&fpriv->lock);
 165
 166        context = xa_load(&fpriv->contexts, args->context);
 167        if (!context) {
 168                mutex_unlock(&fpriv->lock);
 169                return -EINVAL;
 170        }
 171
 172        mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
 173        if (!mapping) {
 174                err = -ENOMEM;
 175                goto unlock;
 176        }
 177
 178        kref_init(&mapping->ref);
 179
 180        mapping->bo = tegra_gem_lookup(file, args->handle);
 181        if (!mapping->bo) {
 182                err = -EINVAL;
 183                goto free;
 184        }
 185
 186        switch (args->flags & DRM_TEGRA_CHANNEL_MAP_READ_WRITE) {
 187        case DRM_TEGRA_CHANNEL_MAP_READ_WRITE:
 188                direction = DMA_BIDIRECTIONAL;
 189                break;
 190
 191        case DRM_TEGRA_CHANNEL_MAP_WRITE:
 192                direction = DMA_FROM_DEVICE;
 193                break;
 194
 195        case DRM_TEGRA_CHANNEL_MAP_READ:
 196                direction = DMA_TO_DEVICE;
 197                break;
 198
 199        default:
 200                err = -EINVAL;
 201                goto put_gem;
 202        }
 203
 204        mapping->map = host1x_bo_pin(context->client->base.dev, mapping->bo, direction, NULL);
 205        if (IS_ERR(mapping->map)) {
 206                err = PTR_ERR(mapping->map);
 207                goto put_gem;
 208        }
 209
 210        mapping->iova = mapping->map->phys;
 211        mapping->iova_end = mapping->iova + host1x_to_tegra_bo(mapping->bo)->gem.size;
 212
 213        err = xa_alloc(&context->mappings, &args->mapping, mapping, XA_LIMIT(1, U32_MAX),
 214                       GFP_KERNEL);
 215        if (err < 0)
 216                goto unpin;
 217
 218        mutex_unlock(&fpriv->lock);
 219
 220        return 0;
 221
 222unpin:
 223        host1x_bo_unpin(mapping->map);
 224put_gem:
 225        host1x_bo_put(mapping->bo);
 226free:
 227        kfree(mapping);
 228unlock:
 229        mutex_unlock(&fpriv->lock);
 230        return err;
 231}
 232
 233int tegra_drm_ioctl_channel_unmap(struct drm_device *drm, void *data, struct drm_file *file)
 234{
 235        struct tegra_drm_file *fpriv = file->driver_priv;
 236        struct drm_tegra_channel_unmap *args = data;
 237        struct tegra_drm_mapping *mapping;
 238        struct tegra_drm_context *context;
 239
 240        mutex_lock(&fpriv->lock);
 241
 242        context = xa_load(&fpriv->contexts, args->context);
 243        if (!context) {
 244                mutex_unlock(&fpriv->lock);
 245                return -EINVAL;
 246        }
 247
 248        mapping = xa_erase(&context->mappings, args->mapping);
 249
 250        mutex_unlock(&fpriv->lock);
 251
 252        if (!mapping)
 253                return -EINVAL;
 254
 255        tegra_drm_mapping_put(mapping);
 256        return 0;
 257}
 258
 259int tegra_drm_ioctl_syncpoint_allocate(struct drm_device *drm, void *data, struct drm_file *file)
 260{
 261        struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
 262        struct tegra_drm_file *fpriv = file->driver_priv;
 263        struct drm_tegra_syncpoint_allocate *args = data;
 264        struct host1x_syncpt *sp;
 265        int err;
 266
 267        if (args->id)
 268                return -EINVAL;
 269
 270        sp = host1x_syncpt_alloc(host1x, HOST1X_SYNCPT_CLIENT_MANAGED, current->comm);
 271        if (!sp)
 272                return -EBUSY;
 273
 274        args->id = host1x_syncpt_id(sp);
 275
 276        err = xa_insert(&fpriv->syncpoints, args->id, sp, GFP_KERNEL);
 277        if (err) {
 278                host1x_syncpt_put(sp);
 279                return err;
 280        }
 281
 282        return 0;
 283}
 284
 285int tegra_drm_ioctl_syncpoint_free(struct drm_device *drm, void *data, struct drm_file *file)
 286{
 287        struct tegra_drm_file *fpriv = file->driver_priv;
 288        struct drm_tegra_syncpoint_allocate *args = data;
 289        struct host1x_syncpt *sp;
 290
 291        mutex_lock(&fpriv->lock);
 292        sp = xa_erase(&fpriv->syncpoints, args->id);
 293        mutex_unlock(&fpriv->lock);
 294
 295        if (!sp)
 296                return -EINVAL;
 297
 298        host1x_syncpt_put(sp);
 299
 300        return 0;
 301}
 302
 303int tegra_drm_ioctl_syncpoint_wait(struct drm_device *drm, void *data, struct drm_file *file)
 304{
 305        struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
 306        struct drm_tegra_syncpoint_wait *args = data;
 307        signed long timeout_jiffies;
 308        struct host1x_syncpt *sp;
 309
 310        if (args->padding != 0)
 311                return -EINVAL;
 312
 313        sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
 314        if (!sp)
 315                return -EINVAL;
 316
 317        timeout_jiffies = drm_timeout_abs_to_jiffies(args->timeout_ns);
 318
 319        return host1x_syncpt_wait(sp, args->threshold, timeout_jiffies, &args->value);
 320}
 321