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;