From eb2e9686d6bc94559dc7f25ed1e67ebbf0a9a41b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 24 Jan 2014 10:13:23 +1000 Subject: drm/nv50: fill in crtc mode struct members from crtc_mode_fixup The DRM uses the adjusted mode to calculate constants for vblank timestamping. Our encoder mode_fixup (usually) replaces this data with our backend mode information, which doesn't have the needed data filled in already. Reported-by: Mario Kleiner mario.kleiner.de@gmail.com Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_display.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 6ae7a697866e..2dccafc6e9db 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1035,6 +1035,7 @@ static bool nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); return true; } -- cgit v1.2.3 From d2fa7d32ea7b54d910637e50df4972e3540722d4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 14 Nov 2013 13:37:48 +1000 Subject: drm/nouveau/disp: add a method to fetch info needed by drm vblank timestamping Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/disp/nv04.c | 38 +++++++++++++++++- drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 30 +++++++++++++++ drivers/gpu/drm/nouveau/core/engine/disp/nv50.h | 7 +++- drivers/gpu/drm/nouveau/core/engine/disp/nv84.c | 1 + drivers/gpu/drm/nouveau/core/engine/disp/nv94.c | 1 + drivers/gpu/drm/nouveau/core/engine/disp/nva3.c | 3 +- drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c | 47 ++++++++++++++++++++++- drivers/gpu/drm/nouveau/core/engine/disp/nve0.c | 2 +- drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c | 2 +- drivers/gpu/drm/nouveau/core/include/core/class.h | 22 +++++++++++ 10 files changed, 146 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c index a0bc8a89b699..7cf8b1348632 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c @@ -31,9 +31,45 @@ struct nv04_disp_priv { struct nouveau_disp base; }; +static int +nv04_disp_scanoutpos(struct nouveau_object *object, u32 mthd, + void *data, u32 size) +{ + struct nv04_disp_priv *priv = (void *)object->engine; + struct nv04_display_scanoutpos *args = data; + const int head = (mthd & NV04_DISP_MTHD_HEAD); + u32 line; + + if (size < sizeof(*args)) + return -EINVAL; + + args->vblanks = nv_rd32(priv, 0x680800 + (head * 0x2000)) & 0xffff; + args->vtotal = nv_rd32(priv, 0x680804 + (head * 0x2000)) & 0xffff; + args->vblanke = args->vtotal - 1; + + args->hblanks = nv_rd32(priv, 0x680820 + (head * 0x2000)) & 0xffff; + args->htotal = nv_rd32(priv, 0x680824 + (head * 0x2000)) & 0xffff; + args->hblanke = args->htotal - 1; + + args->time[0] = ktime_to_ns(ktime_get()); + line = nv_rd32(priv, 0x600868 + (head * 0x2000)); + args->time[1] = ktime_to_ns(ktime_get()); + args->hline = (line & 0xffff0000) >> 16; + args->vline = (line & 0x0000ffff); + return 0; +} + +#define HEAD_MTHD(n) (n), (n) + 0x01 + +static struct nouveau_omthds +nv04_disp_omthds[] = { + { HEAD_MTHD(NV04_DISP_SCANOUTPOS), nv04_disp_scanoutpos }, + {} +}; + static struct nouveau_oclass nv04_disp_sclass[] = { - { NV04_DISP_CLASS, &nouveau_object_ofuncs }, + { NV04_DISP_CLASS, &nouveau_object_ofuncs, nv04_disp_omthds }, {}, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index c168ae3eaa97..940eaa5d8b9a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -541,6 +541,35 @@ nv50_disp_curs_ofuncs = { * Base display object ******************************************************************************/ +int +nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, + void *data, u32 size) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv04_display_scanoutpos *args = data; + const int head = (mthd & NV50_DISP_MTHD_HEAD); + u32 blanke, blanks, total; + + if (size < sizeof(*args) || head >= priv->head.nr) + return -EINVAL; + blanke = nv_rd32(priv, 0x610aec + (head * 0x540)); + blanks = nv_rd32(priv, 0x610af4 + (head * 0x540)); + total = nv_rd32(priv, 0x610afc + (head * 0x540)); + + args->vblanke = (blanke & 0xffff0000) >> 16; + args->hblanke = (blanke & 0x0000ffff); + args->vblanks = (blanks & 0xffff0000) >> 16; + args->hblanks = (blanks & 0x0000ffff); + args->vtotal = ( total & 0xffff0000) >> 16; + args->htotal = ( total & 0x0000ffff); + + args->time[0] = ktime_to_ns(ktime_get()); + args->vline = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff; + args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */ + args->hline = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff; + return 0; +} + static void nv50_disp_base_vblank_enable(struct nouveau_event *event, int head) { @@ -675,6 +704,7 @@ nv50_disp_base_ofuncs = { static struct nouveau_omthds nv50_disp_base_omthds[] = { + { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos }, { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index 1ae6ceb56704..d31d426ea1f6 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -43,6 +43,10 @@ struct nv50_disp_priv { } pior; }; +#define HEAD_MTHD(n) (n), (n) + 0x03 + +int nv50_disp_base_scanoutpos(struct nouveau_object *, u32, void *, u32); + #define DAC_MTHD(n) (n), (n) + 0x03 int nv50_dac_mthd(struct nouveau_object *, u32, void *, u32); @@ -132,13 +136,12 @@ void nv50_disp_intr(struct nouveau_subdev *); extern struct nouveau_omthds nv84_disp_base_omthds[]; -extern struct nouveau_omthds nva3_disp_base_omthds[]; - extern struct nouveau_ofuncs nvd0_disp_mast_ofuncs; extern struct nouveau_ofuncs nvd0_disp_sync_ofuncs; extern struct nouveau_ofuncs nvd0_disp_ovly_ofuncs; extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs; extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs; +extern struct nouveau_omthds nvd0_disp_base_omthds[]; extern struct nouveau_ofuncs nvd0_disp_base_ofuncs; extern struct nouveau_oclass nvd0_disp_cclass; void nvd0_disp_intr_supervisor(struct work_struct *); diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index d8c74c0883a1..ef9ce300a496 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -41,6 +41,7 @@ nv84_disp_sclass[] = { struct nouveau_omthds nv84_disp_base_omthds[] = { + { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos }, { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index a66f949c1f84..a518543c00ab 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -41,6 +41,7 @@ nv94_disp_sclass[] = { static struct nouveau_omthds nv94_disp_base_omthds[] = { + { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos }, { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index b75413169eae..6ad6dcece43b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -39,8 +39,9 @@ nva3_disp_sclass[] = { {} }; -struct nouveau_omthds +static struct nouveau_omthds nva3_disp_base_omthds[] = { + { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos }, { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index d52c0f50a1a9..1c5e4e8b2c82 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -440,6 +440,36 @@ nvd0_disp_curs_ofuncs = { * Base display object ******************************************************************************/ +static int +nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, + void *data, u32 size) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv04_display_scanoutpos *args = data; + const int head = (mthd & NV50_DISP_MTHD_HEAD); + u32 blanke, blanks, total; + + if (size < sizeof(*args) || head >= priv->head.nr) + return -EINVAL; + + total = nv_rd32(priv, 0x640414 + (head * 0x300)); + blanke = nv_rd32(priv, 0x64041c + (head * 0x300)); + blanks = nv_rd32(priv, 0x640420 + (head * 0x300)); + + args->vblanke = (blanke & 0xffff0000) >> 16; + args->hblanke = (blanke & 0x0000ffff); + args->vblanks = (blanks & 0xffff0000) >> 16; + args->hblanks = (blanks & 0x0000ffff); + args->vtotal = ( total & 0xffff0000) >> 16; + args->htotal = ( total & 0x0000ffff); + + args->time[0] = ktime_to_ns(ktime_get()); + args->vline = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff; + args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */ + args->hline = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff; + return 0; +} + static void nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head) { @@ -573,9 +603,24 @@ nvd0_disp_base_ofuncs = { .fini = nvd0_disp_base_fini, }; +struct nouveau_omthds +nvd0_disp_base_omthds[] = { + { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nvd0_disp_base_scanoutpos }, + { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, + { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, + { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd }, + {}, +}; + static struct nouveau_oclass nvd0_disp_base_oclass[] = { - { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nva3_disp_base_omthds }, + { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c index 20725b363d58..ab63f32c00b2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -41,7 +41,7 @@ nve0_disp_sclass[] = { static struct nouveau_oclass nve0_disp_base_oclass[] = { - { NVE0_DISP_CLASS, &nvd0_disp_base_ofuncs, nva3_disp_base_omthds }, + { NVE0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c index a488c36e40f9..05fee10e0c97 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c @@ -41,7 +41,7 @@ nvf0_disp_sclass[] = { static struct nouveau_oclass nvf0_disp_base_oclass[] = { - { NVF0_DISP_CLASS, &nvd0_disp_base_ofuncs, nva3_disp_base_omthds }, + { NVF0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h index 560c3593dae7..e71a4325e670 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/class.h +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -230,9 +230,26 @@ struct nve0_channel_ind_class { #define NV04_DISP_CLASS 0x00000046 +#define NV04_DISP_MTHD 0x00000000 +#define NV04_DISP_MTHD_HEAD 0x00000001 + +#define NV04_DISP_SCANOUTPOS 0x00000000 + struct nv04_display_class { }; +struct nv04_display_scanoutpos { + s64 time[2]; + u32 vblanks; + u32 vblanke; + u32 vtotal; + u32 vline; + u32 hblanks; + u32 hblanke; + u32 htotal; + u32 hline; +}; + /* 5070: NV50_DISP * 8270: NV84_DISP * 8370: NVA0_DISP @@ -252,6 +269,11 @@ struct nv04_display_class { #define NVE0_DISP_CLASS 0x00009170 #define NVF0_DISP_CLASS 0x00009270 +#define NV50_DISP_MTHD 0x00000000 +#define NV50_DISP_MTHD_HEAD 0x00000003 + +#define NV50_DISP_SCANOUTPOS 0x00000000 + #define NV50_DISP_SOR_MTHD 0x00010000 #define NV50_DISP_SOR_MTHD_TYPE 0x0000f000 #define NV50_DISP_SOR_MTHD_HEAD 0x00000018 -- cgit v1.2.3 From d83ef85395c9c1fae7636dca59f95c64963b307d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 14 Nov 2013 13:37:49 +1000 Subject: drm/nouveau: implement hooks for needed for drm vblank timestamping support Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 80 +++++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nouveau_display.h | 4 ++ drivers/gpu/drm/nouveau/nouveau_drm.c | 2 + 3 files changed, 86 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index b4262ad66b18..02a65259d8fb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -68,6 +68,86 @@ nouveau_display_vblank_disable(struct drm_device *dev, int head) nouveau_event_put(disp->vblank[head]); } +static inline int +calc(int blanks, int blanke, int total, int line) +{ + if (blanke >= blanks) { + if (line >= blanks) + line -= total; + } else { + if (line >= blanks) + line -= total; + line -= blanke + 1; + } + return line; +} + +int +nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime) +{ + const u32 mthd = NV04_DISP_SCANOUTPOS + nouveau_crtc(crtc)->index; + struct nouveau_display *disp = nouveau_display(crtc->dev); + struct nv04_display_scanoutpos args; + int ret, retry = 1; + + do { + ret = nv_exec(disp->core, mthd, &args, sizeof(args)); + if (ret != 0) + return 0; + + if (args.vline) { + ret |= DRM_SCANOUTPOS_ACCURATE; + ret |= DRM_SCANOUTPOS_VALID; + break; + } + + if (retry) ndelay(crtc->linedur_ns); + } while (retry--); + + *hpos = calc(args.hblanks, args.hblanke, args.htotal, args.hline); + *vpos = calc(args.vblanks, args.vblanke, args.vtotal, args.vline); + if (stime) *stime = ns_to_ktime(args.time[0]); + if (etime) *etime = ns_to_ktime(args.time[1]); + + if (*vpos < 0) + ret |= DRM_SCANOUTPOS_INVBL; + return ret; +} + +int +nouveau_display_scanoutpos(struct drm_device *dev, int head, unsigned int flags, + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(crtc)->index == head) { + return nouveau_display_scanoutpos_head(crtc, vpos, hpos, + stime, etime); + } + } + + return 0; +} + +int +nouveau_display_vblstamp(struct drm_device *dev, int head, int *max_error, + struct timeval *time, unsigned flags) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (nouveau_crtc(crtc)->index == head) { + return drm_calc_vbltimestamp_from_scanoutpos(dev, + head, max_error, time, flags, crtc, + &crtc->hwmode); + } + } + + return -EINVAL; +} + static void nouveau_display_vblank_fini(struct drm_device *dev) { diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 73aa231130b8..a71cf77e55b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -64,6 +64,10 @@ void nouveau_display_repin(struct drm_device *dev); void nouveau_display_resume(struct drm_device *dev); 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, + int *, int *, ktime_t *, ktime_t *); +int nouveau_display_vblstamp(struct drm_device *, int, int *, + struct timeval *, unsigned); int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 98a22e6e27a1..eecc6ca377c8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -798,6 +798,8 @@ driver = { .get_vblank_counter = drm_vblank_count, .enable_vblank = nouveau_display_vblank_enable, .disable_vblank = nouveau_display_vblank_disable, + .get_scanout_position = nouveau_display_scanoutpos, + .get_vblank_timestamp = nouveau_display_vblstamp, .ioctls = nouveau_ioctls, .num_ioctls = ARRAY_SIZE(nouveau_ioctls), -- cgit v1.2.3 From 09c3de135063f93d7137ad112f551f293b1204cf Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 29 Jan 2014 11:05:09 +0100 Subject: drm/nouveau: fix lock unbalance in nouveau_crtc_page_flip Fixes a regression introduced by d5c1e84b3a130f0 "drm/nouveau: hold mutex while syncing to kernel channel". Cc: stable@vger.kernel.org #3.13 Reported-by: Fengguang Wu Signed-off-by: Maarten Lankhorst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 02a65259d8fb..24011596af43 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -722,7 +722,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, ret = nouveau_fence_sync(fence, chan); nouveau_fence_unref(&fence); if (ret) - goto fail_free; + goto fail_unpin; ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL); if (ret) -- cgit v1.2.3 From f3980dc50c5117a952baee7135aae50d48304af3 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Thu, 23 Jan 2014 02:45:02 -0500 Subject: drm/nouveau: resume display if any later suspend bits fail If either idling channels or suspending the fence were to fail, the display would never be resumed. Also if a client fails, resume the fence (not functionally important, but it would potentially leak memory). See https://bugs.freedesktop.org/show_bug.cgi?id=70213 Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drm.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index eecc6ca377c8..78c8e7146d56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -503,19 +503,21 @@ nouveau_do_suspend(struct drm_device *dev) if (drm->cechan) { ret = nouveau_channel_idle(drm->cechan); if (ret) - return ret; + goto fail_display; } if (drm->channel) { ret = nouveau_channel_idle(drm->channel); if (ret) - return ret; + goto fail_display; } NV_INFO(drm, "suspending client object trees...\n"); if (drm->fence && nouveau_fence(drm)->suspend) { - if (!nouveau_fence(drm)->suspend(drm)) - return -ENOMEM; + if (!nouveau_fence(drm)->suspend(drm)) { + ret = -ENOMEM; + goto fail_display; + } } list_for_each_entry(cli, &drm->clients, head) { @@ -537,6 +539,10 @@ fail_client: nouveau_client_init(&cli->base); } + if (drm->fence && nouveau_fence(drm)->resume) + nouveau_fence(drm)->resume(drm); + +fail_display: if (dev->mode_config.num_crtc) { NV_INFO(drm, "resuming display...\n"); nouveau_display_resume(dev); -- cgit v1.2.3