summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv50_fb.c
blob: 50290dea0ac4ab0a89c9807e2132073e5c7db640 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"

struct nv50_fb_priv {
	struct page *r100c08_page;
	dma_addr_t r100c08;
};

static int
nv50_fb_create(struct drm_device *dev)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nv50_fb_priv *priv;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
	if (!priv->r100c08_page) {
		kfree(priv);
		return -ENOMEM;
	}

	priv->r100c08 = pci_map_page(dev->pdev, priv->r100c08_page, 0,
				     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
	if (pci_dma_mapping_error(dev->pdev, priv->r100c08)) {
		__free_page(priv->r100c08_page);
		kfree(priv);
		return -EFAULT;
	}

	dev_priv->engine.fb.priv = priv;
	return 0;
}

int
nv50_fb_init(struct drm_device *dev)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nv50_fb_priv *priv;
	int ret;

	if (!dev_priv->engine.fb.priv) {
		ret = nv50_fb_create(dev);
		if (ret)
			return ret;
	}
	priv = dev_priv->engine.fb.priv;

	/* Not a clue what this is exactly.  Without pointing it at a
	 * scratch page, VRAM->GART blits with M2MF (as in DDX DFS)
	 * cause IOMMU "read from address 0" errors (rh#561267)
	 */
	nv_wr32(dev, 0x100c08, priv->r100c08 >> 8);

	/* This is needed to get meaningful information from 100c90
	 * on traps. No idea what these values mean exactly. */
	switch (dev_priv->chipset) {
	case 0x50:
		nv_wr32(dev, 0x100c90, 0x000707ff);
		break;
	case 0xa3:
	case 0xa5:
	case 0xa8:
		nv_wr32(dev, 0x100c90, 0x000d0fff);
		break;
	case 0xaf:
		nv_wr32(dev, 0x100c90, 0x089d1fff);
		break;
	default:
		nv_wr32(dev, 0x100c90, 0x001d07ff);
		break;
	}

	return 0;
}

void
nv50_fb_takedown(struct drm_device *dev)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nv50_fb_priv *priv;

	priv = dev_priv->engine.fb.priv;
	if (!priv)
		return;
	dev_priv->engine.fb.priv = NULL;

	pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE,
		       PCI_DMA_BIDIRECTIONAL);
	__free_page(priv->r100c08_page);
	kfree(priv);
}

void
nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	unsigned long flags;
	u32 trap[6], idx, chinst;
	int i, ch;

	idx = nv_rd32(dev, 0x100c90);
	if (!(idx & 0x80000000))
		return;
	idx &= 0x00ffffff;

	for (i = 0; i < 6; i++) {
		nv_wr32(dev, 0x100c90, idx | i << 24);
		trap[i] = nv_rd32(dev, 0x100c94);
	}
	nv_wr32(dev, 0x100c90, idx | 0x80000000);

	if (!display)
		return;

	chinst = (trap[2] << 16) | trap[1];

	spin_lock_irqsave(&dev_priv->channels.lock, flags);
	for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) {
		struct nouveau_channel *chan = dev_priv->channels.ptr[ch];

		if (!chan || !chan->ramin)
			continue;

		if (chinst == chan->ramin->vinst >> 12)
			break;
	}
	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);

	NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x "
		     "channel %d (0x%08x)\n",
		name, (trap[5] & 0x100 ? "read" : "write"),
		trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff,
		trap[0], ch, chinst);
}