1/***************************************************************************
2
3 Copyright 2000-2011 Intel Corporation.  All Rights Reserved.
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sub license, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
12
13 The above copyright notice and this permission notice (including the
14 next paragraph) shall be included in all copies or substantial portions
15 of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
21 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
23 THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 **************************************************************************/
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#include "sna.h"
32#include "sna_video.h"
33
34#include <xf86xv.h>
35#include <X11/extensions/Xv.h>
36
37#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, true)
38
39static Atom xvBrightness, xvContrast, xvSyncToVblank;
40
41static XvFormatRec Formats[] = {
42	{15}, {16}, {24}
43};
44
45static const XvAttributeRec Attributes[] = {
46	{XvSettable | XvGettable, -1, 1, (char *)"XV_SYNC_TO_VBLANK"},
47	//{XvSettable | XvGettable, -128, 127, (char *)"XV_BRIGHTNESS"},
48	//{XvSettable | XvGettable, 0, 255, (char *)"XV_CONTRAST"},
49};
50
51static const XvImageRec Images[] = {
52	XVIMAGE_YUY2,
53	XVIMAGE_YV12,
54	XVIMAGE_I420,
55	XVIMAGE_UYVY,
56	XVMC_YUV,
57};
58
59static int sna_video_textured_stop(ddStopVideo_ARGS)
60{
61	struct sna_video *video = port->devPriv.ptr;
62
63	DBG(("%s()\n", __FUNCTION__));
64
65	RegionUninit(&video->clip);
66	sna_video_free_buffers(video);
67
68	return Success;
69}
70
71static int
72sna_video_textured_set_attribute(ddSetPortAttribute_ARGS)
73{
74	struct sna_video *video = port->devPriv.ptr;
75
76	if (attribute == xvBrightness) {
77		if (value < -128 || value > 127)
78			return BadValue;
79
80		video->brightness = value;
81	} else if (attribute == xvContrast) {
82		if (value < 0 || value > 255)
83			return BadValue;
84
85		video->contrast = value;
86	} else if (attribute == xvSyncToVblank) {
87		if (value < -1 || value > 1)
88			return BadValue;
89
90		video->SyncToVblank = value;
91	} else
92		return BadMatch;
93
94	return Success;
95}
96
97static int
98sna_video_textured_get_attribute(ddGetPortAttribute_ARGS)
99{
100	struct sna_video *video = port->devPriv.ptr;
101
102	if (attribute == xvBrightness)
103		*value = video->brightness;
104	else if (attribute == xvContrast)
105		*value = video->contrast;
106	else if (attribute == xvSyncToVblank)
107		*value = video->SyncToVblank;
108	else
109		return BadMatch;
110
111	return Success;
112}
113
114static int
115sna_video_textured_best_size(ddQueryBestSize_ARGS)
116{
117	if (vid_w > (drw_w << 1))
118		drw_w = vid_w >> 1;
119	if (vid_h > (drw_h << 1))
120		drw_h = vid_h >> 1;
121
122	*p_w = drw_w;
123	*p_h = drw_h;
124
125	return Success;
126}
127
128/*
129 * The source rectangle of the video is defined by (src_x, src_y, src_w, src_h).
130 * The dest rectangle of the video is defined by (drw_x, drw_y, drw_w, drw_h).
131 * id is a fourcc code for the format of the video.
132 * buf is the pointer to the source data in system memory.
133 * width and height are the w/h of the source data.
134 * If "sync" is true, then we must be finished with *buf at the point of return
135 * (which we always are).
136 * clip is the clipping region in screen space.
137 * data is a pointer to our port private.
138 * drawable is some Drawable, which might not be the screen in the case of
139 * compositing.  It's a new argument to the function in the 1.1 server.
140 */
141static int
142sna_video_textured_put_image(ddPutImage_ARGS)
143{
144	struct sna_video *video = port->devPriv.ptr;
145	struct sna *sna = video->sna;
146	struct sna_video_frame frame;
147	PixmapPtr pixmap = get_drawable_pixmap(draw);
148	unsigned int flags;
149	BoxRec dstBox;
150	RegionRec clip;
151	xf86CrtcPtr crtc;
152	bool flush = false;
153	bool ret;
154
155	clip.extents.x1 = draw->x + drw_x;
156	clip.extents.y1 = draw->y + drw_y;
157	clip.extents.x2 = clip.extents.x1 + drw_w;
158	clip.extents.y2 = clip.extents.y1 + drw_h;
159	clip.data = NULL;
160
161	RegionIntersect(&clip, &clip, gc->pCompositeClip);
162	if (!RegionNotEmpty(&clip))
163		return Success;
164
165	DBG(("%s: src=(%d, %d),(%d, %d), dst=(%d, %d),(%d, %d), id=%d, sizep=%dx%d, sync?=%d\n",
166	     __FUNCTION__,
167	     src_x, src_y, src_w, src_h,
168	     drw_x, drw_y, drw_w, drw_h,
169	     format->id, width, height, sync));
170
171	DBG(("%s: region %d:(%d, %d), (%d, %d)\n", __FUNCTION__,
172	     region_num_rects(&clip),
173	     clip.extents.x1, clip.extents.y1,
174	     clip.extents.x2, clip.extents.y2));
175
176	sna_video_frame_init(video, format->id, width, height, &frame);
177
178	if (!sna_video_clip_helper(video, &frame, &crtc, &dstBox,
179				   src_x, src_y, drw_x + draw->x, drw_y + draw->y,
180				   src_w, src_h, drw_w, drw_h,
181				   &clip))
182		return Success;
183
184	flags = MOVE_WRITE | __MOVE_FORCE;
185	if (clip.data)
186		flags |= MOVE_READ;
187
188	if (!sna_pixmap_move_area_to_gpu(pixmap, &clip.extents, flags)) {
189		DBG(("%s: attempting to render to a non-GPU pixmap\n",
190		     __FUNCTION__));
191		return BadAlloc;
192	}
193
194	sna_video_frame_set_rotation(video, &frame, RR_Rotate_0);
195
196	if (xvmc_passthrough(format->id)) {
197		DBG(("%s: using passthough, name=%d\n",
198		     __FUNCTION__, *(uint32_t *)buf));
199
200		frame.bo = kgem_create_for_name(&sna->kgem, *(uint32_t*)buf);
201		if (frame.bo == NULL) {
202			DBG(("%s: failed to open bo\n", __FUNCTION__));
203			return BadAlloc;
204		}
205
206		if (kgem_bo_size(frame.bo) < frame.size) {
207			DBG(("%s: bo size=%d, expected=%d\n",
208			     __FUNCTION__, kgem_bo_size(frame.bo), frame.size));
209			kgem_bo_destroy(&sna->kgem, frame.bo);
210			return BadAlloc;
211		}
212
213		frame.image.x1 = 0;
214		frame.image.y1 = 0;
215		frame.image.x2 = frame.width;
216		frame.image.y2 = frame.height;
217	} else {
218		if (!sna_video_copy_data(video, &frame, buf)) {
219			DBG(("%s: failed to copy frame\n", __FUNCTION__));
220			kgem_bo_destroy(&sna->kgem, frame.bo);
221			return BadAlloc;
222		}
223	}
224
225	if (crtc && video->SyncToVblank != 0 &&
226	    sna_pixmap_is_scanout(sna, pixmap)) {
227		kgem_set_mode(&sna->kgem, KGEM_RENDER, sna_pixmap(pixmap)->gpu_bo);
228		flush = sna_wait_for_scanline(sna, pixmap, crtc,
229					      &clip.extents);
230	}
231
232	ret = Success;
233	if (!sna->render.video(sna, video, &frame, &clip, pixmap)) {
234		DBG(("%s: failed to render video\n", __FUNCTION__));
235		ret = BadAlloc;
236	} else
237		DamageDamageRegion(draw, &clip);
238
239	kgem_bo_destroy(&sna->kgem, frame.bo);
240
241	/* Push the frame to the GPU as soon as possible so
242	 * we can hit the next vsync.
243	 */
244	if (flush || sync)
245		kgem_submit(&sna->kgem);
246
247	RegionUninit(&clip);
248
249	return ret;
250}
251
252static int
253sna_video_textured_query(ddQueryImageAttributes_ARGS)
254{
255	int size, tmp;
256
257	if (*w > 8192)
258		*w = 8192;
259	if (*h > 8192)
260		*h = 8192;
261
262	*w = (*w + 1) & ~1;
263	if (offsets)
264		offsets[0] = 0;
265
266	switch (format->id) {
267		/* IA44 is for XvMC only */
268	case FOURCC_IA44:
269	case FOURCC_AI44:
270		if (pitches)
271			pitches[0] = *w;
272		size = *w * *h;
273		break;
274	case FOURCC_YV12:
275	case FOURCC_I420:
276		*h = (*h + 1) & ~1;
277		size = (*w + 3) & ~3;
278		if (pitches)
279			pitches[0] = size;
280		size *= *h;
281		if (offsets)
282			offsets[1] = size;
283		tmp = ((*w >> 1) + 3) & ~3;
284		if (pitches)
285			pitches[1] = pitches[2] = tmp;
286		tmp *= (*h >> 1);
287		size += tmp;
288		if (offsets)
289			offsets[2] = size;
290		size += tmp;
291		break;
292	case FOURCC_UYVY:
293	case FOURCC_YUY2:
294	default:
295		size = *w << 1;
296		if (pitches)
297			pitches[0] = size;
298		size *= *h;
299		break;
300	case FOURCC_XVMC:
301		*h = (*h + 1) & ~1;
302		size = sizeof(uint32_t);
303		if (pitches)
304			pitches[0] = size;
305		break;
306	}
307
308	return size;
309}
310
311void sna_video_textured_setup(struct sna *sna, ScreenPtr screen)
312{
313	XvAdaptorPtr adaptor;
314	struct sna_video *video;
315	int nports, i;
316
317	if (!sna->render.video) {
318		xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
319			   "Textured video not supported on this hardware\n");
320		return;
321	}
322
323	if (wedged(sna)) {
324		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
325			   "cannot enable XVideo whilst the GPU is wedged\n");
326		return;
327	}
328
329	adaptor = sna_xv_adaptor_alloc(sna);
330	if (adaptor == NULL)
331		return;
332
333	nports = 16;
334	if (sna->kgem.gen >= 060)
335		nports = 32;
336	if (sna->kgem.gen >= 0100)
337		nports = 64;
338
339	video = calloc(nports, sizeof(struct sna_video));
340	adaptor->pPorts = calloc(nports, sizeof(XvPortRec));
341	if (video == NULL || adaptor->pPorts == NULL) {
342		free(video);
343		free(adaptor->pPorts);
344		sna->xv.num_adaptors--;
345		return;
346	}
347
348	adaptor->type = XvInputMask | XvImageMask;
349	adaptor->pScreen = screen;
350	adaptor->name = (char *)"Intel(R) Textured Video";
351	adaptor->nEncodings = 1;
352	adaptor->pEncodings = xnfalloc(sizeof(XvEncodingRec));
353	adaptor->pEncodings[0].id = 0;
354	adaptor->pEncodings[0].pScreen = screen;
355	adaptor->pEncodings[0].name = (char *)"XV_IMAGE";
356	adaptor->pEncodings[0].width = sna->render.max_3d_size;
357	adaptor->pEncodings[0].height = sna->render.max_3d_size;
358	adaptor->pEncodings[0].rate.numerator = 1;
359	adaptor->pEncodings[0].rate.denominator = 1;
360	adaptor->pFormats = Formats;
361	adaptor->nFormats = sna_xv_fixup_formats(screen, Formats,
362						 ARRAY_SIZE(Formats));
363	adaptor->nAttributes = ARRAY_SIZE(Attributes);
364	adaptor->pAttributes = (XvAttributeRec *)Attributes;
365	adaptor->nImages = ARRAY_SIZE(Images);
366	adaptor->pImages = (XvImageRec *)Images;
367#if XORG_XV_VERSION < 2
368	adaptor->ddAllocatePort = sna_xv_alloc_port;
369	adaptor->ddFreePort = sna_xv_free_port;
370#endif
371	adaptor->ddPutVideo = NULL;
372	adaptor->ddPutStill = NULL;
373	adaptor->ddGetVideo = NULL;
374	adaptor->ddGetStill = NULL;
375	adaptor->ddStopVideo = sna_video_textured_stop;
376	adaptor->ddSetPortAttribute = sna_video_textured_set_attribute;
377	adaptor->ddGetPortAttribute = sna_video_textured_get_attribute;
378	adaptor->ddQueryBestSize = sna_video_textured_best_size;
379	adaptor->ddPutImage = sna_video_textured_put_image;
380	adaptor->ddQueryImageAttributes = sna_video_textured_query;
381
382	for (i = 0; i < nports; i++) {
383		struct sna_video *v = &video[i];
384		XvPortPtr port = &adaptor->pPorts[i];
385
386		v->sna = sna;
387		v->textured = true;
388		v->alignment = 4;
389		v->SyncToVblank = (sna->flags & SNA_NO_WAIT) == 0;
390
391		RegionNull(&v->clip);
392
393		port->id = FakeClientID(0);
394		AddResource(port->id, XvGetRTPort(), port);
395
396		port->pAdaptor = adaptor;
397		port->pNotify =  NULL;
398		port->pDraw =  NULL;
399		port->client =  NULL;
400		port->grab.client =  NULL;
401		port->time = currentTime;
402		port->devPriv.ptr = v;
403	}
404	adaptor->base_id = adaptor->pPorts[0].id;
405	adaptor->nPorts = nports;
406
407	xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
408	xvContrast = MAKE_ATOM("XV_CONTRAST");
409	xvSyncToVblank = MAKE_ATOM("XV_SYNC_TO_VBLANK");
410
411	DBG(("%s: '%s' initialized %d ports\n", __FUNCTION__, adaptor->name, adaptor->nPorts));
412}
413