summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_display_engine.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gsp_display_engine.c')
-rw-r--r--kernel/gsp_display_engine.c37
1 files changed, 32 insertions, 5 deletions
diff --git a/kernel/gsp_display_engine.c b/kernel/gsp_display_engine.c
index a6dd11c..c97458c 100644
--- a/kernel/gsp_display_engine.c
+++ b/kernel/gsp_display_engine.c
@@ -144,6 +144,7 @@ struct gsp_disp_chan {
unsigned long event_retry_at; /* next forced re-post attempt */
u32 event_drop_logs;
u32 update_logs;
+ u32 ctrl_logs;
/* Stats. */
u64 updates;
@@ -1014,6 +1015,29 @@ void gsp_disp_engine_channel_add(struct nv_vgpu_device *vgpu, u32 hclass,
* else is state the engine would apply to the display pipeline (head/window/
* cursor registers) which we deliberately do not render.
*/
+/*
+ * Is this UPDATE's notifier armed?
+ *
+ * NVC57D (core) SET_NOTIFIER_CONTROL carries a NOTIFY bit (12:12) and the
+ * class disables the notifier with ctxDMA 0 + control 0.
+ *
+ * NVC57E (window) SET_NOTIFIER_CONTROL has only MODE (0:0) and OFFSET
+ * (11:4); there is no NOTIFY bit. The bound notifier ctxDMA *is* the
+ * enable, and the class disables with ctxDMA 0 + control 0. Treating
+ * bit 12 as the arm bit made every window flip compute armed=0, so the
+ * emulation never wrote FINISHED and never posted the channel event for
+ * the X server's window channel (0xc57e). The DDX's composite/flip
+ * waits then sat out their timeout (Xorg log "Wait for channel idle
+ * timed out") and the display pipeline stalled.
+ */
+static bool gsp_disp_update_armed(const struct gsp_disp_chan *ch)
+{
+ if (ch->hclass == GSP_DISP_CLASS_CORE)
+ return (ch->notifier_ctrl &
+ GSP_DISP_NOTIFIER_CTRL_NOTIFY) != 0;
+ return ch->notifier_handle != 0 && ch->notifier_pa_valid;
+}
+
static void gsp_disp_method(struct nv_vgpu_device *vgpu,
struct gsp_disp_chan *ch, u32 addr, u32 value,
u32 push_off)
@@ -1048,11 +1072,15 @@ static void gsp_disp_method(struct nv_vgpu_device *vgpu,
}
if (addr == notifier_ctrl) {
ch->notifier_ctrl = value;
- if (gsp_disp_dbg)
- pr_info("h=0x%x push=0x%x SET_NOTIFIER_CTRL=0x%x (notify=%u off=%u)\n",
+ if (gsp_disp_dbg || ch->ctrl_logs < 8) {
+ ch->ctrl_logs++;
+ pr_info("h=0x%x push=0x%x SET_NOTIFIER_CTRL=0x%x (notify=%u off=%u armed=%d handle=0x%x pa_valid=%d)\n",
ch->hclass, push_off, value,
!!(value & GSP_DISP_NOTIFIER_CTRL_NOTIFY),
- GSP_DISP_NOTIFIER_CTRL_OFFSET(value));
+ GSP_DISP_NOTIFIER_CTRL_OFFSET(value),
+ gsp_disp_update_armed(ch),
+ ch->notifier_handle, ch->notifier_pa_valid);
+ }
return;
}
if (addr == GSP_DISP_M_UPDATE) {
@@ -1060,8 +1088,7 @@ static void gsp_disp_method(struct nv_vgpu_device *vgpu,
u64 pa = ch->notifier_pa + off_bytes;
u32 target = ch->notifier_target;
u32 finished = GSP_DISP_NOTIFIER_FINISHED;
- bool armed = (ch->notifier_ctrl &
- GSP_DISP_NOTIFIER_CTRL_NOTIFY) != 0;
+ bool armed = gsp_disp_update_armed(ch);
bool wrote = false;
ch->updates++;