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, xvColorspace;
40
41static XvFormatRec Formats[] = {
42	{15}, {16}, {24}, {30}
43};
44
45static const XvAttributeRec Attributes[] = {
46	{XvSettable | XvGettable, -1, 1, (char *)"XV_SYNC_TO_VBLANK"},
47	{XvSettable | XvGettable, 0, 1, (char *)"XV_COLORSPACE"}, /* BT.601, BT.709 */
48	//{XvSettable | XvGettable, -128, 127, (char *)"XV_BRIGHTNESS"},
49	//{XvSettable | XvGettable, 0, 255, (char *)"XV_CONTRAST"},
50};
51
52static const XvImageRec gen2_Images[] = {
53	XVIMAGE_YUY2,
54	XVIMAGE_UYVY,
55};
56
57static const XvImageRec gen3_Images[] = {
58	XVIMAGE_YUY2,
59	XVIMAGE_YV12,
60	XVIMAGE_I420,
61	XVIMAGE_UYVY,
62	XVMC_YUV,
63};
64
65static const XvImageRec gen4_Images[] = {
66	XVIMAGE_YUY2,
67	XVIMAGE_YV12,
68	XVIMAGE_I420,
69	XVIMAGE_NV12,
70	XVIMAGE_UYVY,
71	XVMC_YUV,
72};
73
74static const XvImageRec gen9_Images[] = {
75	XVIMAGE_YUY2,
76	XVIMAGE_YV12,
77	XVIMAGE_I420,
78	XVIMAGE_NV12,
79	XVIMAGE_UYVY,
80	XVIMAGE_AYUV,
81	XVMC_YUV,
82};
83
84static int sna_video_textured_stop(ddStopVideo_ARGS)
85{
86	struct sna_video *video = port->devPriv.ptr;
87
88	DBG(("%s()\n", __FUNCTION__));
89
90	RegionUninit(&video->clip);
91	sna_video_free_buffers(video);
92
93	return Success;
94}
95
96static int
97sna_video_textured_set_attribute(ddSetPortAttribute_ARGS)
98{
99	struct sna_video *video = port->devPriv.ptr;
100
101	if (attribute == xvBrightness) {
102		if (value < -128 || value > 127)
103			return BadValue;
104
105		video->brightness = value;
106	} else if (attribute == xvContrast) {
107		if (value < 0 || value > 255)
108			return BadValue;
109
110		video->contrast = value;
111	} else if (attribute == xvSyncToVblank) {
112		if (value < -1 || value > 1)
113			return BadValue;
114
115		video->SyncToVblank = value;
116	} else if (attribute == xvColorspace) {
117		if (value < 0 || value > 1)
118			return BadValue;
119
120		video->colorspace = value;
121	} else
122		return BadMatch;
123
124	return Success;
125}
126
127static int
128sna_video_textured_get_attribute(ddGetPortAttribute_ARGS)
129{
130	struct sna_video *video = port->devPriv.ptr;
131
132	if (attribute == xvBrightness)
133		*value = video->brightness;
134	else if (attribute == xvContrast)
135		*value = video->contrast;
136	else if (attribute == xvSyncToVblank)
137		*value = video->SyncToVblank;
138	else if (attribute == xvColorspace)
139		*value = video->colorspace;
140	else
141		return BadMatch;
142
143	return Success;
144}
145
146static int
147sna_video_textured_best_size(ddQueryBestSize_ARGS)
148{
149	if (vid_w > (drw_w << 1))
150		drw_w = vid_w >> 1;
151	if (vid_h > (drw_h << 1))
152		drw_h = vid_h >> 1;
153
154	*p_w = drw_w;
155	*p_h = drw_h;
156
157	return Success;
158}
159
160/*
161 * The source rectangle of the video is defined by (src_x, src_y, src_w, src_h).
162 * The dest rectangle of the video is defined by (drw_x, drw_y, drw_w, drw_h).
163 * id is a fourcc code for the format of the video.
164 * buf is the pointer to the source data in system memory.
165 * width and height are the w/h of the source data.
166 * If "sync" is true, then we must be finished with *buf at the point of return
167 * (which we always are).
168 * clip is the clipping region in screen space.
169 * data is a pointer to our port private.
170 * drawable is some Drawable, which might not be the screen in the case of
171 * compositing.  It's a new argument to the function in the 1.1 server.
172 */
173static int
174sna_video_textured_put_image(ddPutImage_ARGS)
175{
176	struct sna_video *video = port->devPriv.ptr;
177	struct sna *sna = video->sna;
178	struct sna_video_frame frame;
179	PixmapPtr pixmap = get_drawable_pixmap(draw);
180	unsigned int flags;
181	BoxRec dstBox;
182	RegionRec clip;
183	xf86CrtcPtr crtc;
184	int16_t dx, dy;
185	bool flush = false;
186	bool ret;
187
188	if (wedged(sna))
189		return BadAlloc;
190
191	init_video_region(&clip, draw, drw_x, drw_y, drw_w, drw_h);
192
193	ValidateGC(draw, gc);
194	RegionIntersect(&clip, &clip, gc->pCompositeClip);
195	if (!RegionNotEmpty(&clip))
196		return Success;
197
198	DBG(("%s: src=(%d, %d),(%d, %d), dst=(%d, %d),(%d, %d), id=%d, sizep=%dx%d, sync?=%d\n",
199	     __FUNCTION__,
200	     src_x, src_y, src_w, src_h,
201	     drw_x, drw_y, drw_w, drw_h,
202	     format->id, width, height, sync));
203
204	DBG(("%s: region %d:(%d, %d), (%d, %d)\n", __FUNCTION__,
205	     region_num_rects(&clip),
206	     clip.extents.x1, clip.extents.y1,
207	     clip.extents.x2, clip.extents.y2));
208
209	sna_video_frame_init(video, format->id, width, height, &frame);
210
211	if (!sna_video_clip_helper(video, &frame, &crtc, &dstBox,
212				   src_x, src_y, drw_x + draw->x, drw_y + draw->y,
213				   src_w, src_h, drw_w, drw_h,
214				   &clip))
215		return Success;
216
217	if (get_drawable_deltas(draw, pixmap, &dx, &dy))
218		RegionTranslate(&clip, dx, dy);
219
220	flags = MOVE_WRITE | __MOVE_FORCE;
221	if (clip.data)
222		flags |= MOVE_READ;
223
224	if (!sna_pixmap_move_area_to_gpu(pixmap, &clip.extents, flags)) {
225		DBG(("%s: attempting to render to a non-GPU pixmap\n",
226		     __FUNCTION__));
227		return BadAlloc;
228	}
229
230	sna_video_frame_set_rotation(video, &frame, RR_Rotate_0);
231
232	if (xvmc_passthrough(format->id)) {
233		DBG(("%s: using passthough, name=%d\n",
234		     __FUNCTION__, *(uint32_t *)buf));
235
236		frame.bo = kgem_create_for_name(&sna->kgem, *(uint32_t*)buf);
237		if (frame.bo == NULL) {
238			DBG(("%s: failed to open bo\n", __FUNCTION__));
239			return BadAlloc;
240		}
241
242		if (kgem_bo_size(frame.bo) < frame.size) {
243			DBG(("%s: bo size=%d, expected=%d\n",
244			     __FUNCTION__, kgem_bo_size(frame.bo), frame.size));
245			kgem_bo_destroy(&sna->kgem, frame.bo);
246			return BadAlloc;
247		}
248
249		frame.image.x1 = 0;
250		frame.image.y1 = 0;
251		frame.image.x2 = frame.width;
252		frame.image.y2 = frame.height;
253	} else {
254		if (!sna_video_copy_data(video, &frame, buf)) {
255			DBG(("%s: failed to copy frame\n", __FUNCTION__));
256			kgem_bo_destroy(&sna->kgem, frame.bo);
257			return BadAlloc;
258		}
259	}
260
261	if (crtc && video->SyncToVblank != 0 &&
262	    sna_pixmap_is_scanout(sna, pixmap)) {
263		kgem_set_mode(&sna->kgem, KGEM_RENDER, sna_pixmap(pixmap)->gpu_bo);
264		flush = sna_wait_for_scanline(sna, pixmap, crtc,
265					      &clip.extents);
266	}
267
268	ret = Success;
269	if (!sna->render.video(sna, video, &frame, &clip, pixmap)) {
270		DBG(("%s: failed to render video\n", __FUNCTION__));
271		ret = BadAlloc;
272	} else
273		DamageDamageRegion(&pixmap->drawable, &clip);
274
275	kgem_bo_destroy(&sna->kgem, frame.bo);
276
277	/* Push the frame to the GPU as soon as possible so
278	 * we can hit the next vsync.
279	 */
280	if (flush || sync)
281		kgem_submit(&sna->kgem);
282
283	RegionUninit(&clip);
284
285	return ret;
286}
287
288static int
289sna_video_textured_query(ddQueryImageAttributes_ARGS)
290{
291	int size, tmp;
292
293	if (*w > 8192)
294		*w = 8192;
295	if (*h > 8192)
296		*h = 8192;
297
298	*w = (*w + 1) & ~1;
299	if (offsets)
300		offsets[0] = 0;
301
302	switch (format->id) {
303		/* IA44 is for XvMC only */
304	case FOURCC_IA44:
305	case FOURCC_AI44:
306		if (pitches)
307			pitches[0] = *w;
308		size = *w * *h;
309		break;
310	case FOURCC_YV12:
311	case FOURCC_I420:
312		*h = (*h + 1) & ~1;
313		size = (*w + 3) & ~3;
314		if (pitches)
315			pitches[0] = size;
316		size *= *h;
317		if (offsets)
318			offsets[1] = size;
319		tmp = ((*w >> 1) + 3) & ~3;
320		if (pitches)
321			pitches[1] = pitches[2] = tmp;
322		tmp *= (*h >> 1);
323		size += tmp;
324		if (offsets)
325			offsets[2] = size;
326		size += tmp;
327		break;
328	case FOURCC_NV12:
329		*h = (*h + 1) & ~1;
330		size = (*w + 3) & ~3;
331		if (pitches)
332			pitches[0] = size;
333		size *= *h;
334		if (offsets)
335			offsets[1] = size;
336		tmp = (*w + 3) & ~3;
337		if (pitches)
338			pitches[1] = tmp;
339		tmp *= (*h >> 1);
340		size += tmp;
341		break;
342	case FOURCC_UYVY:
343	case FOURCC_YUY2:
344	default:
345		size = *w << 1;
346		if (pitches)
347			pitches[0] = size;
348		size *= *h;
349		break;
350	case FOURCC_AYUV:
351		size = *w << 2;
352		if (pitches)
353			pitches[0] = size;
354		size *= *h;
355		break;
356	case FOURCC_XVMC:
357		*h = (*h + 1) & ~1;
358		size = sizeof(uint32_t);
359		if (pitches)
360			pitches[0] = size;
361		break;
362	}
363
364	return size;
365}
366
367void sna_video_textured_setup(struct sna *sna, ScreenPtr screen)
368{
369	XvAdaptorPtr adaptor;
370	struct sna_video *video;
371	int nports, i;
372
373	if (sna->scrn->depth == 8) {
374		xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
375			   "Textured video not supported in 8bpp mode\n");
376		return;
377	}
378
379	if (!sna->render.video) {
380		xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
381			   "Textured video not supported on this hardware or backend\n");
382		return;
383	}
384
385	if (wedged(sna)) {
386		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
387			   "cannot enable XVideo whilst the GPU is wedged\n");
388		return;
389	}
390
391	adaptor = sna_xv_adaptor_alloc(sna);
392	if (adaptor == NULL)
393		return;
394
395	nports = 16;
396	if (sna->kgem.gen >= 060)
397		nports = 32;
398	if (sna->kgem.gen >= 0100)
399		nports = 64;
400
401	video = calloc(nports, sizeof(struct sna_video));
402	adaptor->pPorts = calloc(nports, sizeof(XvPortRec));
403	if (video == NULL || adaptor->pPorts == NULL) {
404		free(video);
405		free(adaptor->pPorts);
406		sna->xv.num_adaptors--;
407		return;
408	}
409
410	adaptor->type = XvInputMask | XvImageMask;
411	adaptor->pScreen = screen;
412	adaptor->name = (char *)"Intel(R) Textured Video";
413	adaptor->nEncodings = 1;
414	adaptor->pEncodings = xnfalloc(sizeof(XvEncodingRec));
415	adaptor->pEncodings[0].id = 0;
416	adaptor->pEncodings[0].pScreen = screen;
417	adaptor->pEncodings[0].name = (char *)"XV_IMAGE";
418	adaptor->pEncodings[0].width = sna->render.max_3d_size;
419	adaptor->pEncodings[0].height = sna->render.max_3d_size;
420	adaptor->pEncodings[0].rate.numerator = 1;
421	adaptor->pEncodings[0].rate.denominator = 1;
422	adaptor->pFormats = Formats;
423	adaptor->nFormats = sna_xv_fixup_formats(screen, Formats,
424						 ARRAY_SIZE(Formats));
425	adaptor->nAttributes = ARRAY_SIZE(Attributes);
426	adaptor->pAttributes = (XvAttributeRec *)Attributes;
427	if (sna->kgem.gen < 030) {
428		adaptor->nImages = ARRAY_SIZE(gen2_Images);
429		adaptor->pImages = (XvImageRec *)gen2_Images;
430	} else if (sna->kgem.gen < 040) {
431		adaptor->nImages = ARRAY_SIZE(gen3_Images);
432		adaptor->pImages = (XvImageRec *)gen3_Images;
433	} else if (sna->kgem.gen < 0110) {
434		adaptor->nImages = ARRAY_SIZE(gen4_Images);
435		adaptor->pImages = (XvImageRec *)gen4_Images;
436	} else {
437		adaptor->nImages = ARRAY_SIZE(gen9_Images);
438		adaptor->pImages = (XvImageRec *)gen9_Images;
439	}
440#if XORG_XV_VERSION < 2
441	adaptor->ddAllocatePort = sna_xv_alloc_port;
442	adaptor->ddFreePort = sna_xv_free_port;
443#endif
444	adaptor->ddPutVideo = NULL;
445	adaptor->ddPutStill = NULL;
446	adaptor->ddGetVideo = NULL;
447	adaptor->ddGetStill = NULL;
448	adaptor->ddStopVideo = sna_video_textured_stop;
449	adaptor->ddSetPortAttribute = sna_video_textured_set_attribute;
450	adaptor->ddGetPortAttribute = sna_video_textured_get_attribute;
451	adaptor->ddQueryBestSize = sna_video_textured_best_size;
452	adaptor->ddPutImage = sna_video_textured_put_image;
453	adaptor->ddQueryImageAttributes = sna_video_textured_query;
454
455	for (i = 0; i < nports; i++) {
456		struct sna_video *v = &video[i];
457		XvPortPtr port = &adaptor->pPorts[i];
458
459		v->sna = sna;
460		v->textured = true;
461		v->alignment = 4;
462		v->colorspace = 1; /* BT.709 */
463		v->SyncToVblank = (sna->flags & SNA_NO_WAIT) == 0;
464
465		RegionNull(&v->clip);
466
467		port->id = FakeClientID(0);
468		AddResource(port->id, XvGetRTPort(), port);
469
470		port->pAdaptor = adaptor;
471		port->pNotify =  NULL;
472		port->pDraw =  NULL;
473		port->client =  NULL;
474		port->grab.client =  NULL;
475		port->time = currentTime;
476		port->devPriv.ptr = v;
477	}
478	adaptor->base_id = adaptor->pPorts[0].id;
479	adaptor->nPorts = nports;
480
481	xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
482	xvContrast = MAKE_ATOM("XV_CONTRAST");
483	xvColorspace = MAKE_ATOM("XV_COLORSPACE");
484	xvSyncToVblank = MAKE_ATOM("XV_SYNC_TO_VBLANK");
485
486	DBG(("%s: '%s' initialized %d ports\n", __FUNCTION__, adaptor->name, adaptor->nPorts));
487}
488