1/*
2 * Copyright 2008 Ben Skeggs
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "xorg-config.h"
28#include "xf86xv.h"
29#include <X11/extensions/Xv.h>
30#include "exa.h"
31#include "damage.h"
32#include "dixstruct.h"
33#include "fourcc.h"
34
35#include "nv_include.h"
36#include "nv_dma.h"
37#include "nv50_accel.h"
38
39extern Atom xvSyncToVBlank, xvSetDefaults;
40extern Atom xvBrightness, xvContrast, xvHue, xvSaturation;
41extern Atom xvITURBT709;
42
43static Bool
44nv50_xv_check_image_put(PixmapPtr ppix)
45{
46	switch (ppix->drawable.bitsPerPixel) {
47	case 32:
48	case 24:
49	case 16:
50	case 15:
51		break;
52	default:
53		return FALSE;
54	}
55
56	if (!nv50_style_tiled_pixmap(ppix))
57		return FALSE;
58
59	return TRUE;
60}
61
62int
63nv50_xv_image_put(ScrnInfoPtr pScrn,
64		  struct nouveau_bo *src, int packed_y, int uv,
65		  int id, int src_pitch, BoxPtr dstBox,
66		  int x1, int y1, int x2, int y2,
67		  uint16_t width, uint16_t height,
68		  uint16_t src_w, uint16_t src_h,
69		  uint16_t drw_w, uint16_t drw_h,
70		  RegionPtr clipBoxes, PixmapPtr ppix,
71		  NVPortPrivPtr pPriv)
72{
73	NVPtr pNv = NVPTR(pScrn);
74	struct nouveau_bo *dst = nouveau_pixmap_bo(ppix);
75	struct nouveau_pushbuf *push = pNv->pushbuf;
76	struct nouveau_pushbuf_refn refs[] = {
77		{ pNv->scratch, NOUVEAU_BO_VRAM | NOUVEAU_BO_RDWR },
78		{ src, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD },
79		{ dst, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR },
80	};
81	uint32_t mode = 0xd0005000 | (src->config.nv50.tile_mode << 18);
82	float X1, X2, Y1, Y2;
83	BoxPtr pbox;
84	int nbox;
85
86	if (!nv50_xv_check_image_put(ppix))
87		return BadMatch;
88
89	if (!PUSH_SPACE(push, 256))
90		return BadImplementation;
91
92	BEGIN_NV04(push, NV50_3D(RT_ADDRESS_HIGH(0)), 5);
93	PUSH_DATA (push, dst->offset >> 32);
94	PUSH_DATA (push, dst->offset);
95	switch (ppix->drawable.depth) {
96	case 32: PUSH_DATA (push, NV50_SURFACE_FORMAT_BGRA8_UNORM); break;
97	case 30: PUSH_DATA (push, NV50_SURFACE_FORMAT_RGB10_A2_UNORM); break;
98	case 24: PUSH_DATA (push, NV50_SURFACE_FORMAT_BGRX8_UNORM); break;
99	case 16: PUSH_DATA (push, NV50_SURFACE_FORMAT_B5G6R5_UNORM); break;
100	case 15: PUSH_DATA (push, NV50_SURFACE_FORMAT_BGR5_X1_UNORM); break;
101	}
102	PUSH_DATA (push, dst->config.nv50.tile_mode);
103	PUSH_DATA (push, 0);
104	BEGIN_NV04(push, NV50_3D(RT_HORIZ(0)), 2);
105	PUSH_DATA (push, ppix->drawable.width);
106	PUSH_DATA (push, ppix->drawable.height);
107	BEGIN_NV04(push, NV50_3D(RT_ARRAY_MODE), 1);
108	PUSH_DATA (push, 1);
109
110	BEGIN_NV04(push, NV50_3D(BLEND_ENABLE(0)), 1);
111	PUSH_DATA (push, 0);
112
113	PUSH_DATAu(push, pNv->scratch, TIC_OFFSET, 16);
114	if (id == FOURCC_YV12 || id == FOURCC_I420) {
115	PUSH_DATA (push, NV50TIC_0_0_MAPA_C0 | NV50TIC_0_0_TYPEA_UNORM |
116			 NV50TIC_0_0_MAPB_ZERO | NV50TIC_0_0_TYPEB_UNORM |
117			 NV50TIC_0_0_MAPG_ZERO | NV50TIC_0_0_TYPEG_UNORM |
118			 NV50TIC_0_0_MAPR_ZERO | NV50TIC_0_0_TYPER_UNORM |
119			 NV50TIC_0_0_FMT_8);
120	PUSH_DATA (push, (src->offset + packed_y));
121	PUSH_DATA (push, (src->offset + packed_y) >> 32 | mode);
122	PUSH_DATA (push, 0x00300000);
123	PUSH_DATA (push, width);
124	PUSH_DATA (push, (1 << NV50TIC_0_5_DEPTH_SHIFT) | height);
125	PUSH_DATA (push, 0x03000000);
126	PUSH_DATA (push, 0x00000000);
127	PUSH_DATA (push, NV50TIC_0_0_MAPA_C1 | NV50TIC_0_0_TYPEA_UNORM |
128			 NV50TIC_0_0_MAPB_C0 | NV50TIC_0_0_TYPEB_UNORM |
129			 NV50TIC_0_0_MAPG_ZERO | NV50TIC_0_0_TYPEG_UNORM |
130			 NV50TIC_0_0_MAPR_ZERO | NV50TIC_0_0_TYPER_UNORM |
131			 NV50TIC_0_0_FMT_8_8);
132	PUSH_DATA (push, (src->offset + uv));
133	PUSH_DATA (push, (src->offset + uv) >> 32 | mode);
134	PUSH_DATA (push, 0x00300000);
135	PUSH_DATA (push, width >> 1);
136	PUSH_DATA (push, (1 << NV50TIC_0_5_DEPTH_SHIFT) | (height >> 1));
137	PUSH_DATA (push, 0x03000000);
138	PUSH_DATA (push, 0x00000000);
139	} else {
140	if (id == FOURCC_UYVY) {
141	PUSH_DATA (push, NV50TIC_0_0_MAPA_C1 | NV50TIC_0_0_TYPEA_UNORM |
142			 NV50TIC_0_0_MAPB_ZERO | NV50TIC_0_0_TYPEB_UNORM |
143			 NV50TIC_0_0_MAPG_ZERO | NV50TIC_0_0_TYPEG_UNORM |
144			 NV50TIC_0_0_MAPR_ZERO | NV50TIC_0_0_TYPER_UNORM |
145			 NV50TIC_0_0_FMT_8_8);
146	} else {
147	PUSH_DATA (push, NV50TIC_0_0_MAPA_C0 | NV50TIC_0_0_TYPEA_UNORM |
148			 NV50TIC_0_0_MAPB_ZERO | NV50TIC_0_0_TYPEB_UNORM |
149			 NV50TIC_0_0_MAPG_ZERO | NV50TIC_0_0_TYPEG_UNORM |
150			 NV50TIC_0_0_MAPR_ZERO | NV50TIC_0_0_TYPER_UNORM |
151			 NV50TIC_0_0_FMT_8_8);
152	}
153	PUSH_DATA (push, (src->offset + packed_y));
154	PUSH_DATA (push, (src->offset + packed_y) >> 32 | mode);
155	PUSH_DATA (push, 0x00300000);
156	PUSH_DATA (push, width);
157	PUSH_DATA (push, (1 << NV50TIC_0_5_DEPTH_SHIFT) | height);
158	PUSH_DATA (push, 0x03000000);
159	PUSH_DATA (push, 0x00000000);
160	if (id == FOURCC_UYVY) {
161	PUSH_DATA (push, NV50TIC_0_0_MAPA_C2 | NV50TIC_0_0_TYPEA_UNORM |
162			 NV50TIC_0_0_MAPB_C0 | NV50TIC_0_0_TYPEB_UNORM |
163			 NV50TIC_0_0_MAPG_ZERO | NV50TIC_0_0_TYPEG_UNORM |
164			 NV50TIC_0_0_MAPR_ZERO | NV50TIC_0_0_TYPER_UNORM |
165			 NV50TIC_0_0_FMT_8_8_8_8);
166	} else {
167	PUSH_DATA (push, NV50TIC_0_0_MAPA_C3 | NV50TIC_0_0_TYPEA_UNORM |
168			 NV50TIC_0_0_MAPB_C1 | NV50TIC_0_0_TYPEB_UNORM |
169			 NV50TIC_0_0_MAPG_ZERO | NV50TIC_0_0_TYPEG_UNORM |
170			 NV50TIC_0_0_MAPR_ZERO | NV50TIC_0_0_TYPER_UNORM |
171			 NV50TIC_0_0_FMT_8_8_8_8);
172	}
173	PUSH_DATA (push, (src->offset + packed_y));
174	PUSH_DATA (push, (src->offset + packed_y) >> 32 | mode);
175	PUSH_DATA (push, 0x00300000);
176	PUSH_DATA (push, (width >> 1));
177	PUSH_DATA (push, (1 << NV50TIC_0_5_DEPTH_SHIFT) | height);
178	PUSH_DATA (push, 0x03000000);
179	PUSH_DATA (push, 0x00000000);
180	}
181
182	PUSH_DATAu(push, pNv->scratch, TSC_OFFSET, 16);
183	PUSH_DATA (push, NV50TSC_1_0_WRAPS_CLAMP_TO_EDGE |
184			 NV50TSC_1_0_WRAPT_CLAMP_TO_EDGE |
185			 NV50TSC_1_0_WRAPR_CLAMP_TO_EDGE);
186	PUSH_DATA (push, NV50TSC_1_1_MAGF_LINEAR |
187			 NV50TSC_1_1_MINF_LINEAR |
188			 NV50TSC_1_1_MIPF_NONE);
189	PUSH_DATA (push, 0x00000000);
190	PUSH_DATA (push, 0x00000000);
191	PUSH_DATA (push, 0x00000000);
192	PUSH_DATA (push, 0x00000000);
193	PUSH_DATA (push, 0x00000000);
194	PUSH_DATA (push, 0x00000000);
195	PUSH_DATA (push, NV50TSC_1_0_WRAPS_CLAMP_TO_EDGE |
196			 NV50TSC_1_0_WRAPT_CLAMP_TO_EDGE |
197			 NV50TSC_1_0_WRAPR_CLAMP_TO_EDGE);
198	PUSH_DATA (push, NV50TSC_1_1_MAGF_LINEAR |
199			 NV50TSC_1_1_MINF_LINEAR |
200			 NV50TSC_1_1_MIPF_NONE);
201	PUSH_DATA (push, 0x00000000);
202	PUSH_DATA (push, 0x00000000);
203	PUSH_DATA (push, 0x00000000);
204	PUSH_DATA (push, 0x00000000);
205	PUSH_DATA (push, 0x00000000);
206	PUSH_DATA (push, 0x00000000);
207
208	BEGIN_NV04(push, NV50_3D(FP_START_ID), 1);
209	PUSH_DATA (push, PFP_NV12);
210
211	BEGIN_NV04(push, NV50_3D(TIC_FLUSH), 1);
212	PUSH_DATA (push, 0);
213
214	BEGIN_NV04(push, NV50_3D(BIND_TIC(2)), 1);
215	PUSH_DATA (push, 1);
216	BEGIN_NV04(push, NV50_3D(BIND_TIC(2)), 1);
217	PUSH_DATA (push, 0x203);
218
219	PUSH_DATAu(push, pNv->scratch, PVP_DATA, 11);
220	PUSH_DATAf(push, 1.0);
221	PUSH_DATAf(push, 0.0);
222	PUSH_DATAf(push, 0.0);
223	PUSH_DATAf(push, 0.0);
224	PUSH_DATAf(push, 1.0);
225	PUSH_DATAf(push, 0.0);
226	PUSH_DATAf(push, 0.0);
227	PUSH_DATAf(push, 0.0);
228	PUSH_DATAf(push, 1.0);
229	PUSH_DATAf(push, 1.0 / width);
230	PUSH_DATAf(push, 1.0 / height);
231
232	if (pPriv->SyncToVBlank)
233		NV50SyncToVBlank(ppix, dstBox);
234
235	/* These are fixed point values in the 16.16 format. */
236	X1 = (float)(x1>>16)+(float)(x1&0xFFFF)/(float)0x10000;
237	Y1 = (float)(y1>>16)+(float)(y1&0xFFFF)/(float)0x10000;
238	X2 = (float)(x2>>16)+(float)(x2&0xFFFF)/(float)0x10000;
239	Y2 = (float)(y2>>16)+(float)(y2&0xFFFF)/(float)0x10000;
240
241	pbox = REGION_RECTS(clipBoxes);
242	nbox = REGION_NUM_RECTS(clipBoxes);
243	while(nbox--) {
244		float tx1=X1+(float)(pbox->x1 - dstBox->x1)*(X2-X1)/(float)(drw_w);
245		float tx2=X1+(float)(pbox->x2 - dstBox->x1)*(src_w)/(float)(drw_w);
246		float ty1=Y1+(float)(pbox->y1 - dstBox->y1)*(Y2-Y1)/(float)(drw_h);
247		float ty2=Y1+(float)(pbox->y2 - dstBox->y1)*(src_h)/(float)(drw_h);
248		int sx1=pbox->x1;
249		int sx2=pbox->x2;
250		int sy1=pbox->y1;
251		int sy2=pbox->y2;
252
253		if (nouveau_pushbuf_space(push, 64, 0, 0) ||
254		    nouveau_pushbuf_refn (push, refs, 3))
255			return BadImplementation;
256
257		/* NV50_3D_SCISSOR_VERT_T_SHIFT is wrong, because it was deducted with
258		* origin lying at the bottom left. This will be changed to _MIN_ and _MAX_
259		* later, because it is origin dependent.
260		*/
261		BEGIN_NV04(push, NV50_3D(SCISSOR_HORIZ(0)), 2);
262		PUSH_DATA (push, sx2 << NV50_3D_SCISSOR_HORIZ_MAX__SHIFT | sx1);
263		PUSH_DATA (push, sy2 << NV50_3D_SCISSOR_VERT_MAX__SHIFT | sy1 );
264		BEGIN_NV04(push, NV50_3D(VERTEX_BEGIN_GL), 1);
265		PUSH_DATA (push, NV50_3D_VERTEX_BEGIN_GL_PRIMITIVE_TRIANGLES);
266		PUSH_VTX1s(push, tx1, ty1, sx1, sy1);
267		PUSH_VTX1s(push, tx2+(tx2-tx1), ty1, sx2+(sx2-sx1), sy1);
268		PUSH_VTX1s(push, tx1, ty2+(ty2-ty1), sx1, sy2+(sy2-sy1));
269		BEGIN_NV04(push, NV50_3D(VERTEX_END_GL), 1);
270		PUSH_DATA (push, 0);
271
272		pbox++;
273	}
274
275	PUSH_KICK(push);
276	return Success;
277}
278
279void
280nv50_xv_video_stop(ScrnInfoPtr pScrn, pointer data, Bool exit)
281{
282}
283
284/* Reference color space transform data */
285struct REF_TRANSFORM {
286    float   RefLuma;
287    float   RefRCb;
288    float   RefRCr;
289    float   RefGCb;
290    float   RefGCr;
291    float   RefBCb;
292    float   RefBCr;
293} trans[] = {
294	{ 1.1643, 0.0, 1.5960, -0.3918, -0.8129, 2.0172, 0.0 }, /* BT.601 */
295	{ 1.1643, 0.0, 1.7927, -0.2132, -0.5329, 2.1124, 0.0 }  /* BT.709 */
296};
297
298#define RTFSaturation(a)   (1.0 + ((a)*1.0)/1000.0)
299#define RTFBrightness(a)   (((a)*1.0)/2000.0)
300#define RTFContrast(a)   (1.0 + ((a)*1.0)/1000.0)
301#define RTFHue(a)   (((a)*3.1416)/1000.0)
302
303void
304nv50_xv_csc_update(ScrnInfoPtr pScrn, NVPortPrivPtr pPriv)
305{
306	NVPtr pNv = NVPTR(pScrn);
307	struct nouveau_pushbuf *push = pNv->pushbuf;
308	const float Loff = -0.0627;
309	const float Coff = -0.502;
310	float yco, off[3], uco[3], vco[3];
311	float uvcosf, uvsinf;
312	float bright, cont;
313	int ref = pPriv->iturbt_709;
314
315	cont = RTFContrast(pPriv->contrast);
316	bright = RTFBrightness(pPriv->brightness);
317	uvcosf = RTFSaturation(pPriv->saturation) * cos(RTFHue(pPriv->hue));
318	uvsinf = RTFSaturation(pPriv->saturation) * sin(RTFHue(pPriv->hue));
319
320	yco = trans[ref].RefLuma * cont;
321	uco[0] = -trans[ref].RefRCr * uvsinf;
322	uco[1] = trans[ref].RefGCb * uvcosf - trans[ref].RefGCr * uvsinf;
323	uco[2] = trans[ref].RefBCb * uvcosf;
324	vco[0] = trans[ref].RefRCr * uvcosf;
325	vco[1] = trans[ref].RefGCb * uvsinf + trans[ref].RefGCr * uvcosf;
326	vco[2] = trans[ref].RefBCb * uvsinf;
327	off[0] = Loff * yco + Coff * (uco[0] + vco[0]) + bright;
328	off[1] = Loff * yco + Coff * (uco[1] + vco[1]) + bright;
329	off[2] = Loff * yco + Coff * (uco[2] + vco[2]) + bright;
330
331	if (pNv->Architecture >= NV_FERMI) {
332		nvc0_xv_csc_update(pNv, yco, off, uco, vco);
333		return;
334	}
335
336	if (nouveau_pushbuf_space(push, 64, 0, 0) ||
337	    nouveau_pushbuf_refn (push, &(struct nouveau_pushbuf_refn) {
338					pNv->scratch, NOUVEAU_BO_WR |
339					NOUVEAU_BO_VRAM }, 1))
340		return;
341
342	PUSH_DATAu(push, pNv->scratch, PFP_DATA, 10);
343	PUSH_DATAf(push, yco);
344	PUSH_DATAf(push, off[0]);
345	PUSH_DATAf(push, off[1]);
346	PUSH_DATAf(push, off[2]);
347	PUSH_DATAf(push, uco[0]);
348	PUSH_DATAf(push, uco[1]);
349	PUSH_DATAf(push, uco[2]);
350	PUSH_DATAf(push, vco[0]);
351	PUSH_DATAf(push, vco[1]);
352	PUSH_DATAf(push, vco[2]);
353}
354
355void
356nv50_xv_set_port_defaults(ScrnInfoPtr pScrn, NVPortPrivPtr pPriv)
357{
358	pPriv->videoStatus	= 0;
359	pPriv->grabbedByV4L	= FALSE;
360	pPriv->blitter		= FALSE;
361	pPriv->texture		= TRUE;
362	pPriv->doubleBuffer	= FALSE;
363	pPriv->SyncToVBlank	= TRUE;
364	pPriv->brightness	= 0;
365	pPriv->contrast		= 0;
366	pPriv->saturation	= 0;
367	pPriv->hue		= 0;
368	pPriv->iturbt_709	= 0;
369	pPriv->max_image_dim    = 8192;
370}
371
372int
373nv50_xv_port_attribute_set(ScrnInfoPtr pScrn, Atom attribute,
374			   INT32 value, pointer data)
375{
376	NVPortPrivPtr pPriv = (NVPortPrivPtr)data;
377
378	if (attribute == xvSyncToVBlank) {
379		if (value < 0 || value > 1)
380			return BadValue;
381		pPriv->SyncToVBlank = value;
382	} else
383	if (attribute == xvBrightness) {
384		if (value < -1000 || value > 1000)
385			return BadValue;
386		pPriv->brightness = value;
387	} else
388	if (attribute == xvContrast) {
389		if (value < -1000 || value > 1000)
390			return BadValue;
391		pPriv->contrast = value;
392	} else
393	if (attribute == xvSaturation) {
394		if (value < -1000 || value > 1000)
395			return BadValue;
396		pPriv->saturation = value;
397	} else
398	if (attribute == xvHue) {
399		if (value < -1000 || value > 1000)
400			return BadValue;
401		pPriv->hue = value;
402	} else
403	if (attribute == xvITURBT709) {
404		if (value < 0 || value > 1)
405			return BadValue;
406		pPriv->iturbt_709 = value;
407	} else
408	if (attribute == xvSetDefaults) {
409		nv50_xv_set_port_defaults(pScrn, pPriv);
410	} else
411		return BadMatch;
412
413	nv50_xv_csc_update(pScrn, pPriv);
414	return Success;
415}
416
417int
418nv50_xv_port_attribute_get(ScrnInfoPtr pScrn, Atom attribute,
419			   INT32 *value, pointer data)
420{
421	NVPortPrivPtr pPriv = (NVPortPrivPtr)data;
422
423	if (attribute == xvSyncToVBlank)
424		*value = (pPriv->SyncToVBlank) ? 1 : 0;
425	else
426	if (attribute == xvBrightness)
427		*value = pPriv->brightness;
428	else
429	if (attribute == xvContrast)
430		*value = pPriv->contrast;
431	else
432	if (attribute == xvSaturation)
433		*value = pPriv->saturation;
434	else
435	if (attribute == xvHue)
436		*value = pPriv->hue;
437	else
438	if (attribute == xvITURBT709)
439		*value = pPriv->iturbt_709;
440	else
441		return BadMatch;
442
443	return Success;
444}
445
446
447