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>
This commit is contained in:
@@ -8,11 +8,23 @@
|
|||||||
enum nv_subdev_type {
|
enum nv_subdev_type {
|
||||||
NVDEV_SUBDEV_DEVICE,
|
NVDEV_SUBDEV_DEVICE,
|
||||||
NVDEV_SUBDEV_VBIOS,
|
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_GPIO,
|
||||||
NVDEV_SUBDEV_I2C,
|
NVDEV_SUBDEV_I2C,
|
||||||
NVDEV_SUBDEV_CLOCK,
|
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_MXM,
|
||||||
NVDEV_SUBDEV_DEVINIT,
|
|
||||||
NVDEV_SUBDEV_MC,
|
NVDEV_SUBDEV_MC,
|
||||||
NVDEV_SUBDEV_TIMER,
|
NVDEV_SUBDEV_TIMER,
|
||||||
NVDEV_SUBDEV_FB,
|
NVDEV_SUBDEV_FB,
|
||||||
@@ -23,6 +35,7 @@ enum nv_subdev_type {
|
|||||||
NVDEV_SUBDEV_BAR,
|
NVDEV_SUBDEV_BAR,
|
||||||
NVDEV_SUBDEV_VOLT,
|
NVDEV_SUBDEV_VOLT,
|
||||||
NVDEV_SUBDEV_THERM,
|
NVDEV_SUBDEV_THERM,
|
||||||
|
|
||||||
NVDEV_ENGINE_DMAOBJ,
|
NVDEV_ENGINE_DMAOBJ,
|
||||||
NVDEV_ENGINE_FIFO,
|
NVDEV_ENGINE_FIFO,
|
||||||
NVDEV_ENGINE_SW,
|
NVDEV_ENGINE_SW,
|
||||||
@@ -38,6 +51,7 @@ enum nv_subdev_type {
|
|||||||
NVDEV_ENGINE_UNK1C1,
|
NVDEV_ENGINE_UNK1C1,
|
||||||
NVDEV_ENGINE_VENC,
|
NVDEV_ENGINE_VENC,
|
||||||
NVDEV_ENGINE_DISP,
|
NVDEV_ENGINE_DISP,
|
||||||
|
|
||||||
NVDEV_SUBDEV_NR,
|
NVDEV_SUBDEV_NR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -96,14 +96,13 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
|
|||||||
struct nouveau_object **pobject)
|
struct nouveau_object **pobject)
|
||||||
{
|
{
|
||||||
struct nouveau_client *client = nv_client(parent);
|
struct nouveau_client *client = nv_client(parent);
|
||||||
struct nouveau_object *subdev = NULL;
|
|
||||||
struct nouveau_device *device;
|
struct nouveau_device *device;
|
||||||
struct nouveau_devobj *devobj;
|
struct nouveau_devobj *devobj;
|
||||||
struct nv_device_class *args = data;
|
struct nv_device_class *args = data;
|
||||||
u64 disable, boot0, strap;
|
u64 disable, boot0, strap;
|
||||||
u64 mmio_base, mmio_size;
|
u64 mmio_base, mmio_size;
|
||||||
void __iomem *map;
|
void __iomem *map;
|
||||||
int ret, i;
|
int ret, i, c;
|
||||||
|
|
||||||
if (size < sizeof(struct nv_device_class))
|
if (size < sizeof(struct nv_device_class))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -234,34 +233,43 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ensure requested subsystems are available for use */
|
/* 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)))
|
if (!(oclass = device->oclass[i]) || (disable & (1ULL << i)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!device->subdev[i]) {
|
if (!device->subdev[i]) {
|
||||||
ret = nouveau_object_ctor(nv_object(device), NULL,
|
ret = nouveau_object_ctor(nv_object(device), NULL,
|
||||||
oclass, NULL, i, &subdev);
|
oclass, NULL, i,
|
||||||
|
&devobj->subdev[i]);
|
||||||
if (ret == -ENODEV)
|
if (ret == -ENODEV)
|
||||||
continue;
|
continue;
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (nv_iclass(subdev, NV_ENGINE_CLASS))
|
if (nv_iclass(devobj->subdev[i], NV_ENGINE_CLASS))
|
||||||
nouveau_subdev_reset(subdev);
|
nouveau_subdev_reset(devobj->subdev[i]);
|
||||||
} else {
|
} else {
|
||||||
nouveau_object_ref(device->subdev[i], &subdev);
|
nouveau_object_ref(device->subdev[i],
|
||||||
|
&devobj->subdev[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nv_iclass(subdev, NV_ENGINE_CLASS)) {
|
/* note: can't init *any* subdevs until devinit has been run
|
||||||
ret = nouveau_object_inc(subdev);
|
* due to not knowing exactly what the vbios init tables will
|
||||||
if (ret) {
|
* mess with. devinit also can't be run until all of its
|
||||||
nouveau_object_ref(NULL, &subdev);
|
* dependencies have been created.
|
||||||
return ret;
|
*
|
||||||
|
* 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;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user