summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2015-11-24 09:17:52 +1000
committerBen Skeggs <bskeggs@redhat.com>2015-12-22 13:21:56 +1000
commit4a3cbf5c0a38adf41d637c5d7273480336df39d0 (patch)
treebb07ba39db1d77bf2c86c1d9d76dbe0474f575d6
parentd1ec093e4c5b08c3825fe07e287aa3d023e9c9ae (diff)
nouveau: move more abi16-specific logic into abi16.c
v2. - add a comment about the (ab)use of nouveau_object::length - add a comment about abi16_object() return values v3. - handle new client + old kernel for sw classes Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Tested-by: Samuel Pitoiset <samuel.pitoiset@gmail.com> Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
-rw-r--r--nouveau/abi16.c88
-rw-r--r--nouveau/nouveau.c56
-rw-r--r--nouveau/private.h7
3 files changed, 93 insertions, 58 deletions
diff --git a/nouveau/abi16.c b/nouveau/abi16.c
index 59bc4360..44fda645 100644
--- a/nouveau/abi16.c
+++ b/nouveau/abi16.c
@@ -32,8 +32,10 @@
#include "private.h"
+#include "nvif/class.h"
-drm_private int
+
+static int
abi16_chan_nv04(struct nouveau_object *obj)
{
struct nouveau_device *dev = (struct nouveau_device *)obj->parent;
@@ -57,7 +59,7 @@ abi16_chan_nv04(struct nouveau_object *obj)
return 0;
}
-drm_private int
+static int
abi16_chan_nvc0(struct nouveau_object *obj)
{
struct nouveau_device *dev = (struct nouveau_device *)obj->parent;
@@ -78,7 +80,7 @@ abi16_chan_nvc0(struct nouveau_object *obj)
return 0;
}
-drm_private int
+static int
abi16_chan_nve0(struct nouveau_object *obj)
{
struct nouveau_device *dev = (struct nouveau_device *)obj->parent;
@@ -104,7 +106,7 @@ abi16_chan_nve0(struct nouveau_object *obj)
return 0;
}
-drm_private int
+static int
abi16_engobj(struct nouveau_object *obj)
{
struct drm_nouveau_grobj_alloc req = {
@@ -115,6 +117,27 @@ abi16_engobj(struct nouveau_object *obj)
struct nouveau_device *dev;
int ret;
+ /* Older kernel versions did not have the concept of nouveau-
+ * specific classes and abused some NVIDIA-assigned ones for
+ * a SW class. The ABI16 layer has compatibility in place to
+ * translate these older identifiers to the newer ones.
+ *
+ * Clients that have been updated to use NVIF are required to
+ * use the newer class identifiers, which means that they'll
+ * break if running on an older kernel.
+ *
+ * To handle this case, when using ABI16, we translate to the
+ * older values which work on any kernel.
+ */
+ switch (req.class) {
+ case NVIF_CLASS_SW_NV04 : req.class = 0x006e; break;
+ case NVIF_CLASS_SW_NV10 : req.class = 0x016e; break;
+ case NVIF_CLASS_SW_NV50 : req.class = 0x506e; break;
+ case NVIF_CLASS_SW_GF100: req.class = 0x906e; break;
+ default:
+ break;
+ }
+
dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
ret = drmCommandWrite(dev->fd, DRM_NOUVEAU_GROBJ_ALLOC,
&req, sizeof(req));
@@ -125,7 +148,7 @@ abi16_engobj(struct nouveau_object *obj)
return 0;
}
-drm_private int
+static int
abi16_ntfy(struct nouveau_object *obj)
{
struct nv04_notify *ntfy = obj->data;
@@ -149,6 +172,61 @@ abi16_ntfy(struct nouveau_object *obj)
}
drm_private void
+abi16_delete(struct nouveau_object *obj)
+{
+ struct nouveau_device *dev =
+ nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
+ if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
+ struct drm_nouveau_channel_free req;
+ req.channel = obj->handle;
+ drmCommandWrite(dev->fd, DRM_NOUVEAU_CHANNEL_FREE,
+ &req, sizeof(req));
+ } else {
+ struct drm_nouveau_gpuobj_free req;
+ req.channel = obj->parent->handle;
+ req.handle = obj->handle;
+ drmCommandWrite(dev->fd, DRM_NOUVEAU_GPUOBJ_FREE,
+ &req, sizeof(req));
+ }
+}
+
+drm_private bool
+abi16_object(struct nouveau_object *obj, int (**func)(struct nouveau_object *))
+{
+ struct nouveau_object *parent = obj->parent;
+
+ /* nouveau_object::length is (ab)used to determine whether the
+ * object is a legacy object (!=0), or a real NVIF object.
+ */
+ if ((parent->length != 0 && parent->oclass == NOUVEAU_DEVICE_CLASS)) {
+ if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
+ struct nouveau_device *dev = (void *)parent;
+ if (dev->chipset < 0xc0)
+ *func = abi16_chan_nv04;
+ else
+ if (dev->chipset < 0xe0)
+ *func = abi16_chan_nvc0;
+ else
+ *func = abi16_chan_nve0;
+ return true;
+ }
+ } else
+ if ((parent->length != 0 &&
+ parent->oclass == NOUVEAU_FIFO_CHANNEL_CLASS)) {
+ if (obj->oclass == NOUVEAU_NOTIFIER_CLASS) {
+ *func = abi16_ntfy;
+ return true;
+ }
+
+ *func = abi16_engobj;
+ return false; /* try NVIF, if supported, before calling func */
+ }
+
+ *func = NULL;
+ return false;
+}
+
+drm_private void
abi16_bo_info(struct nouveau_bo *bo, struct drm_nouveau_gem_info *info)
{
struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
diff --git a/nouveau/nouveau.c b/nouveau/nouveau.c
index 97fd77b9..8a0be2fa 100644
--- a/nouveau/nouveau.c
+++ b/nouveau/nouveau.c
@@ -135,6 +135,7 @@ nouveau_device_wrap(int fd, int close, struct nouveau_device **pdev)
nvdev->gart_limit_percent = 80;
DRMINITLISTHEAD(&nvdev->bo_list);
nvdev->base.object.oclass = NOUVEAU_DEVICE_CLASS;
+ nvdev->base.object.length = ~0;
nvdev->base.lib_version = 0x01000000;
nvdev->base.chipset = chipset;
nvdev->base.vram_size = vram;
@@ -251,8 +252,8 @@ nouveau_object_new(struct nouveau_object *parent, uint64_t handle,
uint32_t oclass, void *data, uint32_t length,
struct nouveau_object **pobj)
{
- struct nouveau_device *dev;
struct nouveau_object *obj;
+ int (*func)(struct nouveau_object *);
int ret = -EINVAL;
if (length == 0)
@@ -267,37 +268,9 @@ nouveau_object_new(struct nouveau_object *parent, uint64_t handle,
memcpy(obj->data, data, length);
*(struct nouveau_object **)obj->data = obj;
- dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
- switch (parent->oclass) {
- case NOUVEAU_DEVICE_CLASS:
- switch (obj->oclass) {
- case NOUVEAU_FIFO_CHANNEL_CLASS:
- {
- if (dev->chipset < 0xc0)
- ret = abi16_chan_nv04(obj);
- else
- if (dev->chipset < 0xe0)
- ret = abi16_chan_nvc0(obj);
- else
- ret = abi16_chan_nve0(obj);
- }
- break;
- default:
- break;
- }
- break;
- case NOUVEAU_FIFO_CHANNEL_CLASS:
- switch (obj->oclass) {
- case NOUVEAU_NOTIFIER_CLASS:
- ret = abi16_ntfy(obj);
- break;
- default:
- ret = abi16_engobj(obj);
- break;
- }
- default:
- break;
- }
+ abi16_object(obj, &func);
+ if (func)
+ ret = func(obj);
if (ret) {
free(obj);
@@ -312,24 +285,11 @@ void
nouveau_object_del(struct nouveau_object **pobj)
{
struct nouveau_object *obj = *pobj;
- struct nouveau_device *dev;
if (obj) {
- dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
- if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
- struct drm_nouveau_channel_free req;
- req.channel = obj->handle;
- drmCommandWrite(dev->fd, DRM_NOUVEAU_CHANNEL_FREE,
- &req, sizeof(req));
- } else {
- struct drm_nouveau_gpuobj_free req;
- req.channel = obj->parent->handle;
- req.handle = obj->handle;
- drmCommandWrite(dev->fd, DRM_NOUVEAU_GPUOBJ_FREE,
- &req, sizeof(req));
- }
+ abi16_delete(obj);
+ free(obj);
+ *pobj = NULL;
}
- free(obj);
- *pobj = NULL;
}
void *
diff --git a/nouveau/private.h b/nouveau/private.h
index e9439f30..5f352a49 100644
--- a/nouveau/private.h
+++ b/nouveau/private.h
@@ -114,11 +114,8 @@ int
nouveau_device_open_existing(struct nouveau_device **, int, int, drm_context_t);
/* abi16.c */
-drm_private int abi16_chan_nv04(struct nouveau_object *);
-drm_private int abi16_chan_nvc0(struct nouveau_object *);
-drm_private int abi16_chan_nve0(struct nouveau_object *);
-drm_private int abi16_engobj(struct nouveau_object *);
-drm_private int abi16_ntfy(struct nouveau_object *);
+drm_private bool abi16_object(struct nouveau_object *, int (**)(struct nouveau_object *));
+drm_private void abi16_delete(struct nouveau_object *);
drm_private void abi16_bo_info(struct nouveau_bo *, struct drm_nouveau_gem_info *);
drm_private int abi16_bo_init(struct nouveau_bo *, uint32_t alignment,
union nouveau_bo_config *);