diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c')
| -rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c | 131 |
1 files changed, 125 insertions, 6 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index 09e8ebbd4ee9..ef201f1597c7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -28,8 +28,35 @@ #include <subdev/bios/dcb.h> #include <subdev/i2c.h> +void +nvkm_outp_route(struct nvkm_disp *disp) +{ + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + list_for_each_entry(ior, &disp->ior, head) { + if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) { + OUTP_DBG(outp, "release %s", ior->name); + if (ior->func->route.set) + ior->func->route.set(outp, NULL); + ior->arm.outp = NULL; + } + } + + list_for_each_entry(ior, &disp->ior, head) { + if ((outp = ior->asy.outp)) { + OUTP_DBG(outp, "acquire %s", ior->name); + if (ior->asy.outp != ior->arm.outp) { + if (ior->func->route.set) + ior->func->route.set(outp, ior); + ior->arm.outp = ior->asy.outp; + } + } + } +} + static enum nvkm_ior_proto -nvkm_outp_xlat(struct nvkm_output *outp, enum nvkm_ior_type *type) +nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) { switch (outp->info.location) { case 0: @@ -58,6 +85,75 @@ nvkm_outp_xlat(struct nvkm_output *outp, enum nvkm_ior_type *type) } void +nvkm_outp_release(struct nvkm_outp *outp, u8 user) +{ + struct nvkm_ior *ior = outp->ior; + OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); + if (ior) { + outp->acquired &= ~user; + if (!outp->acquired) { + outp->ior->asy.outp = NULL; + outp->ior = NULL; + } + } +} + +static inline int +nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) +{ + outp->ior = ior; + outp->ior->asy.outp = outp; + outp->ior->asy.link = outp->info.sorconf.link; + outp->acquired |= user; + return 0; +} + +int +nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) +{ + struct nvkm_ior *ior = outp->ior; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + + OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior); + if (ior) { + outp->acquired |= user; + return 0; + } + + /* Lookup a compatible, and unused, OR to assign to the device. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return -ENOSYS; + + /* First preference is to reuse the OR that is currently armed + * on HW, if any, in order to prevent unnecessary switching. + */ + list_for_each_entry(ior, &outp->disp->ior, head) { + if (!ior->asy.outp && ior->arm.outp == outp) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + /* Failing that, a completely unused OR is the next best thing. */ + list_for_each_entry(ior, &outp->disp->ior, head) { + if (!ior->asy.outp && ior->type == type && !ior->arm.outp && + ior->id == __ffs(outp->info.or)) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + /* Last resort is to assign an OR that's already active on HW, + * but will be released during the next modeset. + */ + list_for_each_entry(ior, &outp->disp->ior, head) { + if (!ior->asy.outp && ior->type == type && + ior->id == __ffs(outp->info.or)) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + return -ENOSPC; +} + +void nvkm_outp_fini(struct nvkm_outp *outp) { if (outp->func->fini) @@ -65,22 +161,36 @@ nvkm_outp_fini(struct nvkm_outp *outp) } static void -nvkm_outp_init_route(struct nvkm_output *outp) +nvkm_outp_init_route(struct nvkm_outp *outp) { struct nvkm_disp *disp = outp->disp; enum nvkm_ior_proto proto; enum nvkm_ior_type type; struct nvkm_ior *ior; - int id; + int id, link; + /* Find any OR from the class that is able to support this device. */ proto = nvkm_outp_xlat(outp, &type); if (proto == UNKNOWN) return; + ior = nvkm_ior_find(disp, type, -1); + if (!ior) { + WARN_ON(1); + return; + } + /* Determine the specific OR, if any, this device is attached to. */ - if (1) { + if (ior->func->route.get) { + id = ior->func->route.get(outp, &link); + if (id < 0) { + OUTP_DBG(outp, "no route"); + return; + } + } else { /* Prior to DCB 4.1, this is hardwired like so. */ - id = ffs(outp->info.or) - 1; + id = ffs(outp->info.or) - 1; + link = (ior->type == SOR) ? outp->info.sorconf.link : 0; } ior = nvkm_ior_find(disp, type, id); @@ -89,7 +199,16 @@ nvkm_outp_init_route(struct nvkm_output *outp) return; } - outp->ior = ior; + /* Determine if the OR is already configured for this device. */ + ior->func->state(ior, &ior->arm); + if (!ior->arm.head || ior->arm.proto != proto) { + OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head, + ior->arm.proto, proto); + return; + } + + OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link); + ior->arm.outp = outp; } void |
