1/*
2 * Copyright 2007-2008 Maarten Maathuis
3 * Copyright 2008 Stephane Marchesin
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "xorg-config.h"
30#include "xf86xv.h"
31#include <X11/extensions/Xv.h>
32#include "exa.h"
33#include "damage.h"
34#include "dixstruct.h"
35#include "fourcc.h"
36
37#include "nv_include.h"
38#include "nv_dma.h"
39
40#include "hwdefs/nv30-40_3d.xml.h"
41#include "nv04_accel.h"
42
43extern Atom xvSyncToVBlank, xvSetDefaults;
44
45#define SWIZZLE(ts0x,ts0y,ts0z,ts0w,ts1x,ts1y,ts1z,ts1w) (                     \
46	NV30_3D_TEX_SWIZZLE_S0_X_##ts0x | NV30_3D_TEX_SWIZZLE_S0_Y_##ts0y |    \
47	NV30_3D_TEX_SWIZZLE_S0_Z_##ts0z | NV30_3D_TEX_SWIZZLE_S0_W_##ts0w |    \
48	NV30_3D_TEX_SWIZZLE_S1_X_##ts1x | NV30_3D_TEX_SWIZZLE_S1_Y_##ts1y |    \
49	NV30_3D_TEX_SWIZZLE_S1_Z_##ts1z | NV30_3D_TEX_SWIZZLE_S1_W_##ts1w      \
50)
51
52/*
53 * Texture 0 : filter table
54 * Texture 1 : Y data
55 * Texture 2 : UV data
56 */
57static Bool
58NV40VideoTexture(ScrnInfoPtr pScrn, struct nouveau_bo *src, int offset,
59		 uint16_t width, uint16_t height, uint16_t src_pitch, int unit)
60{
61	NVPtr pNv = NVPTR(pScrn);
62	unsigned reloc = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD;
63	struct nouveau_pushbuf *push = pNv->pushbuf;
64	uint32_t card_fmt = 0;
65	uint32_t card_swz = 0;
66
67	switch(unit) {
68	case 0:
69		card_fmt = NV40_3D_TEX_FORMAT_FORMAT_A8R8G8B8;
70		card_swz = SWIZZLE(S1, S1, S1, S1, X, Y, Z, W);
71		break;
72	case 1:
73		card_fmt = NV40_3D_TEX_FORMAT_FORMAT_L8;
74		card_swz = SWIZZLE(S1, S1, S1, S1, X, X, X, X);
75		break;
76	case 2:
77		card_fmt = NV40_3D_TEX_FORMAT_FORMAT_A8L8;
78#if X_BYTE_ORDER == X_BIG_ENDIAN
79		card_swz = SWIZZLE(S1, S1, S1, S1, Z, W, X, Y);
80#else
81		card_swz = SWIZZLE(S1, S1, S1, S1, W, Z, Y, X);
82#endif
83		break;
84	}
85
86	BEGIN_NV04(push, NV30_3D(TEX_OFFSET(unit)), 8);
87	PUSH_MTHDl(push, NV30_3D(TEX_OFFSET(unit)), src, offset, reloc);
88	if (unit==0) {
89		PUSH_MTHDs(push, NV30_3D(TEX_FORMAT(unit)), src,
90				 card_fmt | 0x8000 |
91				 NV30_3D_TEX_FORMAT_DIMS_1D |
92				 NV30_3D_TEX_FORMAT_NO_BORDER |
93				 (1 << NV40_3D_TEX_FORMAT_MIPMAP_COUNT__SHIFT),
94				 reloc | NOUVEAU_BO_OR,
95				 NV30_3D_TEX_FORMAT_DMA0,
96				 NV30_3D_TEX_FORMAT_DMA1);
97		PUSH_DATA (push, NV30_3D_TEX_WRAP_S_REPEAT |
98				 NV30_3D_TEX_WRAP_T_CLAMP_TO_EDGE |
99				 NV30_3D_TEX_WRAP_R_CLAMP_TO_EDGE);
100	} else {
101		PUSH_MTHDs(push, NV30_3D(TEX_FORMAT(unit)), src,
102				 card_fmt | 0x8000 |
103				 NV40_3D_TEX_FORMAT_LINEAR |
104				 NV40_3D_TEX_FORMAT_RECT |
105				 NV30_3D_TEX_FORMAT_DIMS_2D |
106				 NV30_3D_TEX_FORMAT_NO_BORDER |
107				 (1 << NV40_3D_TEX_FORMAT_MIPMAP_COUNT__SHIFT),
108				 reloc | NOUVEAU_BO_OR,
109				 NV30_3D_TEX_FORMAT_DMA0,
110				 NV30_3D_TEX_FORMAT_DMA1);
111		PUSH_DATA (push, NV30_3D_TEX_WRAP_S_CLAMP_TO_EDGE |
112				 NV30_3D_TEX_WRAP_T_CLAMP_TO_EDGE |
113				 NV30_3D_TEX_WRAP_R_CLAMP_TO_EDGE);
114	}
115
116	PUSH_DATA (push, NV40_3D_TEX_ENABLE_ENABLE);
117	PUSH_DATA (push, card_swz);
118	if (unit == 0)
119		PUSH_DATA (push, NV30_3D_TEX_FILTER_SIGNED_ALPHA |
120				 NV30_3D_TEX_FILTER_SIGNED_RED |
121				 NV30_3D_TEX_FILTER_SIGNED_GREEN |
122				 NV30_3D_TEX_FILTER_SIGNED_BLUE |
123				 NV30_3D_TEX_FILTER_MIN_LINEAR |
124				 NV30_3D_TEX_FILTER_MAG_LINEAR | 0x3fd6);
125	else
126		PUSH_DATA (push, NV30_3D_TEX_FILTER_MIN_LINEAR |
127				 NV30_3D_TEX_FILTER_MAG_LINEAR | 0x3fd6);
128	PUSH_DATA (push, (width << 16) | height);
129	PUSH_DATA (push, 0); /* border ARGB */
130
131	BEGIN_NV04(push, NV40_3D(TEX_SIZE1(unit)), 1);
132	PUSH_DATA (push, (1 << NV40_3D_TEX_SIZE1_DEPTH__SHIFT) |
133			 (uint16_t) src_pitch);
134
135	return TRUE;
136}
137
138static Bool
139NV40GetSurfaceFormat(PixmapPtr ppix, int *fmt_ret)
140{
141	switch (ppix->drawable.bitsPerPixel) {
142	case 32:
143		*fmt_ret = NV30_3D_RT_FORMAT_COLOR_A8R8G8B8;
144		break;
145	case 24:
146		*fmt_ret = NV30_3D_RT_FORMAT_COLOR_X8R8G8B8;
147		break;
148	case 16:
149		*fmt_ret = NV30_3D_RT_FORMAT_COLOR_R5G6B5;
150		break;
151	case 8:
152		*fmt_ret = NV30_3D_RT_FORMAT_COLOR_B8;
153		break;
154	default:
155		return FALSE;
156	}
157
158	return TRUE;
159}
160
161void
162NV40StopTexturedVideo(ScrnInfoPtr pScrn, pointer data, Bool Exit)
163{
164}
165
166#define VERTEX_OUT(sx,sy,dx,dy) do {                                           \
167	BEGIN_NV04(push, NV30_3D(VTX_ATTR_2F_X(8)), 4);                        \
168	PUSH_DATAf(push, (sx)); PUSH_DATAf(push, (sy));                        \
169	PUSH_DATAf(push, (sx)/2.0); PUSH_DATAf(push, (sy)/2.0);                \
170	BEGIN_NV04(push, NV30_3D(VTX_ATTR_2I(0)), 1);                          \
171	PUSH_DATA (push, (((dy)&0xffff)<<16)|((dx)&0xffff));                   \
172} while(0)
173
174int
175NV40PutTextureImage(ScrnInfoPtr pScrn,
176		    struct nouveau_bo *src, int src_offset, int src_offset2,
177		    int id, int src_pitch, BoxPtr dstBox,
178		    int x1, int y1, int x2, int y2,
179		    uint16_t width, uint16_t height,
180		    uint16_t src_w, uint16_t src_h,
181		    uint16_t drw_w, uint16_t drw_h,
182		    RegionPtr clipBoxes, PixmapPtr ppix,
183		    NVPortPrivPtr pPriv)
184{
185	NVPtr pNv = NVPTR(pScrn);
186	struct nouveau_pushbuf *push = pNv->pushbuf;
187	struct nouveau_bo *bo = nouveau_pixmap_bo(ppix);
188	Bool bicubic = pPriv->bicubic;
189	float X1, X2, Y1, Y2;
190	BoxPtr pbox;
191	int nbox, i;
192	int dst_format = 0;
193
194	if (drw_w > 4096 || drw_h > 4096) {
195		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
196			"XV: Draw size too large.\n");
197		return BadAlloc;
198	}
199
200	if (!NV40GetSurfaceFormat(ppix, &dst_format)) {
201		ErrorF("No surface format, bad.\n");
202		return BadImplementation;
203	}
204
205	pbox = REGION_RECTS(clipBoxes);
206	nbox = REGION_NUM_RECTS(clipBoxes);
207
208	if (!PUSH_SPACE(push, 128))
209		return BadImplementation;
210	PUSH_RESET(push);
211
212	BEGIN_NV04(push, NV30_3D(BLEND_FUNC_ENABLE), 1);
213	PUSH_DATA (push, 0);
214	BEGIN_NV04(push, NV30_3D(RT_FORMAT), 3);
215	PUSH_DATA (push, NV30_3D_RT_FORMAT_TYPE_LINEAR |
216			 NV30_3D_RT_FORMAT_ZETA_Z24S8 | dst_format);
217	PUSH_DATA (push, exaGetPixmapPitch(ppix));
218	PUSH_MTHDl(push, NV30_3D(COLOR0_OFFSET), bo, 0,
219			 NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
220
221	if (!NV40VideoTexture(pScrn, pNv->scratch, XV_TABLE, XV_TABLE_SIZE,
222				     1, 0, 0) ||
223	    !NV40VideoTexture(pScrn, src, src_offset, src_w, src_h,
224			      src_pitch, 1)) {
225		PUSH_RESET(push);
226		return BadImplementation;
227	}
228
229	/* We've got NV12 format, which means half width and half height
230	 * texture of chroma channels.
231	 */
232	if (!NV40VideoTexture(pScrn, src, src_offset2, src_w/2,
233			      src_h/2, src_pitch, 2)) {
234		PUSH_RESET(push);
235		return BadImplementation;
236	}
237
238	if (drw_w / 2 < src_w || drw_h / 2 < src_h)
239		bicubic = FALSE;
240
241	BEGIN_NV04(push, NV30_3D(FP_ACTIVE_PROGRAM), 1);
242	PUSH_MTHD (push, NV30_3D(FP_ACTIVE_PROGRAM), pNv->scratch,
243			 bicubic ? PFP_NV12_BICUBIC : PFP_NV12_BILINEAR,
244			 NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | NOUVEAU_BO_LOW |
245			 NOUVEAU_BO_OR,
246			 NV30_3D_FP_ACTIVE_PROGRAM_DMA0,
247			 NV30_3D_FP_ACTIVE_PROGRAM_DMA1);
248	BEGIN_NV04(push, NV30_3D(FP_CONTROL), 1);
249	PUSH_DATA (push, 0x04000000);
250
251	/* Appears to be some kind of cache flush, needed here at least
252	 * sometimes.. funky text rendering otherwise :)
253	 */
254	BEGIN_NV04(push, NV40_3D(TEX_CACHE_CTL), 1);
255	PUSH_DATA (push, 2);
256	BEGIN_NV04(push, NV40_3D(TEX_CACHE_CTL), 1);
257	PUSH_DATA (push, 1);
258
259	for (i = 0; i < 2; i++) {
260		BEGIN_NV04(push, NV30_3D(VP_UPLOAD_CONST_ID), 17);
261		PUSH_DATA (push, i * 4);
262		PUSH_DATAf(push, 1.0);
263		PUSH_DATAf(push, 0.0);
264		PUSH_DATAf(push, 0.0);
265		PUSH_DATAf(push, 0.0);
266		PUSH_DATAf(push, 0.0);
267		PUSH_DATAf(push, 1.0);
268		PUSH_DATAf(push, 0.0);
269		PUSH_DATAf(push, 0.0);
270		PUSH_DATAf(push, 0.0);
271		PUSH_DATAf(push, 0.0);
272		PUSH_DATAf(push, 1.0);
273		PUSH_DATAf(push, 0.0);
274		PUSH_DATAf(push, 1.0);
275		PUSH_DATAf(push, 1.0);
276		PUSH_DATAf(push, 0.0);
277		PUSH_DATAf(push, 0.0);
278	}
279
280	nouveau_pushbuf_bufctx(push, pNv->bufctx);
281	if (nouveau_pushbuf_validate(push)) {
282		nouveau_pushbuf_bufctx(push, NULL);
283		return BadAlloc;
284	}
285
286	/* Before rendering we wait for vblank in the non-composited case. */
287	if (pPriv->SyncToVBlank)
288		NV11SyncToVBlank(ppix, dstBox);
289
290	/* These are fixed point values in the 16.16 format. */
291	X1 = (float)(x1>>16)+(float)(x1&0xFFFF)/(float)0x10000;
292	Y1 = (float)(y1>>16)+(float)(y1&0xFFFF)/(float)0x10000;
293	X2 = (float)(x2>>16)+(float)(x2&0xFFFF)/(float)0x10000;
294	Y2 = (float)(y2>>16)+(float)(y2&0xFFFF)/(float)0x10000;
295
296	BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
297	PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_TRIANGLES);
298
299	while(nbox--) {
300		float tx1=X1+(float)(pbox->x1 - dstBox->x1)*(X2-X1)/(float)(drw_w);
301		float tx2=X1+(float)(pbox->x2 - dstBox->x1)*(src_w)/(float)(drw_w);
302		float ty1=Y1+(float)(pbox->y1 - dstBox->y1)*(Y2-Y1)/(float)(drw_h);
303		float ty2=Y1+(float)(pbox->y2 - dstBox->y1)*(src_h)/(float)(drw_h);
304		int sx1=pbox->x1;
305		int sx2=pbox->x2;
306		int sy1=pbox->y1;
307		int sy2=pbox->y2;
308
309		if (!PUSH_SPACE(push, 64)) {
310			nouveau_pushbuf_bufctx(push, NULL);
311			return BadImplementation;
312		}
313
314		BEGIN_NV04(push, NV30_3D(SCISSOR_HORIZ), 2);
315		PUSH_DATA (push, (sx2 << 16) | 0);
316		PUSH_DATA (push, (sy2 << 16) | 0);
317
318		VERTEX_OUT(tx1, ty1, sx1, sy1);
319		VERTEX_OUT(tx2+(tx2-tx1), ty1, sx2+(sx2-sx1), sy1);
320		VERTEX_OUT(tx1, ty2+(ty2-ty1), sx1, sy2+(sy2-sy1));
321
322		pbox++;
323	}
324
325	BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
326	PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP);
327
328	nouveau_pushbuf_bufctx(push, NULL);
329	PUSH_KICK(push);
330	return Success;
331}
332
333/**
334 * NV40SetTexturePortAttribute
335 * sets the attribute "attribute" of port "data" to value "value"
336 * supported attributes:
337 * Sync to vblank.
338 *
339 * @param pScrenInfo
340 * @param attribute attribute to set
341 * @param value value to which attribute is to be set
342 * @param data port from which the attribute is to be set
343 *
344 * @return Success, if setting is successful
345 * BadValue/BadMatch, if value/attribute are invalid
346 */
347int
348NV40SetTexturePortAttribute(ScrnInfoPtr pScrn, Atom attribute,
349                       INT32 value, pointer data)
350{
351        NVPortPrivPtr pPriv = (NVPortPrivPtr)data;
352
353        if (attribute == xvSyncToVBlank) {
354                if ((value < 0) || (value > 1))
355                        return BadValue;
356                pPriv->SyncToVBlank = value;
357        } else
358        if (attribute == xvSetDefaults) {
359                pPriv->SyncToVBlank = TRUE;
360        } else
361                return BadMatch;
362
363        return Success;
364}
365
366/**
367 * NV40GetTexturePortAttribute
368 * reads the value of attribute "attribute" from port "data" into INT32 "*value"
369 * Sync to vblank.
370 *
371 * @param pScrn unused
372 * @param attribute attribute to be read
373 * @param value value of attribute will be stored here
374 * @param data port from which attribute will be read
375 * @return Success, if queried attribute exists
376 */
377int
378NV40GetTexturePortAttribute(ScrnInfoPtr pScrn, Atom attribute,
379                       INT32 *value, pointer data)
380{
381        NVPortPrivPtr pPriv = (NVPortPrivPtr)data;
382
383        if(attribute == xvSyncToVBlank)
384                *value = (pPriv->SyncToVBlank) ? 1 : 0;
385        else
386                return BadMatch;
387
388        return Success;
389}
390
391