Back to index

libdrm  2.4.37
nouveau.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 2012 Red Hat Inc.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
00018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  * Authors: Ben Skeggs
00023  */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <stdint.h>
00032 #include <string.h>
00033 #include <stdbool.h>
00034 #include <assert.h>
00035 #include <errno.h>
00036 #include <sys/mman.h>
00037 
00038 #include <xf86drm.h>
00039 #include <xf86atomic.h>
00040 #include "libdrm_lists.h"
00041 #include "nouveau_drm.h"
00042 
00043 #include "nouveau.h"
00044 #include "private.h"
00045 
00046 #ifdef DEBUG
00047 uint32_t nouveau_debug = 0;
00048 
00049 static void
00050 debug_init(char *args)
00051 {
00052        if (args) {
00053               int n = strtol(args, NULL, 0);
00054               if (n >= 0)
00055                      nouveau_debug = n;
00056        }
00057 }
00058 #endif
00059 
00060 /* this is the old libdrm's version of nouveau_device_wrap(), the symbol
00061  * is kept here to prevent AIGLX from crashing if the DDX is linked against
00062  * the new libdrm, but the DRI driver against the old
00063  */
00064 int
00065 nouveau_device_open_existing(struct nouveau_device **pdev, int close, int fd,
00066                           drm_context_t ctx)
00067 {
00068        return -EACCES;
00069 }
00070 
00071 int
00072 nouveau_device_wrap(int fd, int close, struct nouveau_device **pdev)
00073 {
00074        struct nouveau_device_priv *nvdev = calloc(1, sizeof(*nvdev));
00075        struct nouveau_device *dev = &nvdev->base;
00076        uint64_t chipset, vram, gart, bousage;
00077        drmVersionPtr ver;
00078        int ret;
00079 
00080 #ifdef DEBUG
00081        debug_init(getenv("NOUVEAU_LIBDRM_DEBUG"));
00082 #endif
00083 
00084        if (!nvdev)
00085               return -ENOMEM;
00086        nvdev->base.fd = fd;
00087 
00088        ver = drmGetVersion(fd);
00089        if (ver) dev->drm_version = (ver->version_major << 24) |
00090                                 (ver->version_minor << 8) |
00091                                  ver->version_patchlevel;
00092        drmFreeVersion(ver);
00093 
00094        if ( dev->drm_version != 0x00000010 &&
00095            (dev->drm_version <  0x01000000 ||
00096             dev->drm_version >= 0x02000000)) {
00097               nouveau_device_del(&dev);
00098               return -EINVAL;
00099        }
00100 
00101        ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_CHIPSET_ID, &chipset);
00102        if (ret == 0)
00103        ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_FB_SIZE, &vram);
00104        if (ret == 0)
00105        ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_AGP_SIZE, &gart);
00106        if (ret) {
00107               nouveau_device_del(&dev);
00108               return ret;
00109        }
00110 
00111        ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_HAS_BO_USAGE, &bousage);
00112        if (ret == 0)
00113               nvdev->have_bo_usage = (bousage != 0);
00114 
00115        nvdev->close = close;
00116        DRMINITLISTHEAD(&nvdev->bo_list);
00117        nvdev->base.object.oclass = NOUVEAU_DEVICE_CLASS;
00118        nvdev->base.lib_version = 0x01000000;
00119        nvdev->base.chipset = chipset;
00120        nvdev->base.vram_size = vram;
00121        nvdev->base.gart_size = gart;
00122        nvdev->base.vram_limit = (nvdev->base.vram_size * 80) / 100;
00123        nvdev->base.gart_limit = (nvdev->base.gart_size * 80) / 100;
00124 
00125        *pdev = &nvdev->base;
00126        return 0;
00127 }
00128 
00129 int
00130 nouveau_device_open(const char *busid, struct nouveau_device **pdev)
00131 {
00132        int ret = -ENODEV, fd = drmOpen("nouveau", busid);
00133        if (fd >= 0) {
00134               ret = nouveau_device_wrap(fd, 1, pdev);
00135               if (ret)
00136                      drmClose(fd);
00137        }
00138        return ret;
00139 }
00140 
00141 void
00142 nouveau_device_del(struct nouveau_device **pdev)
00143 {
00144        struct nouveau_device_priv *nvdev = nouveau_device(*pdev);
00145        if (nvdev) {
00146               if (nvdev->close)
00147                      drmClose(nvdev->base.fd);
00148               free(nvdev->client);
00149               free(nvdev);
00150               *pdev = NULL;
00151        }
00152 }
00153 
00154 int
00155 nouveau_getparam(struct nouveau_device *dev, uint64_t param, uint64_t *value)
00156 {
00157        struct drm_nouveau_getparam r = { param, 0 };
00158        int fd = dev->fd, ret =
00159               drmCommandWriteRead(fd, DRM_NOUVEAU_GETPARAM, &r, sizeof(r));
00160        *value = r.value;
00161        return ret;
00162 }
00163 
00164 int
00165 nouveau_setparam(struct nouveau_device *dev, uint64_t param, uint64_t value)
00166 {
00167        struct drm_nouveau_setparam r = { param, value };
00168        return drmCommandWrite(dev->fd, DRM_NOUVEAU_SETPARAM, &r, sizeof(r));
00169 }
00170 
00171 int
00172 nouveau_client_new(struct nouveau_device *dev, struct nouveau_client **pclient)
00173 {
00174        struct nouveau_device_priv *nvdev = nouveau_device(dev);
00175        struct nouveau_client_priv *pcli;
00176        int id = 0, i, ret = -ENOMEM;
00177        uint32_t *clients;
00178 
00179        for (i = 0; i < nvdev->nr_client; i++) {
00180               id = ffs(nvdev->client[i]) - 1;
00181               if (id >= 0)
00182                      goto out;
00183        }
00184 
00185        clients = realloc(nvdev->client, sizeof(uint32_t) * (i + 1));
00186        if (!clients)
00187               return ret;
00188        nvdev->client = clients;
00189        nvdev->client[i] = 0;
00190        nvdev->nr_client++;
00191 
00192 out:
00193        pcli = calloc(1, sizeof(*pcli));
00194        if (pcli) {
00195               nvdev->client[i] |= (1 << id);
00196               pcli->base.device = dev;
00197               pcli->base.id = (i * 32) + id;
00198               ret = 0;
00199        }
00200 
00201        *pclient = &pcli->base;
00202        return ret;
00203 }
00204 
00205 void
00206 nouveau_client_del(struct nouveau_client **pclient)
00207 {
00208        struct nouveau_client_priv *pcli = nouveau_client(*pclient);
00209        struct nouveau_device_priv *nvdev;
00210        if (pcli) {
00211               int id = pcli->base.id;
00212               nvdev = nouveau_device(pcli->base.device);
00213               nvdev->client[id / 32] &= ~(1 << (id % 32));
00214               free(pcli->kref);
00215               free(pcli);
00216        }
00217 }
00218 
00219 int
00220 nouveau_object_new(struct nouveau_object *parent, uint64_t handle,
00221                  uint32_t oclass, void *data, uint32_t length,
00222                  struct nouveau_object **pobj)
00223 {
00224        struct nouveau_device *dev;
00225        struct nouveau_object *obj;
00226        int ret = -EINVAL;
00227 
00228        if (length == 0)
00229               length = sizeof(struct nouveau_object *);
00230        obj = malloc(sizeof(*obj) + length);
00231        obj->parent = parent;
00232        obj->handle = handle;
00233        obj->oclass = oclass;
00234        obj->length = length;
00235        obj->data = obj + 1;
00236        if (data)
00237               memcpy(obj->data, data, length);
00238        *(struct nouveau_object **)obj->data = obj;
00239 
00240        dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
00241        switch (parent->oclass) {
00242        case NOUVEAU_DEVICE_CLASS:
00243               switch (obj->oclass) {
00244               case NOUVEAU_FIFO_CHANNEL_CLASS:
00245               {
00246                      if (dev->chipset < 0xc0)
00247                             ret = abi16_chan_nv04(obj);
00248                      else
00249                             ret = abi16_chan_nvc0(obj);
00250               }
00251                      break;
00252               default:
00253                      break;
00254               }
00255               break;
00256        case NOUVEAU_FIFO_CHANNEL_CLASS:
00257               switch (obj->oclass) {
00258               case NOUVEAU_NOTIFIER_CLASS:
00259                      ret = abi16_ntfy(obj);
00260                      break;
00261               default:
00262                      ret = abi16_engobj(obj);
00263                      break;
00264               }
00265        default:
00266               break;
00267        }
00268 
00269        if (ret) {
00270               free(obj);
00271               return ret;
00272        }
00273 
00274        *pobj = obj;
00275        return 0;
00276 }
00277 
00278 void
00279 nouveau_object_del(struct nouveau_object **pobj)
00280 {
00281        struct nouveau_object *obj = *pobj;
00282        struct nouveau_device *dev;
00283        if (obj) {
00284               dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
00285               if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
00286                      struct drm_nouveau_channel_free req;
00287                      req.channel = obj->handle;
00288                      drmCommandWrite(dev->fd, DRM_NOUVEAU_CHANNEL_FREE,
00289                                    &req, sizeof(req));
00290               } else {
00291                      struct drm_nouveau_gpuobj_free req;
00292                      req.channel = obj->parent->handle;
00293                      req.handle  = obj->handle;
00294                      drmCommandWrite(dev->fd, DRM_NOUVEAU_GPUOBJ_FREE,
00295                                    &req, sizeof(req));
00296               }
00297        }
00298        free(obj);
00299        *pobj = NULL;
00300 }
00301 
00302 void *
00303 nouveau_object_find(struct nouveau_object *obj, uint32_t pclass)
00304 {
00305        while (obj && obj->oclass != pclass) {
00306               obj = obj->parent;
00307               if (pclass == NOUVEAU_PARENT_CLASS)
00308                      break;
00309        }
00310        return obj;
00311 }
00312 
00313 static void
00314 nouveau_bo_del(struct nouveau_bo *bo)
00315 {
00316        struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
00317        struct drm_gem_close req = { bo->handle };
00318        DRMLISTDEL(&nvbo->head);
00319        if (bo->map)
00320               munmap(bo->map, bo->size);
00321        drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &req);
00322        free(nvbo);
00323 }
00324 
00325 int
00326 nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, uint32_t align,
00327               uint64_t size, union nouveau_bo_config *config,
00328               struct nouveau_bo **pbo)
00329 {
00330        struct nouveau_device_priv *nvdev = nouveau_device(dev);
00331        struct nouveau_bo_priv *nvbo = calloc(1, sizeof(*nvbo));
00332        struct nouveau_bo *bo = &nvbo->base;
00333        int ret;
00334 
00335        if (!nvbo)
00336               return -ENOMEM;
00337        atomic_set(&nvbo->refcnt, 1);
00338        bo->device = dev;
00339        bo->flags = flags;
00340        bo->size = size;
00341 
00342        ret = abi16_bo_init(bo, align, config);
00343        if (ret) {
00344               free(nvbo);
00345               return ret;
00346        }
00347 
00348        DRMLISTADD(&nvbo->head, &nvdev->bo_list);
00349 
00350        *pbo = bo;
00351        return 0;
00352 }
00353 
00354 int
00355 nouveau_bo_wrap(struct nouveau_device *dev, uint32_t handle,
00356               struct nouveau_bo **pbo)
00357 {
00358        struct nouveau_device_priv *nvdev = nouveau_device(dev);
00359        struct drm_nouveau_gem_info req = { .handle = handle };
00360        struct nouveau_bo_priv *nvbo;
00361        int ret;
00362 
00363        DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
00364               if (nvbo->base.handle == handle) {
00365                      *pbo = NULL;
00366                      nouveau_bo_ref(&nvbo->base, pbo);
00367                      return 0;
00368               }
00369        }
00370 
00371        ret = drmCommandWriteRead(dev->fd, DRM_NOUVEAU_GEM_INFO,
00372                               &req, sizeof(req));
00373        if (ret)
00374               return ret;
00375 
00376        nvbo = calloc(1, sizeof(*nvbo));
00377        if (nvbo) {
00378               atomic_set(&nvbo->refcnt, 1);
00379               nvbo->base.device = dev;
00380               abi16_bo_info(&nvbo->base, &req);
00381               DRMLISTADD(&nvbo->head, &nvdev->bo_list);
00382               *pbo = &nvbo->base;
00383               return 0;
00384        }
00385 
00386        return -ENOMEM;
00387 }
00388 
00389 int
00390 nouveau_bo_name_ref(struct nouveau_device *dev, uint32_t name,
00391                   struct nouveau_bo **pbo)
00392 {
00393        struct nouveau_device_priv *nvdev = nouveau_device(dev);
00394        struct nouveau_bo_priv *nvbo;
00395        struct drm_gem_open req = { .name = name };
00396        int ret;
00397 
00398        DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
00399               if (nvbo->name == name) {
00400                      *pbo = NULL;
00401                      nouveau_bo_ref(&nvbo->base, pbo);
00402                      return 0;
00403               }
00404        }
00405 
00406        ret = drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req);
00407        if (ret == 0) {
00408               ret = nouveau_bo_wrap(dev, req.handle, pbo);
00409               nouveau_bo((*pbo))->name = name;
00410        }
00411 
00412        return ret;
00413 }
00414 
00415 int
00416 nouveau_bo_name_get(struct nouveau_bo *bo, uint32_t *name)
00417 {
00418        struct drm_gem_flink req = { .handle = bo->handle };
00419        struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
00420        if (!nvbo->name) {
00421               int ret = drmIoctl(bo->device->fd, DRM_IOCTL_GEM_FLINK, &req);
00422               if (ret)
00423                      return ret;
00424               nvbo->name = req.name;
00425        }
00426        *name = nvbo->name;
00427        return 0;
00428 }
00429 
00430 void
00431 nouveau_bo_ref(struct nouveau_bo *bo, struct nouveau_bo **pref)
00432 {
00433        struct nouveau_bo *ref = *pref;
00434        if (bo) {
00435               atomic_inc(&nouveau_bo(bo)->refcnt);
00436        }
00437        if (ref) {
00438               if (atomic_dec_and_test(&nouveau_bo(ref)->refcnt))
00439                      nouveau_bo_del(ref);
00440        }
00441        *pref = bo;
00442 }
00443 
00444 int
00445 nouveau_bo_wait(struct nouveau_bo *bo, uint32_t access,
00446               struct nouveau_client *client)
00447 {
00448        struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
00449        struct drm_nouveau_gem_cpu_prep req;
00450        struct nouveau_pushbuf *push;
00451        int ret = 0;
00452 
00453        if (!(access & NOUVEAU_BO_RDWR))
00454               return 0;
00455 
00456        push = cli_push_get(client, bo);
00457        if (push && push->channel)
00458               nouveau_pushbuf_kick(push, push->channel);
00459 
00460        if (!nvbo->name && !(nvbo->access & NOUVEAU_BO_WR) &&
00461                         !(      access & NOUVEAU_BO_WR))
00462               return 0;
00463 
00464        req.handle = bo->handle;
00465        req.flags = 0;
00466        if (access & NOUVEAU_BO_WR)
00467               req.flags |= NOUVEAU_GEM_CPU_PREP_WRITE;
00468        if (access & NOUVEAU_BO_NOBLOCK)
00469               req.flags |= NOUVEAU_GEM_CPU_PREP_NOWAIT;
00470 
00471        ret = drmCommandWrite(bo->device->fd, DRM_NOUVEAU_GEM_CPU_PREP,
00472                            &req, sizeof(req));
00473        if (ret == 0)
00474               nvbo->access = 0;
00475        return ret;
00476 }
00477 
00478 int
00479 nouveau_bo_map(struct nouveau_bo *bo, uint32_t access,
00480               struct nouveau_client *client)
00481 {
00482        struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
00483        if (bo->map == NULL) {
00484               bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE,
00485                             MAP_SHARED, bo->device->fd, nvbo->map_handle);
00486               if (bo->map == MAP_FAILED) {
00487                      bo->map = NULL;
00488                      return -errno;
00489               }
00490        }
00491        return nouveau_bo_wait(bo, access, client);
00492 }