summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/nv50_display.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 11:04:36 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 11:04:36 -0800
commit1a464cbb3d483f2f195b614cffa4aa1b910a0440 (patch)
treeaf57dee6436532dbb546b8670e9e1f6910d489b5 /drivers/gpu/drm/nouveau/nv50_display.c
parentdbe950f201a8edd353b0bd9079e8d536ee4ce37c (diff)
parent095f979a539245a46b9e5d600ec9c720b4d928e5 (diff)
Merge branch 'drm-core-next' of git://people.freedesktop.org/~airlied/linux
* 'drm-core-next' of git://people.freedesktop.org/~airlied/linux: (307 commits) drm/nouveau/pm: fix build with HWMON off gma500: silence gcc warnings in mid_get_vbt_data() drm/ttm: fix condition (and vs or) drm/radeon: double lock typo in radeon_vm_bo_rmv() drm/radeon: use after free in radeon_vm_bo_add() drm/sis|via: don't return stack garbage from free_mem ioctl drm/radeon/kms: remove pointless CS flags priority struct drm/radeon/kms: check if vm is supported in VA ioctl drm: introduce drm_can_sleep and use in intel/radeon drivers. (v2) radeon: Fix disabling PCI bus mastering on big endian hosts. ttm: fix agp since ttm tt rework agp: Fix multi-line warning message whitespace drm/ttm/dma: Fix accounting error when calling ttm_mem_global_free_page and don't try to free freed pages. drm/ttm/dma: Only call set_pages_array_wb when the page is not in WB pool. drm/radeon/kms: sync across multiple rings when doing bo moves v3 drm/radeon/kms: Add support for multi-ring sync in CS ioctl (v2) drm/radeon: GPU virtual memory support v22 drm: make DRM_UNLOCKED ioctls with their own mutex drm: no need to hold global mutex for static data drm/radeon/benchmark: common modes sweep ignores 640x480@32 ... Fix up trivial conflicts in radeon/evergreen.c and vmwgfx/vmwgfx_kms.c
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_display.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c140
1 files changed, 89 insertions, 51 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 06de250fe617..7ba28e08ee31 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;
@@ -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;