Home | History | Annotate | Line # | Download | only in shared-core
      1 /* mach64_state.c -- State support for mach64 (Rage Pro) driver -*- linux-c -*-
      2  * Created: Sun Dec 03 19:20:26 2000 by gareth (at) 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 (at) valinux.com>
     29  *    Leif Delgass <ldelgass (at) retinalburn.net>
     30  *    Jos Fonseca <j_r_fonseca (at) 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 
     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_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 
    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 }
    911