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