From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/systems/fbdev/agp.c | 511 ++++++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100755 Source/DirectFB/systems/fbdev/agp.c (limited to 'Source/DirectFB/systems/fbdev/agp.c') diff --git a/Source/DirectFB/systems/fbdev/agp.c b/Source/DirectFB/systems/fbdev/agp.c new file mode 100755 index 0000000..286d4db --- /dev/null +++ b/Source/DirectFB/systems/fbdev/agp.c @@ -0,0 +1,511 @@ +/* + (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 /* Needs to be included before dfb_types.h */ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "fbdev.h" +#include "agp.h" + +#define PAGE_SIZE direct_pagesize() + +/*****************************************************************************/ + +extern FBDev *dfb_fbdev; + +static AGPDevice *dfb_agp = NULL; + +/*****************************************************************************/ + +static DFBResult +dfb_agp_info( agp_info *info ) +{ + D_ASSERT( info != NULL ); + + if (ioctl( dfb_agp->fd, AGPIOC_INFO, info )) { + D_PERROR( "DirectFB/FBDev/agp: Could not get AGP info!\n" ); + return errno2result( errno ); + } + + return DFB_OK; +} + +static DFBResult +dfb_agp_setup( u32 mode ) +{ + agp_setup setup; + + setup.agp_mode = mode; + + if (ioctl( dfb_agp->fd, AGPIOC_SETUP, &setup )) { + D_PERROR( "DirectFB/FBDev/agp: AGP setup failed!\n" ); + return errno2result( errno ); + } + + return DFB_OK; +} + +static DFBResult +dfb_agp_acquire( void ) +{ + if (ioctl( dfb_agp->fd, AGPIOC_ACQUIRE, 0 )) { + D_PERROR( "DirectFB/FBDev/agp: Acquire failed!\n" ); + return errno2result( errno ); + } + + return DFB_OK; +} + +static DFBResult +dfb_agp_release( void ) +{ + if (ioctl( dfb_agp->fd, AGPIOC_RELEASE, 0 )) { + D_PERROR( "DirectFB/FBDev/agp: Release failed!\n" ); + return errno2result( errno ); + } + + return DFB_OK; +} + +static DFBResult +dfb_agp_allocate( unsigned long size, int *key ) +{ + agp_allocate alloc; + int pages; + + D_ASSERT( key != NULL ); + + pages = size / PAGE_SIZE; + if (pages % PAGE_SIZE) + pages++; + + if (pages == 0) { + D_BUG( "attempted to allocate 0 pages!"); + return DFB_BUG; + } + + alloc.pg_count = pages; + alloc.type = 0; + + if (ioctl( dfb_agp->fd, AGPIOC_ALLOCATE, &alloc )) { + D_PERROR( "DirectFB/FBDev/agp: " + "Could not allocate %d pages!\n", pages ); + return errno2result( errno ); + } + + *key = alloc.key; + + return DFB_OK; +} + +static DFBResult +dfb_agp_deallocate( int key ) +{ + if (ioctl( dfb_agp->fd, AGPIOC_DEALLOCATE, key )) { + D_PERROR( "DirectFB/FBDev/agp: " + "Deallocate failed (key = %d)!\n", key ); + return errno2result( errno ); + } + + return DFB_OK; +} + +static DFBResult +dfb_agp_bind( unsigned int offset, int key ) +{ + agp_bind bind; + + if (offset % PAGE_SIZE) { + D_BUG( "offset is not page-aligned!" ); + return DFB_BUG; + } + + bind.pg_start = offset / PAGE_SIZE; + bind.key = key; + + if (ioctl( dfb_agp->fd, AGPIOC_BIND, &bind )) { + D_PERROR( "DirectFB/FBDev/agp: " + "Bind failed (key = %d, offset = 0x%x)!\n", + key, offset ); + return errno2result( errno ); + } + + return DFB_OK; +} + +static DFBResult +dfb_agp_unbind( int key ) +{ + agp_unbind unbind; + + unbind.priority = 0; + unbind.key = key; + + if (ioctl( dfb_agp->fd, AGPIOC_UNBIND, &unbind )) { + D_PERROR( "DirectFB/FBDev/agp: " + "Unbind failed (key = %d)!\n", + key ); + return errno2result( errno ); + } + + return DFB_OK; +} + +/*****************************************************************************/ + +static inline u16 +pci_read_word( int fd, int pos ) +{ + u8 b[2]; + + if (pread( fd, b, 2, pos ) < 2) + return 0; + + return b[0] | (b[1] << 8); +} + +static inline u8 +pci_read_byte( int fd, int pos ) +{ + u8 b; + + if (pread( fd, &b, 1, pos ) < 1) + return 0; + + return b; +} + +#define PCI_STATUS 0x06 +#define PCI_STATUS_CAP_LIST 0x10 +#define PCI_CAPABILITY_LIST 0x34 +#define PCI_CAP_ID_AGP 0x02 + +static bool +dfb_agp_capable( int bus, int dev, int func ) +{ + bool found = false; + char path[22]; + int fd; + + /* XXX: the following detection method requires suid root */ + + snprintf( path, sizeof(path), + "/proc/bus/pci/%02x/%02x.%01x", bus, dev, func ); + + fd = open( path, O_RDONLY | O_SYNC ); + if (fd < 0) { + D_PERROR( "DirectFB/FBDev/agp: " + "Couldn't open '%s'!\n", path ); + return false; + } + + /* stolen from linux/drivers/pci/pci.c */ + if (pci_read_word( fd, PCI_STATUS ) & PCI_STATUS_CAP_LIST) { + int pos, id; + int ttl = 48; + + pos = pci_read_byte( fd, PCI_CAPABILITY_LIST ); + while (ttl-- && pos >= 0x40) { + pos &= ~3; + + id = pci_read_byte( fd, pos ); + if (id == 0xff) + break; + if (id == PCI_CAP_ID_AGP) { + found = true; + break; + } + + pos = pci_read_byte( fd, pos+1 ); + } + } + + close( fd ); + + return found; +} + +/*****************************************************************************/ + +DFBResult +dfb_agp_initialize( void ) +{ + AGPShared *shared; + unsigned int agp_avail; + DFBResult ret = DFB_FAILURE; + + if (dfb_agp) { + D_BUG( "dfb_agp_initialize() already called!" ); + return DFB_BUG; + } + + /* Precheck for AGP capable device. */ + if (!dfb_agp_capable( dfb_fbdev->shared->pci.bus, + dfb_fbdev->shared->pci.dev, + dfb_fbdev->shared->pci.func )) + return DFB_UNSUPPORTED; + + dfb_agp = D_CALLOC( 1, sizeof(AGPDevice) ); + if (!dfb_agp) + return D_OOM(); + + shared = SHCALLOC( dfb_fbdev->shared->shmpool, 1, sizeof(AGPShared) ); + if (!shared) { + D_ERROR( "DirectFB/FBDev/agp: Could not allocate shared memory!\n" ); + ret = DFB_NOSHAREDMEMORY; + goto error0; + } + + dfb_agp->fd = direct_try_open( "/dev/agpgart", + "/dev/misc/agpgart", O_RDWR, true ); + if (dfb_agp->fd < 0) { + ret = errno2result( errno ); + D_ERROR( "DirectFB/FBDev/agp: Error opening AGP device!\n" ); + goto error1; + } + + ret = dfb_agp_acquire(); + if (ret) + goto error2; + + ret = dfb_agp_info( &shared->info ); + if (ret) + goto error2; + + D_DEBUG( "DirectFB/FBDev/agp: " + "Bridge supports: AGP%s%s%s%s%s%s\n", + shared->info.agp_mode & 0x001 ? " 1X" : "", + shared->info.agp_mode & 0x002 ? " 2X" : "", + shared->info.agp_mode & 0x004 ? " 4X" : "", + shared->info.agp_mode & 0x008 ? " 8X" : "", + shared->info.agp_mode & 0x200 ? ", SBA" : "", + shared->info.agp_mode & 0x010 ? ", FW" : "" ); + + shared->info.agp_mode &= ~0xf; + shared->info.agp_mode |= dfb_config->agp; + shared->info.agp_mode |= dfb_config->agp - 1; + + ret = dfb_agp_setup( shared->info.agp_mode ); + if (ret) + goto error2; + dfb_agp_info( &shared->info ); + + D_DEBUG( "DirectFB/FBDev/agp: " + "AGP aperture at 0x%x (%zu MB)\n", + (unsigned int)shared->info.aper_base, shared->info.aper_size ); + + agp_avail = (shared->info.pg_total - shared->info.pg_used) * PAGE_SIZE; + if (agp_avail == 0) { + D_ERROR( "DirectFB/FBDev/agp: No AGP memory available!\n" ); + ret = DFB_INIT; + goto error2; + } + + shared->agp_mem = shared->info.aper_size << 20; + if (shared->agp_mem > agp_avail) + shared->agp_mem = agp_avail; + + ret = dfb_agp_allocate( shared->agp_mem, &shared->agp_key ); + if (ret) + goto error3; + + ret = dfb_agp_bind( shared->agp_key, 0 ); + if (ret) + goto error4; + + dfb_agp->base = mmap( NULL, shared->info.aper_size << 20, + PROT_READ | PROT_WRITE, MAP_SHARED, + dfb_agp->fd, 0 ); + if (dfb_agp->base == MAP_FAILED) { + D_PERROR( "DirectFB/FBDev/agp: Could not mmap the AGP aperture!\n" ); + ret = DFB_INIT; + goto error5; + } + + dfb_agp_release(); + + dfb_fbdev->agp = dfb_agp; + dfb_fbdev->shared->agp = shared; + + return DFB_OK; + +error5: + dfb_agp_unbind( shared->agp_key ); +error4: + dfb_agp_deallocate( shared->agp_key ); +error3: + dfb_agp_release(); +error2: + close( dfb_agp->fd ); +error1: + SHFREE( dfb_fbdev->shared->shmpool, shared ); +error0: + D_FREE( dfb_agp ); + dfb_agp = NULL; + + return ret; +} + +DFBResult +dfb_agp_join( void ) +{ + AGPShared *shared; + DFBResult ret = DFB_FAILURE; + + if (dfb_agp) { + D_BUG( "dfb_agp_join() already called!" ); + return DFB_BUG; + } + + shared = dfb_fbdev->shared->agp; + if (!shared) + return DFB_OK; + + dfb_agp = D_CALLOC( 1, sizeof(AGPDevice) ); + if (!dfb_agp) + return D_OOM(); + + dfb_agp->fd = direct_try_open( "/dev/agpgart", + "/dev/misc/agpgart", O_RDWR, true ); + if (dfb_agp->fd < 0) { + ret = errno2result( errno ); + D_ERROR( "DirectFB/FBDev/agp: Error opening AGP device!\n" ); + goto error0; + } + + ret = dfb_agp_acquire(); + if (ret) + goto error1; + + dfb_agp->base = mmap( NULL, shared->info.aper_size << 20, + PROT_READ | PROT_WRITE, MAP_SHARED, + dfb_agp->fd, 0 ); + if (dfb_agp->base == MAP_FAILED) { + D_PERROR( "DirectFB/FBDev/agp: Could not mmap the AGP aperture!\n" ); + ret = DFB_INIT; + goto error2; + } + + D_DEBUG( "DirectFB/FBDev/agp: AGP aperture mapped at %p\n", dfb_agp->base ); + + dfb_agp_release(); + + dfb_fbdev->agp = dfb_agp; + + return DFB_OK; + +error2: + dfb_agp_release(); +error1: + close( dfb_agp->fd ); +error0: + D_FREE( dfb_agp ); + dfb_agp = NULL; + + return ret; +} + +DFBResult +dfb_agp_shutdown( void ) +{ + AGPShared *shared; + + if (!dfb_agp) + return DFB_INVARG; + + shared = dfb_fbdev->shared->agp; + + dfb_agp_acquire(); + + munmap( dfb_agp->base, shared->info.aper_size << 20 ); + + dfb_agp_unbind( shared->agp_key ); + dfb_agp_deallocate( shared->agp_key ); + + dfb_agp_release(); + close( dfb_agp->fd ); + + SHFREE( dfb_fbdev->shared->shmpool, shared ); + D_FREE( dfb_agp ); + + dfb_fbdev->shared->agp = NULL; + dfb_fbdev->agp = dfb_agp = NULL; + + return DFB_OK; +} + +DFBResult +dfb_agp_leave( void ) +{ + AGPShared *shared; + + if (!dfb_agp) + return DFB_INVARG; + + shared = dfb_fbdev->shared->agp; + + dfb_agp_acquire(); + + munmap( dfb_agp->base, shared->info.aper_size << 20 ); + + dfb_agp_release(); + + close( dfb_agp->fd ); + D_FREE( dfb_agp ); + + dfb_fbdev->agp = dfb_agp = NULL; + + return DFB_OK; +} + -- cgit