7d5eddb92ff81bc41500125437f94f92294f09d6
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
;
100 nouveau_pushbuf_calc_reloc(struct nouveau_bo
*bo
,
101 struct nouveau_pushbuf_reloc
*r
)
105 if (r
->flags
& NOUVEAU_BO_LOW
) {
106 push
= bo
->offset
+ r
->data
;
108 if (r
->flags
& NOUVEAU_BO_HIGH
) {
109 push
= (bo
->offset
+ r
->data
) >> 32;
114 if (r
->flags
& NOUVEAU_BO_OR
) {
115 if (bo
->flags
& NOUVEAU_BO_VRAM
)
124 /* This would be our TTM "superioctl" */
126 nouveau_pushbuf_flush(struct nouveau_channel
*chan
, unsigned min
)
128 struct nouveau_channel_priv
*nvchan
= nouveau_channel(chan
);
129 struct nouveau_pushbuf_priv
*nvpb
= &nvchan
->pb
;
130 struct nouveau_pushbuf_bo
*pbbo
;
131 struct nouveau_fence
*fence
= NULL
;
134 if (nvpb
->base
.remaining
== nvpb
->size
)
137 nvpb
->size
-= nvpb
->base
.remaining
;
138 nvchan
->dma
->cur
+= nvpb
->size
;
139 nvchan
->dma
->free
-= nvpb
->size
;
140 assert(nvchan
->dma
->cur
<= nvchan
->dma
->max
);
142 ret
= nouveau_fence_new(chan
, &fence
);
146 nvchan
->dma
= &nvchan
->dma_bufmgr
;
147 nvchan
->pushbuf
[nvpb
->nop_jump
] = 0x20000000 |
148 (nvchan
->dma
->base
+ (nvchan
->dma
->cur
<< 2));
150 /* Validate buffers + apply relocations */
151 nvchan
->user_charge
= 0;
152 while ((pbbo
= ptr_to_pbbo(nvpb
->buffers
))) {
153 struct nouveau_pushbuf_reloc
*r
;
154 struct nouveau_bo
*bo
= &ptr_to_bo(pbbo
->handle
)->base
;
156 ret
= nouveau_bo_validate(chan
, bo
, fence
, pbbo
->flags
);
159 if (bo
->offset
== nouveau_bo(bo
)->offset
&&
160 bo
->flags
== nouveau_bo(bo
)->flags
) {
161 while ((r
= ptr_to_pbrel(pbbo
->relocs
))) {
162 pbbo
->relocs
= r
->next
;
166 nvpb
->buffers
= pbbo
->next
;
170 bo
->offset
= nouveau_bo(bo
)->offset
;
171 bo
->flags
= nouveau_bo(bo
)->flags
;
173 while ((r
= ptr_to_pbrel(pbbo
->relocs
))) {
174 *r
->ptr
= nouveau_pushbuf_calc_reloc(bo
, r
);
175 pbbo
->relocs
= r
->next
;
179 nvpb
->buffers
= pbbo
->next
;
182 nvpb
->nr_buffers
= 0;
184 /* Switch back to user's ring */
185 RING_SPACE_CH(chan
, 1);
186 OUT_RING_CH(chan
, 0x20000000 | ((nvpb
->start
<< 2) +
187 nvchan
->dma_master
.base
));
188 nvchan
->dma
= &nvchan
->dma_master
;
190 /* Fence + kickoff */
191 nouveau_fence_emit(fence
);
193 nouveau_fence_ref(NULL
, &fence
);
195 /* Allocate space for next push buffer */
196 assert(!nouveau_pushbuf_space(chan
, min
));
201 static struct nouveau_pushbuf_bo
*
202 nouveau_pushbuf_emit_buffer(struct nouveau_channel
*chan
, struct nouveau_bo
*bo
)
204 struct nouveau_pushbuf_priv
*nvpb
= nouveau_pushbuf(chan
->pushbuf
);
205 struct nouveau_pushbuf_bo
*pbbo
= ptr_to_pbbo(nvpb
->buffers
);
208 if (pbbo
->handle
== bo
->handle
)
210 pbbo
= ptr_to_pbbo(pbbo
->next
);
213 pbbo
= malloc(sizeof(struct nouveau_pushbuf_bo
));
214 pbbo
->next
= nvpb
->buffers
;
215 nvpb
->buffers
= pbbo_to_ptr(pbbo
);
218 pbbo
->handle
= bo_to_ptr(bo
);
219 pbbo
->flags
= NOUVEAU_BO_VRAM
| NOUVEAU_BO_GART
;
226 nouveau_pushbuf_emit_reloc(struct nouveau_channel
*chan
, void *ptr
,
227 struct nouveau_bo
*bo
, uint32_t data
, uint32_t flags
,
228 uint32_t vor
, uint32_t tor
)
230 struct nouveau_pushbuf_bo
*pbbo
;
231 struct nouveau_pushbuf_reloc
*r
;
236 pbbo
= nouveau_pushbuf_emit_buffer(chan
, bo
);
240 r
= malloc(sizeof(struct nouveau_pushbuf_reloc
));
241 r
->next
= pbbo
->relocs
;
242 pbbo
->relocs
= pbrel_to_ptr(r
);
245 pbbo
->flags
|= (flags
& NOUVEAU_BO_RDWR
);
246 pbbo
->flags
&= (flags
| NOUVEAU_BO_RDWR
);
248 r
->handle
= bo_to_ptr(r
);
255 if (flags
& NOUVEAU_BO_DUMMY
)
256 *(uint32_t *)ptr
= 0;
258 *(uint32_t *)ptr
= nouveau_pushbuf_calc_reloc(bo
, r
);