s3_video.c revision bd35f0db
1/*
2 *      Copyright 2001  Ani Joshi <ajoshi@unixbox.com>
3 *
4 *      XFree86 4.x driver for S3 chipsets
5 *
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that copyright
10 * notice and this permission notice appear in supporting documentation and
11 * that the name of Ani Joshi not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission.  Ani Joshi makes no representations
14 * about the suitability of this software for any purpose.  It is provided
15 * "as-is" without express or implied warranty.
16 *
17 * ANI JOSHI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL ANI JOSHI BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
24 *
25 *
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include "xf86.h"
33#include "xf86_OSproc.h"
34
35#include "compiler.h"
36
37#include "s3.h"
38#include "s3_reg.h"
39
40
41#define CLIENT_VIDEO_ON	0x04
42#define S3_MAX_PORTS	1
43#define NUM_FORMATS_OVERLAY 4
44#define NUM_FORMATS_TEXTURE 4
45
46
47static XF86VideoAdaptorPtr S3AllocAdaptor(ScrnInfoPtr pScrn);
48static XF86VideoAdaptorPtr S3SetupImageVideoOverlay(ScreenPtr);
49static int S3SetPortAttributeOverlay(ScrnInfoPtr, Atom, INT32, pointer);
50static int S3GetPortAttributeOverlay(ScrnInfoPtr, Atom ,INT32 *, pointer);
51static void S3StopVideo(ScrnInfoPtr, pointer, Bool);
52static void S3QueryBestSize(ScrnInfoPtr, Bool, short, short, short, short,
53                            unsigned int *, unsigned int *, pointer);
54static int  S3PutImage(ScrnInfoPtr, short, short, short, short, short,
55                       short, short, short, int, unsigned char*, short,
56                       short, Bool, RegionPtr, pointer, DrawablePtr);
57static int  S3QueryImageAttributes(ScrnInfoPtr, int, unsigned short *,
58                        	   unsigned short *,  int *, int *);
59static void S3ResetVideoOverlay(ScrnInfoPtr);
60static FBLinearPtr S3XVMemAlloc(ScrnInfoPtr pScrn, pointer pVideo, int size);
61
62
63void S3InitVideo(ScreenPtr pScreen)
64{
65	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
66	S3Ptr pS3 = S3PTR(pScrn);
67	XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
68	XF86VideoAdaptorPtr newAdaptor = NULL;
69	int num_adaptors;
70
71	newAdaptor = S3SetupImageVideoOverlay(pScreen);
72	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using overlay video\n");
73
74	num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);
75
76    	if(newAdaptor) {
77        	if(!num_adaptors) {
78            		num_adaptors = 1;
79            		adaptors = &newAdaptor;
80        	} else {
81            		newAdaptors =  /* need to free this someplace */
82                		xalloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr*));
83            		if(newAdaptors) {
84                		memcpy(newAdaptors, adaptors, num_adaptors *
85                                       sizeof(XF86VideoAdaptorPtr));
86                		newAdaptors[num_adaptors] = newAdaptor;
87                		adaptors = newAdaptors;
88                		num_adaptors++;
89           		}
90        	}
91    	}
92
93    	if(num_adaptors)
94        	xf86XVScreenInit(pScreen, adaptors, num_adaptors);
95
96    	if(newAdaptors)
97        	xfree(newAdaptors);
98}
99
100
101/* client libraries expect an encoding */
102static XF86VideoEncodingRec DummyEncoding[2] =
103{
104 	{   /* overlay limit */
105   		0,
106   		"XV_IMAGE",
107   		1024, 1024,
108   		{1, 1}
109 	},
110 	{  /* texture limit */
111   		0,
112   		"XV_IMAGE",
113   		2046, 2046,
114   		{1, 1}
115 	}
116};
117
118
119static FBLinearPtr S3XVMemAlloc(ScrnInfoPtr pScrn, pointer pVideo, int size)
120{
121	FBLinearPtr pLinear = (FBLinearPtr)pVideo;
122	ScreenPtr pScreen = pScrn->pScreen;
123
124	if (pLinear) {
125		if ((pLinear->size >= size) ||
126		    xf86ResizeOffscreenLinear(pLinear, size)) {
127			pLinear->MoveLinearCallback = NULL;
128			pLinear->RemoveLinearCallback = NULL;
129			return pLinear;
130		}
131		xf86FreeOffscreenLinear(pLinear);
132	}
133	pLinear = xf86AllocateOffscreenLinear(pScreen, size, 16,
134					      NULL, NULL, NULL);
135
136	if (!pLinear) {
137		int maxSize;
138
139		xf86QueryLargestOffscreenLinear(pScreen, &maxSize, 16,
140						PRIORITY_EXTREME);
141		if (maxSize < size)
142			return NULL;
143
144		xf86PurgeUnlockedOffscreenAreas(pScreen);
145		pLinear = xf86AllocateOffscreenLinear(pScreen, size, 16,
146						      NULL, NULL, NULL);
147	}
148	return pLinear;
149}
150
151static XF86VideoFormatRec Formats[NUM_FORMATS_TEXTURE] =
152{
153  	{16, TrueColor}, {24, TrueColor},
154	{16, DirectColor}, {24, DirectColor}
155};
156
157
158
159#define NUM_IMAGES 3
160
161static XF86ImageRec Images[NUM_IMAGES] =
162{
163  	XVIMAGE_YUY2,
164  	/* As in mga, YV12 & I420 are converted to YUY2 on the fly by */
165  	/* copy over conversion. */
166  	XVIMAGE_YV12,
167  	XVIMAGE_I420
168        /* XVIMAGE_UYVY */
169};
170
171
172
173static int S3SetPortAttributeOverlay(ScrnInfoPtr pScrn, Atom attribute,
174				     INT32 value, pointer data)
175{
176	return BadMatch;
177}
178
179
180static int S3GetPortAttributeOverlay(ScrnInfoPtr pScrn, Atom attribute,
181				     INT32 *value, pointer data)
182{
183	return BadMatch;
184}
185
186
187
188static void S3QueryBestSize(ScrnInfoPtr pScrn, Bool motion, short vid_w,
189			    short vid_h, short drw_w, short drw_h,
190                            unsigned int *p_w, unsigned int *p_h,
191			    pointer data)
192{
193	*p_w = drw_w;
194	*p_h = drw_h;
195}
196
197
198
199static void S3ResetVideoOverlay(ScrnInfoPtr pScrn)
200{
201}
202
203
204static XF86VideoAdaptorPtr S3AllocAdaptor(ScrnInfoPtr pScrn)
205{
206	S3Ptr pS3 = S3PTR(pScrn);
207	XF86VideoAdaptorPtr adapt;
208	S3PortPrivPtr pPriv;
209	int i;
210
211    	if(!(adapt = xf86XVAllocateVideoAdaptorRec(pScrn)))
212        	return NULL;
213
214    	if(!(pPriv = xcalloc(1, sizeof(S3PortPrivRec)  +
215			     (sizeof(DevUnion) * S3_MAX_PORTS))))
216    	{
217        	xfree(adapt);
218        	return NULL;
219    	}
220
221    	adapt->pPortPrivates = (DevUnion*)(&pPriv[1]);
222
223    	for(i = 0; i < S3_MAX_PORTS; i++)
224        	adapt->pPortPrivates[i].val = i;
225
226	pPriv->colorKey = (1 << pScrn->offset.red) |
227		(1 << pScrn->offset.green) |
228		(((pScrn->mask.blue >> pScrn->offset.blue) - 1) << pScrn->offset.blue);
229
230	pPriv->videoStatus = 0;
231	pPriv->lastPort = -1;
232
233	pS3->adaptor = adapt;
234	pS3->portPrivate = pPriv;
235
236	return adapt;
237}
238
239
240static XF86VideoAdaptorPtr S3SetupImageVideoOverlay(ScreenPtr pScreen)
241{
242	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
243	S3Ptr pS3 = S3PTR(pScrn);
244	XF86VideoAdaptorPtr adapt;
245
246	adapt = S3AllocAdaptor(pScrn);
247
248    	adapt->type = XvWindowMask | XvInputMask | XvImageMask;
249    	adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
250    	adapt->name = "S3 Backend Scaler";
251    	adapt->nEncodings = 1;
252    	adapt->pEncodings = &DummyEncoding[0];
253    	adapt->nFormats = NUM_FORMATS_OVERLAY;
254    	adapt->pFormats = Formats;
255    	adapt->nPorts = 1;
256    	adapt->pAttributes = NULL /*Attributes*/;
257	adapt->nImages = 3;
258	adapt->nAttributes = 0;
259
260    	adapt->pImages = Images;
261    	adapt->PutVideo = NULL;
262    	adapt->PutStill = NULL;
263    	adapt->GetVideo = NULL;
264    	adapt->GetStill = NULL;
265    	adapt->StopVideo = S3StopVideo;
266    	/* Empty Attrib functions - required anyway */
267    	adapt->SetPortAttribute = S3SetPortAttributeOverlay;
268    	adapt->GetPortAttribute = S3GetPortAttributeOverlay;
269    	adapt->QueryBestSize = S3QueryBestSize;
270    	adapt->PutImage = S3PutImage;
271    	adapt->QueryImageAttributes = S3QueryImageAttributes;
272
273    	/* gotta uninit this someplace */
274	REGION_NULL(pScreen, &(pS3->portPrivate->clip));
275
276    	S3ResetVideoOverlay(pScrn);
277
278    	return adapt;
279}
280
281
282static void S3StopVideo(ScrnInfoPtr pScrn, pointer data, Bool exit)
283{
284	S3Ptr pS3 = S3PTR(pScrn);
285	S3PortPrivPtr pPriv = pS3->portPrivate;
286
287	REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
288
289	if (exit) {
290		SET_FIFO_CNTL(0x00080000 | FIFO_PS24_SS0);
291
292		if (pPriv->videoStatus & CLIENT_VIDEO_ON)
293			SET_BLEND_CNTL(0x01000000);
294
295		if (pPriv->area) {
296			xf86FreeOffscreenLinear(pPriv->area);
297	        	pPriv->area = NULL;
298		}
299
300		pPriv->videoStatus = 0;
301	}
302}
303
304static void S3DisplayVideoOverlay(ScrnInfoPtr pScrn, int id, int offset,
305				  short width, short height, int pitch,
306				  int x1, int y1, int x2, int y2,
307				  BoxPtr dstBox, short src_w, short src_h,
308				  short drw_w, short drw_h)
309{
310	S3Ptr pS3 = S3PTR(pScrn);
311	S3PortPrivPtr pPriv = pS3->portPrivate;
312	int tmp;
313
314	if (drw_w == src_w)
315		tmp = 0;
316	else
317		tmp = 2;
318
319	SET_SSTREAM_CNTL((tmp << 28) | 0x01000000 |
320			 ((((src_w - 1) << 1) - (drw_w - 1)) & 0xfff));
321	SET_SSTRETCH(((src_w - 1) & 0x7ff) |
322		     (((src_w - drw_w) & 0x7ff) << 16));
323	SET_BLEND_CNTL(0x05000000);
324	SET_SSTREAM_FBADDR0(offset & 0x3fffff);
325	SET_SSTREAM_FBADDR1(offset & 0x3fffff);
326	SET_SSTREAM_STRIDE(pitch & 0xfff);
327	SET_SSTREAM_START(((dstBox->x1 + 1) << 16) | (dstBox->y1 + 1));
328	SET_SSTREAM_WIND((((drw_w - 1) << 16) | drw_h) & 0x7ff07ff);
329
330	SET_K1_VSCALE(src_h - 1);
331	SET_K2_VSCALE((src_h - drw_h) & 0x7ff);
332	SET_DDA_VERT(((~drw_h - 1)) & 0xfff);
333
334	SET_CHROMA_KEY(0x10000000 |
335		       ((pScrn->weight.red-1) << 24) |
336		       ((pPriv->colorKey & pScrn->mask.red) >> pScrn->offset.red) <<
337		       (16 + 8-pScrn->weight.red) |
338		       ((pPriv->colorKey & pScrn->mask.green) >> pScrn->offset.green) <<
339		       (8 + 8-pScrn->weight.green) |
340		       ((pPriv->colorKey & pScrn->mask.blue) >> pScrn->offset.blue) <<
341		       (8-pScrn->weight.blue));
342
343	SET_FIFO_CNTL(0x00080000 | pS3->Streams_FIFO);
344}
345
346static int S3PutImage(ScrnInfoPtr pScrn, short src_x, short src_y,
347		      short drw_x, short drw_y, short src_w, short src_h,
348		      short drw_w, short drw_h, int id, unsigned char *buf,
349		      short width, short height, Bool sync,
350		      RegionPtr clipBoxes, pointer data, DrawablePtr pDraw)
351{
352	S3Ptr pS3 = S3PTR(pScrn);
353	S3PortPrivPtr pPriv = pS3->portPrivate;
354   	INT32 x1, x2, y1, y2;
355   	CARD8 *dst_start;
356   	int pitch, new_h, offset, offsetV = 0, offsetU = 0;
357   	int srcPitch, srcPitchUV = 0, dstPitch, dstSize;
358   	int top, bottom, right, left, npixels, nlines;
359   	BoxRec dstBox;
360	CARD32 tmp;
361	int cpp = (pScrn->bitsPerPixel + 7) >> 3;
362
363	/* Clip */
364	x1 = src_x;
365	x2 = src_x + src_w;
366	y1 = src_y;
367	y2 = src_y + src_h;
368
369	dstBox.x1 = drw_x;
370	dstBox.x2 = drw_x + drw_w;
371	dstBox.y1 = drw_y;
372	dstBox.y2 = drw_y + drw_h;
373
374	if(!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes,
375				  width, height))
376		return Success;
377
378	dstBox.x1 -= pScrn->frameX0;
379	dstBox.x2 -= pScrn->frameX0;
380	dstBox.y1 -= pScrn->frameY0;
381	dstBox.y2 -= pScrn->frameY0;
382
383	/* requested size in bytes */
384	dstPitch = ((width << 1) + 15) & ~15;
385	dstSize = dstPitch * height;
386
387	pPriv->area = S3XVMemAlloc(pScrn, pPriv->area,
388				   (dstSize + cpp - 1) / cpp);
389	if (!pPriv->area)
390		return BadAlloc;
391
392	offset = pPriv->area->offset * cpp;
393	dst_start = pS3->FBBase + offset;
394
395	switch (id) {
396        case FOURCC_YV12:
397        case FOURCC_I420:
398		left = (x1 >> 16) & ~1;
399		right = ((x2 + 0x1ffff) >> 16) & ~1;
400		top = (y1 >> 16) & ~1;
401		bottom = ((y2 + 0x1ffff) >> 16) & ~1;
402
403		if ((right < width) && ((x1 & 0x1ffff) <= (x2 & 0x1ffff)))
404			right += 2;
405		if ((bottom < height) && ((y1 & 0x1ffff) <= (y2 & 0x1ffff)))
406			bottom += 2;
407
408		npixels = right - left;
409		nlines = bottom - top;
410
411		srcPitch = (width + 3) & ~3;
412		offsetV = srcPitch * height;
413		srcPitchUV = ((width >> 1) + 3) & ~3;
414		offsetU = ((height >> 1) * srcPitchUV) + offsetV;
415
416		tmp = ((top >> 1) * srcPitchUV) + (left >> 1);
417		offsetV += tmp;
418		offsetU += tmp;
419
420		if (id == FOURCC_I420)
421		{
422			tmp = offsetV;
423			offsetV = offsetU;
424			offsetU = tmp;
425		}
426
427		dst_start += top * dstPitch + (left << 1);
428
429		xf86XVCopyYUV12ToPacked(buf + (top * srcPitch) + left,
430					buf + offsetV, buf + offsetU,
431					dst_start, srcPitch, srcPitchUV,
432					dstPitch, nlines, npixels);
433		break;
434
435        case FOURCC_UYVY:
436        case FOURCC_YUY2:
437        default:
438		left = (x1 >> 16) & ~1;
439		right = ((x2 + 0x1ffff) >> 16) & ~1;
440		top = y1 >> 16;
441		bottom = (y2 + 0x0ffff) >> 16;
442
443		if ((right < width) && ((x1 & 0x1ffff) <= (x2 & 0x1ffff)))
444			right += 2;
445		if ((bottom < height) && ((y1 & 0x0ffff) <= (y2 & 0x0ffff)))
446			bottom++;
447
448		npixels = right - left;
449		nlines = bottom - top;
450
451		srcPitch = width << 1;
452		buf += (top * srcPitch) + (left << 1);
453		dst_start += top * dstPitch + (left << 1);
454
455		xf86XVCopyPacked(buf, dst_start, srcPitch, dstPitch,
456				 nlines, npixels);
457		break;
458	}
459
460	/* update cliplist */
461	if(!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) {
462		REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes);
463		/* draw these */
464		xf86XVFillKeyHelper(pScrn->pScreen, pPriv->colorKey,
465				    clipBoxes);
466	}
467
468	offset += (left << 1) + (top * dstPitch);
469	S3DisplayVideoOverlay(pScrn, id, offset, width, height, dstPitch,
470			      x1, y1, x2, y2, &dstBox,
471			      src_w, src_h, drw_w, drw_h);
472
473	pPriv->videoStatus = CLIENT_VIDEO_ON;
474	return Success;
475}
476
477
478
479static int S3QueryImageAttributes(ScrnInfoPtr pScrn, int id,
480				  unsigned short *w, unsigned short *h,
481				  int *pitches, int *offsets)
482{
483	int size, tmp;
484
485	*w = (*w + 1) & ~1;
486	if(offsets) offsets[0] = 0;
487
488	switch(id) {
489	case FOURCC_YV12:
490	case FOURCC_I420:
491		*h = (*h + 1) & ~1;
492		size = (*w + 3) & ~3;
493		if(pitches) pitches[0] = size;
494		size *= *h;
495		if(offsets) offsets[1] = size;
496		tmp = ((*w >> 1) + 3) & ~3;
497		if(pitches) pitches[1] = pitches[2] = tmp;
498		tmp *= (*h >> 1);
499		size += tmp;
500		if(offsets) offsets[2] = size;
501		size += tmp;
502		break;
503	case FOURCC_UYVY:
504	case FOURCC_YUY2:
505	default:
506		size = *w << 1;
507		if(pitches) pitches[0] = size;
508		size *= *h;
509		break;
510	}
511
512	return size;
513}
514
515
516void S3InitStreams(ScrnInfoPtr pScrn, DisplayModePtr mode)
517{
518        S3Ptr pS3 = S3PTR(pScrn);
519        unsigned int pst_wind = (mode->HDisplay-1) << 16 | (mode->VDisplay);
520
521	WaitVSync();
522
523	switch (pScrn->bitsPerPixel) {
524	case 8:
525		SET_PSTREAM_CNTL(0x00000000);
526		break;
527	case 15:
528		SET_PSTREAM_CNTL(0x03000000);
529		break;
530	case 16:
531		SET_PSTREAM_CNTL(0x05000000);
532		break;
533	case 24:
534		SET_PSTREAM_CNTL(0x06000000);
535		break;
536	case 32:
537		SET_PSTREAM_CNTL(0x07000000);
538		break;
539	}
540
541	SET_PSTREAM_FBADDR0(0x00000000);
542	SET_PSTREAM_FBADDR1(0x00000000);
543
544	SET_PSTREAM_STRIDE(pS3->s3BppDisplayWidth & 0x0fff);
545
546        SET_PSTREAM_WIND(pst_wind & 0x07ff07ff);
547        SET_PSTREAM_START(0x00010001);
548
549        SET_CHROMA_KEY(0x00000000);
550	SET_SSTRETCH(0x00000000);
551        SET_BLEND_CNTL(0x01000000);
552	SET_DOUBLE_BUFFER(0x00000000);
553
554        SET_SSTREAM_CNTL(0x03000000);
555	SET_SSTREAM_FBADDR0(0x00000000);
556	SET_SSTREAM_FBADDR1(0x00000000);
557        SET_SSTREAM_STRIDE(0x00000001);
558        SET_SSTREAM_START(0x07ff07ff);
559        SET_SSTREAM_WIND(0x00010001);
560
561        SET_OPAQUE_OVERLAY(0x40000000);
562	SET_K1_VSCALE(0x00000000);
563	SET_K2_VSCALE(0x00000000);
564	SET_DDA_VERT(0x00000000);
565
566	/*
567	  ps thr | ss thr | ss fifo slots
568	  set primary stream FIFO to 24 slots and 12 slots for threshold
569	*/
570	SET_FIFO_CNTL(0x00080000 | FIFO_PS24_SS0);
571}
572
573