Add a user callback for when a channel hang is detected.
[nouveau] / src / nv_dma.c
1 /*
2  * Copyright 2007 Ben Skeggs
3  * Copyright 2007 Stephane Marchesin
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
20  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23
24 #include <errno.h>
25 #include "nv_include.h"
26 #include "nvreg.h"
27
28 static void NVDumpLockupInfo(NVPtr pNv)
29 {
30         struct nouveau_channel_priv *chan = nouveau_channel(pNv->chan);
31         int i, start;
32
33         start = ((*chan->get - chan->dma.base) >> 2) - 20;
34         if (start < 0)
35                 start = 0;
36
37         xf86DrvMsg(0, X_INFO, "Fifo dump (lockup 0x%04x,0x%04x):\n",
38                    (*chan->get - chan->dma.base) >> 2, chan->dma.put);
39         for(i = start; i < chan->dma.put + 10; i++)
40                 xf86DrvMsg(0, X_INFO, "[0x%04x] 0x%08x\n", i, chan->pushbuf[i]);
41         xf86DrvMsg(0, X_INFO, "End of fifo dump\n");
42 }
43
44 static void
45 NVLockedUp(ScrnInfoPtr pScrn)
46 {
47         NVPtr pNv = NVPTR(pScrn);
48         struct nouveau_channel_priv *chan = nouveau_channel(pNv->chan);
49
50         /* avoid re-entering FatalError on shutdown */
51         if (pNv->LockedUp)
52                 return;
53         pNv->LockedUp = TRUE;
54
55         NVDumpLockupInfo(pNv);
56
57         FatalError("DMA queue hang: dmaPut=%x, current=%x, status=%x\n",
58                    chan->dma.put, (*chan->get - chan->dma.base) >> 2,
59                    pNv->PGRAPH[NV_PGRAPH_STATUS/4]);
60 }
61
62 static void
63 NVChannelHangNotify(struct nouveau_channel *chan)
64 {
65         ScrnInfoPtr pScrn = chan->user_private;
66         NVLockedUp(pScrn);
67 }
68
69 void NVSync(ScrnInfoPtr pScrn)
70 {
71         NVPtr pNv = NVPTR(pScrn);
72         struct nouveau_channel_priv *chan = nouveau_channel(pNv->chan);
73         int t_start, timeout = 2000;
74
75         if (pNv->NoAccel)
76                 return;
77
78         /* Wait for entire FIFO to be processed */
79         t_start = GetTimeInMillis();
80         while((GetTimeInMillis() - t_start) < timeout &&
81               (((*chan->get - chan->dma.base) >> 2)!= chan->dma.put));
82         if ((GetTimeInMillis() - t_start) >= timeout) {
83                 NVLockedUp(pScrn);
84                 return;
85         }
86
87         /* Wait for channel to go completely idle */
88         nouveau_notifier_reset(pNv->notify0, 0);
89         if (pNv->Architecture < NV_ARCH_50) {
90                 BEGIN_RING(NvImageBlit, 0x104, 1);
91                 OUT_RING  (0);
92                 BEGIN_RING(NvImageBlit, 0x100, 1);
93                 OUT_RING  (0);
94         } else {
95                 BEGIN_RING(Nv2D, 0x104, 1);
96                 OUT_RING  (0);
97                 BEGIN_RING(Nv2D, 0x100, 1);
98                 OUT_RING  (0);
99         }
100         FIRE_RING();
101         if (nouveau_notifier_wait_status(pNv->notify0, 0,
102                                          NV_NOTIFY_STATE_STATUS_COMPLETED,
103                                          timeout))
104                 NVLockedUp(pScrn);
105 }
106
107 void NVResetGraphics(ScrnInfoPtr pScrn)
108 {
109         NVAccelCommonInit(pScrn);
110 }
111
112 static void NVInitDmaCB(ScrnInfoPtr pScrn)
113 {
114         NVPtr pNv = NVPTR(pScrn);
115         unsigned int cb_location;
116         int cb_size;
117         char *s;
118
119         /* I'm not bothering to check for failures here, the DRM will fall back
120          * on defaults if anything's wrong (ie. out of AGP, invalid sizes)
121          */
122 #ifndef __powerpc__
123         if (pNv->GART)
124                 cb_location = NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI_ACCEPTABLE;
125         else
126 #endif
127         cb_location = NOUVEAU_MEM_FB;
128         if((s = (char *)xf86GetOptValString(pNv->Options, OPTION_CMDBUF_LOCATION))) {
129                 if(!xf86NameCmp(s, "AGP"))
130                         cb_location = NOUVEAU_MEM_AGP;
131                 else if (!xf86NameCmp(s, "VRAM"))
132                         cb_location = NOUVEAU_MEM_FB;
133                 else if (!xf86NameCmp(s, "PCI"))
134                         cb_location = NOUVEAU_MEM_PCI;
135                 else
136                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid value \"%s\" for CBLocation\n", s);
137         }
138         nouveau_device_set_param(pNv->dev, NOUVEAU_SETPARAM_CMDBUF_LOCATION,
139                                  cb_location);
140
141         /* CBSize == size of space reserved for *all* FIFOs in MiB */
142         if (xf86GetOptValInteger(pNv->Options, OPTION_CMDBUF_SIZE, &cb_size))
143                 nouveau_device_set_param(pNv->dev, NOUVEAU_SETPARAM_CMDBUF_SIZE,
144                                          (cb_size << 20));
145 }
146
147 Bool
148 NVInitDma(ScrnInfoPtr pScrn)
149 {
150         NVPtr pNv = NVPTR(pScrn);
151         int ret;
152
153         NVInitDmaCB(pScrn);
154
155         ret = nouveau_channel_alloc(pNv->dev, NvDmaFB, NvDmaTT, &pNv->chan);
156         if (ret) {
157                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
158                            "Error creating GPU channel: %d\n", ret);
159                 return FALSE;
160         }
161         pNv->chan->user_private = pScrn;
162         pNv->chan->hang_notify = NVChannelHangNotify;
163
164         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
165                    "Opened GPU channel %d\n", pNv->chan->id);
166
167         return TRUE;
168 }
169