Hi, Here is my patch queue I accumulated over quite a long time. Patches 1-6 are bugfixes, and rest is mostly RFC. Comments are welcome. Best regards, Maxim Levitsky
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 01/10] nv50: fix stability issue on NV86.
Confirmed to fix random hangs while running all Unegine demos on NV86. Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_grctx.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c index d05c2c3..4b46d69 100644 --- a/drivers/gpu/drm/nouveau/nv50_grctx.c +++ b/drivers/gpu/drm/nouveau/nv50_grctx.c @@ -601,7 +601,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) gr_def(ctx, offset + 0x1c, 0x00880000); break; case 0x86: - gr_def(ctx, offset + 0x1c, 0x008c0000); + gr_def(ctx, offset + 0x1c, 0x018c0000); break; case 0x92: case 0x96: -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 02/10] nv50: also report errors in MP1/MP2 when they happen.
Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_graph.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index 8c979b3..63e0ccd 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -616,9 +616,9 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old, } break; case 7: /* MP error */ - if (ustatus & 0x00010000) { + if (ustatus & 0x04030000) { nv50_pgraph_mp_trap(dev, i, display); - ustatus &= ~0x00010000; + ustatus &= ~0x04030000; } break; case 8: /* TPDMA error */ -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 03/10] nouveau: disable output polling through suspend.
Because doing polling while hardware is disabled is a bad idea... Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_drv.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 033cf3a..b404840 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -183,6 +183,8 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + NV_INFO(dev, "Disabling fbcon acceleration...\n"); nouveau_fbcon_save_disable_accel(dev); @@ -390,6 +392,7 @@ nouveau_pci_resume(struct pci_dev *pdev) drm_helper_resume_force_mode(dev); nouveau_fbcon_restore_accel(dev); + drm_kms_helper_poll_enable(dev); return 0; } -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 04/10] nouveau: hide cursor before suspending.
Otherwise PDISPLAY might try to access the unmapped and filled with garbage cursor on resume. Fixes external output not comping to life after resume. Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_drv.c | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index b404840..0d6a199 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -367,15 +367,6 @@ nouveau_pci_resume(struct pci_dev *pdev) engine->display.init(dev); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 offset = nv_crtc->cursor.nvbo->bo.offset; - - nv_crtc->cursor.set_offset(nv_crtc, offset); - nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, - nv_crtc->cursor_saved_y); - } - /* Force CLUT to get re-loaded during modeset */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); @@ -391,6 +382,15 @@ nouveau_pci_resume(struct pci_dev *pdev) drm_helper_resume_force_mode(dev); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 offset = nv_crtc->cursor.nvbo->bo.offset; + + nv_crtc->cursor.set_offset(nv_crtc, offset); + nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, + nv_crtc->cursor_saved_y); + } + nouveau_fbcon_restore_accel(dev); drm_kms_helper_poll_enable(dev); return 0; -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 05/10] nouveau: restore performance mode a bit later.
Otherwice code that responsible for idling the card can't work. BIOS init tables are supposed to init the clocks to correct values, so that shouldn't cause any problems (we don't reclock by default anyway) Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_drv.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 0d6a199..12ce793 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -303,8 +303,6 @@ nouveau_pci_resume(struct pci_dev *pdev) if (ret) return ret; - nouveau_pm_resume(dev); - if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { ret = nouveau_mem_init_agp(dev); if (ret) { @@ -344,6 +342,8 @@ nouveau_pci_resume(struct pci_dev *pdev) } } + nouveau_pm_resume(dev); + NV_INFO(dev, "Restoring mode...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; -- 1.7.4.1
Scaling code didn't took into account that doublescan modes actually are physically 2x verical resolution, thus scaler needs to scale logical resolution 2x Also remove stray OUT_RING that just by a chance didn't cause problems Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_crtc.c | 21 ++++++++++++++------- 1 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index 882080e..d1315ce 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -235,12 +235,14 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) if (ret) return ret; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + outY *= 2; + /* Got a better name for SCALER_ACTIVE? */ /* One day i've got to really figure out why this is needed. */ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); - if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) || - (mode->flags & DRM_MODE_FLAG_INTERLACE) || - mode->hdisplay != outX || mode->vdisplay != outY) { + if (mode->flags & DRM_MODE_FLAG_INTERLACE || + mode->hdisplay != outX || mode->vdisplay != outY) { OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE); } else { OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE); @@ -603,6 +605,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct nouveau_connector *nv_connector = NULL; uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; uint32_t hunk1, vunk1, vunk2a, vunk2b; + uint32_t vtotal, htotal; int ret; /* Find the connector attached to this CRTC */ @@ -626,6 +629,8 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, adjusted_mode->vsync_start + adjusted_mode->vdisplay; vunk2b = adjusted_mode->vtotal - adjusted_mode->vsync_start + adjusted_mode->vtotal; + vtotal = adjusted_mode->vtotal; + htotal = adjusted_mode->htotal; if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { vsync_dur /= 2; @@ -640,6 +645,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, vunk2a -= 1; vunk2b -= 1; } + } else if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { + vtotal *= 2; + vsync_dur *= 2; + vsync_start_to_end *= 2; + vunk1 *= 2; } ret = RING_SPACE(evo, 17); @@ -652,7 +662,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5); OUT_RING(evo, 0); - OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal); + OUT_RING(evo, (vtotal << 16) | htotal); OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1)); OUT_RING(evo, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1)); @@ -661,9 +671,6 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1); OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1)); - } else { - OUT_RING(evo, 0); - OUT_RING(evo, 0); } BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1); -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 07/10] nv50: cosmetic cleanup in modesetting code.
Since I figured out the logic behind hardware mode values, this renames variables adds comments to reflect this, etc Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_crtc.c | 78 ++++++++++++++++++++--------------- drivers/gpu/drm/nouveau/nv50_evo.h | 2 +- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index d1315ce..cb8943f 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -603,9 +603,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct nouveau_channel *evo = nv50_display(dev)->master; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_connector *nv_connector = NULL; - uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; - uint32_t hunk1, vunk1, vunk2a, vunk2b; + uint32_t hsync_dur, vsync_dur; + uint32_t hsync_to_hdisp_start, vsync_to_vdisp_start; + uint32_t hsync_to_hdisp_end, vsync_to_vdisp_end; uint32_t vtotal, htotal; + uint32_t vsync_to_vdisp_next_field_end = 0, vsync_to_vdisp_next_field_start = 0; int ret; /* Find the connector attached to this CRTC */ @@ -615,43 +617,54 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + /* from start of sync to end of sync (start of back porch)*/ hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start; vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start; - hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start; - vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start; - /* I can't give this a proper name, anyone else can? */ - hunk1 = adjusted_mode->htotal - - adjusted_mode->hsync_start + adjusted_mode->hdisplay; - vunk1 = adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vdisplay; - /* Another strange value, this time only for interlaced adjusted_modes. */ - vunk2a = 2 * adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vdisplay; - vunk2b = adjusted_mode->vtotal - - adjusted_mode->vsync_start + adjusted_mode->vtotal; + + /* from start of sync to start of visible portion (end of back porch) */ + hsync_to_hdisp_start = adjusted_mode->htotal - adjusted_mode->hsync_start; + vsync_to_vdisp_start = adjusted_mode->vtotal - adjusted_mode->vsync_start; + + /* from start of sync to end of visible portion (start of front porch) */ + hsync_to_hdisp_end = hsync_to_hdisp_start + adjusted_mode->hdisplay; + vsync_to_vdisp_end = vsync_to_vdisp_start + adjusted_mode->vdisplay; + + /* from start of sync to start of another sync (end of front porch)*/ vtotal = adjusted_mode->vtotal; htotal = adjusted_mode->htotal; if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { + + /* from vsync to start of visible portion of _next_ field */ + vsync_to_vdisp_next_field_start + vsync_to_vdisp_start + adjusted_mode->vtotal; + + /* from vsync to end of visible portion of _next_ field */ + vsync_to_vdisp_next_field_end + vsync_to_vdisp_next_field_start + adjusted_mode->vdisplay; + vsync_dur /= 2; - vsync_start_to_end /= 2; - vunk1 /= 2; - vunk2a /= 2; - vunk2b /= 2; - /* magic */ + vsync_to_vdisp_start /= 2; + vsync_to_vdisp_end /= 2; + vsync_to_vdisp_next_field_end /= 2; + vsync_to_vdisp_next_field_start /= 2; + + /* magic ??? */ if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { - vsync_start_to_end -= 1; - vunk1 -= 1; - vunk2a -= 1; - vunk2b -= 1; + vsync_to_vdisp_start -= 1; + vsync_to_vdisp_end -= 1; + vsync_to_vdisp_next_field_end -= 1; + vsync_to_vdisp_next_field_start -= 1; } + } else if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { vtotal *= 2; vsync_dur *= 2; - vsync_start_to_end *= 2; - vunk1 *= 2; + vsync_to_vdisp_start *= 2; + vsync_to_vdisp_end *= 2; } + ret = RING_SPACE(evo, 17); if (ret) return ret; @@ -660,18 +673,17 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, OUT_RING(evo, adjusted_mode->clock | 0x800000); OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5); + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), + (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 6 : 5); OUT_RING(evo, 0); OUT_RING(evo, (vtotal << 16) | htotal); OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1)); - OUT_RING(evo, (vsync_start_to_end - 1) << 16 | - (hsync_start_to_end - 1)); - OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1)); + OUT_RING(evo, (vsync_to_vdisp_start - 1) << 16 | (hsync_to_hdisp_start - 1)); + OUT_RING(evo, (vsync_to_vdisp_end - 1) << 16 | (hsync_to_hdisp_end - 1)); - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { - BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1); - OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1)); - } + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + OUT_RING(evo, (vsync_to_vdisp_next_field_start - 1) << 16 | + (vsync_to_vdisp_next_field_end - 1)); BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1); OUT_RING(evo, 0); diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h index 3860ca6..3131553 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.h +++ b/drivers/gpu/drm/nouveau/nv50_evo.h @@ -63,7 +63,7 @@ #define NV50_EVO_CRTC_DISPLAY_TOTAL 0x00000814 #define NV50_EVO_CRTC_SYNC_DURATION 0x00000818 #define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END 0x0000081c -#define NV50_EVO_CRTC_UNK0820 0x00000820 +#define NV50_EVO_CRTC_SYNC_START_TO_DISP_END 0x00000820 #define NV50_EVO_CRTC_UNK0824 0x00000824 #define NV50_EVO_CRTC_UNK082C 0x0000082c #define NV50_EVO_CRTC_CLUT_MODE 0x00000840 -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 08/10] drm: debugfs: remove debug directores using recursion.
Well, I implemented a custom debugfs file for nouveau, but found it hard to remove it cleanly due to constrains on drm debugfs api. I implemented a hack and then surprised that other drivers (intel and radeon) have exactly same ugly hack. So why? Just one liner does the job. This patch shouldn't break other driver, and with it, some code (the hacks) can be removed from them now. Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/drm_debugfs.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 9d2668a..4a859a9 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -228,7 +228,7 @@ int drm_debugfs_cleanup(struct drm_minor *minor) drm_debugfs_remove_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, minor); - debugfs_remove(minor->debugfs_root); + debugfs_remove_recursive(minor->debugfs_root); minor->debugfs_root = NULL; return 0; -- 1.7.4.1
This code adds new debugfs file that allows to send evo commands from userspace. Its usefull for RE. Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_debugfs.c | 101 +++++++++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_crtc.c | 2 +- drivers/gpu/drm/nouveau/nv50_display.h | 1 + 3 files changed, 103 insertions(+), 1 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index 8e15923..c79bf56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -30,11 +30,16 @@ #include <linux/debugfs.h> +#define NOUVEAU_DMA_DEBUG 1 #include "drmP.h" #include "nouveau_drv.h" +#include "nouveau_dma.h" +#include "nv50_display.h" +#include "nouveau_crtc.h" #include <ttm/ttm_page_alloc.h> + static int nouveau_debugfs_channel_info(struct seq_file *m, void *data) { @@ -181,11 +186,107 @@ static struct drm_info_list nouveau_debugfs_list[] = { }; #define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list) +static int nouveau_evo_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int nouveau_evo_debugfs_release (struct inode *inode, struct file *file) +{ + return 0; +} + +int +nv50_crtc_wait_complete(struct drm_crtc *crtc); + + +static ssize_t nouveau_evo_debugfs_write(struct file *file, const char __user * user_buf, + size_t count, loff_t *ppos) +{ + struct drm_minor *minor = (struct drm_minor *)file->private_data; + struct drm_device* dev = minor->dev; + struct nouveau_channel *evo; + struct drm_crtc *crtc; + struct nv50_display *display; + u32 method, value; + unsigned char *cmd; + int ret; + + if (count > 100 || *ppos) + return -EINVAL; + + cmd = kmalloc(count + 1, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + if (copy_from_user(cmd, user_buf, count)) { + kfree(cmd); + return -EINVAL; + } + + cmd[count] = '\0'; + + mutex_lock(&dev->mode_config.mutex); + + display = nv50_display(dev); + evo = display->master; + + if (sscanf(cmd, "%x %x", &method, &value) == 2) { + + ret = RING_SPACE(evo, 2); + if (ret) + goto out; + + BEGIN_RING(evo, 0, method, 1); + OUT_RING(evo, value); + FIRE_RING(evo); + + } else if (!strncmp(cmd, "flush", 5)) { + + ret = RING_SPACE(evo, 6); + if (ret) + goto out; + + BEGIN_RING(evo, 0, 0x84, 1); + OUT_RING(evo, 0x80000004); + BEGIN_RING(evo, 0, 0x84, 1); + OUT_RING(evo, 0x80000008); + BEGIN_RING(evo, 0, 0x80, 1); + OUT_RING(evo, 0x00000000); + FIRE_RING(evo); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb) { + nv50_display_flip_stop(crtc); + nv50_crtc_wait_complete(crtc); + nv50_display_flip_next(crtc, crtc->fb, NULL); + } + } + } else + NV_ERROR(dev, "evodbg: malformated input %s\n", cmd); + + ret = count; +out: + kfree(cmd); + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +static const struct file_operations evo_debugfs_ops = { + .open = nouveau_evo_debugfs_open, + .release = nouveau_evo_debugfs_release, + .write = nouveau_evo_debugfs_write, + .llseek = default_llseek, +}; + int nouveau_debugfs_init(struct drm_minor *minor) { drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, minor->debugfs_root, minor); + debugfs_create_file("evo", + S_IWUSR, minor->debugfs_root, minor, &evo_debugfs_ops); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c index cb8943f..4b2d9ec 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c @@ -443,7 +443,7 @@ nv50_crtc_dpms(struct drm_crtc *crtc, int mode) { } -static int +int nv50_crtc_wait_complete(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index c2da503..44c0157 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -72,6 +72,7 @@ int nv50_display_init(struct drm_device *dev); void nv50_display_destroy(struct drm_device *dev); int nv50_crtc_blank(struct nouveau_crtc *, bool blank); int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); +int nv50_crtc_wait_complete(struct drm_crtc *crtc); int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *chan); -- 1.7.4.1
Maxim Levitsky
2011-Oct-09 20:58 UTC
[Nouveau] [PATCH 10/10] nv50: WIP: initial import of power save magic.
NOT-signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_dac.c | 47 +++++++++++++++++++++++++++----- drivers/gpu/drm/nouveau/nv50_display.c | 2 +- drivers/gpu/drm/nouveau/nv50_fb.c | 9 ++++++ drivers/gpu/drm/nouveau/nv50_mc.c | 11 +++++++ 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c index 808f3ec..8b5a40d 100644 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ b/drivers/gpu/drm/nouveau/nv50_dac.c @@ -70,10 +70,12 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) struct drm_device *dev = encoder->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; enum drm_connector_status status = connector_status_disconnected; - uint32_t dpms_state, load_pattern, load_state; + uint32_t dpms_state, load_pattern, load_state, clk; int or = nv_encoder->or; - nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001); + clk = nv_rd32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or)); + nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x01); + dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)); nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), @@ -115,6 +117,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) else NV_DEBUG_KMS(dev, "Load was not detected on output with or %d\n", or); + nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), clk); + return status; } @@ -123,7 +127,7 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t val; + uint32_t val, clk; int or = nv_encoder->or; NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode); @@ -138,9 +142,13 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode) } val = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F; + clk = nv_rd32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or)); - if (mode != DRM_MODE_DPMS_ON) + if (mode != DRM_MODE_DPMS_ON) { val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED; + clk |= 0x02; + } else + clk &= ~0x02; switch (mode) { case DRM_MODE_DPMS_STANDBY: @@ -160,6 +168,9 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode) nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); + + nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), clk); + } static void @@ -297,6 +308,29 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_entry *entry) { struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + int type, or; + + switch (entry->type) { + case OUTPUT_ANALOG: + type = DRM_MODE_ENCODER_DAC; + break; + case OUTPUT_TV: + type = DRM_MODE_ENCODER_TVDAC; + break; + default: + return -EINVAL; + } + + or = ffs(entry->or) - 1; + + if (type == DRM_MODE_ENCODER_DAC) + nv_wr32(connector->dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000003); + else + nv_wr32(connector->dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x80000002); + + /* FIXME: add support for TV-out */ + if (type != DRM_MODE_ENCODER_DAC) + return -EINVAL; nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); if (!nv_encoder) @@ -304,10 +338,9 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_entry *entry) encoder = to_drm_encoder(nv_encoder); nv_encoder->dcb = entry; - nv_encoder->or = ffs(entry->or) - 1; + nv_encoder->or = or; - drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs, - DRM_MODE_ENCODER_DAC); + drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs, type); drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs); encoder->possible_crtcs = entry->heads; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index d23ca00..d9dac41 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -112,7 +112,6 @@ nv50_display_init(struct drm_device *dev) for (i = 0; i < 3; i++) { nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); - nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001); } /* The precise purpose is unknown, i suspect it has something to do @@ -321,6 +320,7 @@ int nv50_display_create(struct drm_device *dev) nv50_sor_create(connector, entry); break; case OUTPUT_ANALOG: + case OUTPUT_TV: nv50_dac_create(connector, entry); break; default: diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c index bdd2afe..fe49500 100644 --- a/drivers/gpu/drm/nouveau/nv50_fb.c +++ b/drivers/gpu/drm/nouveau/nv50_fb.c @@ -86,6 +86,15 @@ nv50_fb_init(struct drm_device *dev) */ nv_wr32(dev, 0x100c08, priv->r100c08 >> 8); + /* Power usage magic */ + nv_wr32(dev, 0x100000, 0x0000c042); + nv_wr32(dev, 0x100004, 0x0000c042); + nv_wr32(dev, 0x100008, 0x0000c042); + nv_wr32(dev, 0x100b78, 0x0000c042); + nv_wr32(dev, 0x100c0c, 0x0000c042); + nv_wr32(dev, 0x100d04, 0x0000c042); + nv_wr32(dev, 0x100e0c, 0x0000c042); + /* This is needed to get meaningful information from 100c90 * on traps. No idea what these values mean exactly. */ switch (dev_priv->chipset) { diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c index e0a9c3f..76367568 100644 --- a/drivers/gpu/drm/nouveau/nv50_mc.c +++ b/drivers/gpu/drm/nouveau/nv50_mc.c @@ -32,6 +32,17 @@ int nv50_mc_init(struct drm_device *dev) { nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF); + + /* PPCI+0x150 - some PCIE powersave bit*/ + nv_mask(dev, 0x088150, 0x00000100, 0x00000000); + + /* PBUS.DEBUG_6 - enable PGRAPH automatic clock gating*/ + nv_mask(dev, 0x001098, 0x00000020, 0x00000020); + nv_mask(dev, 0x001588, 0x00000003, 0x00000001); + + /* PCONTROL+0x40 - disable VP2 clock */ + nv_mask(dev, 0x00c040, 0x00000F00, 0x00000200); + return 0; } -- 1.7.4.1
On Sun, 2011-10-09 at 22:58 +0200, Maxim Levitsky wrote:> Hi,Hey, I pushed patches 1-5, fixing up the log messages to be "drm/nouveau" instead of just "nouveau". Patch 6: needs a rebase on top of nouveau git, it conflicts with the overscan compensation patches I pushed a short while ago. Patch 7: not 100% set on this idea, but, maybe use the same variable names as nvd0_display.c does? They're much shorter :) Patch 8: i'd send this one to dri-devel, so it gets review from all the driver maintainers Patch 9: i pushed a tool to envytools which'll get you the same functionality. Thanks! Ben.> > Here is my patch queue I accumulated over quite a long time. > Patches 1-6 are bugfixes, and rest is mostly RFC. > > Comments are welcome. > > Best regards, > Maxim Levitsky >
Maybe Matching Threads
- 2 remaining patches in my patch queue that can be merged
- [NOT for merge] Patches that reduce power usage on NV86
- [Bug 64904] New: [BISECTED] nouveau, nv50: External VGA not detected anymore
- [PATCH] drm/nouveau: use drm debug levels
- [PATCH 1/2] nv04-nv40/crtc: Don't perform the LUT expansion twice.