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 "intel_options.h"
35
36#include <xf86drm.h>
37#include <xf86xv.h>
38#include <xf86Crtc.h>
39#include <X11/extensions/Xv.h>
40#include <fourcc.h>
41#include <i915_drm.h>
42#include <errno.h>
43
44#define fourcc_code(a,b,c,d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
45#define DRM_FORMAT_RGB565       fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
46#define DRM_FORMAT_XRGB8888     fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */
47#define DRM_FORMAT_YUYV         fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
48#define DRM_FORMAT_UYVY         fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */
49#define DRM_FORMAT_NV12         fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
50#define DRM_FORMAT_XYUV8888     fourcc_code('X', 'Y', 'U', 'V') /* [31:0] x:Y:U:V 8:8:8:8 little endian */
51
52#define has_hw_scaling(sna, video) ((sna)->kgem.gen < 071 || \
53				    (sna)->kgem.gen >= 0110)
54
55#define LOCAL_IOCTL_MODE_SETPLANE	DRM_IOWR(0xB7, struct local_mode_set_plane)
56struct local_mode_set_plane {
57	uint32_t plane_id;
58	uint32_t crtc_id;
59	uint32_t fb_id; /* fb object contains surface format type */
60	uint32_t flags;
61
62	/* Signed dest location allows it to be partially off screen */
63	int32_t crtc_x, crtc_y;
64	uint32_t crtc_w, crtc_h;
65
66	/* Source values are 16.16 fixed point */
67	uint32_t src_x, src_y;
68	uint32_t src_h, src_w;
69};
70
71#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, true)
72
73static Atom xvColorKey, xvAlwaysOnTop, xvSyncToVblank, xvColorspace;
74
75static XvFormatRec formats[] = { {8}, {15}, {16}, {24}, {30} };
76static const XvImageRec images[] = { XVIMAGE_YUY2, XVIMAGE_UYVY,
77				     XVMC_RGB888 };
78static const XvImageRec images_rgb565[] = { XVIMAGE_YUY2, XVIMAGE_UYVY,
79					    XVMC_RGB888, XVMC_RGB565 };
80static const XvImageRec images_nv12[] = { XVIMAGE_YUY2, XVIMAGE_UYVY,
81					  XVIMAGE_NV12, XVMC_RGB888, XVMC_RGB565 };
82static const XvImageRec images_ayuv[] = { XVIMAGE_AYUV, XVIMAGE_YUY2, XVIMAGE_UYVY,
83					  XVIMAGE_NV12, XVMC_RGB888, XVMC_RGB565 };
84static const XvAttributeRec attribs[] = {
85	{ XvSettable | XvGettable, 0, 1, (char *)"XV_COLORSPACE" }, /* BT.601, BT.709 */
86	{ XvSettable | XvGettable, 0, 0xffffff, (char *)"XV_COLORKEY" },
87	{ XvSettable | XvGettable, 0, 1, (char *)"XV_ALWAYS_ON_TOP" },
88};
89
90static int sna_video_sprite_stop(ddStopVideo_ARGS)
91{
92	struct sna_video *video = port->devPriv.ptr;
93	struct local_mode_set_plane s;
94	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(video->sna->scrn);
95	int i;
96
97	for (i = 0; i < video->sna->mode.num_real_crtc; i++) {
98		xf86CrtcPtr crtc = config->crtc[i];
99		int pipe;
100
101		pipe = sna_crtc_pipe(crtc);
102		assert(pipe < ARRAY_SIZE(video->bo));
103		if (video->bo[pipe] == NULL)
104			continue;
105
106		memset(&s, 0, sizeof(s));
107		s.plane_id = sna_crtc_to_sprite(crtc, video->idx);
108		if (drmIoctl(video->sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s))
109			xf86DrvMsg(video->sna->scrn->scrnIndex, X_ERROR,
110				   "failed to disable plane\n");
111
112		if (video->bo[pipe])
113			kgem_bo_destroy(&video->sna->kgem, video->bo[pipe]);
114		video->bo[pipe] = NULL;
115	}
116
117	sna_window_set_port((WindowPtr)draw, NULL);
118
119	return Success;
120}
121
122static int sna_video_sprite_set_attr(ddSetPortAttribute_ARGS)
123{
124	struct sna_video *video = port->devPriv.ptr;
125
126	if (attribute == xvColorKey) {
127		video->color_key_changed = ~0;
128		video->color_key = value;
129		RegionEmpty(&video->clip);
130		DBG(("COLORKEY = %ld\n", (long)value));
131	} else if (attribute == xvColorspace) {
132		video->colorspace_changed = ~0;
133		video->colorspace = value;
134		DBG(("COLORSPACE = %ld\n", (long)value));
135	} else if (attribute == xvSyncToVblank) {
136		DBG(("%s: SYNC_TO_VBLANK: %d -> %d\n", __FUNCTION__,
137		     video->SyncToVblank, !!value));
138		video->SyncToVblank = !!value;
139	} else if (attribute == xvAlwaysOnTop) {
140		DBG(("%s: ALWAYS_ON_TOP: %d -> %d\n", __FUNCTION__,
141		     video->AlwaysOnTop, !!value));
142		video->color_key_changed = ~0;
143		video->AlwaysOnTop = !!value;
144	} else
145		return BadMatch;
146
147	return Success;
148}
149
150static int sna_video_sprite_get_attr(ddGetPortAttribute_ARGS)
151{
152	struct sna_video *video = port->devPriv.ptr;
153
154	if (attribute == xvColorKey)
155		*value = video->color_key;
156	else if (attribute == xvColorspace)
157		*value = video->colorspace;
158	else if (attribute == xvAlwaysOnTop)
159		*value = video->AlwaysOnTop;
160	else if (attribute == xvSyncToVblank)
161		*value = video->SyncToVblank;
162	else
163		return BadMatch;
164
165	return Success;
166}
167
168static int sna_video_sprite_best_size(ddQueryBestSize_ARGS)
169{
170	struct sna_video *video = port->devPriv.ptr;
171	struct sna *sna = video->sna;
172
173	if (!has_hw_scaling(sna, video) && !sna->render.video) {
174		*p_w = vid_w;
175		*p_h = vid_h;
176	} else {
177		*p_w = drw_w;
178		*p_h = drw_h;
179	}
180
181	return Success;
182}
183
184static void
185update_dst_box_to_crtc_coords(struct sna *sna, xf86CrtcPtr crtc, BoxPtr dstBox)
186{
187	ScrnInfoPtr scrn = sna->scrn;
188	int tmp;
189
190	switch (crtc->rotation & 0xf) {
191	case RR_Rotate_0:
192		dstBox->x1 -= crtc->x;
193		dstBox->x2 -= crtc->x;
194		dstBox->y1 -= crtc->y;
195		dstBox->y2 -= crtc->y;
196		break;
197
198	case RR_Rotate_90:
199		tmp = dstBox->x1;
200		dstBox->x1 = dstBox->y1 - crtc->x;
201		dstBox->y1 = scrn->virtualX - tmp - crtc->y;
202		tmp = dstBox->x2;
203		dstBox->x2 = dstBox->y2 - crtc->x;
204		dstBox->y2 = scrn->virtualX - tmp - crtc->y;
205		tmp = dstBox->y1;
206		dstBox->y1 = dstBox->y2;
207		dstBox->y2 = tmp;
208		break;
209
210	case RR_Rotate_180:
211		tmp = dstBox->x1;
212		dstBox->x1 = scrn->virtualX - dstBox->x2 - crtc->x;
213		dstBox->x2 = scrn->virtualX - tmp - crtc->x;
214		tmp = dstBox->y1;
215		dstBox->y1 = scrn->virtualY - dstBox->y2 - crtc->y;
216		dstBox->y2 = scrn->virtualY - tmp - crtc->y;
217		break;
218
219	case RR_Rotate_270:
220		tmp = dstBox->x1;
221		dstBox->x1 = scrn->virtualY - dstBox->y1 - crtc->x;
222		dstBox->y1 = tmp - crtc->y;
223		tmp = dstBox->x2;
224		dstBox->x2 = scrn->virtualY - dstBox->y2 - crtc->x;
225		dstBox->y2 = tmp - crtc->y;
226		tmp = dstBox->x1;
227		dstBox->x1 = dstBox->x2;
228		dstBox->x2 = tmp;
229		break;
230	}
231}
232
233static uint32_t ckey_chan(uint32_t value, int weight)
234{
235	return value << 8 >> weight;
236}
237
238static uint32_t ckey_value_chan(uint32_t value, uint32_t mask,
239				int offset, int weight)
240{
241	return ckey_chan((value & mask) >> offset, weight);
242}
243
244static uint32_t ckey_value(struct sna *sna,
245			   struct sna_video *video)
246{
247	ScrnInfoPtr scrn = sna->scrn;
248	uint32_t r, g ,b;
249
250	if (scrn->depth == 8) {
251		r = g = b = video->color_key & 0xff;
252	} else {
253		r = ckey_value_chan(video->color_key, scrn->mask.red,
254				    scrn->offset.red, scrn->weight.red);
255		g = ckey_value_chan(video->color_key, scrn->mask.green,
256				    scrn->offset.green, scrn->weight.green);
257		b = ckey_value_chan(video->color_key, scrn->mask.blue,
258				    scrn->offset.blue, scrn->weight.blue);
259	}
260
261	return r << 16 | g << 8 | b;
262}
263
264static uint32_t ckey_mask_chan(int weight)
265{
266	return ckey_chan((1 << weight) - 1, weight);
267}
268
269static uint32_t ckey_mask(struct sna *sna)
270{
271	ScrnInfoPtr scrn = sna->scrn;
272	uint32_t r = ckey_mask_chan(scrn->weight.red);
273	uint32_t g = ckey_mask_chan(scrn->weight.green);
274	uint32_t b = ckey_mask_chan(scrn->weight.blue);
275
276	return 0x7 << 24 | r << 16 | g << 8 | b;
277}
278
279static bool
280sna_video_sprite_show(struct sna *sna,
281		      struct sna_video *video,
282		      struct sna_video_frame *frame,
283		      xf86CrtcPtr crtc,
284		      BoxPtr dstBox)
285{
286	struct local_mode_set_plane s;
287	int pipe = sna_crtc_pipe(crtc);
288
289	/* XXX handle video spanning multiple CRTC */
290
291	VG_CLEAR(s);
292	s.plane_id = sna_crtc_to_sprite(crtc, video->idx);
293
294#define DRM_I915_SET_SPRITE_COLORKEY 0x2b
295#define LOCAL_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct local_intel_sprite_colorkey)
296#define LOCAL_IOCTL_MODE_ADDFB2 DRM_IOWR(0xb8, struct local_mode_fb_cmd2)
297
298	if (video->color_key_changed & (1 << pipe) && video->has_color_key) {
299		struct local_intel_sprite_colorkey {
300			uint32_t plane_id;
301			uint32_t min_value;
302			uint32_t channel_mask;
303			uint32_t max_value;
304			uint32_t flags;
305		} set;
306
307		DBG(("%s: updating color key: %x\n",
308		     __FUNCTION__, video->color_key));
309
310		set.plane_id = s.plane_id;
311		set.min_value = ckey_value(sna, video);
312		set.max_value = 0; /* not used for destkey */
313		set.channel_mask = ckey_mask(sna);
314		set.flags = 0;
315		if (!video->AlwaysOnTop)
316			set.flags |= 1 << 1; /* COLORKEY_DESTINATION */
317
318		if (drmIoctl(sna->kgem.fd,
319			     LOCAL_IOCTL_I915_SET_SPRITE_COLORKEY,
320			     &set)) {
321			memset(&s, 0, sizeof(s));
322			s.plane_id = sna_crtc_to_sprite(crtc, video->idx);
323
324			/* try to disable the plane first */
325			if (drmIoctl(video->sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s))
326				xf86DrvMsg(video->sna->scrn->scrnIndex, X_ERROR,
327					   "failed to disable plane\n");
328
329			if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_I915_SET_SPRITE_COLORKEY, &set)) {
330				xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
331					   "failed to update color key, disabling future updates\n");
332				video->has_color_key = false;
333			}
334		}
335
336		video->color_key_changed &= ~(1 << pipe);
337	}
338
339	if (video->colorspace_changed & (1 << pipe)) {
340		DBG(("%s: updating colorspace: %x\n",
341		     __FUNCTION__, video->colorspace));
342
343		sna_crtc_set_sprite_colorspace(crtc, video->idx,
344					       video->colorspace);
345
346		video->colorspace_changed &= ~(1 << pipe);
347	}
348
349	update_dst_box_to_crtc_coords(sna, crtc, dstBox);
350	if (frame->rotation & (RR_Rotate_90 | RR_Rotate_270)) {
351		int tmp = frame->width;
352		frame->width = frame->height;
353		frame->height = tmp;
354	}
355
356	if (frame->bo->delta == 0) {
357		struct local_mode_fb_cmd2 {
358			uint32_t fb_id;
359			uint32_t width, height;
360			uint32_t pixel_format;
361			uint32_t flags;
362
363			uint32_t handles[4];
364			uint32_t pitches[4]; /* pitch for each plane */
365			uint32_t offsets[4]; /* offset of each plane */
366			uint64_t modifiers[4];
367		} f;
368		bool purged = true;
369
370		memset(&f, 0, sizeof(f));
371		f.width = frame->width;
372		f.height = frame->height;
373		f.flags = 1 << 1; /* +modifiers */
374
375		switch (frame->bo->tiling) {
376		case I915_TILING_NONE:
377			break;
378		case I915_TILING_X:
379			/* I915_FORMAT_MOD_X_TILED */
380			f.modifiers[0] = (uint64_t)1 << 56 | 1;
381			break;
382		case I915_TILING_Y:
383			/* I915_FORMAT_MOD_X_TILED */
384			f.modifiers[0] = (uint64_t)1 << 56 | 2;
385			break;
386		}
387
388		if (is_nv12_fourcc(frame->id)) {
389			f.handles[0] = frame->bo->handle;
390			f.handles[1] = frame->bo->handle;
391			f.pitches[0] = frame->pitch[1];
392			f.pitches[1] = frame->pitch[0];
393			f.offsets[0] = 0;
394			f.offsets[1] = frame->UBufOffset;
395		} else {
396			f.handles[0] = frame->bo->handle;
397			f.pitches[0] = frame->pitch[0];
398		}
399
400		switch (frame->id) {
401		case FOURCC_RGB565:
402			f.pixel_format = DRM_FORMAT_RGB565;
403			purged = sna->scrn->depth != 16;
404			break;
405		case FOURCC_RGB888:
406			f.pixel_format = DRM_FORMAT_XRGB8888;
407			purged = sna->scrn->depth != 24;
408			break;
409		case FOURCC_NV12:
410			f.pixel_format = DRM_FORMAT_NV12;
411			break;
412		case FOURCC_UYVY:
413			f.pixel_format = DRM_FORMAT_UYVY;
414			break;
415		case FOURCC_AYUV:
416			/* i915 doesn't support alpha, so we use XYUV */
417			f.pixel_format = DRM_FORMAT_XYUV8888;
418			break;
419		case FOURCC_YUY2:
420		default:
421			f.pixel_format = DRM_FORMAT_YUYV;
422			break;
423		}
424
425		DBG(("%s: creating new fb for handle=%d, width=%d, height=%d, stride=%d, format=%x\n",
426		     __FUNCTION__, frame->bo->handle, frame->width, frame->height,
427		     f.pitches[0], f.pixel_format));
428
429		if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_ADDFB2, &f)) {
430			ERR(("%s: ADDFB2 failed, errno=%d\n", __FUNCTION__, errno));
431			xf86DrvMsg(sna->scrn->scrnIndex,
432				   X_ERROR, "failed to add fb, unable to update video\n");
433			return false;
434		}
435
436		frame->bo->delta = f.fb_id;
437
438		frame->bo->scanout = true;
439		/* Don't allow the scanout to be cached if not suitable for front */
440		frame->bo->purged = purged;
441	}
442
443	assert(frame->bo->scanout);
444	assert(frame->bo->delta);
445
446	s.crtc_id = sna_crtc_id(crtc);
447	s.fb_id = frame->bo->delta;
448	s.flags = 0;
449	s.crtc_x = dstBox->x1;
450	s.crtc_y = dstBox->y1;
451	s.crtc_w = dstBox->x2 - dstBox->x1;
452	s.crtc_h = dstBox->y2 - dstBox->y1;
453	s.src_x = 0;
454	s.src_y = 0;
455	s.src_w = (frame->image.x2 - frame->image.x1) << 16;
456	s.src_h = (frame->image.y2 - frame->image.y1) << 16;
457
458	DBG(("%s: updating crtc=%d, plane=%d, handle=%d [fb %d], dst=(%d,%d)x(%d,%d), src=(%d,%d)x(%d,%d)\n",
459	     __FUNCTION__, s.crtc_id, s.plane_id, frame->bo->handle, s.fb_id,
460	     s.crtc_x, s.crtc_y, s.crtc_w, s.crtc_h,
461	     s.src_x >> 16, s.src_y >> 16, s.src_w >> 16, s.src_h >> 16));
462
463	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s)) {
464		DBG(("SET_PLANE failed: ret=%d\n", errno));
465		if (video->bo[pipe]) {
466			kgem_bo_destroy(&sna->kgem, video->bo[pipe]);
467			video->bo[pipe] = NULL;
468		}
469		return false;
470	}
471
472	__kgem_bo_clear_dirty(frame->bo);
473
474	if (video->bo[pipe])
475		kgem_bo_destroy(&sna->kgem, video->bo[pipe]);
476	video->bo[pipe] = kgem_bo_reference(frame->bo);
477	return true;
478}
479
480static bool need_scaling(const struct sna_video_frame *frame,
481			 const BoxRec *dst)
482{
483	/* SKL+ need the plane scaler even for unscaled NV12 */
484	return frame->id == FOURCC_NV12 ||
485		frame->src.x2 - frame->src.x1 != dst->x2 - dst->x1 ||
486		frame->src.y2 - frame->src.y1 != dst->y2 - dst->y1;
487}
488
489static int sna_video_sprite_put_image(ddPutImage_ARGS)
490{
491	struct sna_video *video = port->devPriv.ptr;
492	struct sna *sna = video->sna;
493	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
494	RegionRec clip;
495	BoxRec draw_extents;
496	int ret, i;
497
498	init_video_region(&clip, draw, drw_x, drw_y, drw_w, drw_h);
499	draw_extents = clip.extents;
500
501	DBG(("%s: always_on_top=%d\n", __FUNCTION__, video->AlwaysOnTop));
502	if (!video->AlwaysOnTop) {
503		ValidateGC(draw, gc);
504		RegionIntersect(&clip, &clip, gc->pCompositeClip);
505	}
506
507	DBG(("%s: src=(%d, %d),(%d, %d), dst=(%d, %d),(%d, %d), id=%d, sizep=%dx%d, sync?=%d\n",
508	     __FUNCTION__,
509	     src_x, src_y, src_w, src_h,
510	     drw_x, drw_y, drw_w, drw_h,
511	     format->id, width, height, sync));
512
513	DBG(("%s: region %d:(%d, %d), (%d, %d)\n", __FUNCTION__,
514	     region_num_rects(&clip),
515	     clip.extents.x1, clip.extents.y1,
516	     clip.extents.x2, clip.extents.y2));
517
518	if (RegionNil(&clip)) {
519		ret = Success;
520		goto err;
521	}
522
523	for (i = 0; i < video->sna->mode.num_real_crtc; i++) {
524		xf86CrtcPtr crtc = config->crtc[i];
525		struct sna_video_frame frame;
526		const int pipe = sna_crtc_pipe(crtc);
527		bool hw_scaling = has_hw_scaling(sna, video);
528		INT32 x1, x2, y1, y2;
529		Rotation rotation;
530		RegionRec reg;
531		BoxRec dst;
532		bool cache_bo;
533
534retry:
535		dst = draw_extents;
536
537		sna_video_frame_init(video, format->id, width, height, &frame);
538
539		reg.extents = crtc->bounds;
540		reg.data = NULL;
541		RegionIntersect(&reg, &reg, &clip);
542		if (RegionNil(&reg)) {
543off:
544			assert(pipe < ARRAY_SIZE(video->bo));
545			if (video->bo[pipe]) {
546				struct local_mode_set_plane s;
547				memset(&s, 0, sizeof(s));
548				s.plane_id = sna_crtc_to_sprite(crtc, video->idx);
549				if (drmIoctl(video->sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s))
550					xf86DrvMsg(video->sna->scrn->scrnIndex, X_ERROR,
551						   "failed to disable plane\n");
552				video->bo[pipe] = NULL;
553			}
554			continue;
555		}
556
557		x1 = src_x;
558		x2 = src_x + src_w;
559		y1 = src_y;
560		y2 = src_y + src_h;
561
562		ret = xf86XVClipVideoHelper(&dst, &x1, &x2, &y1, &y2,
563					    &reg, frame.width, frame.height);
564		RegionUninit(&reg);
565		if (!ret)
566			goto off;
567
568		frame.src.x1 = x1 >> 16;
569		frame.src.y1 = y1 >> 16;
570		frame.src.x2 = (x2 + 0xffff) >> 16;
571		frame.src.y2 = (y2 + 0xffff) >> 16;
572
573		frame.image.x1 = frame.src.x1 & ~1;
574		frame.image.x2 = ALIGN(frame.src.x2, 2);
575		if (is_planar_fourcc(frame.id)) {
576			frame.image.y1 = frame.src.y1 & ~1;
577			frame.image.y2 = ALIGN(frame.src.y2, 2);
578		} else {
579			frame.image.y1 = frame.src.y1;
580			frame.image.y2 = frame.src.y2;
581		}
582
583		/* if sprite can't handle rotation natively, store it for the copy func */
584		rotation = RR_Rotate_0;
585		if (!sna_crtc_set_sprite_rotation(crtc, video->idx, crtc->rotation)) {
586			sna_crtc_set_sprite_rotation(crtc, video->idx, RR_Rotate_0);
587			rotation = crtc->rotation;
588		}
589		sna_video_frame_set_rotation(video, &frame, rotation);
590
591		if (xvmc_passthrough(format->id)) {
592			DBG(("%s: using passthough, name=%d\n",
593			     __FUNCTION__, *(uint32_t *)buf));
594
595			if (*(uint32_t*)buf == 0)
596				goto err;
597
598			frame.bo = kgem_create_for_name(&sna->kgem, *(uint32_t*)buf);
599			if (frame.bo == NULL) {
600				ret = BadAlloc;
601				goto err;
602			}
603
604			if (kgem_bo_size(frame.bo) < frame.size) {
605				DBG(("%s: bo size=%d, expected=%d\n",
606				     __FUNCTION__, kgem_bo_size(frame.bo), frame.size));
607				kgem_bo_destroy(&sna->kgem, frame.bo);
608				ret = BadAlloc;
609				goto err;
610			}
611
612			frame.image.x1 = 0;
613			frame.image.y1 = 0;
614			frame.image.x2 = frame.width;
615			frame.image.y2 = frame.height;
616
617			cache_bo = false;
618		} else {
619			frame.bo = sna_video_buffer(video, &frame);
620			if (frame.bo == NULL) {
621				DBG(("%s: failed to allocate video bo\n", __FUNCTION__));
622				ret = BadAlloc;
623				goto err;
624			}
625
626			if (!sna_video_copy_data(video, &frame, buf)) {
627				DBG(("%s: failed to copy video data\n", __FUNCTION__));
628				ret = BadAlloc;
629				goto err;
630			}
631
632			cache_bo = true;
633		}
634
635		if (!hw_scaling && sna->render.video &&
636		    need_scaling(&frame, &dst)) {
637			ScreenPtr screen = to_screen_from_sna(sna);
638			PixmapPtr scaled;
639			RegionRec r;
640
641			r.extents.x1 = r.extents.y1 = 0;
642			r.extents.x2 = dst.x2 - dst.x1;
643			r.extents.y2 = dst.y2 - dst.y1;
644			r.data = NULL;
645
646			DBG(("%s: scaling from (%d, %d) to (%d, %d)\n",
647			     __FUNCTION__,
648			     frame.src.x2 - frame.src.x1,
649			     frame.src.y2 - frame.src.y1,
650			     r.extents.x2, r.extents.y2));
651
652			scaled = screen->CreatePixmap(screen,
653						      r.extents.x2,
654						      r.extents.y2,
655						      24,
656						      CREATE_PIXMAP_USAGE_SCRATCH);
657			if (scaled == NULL) {
658				ret = BadAlloc;
659				goto err;
660			}
661
662			if (!sna->render.video(sna, video, &frame, &r, scaled)) {
663				screen->DestroyPixmap(scaled);
664				ret = BadAlloc;
665				goto err;
666			}
667
668			if (cache_bo)
669				sna_video_buffer_fini(video);
670			else
671				kgem_bo_destroy(&sna->kgem, frame.bo);
672
673			frame.bo = kgem_bo_reference(__sna_pixmap_get_bo(scaled));
674			kgem_bo_submit(&sna->kgem, frame.bo);
675
676			frame.id = FOURCC_RGB888;
677			frame.src = frame.image = r.extents;
678			frame.width = frame.image.x2;
679			frame.height = frame.image.y2;
680			frame.pitch[0] = frame.bo->pitch;
681
682			screen->DestroyPixmap(scaled);
683			cache_bo = false;
684		}
685
686		ret = Success;
687		if (!sna_video_sprite_show(sna, video, &frame, crtc, &dst)) {
688			DBG(("%s: failed to show video frame\n", __FUNCTION__));
689			ret = BadAlloc;
690		}
691
692		if (cache_bo)
693			sna_video_buffer_fini(video);
694		else
695			kgem_bo_destroy(&sna->kgem, frame.bo);
696
697		if (ret != Success) {
698			/* retry with GPU scaling */
699			if (hw_scaling) {
700				hw_scaling = false;
701				goto retry;
702			}
703			goto err;
704		}
705	}
706
707	sna_video_fill_colorkey(video, &clip);
708	sna_window_set_port((WindowPtr)draw, port);
709
710	return Success;
711
712err:
713#if XORG_XV_VERSION < 2
714	(void)sna_video_sprite_stop(client, port, draw);
715#else
716	(void)sna_video_sprite_stop(port, draw);
717#endif
718	return ret;
719}
720
721static int sna_video_sprite_query(ddQueryImageAttributes_ARGS)
722{
723	struct sna_video *video = port->devPriv.ptr;
724	struct sna_video_frame frame;
725	int size, tmp;
726
727	if (*w > video->sna->mode.max_crtc_width)
728		*w = video->sna->mode.max_crtc_width;
729	if (*h > video->sna->mode.max_crtc_height)
730		*h = video->sna->mode.max_crtc_height;
731
732	if (offsets)
733		offsets[0] = 0;
734
735	switch (format->id) {
736	case FOURCC_RGB888:
737	case FOURCC_RGB565:
738		if (pitches) {
739			sna_video_frame_init(video, format->id, *w, *h, &frame);
740			sna_video_frame_set_rotation(video, &frame, RR_Rotate_0);
741			pitches[0] = frame.pitch[0];
742		}
743		size = 4;
744		break;
745
746	case FOURCC_NV12:
747		*h = (*h + 1) & ~1;
748		size = (*w + 3) & ~3;
749		if (pitches)
750			pitches[0] = size;
751		size *= *h;
752		if (offsets)
753			offsets[1] = size;
754		tmp = (*w + 3) & ~3;
755		if (pitches)
756			pitches[1] = tmp;
757		tmp *= (*h >> 1);
758		size += tmp;
759		break;
760	case FOURCC_AYUV:
761		tmp = *w << 2;
762		if (pitches)
763			pitches[0] = tmp;
764		size = *h * tmp;
765		break;
766	default:
767		*w = (*w + 1) & ~1;
768		*h = (*h + 1) & ~1;
769
770		size = *w << 1;
771		if (pitches)
772			pitches[0] = size;
773		size *= *h;
774		break;
775	}
776
777	return size;
778}
779
780static int sna_video_sprite_color_key(struct sna *sna)
781{
782	ScrnInfoPtr scrn = sna->scrn;
783	int color_key;
784
785	if (xf86GetOptValInteger(sna->Options, OPTION_VIDEO_KEY,
786				 &color_key)) {
787	} else if (xf86GetOptValInteger(sna->Options, OPTION_COLOR_KEY,
788					&color_key)) {
789	} else {
790		color_key =
791		    (1 << scrn->offset.red) |
792		    (1 << scrn->offset.green) |
793		    (((scrn->mask.blue >> scrn->offset.blue) - 1) << scrn->offset.blue);
794	}
795
796	return color_key & ((1 << scrn->depth) - 1);
797}
798
799static int sna_video_has_sprites(struct sna *sna)
800{
801	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
802	unsigned min;
803	int i;
804
805	DBG(("%s: num_crtc=%d\n", __FUNCTION__, sna->mode.num_real_crtc));
806
807	if (sna->mode.num_real_crtc == 0)
808		return 0;
809
810	min = -1;
811	for (i = 0; i < sna->mode.num_real_crtc; i++) {
812		unsigned count =  sna_crtc_count_sprites(config->crtc[i]);
813		DBG(("%s: %d sprites found on pipe %d\n", __FUNCTION__,
814		     count, sna_crtc_pipe(config->crtc[i])));
815		if (count < min)
816			min = count;
817	}
818
819	DBG(("%s: min=%d\n", __FUNCTION__, min));
820	return min;
821}
822
823void sna_video_sprite_setup(struct sna *sna, ScreenPtr screen)
824{
825	XvAdaptorPtr adaptor;
826	struct sna_video *video;
827	XvPortPtr port;
828	int count, i;
829
830	count = sna_video_has_sprites(sna);
831	if (!count)
832		return;
833
834	adaptor = sna_xv_adaptor_alloc(sna);
835	if (!adaptor)
836		return;
837
838	video = calloc(count, sizeof(*video));
839	port = calloc(count, sizeof(*port));
840	if (video == NULL || port == NULL) {
841		free(video);
842		free(port);
843		sna->xv.num_adaptors--;
844		return;
845	}
846
847	adaptor->type = XvInputMask | XvImageMask;
848	adaptor->pScreen = screen;
849	adaptor->name = (char *)"Intel(R) Video Sprite";
850	adaptor->nEncodings = 1;
851	adaptor->pEncodings = xnfalloc(sizeof(XvEncodingRec));
852	adaptor->pEncodings[0].id = 0;
853	adaptor->pEncodings[0].pScreen = screen;
854	adaptor->pEncodings[0].name = (char *)"XV_IMAGE";
855	adaptor->pEncodings[0].width = sna->mode.max_crtc_width;
856	adaptor->pEncodings[0].height = sna->mode.max_crtc_height;
857	adaptor->pEncodings[0].rate.numerator = 1;
858	adaptor->pEncodings[0].rate.denominator = 1;
859	adaptor->pFormats = formats;
860	adaptor->nFormats = sna_xv_fixup_formats(screen, formats,
861						 ARRAY_SIZE(formats));
862	adaptor->nAttributes = ARRAY_SIZE(attribs);
863	adaptor->pAttributes = (XvAttributeRec *)attribs;
864
865	if (sna_has_sprite_format(sna, DRM_FORMAT_XYUV8888)) {
866		adaptor->pImages = (XvImageRec *)images_ayuv;
867		adaptor->nImages = ARRAY_SIZE(images_ayuv);
868	} else if (sna_has_sprite_format(sna, DRM_FORMAT_NV12)) {
869		adaptor->pImages = (XvImageRec *)images_nv12;
870		adaptor->nImages = ARRAY_SIZE(images_nv12);
871	} else if (sna_has_sprite_format(sna, DRM_FORMAT_RGB565)) {
872		adaptor->pImages = (XvImageRec *)images_rgb565;
873		adaptor->nImages = ARRAY_SIZE(images_rgb565);
874	} else {
875		adaptor->pImages = (XvImageRec *)images;
876		adaptor->nImages = ARRAY_SIZE(images);
877	}
878
879#if XORG_XV_VERSION < 2
880	adaptor->ddAllocatePort = sna_xv_alloc_port;
881	adaptor->ddFreePort = sna_xv_free_port;
882#endif
883	adaptor->ddPutVideo = NULL;
884	adaptor->ddPutStill = NULL;
885	adaptor->ddGetVideo = NULL;
886	adaptor->ddGetStill = NULL;
887	adaptor->ddStopVideo = sna_video_sprite_stop;
888	adaptor->ddSetPortAttribute = sna_video_sprite_set_attr;
889	adaptor->ddGetPortAttribute = sna_video_sprite_get_attr;
890	adaptor->ddQueryBestSize = sna_video_sprite_best_size;
891	adaptor->ddPutImage = sna_video_sprite_put_image;
892	adaptor->ddQueryImageAttributes = sna_video_sprite_query;
893
894	adaptor->nPorts = count;
895	adaptor->pPorts = port;
896
897	for (i = 0; i < count; i++) {
898		port->id = FakeClientID(0);
899		AddResource(port->id, XvGetRTPort(), port);
900		port->pAdaptor = adaptor;
901		port->pNotify =  NULL;
902		port->pDraw =  NULL;
903		port->client =  NULL;
904		port->grab.client =  NULL;
905		port->time = currentTime;
906		port->devPriv.ptr = video;
907
908		video->sna = sna;
909		video->idx = i;
910		video->alignment = 64;
911		video->color_key = sna_video_sprite_color_key(sna);
912		video->color_key_changed = ~0;
913		video->colorspace = 1; /* BT.709 */
914		video->colorspace_changed = ~0;
915		video->has_color_key = true;
916		video->brightness = -19;	/* (255/219) * -16 */
917		video->contrast = 75;	/* 255/219 * 64 */
918		video->saturation = 146;	/* 128/112 * 128 */
919		video->desired_crtc = NULL;
920		video->gamma5 = 0xc0c0c0;
921		video->gamma4 = 0x808080;
922		video->gamma3 = 0x404040;
923		video->gamma2 = 0x202020;
924		video->gamma1 = 0x101010;
925		video->gamma0 = 0x080808;
926		RegionNull(&video->clip);
927		video->SyncToVblank = 1;
928
929		port++;
930		video++;
931	}
932	adaptor->base_id = adaptor->pPorts[0].id;
933
934	xvColorKey = MAKE_ATOM("XV_COLORKEY");
935	xvColorspace = MAKE_ATOM("XV_COLORSPACE");
936	xvAlwaysOnTop = MAKE_ATOM("XV_ALWAYS_ON_TOP");
937	xvSyncToVblank = MAKE_ATOM("XV_SYNC_TO_VBLANK");
938
939	DBG(("%s: '%s' initialized %d ports\n", __FUNCTION__, adaptor->name, adaptor->nPorts));
940}
941