/* (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä and Claudio Ciccani . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "nvidia.h" #include "nvidia_regs.h" #include "nvidia_accel.h" #include "nvidia_2d.h" static void nv_copy32( volatile u32 *dst, u8 *src, int n ) { u32 *D = (u32*) dst; u32 *S = (u32*) src; #ifdef ARCH_X86 __asm__ __volatile__( "rep; movsl" : "=&D" (D), "=&S" (S) : "c" (n), "0" (D), "1" (S) : "memory" ); #else do { *D++ = *S++; } while (--n); #endif } static void nv_copy16( volatile u32 *dst, u8 *src, int n ) { u32 *D = (u32*) dst; u16 *S = (u16*) src; #ifdef ARCH_X86 __asm__ __volatile__( "rep; movsl" : "=&D" (D), "=&S" (S) : "c" (n/2), "0" (D), "1" (S) : "memory" ); #else for (; n > 1; n -= 2) { *D++ = *((u32*)S); S += 2; } #endif if (n & 1) *D = *S; } static inline bool nv_clip_source( DFBRectangle *rect, u32 width, u32 height ) { if (rect->x >= width || rect->y >= height) return false; if (rect->x < 0) { rect->w += rect->x; rect->x = 0; } if (rect->y < 0) { rect->h += rect->y; rect->y = 0; } rect->w = MIN( rect->w, width - rect->x ); rect->h = MIN( rect->h, height - rect->y ); return (rect->w > 0 && rect->h > 0); } #define M_TRANSFORM(x, y, retx, rety, m) { \ s32 _x, _y; \ _x = ((s64)(x) * (m)[0] + (y) * (m)[1] + (m)[2] + 0x8000) >> 16; \ _y = ((s64)(x) * (m)[3] + (y) * (m)[4] + (m)[5] + 0x8000) >> 16; \ retx = _x; \ rety = _y; \ } bool nvFillRectangle2D( void *drv, void *dev, DFBRectangle *rect ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; if (nvdev->dst_422) { rect->x /= 2; rect->w = (rect->w+1) >> 1; } if (nvdev->matrix) { int x1 = rect->x, x2 = rect->x+rect->w; int y1 = rect->y, y2 = rect->y+rect->h; int x, y; nv_begin( SUBC_TRIANGLE, TRI_COLOR, 1 ); nv_outr( nvdev->color2d ); nv_begin( SUBC_TRIANGLE, TRI_POINT0, 3 ); M_TRANSFORM( x1, y1, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x2, y1, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x1, y2, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); nv_begin( SUBC_TRIANGLE, TRI_POINT0, 3 ); M_TRANSFORM( x2, y1, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x2, y2, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x1, y2, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); } else { nv_begin( SUBC_RECTANGLE, RECT_COLOR, 1 ); nv_outr( nvdev->color2d ); nv_begin( SUBC_RECTANGLE, RECT_TOP_LEFT, 2 ); nv_outr( (rect->y << 16) | (rect->x & 0xFFFF) ); nv_outr( (rect->h << 16) | (rect->w & 0xFFFF) ); } return true; } bool nvFillTriangle2D( void *drv, void *dev, DFBTriangle *tri ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; if (nvdev->matrix) { M_TRANSFORM( tri->x1, tri->y1, tri->x1, tri->y1, nvdev->matrix ); M_TRANSFORM( tri->x2, tri->y3, tri->x2, tri->y2, nvdev->matrix ); M_TRANSFORM( tri->x3, tri->y3, tri->x3, tri->y3, nvdev->matrix ); } nv_begin( SUBC_TRIANGLE, TRI_COLOR, 1 ); nv_outr( nvdev->color2d ); nv_begin( SUBC_TRIANGLE, TRI_POINT0, 3 ); nv_outr( (tri->y1 << 16) | (tri->x1 & 0xFFFF) ); nv_outr( (tri->y2 << 16) | (tri->x2 & 0xFFFF) ); nv_outr( (tri->y3 << 16) | (tri->x3 & 0xFFFF) ); return true; } bool nvDrawRectangle2D( void *drv, void *dev, DFBRectangle *rect ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; if (nvdev->dst_422) { rect->x /= 2; rect->w = (rect->w+1) >> 1; } if (nvdev->matrix) { int x1 = rect->x, x2 = rect->x+rect->w; int y1 = rect->y, y2 = rect->y+rect->h; int x, y; nv_begin( SUBC_LINE, LINE_COLOR, 1 ); nv_outr( nvdev->color2d ); nv_begin( SUBC_LINE, LINE_POINT0, 8 ); /* top */ M_TRANSFORM( x1, y1, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x2, y1, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); /* right */ nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x2, y2, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); /* bottom */ nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x1, y2, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); /* left */ nv_outr( (y << 16) | (x & 0xFFFF) ); M_TRANSFORM( x1, y1, x, y, nvdev->matrix ); nv_outr( (y << 16) | (x & 0xFFFF) ); } else { nv_begin( SUBC_RECTANGLE, RECT_COLOR, 1 ); nv_outr( nvdev->color2d ); nv_begin( SUBC_RECTANGLE, RECT_TOP_LEFT, 8 ); /* top */ nv_outr( (rect->y << 16) | (rect->x & 0xFFFF) ); nv_outr( (1 << 16) | (rect->w & 0xFFFF) ); /* bottom */ nv_outr( ((rect->y + rect->h - 1) << 16) | (rect->x & 0xFFFF) ); nv_outr( (1 << 16) | (rect->w & 0xFFFF) ); /* left */ nv_outr( ((rect->y + 1) << 16) | (rect->x & 0xFFFF) ); nv_outr( ((rect->h - 2) << 16) | 1 ); /* right */ nv_outr( ((rect->y + 1) << 16) | ((rect->x + rect->w - 1) & 0xFFFF) ); nv_outr( ((rect->h - 2) << 16) | 1 ); } return true; } bool nvDrawLine2D( void *drv, void *dev, DFBRegion *line ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; if (nvdev->matrix) { M_TRANSFORM( line->x1, line->y1, line->x1, line->y1, nvdev->matrix ); M_TRANSFORM( line->x2, line->y2, line->x2, line->y2, nvdev->matrix ); } nv_begin( SUBC_LINE, LINE_COLOR, 1 ); nv_outr( nvdev->color2d ); nv_begin( SUBC_LINE, LINE_POINT0, 2 ); nv_outr( (line->y1 << 16) | (line->x1 & 0xFFFF) ); nv_outr( (line->y2 << 16) | (line->x2 & 0xFFFF) ); return true; } bool nvBlit( void *drv, void *dev, DFBRectangle *rect, int dx, int dy ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; if (nvdev->blittingflags & DSBLIT_DEINTERLACE || nvdev->matrix) { DFBRectangle dr = { dx, dy, rect->w, rect->h }; return nvStretchBlit( drv, dev, rect, &dr ); } if (nvdev->dst_422) { dx /= 2; rect->x /= 2; rect->w = (rect->w+1) >> 1; } if (nvdev->blittingflags || nvdev->src_format != nvdev->dst_format) { DFBRectangle *clip = &nvdev->clip; u32 src_width = (nvdev->src_width + 1) & ~1; u32 src_height = (nvdev->src_height + 1) & ~1; u32 filter = 0; if (nvdev->dst_422) src_width >>= 1; if (nvdev->arch > NV_ARCH_04) filter = SCALER_IN_FORMAT_ORIGIN_CORNER | SCALER_IN_FORMAT_FILTER_NEAREST; nv_begin( SUBC_SCALEDIMAGE, SCALER_COLOR_FORMAT, 1 ); nv_outr( nvdev->scaler_format ); nv_begin( SUBC_SCALEDIMAGE, SCALER_CLIP_POINT, 6 ); nv_outr( (clip->y << 16) | (clip->x & 0xFFFF) ); nv_outr( (clip->h << 16) | (clip->w & 0xFFFF) ); nv_outr( (dy << 16) | (dx & 0xFFFF) ); nv_outr( (rect->h << 16) | (rect->w & 0xFFFF) ); nv_outr( 0x100000 ); nv_outr( 0x100000 ); nv_begin( SUBC_SCALEDIMAGE, SCALER_IN_SIZE, 4 ); nv_outr( (src_height << 16) | (src_width & 0xFFFF) ); nv_outr( (nvdev->src_pitch & 0xFFFF) | filter ); nv_outr( nvdev->src_offset ); nv_outr( (rect->y << 20) | ((rect->x<<4) & 0xFFFF) ); } else { nv_begin( SUBC_SCREENBLT, BLIT_TOP_LEFT_SRC, 3 ); nv_outr( (rect->y << 16) | (rect->x & 0xFFFF) ); nv_outr( (dy << 16) | (dx & 0xFFFF) ); nv_outr( (rect->h << 16) | (rect->w & 0xFFFF) ); } return true; } bool nvBlitFromCPU( void *drv, void *dev, DFBRectangle *rect, int dx, int dy ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; u8 *src = nvdev->src_address; u32 src_w; u32 src_h; int w, h, n; if (nvdev->blittingflags & DSBLIT_DEINTERLACE || nvdev->matrix) { DFBRectangle dr = { dx, dy, rect->x, rect->y }; return nvStretchBlitFromCPU( drv, dev, rect, &dr ); } if (!nv_clip_source( rect, nvdev->src_width, nvdev->src_height )) return true; src_w = (DFB_BYTES_PER_PIXEL(nvdev->src_format) == 2) ? ((rect->w + 1) & ~1) : rect->w; src_h = rect->h; nv_begin( SUBC_IMAGEBLT, IBLIT_COLOR_FORMAT, 1 ); nv_outr( nvdev->system_format ); nv_begin( SUBC_IMAGEBLT, IBLIT_POINT, 3 ); nv_outr( (dy << 16) | (dx & 0xFFFF) ); nv_outr( (rect->h << 16) | (rect->w & 0xFFFF) ); nv_outr( (src_h << 16) | (src_w & 0xFFFF) ); n = nvdev->use_dma ? 256 : 128; switch (nvdev->src_format) { case DSPF_ARGB1555: case DSPF_RGB16: src += rect->y * nvdev->src_pitch + rect->x * 2; for (h = rect->h; h--;) { u8 *s = src; for (w = rect->w; w >= n*2; w -= n*2) { nv_begin( SUBC_IMAGEBLT, IBLIT_PIXEL0, n ); direct_memcpy( (void*)nvdev->cmd_ptr, s, n*4 ); s += n*4; } if (w > 0) { nv_begin( SUBC_IMAGEBLT, IBLIT_PIXEL0, (w+1)>>1 ); nv_copy16( nvdev->cmd_ptr, s, w ); } src += nvdev->src_pitch; } break; default: src += rect->y * nvdev->src_pitch + rect->x * 4; for (h = rect->h; h--;) { u8 *s = src; for (w = rect->w; w >= n; w -= n) { nv_begin( SUBC_IMAGEBLT, IBLIT_PIXEL0, n ); direct_memcpy( (void*)nvdev->cmd_ptr, s, n*4 ); s += n*4; } if (w > 0) { nv_begin( SUBC_IMAGEBLT, IBLIT_PIXEL0, w ); nv_copy32( nvdev->cmd_ptr, s, w ); } src += nvdev->src_pitch; } break; } return true; } bool nvStretchBlit( void *drv, void *dev, DFBRectangle *sr, DFBRectangle *dr ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; DFBRectangle *cr = &nvdev->clip; u32 src_width = (nvdev->src_width + 1) & ~1; u32 src_height = (nvdev->src_height + 1) & ~1; if (nvdev->dst_422) { sr->x /= 2; sr->w = (sr->w+1) >> 1; dr->x /= 2; dr->w = (dr->w+1) >> 1; src_width >>= 1; } if (nvdev->blittingflags & DSBLIT_DEINTERLACE) { sr->y /= 2; sr->h = (sr->h+1) / 2; } if (nvdev->matrix) { int x1, y1, x2, y2; if (!nvdev->matrix[0] || !nvdev->matrix[4]) return true; M_TRANSFORM( dr->x, dr->y, x1, y1, nvdev->matrix ); M_TRANSFORM( dr->x+dr->w, dr->y+dr->h, x2, y2, nvdev->matrix ); dr->x = x1; dr->w = x2-x1; dr->y = y1; dr->h = y2-y1; } nv_begin( SUBC_SCALEDIMAGE, SCALER_COLOR_FORMAT, 1 ); nv_outr( nvdev->scaler_format ); nv_begin( SUBC_SCALEDIMAGE, SCALER_CLIP_POINT, 6 ); nv_outr( (cr->y << 16) | (cr->x & 0xFFFF) ); nv_outr( (cr->h << 16) | (cr->w & 0xFFFF) ); nv_outr( (dr->y << 16) | (dr->x & 0xFFFF) ); nv_outr( (dr->h << 16) | (dr->w & 0xFFFF) ); nv_outr( (sr->w << 20) / dr->w ); nv_outr( (sr->h << 20) / dr->h ); nv_begin( SUBC_SCALEDIMAGE, SCALER_IN_SIZE, 4 ); nv_outr( (src_height << 16) | (src_width & 0xFFFF) ); nv_outr( (nvdev->src_pitch & 0xFFFF) | nvdev->scaler_filter ); nv_outr( nvdev->src_offset ); nv_outr( (sr->y << 20) | ((sr->x << 4) & 0xFFFF) ); return true; } bool nvStretchBlitFromCPU( void *drv, void *dev, DFBRectangle *sr, DFBRectangle *dr ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; DFBRectangle *cr = &nvdev->clip; u8 *src = nvdev->src_address; u32 src_w; u32 src_h; int w, h, n; if (!nv_clip_source( sr, nvdev->src_width, nvdev->src_height )) return true; if (nvdev->blittingflags & DSBLIT_DEINTERLACE) { sr->y /= 2; sr->h /= 2; } if (nvdev->matrix) { int x1, y1, x2, y2; if (!nvdev->matrix[0] || !nvdev->matrix[4]) return true; M_TRANSFORM( dr->x, dr->y, x1, y1, nvdev->matrix ); M_TRANSFORM( dr->x+dr->w, dr->y+dr->h, x2, y2, nvdev->matrix ); dr->x = x1; dr->w = x2-x1; dr->y = y1; dr->h = y2-y1; } src_w = (DFB_BYTES_PER_PIXEL(nvdev->src_format) == 2) ? ((sr->w + 1) & ~1) : sr->w; src_h = sr->h; nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_COLOR_FORMAT, 1 ); nv_outr( nvdev->system_format ); nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_IN_SIZE, 6 ); nv_outr( (src_h << 16) | (src_w & 0xFFFF) ); nv_outr( (dr->w << 20) / src_w ); nv_outr( (dr->h << 20) / src_h ); nv_outr( (cr->y << 16) | (cr->x & 0xFFFF) ); nv_outr( (cr->h << 16) | (cr->w & 0xFFFF) ); nv_outr( (dr->y << 20) | ((dr->x<<4) & 0xFFFF) ); n = nvdev->use_dma ? 256 : 128; switch (nvdev->src_format) { case DSPF_ARGB1555: case DSPF_RGB16: src += sr->y * nvdev->src_pitch + sr->x * 2; for (h = sr->h; h--;) { u8 *s = src; for (w = sr->w; w >= n*2; w -= n*2) { nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_PIXEL0, n ); direct_memcpy( (void*)nvdev->cmd_ptr, s, n*4 ); s += n*4; } if (w > 0) { nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_PIXEL0, (w+1)>>1 ); nv_copy16( nvdev->cmd_ptr, s, w ); } src += nvdev->src_pitch; } break; default: src += sr->y * nvdev->src_pitch + sr->x * 4; for (h = sr->h; h--;) { u8 *s= src; for (w = sr->w; w >= n; w -= n) { nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_PIXEL0, n ); direct_memcpy( (void*)nvdev->cmd_ptr, s, n*4 ); s += n*4; } if (w > 0) { nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_PIXEL0, w ); nv_copy32( nvdev->cmd_ptr, s, w ); } src += nvdev->src_pitch; } break; } return true; }