diff options
Diffstat (limited to 'bltsville/gcbv/mirror/gcfilter.c')
-rw-r--r-- | bltsville/gcbv/mirror/gcfilter.c | 1788 |
1 files changed, 1788 insertions, 0 deletions
diff --git a/bltsville/gcbv/mirror/gcfilter.c b/bltsville/gcbv/mirror/gcfilter.c new file mode 100644 index 0000000..525e242 --- /dev/null +++ b/bltsville/gcbv/mirror/gcfilter.c @@ -0,0 +1,1788 @@ +/* + * Copyright(c) 2012, + * Texas Instruments, Inc. and Vivante Corporation. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Vivante Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gcbv.h" + +#define GCZONE_NONE 0 +#define GCZONE_ALL (~0U) +#define GCZONE_KERNEL (1 << 0) +#define GCZONE_FILTER (1 << 1) +#define GCZONE_BLEND (1 << 2) +#define GCZONE_TYPE (1 << 3) +#define GCZONE_SRC (1 << 4) +#define GCZONE_DEST (1 << 5) +#define GCZONE_SURF (1 << 6) + +GCDBG_FILTERDEF(filter, GCZONE_NONE, + "kernel", + "filter", + "blend", + "type", + "src", + "dest", + "surf") + + +/******************************************************************************* + * Miscellaneous defines. + */ + +#define GC_BYTES_PER_CACHELINE (64) +#define GC_BITS_PER_CACHELINE (GC_BYTES_PER_CACHELINE * 8) +#define GC_CACHELINE_ALIGN_16 (GC_BITS_PER_CACHELINE / 16 - 1) +#define GC_CACHELINE_ALIGN_32 (GC_BITS_PER_CACHELINE / 32 - 1) + +enum gcscaletype { + GC_SCALE_OPF, + GC_SCALE_HOR, + GC_SCALE_VER, + GC_SCALE_HOR_FLIPPED, + GC_SCALE_VER_FLIPPED +}; + +/******************************************************************************* + * Scale factor format: unsigned 1.31 fixed point. + */ + +#define GC_SCALE_TYPE unsigned int +#define GC_SCALE_FRACTION 31 +#define GC_SCALE_ONE ((GC_SCALE_TYPE) (1 << GC_SCALE_FRACTION)) + + +/******************************************************************************* + * X coordinate format: signed 4.28 fixed point. + */ + +#define GC_COORD_TYPE int +#define GC_COORD_FRACTION 28 +#define GC_COORD_PI ((GC_COORD_TYPE) 0x3243F6C0) +#define GC_COORD_2OVERPI ((GC_COORD_TYPE) 0x0A2F9832) +#define GC_COORD_PIOVER2 ((GC_COORD_TYPE) 0x1921FB60) +#define GC_COORD_ZERO ((GC_COORD_TYPE) 0) +#define GC_COORD_HALF ((GC_COORD_TYPE) (1 << (GC_COORD_FRACTION - 1))) +#define GC_COORD_ONE ((GC_COORD_TYPE) (1 << GC_COORD_FRACTION)) +#define GC_COORD_NEGONE ((GC_COORD_TYPE) (~GC_COORD_ONE + 1)) +#define GC_COORD_SUBPIX_STEP ((GC_COORD_TYPE) \ + (1 << (GC_COORD_FRACTION - GC_PHASE_BITS))) + + +/******************************************************************************* + * Hardware coefficient format: signed 2.14 fixed point. + */ + +#define GC_COEF_TYPE short +#define GC_COEF_FRACTION 14 +#define GC_COEF_ZERO ((GC_COEF_TYPE) 0) +#define GC_COEF_ONE ((GC_COEF_TYPE) (1 << GC_COEF_FRACTION)) +#define GC_COEF_NEGONE ((GC_COEF_TYPE) (~GC_COEF_ONE + 1)) + + +/******************************************************************************* + * Weight sum format: x.28 fixed point. + */ + +#define GC_SUM_TYPE long long +#define GC_SUM_FRACTION GC_COORD_FRACTION + + +/******************************************************************************* + * Math shortcuts. + */ + +#define computescale(dstsize, srcsize) ((GC_SCALE_TYPE) \ + div_u64(((u64) (dstsize)) << GC_SCALE_FRACTION, (srcsize)) \ +) + +#define normweight(weight, sum) ((GC_COORD_TYPE) \ + div64_s64(((s64) (weight)) << GC_COORD_FRACTION, (sum)) \ +) + +#define convertweight(weight) ((GC_COEF_TYPE) \ + ((weight) >> (GC_COORD_FRACTION - GC_COEF_FRACTION)) \ +) + + +/******************************************************************************* + * Fixed point SINE function. Takes a positive value in range [0..pi/2]. + */ + +static GC_COORD_TYPE sine(GC_COORD_TYPE x) +{ + static const GC_COORD_TYPE sinetable[] = { + 0x00000000, 0x001FFFEB, 0x003FFF55, 0x005FFDC0, + 0x007FFAAB, 0x009FF596, 0x00BFEE01, 0x00DFE36C, + 0x00FFD557, 0x011FC344, 0x013FACB2, 0x015F9120, + 0x017F7010, 0x019F4902, 0x01BF1B78, 0x01DEE6F2, + 0x01FEAAEE, 0x021E66F0, 0x023E1A7C, 0x025DC50C, + 0x027D6624, 0x029CFD48, 0x02BC89F8, 0x02DC0BB8, + 0x02FB8204, 0x031AEC64, 0x033A4A5C, 0x03599B64, + 0x0378DF08, 0x039814CC, 0x03B73C2C, 0x03D654B0, + 0x03F55DDC, 0x04145730, 0x04334030, 0x04521868, + 0x0470DF58, 0x048F9488, 0x04AE3770, 0x04CCC7A8, + 0x04EB44A8, 0x0509ADF8, 0x05280328, 0x054643B0, + 0x05646F28, 0x05828508, 0x05A084E0, 0x05BE6E38, + 0x05DC4098, 0x05F9FB80, 0x06179E88, 0x06352928, + 0x06529AF8, 0x066FF380, 0x068D3248, 0x06AA56D8, + 0x06C760C0, 0x06E44F90, 0x070122C8, 0x071DD9F8, + 0x073A74B8, 0x0756F290, 0x07735308, 0x078F95B0, + 0x07ABBA20, 0x07C7BFD8, 0x07E3A678, 0x07FF6D88, + 0x081B14A0, 0x08369B40, 0x08520110, 0x086D4590, + 0x08886860, 0x08A36910, 0x08BE4730, 0x08D90250, + 0x08F39A20, 0x090E0E10, 0x09285DD0, 0x094288E0, + 0x095C8EF0, 0x09766F90, 0x09902A60, 0x09A9BEE0, + 0x09C32CC0, 0x09DC7390, 0x09F592F0, 0x0A0E8A70, + 0x0A2759C0, 0x0A400070, 0x0A587E20, 0x0A70D270, + 0x0A88FD00, 0x0AA0FD60, 0x0AB8D350, 0x0AD07E50, + 0x0AE7FE10, 0x0AFF5230, 0x0B167A50, 0x0B2D7610, + 0x0B444520, 0x0B5AE730, 0x0B715BC0, 0x0B87A290, + 0x0B9DBB40, 0x0BB3A580, 0x0BC960F0, 0x0BDEED30, + 0x0BF44A00, 0x0C0976F0, 0x0C1E73D0, 0x0C334020, + 0x0C47DBB0, 0x0C5C4620, 0x0C707F20, 0x0C848660, + 0x0C985B80, 0x0CABFE50, 0x0CBF6E60, 0x0CD2AB80, + 0x0CE5B550, 0x0CF88B80, 0x0D0B2DE0, 0x0D1D9C10, + 0x0D2FD5C0, 0x0D41DAB0, 0x0D53AAA0, 0x0D654540, + 0x0D76AA40, 0x0D87D970, 0x0D98D280, 0x0DA99530, + 0x0DBA2140, 0x0DCA7650, 0x0DDA9450, 0x0DEA7AD0, + 0x0DFA29B0, 0x0E09A0B0, 0x0E18DF80, 0x0E27E5F0, + 0x0E36B3C0, 0x0E4548B0, 0x0E53A490, 0x0E61C720, + 0x0E6FB020, 0x0E7D5F70, 0x0E8AD4C0, 0x0E980FF0, + 0x0EA510B0, 0x0EB1D6F0, 0x0EBE6260, 0x0ECAB2D0, + 0x0ED6C810, 0x0EE2A200, 0x0EEE4070, 0x0EF9A310, + 0x0F04C9E0, 0x0F0FB490, 0x0F1A6300, 0x0F24D510, + 0x0F2F0A80, 0x0F390340, 0x0F42BF10, 0x0F4C3DE0, + 0x0F557F70, 0x0F5E83C0, 0x0F674A80, 0x0F6FD3B0, + 0x0F781F20, 0x0F802CB0, 0x0F87FC40, 0x0F8F8DA0, + 0x0F96E0D0, 0x0F9DF5B0, 0x0FA4CC00, 0x0FAB63D0, + 0x0FB1BCF0, 0x0FB7D740, 0x0FBDB2B0, 0x0FC34F30, + 0x0FC8ACA0, 0x0FCDCAF0, 0x0FD2AA10, 0x0FD749E0, + 0x0FDBAA50, 0x0FDFCB50, 0x0FE3ACD0, 0x0FE74EC0, + 0x0FEAB110, 0x0FEDD3C0, 0x0FF0B6B0, 0x0FF359F0, + 0x0FF5BD50, 0x0FF7E0E0, 0x0FF9C490, 0x0FFB6850, + 0x0FFCCC30, 0x0FFDF010, 0x0FFED400, 0x0FFF77F0, + 0x0FFFDBF0, 0x0FFFFFE0, 0x0FFFE3D0, 0x0FFF87D0, + 0x0FFEEBC0, 0x0FFE0FC0, 0x0FFCF3D0, 0x0FFB97E0 + }; + + enum { + indexwidth = 8, + intwidth = 1, + indexshift = intwidth + + GC_COORD_FRACTION + - indexwidth + }; + + unsigned int p1, p2; + GC_COORD_TYPE p1x, p2x; + GC_COORD_TYPE p1y, p2y; + GC_COORD_TYPE dx, dy; + GC_COORD_TYPE a, b; + GC_COORD_TYPE result; + + /* Determine the indices of two closest points in the table. */ + p1 = ((unsigned int) x) >> indexshift; + p2 = p1 + 1; + + if ((p1 >= countof(sinetable)) || (p2 >= countof(sinetable))) { + GCERR("invalid table index.\n"); + return GC_COORD_ZERO; + } + + /* Determine the coordinates of the two closest points. */ + p1x = p1 << indexshift; + p2x = p2 << indexshift; + + p1y = sinetable[p1]; + p2y = sinetable[p2]; + + /* Determine the deltas. */ + dx = p2x - p1x; + dy = p2y - p1y; + + /* Find the slope and the y-intercept. */ + b = (GC_COORD_TYPE) div64_s64(((s64) dy) << GC_COORD_FRACTION, dx); + a = p1y - (GC_COORD_TYPE) (((s64) b * p1x) >> GC_COORD_FRACTION); + + /* Compute the result. */ + result = a + (GC_COORD_TYPE) (((s64) b * x) >> GC_COORD_FRACTION); + return result; +} + + +/******************************************************************************* + * SINC function used in filter kernel generation. + */ + +static GC_COORD_TYPE sinc_filter(GC_COORD_TYPE x, int radius) +{ + GC_COORD_TYPE result; + s64 radius64; + s64 pit, pitd; + s64 normpit, normpitd; + int negpit, negpitd; + int quadpit, quadpitd; + GC_COORD_TYPE sinpit, sinpitd; + GC_COORD_TYPE f1, f2; + + if (x == GC_COORD_ZERO) + return GC_COORD_ONE; + + radius64 = abs(radius) << GC_COORD_FRACTION; + if (x > radius64) + return GC_COORD_ZERO; + + pit = (((s64) GC_COORD_PI) * x) >> GC_COORD_FRACTION; + pitd = div_s64(pit, radius); + + /* Sine table only has values for the first positive quadrant, + * remove the sign here. */ + if (pit < 0) { + normpit = -pit; + negpit = 1; + } else { + normpit = pit; + negpit = 0; + } + + if (pitd < 0) { + normpitd = -pitd; + negpitd = 1; + } else { + normpitd = pitd; + negpitd = 0; + } + + /* Determine which quadrant we are in. */ + quadpit = (int) ((normpit * GC_COORD_2OVERPI) + >> (2 * GC_COORD_FRACTION)); + quadpitd = (int) ((normpitd * GC_COORD_2OVERPI) + >> (2 * GC_COORD_FRACTION)); + + /* Move coordinates to the first quadrant. */ + normpit -= (s64) GC_COORD_PIOVER2 * quadpit; + normpitd -= (s64) GC_COORD_PIOVER2 * quadpitd; + + /* Normalize the quadrant numbers. */ + quadpit %= 4; + quadpitd %= 4; + + /* Flip the coordinates if necessary. */ + if ((quadpit == 1) || (quadpit == 3)) + normpit = GC_COORD_PIOVER2 - normpit; + + if ((quadpitd == 1) || (quadpitd == 3)) + normpitd = GC_COORD_PIOVER2 - normpitd; + + sinpit = sine((GC_COORD_TYPE) normpit); + sinpitd = sine((GC_COORD_TYPE) normpitd); + + /* Negate depending on the quadrant. */ + if (negpit) { + if ((quadpit == 0) || (quadpit == 1)) + sinpit = -sinpit; + } else { + if ((quadpit == 2) || (quadpit == 3)) + sinpit = -sinpit; + } + + if (negpitd) { + if ((quadpitd == 0) || (quadpitd == 1)) + sinpitd = -sinpitd; + } else { + if ((quadpitd == 2) || (quadpitd == 3)) + sinpitd = -sinpitd; + } + + f1 = (GC_COORD_TYPE) + div64_s64(((s64) sinpit) << GC_COORD_FRACTION, pit); + f2 = (GC_COORD_TYPE) + div64_s64(((s64) sinpitd) << GC_COORD_FRACTION, pitd); + + result = (GC_COORD_TYPE) ((((s64) f1) * f2) + >> GC_COORD_FRACTION); + + return result; +} + + +/******************************************************************************* + * Filter kernel generator based on SINC function. + */ + +static void calculate_sync_filter(struct gcfilterkernel *gcfilterkernel) +{ + GC_SCALE_TYPE scale; + GC_COORD_TYPE subpixset[GC_TAP_COUNT]; + GC_COORD_TYPE subpixeloffset; + GC_COORD_TYPE x, weight; + GC_SUM_TYPE weightsum; + short convweightsum; + int kernelhalf, padding; + int subpixpos, kernelpos; + short *kernelarray; + short count, adjustfrom, adjustment; + int index; + + /* Compute the scale factor. */ + scale = (gcfilterkernel->dstsize >= gcfilterkernel->srcsize) + ? GC_SCALE_ONE + : computescale(gcfilterkernel->dstsize, gcfilterkernel->srcsize); + + /* Calculate the kernel half. */ + kernelhalf = (int) (gcfilterkernel->kernelsize >> 1); + + /* Init the subpixel offset. */ + subpixeloffset = GC_COORD_HALF; + + /* Determine kernel padding size. */ + padding = (GC_TAP_COUNT - gcfilterkernel->kernelsize) / 2; + + /* Set initial kernel array pointer. */ + kernelarray = gcfilterkernel->kernelarray; + + /* Loop through each subpixel. */ + for (subpixpos = 0; subpixpos < GC_PHASE_LOAD_COUNT; subpixpos += 1) { + /* Compute weights. */ + weightsum = GC_COORD_ZERO; + for (kernelpos = 0; kernelpos < GC_TAP_COUNT; kernelpos += 1) { + /* Determine the current index. */ + index = kernelpos - padding; + + /* Pad with zeros left side. */ + if (index < 0) { + subpixset[kernelpos] = GC_COORD_ZERO; + continue; + } + + /* Pad with zeros right side. */ + if (index >= (int) gcfilterkernel->kernelsize) { + subpixset[kernelpos] = GC_COORD_ZERO; + continue; + } + + /* "Filter off" case. */ + if (gcfilterkernel->kernelsize == 1) { + subpixset[kernelpos] = GC_COORD_ONE; + + /* Update the sum of the weights. */ + weightsum += GC_COORD_ONE; + continue; + } + + /* Compute X coordinate. */ + x = ((index - kernelhalf) << GC_COORD_FRACTION) + + subpixeloffset; + + /* Scale the coordinate. */ + x = (GC_COORD_TYPE) + ((((s64) x) * scale) >> GC_SCALE_FRACTION); + + /* Compute the weight. */ + subpixset[kernelpos] = sinc_filter(x, kernelhalf); + + /* Update the sum of the weights. */ + weightsum += subpixset[kernelpos]; + } + + /* Convert the weights to the hardware format. */ + convweightsum = 0; + for (kernelpos = 0; kernelpos < GC_TAP_COUNT; kernelpos += 1) { + /* Normalize the current weight. */ + weight = normweight(subpixset[kernelpos], weightsum); + + /* Convert the weight to fixed point. */ + if (weight == GC_COORD_ZERO) + kernelarray[kernelpos] = GC_COEF_ZERO; + else if (weight >= GC_COORD_ONE) + kernelarray[kernelpos] = GC_COEF_ONE; + else if (weight <= GC_COORD_NEGONE) + kernelarray[kernelpos] = GC_COEF_NEGONE; + else + kernelarray[kernelpos] = convertweight(weight); + + /* Compute the sum of all coefficients. */ + convweightsum += kernelarray[kernelpos]; + } + + /* Adjust the fixed point coefficients so that the sum is 1. */ + count = GC_COEF_ONE - convweightsum; + if (count < 0) { + count = -count; + adjustment = -1; + } else { + adjustment = 1; + } + + if (count > GC_TAP_COUNT) { + GCERR("adjust count is too high = %d\n", count); + } else { + adjustfrom = (GC_TAP_COUNT - count) / 2; + for (kernelpos = 0; kernelpos < count; kernelpos += 1) + kernelarray[adjustfrom + kernelpos] + += adjustment; + } + + /* Advance the array pointer. */ + kernelarray += GC_TAP_COUNT; + + /* Advance to the next subpixel. */ + subpixeloffset -= GC_COORD_SUBPIX_STEP; + } +} + + +/******************************************************************************* + * Loads a filter into the GPU. + */ + +static enum bverror load_filter(struct bvbltparams *bvbltparams, + struct gcbatch *batch, + enum gcfiltertype type, + unsigned int kernelsize, + unsigned int scalefactor, + unsigned int srcsize, + unsigned int dstsize, + struct gccmdldstate arraystate) +{ + enum bverror bverror = BVERR_NONE; + struct gccontext *gccontext = get_context(); + struct gcfiltercache *filtercache; + struct list_head *filterlist; + struct list_head *filterhead; + struct gcfilterkernel *gcfilterkernel; + struct gcmofilterkernel *gcmofilterkernel; + + GCDBG(GCZONE_KERNEL, "kernelsize = %d\n", kernelsize); + GCDBG(GCZONE_KERNEL, "srcsize = %d\n", srcsize); + GCDBG(GCZONE_KERNEL, "dstsize = %d\n", dstsize); + GCDBG(GCZONE_KERNEL, "scalefactor = 0x%08X\n", scalefactor); + + /* Is the filter already loaded? */ + if ((gccontext->loadedfilter != NULL) && + (gccontext->loadedfilter->type == type) && + (gccontext->loadedfilter->kernelsize == kernelsize) && + (gccontext->loadedfilter->scalefactor == scalefactor)) { + GCDBG(GCZONE_KERNEL, "filter already computed.\n"); + gcfilterkernel = gccontext->loadedfilter; + goto load; + } + + /* Get the proper filter cache. */ + filtercache = &gccontext->filtercache[type][kernelsize]; + filterlist = &filtercache->list; + + /* Try to find existing filter. */ + GCDBG(GCZONE_KERNEL, "scanning for existing filter.\n"); + list_for_each(filterhead, filterlist) { + gcfilterkernel = list_entry(filterhead, + struct gcfilterkernel, + link); + if (gcfilterkernel->scalefactor == scalefactor) { + GCDBG(GCZONE_KERNEL, "filter found @ 0x%08X.\n", + (unsigned int) gcfilterkernel); + break; + } + } + + /* Found the filter? */ + if (filterhead != filterlist) { + /* Move the filter to the head of the list. */ + if (filterlist->next != filterhead) { + GCDBG(GCZONE_KERNEL, "moving to the head.\n"); + list_move(filterhead, filterlist); + } + } else { + GCDBG(GCZONE_KERNEL, "filter not found.\n"); + if (filtercache->count == GC_FILTER_CACHE_MAX) { + GCDBG(GCZONE_KERNEL, + "reached the maximum number of filters.\n"); + filterhead = filterlist->prev; + list_move(filterhead, filterlist); + + gcfilterkernel = list_entry(filterhead, + struct gcfilterkernel, + link); + } else { + GCDBG(GCZONE_KERNEL, "allocating new filter.\n"); + gcfilterkernel = gcalloc(struct gcfilterkernel, + sizeof(struct gcfilterkernel)); + if (gcfilterkernel == NULL) { + BVSETBLTERROR(BVERR_OOM, + "filter allocation failed"); + goto exit; + } + + list_add(&gcfilterkernel->link, filterlist); + } + + /* Update the number of filters. */ + filtercache->count += 1; + + /* Initialize the filter. */ + gcfilterkernel->type = type; + gcfilterkernel->kernelsize = kernelsize; + gcfilterkernel->srcsize = srcsize; + gcfilterkernel->dstsize = dstsize; + gcfilterkernel->scalefactor = scalefactor; + + /* Compute the coefficients. */ + calculate_sync_filter(gcfilterkernel); + } + +load: + GCDBG(GCZONE_KERNEL, "loading filter.\n"); + + /* Load the filter. */ + bverror = claim_buffer(bvbltparams, batch, + sizeof(struct gcmofilterkernel), + (void **) &gcmofilterkernel); + if (bverror != BVERR_NONE) + goto exit; + + gcmofilterkernel->kernelarray_ldst = arraystate; + memcpy(&gcmofilterkernel->kernelarray, + gcfilterkernel->kernelarray, + sizeof(gcfilterkernel->kernelarray)); + + /* Set the filter. */ + gccontext->loadedfilter = gcfilterkernel; + +exit: + return bverror; +} + + +/******************************************************************************* + * Compute the scale factor. + */ + +static inline unsigned int get_scale_factor(unsigned int srcsize, + unsigned int dstsize) +{ + if ((srcsize <= 1) || (dstsize <= 1)) + return 0; + + return ((srcsize - 1) << 16) / (dstsize - 1); +} + + +/******************************************************************************* + * Rotates the specified rectangle to the specified angle. + */ + +static void rotate_gcrect(int angle, + struct bvsurfgeom *srcgeom, struct gcrect *srcrect, + struct bvsurfgeom *dstgeom, struct gcrect *dstrect) +{ + unsigned int width, height; + struct gcrect rect; + + GCENTER(GCZONE_SURF); + + GCDBG(GCZONE_SURF, "src geom size = %dx%d\n", + srcgeom->width, srcgeom->height); + + switch (angle) { + case ROT_ANGLE_0: + GCDBG(GCZONE_SURF, "ROT_ANGLE_0\n"); + + if (dstgeom != srcgeom) { + dstgeom->width = srcgeom->width; + dstgeom->height = srcgeom->height; + } + + if (dstrect != srcrect) + *dstrect = *srcrect; + break; + + case ROT_ANGLE_90: + GCDBG(GCZONE_SURF, "ROT_ANGLE_90\n"); + + width = srcgeom->width; + height = srcgeom->height; + + dstgeom->width = height; + dstgeom->height = width; + + rect.left = height - srcrect->bottom; + rect.top = srcrect->left; + rect.right = height - srcrect->top; + rect.bottom = srcrect->right; + + *dstrect = rect; + break; + + case ROT_ANGLE_180: + GCDBG(GCZONE_SURF, "ROT_ANGLE_180\n"); + + width = srcgeom->width; + height = srcgeom->height; + + if (dstgeom != srcgeom) { + dstgeom->width = width; + dstgeom->height = height; + } + + rect.left = width - srcrect->right; + rect.top = height - srcrect->bottom; + rect.right = width - srcrect->left; + rect.bottom = height - srcrect->top; + + *dstrect = rect; + break; + + case ROT_ANGLE_270: + GCDBG(GCZONE_SURF, "ROT_ANGLE_270\n"); + + width = srcgeom->width; + height = srcgeom->height; + + dstgeom->width = height; + dstgeom->height = width; + + rect.left = srcrect->top; + rect.top = width - srcrect->right; + rect.right = srcrect->bottom; + rect.bottom = width - srcrect->left; + + *dstrect = rect; + break; + } + + GCEXIT(GCZONE_SURF); +} + + +/******************************************************************************* + * Setup destination rotation parameters. + */ + +void process_rotation(struct bvbltparams *bvbltparams, + struct gcbatch *batch, + struct surfaceinfo *srcinfo, + int adjangle) +{ + GCENTER(GCZONE_DEST); + + if (srcinfo->newgeom || + ((batch->batchflags & (BVBATCH_CLIPRECT | + BVBATCH_DESTRECT | + BVBATCH_DST)) != 0)) { + bool orthogonal; + struct gcfilter *gcfilter; + struct surfaceinfo *dstinfo; + int dstoffsetX, dstoffsetY; + + /* Get some shortcuts. */ + dstinfo = &batch->dstinfo; + gcfilter = &batch->op.filter; + + /* Compute the adjusted destination angle. */ + gcfilter->dstangle + = (dstinfo->angle + (4 - srcinfo->angle)) % 4; + GCDBG(GCZONE_DEST, "dstangle = %d\n", gcfilter->dstangle); + + /* Determine whether the new and the old destination angles + * are orthogonal to each other. */ + orthogonal = (gcfilter->dstangle % 2) != (dstinfo->angle % 2); + + switch (gcfilter->dstangle) { + case ROT_ANGLE_0: + /* Determine the origin offset. */ + dstoffsetX = dstinfo->xpixalign; + dstoffsetY = dstinfo->ypixalign; + + /* Determine geometry size. */ + if (orthogonal) { + batch->dstwidth = dstinfo->geom->height + - dstinfo->xpixalign; + batch->dstheight = dstinfo->geom->width + - dstinfo->ypixalign; + } else { + batch->dstwidth = dstinfo->geom->width + - dstinfo->xpixalign; + batch->dstheight = dstinfo->geom->height + - dstinfo->ypixalign; + } + + /* Determine the physical size. */ + dstinfo->physwidth = batch->dstwidth; + dstinfo->physheight = batch->dstheight; + break; + + case ROT_ANGLE_90: + /* Determine the origin offset. */ + dstoffsetX = dstinfo->ypixalign; + dstoffsetY = dstinfo->xpixalign; + + if (orthogonal) { + /* Determine geometry size. */ + batch->dstwidth = dstinfo->geom->height + - dstinfo->ypixalign; + batch->dstheight = dstinfo->geom->width + - dstinfo->xpixalign; + + /* Determine the physical size. */ + dstinfo->physwidth = dstinfo->geom->width + - dstinfo->xpixalign; + dstinfo->physheight = dstinfo->geom->height + - dstinfo->ypixalign; + } else { + /* Determine geometry size. */ + batch->dstwidth = dstinfo->geom->width + - dstinfo->ypixalign; + batch->dstheight = dstinfo->geom->height + - dstinfo->xpixalign; + + /* Determine the physical size. */ + dstinfo->physwidth = dstinfo->geom->height + - dstinfo->xpixalign; + dstinfo->physheight = dstinfo->geom->width + - dstinfo->ypixalign; + } + break; + + case ROT_ANGLE_180: + /* Determine the origin offset. */ + dstoffsetX = 0; + dstoffsetY = 0; + + /* Determine geometry size. */ + if (orthogonal) { + batch->dstwidth = dstinfo->geom->height + - dstinfo->xpixalign; + batch->dstheight = dstinfo->geom->width + - dstinfo->ypixalign; + } else { + batch->dstwidth = dstinfo->geom->width + - dstinfo->xpixalign; + batch->dstheight = dstinfo->geom->height + - dstinfo->ypixalign; + } + + /* Determine the physical size. */ + dstinfo->physwidth = batch->dstwidth; + dstinfo->physheight = batch->dstheight; + break; + + case ROT_ANGLE_270: + /* Determine the origin offset. */ + dstoffsetX = 0; + dstoffsetY = 0; + + if (orthogonal) { + /* Determine geometry size. */ + batch->dstwidth = dstinfo->geom->height + = dstinfo->ypixalign; + batch->dstheight = dstinfo->geom->width + - dstinfo->xpixalign; + + /* Determine the physical size. */ + dstinfo->physwidth = dstinfo->geom->width + - dstinfo->xpixalign; + dstinfo->physheight = dstinfo->geom->height + - dstinfo->ypixalign; + } else { + /* Determine geometry size. */ + batch->dstwidth = dstinfo->geom->width + - dstinfo->ypixalign; + batch->dstheight = dstinfo->geom->height + - dstinfo->xpixalign; + + /* Determine the physical size. */ + dstinfo->physwidth = dstinfo->geom->height + - dstinfo->xpixalign; + dstinfo->physheight = dstinfo->geom->width + - dstinfo->ypixalign; + } + break; + + default: + dstoffsetX = 0; + dstoffsetY = 0; + } + + /* Rotate the original destination rectangle + * to match the new angle. */ + rotate_gcrect(adjangle, + dstinfo->geom, &dstinfo->rect, + &gcfilter->dstgeom, &gcfilter->dstrect); + + /* Rotate the clipped destination rectangle. */ + rotate_gcrect(adjangle, + dstinfo->geom, &batch->dstclipped, + &gcfilter->dstgeom, &gcfilter->dstclipped); + + /* Compute the adjusted the destination rectangle. */ + gcfilter->dstadjusted.left + = gcfilter->dstclipped.left - dstoffsetX; + gcfilter->dstadjusted.top + = gcfilter->dstclipped.top - dstoffsetY; + gcfilter->dstadjusted.right + = gcfilter->dstclipped.right - dstoffsetX; + gcfilter->dstadjusted.bottom + = gcfilter->dstclipped.bottom - dstoffsetY; + + GCPRINT_RECT(GCZONE_DEST, "rotated dstrect", + &gcfilter->dstrect); + GCPRINT_RECT(GCZONE_DEST, "rotated dstclipped", + &gcfilter->dstclipped); + GCPRINT_RECT(GCZONE_DEST, "rotated dstadjusted", + &gcfilter->dstadjusted); + + if (batch->haveaux) { + /* Rotate the original aux destination rectangle + * to match the new angle. */ + rotate_gcrect(adjangle, dstinfo->geom, + &batch->dstrectaux, &gcfilter->dstgeom, + &gcfilter->dstrectaux); + + /* Rotate the aux destination rectangle. */ + rotate_gcrect(adjangle, dstinfo->geom, + &batch->dstclippedaux, &gcfilter->dstgeom, + &gcfilter->dstclippedaux); + + /* Compute the adjust the aux destination rectangle. */ + gcfilter->dstadjustedaux.left + = batch->dstclippedaux.left - dstoffsetX; + gcfilter->dstadjustedaux.top + = batch->dstclippedaux.top - dstoffsetY; + gcfilter->dstadjustedaux.right + = batch->dstclippedaux.right - dstoffsetX; + gcfilter->dstadjustedaux.bottom + = batch->dstclippedaux.bottom - dstoffsetY; + + GCPRINT_RECT(GCZONE_DEST, "rotated dstrectaux", + &gcfilter->dstrectaux); + GCPRINT_RECT(GCZONE_DEST, "rotated dstclippedaux", + &gcfilter->dstclippedaux); + GCPRINT_RECT(GCZONE_DEST, "rotated dstadjustedaux", + &gcfilter->dstadjustedaux); + } + + GCDBG(GCZONE_DEST, "aligned geometry size = %dx%d\n", + batch->dstwidth, batch->dstheight); + GCDBG(GCZONE_DEST, "aligned physical size = %dx%d\n", + dstinfo->physwidth, dstinfo->physheight); + GCDBG(GCZONE_DEST, "origin offset (pixels) = %d,%d\n", + dstoffsetX, dstoffsetY); + } + + GCEXIT(GCZONE_DEST); +} + + +/******************************************************************************* + * Rasterizer setup. + */ + +static enum bverror startvr(struct bvbltparams *bvbltparams, + struct gcbatch *batch, + struct bvbuffmap *srcmap, + struct bvbuffmap *dstmap, + struct surfaceinfo *srcinfo, + struct surfaceinfo *dstinfo, + unsigned int srcx, + unsigned int srcy, + struct gcrect *dstrect, + int srcangle, + int dstangle, + enum gcscaletype scaletype) +{ + enum bverror bverror; + struct gccontext *gccontext = get_context(); + struct gcfilter *gcfilter; + + struct gcmovrdst *gcmovrdst; + struct gcmovrsrc *gcmovrsrc; + struct gcmostartvr *gcmostartvr; + + struct gcrect srcrect; + + GCENTERARG(GCZONE_FILTER, "scaletype = %d\n", scaletype); + + /* Get a shortcut to the filter properties. */ + gcfilter = &batch->op.filter; + + /*********************************************************************** + * Program the destination. + */ + + GCDBG(GCZONE_FILTER, "destination:\n"); + GCDBG(GCZONE_FILTER, " angle = %d\n", dstangle); + GCDBG(GCZONE_FILTER, " pixalign = %d,%d\n", + dstinfo->xpixalign, dstinfo->ypixalign); + GCDBG(GCZONE_FILTER, " bytealign = %d\n", dstinfo->bytealign); + GCDBG(GCZONE_FILTER, " virtstride = %d\n", dstinfo->geom->virtstride); + GCDBG(GCZONE_FILTER, " format = %d\n", dstinfo->format.format); + GCDBG(GCZONE_FILTER, " swizzle = %d\n", dstinfo->format.swizzle); + GCDBG(GCZONE_FILTER, " premul = %d\n", dstinfo->format.premultiplied); + GCDBG(GCZONE_FILTER, " physwidth = %d\n", dstinfo->physwidth); + GCDBG(GCZONE_FILTER, " physheight = %d\n", dstinfo->physheight); + GCPRINT_RECT(GCZONE_FILTER, " rect", dstrect); + + /* Allocate command buffer. */ + bverror = claim_buffer(bvbltparams, batch, + sizeof(struct gcmovrdst), + (void **) &gcmovrdst); + if (bverror != BVERR_NONE) + goto exit; + + /* Add the address fixup. */ + add_fixup(bvbltparams, batch, &gcmovrdst->address, dstinfo->bytealign); + + /* Set surface parameters. */ + gcmovrdst->config_ldst = gcmovrdst_config_ldst; + gcmovrdst->address = GET_MAP_HANDLE(dstmap); + gcmovrdst->stride = dstinfo->geom->virtstride; + gcmovrdst->config.raw = 0; + gcmovrdst->config.reg.swizzle = dstinfo->format.swizzle; + gcmovrdst->config.reg.format = dstinfo->format.format; + + /* Set surface width and height. */ + gcmovrdst->rotation.raw = 0; + gcmovrdst->rotation.reg.surf_width = dstinfo->physwidth; + gcmovrdst->rotationheight_ldst = gcmovrdst_rotationheight_ldst; + gcmovrdst->rotationheight.raw = 0; + gcmovrdst->rotationheight.reg.height = dstinfo->physheight; + + /*********************************************************************** + * Program the source. + */ + + /* Determine adjusted source bounding rectangle and origin. */ + srcrect = srcinfo->rect; + srcrect.left -= srcinfo->xpixalign; + srcrect.right -= srcinfo->xpixalign; + srcx -= (srcinfo->xpixalign << 16); + + GCDBG(GCZONE_FILTER, "source:\n"); + GCDBG(GCZONE_FILTER, " angle = %d\n", srcangle); + GCDBG(GCZONE_FILTER, " pixalign = %d,%d\n", + srcinfo->xpixalign, srcinfo->ypixalign); + GCDBG(GCZONE_FILTER, " bytealign = %d\n", srcinfo->bytealign); + GCDBG(GCZONE_FILTER, " virtstride = %d\n", srcinfo->geom->virtstride); + GCDBG(GCZONE_FILTER, " format = %d\n", srcinfo->format.format); + GCDBG(GCZONE_FILTER, " swizzle = %d\n", srcinfo->format.swizzle); + GCDBG(GCZONE_FILTER, " premul = %d\n", srcinfo->format.premultiplied); + GCDBG(GCZONE_FILTER, " physwidth = %d\n", srcinfo->physwidth); + GCDBG(GCZONE_FILTER, " physheight = %d\n", srcinfo->physheight); + GCPRINT_RECT(GCZONE_FILTER, " rect", &srcrect); + + GCDBG(GCZONE_FILTER, "src origin: 0x%08X,0x%08X\n", srcx, srcy); + + /* Allocate command buffer. */ + bverror = claim_buffer(bvbltparams, batch, + sizeof(struct gcmovrsrc), + (void **) &gcmovrsrc); + if (bverror != BVERR_NONE) + goto exit; + + add_fixup(bvbltparams, batch, &gcmovrsrc->address, srcinfo->bytealign); + + gcmovrsrc->config_ldst = gcmovrsrc_config_ldst; + + gcmovrsrc->address = GET_MAP_HANDLE(srcmap); + gcmovrsrc->stride = srcinfo->geom->virtstride; + + gcmovrsrc->rotation.raw = 0; + gcmovrsrc->rotation.reg.surf_width = srcinfo->physwidth; + + gcmovrsrc->config.raw = 0; + gcmovrsrc->config.reg.swizzle = srcinfo->format.swizzle; + gcmovrsrc->config.reg.format = srcinfo->format.format; + + if (gccontext->gcfeatures2.reg.l2cachefor420 && + (srcinfo->format.type == BVFMT_YUV) && + (srcinfo->format.cs.yuv.planecount > 1) && + ((srcinfo->angle & 1) != 0)) + gcmovrsrc->config.reg.disable420L2cache + = GCREG_SRC_CONFIG_DISABLE420_L2_CACHE_DISABLED; + + gcmovrsrc->pos_ldst = gcmovrsrc_pos_ldst; + + /* Source image bounding box. */ + gcmovrsrc->lt.reg.left = srcrect.left; + gcmovrsrc->lt.reg.top = srcrect.top; + gcmovrsrc->rb.reg.right = srcrect.right; + gcmovrsrc->rb.reg.bottom = srcrect.bottom; + + /* Fractional origin. */ + gcmovrsrc->x = srcx; + gcmovrsrc->y = srcy; + + /* Program rotation. */ + gcmovrsrc->rotation_ldst = gcmovrsrc_rotation_ldst; + gcmovrsrc->rotationheight.reg.height = srcinfo->physheight; + gcmovrsrc->rotationangle.raw = 0; + gcmovrsrc->rotationangle.reg.src = rotencoding[srcangle]; + gcmovrsrc->rotationangle.reg.dst = rotencoding[dstangle]; + gcmovrsrc->rotationangle.reg.src_mirror = srcinfo->mirror; + gcmovrsrc->rotationangle.reg.dst_mirror = dstinfo->mirror; + + gcmovrsrc->rop_ldst = gcmovrsrc_rop_ldst; + gcmovrsrc->rop.raw = 0; + gcmovrsrc->rop.reg.type = GCREG_ROP_TYPE_ROP3; + gcmovrsrc->rop.reg.fg = 0xCC; + + /* Program multiply modes. */ + gcmovrsrc->mult_ldst = gcmovrsrc_mult_ldst; + gcmovrsrc->mult.raw = 0; + gcmovrsrc->mult.reg.srcglobalpremul + = GCREG_COLOR_MULTIPLY_MODES_SRC_GLOBAL_PREMULTIPLY_DISABLE; + + if (srcinfo->format.premultiplied) + gcmovrsrc->mult.reg.srcpremul + = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE; + else + gcmovrsrc->mult.reg.srcpremul + = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE; + + if (dstinfo->format.premultiplied) { + gcmovrsrc->mult.reg.dstpremul + = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE; + + gcmovrsrc->mult.reg.dstdemul + = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_DISABLE; + } else { + gcmovrsrc->mult.reg.dstpremul + = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE; + + gcmovrsrc->mult.reg.dstdemul + = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_ENABLE; + } + + /* Program YUV source. */ + if (srcinfo->format.type == BVFMT_YUV) { + bverror = set_yuvsrc(bvbltparams, batch, srcinfo, srcmap); + if (bverror != BVERR_NONE) + goto exit; + } + + /*********************************************************************** + * Program blending. + */ + + bverror = set_blending(bvbltparams, batch, srcinfo); + if (bverror != BVERR_NONE) + goto exit; + + /*********************************************************************** + * Start the operation. + */ + + bverror = claim_buffer(bvbltparams, batch, + sizeof(struct gcmostartvr), + (void **) &gcmostartvr); + if (bverror != BVERR_NONE) + goto exit; + + switch (scaletype) { + case GC_SCALE_OPF: + gcmostartvr->scalex = gcfilter->horscalefactor; + gcmostartvr->scaley = gcfilter->verscalefactor; + gcmostartvr->config = gcregvrconfig_onepass; + break; + + case GC_SCALE_HOR: + gcmostartvr->scalex = gcfilter->horscalefactor; + gcmostartvr->scaley = 0; + gcmostartvr->config = gcregvrconfig_horizontal; + break; + + case GC_SCALE_VER: + gcmostartvr->scalex = 0; + gcmostartvr->scaley = gcfilter->verscalefactor; + gcmostartvr->config = gcregvrconfig_vertical; + break; + + case GC_SCALE_HOR_FLIPPED: + gcmostartvr->scalex = 0; + gcmostartvr->scaley = gcfilter->horscalefactor; + gcmostartvr->config = gcregvrconfig_vertical; + break; + + case GC_SCALE_VER_FLIPPED: + gcmostartvr->scalex = gcfilter->verscalefactor; + gcmostartvr->scaley = 0; + gcmostartvr->config = gcregvrconfig_horizontal; + break; + } + + gcmostartvr->scale_ldst = gcmostartvr_scale_ldst; + gcmostartvr->rect_ldst = gcmostartvr_rect_ldst; + gcmostartvr->config_ldst = gcmostartvr_config_ldst; + + gcmostartvr->lt.left = dstrect->left; + gcmostartvr->lt.top = dstrect->top; + gcmostartvr->rb.right = dstrect->right; + gcmostartvr->rb.bottom = dstrect->bottom; + +exit: + GCEXITARG(GCZONE_FILTER, "bv%s = %d\n", + (bverror == BVERR_NONE) ? "result" : "error", bverror); + return bverror; +} + + +/******************************************************************************* + * Main fiter entry. + */ + +enum bverror do_filter(struct bvbltparams *bvbltparams, + struct gcbatch *batch, + struct surfaceinfo *srcinfo) +{ + enum bverror bverror = BVERR_NONE; + struct gccontext *gccontext = get_context(); + + struct gcfilter *gcfilter; + struct surfaceinfo *dstinfo; + + bool scalex, scaley; + bool singlepass, twopass; + + struct gcrect *srcrect; + struct gcrect *dstrect; + struct gcrect *dstclipped; + struct gcrect *dstadjusted; + + struct bvsurfgeom dstrotated0geom; + struct gcrect dstrotated0; + + struct gcrect dstdelta; + struct gcrect srcdelta; + struct gcrect srcclipped; + + struct bvbuffmap *srcmap = NULL; + struct bvbuffmap *tmpmap = NULL; + struct bvbuffmap *dstmap = NULL; + + struct gcmovrconfigex *gcmovrconfigex; + + int adjangle; + unsigned int srcx, srcy; + unsigned int srcwidth, srcheight; + unsigned int dstwidth, dstheight; + unsigned int horscalefactor, verscalefactor; + unsigned int kernelsize; + + GCENTER(GCZONE_FILTER); + + /* Get some shortcuts. */ + dstinfo = &batch->dstinfo; + gcfilter = &batch->op.filter; + + /* Finish previous batch if any. */ + bverror = batch->batchend(bvbltparams, batch); + if (bverror != BVERR_NONE) + goto exit; + + /* ROP is not supported by the filters. */ + if ((srcinfo->rop & 0xFF) != 0xCC) { + BVSETBLTERROR(BVERR_ROP, + "only copy ROP is supported in scaling mode"); + goto exit; + } + + /* Parse the scale mode. */ + bverror = parse_scalemode(bvbltparams, batch); + if (bverror != BVERR_NONE) + goto exit; + + /* Parse destination parameters. */ + bverror = parse_destination(bvbltparams, batch); + if (bverror != BVERR_NONE) + goto exit; + + /* Compute the source alignments needed to compensate + * for the surface base address misalignment if any. */ + srcinfo->xpixalign = get_pixel_offset(srcinfo, 0); + srcinfo->ypixalign = 0; + srcinfo->bytealign = (srcinfo->xpixalign + * (int) srcinfo->format.bitspp) / 8; + GCDBG(GCZONE_SRC, "source surface offset (pixels) = %d,%d\n", + srcinfo->xpixalign, srcinfo->ypixalign); + GCDBG(GCZONE_SRC, "source surface offset (bytes) = %d\n", + srcinfo->bytealign); + + /* Compute U/V plane offsets. */ + if ((srcinfo->format.type == BVFMT_YUV) && + (srcinfo->format.cs.yuv.planecount > 1)) + set_computeyuv(srcinfo, 0, 0); + + /* Determine physical size. */ + if ((srcinfo->angle % 2) == 0) { + srcinfo->physwidth = srcinfo->geom->width + - srcinfo->xpixalign; + srcinfo->physheight = srcinfo->geom->height + - srcinfo->ypixalign; + } else { + srcinfo->physwidth = srcinfo->geom->height + - srcinfo->xpixalign; + srcinfo->physheight = srcinfo->geom->width + - srcinfo->ypixalign; + } + GCDBG(GCZONE_SRC, "source physical size = %dx%d\n", + srcinfo->physwidth, srcinfo->physheight); + + /* OPF does not support source rotation, which can be compensated by + * using destination rotation. Compute the adjustment angle. + * For simplicity use the same algorythm for both OPF and TPF. */ + adjangle = (4 - srcinfo->angle) % 4; + GCDBG(GCZONE_DEST, "adjangle = %d\n", adjangle); + + /* Compute destination rotation. */ + process_rotation(bvbltparams, batch, srcinfo, adjangle); + + /* Rotate the source rectangle to 0 degree. */ + srcrect = &srcinfo->rect; + GCPRINT_RECT(GCZONE_FILTER, "full src", srcrect); + rotate_gcrect(adjangle, + srcinfo->geom, srcrect, + srcinfo->geom, srcrect); + GCPRINT_RECT(GCZONE_FILTER, "full adjusted src", srcrect); + + /* Get destination rect shortcuts. */ + if ((srcinfo->index == 1) && batch->haveaux) { + dstrect = &gcfilter->dstrectaux; + dstclipped = &gcfilter->dstclippedaux; + dstadjusted = &gcfilter->dstadjustedaux; + } else { + dstrect = &gcfilter->dstrect; + dstclipped = &gcfilter->dstclipped; + dstadjusted = &gcfilter->dstadjusted; + } + + GCPRINT_RECT(GCZONE_FILTER, "full adjusted dst", dstrect); + GCPRINT_RECT(GCZONE_FILTER, "clipped adjusted dst", dstclipped); + GCPRINT_RECT(GCZONE_FILTER, "aligned adjusted dst", dstadjusted); + + /* Determine the source and destination rectangles. */ + srcwidth = srcrect->right - srcrect->left; + srcheight = srcrect->bottom - srcrect->top; + dstwidth = dstrect->right - dstrect->left; + dstheight = dstrect->bottom - dstrect->top; + + GCDBG(GCZONE_FILTER, "adjusted input src size: %dx%d\n", + srcwidth, srcheight); + GCDBG(GCZONE_FILTER, "adjusted input dst size: %dx%d\n", + dstwidth, dstheight); + + /* Determine the data path. */ + scalex = (srcwidth != dstwidth); + scaley = (srcheight != dstheight); + + twopass = scalex && scaley; + if (twopass) { + if (((gcfilter->horkernelsize == 3) || + (gcfilter->horkernelsize == 5)) && + ((gcfilter->verkernelsize == 3) || + (gcfilter->verkernelsize == 5))) { + singlepass = true; + twopass = false; + } else { + singlepass = false; + } + } else { + /* Two pass filter in one pass mode. */ + if (!scalex && !scaley) + GCERR("no scaling needed.\n"); + + GCDBG(GCZONE_FILTER, "only %s scaling needed.\n", + scalex ? "horizontal" : "vertical"); + + singlepass = false; + } + + /* Compute the scale factors. */ + gcfilter->horscalefactor = + horscalefactor = get_scale_factor(srcwidth, dstwidth); + GCDBG(GCZONE_FILTER, "horscalefactor = 0x%08X\n", horscalefactor); + + gcfilter->verscalefactor = + verscalefactor = get_scale_factor(srcheight, dstheight); + GCDBG(GCZONE_FILTER, "verscalefactor = 0x%08X\n", verscalefactor); + + /* Compute the destination offsets. */ + dstdelta.left = dstclipped->left - dstrect->left; + dstdelta.top = dstclipped->top - dstrect->top; + dstdelta.right = dstclipped->right - dstrect->left; + dstdelta.bottom = dstclipped->bottom - dstrect->top; + GCDBG(GCZONE_FILTER, "dst deltas = (%d,%d)-(%d,%d)\n", + dstdelta.left, dstdelta.top, dstdelta.right, dstdelta.bottom); + + /* Compute the source offsets. */ + srcdelta.left = dstdelta.left * horscalefactor; + srcdelta.top = dstdelta.top * verscalefactor; + srcdelta.right = (dstdelta.right - 1) * horscalefactor + (1 << 16); + srcdelta.bottom = (dstdelta.bottom - 1) * verscalefactor + (1 << 16); + + /* Before rendering each destination pixel, the HW will select the + * corresponding source center pixel to apply the kernel around. + * To make this process precise we need to add 0.5 to source initial + * coordinates here; this will make HW pick the next source pixel if + * the fraction is equal or greater then 0.5. */ + srcdelta.left += 0x00008000; + srcdelta.top += 0x00008000; + srcdelta.right += 0x00008000; + srcdelta.bottom += 0x00008000; + GCDBG(GCZONE_FILTER, "src deltas = " + "(0x%08X,0x%08X)-(0x%08X,0x%08X)\n", + srcdelta.left, srcdelta.top, srcdelta.right, srcdelta.bottom); + GCDBG(GCZONE_FILTER, "src deltas (int) = (%d,%d)-(%d,%d)\n", + srcdelta.left >> 16, srcdelta.top >> 16, + srcdelta.right >> 16, srcdelta.bottom >> 16); + + /* Determine clipped source rectangle. */ + srcclipped.left = srcrect->left + (srcdelta.left >> 16); + srcclipped.top = srcrect->top + (srcdelta.top >> 16); + srcclipped.right = srcrect->left + (srcdelta.right >> 16); + srcclipped.bottom = srcrect->top + (srcdelta.bottom >> 16); + + GCDBG(GCZONE_FILTER, "source:\n"); + GCDBG(GCZONE_FILTER, " stride = %d, geom = %dx%d\n", + srcinfo->geom->virtstride, + srcinfo->geom->width, srcinfo->geom->height); + GCDBG(GCZONE_FILTER, " rotation = %d\n", + srcinfo->angle); + GCPRINT_RECT(GCZONE_FILTER, " clipped rect", &srcclipped); + + GCDBG(GCZONE_FILTER, "destination:\n"); + GCDBG(GCZONE_FILTER, " stride = %d, geom size = %dx%d\n", + dstinfo->geom->virtstride, + dstinfo->geom->width, dstinfo->geom->height); + GCDBG(GCZONE_FILTER, " rotation = %d\n", + dstinfo->angle); + GCPRINT_RECT(GCZONE_FILTER, " clipped rect", dstclipped); + + /* Validate the source rectangle. */ + if (!valid_rect(srcinfo->geom, &srcclipped)) { + BVSETBLTERROR((srcinfo->index == 0) + ? BVERR_SRC1RECT + : BVERR_SRC2RECT, + "invalid source rectangle."); + goto exit; + } + + /* Map the source. */ + bverror = do_map(srcinfo->buf.desc, batch, &srcmap); + if (bverror != BVERR_NONE) { + bvbltparams->errdesc = gccontext->bverrorstr; + goto exit; + } + + /* Map the destination. */ + bverror = do_map(dstinfo->buf.desc, batch, &dstmap); + if (bverror != BVERR_NONE) { + bvbltparams->errdesc = gccontext->bverrorstr; + goto exit; + } + + /* Do single pass filter if we can. */ + if (singlepass) { + GCDBG(GCZONE_TYPE, "single pass\n"); + + /* Determine the kernel size to use. */ + kernelsize = max(gcfilter->horkernelsize, + gcfilter->verkernelsize); + + /* Set kernel size. */ + bverror = claim_buffer(bvbltparams, batch, + sizeof(struct gcmovrconfigex), + (void **) &gcmovrconfigex); + if (bverror != BVERR_NONE) + goto exit; + + gcmovrconfigex->config_ldst = gcmovrconfigex_config_ldst; + gcmovrconfigex->config.raw = ~0U; + gcmovrconfigex->config.reg.kernelsize = kernelsize; + gcmovrconfigex->config.reg.mask_kernelsize + = GCREG_VR_CONFIG_EX_MASK_FILTER_TAP_ENABLED; + + /* Setup single pass. */ + srcx = (srcrect->left << 16) + srcdelta.left; + srcy = (srcrect->top << 16) + srcdelta.top; + GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy); + + /* Load the horizontal filter. */ + bverror = load_filter(bvbltparams, batch, + GC_FILTER_SYNC, + gcfilter->horkernelsize, + gcfilter->horscalefactor, + srcwidth, dstwidth, + gcmofilterkernel_horizontal_ldst); + if (bverror != BVERR_NONE) + goto exit; + + /* Load the vertical filter. */ + bverror = load_filter(bvbltparams, batch, + GC_FILTER_SYNC, + gcfilter->verkernelsize, + gcfilter->verscalefactor, + srcheight, dstheight, + gcmofilterkernel_vertical_ldst); + if (bverror != BVERR_NONE) + goto exit; + + /* Start the operation. */ + bverror = startvr(bvbltparams, batch, + srcmap, dstmap, srcinfo, dstinfo, + srcx, srcy, dstadjusted, + ROT_ANGLE_0, gcfilter->dstangle, + GC_SCALE_OPF); + } else if (twopass) { + unsigned int horkernelhalf; + unsigned int leftextra, rightextra; + unsigned int tmprectwidth, tmprectheight; + unsigned int tmpalignmask, dstalignmask; + unsigned int tmpsize; + struct surfaceinfo tmpinfo; + struct bvsurfgeom tmpgeom; + + GCDBG(GCZONE_TYPE, "two pass\n"); + + /* Initialize the temporaty surface geometry. */ + tmpgeom.structsize = sizeof(struct bvsurfgeom); + tmpgeom.orientation = 0; + tmpgeom.paletteformat = 0; + tmpgeom.palette = NULL; + + /* Initialize the temporaty surface descriptor. */ + tmpinfo.index = -1; + tmpinfo.geom = &tmpgeom; + tmpinfo.angle = gcfilter->dstangle; + tmpinfo.mirror = GCREG_MIRROR_NONE; + tmpinfo.rop = 0; + GCDBG(GCZONE_FILTER, "tmp angle = %d\n", tmpinfo.angle); + + /* Transfer blending parameters from the source to the + * temporary buffer so that the blending would happen + * on the second pass. */ + tmpinfo.gca = srcinfo->gca; + srcinfo->gca = NULL; + + /* Determine temporary surface format. */ + if (srcinfo->format.type == BVFMT_YUV) { + if (tmpinfo.angle == ROT_ANGLE_0) { + GCDBG(GCZONE_FILTER, + "tmp format = 4:2:2\n"); + tmpgeom.format = OCDFMT_YUYV; + parse_format(bvbltparams, &tmpinfo); + } else { + GCDBG(GCZONE_FILTER, + "tmp format = dst format\n"); + tmpgeom.format = dstinfo->geom->format; + tmpinfo.format = dstinfo->format; + } + } else { + GCDBG(GCZONE_FILTER, + "tmp format = src format\n"); + tmpgeom.format = srcinfo->geom->format; + tmpinfo.format = srcinfo->format; + } + + /* Determine pixel alignment masks. */ + tmpalignmask = GC_BITS_PER_CACHELINE + / tmpinfo.format.bitspp - 1; + dstalignmask = GC_BITS_PER_CACHELINE + / dstinfo->format.bitspp - 1; + + /* In partial filter blit cases, the vertical pass has to render + * more pixel information to the left and to the right of the + * temporary image so that the next pass has its necessary + * kernel information on the edges of the image. */ + horkernelhalf = gcfilter->horkernelsize >> 1; + + leftextra = srcdelta.left >> 16; + rightextra = srcwidth - (srcdelta.right >> 16); + + if (leftextra > horkernelhalf) + leftextra = horkernelhalf; + + if (rightextra > horkernelhalf) + rightextra = horkernelhalf; + + GCDBG(GCZONE_FILTER, "leftextra = %d, rightextra = %d\n", + leftextra, rightextra); + + /* Determine the source origin. */ + srcx = ((srcrect->left - leftextra) << 16) + srcdelta.left; + srcy = (srcrect->top << 16) + srcdelta.top; + GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy); + GCDBG(GCZONE_SRC, "src origin (int): %d,%d\n", + srcx >> 16, srcy >> 16); + + /* Determine the size of the temporary rectangle. */ + tmprectwidth = leftextra + rightextra + + ((srcdelta.right >> 16) - (srcdelta.left >> 16)); + tmprectheight = dstadjusted->bottom - dstadjusted->top; + GCDBG(GCZONE_FILTER, "tmp rect size: %dx%d\n", + tmprectwidth, tmprectheight); + + /* Determine the temporary destination coordinates. */ + switch (tmpinfo.angle) { + case ROT_ANGLE_0: + case ROT_ANGLE_180: + tmpinfo.rect.left = (srcx >> 16) & tmpalignmask; + tmpinfo.rect.top = 0; + tmpinfo.rect.right = tmpinfo.rect.left + tmprectwidth; + tmpinfo.rect.bottom = tmprectheight; + + tmpgeom.width = (tmpinfo.rect.right + tmpalignmask) + & ~tmpalignmask; + tmpgeom.height = tmprectheight; + + tmpinfo.physwidth = tmpgeom.width; + tmpinfo.physheight = tmpgeom.height; + break; + + case ROT_ANGLE_90: + tmpinfo.rect.left = 0; + tmpinfo.rect.top = dstadjusted->left & dstalignmask; + tmpinfo.rect.right = tmprectwidth; + tmpinfo.rect.bottom = tmpinfo.rect.top + tmprectheight; + + tmpgeom.width = tmprectwidth; + tmpgeom.height = (tmpinfo.rect.bottom + tmpalignmask) + & ~tmpalignmask; + + tmpinfo.physwidth = tmpgeom.height; + tmpinfo.physheight = tmpgeom.width; + break; + + case ROT_ANGLE_270: + tmpinfo.rect.left = 0; + tmpinfo.rect.right = tmprectwidth; + tmpinfo.rect.bottom = dstadjusted->left & dstalignmask; + + tmpgeom.width = tmprectwidth; + tmpgeom.height = (tmpinfo.rect.bottom + tmprectheight + + tmpalignmask) & ~tmpalignmask; + + tmpinfo.rect.bottom = tmpgeom.height + - tmpinfo.rect.bottom; + tmpinfo.rect.top = tmpinfo.rect.bottom + - tmprectheight; + + tmpinfo.physwidth = tmpgeom.height; + tmpinfo.physheight = tmpgeom.width; + break; + } + + GCPRINT_RECT(GCZONE_DEST, "tmp dest", &tmpinfo.rect); + GCDBG(GCZONE_FILTER, "tmp geometry size = %dx%d\n", + tmpgeom.width, tmpgeom.height); + GCDBG(GCZONE_FILTER, "tmp physical size = %dx%d\n", + tmpinfo.physwidth, tmpinfo.physheight); + + /* Determine the size of the temporaty surface. */ + tmpgeom.virtstride = (tmpinfo.physwidth + * tmpinfo.format.bitspp) / 8; + tmpsize = tmpgeom.virtstride * tmpinfo.physheight; + tmpsize += GC_BYTES_PER_CACHELINE; + tmpsize = (tmpsize + ~PAGE_MASK) & PAGE_MASK; + GCDBG(GCZONE_FILTER, "tmp stride = %d\n", tmpgeom.virtstride); + GCDBG(GCZONE_FILTER, "tmp size (bytes) = %d\n", tmpsize); + + /* Allocate the temporary buffer. */ + bverror = allocate_temp(bvbltparams, tmpsize); + if (bverror != BVERR_NONE) + goto exit; + + /* Map the temporary buffer. */ + tmpinfo.buf.desc = gccontext->tmpbuffdesc; + bverror = do_map(tmpinfo.buf.desc, batch, &tmpmap); + if (bverror != BVERR_NONE) { + bvbltparams->errdesc = gccontext->bverrorstr; + goto exit; + } + + /* Compute the temp buffer alignments needed to compensate + * for the surface base address misalignment if any. */ + tmpinfo.xpixalign = 0; + tmpinfo.ypixalign = 0; + tmpinfo.bytealign = (get_pixel_offset(&tmpinfo, 0) + * (int) tmpinfo.format.bitspp) / 8; + GCDBG(GCZONE_SRC, "tmp offset (pixels) = %d,%d\n", + tmpinfo.xpixalign, tmpinfo.ypixalign); + GCDBG(GCZONE_SRC, "tmp offset (bytes) = %d\n", + tmpinfo.bytealign); + + /* Load the vertical filter. */ + bverror = load_filter(bvbltparams, batch, + GC_FILTER_SYNC, + gcfilter->verkernelsize, + gcfilter->verscalefactor, + srcheight, dstheight, + gcmofilterkernel_shared_ldst); + if (bverror != BVERR_NONE) + goto exit; + + /* Start the operation. */ + GCDBG(GCZONE_TYPE, "vertical pass\n"); + bverror = startvr(bvbltparams, batch, + srcmap, tmpmap, srcinfo, &tmpinfo, + srcx, srcy, &tmpinfo.rect, + ROT_ANGLE_0, tmpinfo.angle, + GC_SCALE_VER); + if (bverror != BVERR_NONE) + goto exit; + + /* Fake no rotation. */ + adjangle = (4 - tmpinfo.angle) % 4; + GCDBG(GCZONE_DEST, "adjangle = %d\n", adjangle); + + /* Rotate the source rectangle to 0 degree. */ + rotate_gcrect(adjangle, + tmpinfo.geom, &tmpinfo.rect, + tmpinfo.geom, &tmpinfo.rect); + GCPRINT_RECT(GCZONE_DEST, "tmp src", &tmpinfo.rect); + + /* Rotate the destination rectangle to 0 degree. */ + rotate_gcrect(adjangle, + &gcfilter->dstgeom, dstclipped, + &dstrotated0geom, &dstrotated0); + GCPRINT_RECT(GCZONE_DEST, "dest", &dstrotated0); + + /* Apply adjustment. */ + dstrotated0.left -= dstinfo->xpixalign; + dstrotated0.right -= dstinfo->xpixalign; + + /* Determine the source origin. */ + switch (tmpinfo.angle) { + case ROT_ANGLE_0: + srcx = ((tmpinfo.rect.left + leftextra) << 16) + + (srcdelta.left & 0xFFFF); + srcy = (tmpinfo.rect.top << 16) + + (srcdelta.top & 0xFFFF); + break; + + case ROT_ANGLE_90: + srcx = (tmpinfo.rect.left << 16) + + (srcdelta.top & 0xFFFF); + srcy = ((tmpinfo.rect.top + rightextra) << 16) + + (srcdelta.left & 0xFFFF); + break; + + case ROT_ANGLE_180: + srcx = ((tmpinfo.rect.left + rightextra) << 16) + + (srcdelta.left & 0xFFFF); + srcy = (tmpinfo.rect.top << 16) + + (srcdelta.top & 0xFFFF); + break; + + case ROT_ANGLE_270: + srcx = (tmpinfo.rect.left << 16) + + (srcdelta.top & 0xFFFF); + srcy = ((tmpinfo.rect.top + leftextra) << 16) + + (srcdelta.left & 0xFFFF); + break; + } + + GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy); + + /* Load the horizontal filter. */ + bverror = load_filter(bvbltparams, batch, + GC_FILTER_SYNC, + gcfilter->horkernelsize, + gcfilter->horscalefactor, + srcwidth, dstwidth, + gcmofilterkernel_shared_ldst); + if (bverror != BVERR_NONE) + goto exit; + + /* Start the operation. */ + GCDBG(GCZONE_TYPE, "horizontal pass\n"); + bverror = startvr(bvbltparams, batch, + tmpmap, dstmap, &tmpinfo, dstinfo, + srcx, srcy, &dstrotated0, + ROT_ANGLE_0, ROT_ANGLE_0, + ((gcfilter->dstangle % 2) == 0) + ? GC_SCALE_HOR + : GC_SCALE_HOR_FLIPPED); + if (bverror != BVERR_NONE) + goto exit; + } else { + GCDBG(GCZONE_TYPE, "two pass (%s pass config).\n", + scalex ? "horizontal" : "vertical"); + + /* Setup single pass. */ + srcx = (srcrect->left << 16) + srcdelta.left; + srcy = (srcrect->top << 16) + srcdelta.top; + GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy); + + if (scalex) { + /* Load the horizontal filter. */ + bverror = load_filter(bvbltparams, batch, + GC_FILTER_SYNC, + gcfilter->horkernelsize, + gcfilter->horscalefactor, + srcwidth, dstwidth, + gcmofilterkernel_shared_ldst); + if (bverror != BVERR_NONE) + goto exit; + + /* Start the operation. */ + bverror = startvr(bvbltparams, batch, + srcmap, dstmap, srcinfo, dstinfo, + srcx, srcy, dstadjusted, + ROT_ANGLE_0, gcfilter->dstangle, + GC_SCALE_HOR); + if (bverror != BVERR_NONE) + goto exit; + } else { + /* Load the vertical filter. */ + bverror = load_filter(bvbltparams, batch, + GC_FILTER_SYNC, + gcfilter->verkernelsize, + gcfilter->verscalefactor, + srcheight, dstheight, + gcmofilterkernel_shared_ldst); + if (bverror != BVERR_NONE) + goto exit; + + /* Start the operation. */ + bverror = startvr(bvbltparams, batch, + srcmap, dstmap, srcinfo, dstinfo, + srcx, srcy, dstadjusted, + ROT_ANGLE_0, gcfilter->dstangle, + GC_SCALE_VER); + if (bverror != BVERR_NONE) + goto exit; + } + } + +exit: + GCEXITARG(GCZONE_FILTER, "bv%s = %d\n", + (bverror == BVERR_NONE) ? "result" : "error", bverror); + return bverror; +} |