@@ -1711,7 +1711,8 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
drm_connector_attach_encoder(connector, encoder);
- disp->core->func->sor->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1);
+ nv_encoder->or = ffs(dcbe->or) - 1;
+ disp->core->func->sor->get_caps(disp, nv_encoder, nv_encoder->or);
nv50_outp_dump_caps(drm, nv_encoder);
if (dcbe->type == DCB_OUTPUT_DP) {
@@ -2473,6 +2474,103 @@ nv50_display_fini(struct drm_device *dev, bool runtime, bool suspend)
cancel_work_sync(&drm->hpd_work);
}
+static inline void
+nv50_display_read_hw_or_state(struct drm_device *dev, struct nv50_disp *disp,
+ struct nouveau_encoder *outp)
+{
+ struct drm_crtc *crtc;
+ struct drm_connector_list_iter conn_iter;
+ struct drm_connector *conn;
+ struct nv50_head_atom *armh;
+ const u32 encoder_mask = drm_encoder_mask(&outp->base.base);
+ bool found_conn = false, found_head = false;
+ u8 proto;
+ int head_idx;
+ int ret;
+
+ switch (outp->dcb->type) {
+ case DCB_OUTPUT_TMDS:
+ ret = nvif_outp_inherit_tmds(&outp->outp, &proto);
+ break;
+ case DCB_OUTPUT_DP:
+ ret = nvif_outp_inherit_dp(&outp->outp, &proto);
+ break;
+ case DCB_OUTPUT_LVDS:
+ ret = nvif_outp_inherit_lvds(&outp->outp, &proto);
+ break;
+ case DCB_OUTPUT_ANALOG:
+ ret = nvif_outp_inherit_rgb_crt(&outp->outp, &proto);
+ break;
+ default:
+ drm_dbg_kms(dev, "Readback for %s not implemented yet, skipping\n",
+ outp->base.base.name);
+ drm_WARN_ON(dev, true);
+ return;
+ }
+ if (ret >= 0) {
+ head_idx = ret;
+ ret = 0;
+ } else if (ret == -ENODEV) {
+ return;
+ }
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->index != head_idx)
+ continue;
+
+ armh = nv50_head_atom(crtc->state);
+ found_head = true;
+ break;
+ }
+ if (drm_WARN_ON(dev, !found_head))
+ return;
+
+ /* Figure out which connector is being used by this encoder */
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ nouveau_for_each_non_mst_connector_iter(conn, &conn_iter) {
+ if (nouveau_connector(conn)->index == outp->dcb->connector) {
+ found_conn = true;
+ break;
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+ if (drm_WARN_ON(dev, !found_conn))
+ return;
+
+ armh->state.encoder_mask = encoder_mask;
+ armh->state.connector_mask = drm_connector_mask(conn);
+ armh->state.active = true;
+ armh->state.enable = true;
+
+ outp->crtc = crtc;
+ outp->ctrl = NVVAL(NV507D, SOR_SET_CONTROL, PROTOCOL, proto) | BIT(crtc->index);
+
+ conn->state->crtc = crtc;
+ conn->state->best_encoder = &outp->base.base;
+}
+
+/* Read back the currently programmed display state */
+void
+nv50_display_read_hw_state(struct nouveau_drm *drm)
+{
+ struct drm_device *dev = drm->dev;
+ struct drm_encoder *encoder;
+ struct drm_modeset_acquire_ctx ctx;
+ struct nv50_disp *disp = nv50_disp(dev);
+ int ret;
+
+ DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
+
+ drm_for_each_encoder(encoder, dev) {
+ if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST)
+ continue;
+
+ nv50_display_read_hw_or_state(dev, disp, nouveau_encoder(encoder));
+ }
+
+ DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
+}
+
static int
nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
{
@@ -2490,6 +2588,9 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
}
}
+ if (!runtime)
+ nv50_display_read_hw_state(nouveau_drm(dev));
+
return 0;
}
@@ -18,6 +18,7 @@ union nvif_outp_args {
#define NVIF_OUTP_V0_DP_AUX_PWR 0x05
#define NVIF_OUTP_V0_DP_RETRAIN 0x06
#define NVIF_OUTP_V0_DP_MST_VCPI 0x07
+#define NVIF_OUTP_V0_INHERIT 0x08
union nvif_outp_load_detect_args {
struct nvif_outp_load_detect_v0 {
@@ -69,6 +70,23 @@ union nvif_outp_acquire_args {
} v0;
};
+union nvif_outp_inherit_args {
+ struct nvif_outp_inherit_v0 {
+ __u8 version;
+#define NVIF_OUTP_INHERIT_V0_RGB_CRT 0x00
+#define NVIF_OUTP_INHERIT_V0_TV 0x01
+#define NVIF_OUTP_INHERIT_V0_TMDS 0x02
+#define NVIF_OUTP_INHERIT_V0_LVDS 0x03
+#define NVIF_OUTP_INHERIT_V0_DP 0x04
+ // In/out. Input is one of the above values, output is the actual hw protocol
+ __u8 proto;
+ __u8 or;
+ __u8 link;
+ __u8 head;
+ __u8 pad[3];
+ } v0;
+};
+
union nvif_outp_release_args {
struct nvif_outp_release_vn {
} vn;
@@ -24,6 +24,11 @@ int nvif_outp_acquire_tmds(struct nvif_outp *, int head,
int nvif_outp_acquire_lvds(struct nvif_outp *, bool dual, bool bpc8);
int nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE],
int link_nr, int link_bw, bool hda, bool mst);
+int nvif_outp_inherit_rgb_crt(struct nvif_outp *outp, u8 *proto_out);
+int nvif_outp_inherit_lvds(struct nvif_outp *outp, u8 *proto_out);
+int nvif_outp_inherit_tmds(struct nvif_outp *outp, u8 *proto_out);
+int nvif_outp_inherit_dp(struct nvif_outp *outp, u8 *proto_out);
+
void nvif_outp_release(struct nvif_outp *);
int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size);
int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
@@ -196,6 +196,74 @@ nvif_outp_acquire_rgb_crt(struct nvif_outp *outp)
return ret;
}
+static int
+nvif_outp_inherit(struct nvif_outp *outp,
+ u8 proto,
+ struct nvif_outp_inherit_v0 *args,
+ u8 *proto_out)
+{
+ int ret;
+
+ args->version = 0;
+ args->proto = proto;
+
+ ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_INHERIT, args, sizeof(*args));
+ if (ret)
+ return ret;
+
+ outp->or.id = args->or;
+ outp->or.link = args->link;
+ *proto_out = args->proto;
+ return 0;
+}
+
+int
+nvif_outp_inherit_lvds(struct nvif_outp *outp, u8 *proto_out)
+{
+ struct nvif_outp_inherit_v0 args;
+ int ret;
+
+ ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_LVDS, &args, proto_out);
+ NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:LVDS] ret:%d", ret);
+ return ret ?: args.head;
+}
+
+int
+nvif_outp_inherit_tmds(struct nvif_outp *outp, u8 *proto_out)
+{
+ struct nvif_outp_inherit_v0 args;
+ int ret;
+
+ ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_TMDS, &args, proto_out);
+ NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:TMDS] ret:%d", ret);
+ return ret ?: args.head;
+}
+
+int
+nvif_outp_inherit_dp(struct nvif_outp *outp, u8 *proto_out)
+{
+ struct nvif_outp_inherit_v0 args;
+ int ret;
+
+ ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_DP, &args, proto_out);
+ NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:DP] ret:%d", ret);
+
+ // TODO: Get current link info
+
+ return ret ?: args.head;
+}
+
+int
+nvif_outp_inherit_rgb_crt(struct nvif_outp *outp, u8 *proto_out)
+{
+ struct nvif_outp_inherit_v0 args;
+ int ret;
+
+ ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_RGB_CRT, &args, proto_out);
+ NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:RGB_CRT] ret:%d", ret);
+ return ret ?: args.head;
+}
+
int
nvif_outp_load_detect(struct nvif_outp *outp, u32 loadval)
{
@@ -102,8 +102,8 @@ nvkm_outp_release(struct nvkm_outp *outp, u8 user)
}
}
-static inline int
-nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
+int
+_nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
{
outp->ior = ior;
outp->ior->asy.outp = outp;
@@ -123,7 +123,7 @@ nvkm_outp_acquire_hda(struct nvkm_outp *outp, enum nvkm_ior_type type,
if (!ior->identity && ior->hda == hda &&
!ior->asy.outp && ior->type == type && !ior->arm.outp &&
(ior->func->route.set || ior->id == __ffs(outp->info.or)))
- return nvkm_outp_acquire_ior(outp, user, ior);
+ return _nvkm_outp_acquire_ior(outp, user, ior);
}
/* Last resort is to assign an OR that's already active on HW,
@@ -133,7 +133,7 @@ nvkm_outp_acquire_hda(struct nvkm_outp *outp, enum nvkm_ior_type type,
if (!ior->identity && ior->hda == hda &&
!ior->asy.outp && ior->type == type &&
(ior->func->route.set || ior->id == __ffs(outp->info.or)))
- return nvkm_outp_acquire_ior(outp, user, ior);
+ return _nvkm_outp_acquire_ior(outp, user, ior);
}
return -ENOSPC;
@@ -162,7 +162,7 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda)
ior = nvkm_ior_find(outp->disp, SOR, ffs(outp->info.or) - 1);
if (WARN_ON(!ior))
return -ENOSPC;
- return nvkm_outp_acquire_ior(outp, user, ior);
+ return _nvkm_outp_acquire_ior(outp, user, ior);
}
/* First preference is to reuse the OR that is currently armed
@@ -182,7 +182,7 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda)
* This warning is to make it obvious if that proves wrong.
*/
WARN_ON(hda && !ior->hda);
- return nvkm_outp_acquire_ior(outp, user, ior);
+ return _nvkm_outp_acquire_ior(outp, user, ior);
}
}
@@ -214,30 +214,30 @@ nvkm_outp_fini(struct nvkm_outp *outp)
outp->func->fini(outp);
}
-static void
-nvkm_outp_init_route(struct nvkm_outp *outp)
+struct nvkm_ior *
+nvkm_outp_get_current_ior(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 (WARN_ON(!ior))
- return;
+ 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. */
@@ -247,6 +247,22 @@ nvkm_outp_init_route(struct nvkm_outp *outp)
ior = nvkm_ior_find(disp, type, id);
if (WARN_ON(!ior))
+ return NULL;
+
+ return ior;
+}
+
+static void
+nvkm_outp_init_route(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 = nvkm_outp_get_current_ior(outp);
+ if (!ior)
return;
/* Determine if the OR is already configured for this device. */
@@ -74,8 +74,11 @@ void nvkm_outp_del(struct nvkm_outp **);
void nvkm_outp_init(struct nvkm_outp *);
void nvkm_outp_fini(struct nvkm_outp *);
int nvkm_outp_acquire(struct nvkm_outp *, u8 user, bool hda);
+int _nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior);
+
void nvkm_outp_release(struct nvkm_outp *, u8 user);
void nvkm_outp_route(struct nvkm_disp *);
+struct nvkm_ior *nvkm_outp_get_current_ior(struct nvkm_outp *outp);
struct nvkm_outp_func {
void *(*dtor)(struct nvkm_outp *);
@@ -252,6 +252,69 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
return 0;
}
+static int
+nvkm_uoutp_mthd_inherit(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ union nvif_outp_inherit_args *args = argv;
+ struct nvkm_ior *ior;
+ int ret = 0;
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+
+ /* Ensure an ior is hooked up to this outp already */
+ ior = nvkm_outp_get_current_ior(outp);
+ if (!ior)
+ return -ENODEV;
+
+ /* With iors, there will be a separate output path for each type of connector - and all of
+ * them will appear to be hooked up. Figure out which one is actually the one we're using
+ * based on the protocol we were given over nvif
+ */
+ switch (args->v0.proto) {
+ case NVIF_OUTP_INHERIT_V0_TMDS:
+ if (ior->arm.proto != TMDS)
+ return -ENODEV;
+ break;
+ case NVIF_OUTP_INHERIT_V0_DP:
+ if (ior->arm.proto != DP)
+ return -ENODEV;
+ break;
+ case NVIF_OUTP_INHERIT_V0_LVDS:
+ if (ior->arm.proto != LVDS)
+ return -ENODEV;
+ break;
+ case NVIF_OUTP_INHERIT_V0_TV:
+ if (ior->arm.proto != TV)
+ return -ENODEV;
+ break;
+ case NVIF_OUTP_INHERIT_V0_RGB_CRT:
+ if (ior->arm.proto != CRT)
+ return -ENODEV;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Make sure that userspace hasn't already acquired this */
+ if (outp->acquired) {
+ OUTP_ERR(outp, "cannot inherit an already acquired (%02x) outp", outp->acquired);
+ return -EBUSY;
+ }
+
+ /* Mark the outp acquired by userspace now that we've confirmed it's already active */
+ OUTP_TRACE(outp, "inherit %02x |= %02x %p", outp->acquired, NVKM_OUTP_USER, ior);
+ _nvkm_outp_acquire_ior(outp, NVKM_OUTP_USER, ior);
+
+ args->v0.or = ior->id;
+ args->v0.link = ior->arm.link;
+ args->v0.head = ffs(ior->arm.head) - 1;
+ args->v0.proto = ior->arm.proto_evo;
+
+ return ret;
+}
+
static int
nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)
{
@@ -297,6 +360,7 @@ nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc
switch (mthd) {
case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);
case NVIF_OUTP_V0_ACQUIRE : return nvkm_uoutp_mthd_acquire (outp, argv, argc);
+ case NVIF_OUTP_V0_INHERIT : return nvkm_uoutp_mthd_inherit (outp, argv, argc);
case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
default:
break;