2 * Copyright 2007 Nouveau Project
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #include "nouveau_drmif.h"
28 #include "nouveau_dma.h"
30 #define PB_BUFMGR_DWORDS (4096 / 2)
31 #define PB_MIN_USER_DWORDS 2048
34 nouveau_pushbuf_space(struct nouveau_channel
*chan
, unsigned min
)
36 struct nouveau_channel_priv
*nvchan
= nouveau_channel(chan
);
37 struct nouveau_pushbuf_priv
*nvpb
= &nvchan
->pb
;
39 assert((min
+ 1) <= nvchan
->dma
->max
);
41 /* Wait for enough space in push buffer */
42 min
= min
< PB_MIN_USER_DWORDS
? PB_MIN_USER_DWORDS
: min
;
43 min
+= 1; /* a bit extra for the NOP */
44 if (nvchan
->dma
->free
< min
)
45 WAIT_RING_CH(chan
, min
);
47 /* Insert NOP, may turn into a jump later */
48 RING_SPACE_CH(chan
, 1);
49 nvpb
->nop_jump
= nvchan
->dma
->cur
;
52 /* Any remaining space is available to the user */
53 nvpb
->start
= nvchan
->dma
->cur
;
54 nvpb
->size
= nvchan
->dma
->free
;
55 nvpb
->base
.channel
= chan
;
56 nvpb
->base
.remaining
= nvpb
->size
;
57 nvpb
->base
.cur
= &nvchan
->pushbuf
[nvpb
->start
];
59 /* Create a new fence object for this "frame" */
60 nouveau_fence_ref(NULL
, &nvpb
->fence
);
61 nouveau_fence_new(chan
, &nvpb
->fence
);
67 nouveau_pushbuf_init(struct nouveau_channel
*chan
)
69 struct nouveau_channel_priv
*nvchan
= nouveau_channel(chan
);
70 struct nouveau_dma_priv
*m
= &nvchan
->dma_master
;
71 struct nouveau_dma_priv
*b
= &nvchan
->dma_bufmgr
;
77 /* Reassign last bit of push buffer for a "separate" bufmgr
80 m
->max
-= PB_BUFMGR_DWORDS
;
81 m
->free
-= PB_BUFMGR_DWORDS
;
83 b
->base
= m
->base
+ ((m
->max
+ 2) << 2);
84 b
->max
= PB_BUFMGR_DWORDS
- 2;
86 b
->free
= b
->max
- b
->cur
;
88 /* Some NOPs just to be safe
92 RING_SPACE_CH(chan
, 8);
93 for (i
= 0; i
< 8; i
++)
97 nouveau_pushbuf_space(chan
, 0);
98 chan
->pushbuf
= &nvchan
->pb
.base
;
100 nvchan
->pb
.buffers
= calloc(NOUVEAU_PUSHBUF_MAX_BUFFERS
,
101 sizeof(struct nouveau_pushbuf_bo
));
102 nvchan
->pb
.relocs
= calloc(NOUVEAU_PUSHBUF_MAX_RELOCS
,
103 sizeof(struct nouveau_pushbuf_reloc
));
108 nouveau_pushbuf_calc_reloc(struct nouveau_bo
*bo
,
109 struct nouveau_pushbuf_reloc
*r
)
113 if (r
->flags
& NOUVEAU_BO_LOW
) {
114 push
= bo
->offset
+ r
->data
;
116 if (r
->flags
& NOUVEAU_BO_HIGH
) {
117 push
= (bo
->offset
+ r
->data
) >> 32;
122 if (r
->flags
& NOUVEAU_BO_OR
) {
123 if (bo
->flags
& NOUVEAU_BO_VRAM
)
132 /* This would be our TTM "superioctl" */
134 nouveau_pushbuf_flush(struct nouveau_channel
*chan
, unsigned min
)
136 struct nouveau_channel_priv
*nvchan
= nouveau_channel(chan
);
137 struct nouveau_pushbuf_priv
*nvpb
= &nvchan
->pb
;
140 if (nvpb
->base
.remaining
== nvpb
->size
)
143 nouveau_fence_flush(chan
);
145 nvpb
->size
-= nvpb
->base
.remaining
;
146 nvchan
->dma
->cur
+= nvpb
->size
;
147 nvchan
->dma
->free
-= nvpb
->size
;
148 assert(nvchan
->dma
->cur
<= nvchan
->dma
->max
);
150 nvchan
->dma
= &nvchan
->dma_bufmgr
;
151 nvchan
->pushbuf
[nvpb
->nop_jump
] = 0x20000000 |
152 (nvchan
->dma
->base
+ (nvchan
->dma
->cur
<< 2));
154 /* Validate buffers + apply relocations */
155 nvchan
->user_charge
= 0;
156 for (i
= 0; i
< nvpb
->nr_relocs
; i
++) {
157 struct nouveau_pushbuf_reloc
*r
= &nvpb
->relocs
[i
];
158 struct nouveau_pushbuf_bo
*pbbo
= r
->pbbo
;
159 struct nouveau_bo
*bo
= pbbo
->bo
;
161 /* Validated, mem matches presumed, no relocation necessary */
162 if (pbbo
->handled
& 2) {
163 if (!(pbbo
->handled
& 1))
168 /* Not yet validated, do it now */
169 if (!(pbbo
->handled
& 1)) {
170 ret
= nouveau_bo_validate(chan
, bo
, pbbo
->flags
);
177 if (bo
->offset
== nouveau_bo(bo
)->offset
&&
178 bo
->flags
== nouveau_bo(bo
)->flags
) {
182 bo
->offset
= nouveau_bo(bo
)->offset
;
183 bo
->flags
= nouveau_bo(bo
)->flags
;
186 /* Apply the relocation */
187 *r
->ptr
= nouveau_pushbuf_calc_reloc(bo
, r
);
191 /* Dereference all buffers on validate list */
192 for (i
= 0; i
< nvpb
->nr_buffers
; i
++) {
193 struct nouveau_pushbuf_bo
*pbbo
= &nvpb
->buffers
[i
];
195 nouveau_bo(pbbo
->bo
)->pending
= NULL
;
196 nouveau_bo_del(&pbbo
->bo
);
198 nvpb
->nr_buffers
= 0;
200 /* Switch back to user's ring */
201 RING_SPACE_CH(chan
, 1);
202 OUT_RING_CH(chan
, 0x20000000 | ((nvpb
->start
<< 2) +
203 nvchan
->dma_master
.base
));
204 nvchan
->dma
= &nvchan
->dma_master
;
206 /* Fence + kickoff */
207 nouveau_fence_emit(nvpb
->fence
);
210 /* Allocate space for next push buffer */
211 ret
= nouveau_pushbuf_space(chan
, min
);
217 static struct nouveau_pushbuf_bo
*
218 nouveau_pushbuf_emit_buffer(struct nouveau_channel
*chan
, struct nouveau_bo
*bo
)
220 struct nouveau_pushbuf_priv
*nvpb
= nouveau_pushbuf(chan
->pushbuf
);
221 struct nouveau_bo_priv
*nvbo
= nouveau_bo(bo
);
222 struct nouveau_pushbuf_bo
*pbbo
;
225 return nvbo
->pending
;
227 if (nvpb
->nr_buffers
>= NOUVEAU_PUSHBUF_MAX_BUFFERS
)
229 pbbo
= nvpb
->buffers
+ nvpb
->nr_buffers
++;
230 nvbo
->pending
= pbbo
;
232 nouveau_bo_ref(bo
->device
, bo
->handle
, &pbbo
->bo
);
233 pbbo
->channel
= chan
;
234 pbbo
->flags
= NOUVEAU_BO_VRAM
| NOUVEAU_BO_GART
;
240 nouveau_pushbuf_emit_reloc(struct nouveau_channel
*chan
, void *ptr
,
241 struct nouveau_bo
*bo
, uint32_t data
, uint32_t flags
,
242 uint32_t vor
, uint32_t tor
)
244 struct nouveau_pushbuf_priv
*nvpb
= nouveau_pushbuf(chan
->pushbuf
);
245 struct nouveau_pushbuf_bo
*pbbo
;
246 struct nouveau_pushbuf_reloc
*r
;
248 if (nvpb
->nr_relocs
>= NOUVEAU_PUSHBUF_MAX_RELOCS
)
251 pbbo
= nouveau_pushbuf_emit_buffer(chan
, bo
);
254 pbbo
->flags
|= (flags
& NOUVEAU_BO_RDWR
);
255 pbbo
->flags
&= (flags
| NOUVEAU_BO_RDWR
);
257 r
= nvpb
->relocs
+ nvpb
->nr_relocs
++;
265 if (flags
& NOUVEAU_BO_DUMMY
)
266 *(uint32_t *)ptr
= 0;
268 *(uint32_t *)ptr
= nouveau_pushbuf_calc_reloc(bo
, r
);