summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
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 /drivers/gpu/drm
parent002d0c735c1bd8bffd3786ad5aadb205a76878fa (diff)
downloadlinux-3.10-7234d0230e5a371a0cd1969c46eec802c1d6c4ed.tar.gz
linux-3.10-7234d0230e5a371a0cd1969c46eec802c1d6c4ed.tar.bz2
linux-3.10-7234d0230e5a371a0cd1969c46eec802c1d6c4ed.zip
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>
Diffstat (limited to 'drivers/gpu/drm')
-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 57899fc3b87..e58b6f0984c 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 7bf6f3760b9..ca9a4648bd8 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;