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
];
63 nouveau_pushbuf_init(struct nouveau_channel
*chan
)
65 struct nouveau_channel_priv
*nvchan
= nouveau_channel(chan
);
66 struct nouveau_dma_priv
*m
= &nvchan
->dma_master
;
67 struct nouveau_dma_priv
*b
= &nvchan
->dma_bufmgr
;
73 /* Reassign last bit of push buffer for a "separate" bufmgr
76 m
->max
-= PB_BUFMGR_DWORDS
;
77 m
->free
-= PB_BUFMGR_DWORDS
;
79 b
->base
= m
->base
+ ((m
->max
+ 2) << 2);
80 b
->max
= PB_BUFMGR_DWORDS
- 2;
82 b
->free
= b
->max
- b
->cur
;
84 /* Some NOPs just to be safe
88 RING_SPACE_CH(chan
, 8);
89 for (i
= 0; i
< 8; i
++)
93 nouveau_pushbuf_space(chan
, 0);
94 chan
->pushbuf
= &nvchan
->pb
.base
;
99 /* This would be our TTM "superioctl" */
101 nouveau_pushbuf_flush(struct nouveau_channel
*chan
, unsigned min
)
103 struct nouveau_channel_priv
*nvchan
= nouveau_channel(chan
);
104 struct nouveau_pushbuf_priv
*nvpb
= &nvchan
->pb
;
105 struct nouveau_pushbuf_bo
*pbbo
;
106 struct nouveau_fence
*fence
= NULL
;
109 if (nvpb
->base
.remaining
== nvpb
->size
)
112 nvpb
->size
-= nvpb
->base
.remaining
;
113 nvchan
->dma
->cur
+= nvpb
->size
;
114 nvchan
->dma
->free
-= nvpb
->size
;
115 assert(nvchan
->dma
->cur
<= nvchan
->dma
->max
);
117 ret
= nouveau_fence_new(chan
, &fence
);
121 nvchan
->dma
= &nvchan
->dma_bufmgr
;
122 nvchan
->pushbuf
[nvpb
->nop_jump
] = 0x20000000 |
123 (nvchan
->dma
->base
+ (nvchan
->dma
->cur
<< 2));
125 /* Validate buffers + apply relocations */
126 nvchan
->user_charge
= 0;
127 while ((pbbo
= ptr_to_pbbo(nvpb
->buffers
))) {
128 struct nouveau_pushbuf_reloc
*r
;
129 struct nouveau_bo
*bo
= &ptr_to_bo(pbbo
->handle
)->base
;
131 ret
= nouveau_bo_validate(chan
, bo
, fence
, pbbo
->flags
);
134 if (bo
->offset
== nouveau_bo(bo
)->offset
&&
135 bo
->flags
== nouveau_bo(bo
)->flags
) {
136 /*XXX: could avoid reloc in this case, except with the
137 * current design we'd confuse the GPU quite a bit
138 * if we did this. Will fix soon.
141 bo
->offset
= nouveau_bo(bo
)->offset
;
142 bo
->flags
= nouveau_bo(bo
)->flags
;
144 while ((r
= ptr_to_pbrel(pbbo
->relocs
))) {
147 if (r
->flags
& NOUVEAU_BO_LOW
) {
148 push
= bo
->offset
+ r
->data
;
150 if (r
->flags
& NOUVEAU_BO_HIGH
) {
151 push
= (bo
->offset
+ r
->data
) >> 32;
156 if (r
->flags
& NOUVEAU_BO_OR
) {
157 if (bo
->flags
& NOUVEAU_BO_VRAM
)
164 pbbo
->relocs
= r
->next
;
168 nvpb
->buffers
= pbbo
->next
;
171 nvpb
->nr_buffers
= 0;
173 /* Switch back to user's ring */
174 RING_SPACE_CH(chan
, 1);
175 OUT_RING_CH(chan
, 0x20000000 | ((nvpb
->start
<< 2) +
176 nvchan
->dma_master
.base
));
177 nvchan
->dma
= &nvchan
->dma_master
;
179 /* Fence + kickoff */
180 nouveau_fence_emit(fence
);
182 nouveau_fence_ref(NULL
, &fence
);
184 /* Allocate space for next push buffer */
185 assert(!nouveau_pushbuf_space(chan
, min
));
190 static struct nouveau_pushbuf_bo
*
191 nouveau_pushbuf_emit_buffer(struct nouveau_channel
*chan
, struct nouveau_bo
*bo
)
193 struct nouveau_pushbuf_priv
*nvpb
= nouveau_pushbuf(chan
->pushbuf
);
194 struct nouveau_pushbuf_bo
*pbbo
= ptr_to_pbbo(nvpb
->buffers
);
197 if (pbbo
->handle
== bo
->handle
)
199 pbbo
= ptr_to_pbbo(pbbo
->next
);
202 pbbo
= malloc(sizeof(struct nouveau_pushbuf_bo
));
203 pbbo
->next
= nvpb
->buffers
;
204 nvpb
->buffers
= pbbo_to_ptr(pbbo
);
207 pbbo
->handle
= bo_to_ptr(bo
);
208 pbbo
->flags
= NOUVEAU_BO_VRAM
| NOUVEAU_BO_GART
;
215 nouveau_pushbuf_emit_reloc(struct nouveau_channel
*chan
, void *ptr
,
216 struct nouveau_bo
*bo
, uint32_t data
, uint32_t flags
,
217 uint32_t vor
, uint32_t tor
)
219 struct nouveau_pushbuf_bo
*pbbo
;
220 struct nouveau_pushbuf_reloc
*r
;
225 pbbo
= nouveau_pushbuf_emit_buffer(chan
, bo
);
229 r
= malloc(sizeof(struct nouveau_pushbuf_reloc
));
230 r
->next
= pbbo
->relocs
;
231 pbbo
->relocs
= pbrel_to_ptr(r
);
234 pbbo
->flags
|= (flags
& NOUVEAU_BO_RDWR
);
235 pbbo
->flags
&= (flags
| NOUVEAU_BO_RDWR
);
237 r
->handle
= bo_to_ptr(r
);