From 5838ae610ff36777b8fce6f353c2417980c1a1fa Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 30 Sep 2014 10:27:08 +1000 Subject: drm/nv50/disp: fix dpms regression on certain boards Reported in fdo#82527 comment #2. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 4b5bb5d58a54..f8cbb512132f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -1763,9 +1763,10 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp const int or = ffs(outp->or) - 1; const u32 loff = (or * 0x800) + (link * 0x80); const u16 mask = (outp->sorconf.link << 6) | outp->or; + struct dcb_output match; u8 ver, hdr; - if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp)) + if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match)) nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000); } -- cgit v1.2.3 From f2f9a2cbaf019481feefe231f996d3602612fa99 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 1 Oct 2014 12:46:14 +1000 Subject: drm/nouveau: fix regression on original nv50 board Xorg (and any non-DRM client really) doesn't have permission to directly touch VRAM on nv50 and up, which the fence code prior to g84 depends on. It's less invasive to temporarily grant it premission to do so, as it previously did, than it is to rework fencenv50 to use the VM. That will come later on. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_chan.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 99cd9e4a2aa6..3440fc999f2f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -285,6 +285,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) struct nouveau_software_chan *swch; struct nv_dma_v0 args = {}; int ret, i; + bool save; nvif_object_map(chan->object); @@ -386,7 +387,11 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) } /* initialise synchronisation */ - return nouveau_fence(chan->drm)->context_new(chan); + save = cli->base.super; + cli->base.super = true; /* hack until fencenv50 fixed */ + ret = nouveau_fence(chan->drm)->context_new(chan); + cli->base.super = save; + return ret; } int -- cgit v1.2.3 From 634ffcccfbe59d77652804e1beb415d3329b1bc6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 1 Oct 2014 11:11:25 +1000 Subject: drm/nouveau: punt fbcon resume out to a workqueue Preparation for some runtime pm fixes. Currently we skip over fbcon suspend/resume in the runtime path, which causes issues on resume if fbcon tries to write to the framebuffer before the BAR subdev has been resumed to restore the BAR1 VM setup. As we might be woken up via a sysfs connector, we are unable to call fb_set_suspend() in the resume path as it could make its way down to a modeset and cause all sorts of locking hilarity. To solve this, we'll just delay the fbcon resume to a workqueue. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 23 +++++++++++++++++------ drivers/gpu/drm/nouveau/nouveau_fbcon.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 8bdd27091db8..49fe6075cc7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -486,6 +486,16 @@ static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .fb_probe = nouveau_fbcon_create, }; +static void +nouveau_fbcon_set_suspend_work(struct work_struct *work) +{ + struct nouveau_fbdev *fbcon = container_of(work, typeof(*fbcon), work); + console_lock(); + nouveau_fbcon_accel_restore(fbcon->dev); + nouveau_fbcon_zfill(fbcon->dev, fbcon); + fb_set_suspend(fbcon->helper.fbdev, FBINFO_STATE_RUNNING); + console_unlock(); +} int nouveau_fbcon_init(struct drm_device *dev) @@ -503,6 +513,7 @@ nouveau_fbcon_init(struct drm_device *dev) if (!fbcon) return -ENOMEM; + INIT_WORK(&fbcon->work, nouveau_fbcon_set_suspend_work); fbcon->dev = dev; drm->fbcon = fbcon; @@ -551,14 +562,14 @@ nouveau_fbcon_set_suspend(struct drm_device *dev, int state) { struct nouveau_drm *drm = nouveau_drm(dev); if (drm->fbcon) { - console_lock(); - if (state == 0) { - nouveau_fbcon_accel_restore(dev); - nouveau_fbcon_zfill(dev, drm->fbcon); + if (state == FBINFO_STATE_RUNNING) { + schedule_work(&drm->fbcon->work); + return; } + flush_work(&drm->fbcon->work); + console_lock(); fb_set_suspend(drm->fbcon->helper.fbdev, state); - if (state == 1) - nouveau_fbcon_accel_save_disable(dev); + nouveau_fbcon_accel_save_disable(dev); console_unlock(); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index 34658cfa8f5d..0b465c7d3907 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -36,6 +36,7 @@ struct nouveau_fbdev { struct nouveau_framebuffer nouveau_fb; struct list_head fbdev_list; struct drm_device *dev; + struct work_struct work; unsigned int saved_flags; struct nvif_object surf2d; struct nvif_object clip; -- cgit v1.2.3 From 6fbb702e27d78ad2458df048b58cca3454bc0965 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 2 Oct 2014 13:22:27 +1000 Subject: drm/nouveau: make sure display hardware is reinitialised on runtime resume Linus commit 05c63c2ff23a80b654d6c088ac3ba21628db0173 modified the runtime suspend/resume paths to skip over display-related tasks to avoid locking issues on resume. Unfortunately, this resulted in the display hardware being left in a partially initialised state, preventing subsequent modesets from completing. This commit unifies the (many) suspend/resume paths, bringing back display (and fbcon) handling in the runtime paths. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 23 +++++++------- drivers/gpu/drm/nouveau/nouveau_display.h | 5 ++- drivers/gpu/drm/nouveau/nouveau_drm.c | 51 ++++++++----------------------- 3 files changed, 26 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 65b4fd53dd4e..4a21b2b06ce2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -550,14 +550,12 @@ nouveau_display_destroy(struct drm_device *dev) } int -nouveau_display_suspend(struct drm_device *dev) +nouveau_display_suspend(struct drm_device *dev, bool runtime) { - struct nouveau_drm *drm = nouveau_drm(dev); struct drm_crtc *crtc; nouveau_display_fini(dev); - NV_INFO(drm, "unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -579,12 +577,13 @@ nouveau_display_suspend(struct drm_device *dev) } void -nouveau_display_repin(struct drm_device *dev) +nouveau_display_resume(struct drm_device *dev, bool runtime) { struct nouveau_drm *drm = nouveau_drm(dev); struct drm_crtc *crtc; - int ret; + int ret, head; + /* re-pin fb/cursors */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -606,13 +605,6 @@ nouveau_display_repin(struct drm_device *dev) if (ret) NV_ERROR(drm, "Could not pin/map cursor.\n"); } -} - -void -nouveau_display_resume(struct drm_device *dev) -{ - struct drm_crtc *crtc; - int head; nouveau_display_init(dev); @@ -627,6 +619,13 @@ nouveau_display_resume(struct drm_device *dev) for (head = 0; head < dev->mode_config.num_crtc; head++) drm_vblank_on(dev, head); + /* This should ensure we don't hit a locking problem when someone + * wakes us up via a connector. We should never go into suspend + * while the display is on anyways. + */ + if (runtime) + return; + drm_helper_resume_force_mode(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 88ca177cb1c7..be3d5947c6be 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -63,9 +63,8 @@ int nouveau_display_create(struct drm_device *dev); void nouveau_display_destroy(struct drm_device *dev); int nouveau_display_init(struct drm_device *dev); void nouveau_display_fini(struct drm_device *dev); -int nouveau_display_suspend(struct drm_device *dev); -void nouveau_display_repin(struct drm_device *dev); -void nouveau_display_resume(struct drm_device *dev); +int nouveau_display_suspend(struct drm_device *dev, bool runtime); +void nouveau_display_resume(struct drm_device *dev, bool runtime); int nouveau_display_vblank_enable(struct drm_device *, int); void nouveau_display_vblank_disable(struct drm_device *, int); int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int, diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 9c3af96a7153..3ed32dd90303 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -547,9 +547,11 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime) struct nouveau_cli *cli; int ret; - if (dev->mode_config.num_crtc && !runtime) { + if (dev->mode_config.num_crtc) { + NV_INFO(drm, "suspending console...\n"); + nouveau_fbcon_set_suspend(dev, 1); NV_INFO(drm, "suspending display...\n"); - ret = nouveau_display_suspend(dev); + ret = nouveau_display_suspend(dev, runtime); if (ret) return ret; } @@ -603,7 +605,7 @@ fail_client: fail_display: if (dev->mode_config.num_crtc) { NV_INFO(drm, "resuming display...\n"); - nouveau_display_resume(dev); + nouveau_display_resume(dev, runtime); } return ret; } @@ -618,9 +620,6 @@ int nouveau_pmops_suspend(struct device *dev) drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) return 0; - if (drm_dev->mode_config.num_crtc) - nouveau_fbcon_set_suspend(drm_dev, 1); - ret = nouveau_do_suspend(drm_dev, false); if (ret) return ret; @@ -633,7 +632,7 @@ int nouveau_pmops_suspend(struct device *dev) } static int -nouveau_do_resume(struct drm_device *dev) +nouveau_do_resume(struct drm_device *dev, bool runtime) { struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_cli *cli; @@ -658,7 +657,9 @@ nouveau_do_resume(struct drm_device *dev) if (dev->mode_config.num_crtc) { NV_INFO(drm, "resuming display...\n"); - nouveau_display_repin(dev); + nouveau_display_resume(dev, runtime); + NV_INFO(drm, "resuming console...\n"); + nouveau_fbcon_set_suspend(dev, 0); } return 0; @@ -681,47 +682,21 @@ int nouveau_pmops_resume(struct device *dev) return ret; pci_set_master(pdev); - ret = nouveau_do_resume(drm_dev); - if (ret) - return ret; - - if (drm_dev->mode_config.num_crtc) { - nouveau_display_resume(drm_dev); - nouveau_fbcon_set_suspend(drm_dev, 0); - } - - return 0; + return nouveau_do_resume(drm_dev, false); } static int nouveau_pmops_freeze(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); - int ret; - - if (drm_dev->mode_config.num_crtc) - nouveau_fbcon_set_suspend(drm_dev, 1); - - ret = nouveau_do_suspend(drm_dev, false); - return ret; + return nouveau_do_suspend(drm_dev, false); } static int nouveau_pmops_thaw(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); - int ret; - - ret = nouveau_do_resume(drm_dev); - if (ret) - return ret; - - if (drm_dev->mode_config.num_crtc) { - nouveau_display_resume(drm_dev); - nouveau_fbcon_set_suspend(drm_dev, 0); - } - - return 0; + return nouveau_do_resume(drm_dev, false); } @@ -977,7 +952,7 @@ static int nouveau_pmops_runtime_resume(struct device *dev) return ret; pci_set_master(pdev); - ret = nouveau_do_resume(drm_dev); + ret = nouveau_do_resume(drm_dev, true); drm_kms_helper_poll_enable(drm_dev); /* do magic */ nvif_mask(device, 0x88488, (1 << 25), (1 << 25)); -- cgit v1.2.3