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 = xf86ScreenToScrn(pScreen);
66	XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
67	XF86VideoAdaptorPtr newAdaptor = NULL;
68	int num_adaptors;
69
70	newAdaptor = S3SetupImageVideoOverlay(pScreen);
71	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using overlay video\n");
72
73	num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);
74
75    	if(newAdaptor) {
76        	if(!num_adaptors) {
77            		num_adaptors = 1;
78            		adaptors = &newAdaptor;
79        	} else {
80            		newAdaptors =  /* need to free this someplace */
81                		malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr*));
82            		if(newAdaptors) {
83                		memcpy(newAdaptors, adaptors, num_adaptors *
84                                       sizeof(XF86VideoAdaptorPtr));
85                		newAdaptors[num_adaptors] = newAdaptor;
86                		adaptors = newAdaptors;
87                		num_adaptors++;
88           		}
89        	}
90    	}
91
92    	if(num_adaptors)
93        	xf86XVScreenInit(pScreen, adaptors, num_adaptors);
94
95        free(newAdaptors);
96}
97
98
99/* client libraries expect an encoding */
100static XF86VideoEncodingRec DummyEncoding[2] =
101{
102 	{   /* overlay limit */
103   		0,
104   		"XV_IMAGE",
105   		1024, 1024,
106   		{1, 1}
107 	},
108 	{  /* texture limit */
109   		0,
110   		"XV_IMAGE",
111   		2046, 2046,
112   		{1, 1}
113 	}
114};
115
116
117static FBLinearPtr S3XVMemAlloc(ScrnInfoPtr pScrn, pointer pVideo, int size)
118{
119	FBLinearPtr pLinear = (FBLinearPtr)pVideo;
120	ScreenPtr pScreen = pScrn->pScreen;
121
122	if (pLinear) {
123		if ((pLinear->size >= size) ||
124		    xf86ResizeOffscreenLinear(pLinear, size)) {
125			pLinear->MoveLinearCallback = NULL;
126			pLinear->RemoveLinearCallback = NULL;
127			return pLinear;
128		}
129		xf86FreeOffscreenLinear(pLinear);
130	}
131	pLinear = xf86AllocateOffscreenLinear(pScreen, size, 16,
132					      NULL, NULL, NULL);
133
134	if (!pLinear) {
135		int maxSize;
136
137		xf86QueryLargestOffscreenLinear(pScreen, &maxSize, 16,
138						PRIORITY_EXTREME);
139		if (maxSize < size)
140			return NULL;
141
142		xf86PurgeUnlockedOffscreenAreas(pScreen);
143		pLinear = xf86AllocateOffscreenLinear(pScreen, size, 16,
144						      NULL, NULL, NULL);
145	}
146	return pLinear;
147}
148
149static XF86VideoFormatRec Formats[NUM_FORMATS_TEXTURE] =
150{
151  	{16, TrueColor}, {24, TrueColor},
152	{16, DirectColor}, {24, DirectColor}
153};
154
155
156
157#define NUM_IMAGES 3
158
159static XF86ImageRec Images[NUM_IMAGES] =
160{
161  	XVIMAGE_YUY2,
162  	/* As in mga, YV12 & I420 are converted to YUY2 on the fly by */
163  	/* copy over conversion. */
164  	XVIMAGE_YV12,
165  	XVIMAGE_I420
166        /* XVIMAGE_UYVY */
167};
168
169
170
171static int S3SetPortAttributeOverlay(ScrnInfoPtr pScrn, Atom attribute,
172				     INT32 value, pointer data)
173{
174	return BadMatch;
175}
176
177
178static int S3GetPortAttributeOverlay(ScrnInfoPtr pScrn, Atom attribute,
179				     INT32 *value, pointer data)
180{
181	return BadMatch;
182}
183
184
185
186static void S3QueryBestSize(ScrnInfoPtr pScrn, Bool motion, short vid_w,
187			    short vid_h, short drw_w, short drw_h,
188                            unsigned int *p_w, unsigned int *p_h,
189			    pointer data)
190{
191	*p_w = drw_w;
192	*p_h = drw_h;
193}
194
195
196
197static void S3ResetVideoOverlay(ScrnInfoPtr pScrn)
198{
199}
200
201
202static XF86VideoAdaptorPtr S3AllocAdaptor(ScrnInfoPtr pScrn)
203{
204	S3Ptr pS3 = S3PTR(pScrn);
205	XF86VideoAdaptorPtr adapt;
206	S3PortPrivPtr pPriv;
207	int i;
208
209    	if(!(adapt = xf86XVAllocateVideoAdaptorRec(pScrn)))
210        	return NULL;
211
212    	if(!(pPriv = calloc(1, sizeof(S3PortPrivRec)  +
213			     (sizeof(DevUnion) * S3_MAX_PORTS))))
214    	{
215        	free(adapt);
216        	return NULL;
217    	}
218
219    	adapt->pPortPrivates = (DevUnion*)(&pPriv[1]);
220
221    	for(i = 0; i < S3_MAX_PORTS; i++)
222        	adapt->pPortPrivates[i].val = i;
223
224	pPriv->colorKey = (1 << pScrn->offset.red) |
225		(1 << pScrn->offset.green) |
226		(((pScrn->mask.blue >> pScrn->offset.blue) - 1) << pScrn->offset.blue);
227
228	pPriv->videoStatus = 0;
229	pPriv->lastPort = -1;
230
231	pS3->adaptor = adapt;
232	pS3->portPrivate = pPriv;
233
234	return adapt;
235}
236
237
238static XF86VideoAdaptorPtr S3SetupImageVideoOverlay(ScreenPtr pScreen)
239{
240	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
241	S3Ptr pS3 = S3PTR(pScrn);
242	XF86VideoAdaptorPtr adapt;
243
244	adapt = S3AllocAdaptor(pScrn);
245	if (adapt == NULL)
246		return NULL;
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			WaitVSync();
294			SET_SSTREAM_CNTL(0x03000000);
295			SET_SSTREAM_FBADDR0(0x00000000);
296			SET_SSTREAM_FBADDR1(0x00000000);
297			SET_SSTREAM_STRIDE(0x00000001);
298			SET_SSTREAM_START(0x07ff07ff);
299			SET_SSTREAM_WIND(0x00010001);
300
301			SET_CHROMA_KEY(0x00000000);
302			SET_SSTRETCH(0x00000000);
303			SET_OPAQUE_OVERLAY(0x40000000);
304			SET_K1_VSCALE(0x00000000);
305			SET_K2_VSCALE(0x00000000);
306			SET_DDA_VERT(0x00000000);
307			SET_BLEND_CNTL(0x01000000);
308			WaitVSync();
309		}
310
311		if (pPriv->area) {
312			xf86FreeOffscreenLinear(pPriv->area);
313	        	pPriv->area = NULL;
314		}
315
316		pPriv->videoStatus = 0;
317	}
318}
319
320static void S3DisplayVideoOverlay(ScrnInfoPtr pScrn, int id, int offset,
321				  short width, short height, int pitch,
322				  int x1, int y1, int x2, int y2,
323				  BoxPtr dstBox, short src_w, short src_h,
324				  short drw_w, short drw_h)
325{
326	S3Ptr pS3 = S3PTR(pScrn);
327	S3PortPrivPtr pPriv = pS3->portPrivate;
328	int tmp;
329
330	if (drw_w == src_w)
331		tmp = 0;
332	else
333		tmp = 2;
334
335	SET_SSTREAM_CNTL((tmp << 28) | 0x01000000 |
336			 ((((src_w - 1) << 1) - (drw_w - 1)) & 0xfff));
337	SET_SSTRETCH(((src_w - 1) & 0x7ff) |
338		     (((src_w - drw_w) & 0x7ff) << 16));
339	SET_BLEND_CNTL(0x05000000);
340	SET_SSTREAM_FBADDR0(offset & 0x3fffff);
341	SET_SSTREAM_FBADDR1(offset & 0x3fffff);
342	SET_SSTREAM_STRIDE(pitch & 0xfff);
343	SET_SSTREAM_START(((dstBox->x1 + 1) << 16) | (dstBox->y1 + 1));
344	SET_SSTREAM_WIND((((drw_w - 1) << 16) | drw_h) & 0x7ff07ff);
345
346	SET_K1_VSCALE(src_h - 1);
347	SET_K2_VSCALE((src_h - drw_h) & 0x7ff);
348	SET_DDA_VERT(((~drw_h - 1)) & 0xfff);
349
350	SET_CHROMA_KEY(0x10000000 |
351		       ((pScrn->weight.red-1) << 24) |
352		       ((pPriv->colorKey & pScrn->mask.red) >> pScrn->offset.red) <<
353		       (16 + 8-pScrn->weight.red) |
354		       ((pPriv->colorKey & pScrn->mask.green) >> pScrn->offset.green) <<
355		       (8 + 8-pScrn->weight.green) |
356		       ((pPriv->colorKey & pScrn->mask.blue) >> pScrn->offset.blue) <<
357		       (8-pScrn->weight.blue));
358
359	SET_FIFO_CNTL(0x00080000 | pS3->Streams_FIFO);
360}
361
362static int S3PutImage(ScrnInfoPtr pScrn, short src_x, short src_y,
363		      short drw_x, short drw_y, short src_w, short src_h,
364		      short drw_w, short drw_h, int id, unsigned char *buf,
365		      short width, short height, Bool sync,
366		      RegionPtr clipBoxes, pointer data, DrawablePtr pDraw)
367{
368	S3Ptr pS3 = S3PTR(pScrn);
369	S3PortPrivPtr pPriv = pS3->portPrivate;
370   	INT32 x1, x2, y1, y2;
371   	CARD8 *dst_start;
372   	int offset, offsetV = 0, offsetU = 0;
373   	int srcPitch, srcPitchUV = 0, dstPitch, dstSize;
374   	int top, bottom, right, left, npixels, nlines;
375   	BoxRec dstBox;
376	CARD32 tmp;
377	int cpp = (pScrn->bitsPerPixel + 7) >> 3;
378
379	/* Clip */
380	x1 = src_x;
381	x2 = src_x + src_w;
382	y1 = src_y;
383	y2 = src_y + src_h;
384
385	dstBox.x1 = drw_x;
386	dstBox.x2 = drw_x + drw_w;
387	dstBox.y1 = drw_y;
388	dstBox.y2 = drw_y + drw_h;
389
390	if(!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes,
391				  width, height))
392		return Success;
393
394	dstBox.x1 -= pScrn->frameX0;
395	dstBox.x2 -= pScrn->frameX0;
396	dstBox.y1 -= pScrn->frameY0;
397	dstBox.y2 -= pScrn->frameY0;
398
399	/* requested size in bytes */
400	dstPitch = ((width << 1) + 15) & ~15;
401	dstSize = dstPitch * height;
402
403	pPriv->area = S3XVMemAlloc(pScrn, pPriv->area,
404				   (dstSize + cpp - 1) / cpp);
405	if (!pPriv->area)
406		return BadAlloc;
407
408	offset = pPriv->area->offset * cpp;
409	dst_start = pS3->FBBase + offset;
410
411	switch (id) {
412        case FOURCC_YV12:
413        case FOURCC_I420:
414		left = (x1 >> 16) & ~1;
415		right = ((x2 + 0x1ffff) >> 16) & ~1;
416		top = (y1 >> 16) & ~1;
417		bottom = ((y2 + 0x1ffff) >> 16) & ~1;
418
419		if ((right < width) && ((x1 & 0x1ffff) <= (x2 & 0x1ffff)))
420			right += 2;
421		if ((bottom < height) && ((y1 & 0x1ffff) <= (y2 & 0x1ffff)))
422			bottom += 2;
423
424		npixels = right - left;
425		nlines = bottom - top;
426
427		srcPitch = (width + 3) & ~3;
428		offsetV = srcPitch * height;
429		srcPitchUV = ((width >> 1) + 3) & ~3;
430		offsetU = ((height >> 1) * srcPitchUV) + offsetV;
431
432		tmp = ((top >> 1) * srcPitchUV) + (left >> 1);
433		offsetV += tmp;
434		offsetU += tmp;
435
436		if (id == FOURCC_I420)
437		{
438			tmp = offsetV;
439			offsetV = offsetU;
440			offsetU = tmp;
441		}
442
443		dst_start += top * dstPitch + (left << 1);
444
445		xf86XVCopyYUV12ToPacked(buf + (top * srcPitch) + left,
446					buf + offsetV, buf + offsetU,
447					dst_start, srcPitch, srcPitchUV,
448					dstPitch, nlines, npixels);
449		break;
450
451        case FOURCC_UYVY:
452        case FOURCC_YUY2:
453        default:
454		left = (x1 >> 16) & ~1;
455		right = ((x2 + 0x1ffff) >> 16) & ~1;
456		top = y1 >> 16;
457		bottom = (y2 + 0x0ffff) >> 16;
458
459		if ((right < width) && ((x1 & 0x1ffff) <= (x2 & 0x1ffff)))
460			right += 2;
461		if ((bottom < height) && ((y1 & 0x0ffff) <= (y2 & 0x0ffff)))
462			bottom++;
463
464		npixels = right - left;
465		nlines = bottom - top;
466
467		srcPitch = width << 1;
468		buf += (top * srcPitch) + (left << 1);
469		dst_start += top * dstPitch + (left << 1);
470
471		xf86XVCopyPacked(buf, dst_start, srcPitch, dstPitch,
472				 nlines, npixels);
473		break;
474	}
475
476	/* update cliplist */
477	if(!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) {
478		REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes);
479		/* draw these */
480		xf86XVFillKeyHelper(pScrn->pScreen, pPriv->colorKey,
481				    clipBoxes);
482	}
483
484	offset += (left << 1) + (top * dstPitch);
485	S3DisplayVideoOverlay(pScrn, id, offset, width, height, dstPitch,
486			      x1, y1, x2, y2, &dstBox,
487			      src_w, src_h, drw_w, drw_h);
488
489	pPriv->videoStatus = CLIENT_VIDEO_ON;
490	return Success;
491}
492
493
494
495static int S3QueryImageAttributes(ScrnInfoPtr pScrn, int id,
496				  unsigned short *w, unsigned short *h,
497				  int *pitches, int *offsets)
498{
499	int size, tmp;
500
501	*w = (*w + 1) & ~1;
502	if(offsets) offsets[0] = 0;
503
504	switch(id) {
505	case FOURCC_YV12:
506	case FOURCC_I420:
507		*h = (*h + 1) & ~1;
508		size = (*w + 3) & ~3;
509		if(pitches) pitches[0] = size;
510		size *= *h;
511		if(offsets) offsets[1] = size;
512		tmp = ((*w >> 1) + 3) & ~3;
513		if(pitches) pitches[1] = pitches[2] = tmp;
514		tmp *= (*h >> 1);
515		size += tmp;
516		if(offsets) offsets[2] = size;
517		size += tmp;
518		break;
519	case FOURCC_UYVY:
520	case FOURCC_YUY2:
521	default:
522		size = *w << 1;
523		if(pitches) pitches[0] = size;
524		size *= *h;
525		break;
526	}
527
528	return size;
529}
530
531
532void S3InitStreams(ScrnInfoPtr pScrn, DisplayModePtr mode)
533{
534        S3Ptr pS3 = S3PTR(pScrn);
535        unsigned int pst_wind = (mode->HDisplay-1) << 16 | (mode->VDisplay);
536
537	WaitVSync();
538
539	switch (pScrn->bitsPerPixel) {
540	case 8:
541		SET_PSTREAM_CNTL(0x00000000);
542		break;
543	case 15:
544		SET_PSTREAM_CNTL(0x03000000);
545		break;
546	case 16:
547		SET_PSTREAM_CNTL(0x05000000);
548		break;
549	case 24:
550		SET_PSTREAM_CNTL(0x06000000);
551		break;
552	case 32:
553		SET_PSTREAM_CNTL(0x07000000);
554		break;
555	}
556
557	SET_PSTREAM_FBADDR0(0x00000000);
558	SET_PSTREAM_FBADDR1(0x00000000);
559
560	SET_PSTREAM_STRIDE(pS3->s3BppDisplayWidth & 0x0fff);
561
562        SET_PSTREAM_WIND(pst_wind & 0x07ff07ff);
563        SET_PSTREAM_START(0x00010001);
564
565        SET_CHROMA_KEY(0x00000000);
566	SET_SSTRETCH(0x00000000);
567        SET_BLEND_CNTL(0x01000000);
568	SET_DOUBLE_BUFFER(0x00000000);
569
570        SET_SSTREAM_CNTL(0x03000000);
571	SET_SSTREAM_FBADDR0(0x00000000);
572	SET_SSTREAM_FBADDR1(0x00000000);
573        SET_SSTREAM_STRIDE(0x00000001);
574        SET_SSTREAM_START(0x07ff07ff);
575        SET_SSTREAM_WIND(0x00010001);
576
577        SET_OPAQUE_OVERLAY(0x40000000);
578	SET_K1_VSCALE(0x00000000);
579	SET_K2_VSCALE(0x00000000);
580	SET_DDA_VERT(0x00000000);
581
582	/*
583	  ps thr | ss thr | ss fifo slots
584	  set primary stream FIFO to 24 slots and 12 slots for threshold
585	*/
586	SET_FIFO_CNTL(0x00080000 | FIFO_PS24_SS0);
587}
588
589