summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c144
1 files changed, 114 insertions, 30 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
index 6094805fbd63..bfb2a4db8d64 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
@@ -22,14 +22,16 @@
* Authors: Ben Skeggs
*/
#include "outp.h"
+#include "conn.h"
#include "dp.h"
#include "ior.h"
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
+#include <subdev/gpio.h>
#include <subdev/i2c.h>
-void
+static void
nvkm_outp_route(struct nvkm_disp *disp)
{
struct nvkm_outp *outp;
@@ -46,8 +48,8 @@ nvkm_outp_route(struct nvkm_disp *disp)
list_for_each_entry(ior, &disp->iors, head) {
if ((outp = ior->asy.outp)) {
- OUTP_DBG(outp, "acquire %s", ior->name);
if (ior->asy.outp != ior->arm.outp) {
+ OUTP_DBG(outp, "acquire %s", ior->name);
if (ior->func->route.set)
ior->func->route.set(outp, ior);
ior->arm.outp = ior->asy.outp;
@@ -87,22 +89,20 @@ nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type)
}
void
-nvkm_outp_release(struct nvkm_outp *outp, u8 user)
+nvkm_outp_release_or(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) {
- if (outp->func->release && outp->ior)
- outp->func->release(outp);
outp->ior->asy.outp = NULL;
outp->ior = NULL;
}
}
}
-static inline int
+int
nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
{
outp->ior = ior;
@@ -140,7 +140,7 @@ nvkm_outp_acquire_hda(struct nvkm_outp *outp, enum nvkm_ior_type type,
}
int
-nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda)
+nvkm_outp_acquire_or(struct nvkm_outp *outp, u8 user, bool hda)
{
struct nvkm_ior *ior = outp->ior;
enum nvkm_ior_proto proto;
@@ -207,39 +207,110 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda)
return nvkm_outp_acquire_hda(outp, type, user, false);
}
+int
+nvkm_outp_bl_set(struct nvkm_outp *outp, int level)
+{
+ int ret;
+
+ ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false);
+ if (ret)
+ return ret;
+
+ if (outp->ior->func->bl)
+ ret = outp->ior->func->bl->set(outp->ior, level);
+ else
+ ret = -EINVAL;
+
+ nvkm_outp_release_or(outp, NVKM_OUTP_PRIV);
+ return ret;
+}
+
+int
+nvkm_outp_bl_get(struct nvkm_outp *outp)
+{
+ int ret;
+
+ ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false);
+ if (ret)
+ return ret;
+
+ if (outp->ior->func->bl)
+ ret = outp->ior->func->bl->get(outp->ior);
+ else
+ ret = -EINVAL;
+
+ nvkm_outp_release_or(outp, NVKM_OUTP_PRIV);
+ return ret;
+}
+
+int
+nvkm_outp_detect(struct nvkm_outp *outp)
+{
+ struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio;
+ int ret = -EINVAL;
+
+ if (outp->conn->info.hpd != DCB_GPIO_UNUSED) {
+ ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, outp->conn->info.hpd);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ return 1;
+
+ /*TODO: Look into returning NOT_PRESENT if !HPD on DVI/HDMI.
+ *
+ * It's uncertain whether this is accurate for all older chipsets,
+ * so we're returning UNKNOWN, and the DRM will probe DDC instead.
+ */
+ if (outp->info.type == DCB_OUTPUT_DP)
+ return 0;
+ }
+
+ return ret;
+}
+
void
-nvkm_outp_fini(struct nvkm_outp *outp)
+nvkm_outp_release(struct nvkm_outp *outp)
{
- if (outp->func->fini)
- outp->func->fini(outp);
+ nvkm_outp_release_or(outp, NVKM_OUTP_USER);
+ nvkm_outp_route(outp->disp);
}
-static void
-nvkm_outp_init_route(struct nvkm_outp *outp)
+int
+nvkm_outp_acquire(struct nvkm_outp *outp, bool hda)
+{
+ int ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_USER, hda);
+
+ if (ret)
+ return ret;
+
+ nvkm_outp_route(outp->disp);
+ return 0;
+}
+
+struct nvkm_ior *
+nvkm_outp_inherit(struct nvkm_outp *outp)
{
struct nvkm_disp *disp = outp->disp;
+ struct nvkm_ior *ior;
enum nvkm_ior_proto proto;
enum nvkm_ior_type type;
- struct nvkm_ior *ior;
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;
+ return NULL;
ior = nvkm_ior_find(disp, type, -1);
- if (!ior) {
- WARN_ON(1);
- return;
- }
+ if (WARN_ON(!ior))
+ return NULL;
/* Determine the specific OR, if any, this device is attached to. */
if (ior->func->route.get) {
id = ior->func->route.get(outp, &link);
if (id < 0) {
OUTP_DBG(outp, "no route");
- return;
+ return NULL;
}
} else {
/* Prior to DCB 4.1, this is hardwired like so. */
@@ -248,10 +319,24 @@ nvkm_outp_init_route(struct nvkm_outp *outp)
}
ior = nvkm_ior_find(disp, type, id);
- if (!ior) {
- WARN_ON(1);
+ if (WARN_ON(!ior))
+ return NULL;
+
+ return ior;
+}
+
+void
+nvkm_outp_init(struct nvkm_outp *outp)
+{
+ enum nvkm_ior_proto proto;
+ enum nvkm_ior_type type;
+ struct nvkm_ior *ior;
+
+ /* Find any OR from the class that is able to support this device. */
+ proto = nvkm_outp_xlat(outp, &type);
+ ior = outp->func->inherit(outp);
+ if (!ior)
return;
- }
/* Determine if the OR is already configured for this device. */
ior->func->state(ior, &ior->arm);
@@ -274,14 +359,6 @@ nvkm_outp_init_route(struct nvkm_outp *outp)
}
void
-nvkm_outp_init(struct nvkm_outp *outp)
-{
- nvkm_outp_init_route(outp);
- if (outp->func->init)
- outp->func->init(outp);
-}
-
-void
nvkm_outp_del(struct nvkm_outp **poutp)
{
struct nvkm_outp *outp = *poutp;
@@ -328,6 +405,13 @@ nvkm_outp_new_(const struct nvkm_outp_func *func, struct nvkm_disp *disp,
static const struct nvkm_outp_func
nvkm_outp = {
+ .init = nvkm_outp_init,
+ .detect = nvkm_outp_detect,
+ .inherit = nvkm_outp_inherit,
+ .acquire = nvkm_outp_acquire,
+ .release = nvkm_outp_release,
+ .bl.get = nvkm_outp_bl_get,
+ .bl.set = nvkm_outp_bl_set,
};
int