summary refs log tree commit diff
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2012-10-02 10:30:34 +1000
committerBen Skeggs <bskeggs@redhat.com>2012-10-03 13:13:17 +1000
commit7234d0230e5a371a0cd1969c46eec802c1d6c4ed (patch)
treed03c5498449000f1e809025828c28603914bc253
parent002d0c735c1bd8bffd3786ad5aadb205a76878fa (diff)
downloadlinux-7234d0230e5a371a0cd1969c46eec802c1d6c4ed.tar.gz
drm/nouveau/devinit: fixup various issues with subdev ctor/init ordering
Details of the problem, and solution, are in comments in the commit
proper.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/device.h16
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/device/base.c38
2 files changed, 38 insertions, 16 deletions
diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h
index 57899fc3b877..e58b6f0984c1 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/device.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/device.h
@@ -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,
 };
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/base.c b/drivers/gpu/drm/nouveau/core/subdev/device/base.c
index 7bf6f3760b99..ca9a4648bd8a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/base.c
@@ -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;