nouveau: add interfaces to query information about supported classes

This will expose functionality supported by newer kernel interfaces.

Current userspace uses the chipset to determine which classes are likely
exposed, which generally works pretty well, but isn't as flexible as it
could be.

Unfortunately, the G98:GF100 video code in Mesa is still relying on the
kernel exposing incorrect vdec classes on some chipsets.  The ABI16
kernel interfaces have a workaround for this in place, but that will no
longer be available once libdrm supports NVIF.

To prevent a regression when NVIF support is added, if there's no kernel
support for NVIF, libdrm will magic up a class list containing correct
vdec classes anyway instead of failing with -ENODEV.

v2.
- add description of abi16/vdec workaround
- add description of sclass/mclass
- leave client-provided pointer unmodified on abi16_sclass() failure

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>
diff --git a/nouveau/abi16.c b/nouveau/abi16.c
index 44fda64..f260bf9 100644
--- a/nouveau/abi16.c
+++ b/nouveau/abi16.c
@@ -29,12 +29,12 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <stddef.h>
+#include <errno.h>
 
 #include "private.h"
 
 #include "nvif/class.h"
 
-
 static int
 abi16_chan_nv04(struct nouveau_object *obj)
 {
@@ -171,6 +171,55 @@
 	return 0;
 }
 
+drm_private int
+abi16_sclass(struct nouveau_object *obj, struct nouveau_sclass **psclass)
+{
+	struct nouveau_sclass *sclass;
+	struct nouveau_device *dev;
+
+	if (!(sclass = calloc(8, sizeof(*sclass))))
+		return -ENOMEM;
+	*psclass = sclass;
+
+	switch (obj->oclass) {
+	case NOUVEAU_FIFO_CHANNEL_CLASS:
+		/* Older kernel versions were exposing the wrong video engine
+		 * classes on certain G98:GF100 boards.  This has since been
+		 * corrected, but ABI16 has compatibility in place to avoid
+		 * breaking older userspace.
+		 *
+		 * Clients that have been updated to use NVIF are required to
+		 * use the correct classes, which means that they'll break if
+		 * running on an older kernel.
+		 *
+		 * To handle this issue, if using the older kernel interfaces,
+		 * we'll magic up a list containing the vdec classes that the
+		 * kernel will accept for these boards.  Clients should make
+		 * use of this information instead of hardcoding classes for
+		 * specific chipsets.
+		 */
+		dev = (struct nouveau_device *)obj->parent;
+		if (dev->chipset >= 0x98 &&
+		    dev->chipset != 0xa0 &&
+		    dev->chipset <  0xc0) {
+			*sclass++ = (struct nouveau_sclass){
+				GT212_MSVLD, -1, -1
+			};
+			*sclass++ = (struct nouveau_sclass){
+				GT212_MSPDEC, -1, -1
+			};
+			*sclass++ = (struct nouveau_sclass){
+				GT212_MSPPP, -1, -1
+			};
+		}
+		break;
+	default:
+		break;
+	}
+
+	return sclass - *psclass;
+}
+
 drm_private void
 abi16_delete(struct nouveau_object *obj)
 {