radeon_cs: add relocate hook for mm and non-mm relocations
[platform/upstream/libdrm.git] / shared-core / radeon_cs.c
1 /*
2  * Copyright 2008 Jerome Glisse.
3  * All Rights Reserved.
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 (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Jerome Glisse <glisse@freedesktop.org>
26  */
27 #include "drmP.h"
28 #include "radeon_drm.h"
29 #include "radeon_drv.h"
30 #include "r300_reg.h"
31
32 int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv)
33 {
34         struct drm_radeon_private *radeon = dev->dev_private;
35         struct drm_radeon_cs *cs = data;
36         uint32_t *packets = NULL;
37         uint32_t cs_id;
38         void *ib = NULL;
39         long size;
40         int r;
41
42         /* set command stream id to 0 which is fake id */
43         cs_id = 0;
44         DRM_COPY_TO_USER(&cs->cs_id, &cs_id, sizeof(uint32_t));
45
46         if (radeon == NULL) {
47                 DRM_ERROR("called with no initialization\n");
48                 return -EINVAL;
49         }
50         if (!cs->dwords) {
51                 return 0;
52         }
53         /* limit cs to 64K ib */
54         if (cs->dwords > (16 * 1024)) {
55                 return -EINVAL;
56         }
57         /* copy cs from userspace maybe we should copy into ib to save
58          * one copy but ib will be mapped wc so not good for cmd checking
59          * somethings worth testing i guess (Jerome)
60          */
61         size = cs->dwords * sizeof(uint32_t);
62         packets = drm_alloc(size, DRM_MEM_DRIVER);
63         if (packets == NULL) {
64                 return -ENOMEM;
65         }
66         if (DRM_COPY_FROM_USER(packets, (void __user *)(unsigned long)cs->packets, size)) {
67                 r = -EFAULT;
68                 goto out;
69         }
70         /* get ib */
71         r = radeon->cs.ib_get(dev, &ib, cs->dwords);
72         if (r) {
73                 goto out;
74         }
75
76         /* now parse command stream */
77         r = radeon->cs.parse(dev, fpriv, ib, packets, cs->dwords);
78         if (r) {
79                 goto out;
80         }
81
82         /* emit cs id sequence */
83         radeon->cs.id_emit(dev, &cs_id);
84         DRM_COPY_TO_USER(&cs->cs_id, &cs_id, sizeof(uint32_t));
85 out:
86         radeon->cs.ib_free(dev, ib, cs->dwords);
87         drm_free(packets, size, DRM_MEM_DRIVER);
88         return r;
89 }
90
91 /* for non-mm */
92 static int radeon_nomm_relocate(struct drm_device *dev, struct drm_file *file_priv, uint32_t *reloc, uint32_t *offset)
93 {
94         *offset = reloc[1];
95         return 0;
96 }
97 #define RELOC_SIZE 2
98 #define RADEON_2D_OFFSET_MASK 0x3fffff
99
100 static __inline__ int radeon_cs_relocate_offset(struct drm_device *dev, struct drm_file *file_priv,
101                                                 uint32_t *packets, uint32_t offset_dw)
102 {
103         drm_radeon_private_t *dev_priv = dev->dev_private;
104         uint32_t hdr = packets[offset_dw];
105         uint32_t reg = (hdr & R300_CP_PACKET0_REG_MASK) << 2;
106         uint32_t val = packets[offset_dw + 1];
107         uint32_t packet3_hdr = packets[offset_dw+2];
108         uint32_t tmp, offset;
109         int ret;
110         
111         /* this is too strict we may want to expand the length in the future and have
112          old kernels ignore it. */ 
113         if (packet3_hdr != (RADEON_CP_PACKET3 | RADEON_CP_NOP | (RELOC_SIZE << 16))) {
114                 DRM_ERROR("Packet 3 was %x should have been %x\n", packet3_hdr, RADEON_CP_PACKET3 | RADEON_CP_NOP | (RELOC_SIZE << 16));
115                 return -EINVAL;
116         }
117         
118         switch(reg) {
119         case RADEON_DST_PITCH_OFFSET:
120         case RADEON_SRC_PITCH_OFFSET:
121                 /* pass in the start of the reloc */
122                 ret = dev_priv->cs.relocate(dev, file_priv, packets + offset_dw + 2, &offset);
123                 if (ret)
124                         return ret;
125                 tmp = (val & RADEON_2D_OFFSET_MASK) << 10;
126                 val &= ~RADEON_2D_OFFSET_MASK;
127                 offset += tmp;
128                 offset >>= 10;
129                 val |= offset;
130                 break;
131         case R300_RB3D_COLOROFFSET0:
132         case R300_RB3D_DEPTHOFFSET:
133         case R300_TX_OFFSET_0:
134         case R300_TX_OFFSET_0+4:
135                 offset = packets[offset_dw + 3];
136
137                 ret = dev_priv->cs.relocate(dev, file_priv, packets + offset_dw + 2, &offset);
138                 if (ret)
139                         return ret;
140
141                 offset &= 0xffffffe0;
142                 val += offset;
143                 break;
144         default:
145                 break;
146         }
147
148
149         DRM_ERROR("New offset %x %x %x\n", packets[offset_dw+1], val, offset);
150         packets[offset_dw + 1] = val;
151
152         return 0;
153 }
154 static __inline__ int radeon_cs_check_offset(struct drm_device *dev,
155                                              uint32_t reg, uint32_t val)
156 {
157         uint32_t offset;
158
159         switch(reg) {
160         case RADEON_DST_PITCH_OFFSET:
161         case RADEON_SRC_PITCH_OFFSET:
162                 offset = val & ((1 << 22) - 1);
163                 offset <<= 10;
164                 break;
165         case R300_RB3D_COLOROFFSET0:
166         case R300_RB3D_DEPTHOFFSET:
167                 offset = val;
168                 break;
169         case R300_TX_OFFSET_0:
170         case R300_TX_OFFSET_0+4:
171                 offset = val & 0xffffffe0;
172                 break;
173         }
174         
175         DRM_ERROR("Offset check %x %x\n", reg, offset);
176         return 0;
177 }
178
179 int radeon_cs_packet0(struct drm_device *dev, struct drm_file *file_priv,
180                       uint32_t *packets, uint32_t offset_dw)
181 {
182         drm_radeon_private_t *dev_priv = dev->dev_private;
183         uint32_t hdr = packets[offset_dw];
184         int num_dw = ((hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16) + 2;
185         int need_reloc = 0;
186         int reg = (hdr & R300_CP_PACKET0_REG_MASK) << 2;
187         int count_dw = 1;
188         int ret;
189
190         while (count_dw < num_dw) {
191                 /* need to have something like the r300 validation here - 
192                    list of allowed registers */
193                 int flags;
194
195                 ret = r300_check_range(reg, 1);
196                 switch(ret) {
197                 case -1:
198                         DRM_ERROR("Illegal register %x\n", reg);
199                         break;
200                 case 0:
201                         break;
202                 case 1:
203                         flags = r300_get_reg_flags(reg);
204                         if (flags == MARK_CHECK_OFFSET) {
205                                 if (num_dw > 2) {
206                                         DRM_ERROR("Cannot relocate inside type stream of reg0 packets\n");
207                                         return -EINVAL;
208                                 }
209
210                                 ret = radeon_cs_relocate_offset(dev, file_priv, packets, offset_dw);
211                                 if (ret)
212                                         return ret;
213                                 DRM_DEBUG("need to relocate %x %d\n", reg, flags);
214                                 /* okay it should be followed by a NOP */
215                         } else if (flags == MARK_CHECK_SCISSOR) {
216                                 DRM_DEBUG("need to validate scissor %x %d\n", reg, flags);
217                         } else {
218                                 DRM_DEBUG("illegal register %x %d\n", reg, flags);
219                                 return -EINVAL;
220                         }
221                         break;
222                 }
223                 count_dw++;
224                 reg += 4;
225         }
226         return 0;
227 }
228
229 int radeon_cs_parse(struct drm_device *dev, struct drm_file *file_priv,
230                     void *ib, uint32_t *packets, uint32_t dwords)
231 {
232         drm_radeon_private_t *dev_priv = dev->dev_private;
233         volatile int rb;
234         int size_dw = dwords;
235         /* scan the packet for various things */
236         int count_dw = 0;
237         int ret = 0;
238
239         while (count_dw < size_dw && ret == 0) {
240                 int hdr = packets[count_dw];
241                 int num_dw = (hdr & RADEON_CP_PACKET_COUNT_MASK) >> 16;
242                 int reg;
243
244                 switch (hdr & RADEON_CP_PACKET_MASK) {
245                 case RADEON_CP_PACKET0:
246                         ret = radeon_cs_packet0(dev, file_priv, packets, count_dw);
247                         break;
248                 case RADEON_CP_PACKET1:
249                 case RADEON_CP_PACKET2:
250                         reg = hdr & RADEON_CP_PACKET0_REG_MASK;
251                         DRM_DEBUG("Packet 1/2: %d  %x\n", num_dw, reg);
252                         break;
253
254                 case RADEON_CP_PACKET3:
255                         reg = hdr & 0xff00;
256                         
257                         switch(reg) {
258                         case RADEON_CNTL_HOSTDATA_BLT:
259                         {
260                                 uint32_t offset;
261                                 offset = packets[count_dw+2] & ((1 << 22) - 1);
262                                 offset <<= 10;
263                                 DRM_ERROR("Offset check for Packet 3 %x %x\n", reg, offset);
264                                 /* okay it should be followed by a NOP */
265                                 break;
266                         }
267                         case RADEON_CNTL_BITBLT_MULTI:
268                         case RADEON_3D_LOAD_VBPNTR:     /* load vertex array pointers */
269                         case RADEON_CP_INDX_BUFFER:
270                                 DRM_ERROR("need relocate packet 3 for %x\n", reg);
271                                 break;
272
273                         case RADEON_CP_3D_DRAW_IMMD_2:  /* triggers drawing using in-packet vertex data */
274                         case RADEON_CP_3D_DRAW_VBUF_2:  /* triggers drawing of vertex buffers setup elsewhere */
275                         case RADEON_CP_3D_DRAW_INDX_2:  /* triggers drawing using indices to vertex buffer */
276                         case RADEON_WAIT_FOR_IDLE:
277                         case RADEON_CP_NOP:
278                                 break;
279                         default:
280                                 DRM_ERROR("unknown packet 3 %x\n", reg);
281                                 ret = -EINVAL;
282                         }
283                         break;
284                 }
285
286                 count_dw += num_dw+2;
287         }
288
289         if (ret)
290                 return ret;
291              
292
293         /* copy the packet into the IB */
294         memcpy(ib, packets, dwords * sizeof(uint32_t));
295
296         /* read back last byte to flush WC buffers */
297         rb = readl((ib + (dwords-1) * sizeof(uint32_t)));
298
299         return 0;
300 }
301
302 uint32_t radeon_cs_id_get(struct drm_radeon_private *radeon)
303 {
304         /* FIXME: protect with a spinlock */
305         /* FIXME: check if wrap affect last reported wrap & sequence */
306         radeon->cs.id_scnt = (radeon->cs.id_scnt + 1) & 0x00FFFFFF;
307         if (!radeon->cs.id_scnt) {
308                 /* increment wrap counter */
309                 radeon->cs.id_wcnt += 0x01000000;
310                 /* valid sequence counter start at 1 */
311                 radeon->cs.id_scnt = 1;
312         }
313         return (radeon->cs.id_scnt | radeon->cs.id_wcnt);
314 }
315
316 void r100_cs_id_emit(struct drm_device *dev, uint32_t *id)
317 {
318         drm_radeon_private_t *dev_priv = dev->dev_private;
319         RING_LOCALS;
320
321         /* ISYNC_CNTL should have CPSCRACTH bit set */
322         *id = radeon_cs_id_get(dev_priv);
323         /* emit id in SCRATCH4 (not used yet in old drm) */
324         BEGIN_RING(2);
325         OUT_RING(CP_PACKET0(RADEON_SCRATCH_REG4, 0));
326         OUT_RING(*id);
327         ADVANCE_RING(); 
328 }
329
330 void r300_cs_id_emit(struct drm_device *dev, uint32_t *id)
331 {
332         drm_radeon_private_t *dev_priv = dev->dev_private;
333         RING_LOCALS;
334
335         /* ISYNC_CNTL should not have CPSCRACTH bit set */
336         *id = radeon_cs_id_get(dev_priv);
337         /* emit id in SCRATCH6 */
338         BEGIN_RING(6);
339         OUT_RING(CP_PACKET0(R300_CP_RESYNC_ADDR, 0));
340         OUT_RING(6);
341         OUT_RING(CP_PACKET0(R300_CP_RESYNC_DATA, 0));
342         OUT_RING(*id);
343         OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0));
344         OUT_RING(R300_RB3D_DC_FINISH);
345         ADVANCE_RING(); 
346 }
347
348 uint32_t r100_cs_id_last_get(struct drm_device *dev)
349 {
350         drm_radeon_private_t *dev_priv = dev->dev_private;
351
352         return RADEON_READ(RADEON_SCRATCH_REG4);
353 }
354
355 uint32_t r300_cs_id_last_get(struct drm_device *dev)
356 {
357         drm_radeon_private_t *dev_priv = dev->dev_private;
358
359         return RADEON_READ(RADEON_SCRATCH_REG6);
360 }
361
362 int radeon_cs_init(struct drm_device *dev)
363 {
364         drm_radeon_private_t *dev_priv = dev->dev_private;
365
366         if (dev_priv->chip_family < CHIP_RV280) {
367                 dev_priv->cs.id_emit = r100_cs_id_emit;
368                 dev_priv->cs.id_last_get = r100_cs_id_last_get;
369         } else if (dev_priv->chip_family < CHIP_R600) {
370                 dev_priv->cs.id_emit = r300_cs_id_emit;
371                 dev_priv->cs.id_last_get = r300_cs_id_last_get;
372         }
373
374         dev_priv->cs.parse = radeon_cs_parse;
375         /* ib get depends on memory manager or not so memory manager */
376         dev_priv->cs.relocate = radeon_nomm_relocate;
377         return 0;
378 }