Should fix some initial modeset failures on (at least) Ampere boards.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
                interlock[NV50_DISP_INTERLOCK_CORE] = 0;
        }
 
+       /* Finish updating head(s)...
+        *
+        * NVD is rather picky about both where window assignments can change,
+        * *and* about certain core and window channel states matching.
+        *
+        * The EFI GOP driver on newer GPUs configures window channels with a
+        * different output format to what we do, and the core channel update
+        * in the assign_windows case above would result in a state mismatch.
+        *
+        * Delay some of the head update until after that point to workaround
+        * the issue.  This only affects the initial modeset.
+        *
+        * TODO: handle this better when adding flexible window mapping
+        */
+       for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+               struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state);
+               struct nv50_head *head = nv50_head(crtc);
+
+               NV_ATOMIC(drm, "%s: set %04x (clr %04x)\n", crtc->name,
+                         asyh->set.mask, asyh->clr.mask);
+
+               if (asyh->set.mask) {
+                       nv50_head_flush_set_wndw(head, asyh);
+                       interlock[NV50_DISP_INTERLOCK_CORE] = 1;
+               }
+       }
+
        /* Update plane(s). */
        for_each_new_plane_in_state(state, plane, new_plane_state, i) {
                struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state);
 
 }
 
 void
-nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+nv50_head_flush_set_wndw(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
-       if (asyh->set.view   ) head->func->view    (head, asyh);
-       if (asyh->set.mode   ) head->func->mode    (head, asyh);
-       if (asyh->set.core   ) head->func->core_set(head, asyh);
        if (asyh->set.olut   ) {
                asyh->olut.offset = nv50_lut_load(&head->olut,
                                                  asyh->olut.buffer,
                                                  asyh->olut.load);
                head->func->olut_set(head, asyh);
        }
+}
+
+void
+nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       if (asyh->set.view   ) head->func->view    (head, asyh);
+       if (asyh->set.mode   ) head->func->mode    (head, asyh);
+       if (asyh->set.core   ) head->func->core_set(head, asyh);
        if (asyh->set.curs   ) head->func->curs_set(head, asyh);
        if (asyh->set.base   ) head->func->base    (head, asyh);
        if (asyh->set.ovly   ) head->func->ovly    (head, asyh);
 
 
 struct nv50_head *nv50_head_create(struct drm_device *, int index);
 void nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh);
+void nv50_head_flush_set_wndw(struct nv50_head *head, struct nv50_head_atom *asyh);
 void nv50_head_flush_clr(struct nv50_head *head,
                         struct nv50_head_atom *asyh, bool flush);