1/*
2 *  linux/drivers/video/kyro/STG4000OverlayDevice.c
3 *
4 *  Copyright (C) 2000 Imagination Technologies Ltd
5 *  Copyright (C) 2002 STMicroelectronics
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License.  See the file COPYING in the main directory of this archive
9 * for more details.
10 */
11
12#include <linux/kernel.h>
13#include <linux/errno.h>
14#include <linux/types.h>
15
16#include "STG4000Reg.h"
17#include "STG4000Interface.h"
18
19/* HW Defines */
20
21#define STG4000_NO_SCALING    0x800
22#define STG4000_NO_DECIMATION 0xFFFFFFFF
23
24/* Primary surface */
25#define STG4000_PRIM_NUM_PIX   5
26#define STG4000_PRIM_ALIGN     4
27#define STG4000_PRIM_ADDR_BITS 20
28
29#define STG4000_PRIM_MIN_WIDTH  640
30#define STG4000_PRIM_MAX_WIDTH  1600
31#define STG4000_PRIM_MIN_HEIGHT 480
32#define STG4000_PRIM_MAX_HEIGHT 1200
33
34/* Overlay surface */
35#define STG4000_OVRL_NUM_PIX   4
36#define STG4000_OVRL_ALIGN     2
37#define STG4000_OVRL_ADDR_BITS 20
38#define STG4000_OVRL_NUM_MODES 5
39
40#define STG4000_OVRL_MIN_WIDTH  0
41#define STG4000_OVRL_MAX_WIDTH  720
42#define STG4000_OVRL_MIN_HEIGHT 0
43#define STG4000_OVRL_MAX_HEIGHT 576
44
45/* Decimation and Scaling */
46static u32 adwDecim8[33] = {
47	    0xffffffff, 0xfffeffff, 0xffdffbff, 0xfefefeff, 0xfdf7efbf,
48	    0xfbdf7bdf, 0xf7bbddef, 0xeeeeeeef, 0xeeddbb77, 0xedb76db7,
49	    0xdb6db6db, 0xdb5b5b5b, 0xdab5ad6b, 0xd5ab55ab, 0xd555aaab,
50	    0xaaaaaaab, 0xaaaa5555, 0xaa952a55, 0xa94a5295, 0xa5252525,
51	    0xa4924925, 0x92491249, 0x91224489, 0x91111111, 0x90884211,
52	    0x88410821, 0x88102041, 0x81010101, 0x80800801, 0x80010001,
53	    0x80000001, 0x00000001, 0x00000000
54};
55
56typedef struct _OVRL_SRC_DEST {
57	/*clipped on-screen pixel position of overlay */
58	u32 ulDstX1;
59	u32 ulDstY1;
60	u32 ulDstX2;
61	u32 ulDstY2;
62
63	/*clipped pixel pos of source data within buffer thses need to be 128 bit word aligned */
64	u32 ulSrcX1;
65	u32 ulSrcY1;
66	u32 ulSrcX2;
67	u32 ulSrcY2;
68
69	/* on-screen pixel position of overlay */
70	s32 lDstX1;
71	s32 lDstY1;
72	s32 lDstX2;
73	s32 lDstY2;
74} OVRL_SRC_DEST;
75
76static u32 ovlWidth, ovlHeight, ovlStride;
77static int ovlLinear;
78
79void ResetOverlayRegisters(volatile STG4000REG __iomem *pSTGReg)
80{
81	u32 tmp;
82
83	/* Set Overlay address to default */
84	tmp = STG_READ_REG(DACOverlayAddr);
85	CLEAR_BITS_FRM_TO(0, 20);
86	CLEAR_BIT(31);
87	STG_WRITE_REG(DACOverlayAddr, tmp);
88
89	/* Set Overlay U address */
90	tmp = STG_READ_REG(DACOverlayUAddr);
91	CLEAR_BITS_FRM_TO(0, 20);
92	STG_WRITE_REG(DACOverlayUAddr, tmp);
93
94	/* Set Overlay V address */
95	tmp = STG_READ_REG(DACOverlayVAddr);
96	CLEAR_BITS_FRM_TO(0, 20);
97	STG_WRITE_REG(DACOverlayVAddr, tmp);
98
99	/* Set Overlay Size */
100	tmp = STG_READ_REG(DACOverlaySize);
101	CLEAR_BITS_FRM_TO(0, 10);
102	CLEAR_BITS_FRM_TO(12, 31);
103	STG_WRITE_REG(DACOverlaySize, tmp);
104
105	/* Set Overlay Vt Decimation */
106	tmp = STG4000_NO_DECIMATION;
107	STG_WRITE_REG(DACOverlayVtDec, tmp);
108
109	/* Set Overlay format to default value */
110	tmp = STG_READ_REG(DACPixelFormat);
111	CLEAR_BITS_FRM_TO(4, 7);
112	CLEAR_BITS_FRM_TO(16, 22);
113	STG_WRITE_REG(DACPixelFormat, tmp);
114
115	/* Set Vertical scaling to default */
116	tmp = STG_READ_REG(DACVerticalScal);
117	CLEAR_BITS_FRM_TO(0, 11);
118	CLEAR_BITS_FRM_TO(16, 22);
119	tmp |= STG4000_NO_SCALING;	/* Set to no scaling */
120	STG_WRITE_REG(DACVerticalScal, tmp);
121
122	/* Set Horizontal Scaling to default */
123	tmp = STG_READ_REG(DACHorizontalScal);
124	CLEAR_BITS_FRM_TO(0, 11);
125	CLEAR_BITS_FRM_TO(16, 17);
126	tmp |= STG4000_NO_SCALING;	/* Set to no scaling */
127	STG_WRITE_REG(DACHorizontalScal, tmp);
128
129	/* Set Blend mode to Alpha Blend */
130	/* ????? SG 08/11/2001 Surely this isn't the alpha blend mode,
131	   hopefully its overwrite
132	 */
133	tmp = STG_READ_REG(DACBlendCtrl);
134	CLEAR_BITS_FRM_TO(0, 30);
135	tmp = (GRAPHICS_MODE << 28);
136	STG_WRITE_REG(DACBlendCtrl, tmp);
137
138}
139
140int CreateOverlaySurface(volatile STG4000REG __iomem *pSTGReg,
141			 u32 inWidth,
142			 u32 inHeight,
143			 int bLinear,
144			 u32 ulOverlayOffset,
145			 u32 * retStride, u32 * retUVStride)
146{
147	u32 tmp;
148	u32 ulStride;
149
150	if (inWidth > STG4000_OVRL_MAX_WIDTH ||
151	    inHeight > STG4000_OVRL_MAX_HEIGHT) {
152		return -EINVAL;
153	}
154
155	/* Stride in 16 byte words - 16Bpp */
156	if (bLinear) {
157		/* Format is 16bits so num 16 byte words is width/8 */
158		if ((inWidth & 0x7) == 0) {	/* inWidth % 8 */
159			ulStride = (inWidth / 8);
160		} else {
161			/* Round up to next 16byte boundary */
162			ulStride = ((inWidth + 8) / 8);
163		}
164	} else {
165		/* Y component is 8bits so num 16 byte words is width/16 */
166		if ((inWidth & 0xf) == 0) {	/* inWidth % 16 */
167			ulStride = (inWidth / 16);
168		} else {
169			/* Round up to next 16byte boundary */
170			ulStride = ((inWidth + 16) / 16);
171		}
172	}
173
174
175	/* Set Overlay address and Format mode */
176	tmp = STG_READ_REG(DACOverlayAddr);
177	CLEAR_BITS_FRM_TO(0, 20);
178	if (bLinear) {
179		CLEAR_BIT(31);	/* Overlay format to Linear */
180	} else {
181		tmp |= SET_BIT(31);	/* Overlay format to Planer */
182	}
183
184	/* Only bits 24:4 of the Overlay address */
185	tmp |= (ulOverlayOffset >> 4);
186	STG_WRITE_REG(DACOverlayAddr, tmp);
187
188	if (!bLinear) {
189		u32 uvSize =
190		    (inWidth & 0x1) ? (inWidth + 1 / 2) : (inWidth / 2);
191		u32 uvStride;
192		u32 ulOffset;
193		/* Y component is 8bits so num 32 byte words is width/32 */
194		if ((uvSize & 0xf) == 0) {	/* inWidth % 16 */
195			uvStride = (uvSize / 16);
196		} else {
197			/* Round up to next 32byte boundary */
198			uvStride = ((uvSize + 16) / 16);
199		}
200
201		ulOffset = ulOverlayOffset + (inHeight * (ulStride * 16));
202		/* Align U,V data to 32byte boundary */
203		if ((ulOffset & 0x1f) != 0)
204			ulOffset = (ulOffset + 32L) & 0xffffffE0L;
205
206		tmp = STG_READ_REG(DACOverlayUAddr);
207		CLEAR_BITS_FRM_TO(0, 20);
208		tmp |= (ulOffset >> 4);
209		STG_WRITE_REG(DACOverlayUAddr, tmp);
210
211		ulOffset += (inHeight / 2) * (uvStride * 16);
212		/* Align U,V data to 32byte boundary */
213		if ((ulOffset & 0x1f) != 0)
214			ulOffset = (ulOffset + 32L) & 0xffffffE0L;
215
216		tmp = STG_READ_REG(DACOverlayVAddr);
217		CLEAR_BITS_FRM_TO(0, 20);
218		tmp |= (ulOffset >> 4);
219		STG_WRITE_REG(DACOverlayVAddr, tmp);
220
221		*retUVStride = uvStride * 16;
222	}
223
224
225	/* Set Overlay YUV pixel format
226	 * Make sure that LUT not used - ??????
227	 */
228	tmp = STG_READ_REG(DACPixelFormat);
229	/* Only support Planer or UYVY linear formats */
230	CLEAR_BITS_FRM_TO(4, 9);
231	STG_WRITE_REG(DACPixelFormat, tmp);
232
233	ovlWidth = inWidth;
234	ovlHeight = inHeight;
235	ovlStride = ulStride;
236	ovlLinear = bLinear;
237	*retStride = ulStride << 4;	/* In bytes */
238
239	return 0;
240}
241
242int SetOverlayBlendMode(volatile STG4000REG __iomem *pSTGReg,
243			OVRL_BLEND_MODE mode,
244			u32 ulAlpha, u32 ulColorKey)
245{
246	u32 tmp;
247
248	tmp = STG_READ_REG(DACBlendCtrl);
249	CLEAR_BITS_FRM_TO(28, 30);
250	tmp |= (mode << 28);
251
252	switch (mode) {
253	case COLOR_KEY:
254		CLEAR_BITS_FRM_TO(0, 23);
255		tmp |= (ulColorKey & 0x00FFFFFF);
256		break;
257
258	case GLOBAL_ALPHA:
259		CLEAR_BITS_FRM_TO(24, 27);
260		tmp |= ((ulAlpha & 0xF) << 24);
261		break;
262
263	case CK_PIXEL_ALPHA:
264		CLEAR_BITS_FRM_TO(0, 23);
265		tmp |= (ulColorKey & 0x00FFFFFF);
266		break;
267
268	case CK_GLOBAL_ALPHA:
269		CLEAR_BITS_FRM_TO(0, 23);
270		tmp |= (ulColorKey & 0x00FFFFFF);
271		CLEAR_BITS_FRM_TO(24, 27);
272		tmp |= ((ulAlpha & 0xF) << 24);
273		break;
274
275	case GRAPHICS_MODE:
276	case PER_PIXEL_ALPHA:
277		break;
278
279	default:
280		return -EINVAL;
281	}
282
283	STG_WRITE_REG(DACBlendCtrl, tmp);
284
285	return 0;
286}
287
288void EnableOverlayPlane(volatile STG4000REG __iomem *pSTGReg)
289{
290	u32 tmp;
291	/* Enable Overlay */
292	tmp = STG_READ_REG(DACPixelFormat);
293	tmp |= SET_BIT(7);
294	STG_WRITE_REG(DACPixelFormat, tmp);
295
296	/* Set video stream control */
297	tmp = STG_READ_REG(DACStreamCtrl);
298	tmp |= SET_BIT(1);	/* video stream */
299	STG_WRITE_REG(DACStreamCtrl, tmp);
300}
301
302static u32 Overlap(u32 ulBits, u32 ulPattern)
303{
304	u32 ulCount = 0;
305
306	while (ulBits) {
307		if (!(ulPattern & 1))
308			ulCount++;
309		ulBits--;
310		ulPattern = ulPattern >> 1;
311	}
312
313	return ulCount;
314
315}
316
317int SetOverlayViewPort(volatile STG4000REG __iomem *pSTGReg,
318		       u32 left, u32 top,
319		       u32 right, u32 bottom)
320{
321	OVRL_SRC_DEST srcDest;
322
323	u32 ulSrcTop, ulSrcBottom;
324	u32 ulSrc, ulDest;
325	u32 ulFxScale, ulFxOffset;
326	u32 ulHeight, ulWidth;
327	u32 ulPattern;
328	u32 ulDecimate, ulDecimated;
329	u32 ulApplied;
330	u32 ulDacXScale, ulDacYScale;
331	u32 ulScale;
332	u32 ulLeft, ulRight;
333	u32 ulSrcLeft, ulSrcRight;
334	u32 ulScaleLeft, ulScaleRight;
335	u32 ulhDecim;
336	u32 ulsVal;
337	u32 ulVertDecFactor;
338	int bResult;
339	u32 ulClipOff = 0;
340	u32 ulBits = 0;
341	u32 ulsAdd = 0;
342	u32 tmp, ulStride;
343	u32 ulExcessPixels, ulClip, ulExtraLines;
344
345
346	srcDest.ulSrcX1 = 0;
347	srcDest.ulSrcY1 = 0;
348	srcDest.ulSrcX2 = ovlWidth - 1;
349	srcDest.ulSrcY2 = ovlHeight - 1;
350
351	srcDest.ulDstX1 = left;
352	srcDest.ulDstY1 = top;
353	srcDest.ulDstX2 = right;
354	srcDest.ulDstY2 = bottom;
355
356	srcDest.lDstX1 = srcDest.ulDstX1;
357	srcDest.lDstY1 = srcDest.ulDstY1;
358	srcDest.lDstX2 = srcDest.ulDstX2;
359	srcDest.lDstY2 = srcDest.ulDstY2;
360
361    /************* Vertical decimation/scaling ******************/
362
363	/* Get Src Top and Bottom */
364	ulSrcTop = srcDest.ulSrcY1;
365	ulSrcBottom = srcDest.ulSrcY2;
366
367	ulSrc = ulSrcBottom - ulSrcTop;
368	ulDest = srcDest.lDstY2 - srcDest.lDstY1;	/* on-screen overlay */
369
370	if (ulSrc <= 1)
371		return -EINVAL;
372
373	/* First work out the position we are to display as offset from the
374	 * source of the buffer
375	 */
376	ulFxScale = (ulDest << 11) / ulSrc;	/* fixed point scale factor */
377	ulFxOffset = (srcDest.lDstY2 - srcDest.ulDstY2) << 11;
378
379	ulSrcBottom = ulSrcBottom - (ulFxOffset / ulFxScale);
380	ulSrc = ulSrcBottom - ulSrcTop;
381	ulHeight = ulSrc;
382
383	ulDest = srcDest.ulDstY2 - (srcDest.ulDstY1 - 1);
384	ulPattern = adwDecim8[ulBits];
385
386	/* At this point ulSrc represents the input decimator */
387	if (ulSrc > ulDest) {
388		ulDecimate = ulSrc - ulDest;
389		ulBits = 0;
390		ulApplied = ulSrc / 32;
391
392		while (((ulBits * ulApplied) +
393			Overlap((ulSrc % 32),
394				adwDecim8[ulBits])) < ulDecimate)
395			ulBits++;
396
397		ulPattern = adwDecim8[ulBits];
398		ulDecimated =
399		    (ulBits * ulApplied) + Overlap((ulSrc % 32),
400						   ulPattern);
401		ulSrc = ulSrc - ulDecimated;	/* the number number of lines that will go into the scaler */
402	}
403
404	if (ulBits && (ulBits != 32)) {
405		ulVertDecFactor = (63 - ulBits) / (32 - ulBits);	/* vertical decimation factor scaled up to nearest integer */
406	} else {
407		ulVertDecFactor = 1;
408	}
409
410	ulDacYScale = ((ulSrc - 1) * 2048) / (ulDest + 1);
411
412	tmp = STG_READ_REG(DACOverlayVtDec);	/* Decimation */
413	CLEAR_BITS_FRM_TO(0, 31);
414	tmp = ulPattern;
415	STG_WRITE_REG(DACOverlayVtDec, tmp);
416
417	/***************** Horizontal decimation/scaling ***************************/
418
419	/*
420	 * Now we handle the horizontal case, this is a simplified verison of
421	 * the vertical case in that we decimate by factors of 2.  as we are
422	 * working in words we should always be able to decimate by these
423	 * factors.  as we always have to have a buffer which is aligned to a
424	 * whole number of 128 bit words, we must align the left side to the
425	 * lowest to the next lowest 128 bit boundary, and the right hand edge
426	 * to the next largets boundary, (in a similar way to how we didi it in
427	 * PMX1) as the left and right hand edges are aligned to these
428	 * boundaries normally this only becomes an issue when we are chopping
429	 * of one of the sides We shall work out vertical stuff first
430	 */
431	ulSrc = srcDest.ulSrcX2 - srcDest.ulSrcX1;
432	ulDest = srcDest.lDstX2 - srcDest.lDstX1;
433#ifdef _OLDCODE
434	ulLeft = srcDest.ulDstX1;
435	ulRight = srcDest.ulDstX2;
436#else
437	if (srcDest.ulDstX1 > 2) {
438		ulLeft = srcDest.ulDstX1 + 2;
439		ulRight = srcDest.ulDstX2 + 1;
440	} else {
441		ulLeft = srcDest.ulDstX1;
442		ulRight = srcDest.ulDstX2 + 1;
443	}
444#endif
445	/* first work out the position we are to display as offset from the source of the buffer */
446	bResult = 1;
447
448	do {
449		if (ulDest == 0)
450			return -EINVAL;
451
452		/* source pixels per dest pixel <<11 */
453		ulFxScale = ((ulSrc - 1) << 11) / (ulDest);
454
455		/* then number of destination pixels out we are */
456		ulFxOffset = ulFxScale * ((srcDest.ulDstX1 - srcDest.lDstX1) + ulClipOff);
457		ulFxOffset >>= 11;
458
459		/* this replaces the code which was making a decision as to use either ulFxOffset or ulSrcX1 */
460		ulSrcLeft = srcDest.ulSrcX1 + ulFxOffset;
461
462		/* then number of destination pixels out we are */
463		ulFxOffset = ulFxScale * (srcDest.lDstX2 - srcDest.ulDstX2);
464		ulFxOffset >>= 11;
465
466		ulSrcRight = srcDest.ulSrcX2 - ulFxOffset;
467
468		/*
469		 * we must align these to our 128 bit boundaries. we shall
470		 * round down the pixel pos to the nearest 8 pixels.
471		 */
472		ulScaleLeft = ulSrcLeft;
473		ulScaleRight = ulSrcRight;
474
475		/* shift fxscale until it is in the range of the scaler */
476		ulhDecim = 0;
477		ulScale = (((ulSrcRight - ulSrcLeft) - 1) << (11 - ulhDecim)) / (ulRight - ulLeft + 2);
478
479		while (ulScale > 0x800) {
480			ulhDecim++;
481			ulScale = (((ulSrcRight - ulSrcLeft) - 1) << (11 - ulhDecim)) / (ulRight - ulLeft + 2);
482		}
483
484		/*
485		 * to try and get the best values We first try and use
486		 * src/dwdest for the scale factor, then we move onto src-1
487		 *
488		 * we want to check to see if we will need to clip data, if so
489		 * then we should clip our source so that we don't need to
490		 */
491		if (!ovlLinear) {
492			ulSrcLeft &= ~0x1f;
493
494			/*
495			 * we must align the right hand edge to the next 32
496			 * pixel` boundary, must be on a 256 boundary so u, and
497			 * v are 128 bit aligned
498			 */
499			ulSrcRight = (ulSrcRight + 0x1f) & ~0x1f;
500		} else {
501			ulSrcLeft &= ~0x7;
502
503			/*
504			 * we must align the right hand edge to the next
505			 * 8pixel` boundary
506			 */
507			ulSrcRight = (ulSrcRight + 0x7) & ~0x7;
508		}
509
510		/* this is the input size line store needs to cope with */
511		ulWidth = ulSrcRight - ulSrcLeft;
512
513		/*
514		 * use unclipped value to work out scale factror this is the
515		 * scale factor we want we shall now work out the horizonal
516		 * decimation and scaling
517		 */
518		ulsVal = ((ulWidth / 8) >> ulhDecim);
519
520		if ((ulWidth != (ulsVal << ulhDecim) * 8))
521			ulsAdd = 1;
522
523		/* input pixels to scaler; */
524		ulSrc = ulWidth >> ulhDecim;
525
526		if (ulSrc <= 2)
527			return -EINVAL;
528
529		ulExcessPixels = ((((ulScaleLeft - ulSrcLeft)) << (11 - ulhDecim)) / ulScale);
530
531		ulClip = (ulSrc << 11) / ulScale;
532		ulClip -= (ulRight - ulLeft);
533		ulClip += ulExcessPixels;
534
535		if (ulClip)
536			ulClip--;
537
538		/* We may need to do more here if we really have a HW rev < 5 */
539	} while (!bResult);
540
541	ulExtraLines = (1 << ulhDecim) * ulVertDecFactor;
542	ulExtraLines += 64;
543	ulHeight += ulExtraLines;
544
545	ulDacXScale = ulScale;
546
547
548	tmp = STG_READ_REG(DACVerticalScal);
549	CLEAR_BITS_FRM_TO(0, 11);
550	CLEAR_BITS_FRM_TO(16, 22);	/* Vertical Scaling */
551
552	/* Calculate new output line stride, this is always the number of 422
553	   words in the line buffer, so it doesn't matter if the
554	   mode is 420. Then set the vertical scale register.
555	 */
556	ulStride = (ulWidth >> (ulhDecim + 3)) + ulsAdd;
557	tmp |= ((ulStride << 16) | (ulDacYScale));	/* DAC_LS_CTRL = stride */
558	STG_WRITE_REG(DACVerticalScal, tmp);
559
560	/* Now set up the overlay size using the modified width and height
561	   from decimate and scaling calculations
562	 */
563	tmp = STG_READ_REG(DACOverlaySize);
564	CLEAR_BITS_FRM_TO(0, 10);
565	CLEAR_BITS_FRM_TO(12, 31);
566
567	if (ovlLinear) {
568		tmp |=
569		    (ovlStride | ((ulHeight + 1) << 12) |
570		     (((ulWidth / 8) - 1) << 23));
571	} else {
572		tmp |=
573		    (ovlStride | ((ulHeight + 1) << 12) |
574		     (((ulWidth / 32) - 1) << 23));
575	}
576
577	STG_WRITE_REG(DACOverlaySize, tmp);
578
579	/* Set Video Window Start */
580	tmp = ((ulLeft << 16)) | (srcDest.ulDstY1);
581	STG_WRITE_REG(DACVidWinStart, tmp);
582
583	/* Set Video Window End */
584	tmp = ((ulRight) << 16) | (srcDest.ulDstY2);
585	STG_WRITE_REG(DACVidWinEnd, tmp);
586
587	/* Finally set up the rest of the overlay regs in the order
588	   done in the IMG driver
589	 */
590	tmp = STG_READ_REG(DACPixelFormat);
591	tmp = ((ulExcessPixels << 16) | tmp) & 0x7fffffff;
592	STG_WRITE_REG(DACPixelFormat, tmp);
593
594	tmp = STG_READ_REG(DACHorizontalScal);
595	CLEAR_BITS_FRM_TO(0, 11);
596	CLEAR_BITS_FRM_TO(16, 17);
597	tmp |= ((ulhDecim << 16) | (ulDacXScale));
598	STG_WRITE_REG(DACHorizontalScal, tmp);
599
600	return 0;
601}
602