Merge branch 'vendor/LIBARCHIVE'
[dragonfly.git] / sys / dev / drm / mach64_state.c
1 /* mach64_state.c -- State support for mach64 (Rage Pro) driver -*- linux-c -*-
2  * Created: Sun Dec 03 19:20:26 2000 by gareth@valinux.com
3  */
4 /*-
5  * Copyright 2000 Gareth Hughes
6  * Copyright 2002-2003 Leif Delgass
7  * All Rights Reserved.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Authors:
28  *    Gareth Hughes <gareth@valinux.com>
29  *    Leif Delgass <ldelgass@retinalburn.net>
30  *    José Fonseca <j_r_fonseca@yahoo.co.uk>
31  */
32
33 #include "dev/drm/drmP.h"
34 #include "dev/drm/drm.h"
35 #include "dev/drm/mach64_drm.h"
36 #include "dev/drm/mach64_drv.h"
37
38 /* Interface history:
39  *
40  * 1.0 - Initial mach64 DRM
41  *
42  */
43 struct drm_ioctl_desc mach64_ioctls[] = {
44         DRM_IOCTL_DEF(DRM_MACH64_INIT, mach64_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
45         DRM_IOCTL_DEF(DRM_MACH64_CLEAR, mach64_dma_clear, DRM_AUTH),
46         DRM_IOCTL_DEF(DRM_MACH64_SWAP, mach64_dma_swap, DRM_AUTH),
47         DRM_IOCTL_DEF(DRM_MACH64_IDLE, mach64_dma_idle, DRM_AUTH),
48         DRM_IOCTL_DEF(DRM_MACH64_RESET, mach64_engine_reset, DRM_AUTH),
49         DRM_IOCTL_DEF(DRM_MACH64_VERTEX, mach64_dma_vertex, DRM_AUTH),
50         DRM_IOCTL_DEF(DRM_MACH64_BLIT, mach64_dma_blit, DRM_AUTH),
51         DRM_IOCTL_DEF(DRM_MACH64_FLUSH, mach64_dma_flush, DRM_AUTH),
52         DRM_IOCTL_DEF(DRM_MACH64_GETPARAM, mach64_get_param, DRM_AUTH),
53 };
54
55 int mach64_max_ioctl = DRM_ARRAY_SIZE(mach64_ioctls);
56
57 /* ================================================================
58  * DMA hardware state programming functions
59  */
60
61 static void mach64_print_dirty(const char *msg, unsigned int flags)
62 {
63         DRM_DEBUG("%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s\n",
64                   msg,
65                   flags,
66                   (flags & MACH64_UPLOAD_DST_OFF_PITCH) ? "dst_off_pitch, " :
67                   "",
68                   (flags & MACH64_UPLOAD_Z_ALPHA_CNTL) ? "z_alpha_cntl, " : "",
69                   (flags & MACH64_UPLOAD_SCALE_3D_CNTL) ? "scale_3d_cntl, " :
70                   "", (flags & MACH64_UPLOAD_DP_FOG_CLR) ? "dp_fog_clr, " : "",
71                   (flags & MACH64_UPLOAD_DP_WRITE_MASK) ? "dp_write_mask, " :
72                   "",
73                   (flags & MACH64_UPLOAD_DP_PIX_WIDTH) ? "dp_pix_width, " : "",
74                   (flags & MACH64_UPLOAD_SETUP_CNTL) ? "setup_cntl, " : "",
75                   (flags & MACH64_UPLOAD_MISC) ? "misc, " : "",
76                   (flags & MACH64_UPLOAD_TEXTURE) ? "texture, " : "",
77                   (flags & MACH64_UPLOAD_TEX0IMAGE) ? "tex0 image, " : "",
78                   (flags & MACH64_UPLOAD_TEX1IMAGE) ? "tex1 image, " : "",
79                   (flags & MACH64_UPLOAD_CLIPRECTS) ? "cliprects, " : "");
80 }
81
82 /* Mach64 doesn't have hardware cliprects, just one hardware scissor,
83  * so the GL scissor is intersected with each cliprect here
84  */
85 /* This function returns 0 on success, 1 for no intersection, and
86  * negative for an error
87  */
88 static int mach64_emit_cliprect(struct drm_file *file_priv,
89                                 drm_mach64_private_t * dev_priv,
90                                 struct drm_clip_rect * box)
91 {
92         u32 sc_left_right, sc_top_bottom;
93         struct drm_clip_rect scissor;
94         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
95         drm_mach64_context_regs_t *regs = &sarea_priv->context_state;
96         DMALOCALS;
97
98         DRM_DEBUG("box=%p\n", box);
99
100         /* Get GL scissor */
101         /* FIXME: store scissor in SAREA as a cliprect instead of in
102          * hardware format, or do intersection client-side
103          */
104         scissor.x1 = regs->sc_left_right & 0xffff;
105         scissor.x2 = (regs->sc_left_right & 0xffff0000) >> 16;
106         scissor.y1 = regs->sc_top_bottom & 0xffff;
107         scissor.y2 = (regs->sc_top_bottom & 0xffff0000) >> 16;
108
109         /* Intersect GL scissor with cliprect */
110         if (box->x1 > scissor.x1)
111                 scissor.x1 = box->x1;
112         if (box->y1 > scissor.y1)
113                 scissor.y1 = box->y1;
114         if (box->x2 < scissor.x2)
115                 scissor.x2 = box->x2;
116         if (box->y2 < scissor.y2)
117                 scissor.y2 = box->y2;
118         /* positive return means skip */
119         if (scissor.x1 >= scissor.x2)
120                 return 1;
121         if (scissor.y1 >= scissor.y2)
122                 return 1;
123
124         DMAGETPTR(file_priv, dev_priv, 2);      /* returns on failure to get buffer */
125
126         sc_left_right = ((scissor.x1 << 0) | (scissor.x2 << 16));
127         sc_top_bottom = ((scissor.y1 << 0) | (scissor.y2 << 16));
128
129         DMAOUTREG(MACH64_SC_LEFT_RIGHT, sc_left_right);
130         DMAOUTREG(MACH64_SC_TOP_BOTTOM, sc_top_bottom);
131
132         DMAADVANCE(dev_priv, 1);
133
134         return 0;
135 }
136
137 static __inline__ int mach64_emit_state(struct drm_file *file_priv,
138                                         drm_mach64_private_t * dev_priv)
139 {
140         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
141         drm_mach64_context_regs_t *regs = &sarea_priv->context_state;
142         unsigned int dirty = sarea_priv->dirty;
143         u32 offset = ((regs->tex_size_pitch & 0xf0) >> 2);
144         DMALOCALS;
145
146         if (MACH64_VERBOSE) {
147                 mach64_print_dirty(__FUNCTION__, dirty);
148         } else {
149                 DRM_DEBUG("dirty=0x%08x\n", dirty);
150         }
151
152         DMAGETPTR(file_priv, dev_priv, 17);     /* returns on failure to get buffer */
153
154         if (dirty & MACH64_UPLOAD_MISC) {
155                 DMAOUTREG(MACH64_DP_MIX, regs->dp_mix);
156                 DMAOUTREG(MACH64_DP_SRC, regs->dp_src);
157                 DMAOUTREG(MACH64_CLR_CMP_CNTL, regs->clr_cmp_cntl);
158                 DMAOUTREG(MACH64_GUI_TRAJ_CNTL, regs->gui_traj_cntl);
159                 sarea_priv->dirty &= ~MACH64_UPLOAD_MISC;
160         }
161
162         if (dirty & MACH64_UPLOAD_DST_OFF_PITCH) {
163                 DMAOUTREG(MACH64_DST_OFF_PITCH, regs->dst_off_pitch);
164                 sarea_priv->dirty &= ~MACH64_UPLOAD_DST_OFF_PITCH;
165         }
166         if (dirty & MACH64_UPLOAD_Z_OFF_PITCH) {
167                 DMAOUTREG(MACH64_Z_OFF_PITCH, regs->z_off_pitch);
168                 sarea_priv->dirty &= ~MACH64_UPLOAD_Z_OFF_PITCH;
169         }
170         if (dirty & MACH64_UPLOAD_Z_ALPHA_CNTL) {
171                 DMAOUTREG(MACH64_Z_CNTL, regs->z_cntl);
172                 DMAOUTREG(MACH64_ALPHA_TST_CNTL, regs->alpha_tst_cntl);
173                 sarea_priv->dirty &= ~MACH64_UPLOAD_Z_ALPHA_CNTL;
174         }
175         if (dirty & MACH64_UPLOAD_SCALE_3D_CNTL) {
176                 DMAOUTREG(MACH64_SCALE_3D_CNTL, regs->scale_3d_cntl);
177                 sarea_priv->dirty &= ~MACH64_UPLOAD_SCALE_3D_CNTL;
178         }
179         if (dirty & MACH64_UPLOAD_DP_FOG_CLR) {
180                 DMAOUTREG(MACH64_DP_FOG_CLR, regs->dp_fog_clr);
181                 sarea_priv->dirty &= ~MACH64_UPLOAD_DP_FOG_CLR;
182         }
183         if (dirty & MACH64_UPLOAD_DP_WRITE_MASK) {
184                 DMAOUTREG(MACH64_DP_WRITE_MASK, regs->dp_write_mask);
185                 sarea_priv->dirty &= ~MACH64_UPLOAD_DP_WRITE_MASK;
186         }
187         if (dirty & MACH64_UPLOAD_DP_PIX_WIDTH) {
188                 DMAOUTREG(MACH64_DP_PIX_WIDTH, regs->dp_pix_width);
189                 sarea_priv->dirty &= ~MACH64_UPLOAD_DP_PIX_WIDTH;
190         }
191         if (dirty & MACH64_UPLOAD_SETUP_CNTL) {
192                 DMAOUTREG(MACH64_SETUP_CNTL, regs->setup_cntl);
193                 sarea_priv->dirty &= ~MACH64_UPLOAD_SETUP_CNTL;
194         }
195
196         if (dirty & MACH64_UPLOAD_TEXTURE) {
197                 DMAOUTREG(MACH64_TEX_SIZE_PITCH, regs->tex_size_pitch);
198                 DMAOUTREG(MACH64_TEX_CNTL, regs->tex_cntl);
199                 DMAOUTREG(MACH64_SECONDARY_TEX_OFF, regs->secondary_tex_off);
200                 DMAOUTREG(MACH64_TEX_0_OFF + offset, regs->tex_offset);
201                 sarea_priv->dirty &= ~MACH64_UPLOAD_TEXTURE;
202         }
203
204         DMAADVANCE(dev_priv, 1);
205
206         sarea_priv->dirty &= MACH64_UPLOAD_CLIPRECTS;
207
208         return 0;
209
210 }
211
212 /* ================================================================
213  * DMA command dispatch functions
214  */
215
216 static int mach64_dma_dispatch_clear(struct drm_device * dev,
217                                      struct drm_file *file_priv,
218                                      unsigned int flags,
219                                      int cx, int cy, int cw, int ch,
220                                      unsigned int clear_color,
221                                      unsigned int clear_depth)
222 {
223         drm_mach64_private_t *dev_priv = dev->dev_private;
224         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
225         drm_mach64_context_regs_t *ctx = &sarea_priv->context_state;
226         int nbox = sarea_priv->nbox;
227         struct drm_clip_rect *pbox = sarea_priv->boxes;
228         u32 fb_bpp, depth_bpp;
229         int i;
230         DMALOCALS;
231
232         DRM_DEBUG("\n");
233
234         switch (dev_priv->fb_bpp) {
235         case 16:
236                 fb_bpp = MACH64_DATATYPE_RGB565;
237                 break;
238         case 32:
239                 fb_bpp = MACH64_DATATYPE_ARGB8888;
240                 break;
241         default:
242                 return -EINVAL;
243         }
244         switch (dev_priv->depth_bpp) {
245         case 16:
246                 depth_bpp = MACH64_DATATYPE_RGB565;
247                 break;
248         case 24:
249         case 32:
250                 depth_bpp = MACH64_DATATYPE_ARGB8888;
251                 break;
252         default:
253                 return -EINVAL;
254         }
255
256         if (!nbox)
257                 return 0;
258
259         DMAGETPTR(file_priv, dev_priv, nbox * 31);      /* returns on failure to get buffer */
260
261         for (i = 0; i < nbox; i++) {
262                 int x = pbox[i].x1;
263                 int y = pbox[i].y1;
264                 int w = pbox[i].x2 - x;
265                 int h = pbox[i].y2 - y;
266
267                 DRM_DEBUG("dispatch clear %d,%d-%d,%d flags 0x%x\n",
268                           pbox[i].x1, pbox[i].y1,
269                           pbox[i].x2, pbox[i].y2, flags);
270
271                 if (flags & (MACH64_FRONT | MACH64_BACK)) {
272                         /* Setup for color buffer clears
273                          */
274
275                         DMAOUTREG(MACH64_Z_CNTL, 0);
276                         DMAOUTREG(MACH64_SCALE_3D_CNTL, 0);
277
278                         DMAOUTREG(MACH64_SC_LEFT_RIGHT, ctx->sc_left_right);
279                         DMAOUTREG(MACH64_SC_TOP_BOTTOM, ctx->sc_top_bottom);
280
281                         DMAOUTREG(MACH64_CLR_CMP_CNTL, 0);
282                         DMAOUTREG(MACH64_GUI_TRAJ_CNTL,
283                                   (MACH64_DST_X_LEFT_TO_RIGHT |
284                                    MACH64_DST_Y_TOP_TO_BOTTOM));
285
286                         DMAOUTREG(MACH64_DP_PIX_WIDTH, ((fb_bpp << 0) |
287                                                         (fb_bpp << 4) |
288                                                         (fb_bpp << 8) |
289                                                         (fb_bpp << 16) |
290                                                         (fb_bpp << 28)));
291
292                         DMAOUTREG(MACH64_DP_FRGD_CLR, clear_color);
293                         DMAOUTREG(MACH64_DP_WRITE_MASK, ctx->dp_write_mask);
294                         DMAOUTREG(MACH64_DP_MIX, (MACH64_BKGD_MIX_D |
295                                                   MACH64_FRGD_MIX_S));
296                         DMAOUTREG(MACH64_DP_SRC, (MACH64_BKGD_SRC_FRGD_CLR |
297                                                   MACH64_FRGD_SRC_FRGD_CLR |
298                                                   MACH64_MONO_SRC_ONE));
299
300                 }
301
302                 if (flags & MACH64_FRONT) {
303
304                         DMAOUTREG(MACH64_DST_OFF_PITCH,
305                                   dev_priv->front_offset_pitch);
306                         DMAOUTREG(MACH64_DST_X_Y, (y << 16) | x);
307                         DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w);
308
309                 }
310
311                 if (flags & MACH64_BACK) {
312
313                         DMAOUTREG(MACH64_DST_OFF_PITCH,
314                                   dev_priv->back_offset_pitch);
315                         DMAOUTREG(MACH64_DST_X_Y, (y << 16) | x);
316                         DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w);
317
318                 }
319
320                 if (flags & MACH64_DEPTH) {
321                         /* Setup for depth buffer clear
322                          */
323                         DMAOUTREG(MACH64_Z_CNTL, 0);
324                         DMAOUTREG(MACH64_SCALE_3D_CNTL, 0);
325
326                         DMAOUTREG(MACH64_SC_LEFT_RIGHT, ctx->sc_left_right);
327                         DMAOUTREG(MACH64_SC_TOP_BOTTOM, ctx->sc_top_bottom);
328
329                         DMAOUTREG(MACH64_CLR_CMP_CNTL, 0);
330                         DMAOUTREG(MACH64_GUI_TRAJ_CNTL,
331                                   (MACH64_DST_X_LEFT_TO_RIGHT |
332                                    MACH64_DST_Y_TOP_TO_BOTTOM));
333
334                         DMAOUTREG(MACH64_DP_PIX_WIDTH, ((depth_bpp << 0) |
335                                                         (depth_bpp << 4) |
336                                                         (depth_bpp << 8) |
337                                                         (depth_bpp << 16) |
338                                                         (depth_bpp << 28)));
339
340                         DMAOUTREG(MACH64_DP_FRGD_CLR, clear_depth);
341                         DMAOUTREG(MACH64_DP_WRITE_MASK, 0xffffffff);
342                         DMAOUTREG(MACH64_DP_MIX, (MACH64_BKGD_MIX_D |
343                                                   MACH64_FRGD_MIX_S));
344                         DMAOUTREG(MACH64_DP_SRC, (MACH64_BKGD_SRC_FRGD_CLR |
345                                                   MACH64_FRGD_SRC_FRGD_CLR |
346                                                   MACH64_MONO_SRC_ONE));
347
348                         DMAOUTREG(MACH64_DST_OFF_PITCH,
349                                   dev_priv->depth_offset_pitch);
350                         DMAOUTREG(MACH64_DST_X_Y, (y << 16) | x);
351                         DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w);
352                 }
353         }
354
355         DMAADVANCE(dev_priv, 1);
356
357         return 0;
358 }
359
360 static int mach64_dma_dispatch_swap(struct drm_device * dev,
361                                     struct drm_file *file_priv)
362 {
363         drm_mach64_private_t *dev_priv = dev->dev_private;
364         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
365         int nbox = sarea_priv->nbox;
366         struct drm_clip_rect *pbox = sarea_priv->boxes;
367         u32 fb_bpp;
368         int i;
369         DMALOCALS;
370
371         DRM_DEBUG("\n");
372
373         switch (dev_priv->fb_bpp) {
374         case 16:
375                 fb_bpp = MACH64_DATATYPE_RGB565;
376                 break;
377         case 32:
378         default:
379                 fb_bpp = MACH64_DATATYPE_ARGB8888;
380                 break;
381         }
382
383         if (!nbox)
384                 return 0;
385
386         DMAGETPTR(file_priv, dev_priv, 13 + nbox * 4);  /* returns on failure to get buffer */
387
388         DMAOUTREG(MACH64_Z_CNTL, 0);
389         DMAOUTREG(MACH64_SCALE_3D_CNTL, 0);
390
391         DMAOUTREG(MACH64_SC_LEFT_RIGHT, 0 | (8191 << 16));      /* no scissor */
392         DMAOUTREG(MACH64_SC_TOP_BOTTOM, 0 | (16383 << 16));
393
394         DMAOUTREG(MACH64_CLR_CMP_CNTL, 0);
395         DMAOUTREG(MACH64_GUI_TRAJ_CNTL, (MACH64_DST_X_LEFT_TO_RIGHT |
396                                          MACH64_DST_Y_TOP_TO_BOTTOM));
397
398         DMAOUTREG(MACH64_DP_PIX_WIDTH, ((fb_bpp << 0) |
399                                         (fb_bpp << 4) |
400                                         (fb_bpp << 8) |
401                                         (fb_bpp << 16) | (fb_bpp << 28)));
402
403         DMAOUTREG(MACH64_DP_WRITE_MASK, 0xffffffff);
404         DMAOUTREG(MACH64_DP_MIX, (MACH64_BKGD_MIX_D | MACH64_FRGD_MIX_S));
405         DMAOUTREG(MACH64_DP_SRC, (MACH64_BKGD_SRC_BKGD_CLR |
406                                   MACH64_FRGD_SRC_BLIT | MACH64_MONO_SRC_ONE));
407
408         DMAOUTREG(MACH64_SRC_OFF_PITCH, dev_priv->back_offset_pitch);
409         DMAOUTREG(MACH64_DST_OFF_PITCH, dev_priv->front_offset_pitch);
410
411         for (i = 0; i < nbox; i++) {
412                 int x = pbox[i].x1;
413                 int y = pbox[i].y1;
414                 int w = pbox[i].x2 - x;
415                 int h = pbox[i].y2 - y;
416
417                 DRM_DEBUG("dispatch swap %d,%d-%d,%d\n",
418                           pbox[i].x1, pbox[i].y1, pbox[i].x2, pbox[i].y2);
419
420                 DMAOUTREG(MACH64_SRC_WIDTH1, w);
421                 DMAOUTREG(MACH64_SRC_Y_X, (x << 16) | y);
422                 DMAOUTREG(MACH64_DST_Y_X, (x << 16) | y);
423                 DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w);
424
425         }
426
427         DMAADVANCE(dev_priv, 1);
428
429         if (dev_priv->driver_mode == MACH64_MODE_DMA_ASYNC) {
430                 for (i = 0; i < MACH64_MAX_QUEUED_FRAMES - 1; i++) {
431                         dev_priv->frame_ofs[i] = dev_priv->frame_ofs[i + 1];
432                 }
433                 dev_priv->frame_ofs[i] = GETRINGOFFSET();
434
435                 dev_priv->sarea_priv->frames_queued++;
436         }
437
438         return 0;
439 }
440
441 static int mach64_do_get_frames_queued(drm_mach64_private_t * dev_priv)
442 {
443         drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
444         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
445         int i, start;
446         u32 head, tail, ofs;
447
448         DRM_DEBUG("\n");
449
450         if (sarea_priv->frames_queued == 0)
451                 return 0;
452
453         tail = ring->tail;
454         mach64_ring_tick(dev_priv, ring);
455         head = ring->head;
456
457         start = (MACH64_MAX_QUEUED_FRAMES -
458                  DRM_MIN(MACH64_MAX_QUEUED_FRAMES, sarea_priv->frames_queued));
459
460         if (head == tail) {
461                 sarea_priv->frames_queued = 0;
462                 for (i = start; i < MACH64_MAX_QUEUED_FRAMES; i++) {
463                         dev_priv->frame_ofs[i] = ~0;
464                 }
465                 return 0;
466         }
467
468         for (i = start; i < MACH64_MAX_QUEUED_FRAMES; i++) {
469                 ofs = dev_priv->frame_ofs[i];
470                 DRM_DEBUG("frame_ofs[%d] ofs: %d\n", i, ofs);
471                 if (ofs == ~0 ||
472                     (head < tail && (ofs < head || ofs >= tail)) ||
473                     (head > tail && (ofs < head && ofs >= tail))) {
474                         sarea_priv->frames_queued =
475                             (MACH64_MAX_QUEUED_FRAMES - 1) - i;
476                         dev_priv->frame_ofs[i] = ~0;
477                 }
478         }
479
480         return sarea_priv->frames_queued;
481 }
482
483 /* Copy and verify a client submited buffer.
484  * FIXME: Make an assembly optimized version
485  */
486 static __inline__ int copy_from_user_vertex(u32 *to,
487                                             const u32 __user *ufrom,
488                                             unsigned long bytes)
489 {
490         unsigned long n = bytes;        /* dwords remaining in buffer */
491         u32 *from, *orig_from;
492
493         from = drm_alloc(bytes, DRM_MEM_DRIVER);
494         if (from == NULL)
495                 return -ENOMEM;
496
497         if (DRM_COPY_FROM_USER(from, ufrom, bytes)) {
498                 drm_free(from, bytes, DRM_MEM_DRIVER);
499                 return -EFAULT;
500         }
501         orig_from = from; /* we'll be modifying the "from" ptr, so save it */
502
503         n >>= 2;
504
505         while (n > 1) {
506                 u32 data, reg, count;
507
508                 data = *from++;
509
510                 n--;
511
512                 reg = le32_to_cpu(data);
513                 count = (reg >> 16) + 1;
514                 if (count <= n) {
515                         n -= count;
516                         reg &= 0xffff;
517
518                         /* This is an exact match of Mach64's Setup Engine registers,
519                          * excluding SETUP_CNTL (1_C1).
520                          */
521                         if ((reg >= 0x0190 && reg < 0x01c1) ||
522                             (reg >= 0x01ca && reg <= 0x01cf)) {
523                                 *to++ = data;
524                                 memcpy(to, from, count << 2);
525                                 from += count;
526                                 to += count;
527                         } else {
528                                 DRM_ERROR("Got bad command: 0x%04x\n", reg);
529                                 drm_free(orig_from, bytes, DRM_MEM_DRIVER);
530                                 return -EACCES;
531                         }
532                 } else {
533                         DRM_ERROR
534                             ("Got bad command count(=%u) dwords remaining=%lu\n",
535                              count, n);
536                         drm_free(orig_from, bytes, DRM_MEM_DRIVER);
537                         return -EINVAL;
538                 }
539         }
540
541         drm_free(orig_from, bytes, DRM_MEM_DRIVER);
542         if (n == 0)
543                 return 0;
544         else {
545                 DRM_ERROR("Bad buf->used(=%lu)\n", bytes);
546                 return -EINVAL;
547         }
548 }
549
550 static int mach64_dma_dispatch_vertex(struct drm_device * dev,
551                                       struct drm_file *file_priv,
552                                       drm_mach64_vertex_t * vertex)
553 {
554         drm_mach64_private_t *dev_priv = dev->dev_private;
555         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
556         struct drm_buf *copy_buf;
557         void *buf = vertex->buf;
558         unsigned long used = vertex->used;
559         int ret = 0;
560         int i = 0;
561         int done = 0;
562         int verify_ret = 0;
563         DMALOCALS;
564
565         DRM_DEBUG("buf=%p used=%lu nbox=%d\n",
566                   buf, used, sarea_priv->nbox);
567
568         if (!used)
569                 goto _vertex_done;
570
571         copy_buf = mach64_freelist_get(dev_priv);
572         if (copy_buf == NULL) {
573                 DRM_ERROR("couldn't get buffer\n");
574                 return -EAGAIN;
575         }
576
577         /* Mach64's vertex data is actually register writes. To avoid security
578          * compromises these register writes have to be verified and copied from
579          * user space into a private DMA buffer.
580          */
581         verify_ret = copy_from_user_vertex(GETBUFPTR(copy_buf), buf, used);
582
583         if (verify_ret != 0) {
584                 mach64_freelist_put(dev_priv, copy_buf);
585                 goto _vertex_done;
586         }
587
588         copy_buf->used = used;
589
590         DMASETPTR(copy_buf);
591
592         if (sarea_priv->dirty & ~MACH64_UPLOAD_CLIPRECTS) {
593                 ret = mach64_emit_state(file_priv, dev_priv);
594                 if (ret < 0)
595                         return ret;
596         }
597
598         do {
599                 /* Emit the next cliprect */
600                 if (i < sarea_priv->nbox) {
601                         ret = mach64_emit_cliprect(file_priv, dev_priv,
602                                                    &sarea_priv->boxes[i]);
603                         if (ret < 0) {
604                                 /* failed to get buffer */
605                                 return ret;
606                         } else if (ret != 0) {
607                                 /* null intersection with scissor */
608                                 continue;
609                         }
610                 }
611                 if ((i >= sarea_priv->nbox - 1))
612                         done = 1;
613
614                 /* Add the buffer to the DMA queue */
615                 DMAADVANCE(dev_priv, done);
616
617         } while (++i < sarea_priv->nbox);
618
619         if (!done) {
620                 if (copy_buf->pending) {
621                         DMADISCARDBUF();
622                 } else {
623                         /* This buffer wasn't used (no cliprects), so place it
624                          * back on the free list
625                          */
626                         mach64_freelist_put(dev_priv, copy_buf);
627                 }
628         }
629
630 _vertex_done:
631         sarea_priv->dirty &= ~MACH64_UPLOAD_CLIPRECTS;
632         sarea_priv->nbox = 0;
633
634         return verify_ret;
635 }
636
637 static __inline__ int copy_from_user_blit(u32 *to,
638                                           const u32 __user *ufrom,
639                                           unsigned long bytes)
640 {
641         to = (u32 *)((char *)to + MACH64_HOSTDATA_BLIT_OFFSET);
642
643         if (DRM_COPY_FROM_USER(to, ufrom, bytes)) {
644                 return -EFAULT;
645         }
646
647         return 0;
648 }
649
650 static int mach64_dma_dispatch_blit(struct drm_device * dev,
651                                     struct drm_file *file_priv,
652                                     drm_mach64_blit_t * blit)
653 {
654         drm_mach64_private_t *dev_priv = dev->dev_private;
655         int dword_shift, dwords;
656         unsigned long used;
657         struct drm_buf *copy_buf;
658         int verify_ret = 0;
659         DMALOCALS;
660
661         /* The compiler won't optimize away a division by a variable,
662          * even if the only legal values are powers of two.  Thus, we'll
663          * use a shift instead.
664          */
665         switch (blit->format) {
666         case MACH64_DATATYPE_ARGB8888:
667                 dword_shift = 0;
668                 break;
669         case MACH64_DATATYPE_ARGB1555:
670         case MACH64_DATATYPE_RGB565:
671         case MACH64_DATATYPE_VYUY422:
672         case MACH64_DATATYPE_YVYU422:
673         case MACH64_DATATYPE_ARGB4444:
674                 dword_shift = 1;
675                 break;
676         case MACH64_DATATYPE_CI8:
677         case MACH64_DATATYPE_RGB8:
678                 dword_shift = 2;
679                 break;
680         default:
681                 DRM_ERROR("invalid blit format %d\n", blit->format);
682                 return -EINVAL;
683         }
684
685         /* Set buf->used to the bytes of blit data based on the blit dimensions
686          * and verify the size.  When the setup is emitted to the buffer with
687          * the DMA* macros below, buf->used is incremented to include the bytes
688          * used for setup as well as the blit data.
689          */
690         dwords = (blit->width * blit->height) >> dword_shift;
691         used = dwords << 2;
692         if (used <= 0 ||
693             used > MACH64_BUFFER_SIZE - MACH64_HOSTDATA_BLIT_OFFSET) {
694                 DRM_ERROR("Invalid blit size: %lu bytes\n", used);
695                 return -EINVAL;
696         }
697
698         copy_buf = mach64_freelist_get(dev_priv);
699         if (copy_buf == NULL) {
700                 DRM_ERROR("couldn't get buffer\n");
701                 return -EAGAIN;
702         }
703
704         /* Copy the blit data from userspace.
705          * 
706          * XXX: This is overkill. The most efficient solution would be having 
707          * two sets of buffers (one set private for vertex data, the other set 
708          * client-writable for blits). However that would bring more complexity 
709          * and would break backward compatability. The solution currently 
710          * implemented is keeping all buffers private, allowing to secure the
711          * driver, without increasing complexity at the expense of some speed 
712          * transfering data.
713          */
714         verify_ret = copy_from_user_blit(GETBUFPTR(copy_buf), blit->buf, used);
715
716         if (verify_ret != 0) {
717                 mach64_freelist_put(dev_priv, copy_buf);
718                 goto _blit_done;
719         }
720
721         copy_buf->used = used;
722
723         /* FIXME: Use a last buffer flag and reduce the state emitted for subsequent,
724          * continuation buffers?
725          */
726
727         /* Blit via BM_HOSTDATA (gui-master) - like HOST_DATA[0-15], but doesn't require
728          * a register command every 16 dwords.  State setup is added at the start of the
729          * buffer -- the client leaves space for this based on MACH64_HOSTDATA_BLIT_OFFSET
730          */
731         DMASETPTR(copy_buf);
732
733         DMAOUTREG(MACH64_Z_CNTL, 0);
734         DMAOUTREG(MACH64_SCALE_3D_CNTL, 0);
735
736         DMAOUTREG(MACH64_SC_LEFT_RIGHT, 0 | (8191 << 16));      /* no scissor */
737         DMAOUTREG(MACH64_SC_TOP_BOTTOM, 0 | (16383 << 16));
738
739         DMAOUTREG(MACH64_CLR_CMP_CNTL, 0);      /* disable */
740         DMAOUTREG(MACH64_GUI_TRAJ_CNTL,
741                   MACH64_DST_X_LEFT_TO_RIGHT | MACH64_DST_Y_TOP_TO_BOTTOM);
742
743         DMAOUTREG(MACH64_DP_PIX_WIDTH, (blit->format << 0)      /* dst pix width */
744                   |(blit->format << 4)  /* composite pix width */
745                   |(blit->format << 8)  /* src pix width */
746                   |(blit->format << 16) /* host data pix width */
747                   |(blit->format << 28) /* scaler/3D pix width */
748             );
749
750         DMAOUTREG(MACH64_DP_WRITE_MASK, 0xffffffff);    /* enable all planes */
751         DMAOUTREG(MACH64_DP_MIX, MACH64_BKGD_MIX_D | MACH64_FRGD_MIX_S);
752         DMAOUTREG(MACH64_DP_SRC,
753                   MACH64_BKGD_SRC_BKGD_CLR
754                   | MACH64_FRGD_SRC_HOST | MACH64_MONO_SRC_ONE);
755
756         DMAOUTREG(MACH64_DST_OFF_PITCH,
757                   (blit->pitch << 22) | (blit->offset >> 3));
758         DMAOUTREG(MACH64_DST_X_Y, (blit->y << 16) | blit->x);
759         DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (blit->height << 16) | blit->width);
760
761         DRM_DEBUG("%lu bytes\n", used);
762
763         /* Add the buffer to the queue */
764         DMAADVANCEHOSTDATA(dev_priv);
765
766 _blit_done:
767         return verify_ret;
768 }
769
770 /* ================================================================
771  * IOCTL functions
772  */
773
774 int mach64_dma_clear(struct drm_device *dev, void *data,
775                      struct drm_file *file_priv)
776 {
777         drm_mach64_private_t *dev_priv = dev->dev_private;
778         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
779         drm_mach64_clear_t *clear = data;
780         int ret;
781
782         DRM_DEBUG("pid=%d\n", DRM_CURRENTPID);
783
784         LOCK_TEST_WITH_RETURN(dev, file_priv);
785
786         if (sarea_priv->nbox > MACH64_NR_SAREA_CLIPRECTS)
787                 sarea_priv->nbox = MACH64_NR_SAREA_CLIPRECTS;
788
789         ret = mach64_dma_dispatch_clear(dev, file_priv, clear->flags,
790                                         clear->x, clear->y, clear->w, clear->h,
791                                         clear->clear_color,
792                                         clear->clear_depth);
793
794         /* Make sure we restore the 3D state next time.
795          */
796         sarea_priv->dirty |= (MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC);
797         return ret;
798 }
799
800 int mach64_dma_swap(struct drm_device *dev, void *data,
801                     struct drm_file *file_priv)
802 {
803         drm_mach64_private_t *dev_priv = dev->dev_private;
804         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
805         int ret;
806
807         DRM_DEBUG("pid=%d\n", DRM_CURRENTPID);
808
809         LOCK_TEST_WITH_RETURN(dev, file_priv);
810
811         if (sarea_priv->nbox > MACH64_NR_SAREA_CLIPRECTS)
812                 sarea_priv->nbox = MACH64_NR_SAREA_CLIPRECTS;
813
814         ret = mach64_dma_dispatch_swap(dev, file_priv);
815
816         /* Make sure we restore the 3D state next time.
817          */
818         sarea_priv->dirty |= (MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC);
819         return ret;
820 }
821
822 int mach64_dma_vertex(struct drm_device *dev, void *data,
823                       struct drm_file *file_priv)
824 {
825         drm_mach64_private_t *dev_priv = dev->dev_private;
826         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
827         drm_mach64_vertex_t *vertex = data;
828
829         LOCK_TEST_WITH_RETURN(dev, file_priv);
830
831         if (!dev_priv) {
832                 DRM_ERROR("called with no initialization\n");
833                 return -EINVAL;
834         }
835
836         DRM_DEBUG("pid=%d buf=%p used=%lu discard=%d\n",
837                   DRM_CURRENTPID,
838                   vertex->buf, vertex->used, vertex->discard);
839
840         if (vertex->prim < 0 || vertex->prim > MACH64_PRIM_POLYGON) {
841                 DRM_ERROR("buffer prim %d\n", vertex->prim);
842                 return -EINVAL;
843         }
844
845         if (vertex->used > MACH64_BUFFER_SIZE || (vertex->used & 3) != 0) {
846                 DRM_ERROR("Invalid vertex buffer size: %lu bytes\n",
847                           vertex->used);
848                 return -EINVAL;
849         }
850
851         if (sarea_priv->nbox > MACH64_NR_SAREA_CLIPRECTS)
852                 sarea_priv->nbox = MACH64_NR_SAREA_CLIPRECTS;
853
854         return mach64_dma_dispatch_vertex(dev, file_priv, vertex);
855 }
856
857 int mach64_dma_blit(struct drm_device *dev, void *data,
858                     struct drm_file *file_priv)
859 {
860         drm_mach64_private_t *dev_priv = dev->dev_private;
861         drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
862         drm_mach64_blit_t *blit = data;
863         int ret;
864
865         LOCK_TEST_WITH_RETURN(dev, file_priv);
866
867         ret = mach64_dma_dispatch_blit(dev, file_priv, blit);
868
869         /* Make sure we restore the 3D state next time.
870          */
871         sarea_priv->dirty |= (MACH64_UPLOAD_CONTEXT |
872                               MACH64_UPLOAD_MISC | MACH64_UPLOAD_CLIPRECTS);
873
874         return ret;
875 }
876
877 int mach64_get_param(struct drm_device *dev, void *data,
878                      struct drm_file *file_priv)
879 {
880         drm_mach64_private_t *dev_priv = dev->dev_private;
881         drm_mach64_getparam_t *param = data;
882         int value;
883
884         DRM_DEBUG("\n");
885
886         if (!dev_priv) {
887                 DRM_ERROR("called with no initialization\n");
888                 return -EINVAL;
889         }
890
891         switch (param->param) {
892         case MACH64_PARAM_FRAMES_QUEUED:
893                 /* Needs lock since it calls mach64_ring_tick() */
894                 LOCK_TEST_WITH_RETURN(dev, file_priv);
895                 value = mach64_do_get_frames_queued(dev_priv);
896                 break;
897         case MACH64_PARAM_IRQ_NR:
898                 value = dev->irq;
899                 break;
900         default:
901                 return -EINVAL;
902         }
903
904         if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) {
905                 DRM_ERROR("copy_to_user\n");
906                 return -EFAULT;
907         }
908
909         return 0;
910 }