148406b85a7836a126b16348f45f169421757f56
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 while ((r
= ptr_to_pbrel(pbbo
->relocs
))) {
137 if (r
->flags
& NOUVEAU_BO_LOW
) {
138 push
= bo
->offset
+ r
->data
;
140 if (r
->flags
& NOUVEAU_BO_HIGH
) {
141 push
= (bo
->offset
+ r
->data
) >> 32;
146 if (r
->flags
& NOUVEAU_BO_OR
) {
147 if (bo
->flags
& NOUVEAU_BO_VRAM
)
154 pbbo
->relocs
= r
->next
;
158 nvpb
->buffers
= pbbo
->next
;
161 nvpb
->nr_buffers
= 0;
163 /* Switch back to user's ring */
164 RING_SPACE_CH(chan
, 1);
165 OUT_RING_CH(chan
, 0x20000000 | ((nvpb
->start
<< 2) +
166 nvchan
->dma_master
.base
));
167 nvchan
->dma
= &nvchan
->dma_master
;
169 /* Fence + kickoff */
170 nouveau_fence_emit(fence
);
172 nouveau_fence_del(&fence
);
174 /* Allocate space for next push buffer */
175 assert(!nouveau_pushbuf_space(chan
, min
));
180 static struct nouveau_pushbuf_bo
*
181 nouveau_pushbuf_emit_buffer(struct nouveau_channel
*chan
, struct nouveau_bo
*bo
)
183 struct nouveau_pushbuf_priv
*nvpb
= nouveau_pushbuf(chan
->pushbuf
);
184 struct nouveau_pushbuf_bo
*pbbo
= ptr_to_pbbo(nvpb
->buffers
);
187 if (pbbo
->handle
== bo
->handle
)
189 pbbo
= ptr_to_pbbo(pbbo
->next
);
192 pbbo
= malloc(sizeof(struct nouveau_pushbuf_bo
));
193 pbbo
->next
= nvpb
->buffers
;
194 nvpb
->buffers
= pbbo_to_ptr(pbbo
);
197 pbbo
->handle
= bo_to_ptr(bo
);
198 pbbo
->flags
= NOUVEAU_BO_VRAM
| NOUVEAU_BO_GART
;
205 nouveau_pushbuf_emit_reloc(struct nouveau_channel
*chan
, void *ptr
,
206 struct nouveau_bo
*bo
, uint32_t data
, uint32_t flags
,
207 uint32_t vor
, uint32_t tor
)
209 struct nouveau_pushbuf_bo
*pbbo
;
210 struct nouveau_pushbuf_reloc
*r
;
215 pbbo
= nouveau_pushbuf_emit_buffer(chan
, bo
);
219 r
= malloc(sizeof(struct nouveau_pushbuf_reloc
));
220 r
->next
= pbbo
->relocs
;
221 pbbo
->relocs
= pbrel_to_ptr(r
);
224 pbbo
->flags
|= (flags
& NOUVEAU_BO_RDWR
);
225 pbbo
->flags
&= (flags
| NOUVEAU_BO_RDWR
);
227 r
->handle
= bo_to_ptr(r
);