1/***************************************************************************
2
3 Copyright 2000 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/*
28 * i830_video.c: i830/i845 Xv driver.
29 *
30 * Copyright © 2002 by Alan Hourihane and David Dawes
31 *
32 * Authors:
33 *	Alan Hourihane <alanh@tungstengraphics.com>
34 *	David Dawes <dawes@xfree86.org>
35 *
36 * Derived from i810 Xv driver:
37 *
38 * Authors of i810 code:
39 * 	Jonathan Bian <jonathan.bian@intel.com>
40 *      Offscreen Images:
41 *        Matt Sottek <matthew.j.sottek@intel.com>
42 */
43
44/*
45 * XXX Could support more formats.
46 */
47
48#ifdef HAVE_CONFIG_H
49#include "config.h"
50#endif
51
52#include <inttypes.h>
53#include <math.h>
54#include <string.h>
55#include <assert.h>
56#include <errno.h>
57
58#include "xorg-server.h"
59#include "xf86.h"
60#include "xf86_OSproc.h"
61#include "compiler.h"
62#include "xf86Pci.h"
63#include "xf86fbman.h"
64#include "xf86drm.h"
65#include "regionstr.h"
66#include "randrstr.h"
67#include "windowstr.h"
68#include "damage.h"
69#include "intel.h"
70#include "intel_video.h"
71#include "i830_reg.h"
72#include "xf86xv.h"
73#include <X11/extensions/Xv.h>
74#include "dixstruct.h"
75#include "fourcc.h"
76#include "intel_video_overlay.h"
77
78/* overlay debugging printf function */
79#if 0
80#define OVERLAY_DEBUG ErrorF
81#else
82#define OVERLAY_DEBUG if (0) ErrorF
83#endif
84
85/* kernel modesetting overlay functions */
86static Bool intel_has_overlay(intel_screen_private *intel)
87{
88	struct drm_i915_getparam gp;
89	int has_overlay = 0;
90	int ret;
91
92	gp.param = I915_PARAM_HAS_OVERLAY;
93	gp.value = &has_overlay;
94	ret = drmCommandWriteRead(intel->drmSubFD, DRM_I915_GETPARAM, &gp, sizeof(gp));
95
96	return ret == 0 && !! has_overlay;
97}
98
99static Bool intel_overlay_update_attrs(intel_screen_private *intel)
100{
101	intel_adaptor_private *adaptor_priv = intel_get_adaptor_private(intel);
102	struct drm_intel_overlay_attrs attrs;
103
104	attrs.flags = I915_OVERLAY_UPDATE_ATTRS;
105	attrs.brightness = adaptor_priv->brightness;
106	attrs.contrast = adaptor_priv->contrast;
107	attrs.saturation = adaptor_priv->saturation;
108	attrs.color_key = adaptor_priv->colorKey;
109	attrs.gamma0 = adaptor_priv->gamma0;
110	attrs.gamma1 = adaptor_priv->gamma1;
111	attrs.gamma2 = adaptor_priv->gamma2;
112	attrs.gamma3 = adaptor_priv->gamma3;
113	attrs.gamma4 = adaptor_priv->gamma4;
114	attrs.gamma5 = adaptor_priv->gamma5;
115
116	return drmCommandWriteRead(intel->drmSubFD, DRM_I915_OVERLAY_ATTRS,
117				   &attrs, sizeof(attrs)) == 0;
118}
119
120void intel_video_overlay_off(intel_screen_private *intel)
121{
122	struct drm_intel_overlay_put_image request;
123	int ret;
124
125	request.flags = 0;
126
127	ret = drmCommandWrite(intel->drmSubFD, DRM_I915_OVERLAY_PUT_IMAGE,
128			      &request, sizeof(request));
129	(void) ret;
130}
131static int
132intel_video_overlay_set_port_attribute(ScrnInfoPtr scrn,
133                                       Atom attribute, INT32 value, pointer data)
134{
135	intel_adaptor_private *adaptor_priv = (intel_adaptor_private *) data;
136	intel_screen_private *intel = intel_get_screen_private(scrn);
137
138	if (attribute == intel_xv_Brightness) {
139		if ((value < -128) || (value > 127))
140			return BadValue;
141		adaptor_priv->brightness = value;
142		OVERLAY_DEBUG("BRIGHTNESS\n");
143	} else if (attribute == intel_xv_Contrast) {
144		if ((value < 0) || (value > 255))
145			return BadValue;
146		adaptor_priv->contrast = value;
147		OVERLAY_DEBUG("CONTRAST\n");
148	} else if (attribute == intel_xv_Saturation) {
149		if ((value < 0) || (value > 1023))
150			return BadValue;
151		adaptor_priv->saturation = value;
152	} else if (attribute == intel_xv_Pipe) {
153		xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
154		if ((value < -1) || (value >= xf86_config->num_crtc))
155			return BadValue;
156		if (value < 0)
157			adaptor_priv->desired_crtc = NULL;
158		else
159			adaptor_priv->desired_crtc = xf86_config->crtc[value];
160	} else if (attribute == intel_xv_Gamma0 && (INTEL_INFO(intel)->gen >= 030)) {
161		adaptor_priv->gamma0 = value;
162	} else if (attribute == intel_xv_Gamma1 && (INTEL_INFO(intel)->gen >= 030)) {
163		adaptor_priv->gamma1 = value;
164	} else if (attribute == intel_xv_Gamma2 && (INTEL_INFO(intel)->gen >= 030)) {
165		adaptor_priv->gamma2 = value;
166	} else if (attribute == intel_xv_Gamma3 && (INTEL_INFO(intel)->gen >= 030)) {
167		adaptor_priv->gamma3 = value;
168	} else if (attribute == intel_xv_Gamma4 && (INTEL_INFO(intel)->gen >= 030)) {
169		adaptor_priv->gamma4 = value;
170	} else if (attribute == intel_xv_Gamma5 && (INTEL_INFO(intel)->gen >= 030)) {
171		adaptor_priv->gamma5 = value;
172	} else if (attribute == intel_xv_ColorKey) {
173		adaptor_priv->colorKey = value;
174		OVERLAY_DEBUG("COLORKEY\n");
175	} else
176		return BadMatch;
177
178	if ((attribute == intel_xv_Gamma0 ||
179	     attribute == intel_xv_Gamma1 ||
180	     attribute == intel_xv_Gamma2 ||
181	     attribute == intel_xv_Gamma3 ||
182	     attribute == intel_xv_Gamma4 ||
183	     attribute == intel_xv_Gamma5) && (INTEL_INFO(intel)->gen >= 030)) {
184		OVERLAY_DEBUG("GAMMA\n");
185	}
186
187	if (!intel_overlay_update_attrs(intel))
188		return BadValue;
189
190	if (attribute == intel_xv_ColorKey)
191		REGION_EMPTY(scrn->pScreen, &adaptor_priv->clip);
192
193	return Success;
194}
195
196static Bool
197intel_overlay_put_image(intel_screen_private *intel,
198                        xf86CrtcPtr crtc,
199                        int id, short width, short height,
200                        int dstPitch, int dstPitch2,
201                        BoxPtr dstBox, short src_w, short src_h, short drw_w,
202                        short drw_h)
203{
204	intel_adaptor_private *adaptor_priv = intel_get_adaptor_private(intel);
205	struct drm_intel_overlay_put_image request;
206	int ret;
207	int planar = is_planar_fourcc(id);
208	float scale;
209	dri_bo *tmp;
210
211	request.flags = I915_OVERLAY_ENABLE;
212
213	request.bo_handle = adaptor_priv->buf->handle;
214	if (planar) {
215		request.stride_Y = dstPitch2;
216		request.stride_UV = dstPitch;
217	} else {
218		request.stride_Y = dstPitch;
219		request.stride_UV = 0;
220	}
221	request.offset_Y = adaptor_priv->YBufOffset;
222	request.offset_U = adaptor_priv->UBufOffset;
223	request.offset_V = adaptor_priv->VBufOffset;
224	OVERLAY_DEBUG("off_Y: %i, off_U: %i, off_V: %i\n", request.offset_Y,
225		      request.offset_U, request.offset_V);
226
227	request.crtc_id = intel_crtc_id(crtc);
228	request.dst_x = dstBox->x1;
229	request.dst_y = dstBox->y1;
230	request.dst_width = dstBox->x2 - dstBox->x1;
231	request.dst_height = dstBox->y2 - dstBox->y1;
232
233	request.src_width = width;
234	request.src_height = height;
235	/* adjust src dimensions */
236	if (request.dst_height > 1) {
237		scale = ((float)request.dst_height - 1) / ((float)drw_h - 1);
238		request.src_scan_height = src_h * scale;
239	} else
240		request.src_scan_height = 1;
241
242	if (request.dst_width > 1) {
243		scale = ((float)request.dst_width - 1) / ((float)drw_w - 1);
244		request.src_scan_width = src_w * scale;
245	} else
246		request.src_scan_width = 1;
247
248	if (planar) {
249		request.flags |= I915_OVERLAY_YUV_PLANAR | I915_OVERLAY_YUV420;
250	} else {
251		request.flags |= I915_OVERLAY_YUV_PACKED | I915_OVERLAY_YUV422;
252		if (id == FOURCC_UYVY)
253			request.flags |= I915_OVERLAY_Y_SWAP;
254	}
255
256	ret = drmCommandWrite(intel->drmSubFD, DRM_I915_OVERLAY_PUT_IMAGE,
257			      &request, sizeof(request));
258	if (ret)
259		return FALSE;
260
261	if (!adaptor_priv->reusable) {
262		drm_intel_bo_unreference(adaptor_priv->buf);
263		adaptor_priv->buf = NULL;
264		adaptor_priv->reusable = TRUE;
265	}
266
267	tmp = adaptor_priv->old_buf[1];
268	adaptor_priv->old_buf[1] = adaptor_priv->old_buf[0];
269	adaptor_priv->old_buf[0] = adaptor_priv->buf;
270	adaptor_priv->buf = tmp;
271
272	return TRUE;
273}
274
275static void
276intel_update_dst_box_to_crtc_coords(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
277				    BoxPtr dstBox)
278{
279	int tmp;
280
281	/* for overlay, we should take it from crtc's screen
282	 * coordinate to current crtc's display mode.
283	 * yeah, a bit confusing.
284	 */
285	switch (crtc->rotation & 0xf) {
286	case RR_Rotate_0:
287		dstBox->x1 -= crtc->x;
288		dstBox->x2 -= crtc->x;
289		dstBox->y1 -= crtc->y;
290		dstBox->y2 -= crtc->y;
291		break;
292	case RR_Rotate_90:
293		tmp = dstBox->x1;
294		dstBox->x1 = dstBox->y1 - crtc->x;
295		dstBox->y1 = scrn->virtualX - tmp - crtc->y;
296		tmp = dstBox->x2;
297		dstBox->x2 = dstBox->y2 - crtc->x;
298		dstBox->y2 = scrn->virtualX - tmp - crtc->y;
299		tmp = dstBox->y1;
300		dstBox->y1 = dstBox->y2;
301		dstBox->y2 = tmp;
302		break;
303	case RR_Rotate_180:
304		tmp = dstBox->x1;
305		dstBox->x1 = scrn->virtualX - dstBox->x2 - crtc->x;
306		dstBox->x2 = scrn->virtualX - tmp - crtc->x;
307		tmp = dstBox->y1;
308		dstBox->y1 = scrn->virtualY - dstBox->y2 - crtc->y;
309		dstBox->y2 = scrn->virtualY - tmp - crtc->y;
310		break;
311	case RR_Rotate_270:
312		tmp = dstBox->x1;
313		dstBox->x1 = scrn->virtualY - dstBox->y1 - crtc->x;
314		dstBox->y1 = tmp - crtc->y;
315		tmp = dstBox->x2;
316		dstBox->x2 = scrn->virtualY - dstBox->y2 - crtc->x;
317		dstBox->y2 = tmp - crtc->y;
318		tmp = dstBox->x1;
319		dstBox->x1 = dstBox->x2;
320		dstBox->x2 = tmp;
321		break;
322	}
323
324	return;
325}
326
327static Bool
328intel_video_overlay_display(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
329                            int id, short width, short height,
330                            int dstPitch, int dstPitch2,
331                            BoxPtr dstBox, short src_w, short src_h, short drw_w,
332                            short drw_h)
333{
334	intel_screen_private *intel = intel_get_screen_private(scrn);
335	int tmp;
336
337	OVERLAY_DEBUG("I830DisplayVideo: %dx%d (pitch %d)\n", width, height,
338		      dstPitch);
339
340	/*
341	 * If the video isn't visible on any CRTC, turn it off
342	 */
343	if (!crtc) {
344		intel_video_overlay_off(intel);
345		return TRUE;
346	}
347
348	intel_update_dst_box_to_crtc_coords(scrn, crtc, dstBox);
349
350	if (crtc->rotation & (RR_Rotate_90 | RR_Rotate_270)) {
351		tmp = width;
352		width = height;
353		height = tmp;
354		tmp = drw_w;
355		drw_w = drw_h;
356		drw_h = tmp;
357		tmp = src_w;
358		src_w = src_h;
359		src_h = tmp;
360	}
361
362	return intel_overlay_put_image(intel, crtc, id,
363					 width, height,
364					 dstPitch, dstPitch2, dstBox,
365					 src_w, src_h, drw_w, drw_h);
366}
367
368static int
369intel_video_overlay_put_image(ScrnInfoPtr scrn,
370                              short src_x, short src_y,
371                              short drw_x, short drw_y,
372                              short src_w, short src_h,
373                              short drw_w, short drw_h,
374                              int id, unsigned char *buf,
375                              short width, short height,
376                              Bool sync, RegionPtr clipBoxes, pointer data,
377                              DrawablePtr drawable)
378{
379	intel_adaptor_private *adaptor_priv = (intel_adaptor_private *) data;
380	int dstPitch, dstPitch2;
381	BoxRec dstBox;
382	xf86CrtcPtr crtc;
383	int top, left, npixels, nlines;
384
385#if 0
386	ErrorF("I830PutImage: src: (%d,%d)(%d,%d), dst: (%d,%d)(%d,%d)\n"
387	       "width %d, height %d\n", src_x, src_y, src_w, src_h, drw_x,
388	       drw_y, drw_w, drw_h, width, height);
389#endif
390
391	/* If dst width and height are less than 1/8th the src size, the
392	 * src/dst scale factor becomes larger than 8 and doesn't fit in
393	 * the scale register. */
394	if (src_w >= (drw_w * 8))
395		drw_w = src_w / 7;
396
397	if (src_h >= (drw_h * 8))
398		drw_h = src_h / 7;
399
400	if (!intel_clip_video_helper(scrn,
401				    adaptor_priv,
402				    &crtc,
403				    &dstBox,
404				    src_x, src_y, drw_x, drw_y,
405				    src_w, src_h, drw_w, drw_h,
406				    id,
407				    &top, &left, &npixels, &nlines, clipBoxes,
408				    width, height))
409		return Success;
410
411	/* overlay can't handle rotation natively, store it for the copy func */
412	if (crtc)
413		adaptor_priv->rotation = crtc->rotation;
414	else {
415		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
416			   "Fail to clip video to any crtc!\n");
417		return Success;
418	}
419
420	if (!intel_video_copy_data(scrn, adaptor_priv, width, height,
421				  &dstPitch, &dstPitch2,
422				  top, left, npixels, nlines, id, buf))
423		return BadAlloc;
424
425	if (!intel_video_overlay_display
426	    (scrn, crtc, id, width, height, dstPitch, dstPitch2,
427	     &dstBox, src_w, src_h, drw_w, drw_h))
428		return BadAlloc;
429
430	/* update cliplist */
431	if (!REGION_EQUAL(scrn->pScreen, &adaptor_priv->clip, clipBoxes)) {
432		REGION_COPY(scrn->pScreen, &adaptor_priv->clip, clipBoxes);
433		xf86XVFillKeyHelperDrawable(drawable,
434					    adaptor_priv->colorKey,
435					    clipBoxes);
436	}
437
438	adaptor_priv->videoStatus = CLIENT_VIDEO_ON;
439
440	return Success;
441}
442
443XF86VideoAdaptorPtr intel_video_overlay_setup_image(ScreenPtr screen)
444{
445	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
446	intel_screen_private *intel = intel_get_screen_private(scrn);
447	XF86VideoAdaptorPtr adapt;
448	intel_adaptor_private *adaptor_priv;
449	XF86AttributePtr att;
450
451	/* Set up overlay video if it is available */
452	intel->use_overlay = intel_has_overlay(intel);
453	if (!intel->use_overlay)
454                return NULL;
455
456	OVERLAY_DEBUG("intel_video_overlay_setup_image\n");
457
458	if (!(adapt = calloc(1,
459			     sizeof(XF86VideoAdaptorRec) +
460			     sizeof(intel_adaptor_private) +
461			     sizeof(DevUnion))))
462		return NULL;
463
464	adapt->type = XvWindowMask | XvInputMask | XvImageMask;
465	adapt->flags = VIDEO_OVERLAID_IMAGES /*| VIDEO_CLIP_TO_VIEWPORT */ ;
466	adapt->name = "Intel(R) Video Overlay";
467	adapt->nEncodings = 1;
468	adapt->pEncodings = xnfalloc(sizeof(intel_xv_dummy_encoding));
469	memcpy(adapt->pEncodings, intel_xv_dummy_encoding, sizeof(intel_xv_dummy_encoding));
470	if (IS_845G(intel) || IS_I830(intel)) {
471		adapt->pEncodings->width = IMAGE_MAX_WIDTH_LEGACY;
472		adapt->pEncodings->height = IMAGE_MAX_HEIGHT_LEGACY;
473	}
474	adapt->nFormats = NUM_FORMATS;
475	adapt->pFormats = intel_xv_formats;
476	adapt->nPorts = 1;
477	adapt->pPortPrivates = (DevUnion *) (&adapt[1]);
478
479	adaptor_priv = (intel_adaptor_private *)&adapt->pPortPrivates[1];
480
481	adapt->pPortPrivates[0].ptr = (pointer) (adaptor_priv);
482	adapt->nAttributes = NUM_ATTRIBUTES;
483	if (INTEL_INFO(intel)->gen >= 030)
484		adapt->nAttributes += GAMMA_ATTRIBUTES;	/* has gamma */
485	adapt->pAttributes =
486	    xnfalloc(sizeof(XF86AttributeRec) * adapt->nAttributes);
487	/* Now copy the attributes */
488	att = adapt->pAttributes;
489	memcpy((char *)att, (char *)intel_xv_attributes,
490	       sizeof(XF86AttributeRec) * NUM_ATTRIBUTES);
491	att += NUM_ATTRIBUTES;
492	if (INTEL_INFO(intel)->gen >= 030) {
493		memcpy((char *)att, (char *)intel_xv_gamma_attributes,
494		       sizeof(XF86AttributeRec) * GAMMA_ATTRIBUTES);
495	}
496	adapt->nImages = NUM_IMAGES - XVMC_IMAGE;
497
498	adapt->pImages = intel_xv_images;
499	adapt->PutVideo = NULL;
500	adapt->PutStill = NULL;
501	adapt->GetVideo = NULL;
502	adapt->GetStill = NULL;
503	adapt->StopVideo = intel_video_stop_video;
504	adapt->SetPortAttribute = intel_video_overlay_set_port_attribute;
505	adapt->GetPortAttribute = intel_video_get_port_attribute;
506	adapt->QueryBestSize = intel_video_query_best_size;
507	adapt->PutImage = intel_video_overlay_put_image;
508	adapt->QueryImageAttributes = intel_video_query_image_attributes;
509
510	adaptor_priv->textured = FALSE;
511	adaptor_priv->colorKey = intel->colorKey & ((1 << scrn->depth) - 1);
512	adaptor_priv->videoStatus = 0;
513	adaptor_priv->brightness = -19;	/* (255/219) * -16 */
514	adaptor_priv->contrast = 75;	/* 255/219 * 64 */
515	adaptor_priv->saturation = 146;	/* 128/112 * 128 */
516	adaptor_priv->desired_crtc = NULL;
517	adaptor_priv->buf = NULL;
518	adaptor_priv->old_buf[0] = NULL;
519	adaptor_priv->old_buf[1] = NULL;
520	adaptor_priv->gamma5 = 0xc0c0c0;
521	adaptor_priv->gamma4 = 0x808080;
522	adaptor_priv->gamma3 = 0x404040;
523	adaptor_priv->gamma2 = 0x202020;
524	adaptor_priv->gamma1 = 0x101010;
525	adaptor_priv->gamma0 = 0x080808;
526
527	adaptor_priv->rotation = RR_Rotate_0;
528
529	/* gotta uninit this someplace */
530	REGION_NULL(screen, &adaptor_priv->clip);
531
532	intel->adaptor = adapt;
533
534	intel_xv_ColorKey = MAKE_ATOM("XV_COLORKEY");
535	intel_xv_Brightness = MAKE_ATOM("XV_BRIGHTNESS");
536	intel_xv_Contrast = MAKE_ATOM("XV_CONTRAST");
537	intel_xv_Saturation = MAKE_ATOM("XV_SATURATION");
538
539	/* Allow the pipe to be switched from pipe A to B when in clone mode */
540	intel_xv_Pipe = MAKE_ATOM("XV_PIPE");
541
542	if (INTEL_INFO(intel)->gen >= 030) {
543		intel_xv_Gamma0 = MAKE_ATOM("XV_GAMMA0");
544		intel_xv_Gamma1 = MAKE_ATOM("XV_GAMMA1");
545		intel_xv_Gamma2 = MAKE_ATOM("XV_GAMMA2");
546		intel_xv_Gamma3 = MAKE_ATOM("XV_GAMMA3");
547		intel_xv_Gamma4 = MAKE_ATOM("XV_GAMMA4");
548		intel_xv_Gamma5 = MAKE_ATOM("XV_GAMMA5");
549	}
550
551	intel_overlay_update_attrs(intel);
552
553	return adapt;
554}
555