diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 144 |
1 files changed, 91 insertions, 53 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index d23ca00e7d6..7ba28e08ee3 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -50,9 +50,53 @@ nv50_sor_nr(struct drm_device *dev) return 4; } +static int +evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) +{ + int ret = 0; + nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000001); + nv_wr32(dev, 0x610304 + (ch * 0x08), data); + nv_wr32(dev, 0x610300 + (ch * 0x08), 0x80000001 | mthd); + if (!nv_wait(dev, 0x610300 + (ch * 0x08), 0x80000000, 0x00000000)) + ret = -EBUSY; + if (ret || (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)) + NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data); + nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000000); + return ret; +} + int nv50_display_early_init(struct drm_device *dev) { + u32 ctrl = nv_rd32(dev, 0x610200); + int i; + + /* check if master evo channel is already active, a good a sign as any + * that the display engine is in a weird state (hibernate/kexec), if + * it is, do our best to reset the display engine... + */ + if ((ctrl & 0x00000003) == 0x00000003) { + NV_INFO(dev, "PDISP: EVO(0) 0x%08x, resetting...\n", ctrl); + + /* deactivate both heads first, PDISP will disappear forever + * (well, until you power cycle) on some boards as soon as + * PMC_ENABLE is hit unless they are.. + */ + for (i = 0; i < 2; i++) { + evo_icmd(dev, 0, 0x0880 + (i * 0x400), 0x05000000); + evo_icmd(dev, 0, 0x089c + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0840 + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0844 + (i * 0x400), 0); + evo_icmd(dev, 0, 0x085c + (i * 0x400), 0); + evo_icmd(dev, 0, 0x0874 + (i * 0x400), 0); + } + evo_icmd(dev, 0, 0x0080, 0); + + /* reset PDISP */ + nv_mask(dev, 0x000200, 0x40000000, 0x00000000); + nv_mask(dev, 0x000200, 0x40000000, 0x40000000); + } + return 0; } @@ -62,11 +106,40 @@ nv50_display_late_takedown(struct drm_device *dev) } int -nv50_display_init(struct drm_device *dev) +nv50_display_sync(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; - struct drm_connector *connector; + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + struct nv50_display *disp = nv50_display(dev); + struct nouveau_channel *evo = disp->master; + u64 start; + int ret; + + ret = RING_SPACE(evo, 6); + if (ret == 0) { + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x80000000); + BEGIN_RING(evo, 0, 0x0080, 1); + OUT_RING (evo, 0); + BEGIN_RING(evo, 0, 0x0084, 1); + OUT_RING (evo, 0x00000000); + + nv_wo32(disp->ntfy, 0x000, 0x00000000); + FIRE_RING (evo); + + start = ptimer->read(dev); + do { + if (nv_ro32(disp->ntfy, 0x000)) + return 0; + } while (ptimer->read(dev) - start < 2000000000ULL); + } + + return -EBUSY; +} + +int +nv50_display_init(struct drm_device *dev) +{ struct nouveau_channel *evo; int ret, i; u32 val; @@ -161,16 +234,6 @@ nv50_display_init(struct drm_device *dev) NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 | NV50_PDISPLAY_INTR_EN_1_CLK_UNK40); - /* enable hotplug interrupts */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct nouveau_connector *conn = nouveau_connector(connector); - - if (conn->dcb->gpio_tag == 0xff) - continue; - - pgpio->irq_enable(dev, conn->dcb->gpio_tag, true); - } - ret = nv50_evo_init(dev); if (ret) return ret; @@ -178,36 +241,19 @@ nv50_display_init(struct drm_device *dev) nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9); - ret = RING_SPACE(evo, 15); + ret = RING_SPACE(evo, 3); if (ret) return ret; BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); - OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED); - OUT_RING(evo, NvEvoSync); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1); - OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1); - OUT_RING(evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1); - OUT_RING(evo, 0); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1); - OUT_RING(evo, 0); - /* required to make display sync channels not hate life */ - BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK900), 1); - OUT_RING (evo, 0x00000311); - BEGIN_RING(evo, 0, NV50_EVO_CRTC(1, UNK900), 1); - OUT_RING (evo, 0x00000311); - FIRE_RING(evo); - if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2)) - NV_ERROR(dev, "evo pushbuf stalled\n"); - + OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED); + OUT_RING (evo, NvEvoSync); - return 0; + return nv50_display_sync(dev); } -static int nv50_display_disable(struct drm_device *dev) +void +nv50_display_fini(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); struct nouveau_channel *evo = disp->master; struct drm_crtc *drm_crtc; @@ -270,18 +316,10 @@ static int nv50_display_disable(struct drm_device *dev) /* disable interrupts. */ nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, 0x00000000); - - /* disable hotplug interrupts */ - nv_wr32(dev, 0xe054, 0xffffffff); - nv_wr32(dev, 0xe050, 0x00000000); - if (dev_priv->chipset >= 0x90) { - nv_wr32(dev, 0xe074, 0xffffffff); - nv_wr32(dev, 0xe070, 0x00000000); - } - return 0; } -int nv50_display_create(struct drm_device *dev) +int +nv50_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct dcb_table *dcb = &dev_priv->vbios.dcb; @@ -341,7 +379,7 @@ int nv50_display_create(struct drm_device *dev) tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); nouveau_irq_register(dev, 26, nv50_display_isr); - ret = nv50_display_init(dev); + ret = nv50_evo_create(dev); if (ret) { nv50_display_destroy(dev); return ret; @@ -357,7 +395,7 @@ nv50_display_destroy(struct drm_device *dev) NV_DEBUG_KMS(dev, "\n"); - nv50_display_disable(dev); + nv50_evo_destroy(dev); nouveau_irq_unregister(dev, 26); kfree(disp); } @@ -521,7 +559,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb, } else { /* determine number of lvds links */ if (nv_connector && nv_connector->edid && - nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) { + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { /* http://www.spwg.org */ if (((u8 *)nv_connector->edid)[121] == 2) script |= 0x0100; @@ -616,7 +654,7 @@ nv50_display_unk10_handler(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_display *disp = nv50_display(dev); u32 unk30 = nv_rd32(dev, 0x610030), mc; - int i, crtc, or, type = OUTPUT_ANY; + int i, crtc, or = 0, type = OUTPUT_ANY; NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); disp->irq.dcb = NULL; @@ -708,7 +746,7 @@ nv50_display_unk20_handler(struct drm_device *dev) struct nv50_display *disp = nv50_display(dev); u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc = 0; struct dcb_entry *dcb; - int i, crtc, or, type = OUTPUT_ANY; + int i, crtc, or = 0, type = OUTPUT_ANY; NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); dcb = disp->irq.dcb; @@ -722,8 +760,8 @@ nv50_display_unk20_handler(struct drm_device *dev) if (crtc >= 0) { pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)); pclk &= 0x003fffff; - - nv50_crtc_set_clock(dev, crtc, pclk); + if (pclk) + nv50_crtc_set_clock(dev, crtc, pclk); tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc)); tmp &= ~0x000000f; |