drm/nouveau/devinit: fixup various issues with subdev ctor/init ordering
authorBen Skeggs <bskeggs@redhat.com>
Tue, 2 Oct 2012 00:30:34 +0000 (10:30 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 3 Oct 2012 03:13:17 +0000 (13:13 +1000)
Details of the problem, and solution, are in comments in the commit
proper.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/include/core/device.h
drivers/gpu/drm/nouveau/core/subdev/device/base.c

index 57899fc..e58b6f0 100644 (file)
@@ -8,11 +8,23 @@
 enum nv_subdev_type {
        NVDEV_SUBDEV_DEVICE,
        NVDEV_SUBDEV_VBIOS,
+
+       /* All subdevs from DEVINIT to DEVINIT_LAST will be created before
+        * *any* of them are initialised.  This subdev category is used
+        * for any subdevs that the VBIOS init table parsing may call out
+        * to during POST.
+        */
+       NVDEV_SUBDEV_DEVINIT,
        NVDEV_SUBDEV_GPIO,
        NVDEV_SUBDEV_I2C,
        NVDEV_SUBDEV_CLOCK,
+       NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_CLOCK,
+
+       /* This grouping of subdevs are initialised right after they've
+        * been created, and are allowed to assume any subdevs in the
+        * list above them exist and have been initialised.
+        */
        NVDEV_SUBDEV_MXM,
-       NVDEV_SUBDEV_DEVINIT,
        NVDEV_SUBDEV_MC,
        NVDEV_SUBDEV_TIMER,
        NVDEV_SUBDEV_FB,
@@ -23,6 +35,7 @@ enum nv_subdev_type {
        NVDEV_SUBDEV_BAR,
        NVDEV_SUBDEV_VOLT,
        NVDEV_SUBDEV_THERM,
+
        NVDEV_ENGINE_DMAOBJ,
        NVDEV_ENGINE_FIFO,
        NVDEV_ENGINE_SW,
@@ -38,6 +51,7 @@ enum nv_subdev_type {
        NVDEV_ENGINE_UNK1C1,
        NVDEV_ENGINE_VENC,
        NVDEV_ENGINE_DISP,
+
        NVDEV_SUBDEV_NR,
 };
 
index 7bf6f37..ca9a464 100644 (file)
@@ -96,14 +96,13 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
                    struct nouveau_object **pobject)
 {
        struct nouveau_client *client = nv_client(parent);
-       struct nouveau_object *subdev = NULL;
        struct nouveau_device *device;
        struct nouveau_devobj *devobj;
        struct nv_device_class *args = data;
        u64 disable, boot0, strap;
        u64 mmio_base, mmio_size;
        void __iomem *map;
-       int ret, i;
+       int ret, i, c;
 
        if (size < sizeof(struct nv_device_class))
                return -EINVAL;
@@ -234,34 +233,43 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
        }
 
        /* ensure requested subsystems are available for use */
-       for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
+       for (i = 0, c = 0; i < NVDEV_SUBDEV_NR; i++) {
                if (!(oclass = device->oclass[i]) || (disable & (1ULL << i)))
                        continue;
 
                if (!device->subdev[i]) {
                        ret = nouveau_object_ctor(nv_object(device), NULL,
-                                                 oclass, NULL, i, &subdev);
+                                                 oclass, NULL, i,
+                                                 &devobj->subdev[i]);
                        if (ret == -ENODEV)
                                continue;
                        if (ret)
                                return ret;
 
-                       if (nv_iclass(subdev, NV_ENGINE_CLASS))
-                               nouveau_subdev_reset(subdev);
+                       if (nv_iclass(devobj->subdev[i], NV_ENGINE_CLASS))
+                               nouveau_subdev_reset(devobj->subdev[i]);
                } else {
-                       nouveau_object_ref(device->subdev[i], &subdev);
+                       nouveau_object_ref(device->subdev[i],
+                                         &devobj->subdev[i]);
                }
 
-               if (!nv_iclass(subdev, NV_ENGINE_CLASS)) {
-                       ret = nouveau_object_inc(subdev);
-                       if (ret) {
-                               nouveau_object_ref(NULL, &subdev);
-                               return ret;
+               /* note: can't init *any* subdevs until devinit has been run
+                * due to not knowing exactly what the vbios init tables will
+                * mess with.  devinit also can't be run until all of its
+                * dependencies have been created.
+                *
+                * this code delays init of any subdev until all of devinit's
+                * dependencies have been created, and then initialises each
+                * subdev in turn as they're created.
+                */
+               while (i >= NVDEV_SUBDEV_DEVINIT_LAST && c <= i) {
+                       struct nouveau_object *subdev = devobj->subdev[c++];
+                       if (subdev && !nv_iclass(subdev, NV_ENGINE_CLASS)) {
+                               ret = nouveau_object_inc(subdev);
+                               if (ret)
+                                       return ret;
                        }
                }
-
-               nouveau_object_ref(subdev, &devobj->subdev[i]);
-               nouveau_object_ref(NULL, &subdev);
        }
 
        return 0;