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 "drmP.h"
34 #include "drm.h"
35 #include "mach64_drm.h"
36 #include "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 
mach64_print_dirty(const char * msg,unsigned int flags)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  */
mach64_emit_cliprect(struct drm_file * file_priv,drm_mach64_private_t * dev_priv,struct drm_clip_rect * box)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 
mach64_emit_state(struct drm_file * file_priv,drm_mach64_private_t * dev_priv)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 
mach64_dma_dispatch_clear(struct drm_device * dev,struct drm_file * file_priv,unsigned int flags,int cx,int cy,int cw,int ch,unsigned int clear_color,unsigned int clear_depth)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 
mach64_dma_dispatch_swap(struct drm_device * dev,struct drm_file * file_priv)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 
mach64_do_get_frames_queued(drm_mach64_private_t * dev_priv)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  */
copy_from_user_vertex(u32 * to,const u32 __user * ufrom,unsigned long bytes)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 
mach64_dma_dispatch_vertex(struct drm_device * dev,struct drm_file * file_priv,drm_mach64_vertex_t * vertex)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_NOOUT;
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_NOOUT(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 
copy_from_user_blit(u32 * to,const u32 __user * ufrom,unsigned long bytes)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 
mach64_dma_dispatch_blit(struct drm_device * dev,struct drm_file * file_priv,drm_mach64_blit_t * blit)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 
mach64_dma_clear(struct drm_device * dev,void * data,struct drm_file * file_priv)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 
mach64_dma_swap(struct drm_device * dev,void * data,struct drm_file * file_priv)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 
mach64_dma_vertex(struct drm_device * dev,void * data,struct drm_file * file_priv)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 
mach64_dma_blit(struct drm_device * dev,void * data,struct drm_file * file_priv)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 
mach64_get_param(struct drm_device * dev,void * data,struct drm_file * file_priv)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 }
911