xref: /netbsd-src/sys/external/bsd/drm/dist/shared-core/mach64_state.c (revision b94bb9347c3cebea87b7d6a137917f087c1d2ca7)
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