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