1145132Sanholt/* savage_state.c -- State and drawing support for Savage
2145132Sanholt *
3145132Sanholt * Copyright 2004  Felix Kuehling
4145132Sanholt * All Rights Reserved.
5145132Sanholt *
6145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a
7145132Sanholt * copy of this software and associated documentation files (the "Software"),
8145132Sanholt * to deal in the Software without restriction, including without limitation
9145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sub license,
10145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the
11145132Sanholt * Software is furnished to do so, subject to the following conditions:
12145132Sanholt *
13145132Sanholt * The above copyright notice and this permission notice (including the
14145132Sanholt * next paragraph) shall be included in all copies or substantial portions
15145132Sanholt * of the Software.
16145132Sanholt *
17145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18145132Sanholt * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19145132Sanholt * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20145132Sanholt * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING BE LIABLE FOR
21145132Sanholt * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22145132Sanholt * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23145132Sanholt * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24145132Sanholt */
25145132Sanholt
26152909Sanholt#include <sys/cdefs.h>
27152909Sanholt__FBSDID("$FreeBSD$");
28152909Sanholt#include "dev/drm/drmP.h"
29152909Sanholt#include "dev/drm/savage_drm.h"
30152909Sanholt#include "dev/drm/savage_drv.h"
31145132Sanholt
32145132Sanholtvoid savage_emit_clip_rect_s3d(drm_savage_private_t *dev_priv,
33182080Srnoland			       const struct drm_clip_rect *pbox)
34145132Sanholt{
35145132Sanholt	uint32_t scstart = dev_priv->state.s3d.new_scstart;
36182080Srnoland	uint32_t scend = dev_priv->state.s3d.new_scend;
37145132Sanholt	scstart = (scstart & ~SAVAGE_SCISSOR_MASK_S3D) |
38182080Srnoland		((uint32_t)pbox->x1 & 0x000007ff) |
39145132Sanholt		(((uint32_t)pbox->y1 << 16) & 0x07ff0000);
40182080Srnoland	scend   = (scend & ~SAVAGE_SCISSOR_MASK_S3D) |
41182080Srnoland		(((uint32_t)pbox->x2 - 1) & 0x000007ff) |
42182080Srnoland		((((uint32_t)pbox->y2 - 1) << 16) & 0x07ff0000);
43145132Sanholt	if (scstart != dev_priv->state.s3d.scstart ||
44145132Sanholt	    scend   != dev_priv->state.s3d.scend) {
45145132Sanholt		DMA_LOCALS;
46145132Sanholt		BEGIN_DMA(4);
47182080Srnoland		DMA_WRITE(BCI_CMD_WAIT | BCI_CMD_WAIT_3D);
48145132Sanholt		DMA_SET_REGISTERS(SAVAGE_SCSTART_S3D, 2);
49145132Sanholt		DMA_WRITE(scstart);
50145132Sanholt		DMA_WRITE(scend);
51145132Sanholt		dev_priv->state.s3d.scstart = scstart;
52182080Srnoland		dev_priv->state.s3d.scend = scend;
53145132Sanholt		dev_priv->waiting = 1;
54145132Sanholt		DMA_COMMIT();
55145132Sanholt	}
56145132Sanholt}
57145132Sanholt
58145132Sanholtvoid savage_emit_clip_rect_s4(drm_savage_private_t *dev_priv,
59182080Srnoland			      const struct drm_clip_rect *pbox)
60145132Sanholt{
61145132Sanholt	uint32_t drawctrl0 = dev_priv->state.s4.new_drawctrl0;
62145132Sanholt	uint32_t drawctrl1 = dev_priv->state.s4.new_drawctrl1;
63145132Sanholt	drawctrl0 = (drawctrl0 & ~SAVAGE_SCISSOR_MASK_S4) |
64145132Sanholt		((uint32_t)pbox->x1 & 0x000007ff) |
65145132Sanholt		(((uint32_t)pbox->y1 << 12) & 0x00fff000);
66145132Sanholt	drawctrl1 = (drawctrl1 & ~SAVAGE_SCISSOR_MASK_S4) |
67182080Srnoland		(((uint32_t)pbox->x2 - 1) & 0x000007ff) |
68182080Srnoland		((((uint32_t)pbox->y2 - 1) << 12) & 0x00fff000);
69145132Sanholt	if (drawctrl0 != dev_priv->state.s4.drawctrl0 ||
70145132Sanholt	    drawctrl1 != dev_priv->state.s4.drawctrl1) {
71145132Sanholt		DMA_LOCALS;
72145132Sanholt		BEGIN_DMA(4);
73182080Srnoland		DMA_WRITE(BCI_CMD_WAIT | BCI_CMD_WAIT_3D);
74145132Sanholt		DMA_SET_REGISTERS(SAVAGE_DRAWCTRL0_S4, 2);
75145132Sanholt		DMA_WRITE(drawctrl0);
76145132Sanholt		DMA_WRITE(drawctrl1);
77145132Sanholt		dev_priv->state.s4.drawctrl0 = drawctrl0;
78145132Sanholt		dev_priv->state.s4.drawctrl1 = drawctrl1;
79145132Sanholt		dev_priv->waiting = 1;
80145132Sanholt		DMA_COMMIT();
81145132Sanholt	}
82145132Sanholt}
83145132Sanholt
84145132Sanholtstatic int savage_verify_texaddr(drm_savage_private_t *dev_priv, int unit,
85145132Sanholt				 uint32_t addr)
86145132Sanholt{
87145132Sanholt	if ((addr & 6) != 2) { /* reserved bits */
88145132Sanholt		DRM_ERROR("bad texAddr%d %08x (reserved bits)\n", unit, addr);
89182080Srnoland		return -EINVAL;
90145132Sanholt	}
91145132Sanholt	if (!(addr & 1)) { /* local */
92145132Sanholt		addr &= ~7;
93182080Srnoland		if (addr < dev_priv->texture_offset ||
94182080Srnoland		    addr >= dev_priv->texture_offset + dev_priv->texture_size) {
95157617Sanholt			DRM_ERROR
96157617Sanholt			    ("bad texAddr%d %08x (local addr out of range)\n",
97157617Sanholt			     unit, addr);
98182080Srnoland			return -EINVAL;
99145132Sanholt		}
100145132Sanholt	} else { /* AGP */
101145132Sanholt		if (!dev_priv->agp_textures) {
102145132Sanholt			DRM_ERROR("bad texAddr%d %08x (AGP not available)\n",
103145132Sanholt				  unit, addr);
104182080Srnoland			return -EINVAL;
105145132Sanholt		}
106145132Sanholt		addr &= ~7;
107145132Sanholt		if (addr < dev_priv->agp_textures->offset ||
108145132Sanholt		    addr >= (dev_priv->agp_textures->offset +
109145132Sanholt			     dev_priv->agp_textures->size)) {
110157617Sanholt			DRM_ERROR
111157617Sanholt			    ("bad texAddr%d %08x (AGP addr out of range)\n",
112157617Sanholt			     unit, addr);
113182080Srnoland			return -EINVAL;
114145132Sanholt		}
115145132Sanholt	}
116145132Sanholt	return 0;
117145132Sanholt}
118145132Sanholt
119145132Sanholt#define SAVE_STATE(reg,where)			\
120182080Srnoland	if(start <= reg && start + count > reg)	\
121152909Sanholt		dev_priv->state.where = regs[reg - start]
122145132Sanholt#define SAVE_STATE_MASK(reg,where,mask) do {			\
123182080Srnoland	if(start <= reg && start + count > reg) {			\
124145132Sanholt		uint32_t tmp;					\
125152909Sanholt		tmp = regs[reg - start];			\
126145132Sanholt		dev_priv->state.where = (tmp & (mask)) |	\
127145132Sanholt			(dev_priv->state.where & ~(mask));	\
128145132Sanholt	}							\
129145132Sanholt} while (0)
130145132Sanholtstatic int savage_verify_state_s3d(drm_savage_private_t *dev_priv,
131145132Sanholt				   unsigned int start, unsigned int count,
132152909Sanholt				   const uint32_t *regs)
133145132Sanholt{
134145132Sanholt	if (start < SAVAGE_TEXPALADDR_S3D ||
135182080Srnoland	    start + count - 1 > SAVAGE_DESTTEXRWWATERMARK_S3D) {
136145132Sanholt		DRM_ERROR("invalid register range (0x%04x-0x%04x)\n",
137182080Srnoland			  start, start + count - 1);
138182080Srnoland		return -EINVAL;
139145132Sanholt	}
140145132Sanholt
141145132Sanholt	SAVE_STATE_MASK(SAVAGE_SCSTART_S3D, s3d.new_scstart,
142145132Sanholt			~SAVAGE_SCISSOR_MASK_S3D);
143145132Sanholt	SAVE_STATE_MASK(SAVAGE_SCEND_S3D, s3d.new_scend,
144145132Sanholt			~SAVAGE_SCISSOR_MASK_S3D);
145145132Sanholt
146145132Sanholt	/* if any texture regs were changed ... */
147145132Sanholt	if (start <= SAVAGE_TEXCTRL_S3D &&
148182080Srnoland	    start + count > SAVAGE_TEXPALADDR_S3D) {
149145132Sanholt		/* ... check texture state */
150145132Sanholt		SAVE_STATE(SAVAGE_TEXCTRL_S3D, s3d.texctrl);
151145132Sanholt		SAVE_STATE(SAVAGE_TEXADDR_S3D, s3d.texaddr);
152145132Sanholt		if (dev_priv->state.s3d.texctrl & SAVAGE_TEXCTRL_TEXEN_MASK)
153157617Sanholt			return savage_verify_texaddr(dev_priv, 0,
154157617Sanholt						dev_priv->state.s3d.texaddr);
155145132Sanholt	}
156145132Sanholt
157145132Sanholt	return 0;
158145132Sanholt}
159145132Sanholt
160145132Sanholtstatic int savage_verify_state_s4(drm_savage_private_t *dev_priv,
161145132Sanholt				  unsigned int start, unsigned int count,
162152909Sanholt				  const uint32_t *regs)
163145132Sanholt{
164145132Sanholt	int ret = 0;
165145132Sanholt
166145132Sanholt	if (start < SAVAGE_DRAWLOCALCTRL_S4 ||
167182080Srnoland	    start + count - 1 > SAVAGE_TEXBLENDCOLOR_S4) {
168145132Sanholt		DRM_ERROR("invalid register range (0x%04x-0x%04x)\n",
169182080Srnoland			  start, start + count - 1);
170182080Srnoland		return -EINVAL;
171145132Sanholt	}
172145132Sanholt
173145132Sanholt	SAVE_STATE_MASK(SAVAGE_DRAWCTRL0_S4, s4.new_drawctrl0,
174145132Sanholt			~SAVAGE_SCISSOR_MASK_S4);
175145132Sanholt	SAVE_STATE_MASK(SAVAGE_DRAWCTRL1_S4, s4.new_drawctrl1,
176145132Sanholt			~SAVAGE_SCISSOR_MASK_S4);
177145132Sanholt
178145132Sanholt	/* if any texture regs were changed ... */
179145132Sanholt	if (start <= SAVAGE_TEXDESCR_S4 &&
180157617Sanholt	    start + count > SAVAGE_TEXPALADDR_S4) {
181145132Sanholt		/* ... check texture state */
182145132Sanholt		SAVE_STATE(SAVAGE_TEXDESCR_S4, s4.texdescr);
183145132Sanholt		SAVE_STATE(SAVAGE_TEXADDR0_S4, s4.texaddr0);
184145132Sanholt		SAVE_STATE(SAVAGE_TEXADDR1_S4, s4.texaddr1);
185145132Sanholt		if (dev_priv->state.s4.texdescr & SAVAGE_TEXDESCR_TEX0EN_MASK)
186157617Sanholt			ret |= savage_verify_texaddr(dev_priv, 0,
187157617Sanholt						dev_priv->state.s4.texaddr0);
188145132Sanholt		if (dev_priv->state.s4.texdescr & SAVAGE_TEXDESCR_TEX1EN_MASK)
189157617Sanholt			ret |= savage_verify_texaddr(dev_priv, 1,
190157617Sanholt						dev_priv->state.s4.texaddr1);
191145132Sanholt	}
192145132Sanholt
193145132Sanholt	return ret;
194145132Sanholt}
195145132Sanholt#undef SAVE_STATE
196145132Sanholt#undef SAVE_STATE_MASK
197145132Sanholt
198145132Sanholtstatic int savage_dispatch_state(drm_savage_private_t *dev_priv,
199145132Sanholt				 const drm_savage_cmd_header_t *cmd_header,
200152909Sanholt				 const uint32_t *regs)
201145132Sanholt{
202145132Sanholt	unsigned int count = cmd_header->state.count;
203145132Sanholt	unsigned int start = cmd_header->state.start;
204145132Sanholt	unsigned int count2 = 0;
205145132Sanholt	unsigned int bci_size;
206145132Sanholt	int ret;
207152909Sanholt	DMA_LOCALS;
208145132Sanholt
209145132Sanholt	if (!count)
210145132Sanholt		return 0;
211145132Sanholt
212145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
213145132Sanholt		ret = savage_verify_state_s3d(dev_priv, start, count, regs);
214145132Sanholt		if (ret != 0)
215145132Sanholt			return ret;
216145132Sanholt		/* scissor regs are emitted in savage_dispatch_draw */
217145132Sanholt		if (start < SAVAGE_SCSTART_S3D) {
218182080Srnoland			if (start + count > SAVAGE_SCEND_S3D + 1)
219182080Srnoland				count2 = count - (SAVAGE_SCEND_S3D + 1 - start);
220182080Srnoland			if (start + count > SAVAGE_SCSTART_S3D)
221145132Sanholt				count = SAVAGE_SCSTART_S3D - start;
222145132Sanholt		} else if (start <= SAVAGE_SCEND_S3D) {
223182080Srnoland			if (start + count > SAVAGE_SCEND_S3D + 1) {
224182080Srnoland				count -= SAVAGE_SCEND_S3D + 1 - start;
225182080Srnoland				start = SAVAGE_SCEND_S3D + 1;
226145132Sanholt			} else
227145132Sanholt				return 0;
228145132Sanholt		}
229145132Sanholt	} else {
230145132Sanholt		ret = savage_verify_state_s4(dev_priv, start, count, regs);
231145132Sanholt		if (ret != 0)
232145132Sanholt			return ret;
233145132Sanholt		/* scissor regs are emitted in savage_dispatch_draw */
234145132Sanholt		if (start < SAVAGE_DRAWCTRL0_S4) {
235182080Srnoland			if (start + count > SAVAGE_DRAWCTRL1_S4 + 1)
236157617Sanholt				count2 = count -
237157617Sanholt					 (SAVAGE_DRAWCTRL1_S4 + 1 - start);
238182080Srnoland			if (start + count > SAVAGE_DRAWCTRL0_S4)
239145132Sanholt				count = SAVAGE_DRAWCTRL0_S4 - start;
240145132Sanholt		} else if (start <= SAVAGE_DRAWCTRL1_S4) {
241182080Srnoland			if (start + count > SAVAGE_DRAWCTRL1_S4 + 1) {
242182080Srnoland				count -= SAVAGE_DRAWCTRL1_S4 + 1 - start;
243182080Srnoland				start = SAVAGE_DRAWCTRL1_S4 + 1;
244145132Sanholt			} else
245145132Sanholt				return 0;
246145132Sanholt		}
247145132Sanholt	}
248145132Sanholt
249182080Srnoland	bci_size = count + (count + 254) / 255 + count2 + (count2 + 254) / 255;
250145132Sanholt
251145132Sanholt	if (cmd_header->state.global) {
252182080Srnoland		BEGIN_DMA(bci_size + 1);
253145132Sanholt		DMA_WRITE(BCI_CMD_WAIT | BCI_CMD_WAIT_3D);
254145132Sanholt		dev_priv->waiting = 1;
255145132Sanholt	} else {
256145132Sanholt		BEGIN_DMA(bci_size);
257145132Sanholt	}
258145132Sanholt
259145132Sanholt	do {
260145132Sanholt		while (count > 0) {
261145132Sanholt			unsigned int n = count < 255 ? count : 255;
262145132Sanholt			DMA_SET_REGISTERS(start, n);
263152909Sanholt			DMA_COPY(regs, n);
264145132Sanholt			count -= n;
265145132Sanholt			start += n;
266145132Sanholt			regs += n;
267145132Sanholt		}
268145132Sanholt		start += 2;
269145132Sanholt		regs += 2;
270145132Sanholt		count = count2;
271145132Sanholt		count2 = 0;
272145132Sanholt	} while (count);
273145132Sanholt
274145132Sanholt	DMA_COMMIT();
275145132Sanholt
276145132Sanholt	return 0;
277145132Sanholt}
278145132Sanholt
279145132Sanholtstatic int savage_dispatch_dma_prim(drm_savage_private_t *dev_priv,
280145132Sanholt				    const drm_savage_cmd_header_t *cmd_header,
281182080Srnoland				    const struct drm_buf *dmabuf)
282145132Sanholt{
283145132Sanholt	unsigned char reorder = 0;
284145132Sanholt	unsigned int prim = cmd_header->prim.prim;
285145132Sanholt	unsigned int skip = cmd_header->prim.skip;
286145132Sanholt	unsigned int n = cmd_header->prim.count;
287145132Sanholt	unsigned int start = cmd_header->prim.start;
288145132Sanholt	unsigned int i;
289152909Sanholt	BCI_LOCALS;
290145132Sanholt
291145132Sanholt	if (!dmabuf) {
292182080Srnoland		DRM_ERROR("called without dma buffers!\n");
293182080Srnoland		return -EINVAL;
294145132Sanholt	}
295145132Sanholt
296145132Sanholt	if (!n)
297145132Sanholt		return 0;
298145132Sanholt
299145132Sanholt	switch (prim) {
300145132Sanholt	case SAVAGE_PRIM_TRILIST_201:
301145132Sanholt		reorder = 1;
302145132Sanholt		prim = SAVAGE_PRIM_TRILIST;
303145132Sanholt	case SAVAGE_PRIM_TRILIST:
304145132Sanholt		if (n % 3 != 0) {
305145132Sanholt			DRM_ERROR("wrong number of vertices %u in TRILIST\n",
306145132Sanholt				  n);
307182080Srnoland			return -EINVAL;
308145132Sanholt		}
309145132Sanholt		break;
310145132Sanholt	case SAVAGE_PRIM_TRISTRIP:
311145132Sanholt	case SAVAGE_PRIM_TRIFAN:
312145132Sanholt		if (n < 3) {
313157617Sanholt			DRM_ERROR
314157617Sanholt			   ("wrong number of vertices %u in TRIFAN/STRIP\n",
315157617Sanholt			    n);
316182080Srnoland			return -EINVAL;
317145132Sanholt		}
318145132Sanholt		break;
319145132Sanholt	default:
320145132Sanholt		DRM_ERROR("invalid primitive type %u\n", prim);
321182080Srnoland		return -EINVAL;
322145132Sanholt	}
323145132Sanholt
324145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
325145132Sanholt		if (skip != 0) {
326157617Sanholt			DRM_ERROR("invalid skip flags 0x%04x for DMA\n", skip);
327182080Srnoland			return -EINVAL;
328145132Sanholt		}
329145132Sanholt	} else {
330145132Sanholt		unsigned int size = 10 - (skip & 1) - (skip >> 1 & 1) -
331145132Sanholt			(skip >> 2 & 1) - (skip >> 3 & 1) - (skip >> 4 & 1) -
332145132Sanholt			(skip >> 5 & 1) - (skip >> 6 & 1) - (skip >> 7 & 1);
333145132Sanholt		if (skip > SAVAGE_SKIP_ALL_S4 || size != 8) {
334157617Sanholt			DRM_ERROR("invalid skip flags 0x%04x for DMA\n", skip);
335182080Srnoland			return -EINVAL;
336145132Sanholt		}
337145132Sanholt		if (reorder) {
338145132Sanholt			DRM_ERROR("TRILIST_201 used on Savage4 hardware\n");
339182080Srnoland			return -EINVAL;
340145132Sanholt		}
341145132Sanholt	}
342145132Sanholt
343182080Srnoland	if (start + n > dmabuf->total / 32) {
344145132Sanholt		DRM_ERROR("vertex indices (%u-%u) out of range (0-%u)\n",
345182080Srnoland			  start, start + n - 1, dmabuf->total / 32);
346182080Srnoland		return -EINVAL;
347145132Sanholt	}
348145132Sanholt
349145132Sanholt	/* Vertex DMA doesn't work with command DMA at the same time,
350145132Sanholt	 * so we use BCI_... to submit commands here. Flush buffered
351145132Sanholt	 * faked DMA first. */
352145132Sanholt	DMA_FLUSH();
353145132Sanholt
354145132Sanholt	if (dmabuf->bus_address != dev_priv->state.common.vbaddr) {
355145132Sanholt		BEGIN_BCI(2);
356145132Sanholt		BCI_SET_REGISTERS(SAVAGE_VERTBUFADDR, 1);
357145132Sanholt		BCI_WRITE(dmabuf->bus_address | dev_priv->dma_type);
358145132Sanholt		dev_priv->state.common.vbaddr = dmabuf->bus_address;
359145132Sanholt	}
360145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset) && dev_priv->waiting) {
361145132Sanholt		/* Workaround for what looks like a hardware bug. If a
362145132Sanholt		 * WAIT_3D_IDLE was emitted some time before the
363145132Sanholt		 * indexed drawing command then the engine will lock
364145132Sanholt		 * up. There are two known workarounds:
365145132Sanholt		 * WAIT_IDLE_EMPTY or emit at least 63 NOPs. */
366145132Sanholt		BEGIN_BCI(63);
367145132Sanholt		for (i = 0; i < 63; ++i)
368145132Sanholt			BCI_WRITE(BCI_CMD_WAIT);
369145132Sanholt		dev_priv->waiting = 0;
370145132Sanholt	}
371145132Sanholt
372145132Sanholt	prim <<= 25;
373145132Sanholt	while (n != 0) {
374145132Sanholt		/* Can emit up to 255 indices (85 triangles) at once. */
375145132Sanholt		unsigned int count = n > 255 ? 255 : n;
376145132Sanholt		if (reorder) {
377145132Sanholt			/* Need to reorder indices for correct flat
378145132Sanholt			 * shading while preserving the clock sense
379145132Sanholt			 * for correct culling. Only on Savage3D. */
380182080Srnoland			int reorder[3] = { -1, -1, -1 };
381182080Srnoland			reorder[start % 3] = 2;
382145132Sanholt
383182080Srnoland			BEGIN_BCI((count + 1 + 1) / 2);
384182080Srnoland			BCI_DRAW_INDICES_S3D(count, prim, start + 2);
385145132Sanholt
386182080Srnoland			for (i = start + 1; i + 1 < start + count; i += 2)
387145132Sanholt				BCI_WRITE((i + reorder[i % 3]) |
388157617Sanholt					  ((i + 1 +
389157617Sanholt					    reorder[(i + 1) % 3]) << 16));
390182080Srnoland			if (i < start + count)
391182080Srnoland				BCI_WRITE(i + reorder[i % 3]);
392145132Sanholt		} else if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
393182080Srnoland			BEGIN_BCI((count + 1 + 1) / 2);
394145132Sanholt			BCI_DRAW_INDICES_S3D(count, prim, start);
395145132Sanholt
396182080Srnoland			for (i = start + 1; i + 1 < start + count; i += 2)
397182080Srnoland				BCI_WRITE(i | ((i + 1) << 16));
398182080Srnoland			if (i < start + count)
399145132Sanholt				BCI_WRITE(i);
400145132Sanholt		} else {
401182080Srnoland			BEGIN_BCI((count + 2 + 1) / 2);
402145132Sanholt			BCI_DRAW_INDICES_S4(count, prim, skip);
403145132Sanholt
404182080Srnoland			for (i = start; i + 1 < start + count; i += 2)
405182080Srnoland				BCI_WRITE(i | ((i + 1) << 16));
406182080Srnoland			if (i < start + count)
407145132Sanholt				BCI_WRITE(i);
408145132Sanholt		}
409145132Sanholt
410145132Sanholt		start += count;
411145132Sanholt		n -= count;
412145132Sanholt
413145132Sanholt		prim |= BCI_CMD_DRAW_CONT;
414145132Sanholt	}
415145132Sanholt
416145132Sanholt	return 0;
417145132Sanholt}
418145132Sanholt
419145132Sanholtstatic int savage_dispatch_vb_prim(drm_savage_private_t *dev_priv,
420145132Sanholt				   const drm_savage_cmd_header_t *cmd_header,
421152909Sanholt				   const uint32_t *vtxbuf, unsigned int vb_size,
422145132Sanholt				   unsigned int vb_stride)
423145132Sanholt{
424145132Sanholt	unsigned char reorder = 0;
425145132Sanholt	unsigned int prim = cmd_header->prim.prim;
426145132Sanholt	unsigned int skip = cmd_header->prim.skip;
427145132Sanholt	unsigned int n = cmd_header->prim.count;
428145132Sanholt	unsigned int start = cmd_header->prim.start;
429145132Sanholt	unsigned int vtx_size;
430145132Sanholt	unsigned int i;
431152909Sanholt	DMA_LOCALS;
432145132Sanholt
433145132Sanholt	if (!n)
434145132Sanholt		return 0;
435145132Sanholt
436145132Sanholt	switch (prim) {
437145132Sanholt	case SAVAGE_PRIM_TRILIST_201:
438145132Sanholt		reorder = 1;
439145132Sanholt		prim = SAVAGE_PRIM_TRILIST;
440145132Sanholt	case SAVAGE_PRIM_TRILIST:
441145132Sanholt		if (n % 3 != 0) {
442145132Sanholt			DRM_ERROR("wrong number of vertices %u in TRILIST\n",
443145132Sanholt				  n);
444182080Srnoland			return -EINVAL;
445145132Sanholt		}
446145132Sanholt		break;
447145132Sanholt	case SAVAGE_PRIM_TRISTRIP:
448145132Sanholt	case SAVAGE_PRIM_TRIFAN:
449145132Sanholt		if (n < 3) {
450157617Sanholt			DRM_ERROR
451157617Sanholt			    ("wrong number of vertices %u in TRIFAN/STRIP\n",
452157617Sanholt			     n);
453182080Srnoland			return -EINVAL;
454145132Sanholt		}
455145132Sanholt		break;
456145132Sanholt	default:
457145132Sanholt		DRM_ERROR("invalid primitive type %u\n", prim);
458182080Srnoland		return -EINVAL;
459145132Sanholt	}
460145132Sanholt
461145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
462145132Sanholt		if (skip > SAVAGE_SKIP_ALL_S3D) {
463145132Sanholt			DRM_ERROR("invalid skip flags 0x%04x\n", skip);
464182080Srnoland			return -EINVAL;
465145132Sanholt		}
466145132Sanholt		vtx_size = 8; /* full vertex */
467145132Sanholt	} else {
468145132Sanholt		if (skip > SAVAGE_SKIP_ALL_S4) {
469145132Sanholt			DRM_ERROR("invalid skip flags 0x%04x\n", skip);
470182080Srnoland			return -EINVAL;
471145132Sanholt		}
472145132Sanholt		vtx_size = 10; /* full vertex */
473145132Sanholt	}
474145132Sanholt
475145132Sanholt	vtx_size -= (skip & 1) + (skip >> 1 & 1) +
476145132Sanholt		(skip >> 2 & 1) + (skip >> 3 & 1) + (skip >> 4 & 1) +
477145132Sanholt		(skip >> 5 & 1) + (skip >> 6 & 1) + (skip >> 7 & 1);
478145132Sanholt
479145132Sanholt	if (vtx_size > vb_stride) {
480145132Sanholt		DRM_ERROR("vertex size greater than vb stride (%u > %u)\n",
481145132Sanholt			  vtx_size, vb_stride);
482182080Srnoland		return -EINVAL;
483145132Sanholt	}
484145132Sanholt
485182080Srnoland	if (start + n > vb_size / (vb_stride * 4)) {
486145132Sanholt		DRM_ERROR("vertex indices (%u-%u) out of range (0-%u)\n",
487182080Srnoland			  start, start + n - 1, vb_size / (vb_stride * 4));
488182080Srnoland		return -EINVAL;
489145132Sanholt	}
490145132Sanholt
491145132Sanholt	prim <<= 25;
492145132Sanholt	while (n != 0) {
493145132Sanholt		/* Can emit up to 255 vertices (85 triangles) at once. */
494145132Sanholt		unsigned int count = n > 255 ? 255 : n;
495145132Sanholt		if (reorder) {
496145132Sanholt			/* Need to reorder vertices for correct flat
497145132Sanholt			 * shading while preserving the clock sense
498145132Sanholt			 * for correct culling. Only on Savage3D. */
499182080Srnoland			int reorder[3] = { -1, -1, -1 };
500182080Srnoland			reorder[start % 3] = 2;
501145132Sanholt
502182080Srnoland			BEGIN_DMA(count * vtx_size + 1);
503145132Sanholt			DMA_DRAW_PRIMITIVE(count, prim, skip);
504145132Sanholt
505182080Srnoland			for (i = start; i < start + count; ++i) {
506145132Sanholt				unsigned int j = i + reorder[i % 3];
507182080Srnoland				DMA_COPY(&vtxbuf[vb_stride * j], vtx_size);
508145132Sanholt			}
509145132Sanholt
510145132Sanholt			DMA_COMMIT();
511145132Sanholt		} else {
512182080Srnoland			BEGIN_DMA(count * vtx_size + 1);
513145132Sanholt			DMA_DRAW_PRIMITIVE(count, prim, skip);
514145132Sanholt
515145132Sanholt			if (vb_stride == vtx_size) {
516182080Srnoland				DMA_COPY(&vtxbuf[vb_stride * start],
517182080Srnoland					 vtx_size * count);
518145132Sanholt			} else {
519182080Srnoland				for (i = start; i < start + count; ++i) {
520182080Srnoland					DMA_COPY(&vtxbuf[vb_stride * i],
521152909Sanholt						 vtx_size);
522145132Sanholt				}
523145132Sanholt			}
524145132Sanholt
525145132Sanholt			DMA_COMMIT();
526145132Sanholt		}
527145132Sanholt
528145132Sanholt		start += count;
529145132Sanholt		n -= count;
530145132Sanholt
531145132Sanholt		prim |= BCI_CMD_DRAW_CONT;
532145132Sanholt	}
533145132Sanholt
534145132Sanholt	return 0;
535145132Sanholt}
536145132Sanholt
537145132Sanholtstatic int savage_dispatch_dma_idx(drm_savage_private_t *dev_priv,
538145132Sanholt				   const drm_savage_cmd_header_t *cmd_header,
539152909Sanholt				   const uint16_t *idx,
540182080Srnoland				   const struct drm_buf *dmabuf)
541145132Sanholt{
542145132Sanholt	unsigned char reorder = 0;
543145132Sanholt	unsigned int prim = cmd_header->idx.prim;
544145132Sanholt	unsigned int skip = cmd_header->idx.skip;
545145132Sanholt	unsigned int n = cmd_header->idx.count;
546145132Sanholt	unsigned int i;
547152909Sanholt	BCI_LOCALS;
548145132Sanholt
549145132Sanholt	if (!dmabuf) {
550182080Srnoland		DRM_ERROR("called without dma buffers!\n");
551182080Srnoland		return -EINVAL;
552145132Sanholt	}
553145132Sanholt
554145132Sanholt	if (!n)
555145132Sanholt		return 0;
556145132Sanholt
557145132Sanholt	switch (prim) {
558145132Sanholt	case SAVAGE_PRIM_TRILIST_201:
559145132Sanholt		reorder = 1;
560145132Sanholt		prim = SAVAGE_PRIM_TRILIST;
561145132Sanholt	case SAVAGE_PRIM_TRILIST:
562145132Sanholt		if (n % 3 != 0) {
563157617Sanholt			DRM_ERROR("wrong number of indices %u in TRILIST\n", n);
564182080Srnoland			return -EINVAL;
565145132Sanholt		}
566145132Sanholt		break;
567145132Sanholt	case SAVAGE_PRIM_TRISTRIP:
568145132Sanholt	case SAVAGE_PRIM_TRIFAN:
569145132Sanholt		if (n < 3) {
570157617Sanholt			DRM_ERROR
571157617Sanholt			    ("wrong number of indices %u in TRIFAN/STRIP\n", n);
572182080Srnoland			return -EINVAL;
573145132Sanholt		}
574145132Sanholt		break;
575145132Sanholt	default:
576145132Sanholt		DRM_ERROR("invalid primitive type %u\n", prim);
577182080Srnoland		return -EINVAL;
578145132Sanholt	}
579145132Sanholt
580145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
581145132Sanholt		if (skip != 0) {
582157617Sanholt			DRM_ERROR("invalid skip flags 0x%04x for DMA\n", skip);
583182080Srnoland			return -EINVAL;
584145132Sanholt		}
585145132Sanholt	} else {
586145132Sanholt		unsigned int size = 10 - (skip & 1) - (skip >> 1 & 1) -
587145132Sanholt			(skip >> 2 & 1) - (skip >> 3 & 1) - (skip >> 4 & 1) -
588145132Sanholt			(skip >> 5 & 1) - (skip >> 6 & 1) - (skip >> 7 & 1);
589145132Sanholt		if (skip > SAVAGE_SKIP_ALL_S4 || size != 8) {
590157617Sanholt			DRM_ERROR("invalid skip flags 0x%04x for DMA\n", skip);
591182080Srnoland			return -EINVAL;
592145132Sanholt		}
593145132Sanholt		if (reorder) {
594145132Sanholt			DRM_ERROR("TRILIST_201 used on Savage4 hardware\n");
595182080Srnoland			return -EINVAL;
596145132Sanholt		}
597145132Sanholt	}
598145132Sanholt
599145132Sanholt	/* Vertex DMA doesn't work with command DMA at the same time,
600145132Sanholt	 * so we use BCI_... to submit commands here. Flush buffered
601145132Sanholt	 * faked DMA first. */
602145132Sanholt	DMA_FLUSH();
603145132Sanholt
604145132Sanholt	if (dmabuf->bus_address != dev_priv->state.common.vbaddr) {
605145132Sanholt		BEGIN_BCI(2);
606145132Sanholt		BCI_SET_REGISTERS(SAVAGE_VERTBUFADDR, 1);
607145132Sanholt		BCI_WRITE(dmabuf->bus_address | dev_priv->dma_type);
608145132Sanholt		dev_priv->state.common.vbaddr = dmabuf->bus_address;
609145132Sanholt	}
610145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset) && dev_priv->waiting) {
611145132Sanholt		/* Workaround for what looks like a hardware bug. If a
612145132Sanholt		 * WAIT_3D_IDLE was emitted some time before the
613145132Sanholt		 * indexed drawing command then the engine will lock
614145132Sanholt		 * up. There are two known workarounds:
615145132Sanholt		 * WAIT_IDLE_EMPTY or emit at least 63 NOPs. */
616145132Sanholt		BEGIN_BCI(63);
617145132Sanholt		for (i = 0; i < 63; ++i)
618145132Sanholt			BCI_WRITE(BCI_CMD_WAIT);
619145132Sanholt		dev_priv->waiting = 0;
620145132Sanholt	}
621145132Sanholt
622145132Sanholt	prim <<= 25;
623145132Sanholt	while (n != 0) {
624145132Sanholt		/* Can emit up to 255 indices (85 triangles) at once. */
625145132Sanholt		unsigned int count = n > 255 ? 255 : n;
626145132Sanholt
627152909Sanholt		/* check indices */
628145132Sanholt		for (i = 0; i < count; ++i) {
629182080Srnoland			if (idx[i] > dmabuf->total / 32) {
630145132Sanholt				DRM_ERROR("idx[%u]=%u out of range (0-%u)\n",
631182080Srnoland					  i, idx[i], dmabuf->total / 32);
632182080Srnoland				return -EINVAL;
633145132Sanholt			}
634145132Sanholt		}
635145132Sanholt
636145132Sanholt		if (reorder) {
637145132Sanholt			/* Need to reorder indices for correct flat
638145132Sanholt			 * shading while preserving the clock sense
639145132Sanholt			 * for correct culling. Only on Savage3D. */
640182080Srnoland			int reorder[3] = { 2, -1, -1 };
641145132Sanholt
642182080Srnoland			BEGIN_BCI((count + 1 + 1) / 2);
643145132Sanholt			BCI_DRAW_INDICES_S3D(count, prim, idx[2]);
644145132Sanholt
645182080Srnoland			for (i = 1; i + 1 < count; i += 2)
646145132Sanholt				BCI_WRITE(idx[i + reorder[i % 3]] |
647157617Sanholt					  (idx[i + 1 +
648157617Sanholt					   reorder[(i + 1) % 3]] << 16));
649145132Sanholt			if (i < count)
650182080Srnoland				BCI_WRITE(idx[i + reorder[i % 3]]);
651145132Sanholt		} else if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
652182080Srnoland			BEGIN_BCI((count + 1 + 1) / 2);
653145132Sanholt			BCI_DRAW_INDICES_S3D(count, prim, idx[0]);
654145132Sanholt
655182080Srnoland			for (i = 1; i + 1 < count; i += 2)
656182080Srnoland				BCI_WRITE(idx[i] | (idx[i + 1] << 16));
657145132Sanholt			if (i < count)
658145132Sanholt				BCI_WRITE(idx[i]);
659145132Sanholt		} else {
660182080Srnoland			BEGIN_BCI((count + 2 + 1) / 2);
661145132Sanholt			BCI_DRAW_INDICES_S4(count, prim, skip);
662145132Sanholt
663182080Srnoland			for (i = 0; i + 1 < count; i += 2)
664182080Srnoland				BCI_WRITE(idx[i] | (idx[i + 1] << 16));
665145132Sanholt			if (i < count)
666145132Sanholt				BCI_WRITE(idx[i]);
667145132Sanholt		}
668145132Sanholt
669152909Sanholt		idx += count;
670145132Sanholt		n -= count;
671145132Sanholt
672145132Sanholt		prim |= BCI_CMD_DRAW_CONT;
673145132Sanholt	}
674145132Sanholt
675145132Sanholt	return 0;
676145132Sanholt}
677145132Sanholt
678145132Sanholtstatic int savage_dispatch_vb_idx(drm_savage_private_t *dev_priv,
679145132Sanholt				  const drm_savage_cmd_header_t *cmd_header,
680152909Sanholt				  const uint16_t *idx,
681152909Sanholt				  const uint32_t *vtxbuf,
682157617Sanholt				  unsigned int vb_size, unsigned int vb_stride)
683145132Sanholt{
684145132Sanholt	unsigned char reorder = 0;
685145132Sanholt	unsigned int prim = cmd_header->idx.prim;
686145132Sanholt	unsigned int skip = cmd_header->idx.skip;
687145132Sanholt	unsigned int n = cmd_header->idx.count;
688145132Sanholt	unsigned int vtx_size;
689145132Sanholt	unsigned int i;
690152909Sanholt	DMA_LOCALS;
691145132Sanholt
692145132Sanholt	if (!n)
693145132Sanholt		return 0;
694145132Sanholt
695145132Sanholt	switch (prim) {
696145132Sanholt	case SAVAGE_PRIM_TRILIST_201:
697145132Sanholt		reorder = 1;
698145132Sanholt		prim = SAVAGE_PRIM_TRILIST;
699145132Sanholt	case SAVAGE_PRIM_TRILIST:
700145132Sanholt		if (n % 3 != 0) {
701157617Sanholt			DRM_ERROR("wrong number of indices %u in TRILIST\n", n);
702182080Srnoland			return -EINVAL;
703145132Sanholt		}
704145132Sanholt		break;
705145132Sanholt	case SAVAGE_PRIM_TRISTRIP:
706145132Sanholt	case SAVAGE_PRIM_TRIFAN:
707145132Sanholt		if (n < 3) {
708157617Sanholt			DRM_ERROR
709157617Sanholt			    ("wrong number of indices %u in TRIFAN/STRIP\n", n);
710182080Srnoland			return -EINVAL;
711145132Sanholt		}
712145132Sanholt		break;
713145132Sanholt	default:
714145132Sanholt		DRM_ERROR("invalid primitive type %u\n", prim);
715182080Srnoland		return -EINVAL;
716145132Sanholt	}
717145132Sanholt
718145132Sanholt	if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
719145132Sanholt		if (skip > SAVAGE_SKIP_ALL_S3D) {
720145132Sanholt			DRM_ERROR("invalid skip flags 0x%04x\n", skip);
721182080Srnoland			return -EINVAL;
722145132Sanholt		}
723145132Sanholt		vtx_size = 8; /* full vertex */
724145132Sanholt	} else {
725145132Sanholt		if (skip > SAVAGE_SKIP_ALL_S4) {
726145132Sanholt			DRM_ERROR("invalid skip flags 0x%04x\n", skip);
727182080Srnoland			return -EINVAL;
728145132Sanholt		}
729145132Sanholt		vtx_size = 10; /* full vertex */
730145132Sanholt	}
731145132Sanholt
732145132Sanholt	vtx_size -= (skip & 1) + (skip >> 1 & 1) +
733145132Sanholt		(skip >> 2 & 1) + (skip >> 3 & 1) + (skip >> 4 & 1) +
734145132Sanholt		(skip >> 5 & 1) + (skip >> 6 & 1) + (skip >> 7 & 1);
735145132Sanholt
736145132Sanholt	if (vtx_size > vb_stride) {
737145132Sanholt		DRM_ERROR("vertex size greater than vb stride (%u > %u)\n",
738145132Sanholt			  vtx_size, vb_stride);
739182080Srnoland		return -EINVAL;
740145132Sanholt	}
741145132Sanholt
742145132Sanholt	prim <<= 25;
743145132Sanholt	while (n != 0) {
744145132Sanholt		/* Can emit up to 255 vertices (85 triangles) at once. */
745145132Sanholt		unsigned int count = n > 255 ? 255 : n;
746145132Sanholt
747152909Sanholt		/* Check indices */
748145132Sanholt		for (i = 0; i < count; ++i) {
749182080Srnoland			if (idx[i] > vb_size / (vb_stride * 4)) {
750145132Sanholt				DRM_ERROR("idx[%u]=%u out of range (0-%u)\n",
751182080Srnoland					  i, idx[i],  vb_size / (vb_stride * 4));
752182080Srnoland				return -EINVAL;
753145132Sanholt			}
754145132Sanholt		}
755145132Sanholt
756145132Sanholt		if (reorder) {
757145132Sanholt			/* Need to reorder vertices for correct flat
758145132Sanholt			 * shading while preserving the clock sense
759145132Sanholt			 * for correct culling. Only on Savage3D. */
760182080Srnoland			int reorder[3] = { 2, -1, -1 };
761145132Sanholt
762182080Srnoland			BEGIN_DMA(count * vtx_size + 1);
763145132Sanholt			DMA_DRAW_PRIMITIVE(count, prim, skip);
764145132Sanholt
765145132Sanholt			for (i = 0; i < count; ++i) {
766145132Sanholt				unsigned int j = idx[i + reorder[i % 3]];
767182080Srnoland				DMA_COPY(&vtxbuf[vb_stride * j], vtx_size);
768145132Sanholt			}
769145132Sanholt
770145132Sanholt			DMA_COMMIT();
771145132Sanholt		} else {
772182080Srnoland			BEGIN_DMA(count * vtx_size + 1);
773145132Sanholt			DMA_DRAW_PRIMITIVE(count, prim, skip);
774145132Sanholt
775145132Sanholt			for (i = 0; i < count; ++i) {
776145132Sanholt				unsigned int j = idx[i];
777182080Srnoland				DMA_COPY(&vtxbuf[vb_stride * j], vtx_size);
778145132Sanholt			}
779145132Sanholt
780145132Sanholt			DMA_COMMIT();
781145132Sanholt		}
782145132Sanholt
783152909Sanholt		idx += count;
784145132Sanholt		n -= count;
785145132Sanholt
786145132Sanholt		prim |= BCI_CMD_DRAW_CONT;
787145132Sanholt	}
788145132Sanholt
789145132Sanholt	return 0;
790145132Sanholt}
791145132Sanholt
792145132Sanholtstatic int savage_dispatch_clear(drm_savage_private_t *dev_priv,
793145132Sanholt				 const drm_savage_cmd_header_t *cmd_header,
794152909Sanholt				 const drm_savage_cmd_header_t *data,
795145132Sanholt				 unsigned int nbox,
796182080Srnoland				 const struct drm_clip_rect *boxes)
797145132Sanholt{
798152909Sanholt	unsigned int flags = cmd_header->clear0.flags;
799145132Sanholt	unsigned int clear_cmd;
800145132Sanholt	unsigned int i, nbufs;
801152909Sanholt	DMA_LOCALS;
802145132Sanholt
803145132Sanholt	if (nbox == 0)
804145132Sanholt		return 0;
805145132Sanholt
806145132Sanholt	clear_cmd = BCI_CMD_RECT | BCI_CMD_RECT_XP | BCI_CMD_RECT_YP |
807145132Sanholt		BCI_CMD_SEND_COLOR | BCI_CMD_DEST_PBD_NEW;
808145132Sanholt	BCI_CMD_SET_ROP(clear_cmd,0xCC);
809145132Sanholt
810145132Sanholt	nbufs = ((flags & SAVAGE_FRONT) ? 1 : 0) +
811157617Sanholt	    ((flags & SAVAGE_BACK) ? 1 : 0) + ((flags & SAVAGE_DEPTH) ? 1 : 0);
812145132Sanholt	if (nbufs == 0)
813145132Sanholt		return 0;
814145132Sanholt
815152909Sanholt	if (data->clear1.mask != 0xffffffff) {
816145132Sanholt		/* set mask */
817145132Sanholt		BEGIN_DMA(2);
818145132Sanholt		DMA_SET_REGISTERS(SAVAGE_BITPLANEWTMASK, 1);
819152909Sanholt		DMA_WRITE(data->clear1.mask);
820145132Sanholt		DMA_COMMIT();
821145132Sanholt	}
822145132Sanholt	for (i = 0; i < nbox; ++i) {
823145132Sanholt		unsigned int x, y, w, h;
824145132Sanholt		unsigned int buf;
825152909Sanholt
826152909Sanholt		x = boxes[i].x1, y = boxes[i].y1;
827152909Sanholt		w = boxes[i].x2 - boxes[i].x1;
828152909Sanholt		h = boxes[i].y2 - boxes[i].y1;
829182080Srnoland		BEGIN_DMA(nbufs * 6);
830145132Sanholt		for (buf = SAVAGE_FRONT; buf <= SAVAGE_DEPTH; buf <<= 1) {
831145132Sanholt			if (!(flags & buf))
832145132Sanholt				continue;
833145132Sanholt			DMA_WRITE(clear_cmd);
834182080Srnoland			switch (buf) {
835145132Sanholt			case SAVAGE_FRONT:
836145132Sanholt				DMA_WRITE(dev_priv->front_offset);
837145132Sanholt				DMA_WRITE(dev_priv->front_bd);
838145132Sanholt				break;
839145132Sanholt			case SAVAGE_BACK:
840145132Sanholt				DMA_WRITE(dev_priv->back_offset);
841145132Sanholt				DMA_WRITE(dev_priv->back_bd);
842145132Sanholt				break;
843145132Sanholt			case SAVAGE_DEPTH:
844145132Sanholt				DMA_WRITE(dev_priv->depth_offset);
845145132Sanholt				DMA_WRITE(dev_priv->depth_bd);
846145132Sanholt				break;
847145132Sanholt			}
848152909Sanholt			DMA_WRITE(data->clear1.value);
849145132Sanholt			DMA_WRITE(BCI_X_Y(x, y));
850145132Sanholt			DMA_WRITE(BCI_W_H(w, h));
851145132Sanholt		}
852145132Sanholt		DMA_COMMIT();
853145132Sanholt	}
854152909Sanholt	if (data->clear1.mask != 0xffffffff) {
855145132Sanholt		/* reset mask */
856145132Sanholt		BEGIN_DMA(2);
857145132Sanholt		DMA_SET_REGISTERS(SAVAGE_BITPLANEWTMASK, 1);
858145132Sanholt		DMA_WRITE(0xffffffff);
859145132Sanholt		DMA_COMMIT();
860145132Sanholt	}
861145132Sanholt
862145132Sanholt	return 0;
863145132Sanholt}
864145132Sanholt
865145132Sanholtstatic int savage_dispatch_swap(drm_savage_private_t *dev_priv,
866182080Srnoland				unsigned int nbox, const struct drm_clip_rect *boxes)
867145132Sanholt{
868145132Sanholt	unsigned int swap_cmd;
869145132Sanholt	unsigned int i;
870152909Sanholt	DMA_LOCALS;
871145132Sanholt
872145132Sanholt	if (nbox == 0)
873145132Sanholt		return 0;
874145132Sanholt
875145132Sanholt	swap_cmd = BCI_CMD_RECT | BCI_CMD_RECT_XP | BCI_CMD_RECT_YP |
876145132Sanholt		BCI_CMD_SRC_PBD_COLOR_NEW | BCI_CMD_DEST_GBD;
877145132Sanholt	BCI_CMD_SET_ROP(swap_cmd,0xCC);
878145132Sanholt
879145132Sanholt	for (i = 0; i < nbox; ++i) {
880145132Sanholt		BEGIN_DMA(6);
881145132Sanholt		DMA_WRITE(swap_cmd);
882145132Sanholt		DMA_WRITE(dev_priv->back_offset);
883145132Sanholt		DMA_WRITE(dev_priv->back_bd);
884152909Sanholt		DMA_WRITE(BCI_X_Y(boxes[i].x1, boxes[i].y1));
885152909Sanholt		DMA_WRITE(BCI_X_Y(boxes[i].x1, boxes[i].y1));
886182080Srnoland		DMA_WRITE(BCI_W_H(boxes[i].x2 - boxes[i].x1,
887182080Srnoland				  boxes[i].y2 - boxes[i].y1));
888145132Sanholt		DMA_COMMIT();
889145132Sanholt	}
890145132Sanholt
891145132Sanholt	return 0;
892145132Sanholt}
893145132Sanholt
894145132Sanholtstatic int savage_dispatch_draw(drm_savage_private_t *dev_priv,
895152909Sanholt				const drm_savage_cmd_header_t *start,
896152909Sanholt				const drm_savage_cmd_header_t *end,
897182080Srnoland				const struct drm_buf *dmabuf,
898152909Sanholt				const unsigned int *vtxbuf,
899145132Sanholt				unsigned int vb_size, unsigned int vb_stride,
900145132Sanholt				unsigned int nbox,
901182080Srnoland				const struct drm_clip_rect *boxes)
902145132Sanholt{
903145132Sanholt	unsigned int i, j;
904145132Sanholt	int ret;
905145132Sanholt
906145132Sanholt	for (i = 0; i < nbox; ++i) {
907152909Sanholt		const drm_savage_cmd_header_t *cmdbuf;
908152909Sanholt		dev_priv->emit_clip_rect(dev_priv, &boxes[i]);
909145132Sanholt
910152909Sanholt		cmdbuf = start;
911152909Sanholt		while (cmdbuf < end) {
912145132Sanholt			drm_savage_cmd_header_t cmd_header;
913152909Sanholt			cmd_header = *cmdbuf;
914152909Sanholt			cmdbuf++;
915145132Sanholt			switch (cmd_header.cmd.cmd) {
916145132Sanholt			case SAVAGE_CMD_DMA_PRIM:
917145132Sanholt				ret = savage_dispatch_dma_prim(
918145132Sanholt					dev_priv, &cmd_header, dmabuf);
919145132Sanholt				break;
920145132Sanholt			case SAVAGE_CMD_VB_PRIM:
921145132Sanholt				ret = savage_dispatch_vb_prim(
922145132Sanholt					dev_priv, &cmd_header,
923152909Sanholt					vtxbuf, vb_size, vb_stride);
924145132Sanholt				break;
925145132Sanholt			case SAVAGE_CMD_DMA_IDX:
926145132Sanholt				j = (cmd_header.idx.count + 3) / 4;
927145132Sanholt				/* j was check in savage_bci_cmdbuf */
928152909Sanholt				ret = savage_dispatch_dma_idx(dev_priv,
929152909Sanholt					&cmd_header, (const uint16_t *)cmdbuf,
930145132Sanholt					dmabuf);
931152909Sanholt				cmdbuf += j;
932145132Sanholt				break;
933145132Sanholt			case SAVAGE_CMD_VB_IDX:
934145132Sanholt				j = (cmd_header.idx.count + 3) / 4;
935145132Sanholt				/* j was check in savage_bci_cmdbuf */
936152909Sanholt				ret = savage_dispatch_vb_idx(dev_priv,
937152909Sanholt					&cmd_header, (const uint16_t *)cmdbuf,
938152909Sanholt					(const uint32_t *)vtxbuf, vb_size,
939152909Sanholt					vb_stride);
940152909Sanholt				cmdbuf += j;
941145132Sanholt				break;
942145132Sanholt			default:
943145132Sanholt				/* What's the best return code? EFAULT? */
944145132Sanholt				DRM_ERROR("IMPLEMENTATION ERROR: "
945145132Sanholt					  "non-drawing-command %d\n",
946145132Sanholt					  cmd_header.cmd.cmd);
947182080Srnoland				return -EINVAL;
948145132Sanholt			}
949145132Sanholt
950145132Sanholt			if (ret != 0)
951145132Sanholt				return ret;
952145132Sanholt		}
953145132Sanholt	}
954145132Sanholt
955145132Sanholt	return 0;
956145132Sanholt}
957145132Sanholt
958182080Srnolandint savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_priv)
959145132Sanholt{
960145132Sanholt	drm_savage_private_t *dev_priv = dev->dev_private;
961182080Srnoland	struct drm_device_dma *dma = dev->dma;
962182080Srnoland	struct drm_buf *dmabuf;
963182080Srnoland	drm_savage_cmdbuf_t *cmdbuf = data;
964152909Sanholt	drm_savage_cmd_header_t *kcmd_addr = NULL;
965152909Sanholt	drm_savage_cmd_header_t *first_draw_cmd;
966152909Sanholt	unsigned int *kvb_addr = NULL;
967182080Srnoland	struct drm_clip_rect *kbox_addr = NULL;
968145132Sanholt	unsigned int i, j;
969145132Sanholt	int ret = 0;
970145132Sanholt
971145132Sanholt	DRM_DEBUG("\n");
972145132Sanholt
973182080Srnoland	LOCK_TEST_WITH_RETURN(dev, file_priv);
974145132Sanholt
975145132Sanholt	if (dma && dma->buflist) {
976182080Srnoland		if (cmdbuf->dma_idx > dma->buf_count) {
977157617Sanholt			DRM_ERROR
978157617Sanholt			    ("vertex buffer index %u out of range (0-%u)\n",
979182080Srnoland			     cmdbuf->dma_idx, dma->buf_count - 1);
980182080Srnoland			return -EINVAL;
981145132Sanholt		}
982182080Srnoland		dmabuf = dma->buflist[cmdbuf->dma_idx];
983145132Sanholt	} else {
984145132Sanholt		dmabuf = NULL;
985145132Sanholt	}
986145132Sanholt
987152909Sanholt	/* Copy the user buffers into kernel temporary areas.  This hasn't been
988152909Sanholt	 * a performance loss compared to VERIFYAREA_READ/
989152909Sanholt	 * COPY_FROM_USER_UNCHECKED when done in other drivers, and is correct
990152909Sanholt	 * for locking on FreeBSD.
991152909Sanholt	 */
992182080Srnoland	if (cmdbuf->size) {
993182080Srnoland		kcmd_addr = drm_alloc(cmdbuf->size * 8, DRM_MEM_DRIVER);
994152909Sanholt		if (kcmd_addr == NULL)
995182080Srnoland			return -ENOMEM;
996145132Sanholt
997182080Srnoland		if (DRM_COPY_FROM_USER(kcmd_addr, cmdbuf->cmd_addr,
998182080Srnoland				       cmdbuf->size * 8))
999152909Sanholt		{
1000182080Srnoland			drm_free(kcmd_addr, cmdbuf->size * 8, DRM_MEM_DRIVER);
1001182080Srnoland			return -EFAULT;
1002152909Sanholt		}
1003182080Srnoland		cmdbuf->cmd_addr = kcmd_addr;
1004152909Sanholt	}
1005182080Srnoland	if (cmdbuf->vb_size) {
1006182080Srnoland		kvb_addr = drm_alloc(cmdbuf->vb_size, DRM_MEM_DRIVER);
1007152909Sanholt		if (kvb_addr == NULL) {
1008182080Srnoland			ret = -ENOMEM;
1009152909Sanholt			goto done;
1010152909Sanholt		}
1011152909Sanholt
1012182080Srnoland		if (DRM_COPY_FROM_USER(kvb_addr, cmdbuf->vb_addr,
1013182080Srnoland				       cmdbuf->vb_size)) {
1014182080Srnoland			ret = -EFAULT;
1015152909Sanholt			goto done;
1016152909Sanholt		}
1017182080Srnoland		cmdbuf->vb_addr = kvb_addr;
1018152909Sanholt	}
1019182080Srnoland	if (cmdbuf->nbox) {
1020182080Srnoland		kbox_addr = drm_alloc(cmdbuf->nbox *
1021182080Srnoland				      sizeof(struct drm_clip_rect),
1022182080Srnoland				      DRM_MEM_DRIVER);
1023152909Sanholt		if (kbox_addr == NULL) {
1024182080Srnoland			ret = -ENOMEM;
1025152909Sanholt			goto done;
1026152909Sanholt		}
1027152909Sanholt
1028182080Srnoland		if (DRM_COPY_FROM_USER(kbox_addr, cmdbuf->box_addr,
1029182080Srnoland				       cmdbuf->nbox *
1030182080Srnoland				       sizeof(struct drm_clip_rect))) {
1031182080Srnoland			ret = -EFAULT;
1032152909Sanholt			goto done;
1033152909Sanholt		}
1034182080Srnoland		cmdbuf->box_addr = kbox_addr;
1035152909Sanholt	}
1036152909Sanholt
1037145132Sanholt	/* Make sure writes to DMA buffers are finished before sending
1038145132Sanholt	 * DMA commands to the graphics hardware. */
1039145132Sanholt	DRM_MEMORYBARRIER();
1040145132Sanholt
1041145132Sanholt	/* Coming from user space. Don't know if the Xserver has
1042145132Sanholt	 * emitted wait commands. Assuming the worst. */
1043145132Sanholt	dev_priv->waiting = 1;
1044145132Sanholt
1045145132Sanholt	i = 0;
1046145132Sanholt	first_draw_cmd = NULL;
1047182080Srnoland	while (i < cmdbuf->size) {
1048145132Sanholt		drm_savage_cmd_header_t cmd_header;
1049182080Srnoland		cmd_header = *(drm_savage_cmd_header_t *)cmdbuf->cmd_addr;
1050182080Srnoland		cmdbuf->cmd_addr++;
1051145132Sanholt		i++;
1052145132Sanholt
1053145132Sanholt		/* Group drawing commands with same state to minimize
1054145132Sanholt		 * iterations over clip rects. */
1055145132Sanholt		j = 0;
1056145132Sanholt		switch (cmd_header.cmd.cmd) {
1057145132Sanholt		case SAVAGE_CMD_DMA_IDX:
1058145132Sanholt		case SAVAGE_CMD_VB_IDX:
1059145132Sanholt			j = (cmd_header.idx.count + 3) / 4;
1060182080Srnoland			if (i + j > cmdbuf->size) {
1061145132Sanholt				DRM_ERROR("indexed drawing command extends "
1062145132Sanholt					  "beyond end of command buffer\n");
1063145132Sanholt				DMA_FLUSH();
1064182080Srnoland				return -EINVAL;
1065145132Sanholt			}
1066145132Sanholt			/* fall through */
1067145132Sanholt		case SAVAGE_CMD_DMA_PRIM:
1068145132Sanholt		case SAVAGE_CMD_VB_PRIM:
1069145132Sanholt			if (!first_draw_cmd)
1070182080Srnoland				first_draw_cmd = cmdbuf->cmd_addr - 1;
1071182080Srnoland			cmdbuf->cmd_addr += j;
1072145132Sanholt			i += j;
1073145132Sanholt			break;
1074145132Sanholt		default:
1075145132Sanholt			if (first_draw_cmd) {
1076182080Srnoland				ret = savage_dispatch_draw(
1077152909Sanholt					dev_priv, first_draw_cmd,
1078182080Srnoland					cmdbuf->cmd_addr - 1,
1079182080Srnoland					dmabuf, cmdbuf->vb_addr,
1080182080Srnoland					cmdbuf->vb_size,
1081182080Srnoland					cmdbuf->vb_stride,
1082182080Srnoland					cmdbuf->nbox, cmdbuf->box_addr);
1083145132Sanholt				if (ret != 0)
1084145132Sanholt					return ret;
1085145132Sanholt				first_draw_cmd = NULL;
1086145132Sanholt			}
1087145132Sanholt		}
1088145132Sanholt		if (first_draw_cmd)
1089145132Sanholt			continue;
1090145132Sanholt
1091145132Sanholt		switch (cmd_header.cmd.cmd) {
1092145132Sanholt		case SAVAGE_CMD_STATE:
1093145132Sanholt			j = (cmd_header.state.count + 1) / 2;
1094182080Srnoland			if (i + j > cmdbuf->size) {
1095145132Sanholt				DRM_ERROR("command SAVAGE_CMD_STATE extends "
1096145132Sanholt					  "beyond end of command buffer\n");
1097145132Sanholt				DMA_FLUSH();
1098182080Srnoland				ret = -EINVAL;
1099152909Sanholt				goto done;
1100145132Sanholt			}
1101152909Sanholt			ret = savage_dispatch_state(dev_priv, &cmd_header,
1102182080Srnoland				(const uint32_t *)cmdbuf->cmd_addr);
1103182080Srnoland			cmdbuf->cmd_addr += j;
1104145132Sanholt			i += j;
1105145132Sanholt			break;
1106145132Sanholt		case SAVAGE_CMD_CLEAR:
1107182080Srnoland			if (i + 1 > cmdbuf->size) {
1108145132Sanholt				DRM_ERROR("command SAVAGE_CMD_CLEAR extends "
1109145132Sanholt					  "beyond end of command buffer\n");
1110145132Sanholt				DMA_FLUSH();
1111182080Srnoland				ret = -EINVAL;
1112152909Sanholt				goto done;
1113145132Sanholt			}
1114145132Sanholt			ret = savage_dispatch_clear(dev_priv, &cmd_header,
1115182080Srnoland						    cmdbuf->cmd_addr,
1116182080Srnoland						    cmdbuf->nbox,
1117182080Srnoland						    cmdbuf->box_addr);
1118182080Srnoland			cmdbuf->cmd_addr++;
1119145132Sanholt			i++;
1120145132Sanholt			break;
1121145132Sanholt		case SAVAGE_CMD_SWAP:
1122182080Srnoland			ret = savage_dispatch_swap(dev_priv, cmdbuf->nbox,
1123182080Srnoland						   cmdbuf->box_addr);
1124145132Sanholt			break;
1125145132Sanholt		default:
1126182080Srnoland			DRM_ERROR("invalid command 0x%x\n",
1127182080Srnoland				  cmd_header.cmd.cmd);
1128145132Sanholt			DMA_FLUSH();
1129182080Srnoland			ret = -EINVAL;
1130152909Sanholt			goto done;
1131145132Sanholt		}
1132145132Sanholt
1133145132Sanholt		if (ret != 0) {
1134145132Sanholt			DMA_FLUSH();
1135152909Sanholt			goto done;
1136145132Sanholt		}
1137145132Sanholt	}
1138145132Sanholt
1139145132Sanholt	if (first_draw_cmd) {
1140182080Srnoland		ret = savage_dispatch_draw(
1141182080Srnoland			dev_priv, first_draw_cmd, cmdbuf->cmd_addr, dmabuf,
1142182080Srnoland			cmdbuf->vb_addr, cmdbuf->vb_size, cmdbuf->vb_stride,
1143182080Srnoland			cmdbuf->nbox, cmdbuf->box_addr);
1144145132Sanholt		if (ret != 0) {
1145145132Sanholt			DMA_FLUSH();
1146152909Sanholt			goto done;
1147145132Sanholt		}
1148145132Sanholt	}
1149145132Sanholt
1150145132Sanholt	DMA_FLUSH();
1151145132Sanholt
1152182080Srnoland	if (dmabuf && cmdbuf->discard) {
1153145132Sanholt		drm_savage_buf_priv_t *buf_priv = dmabuf->dev_private;
1154145132Sanholt		uint16_t event;
1155145132Sanholt		event = savage_bci_emit_event(dev_priv, SAVAGE_WAIT_3D);
1156145132Sanholt		SET_AGE(&buf_priv->age, event, dev_priv->event_wrap);
1157145132Sanholt		savage_freelist_put(dev, dmabuf);
1158145132Sanholt	}
1159145132Sanholt
1160152909Sanholtdone:
1161152909Sanholt	/* If we didn't need to allocate them, these'll be NULL */
1162182080Srnoland	drm_free(kcmd_addr, cmdbuf->size * 8, DRM_MEM_DRIVER);
1163182080Srnoland	drm_free(kvb_addr, cmdbuf->vb_size, DRM_MEM_DRIVER);
1164182080Srnoland	drm_free(kbox_addr, cmdbuf->nbox * sizeof(struct drm_clip_rect),
1165152909Sanholt		 DRM_MEM_DRIVER);
1166152909Sanholt
1167152909Sanholt	return ret;
1168145132Sanholt}
1169