/* * Copyright (C) 2006 Claudio Ciccani * * Graphics driver for ATI Radeon cards written by * 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 #include #include #include "radeon.h" #include "radeon_regs.h" #include "radeon_mmio.h" #include "radeon_state.h" #include "radeon_3d.h" #define EMIT_VERTICES( rdrv, rdev, mmio ) { \ u32 *_v = (rdev)->vb; \ u32 _s = (rdev)->vb_size; \ radeon_waitfifo( rdrv, rdev, 1 ); \ radeon_out32( mmio, SE_VF_CNTL, rdev->vb_type | VF_PRIM_WALK_DATA | \ (rdev->vb_count << VF_NUM_VERTICES_SHIFT) ); \ do { \ u32 _n = MIN(_s, 64); \ _s -= _n; \ radeon_waitfifo( rdrv, rdev, _n ); \ while (_n--) \ radeon_out32( mmio, SE_PORT_DATA0, *_v++ ); \ } while (_s); \ radeon_waitfifo( rdrv, rdev, 2 ); \ radeon_out32( mmio, R300_RB3D_DSTCACHE_CTLSTAT, 0xa ); \ radeon_out32( mmio, 0x4f18, 0x3 ); \ } static void r300_flush_vb( RadeonDriverData *rdrv, RadeonDeviceData *rdev ) { volatile u8 *mmio = rdrv->mmio_base; EMIT_VERTICES( rdrv, rdev, mmio ); if (DFB_PLANAR_PIXELFORMAT(rdev->dst_format)) { DFBRegion clip; int i; for (i = 0; i < rdev->vb_size; i += 8) { rdev->vb[i+0] = f2d(d2f(rdev->vb[i+0])*0.5f); rdev->vb[i+1] = f2d(d2f(rdev->vb[i+1])*0.5f); } clip.x1 = rdev->clip.x1 >> 1; clip.y1 = rdev->clip.y1 >> 1; clip.x2 = rdev->clip.x2 >> 1; clip.y2 = rdev->clip.y2 >> 1; /* Prepare Cb plane */ radeon_waitfifo( rdrv, rdev, 5 ); radeon_out32( mmio, R300_RB3D_COLOROFFSET0, rdev->dst_offset_cb ); radeon_out32( mmio, R300_RB3D_COLORPITCH0, (rdev->dst_pitch>>1) | R300_COLOR_FORMAT_RGB8 ); radeon_out32( mmio, R300_TX_SIZE_0, ((rdev->src_width/2 -1) << R300_TX_WIDTH_SHIFT) | ((rdev->src_height/2-1) << R300_TX_HEIGHT_SHIFT) | R300_TX_SIZE_TXPITCH_EN ); radeon_out32( mmio, R300_TX_PITCH_0, (rdev->src_pitch>>1) - 8 ); radeon_out32( mmio, R300_TX_OFFSET_0, rdev->src_offset_cb ); r300_set_clip3d( rdrv, rdev, &clip ); /* Fill Cb plane */ EMIT_VERTICES( rdrv, rdev, mmio ); /* Prepare Cr plane */ radeon_waitfifo( rdrv, rdev, 2 ); radeon_out32( mmio, R300_RB3D_COLOROFFSET0, rdev->dst_offset_cr ); radeon_out32( mmio, R300_TX_OFFSET_0, rdev->src_offset_cr ); /* Fill Cr plane */ EMIT_VERTICES( rdrv, rdev, mmio ); /* Reset */ radeon_waitfifo( rdrv, rdev, 5 ); radeon_out32( mmio, R300_RB3D_COLOROFFSET0, rdev->dst_offset ); radeon_out32( mmio, R300_RB3D_COLORPITCH0, rdev->dst_pitch | R300_COLOR_FORMAT_RGB8 ); radeon_out32( mmio, R300_TX_SIZE_0, ((rdev->src_width -1) << R300_TX_WIDTH_SHIFT) | ((rdev->src_height-1) << R300_TX_HEIGHT_SHIFT) | R300_TX_SIZE_TXPITCH_EN ); radeon_out32( mmio, R300_TX_PITCH_0, rdev->src_pitch - 8 ); radeon_out32( mmio, R300_TX_OFFSET_0, rdev->src_offset ); r300_set_clip3d( rdrv, rdev, &rdev->clip ); } rdev->vb_size = 0; rdev->vb_count = 0; } static inline u32* r300_init_vb( RadeonDriverData *rdrv, RadeonDeviceData *rdev, u32 type, u32 count, u32 size ) { u32 *vb; if ((rdev->vb_size && rdev->vb_type != type) || rdev->vb_size+size > D_ARRAY_SIZE(rdev->vb)) r300_flush_vb( rdrv, rdev ); vb = &rdev->vb[rdev->vb_size]; rdev->vb_type = type; rdev->vb_size += size; rdev->vb_count += count; return vb; } #define VTX(v, x, y, c) \ *(v)++ = f2d(x); \ *(v)++ = f2d(y); \ *(v)++ = f2d(0); \ *(v)++ = f2d(1); \ *(v)++ = f2d(c[0]); \ *(v)++ = f2d(c[1]); \ *(v)++ = f2d(c[2]); \ *(v)++ = f2d(c[3]) bool r300FillRectangle3D( void *drv, void *dev, DFBRectangle *rect ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; float x1, y1; float x2, y2; u32 *v; if (rect->w == 1 && rect->h == 1) { x1 = rect->x+1; y1 = rect->y+1; if (rdev->matrix) RADEON_TRANSFORM( x1, y1, x1, y1, rdev->matrix, rdev->affine_matrix ); v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_POINT_LIST, 1, 8 ); VTX( v, x1, y1, rdev->color ); return true; } x1 = rect->x; y1 = rect->y; x2 = rect->x+rect->w; y2 = rect->y+rect->h; if (rdev->matrix) { float x, y; v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_QUAD_LIST, 4, 32 ); RADEON_TRANSFORM( x1, y1, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); RADEON_TRANSFORM( x2, y1, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); RADEON_TRANSFORM( x2, y2, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); RADEON_TRANSFORM( x1, y2, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); } else { v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_QUAD_LIST, 4, 32 ); VTX( v, x1, y1, rdev->color ); VTX( v, x2, y1, rdev->color ); VTX( v, x2, y2, rdev->color ); VTX( v, x1, y2, rdev->color ); } return true; } bool r300FillTriangle( void *drv, void *dev, DFBTriangle *tri ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; float x1, y1; float x2, y2; float x3, y3; u32 *v; x1 = tri->x1; y1 = tri->y1; x2 = tri->x2; y2 = tri->y2; x3 = tri->x3; y3 = tri->y3; if (rdev->matrix) { RADEON_TRANSFORM( x1, y1, x1, y1, rdev->matrix, rdev->affine_matrix ); RADEON_TRANSFORM( x2, y2, x2, y2, rdev->matrix, rdev->affine_matrix ); RADEON_TRANSFORM( x3, y3, x3, y3, rdev->matrix, rdev->affine_matrix ); } v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_TRIANGLE_LIST, 3, 24 ); VTX( v, x1, y1, rdev->color ); VTX( v, x2, y2, rdev->color ); VTX( v, x3, y3, rdev->color ); return true; } bool r300DrawRectangle3D( void *drv, void *dev, DFBRectangle *rect ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; float x1, y1; float x2, y2; u32 *v; x1 = rect->x; y1 = rect->y; x2 = rect->x+rect->w; y2 = rect->y+rect->h; if (rdev->matrix) { float x, y; v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_LINE_LOOP, 4, 32 ); RADEON_TRANSFORM( x1, y1, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); RADEON_TRANSFORM( x2, y1, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); RADEON_TRANSFORM( x2, y2, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); RADEON_TRANSFORM( x1, y2, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, rdev->color ); } else { v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_LINE_LOOP, 4, 32 ); VTX( v, x1, y1, rdev->color ); VTX( v, x2, y1, rdev->color ); VTX( v, x2, y2, rdev->color ); VTX( v, x1, y2, rdev->color ); } return true; } bool r300DrawLine3D( void *drv, void *dev, DFBRegion *line ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; float x1, y1; float x2, y2; u32 *v; x1 = line->x1; y1 = line->y1; x2 = line->x2; y2 = line->y2; if (rdev->matrix) { RADEON_TRANSFORM( x1, y1, x1, y1, rdev->matrix, rdev->affine_matrix ); RADEON_TRANSFORM( x2, y2, x2, y2, rdev->matrix, rdev->affine_matrix ); } v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_LINE_LIST, 2, 16 ); VTX( v, x1, y1, rdev->color ); VTX( v, x2, y2, rdev->color ); return true; } #undef VTX #define VTX( v, x, y, s, t ) \ *(v)++ = f2d(x); \ *(v)++ = f2d(y); \ *(v)++ = f2d(0); \ *(v)++ = f2d(1); \ *(v)++ = f2d(s); \ *(v)++ = f2d(t); \ *(v)++ = f2d(0); \ *(v)++ = f2d(1) bool r300Blit3D( void *drv, void *dev, DFBRectangle *sr, int dx, int dy ) { DFBRectangle dr = { dx, dy, sr->w, sr->h }; return r300StretchBlit( drv, dev, sr, &dr ); } bool r300StretchBlit( void *drv, void *dev, DFBRectangle *sr, DFBRectangle *dr ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; float x1, y1; float x2, y2; float s1, t1; float s2, t2; u32 *v; if (rdev->blittingflags & DSBLIT_DEINTERLACE) { sr->y /= 2; sr->h /= 2; } s1 = (float)sr->x / rdev->src_width; t1 = (float)sr->y / rdev->src_height; s2 = (float)(sr->x+sr->w) / rdev->src_width; t2 = (float)(sr->y+sr->h) / rdev->src_height; if (rdev->blittingflags & DSBLIT_ROTATE180) { float tmp; tmp = s2; s2 = s1; s1 = tmp; tmp = t2; t2 = t1; t1 = tmp; } x1 = dr->x; y1 = dr->y; x2 = dr->x+dr->w; y2 = dr->y+dr->h; if (rdev->matrix) { float x, y; v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_QUAD_LIST, 4, 32 ); RADEON_TRANSFORM( x1, y1, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, s1, t1 ); RADEON_TRANSFORM( x2, y1, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, s2, t1 ); RADEON_TRANSFORM( x2, y2, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, s2, t2 ); RADEON_TRANSFORM( x1, y2, x, y, rdev->matrix, rdev->affine_matrix ); VTX( v, x, y, s1, t2 ); } else { v = r300_init_vb( rdrv, rdev, VF_PRIM_TYPE_QUAD_LIST, 4, 32 ); VTX( v, x1, y1, s1, t1 ); VTX( v, x2, y1, s2, t1 ); VTX( v, x2, y2, s2, t2 ); VTX( v, x1, y2, s1, t2 ); } return true; } static void r300DoTextureTriangles( RadeonDriverData *rdrv, RadeonDeviceData *rdev, DFBVertex *ve, int num, u32 primitive ) { volatile u8 *mmio = rdrv->mmio_base; int i; radeon_waitfifo( rdrv, rdev, 1 ); radeon_out32( mmio, SE_VF_CNTL, primitive | VF_PRIM_WALK_DATA | (num << VF_NUM_VERTICES_SHIFT) ); for (; num >= 8; num -= 8) { radeon_waitfifo( rdrv, rdev, 64 ); for (i = 0; i < 8; i++) { radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].x) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].y) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].z) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(1) ); // FIXME radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].s) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].t) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(0) ); // r radeon_out32( mmio, SE_PORT_DATA0, f2d(1) ); // q } ve += 8; } if (num > 0) { radeon_waitfifo( rdrv, rdev, num*8 ); for (i = 0; i < num; i++) { radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].x) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].y) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].z) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(1) ); // FIXME radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].s) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(ve[i].t) ); radeon_out32( mmio, SE_PORT_DATA0, f2d(0) ); // r radeon_out32( mmio, SE_PORT_DATA0, f2d(1) ); // q } } radeon_waitfifo( rdrv, rdev, 2 ); radeon_out32( mmio, R300_RB3D_DSTCACHE_CTLSTAT, 0xa ); radeon_out32( mmio, 0x4f18, 0x3 ); } bool r300TextureTriangles( void *drv, void *dev, DFBVertex *ve, int num, DFBTriangleFormation formation ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; u32 prim = 0; int i; if (num > 65535) { D_WARN( "R300 supports maximum 65535 vertices" ); return false; } switch (formation) { case DTTF_LIST: prim = VF_PRIM_TYPE_TRIANGLE_LIST; break; case DTTF_STRIP: prim = VF_PRIM_TYPE_TRIANGLE_STRIP; break; case DTTF_FAN: prim = VF_PRIM_TYPE_TRIANGLE_FAN; break; default: D_BUG( "unexpected triangle formation" ); return false; } if (rdev->matrix) { for (i = 0; i < num; i++) RADEON_TRANSFORM( ve[i].x, ve[i].y, ve[i].x, ve[i].y, rdev->matrix, rdev->affine_matrix ); } r300DoTextureTriangles( rdrv, rdev, ve, num, prim ); if (DFB_PLANAR_PIXELFORMAT(rdev->dst_format)) { volatile u8 *mmio = rdrv->mmio_base; DFBRegion clip; int i; /* Scale coordinates */ for (i = 0; i < num; i++) { ve[i].x *= 0.5; ve[i].y *= 0.5; } clip.x1 = rdev->clip.x1 >> 1; clip.y1 = rdev->clip.y1 >> 1; clip.x2 = rdev->clip.x2 >> 1; clip.y2 = rdev->clip.y2 >> 1; /* Prepare Cb plane */ radeon_waitfifo( rdrv, rdev, 5 ); radeon_out32( mmio, R300_RB3D_COLOROFFSET0, rdev->dst_offset_cb ); radeon_out32( mmio, R300_RB3D_COLORPITCH0, (rdev->dst_pitch>>1) | R300_COLOR_FORMAT_RGB8 ); radeon_out32( mmio, R300_TX_SIZE_0, ((rdev->src_width/2 -1) << R300_TX_WIDTH_SHIFT) | ((rdev->src_height/2-1) << R300_TX_HEIGHT_SHIFT) | R300_TX_SIZE_TXPITCH_EN ); radeon_out32( mmio, R300_TX_PITCH_0, (rdev->src_pitch>>1) - 8 ); radeon_out32( mmio, R300_TX_OFFSET_0, rdev->src_offset_cb ); r300_set_clip3d( rdrv, rdev, &clip ); /* Blit Cb plane */ r300DoTextureTriangles( rdrv, rdev, ve, num, prim ); /* Prepare Cr plane */ radeon_waitfifo( rdrv, rdev, 2 ); radeon_out32( mmio, R300_RB3D_COLOROFFSET0, rdev->dst_offset_cr ); radeon_out32( mmio, R300_TX_OFFSET_0, rdev->src_offset_cr ); /* Blit Cr plane */ r300DoTextureTriangles( rdrv, rdev, ve, num, prim ); /* Reset */ radeon_waitfifo( rdrv, rdev, 5 ); radeon_out32( mmio, R300_RB3D_COLOROFFSET0, rdev->dst_offset ); radeon_out32( mmio, R300_RB3D_COLORPITCH0, rdev->dst_pitch | R300_COLOR_FORMAT_RGB8 ); radeon_out32( mmio, R300_TX_SIZE_0, ((rdev->src_width -1) << R300_TX_WIDTH_SHIFT) | ((rdev->src_height-1) << R300_TX_HEIGHT_SHIFT) | R300_TX_SIZE_TXPITCH_EN ); radeon_out32( mmio, R300_TX_PITCH_0, rdev->src_pitch - 8 ); radeon_out32( mmio, R300_TX_OFFSET_0, rdev->src_offset ); r300_set_clip3d( rdrv, rdev, &rdev->clip ); } return true; } void r300EmitCommands3D( void *drv, void *dev ) { RadeonDriverData *rdrv = (RadeonDriverData*) drv; RadeonDeviceData *rdev = (RadeonDeviceData*) dev; if (rdev->vb_count) r300_flush_vb( rdrv, rdev ); }