/* Intel i830 DirectFB graphics driver (c) Copyright 2005 Servision Ltd. http://www.servision.net/ All rights reserved. Based on i810 driver written by Antonino Daplas 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 /* FIXME: Needs to be included before dfb_types.h to work around a type clash with asm/types.h */ #include "i830.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* need fb handle to get accel, MMIO programming in the i830 is useless */ #include #include #include #include #include #include DFB_GRAPHICS_DRIVER( i830 ) /**************************************************************************************************/ #define TIMER_LOOP 1000000000 #define BUFFER_PADDING 2 #define MMIO_SIZE 512 * 1024 #define I830_SUPPORTED_DRAWINGFLAGS (DSDRAW_NOFX) #define I830_SUPPORTED_DRAWINGFUNCTIONS (DFXL_NONE) #define I830_SUPPORTED_BLITTINGFLAGS (DSBLIT_NOFX) #define I830_SUPPORTED_BLITTINGFUNCTIONS (DFXL_NONE) /**************************************************************************************************/ static void i830_lring_enable( I830DriverData *idrv, u32 mode ) { u32 tmp; D_DEBUG_AT( I830_Ring, "%s lp ring...\n", mode ? "Enabling" : "Disabling" ); tmp = i830_readl(idrv->mmio_base, LP_RING + RING_LEN); tmp = (!mode) ? tmp & ~1 : tmp | 1; i830_writel( idrv->mmio_base, LP_RING + RING_LEN, tmp ); } static inline void i830_wait_for_blit_idle( I830DriverData *idrv, I830DeviceData *idev ) { u32 count = 0; u32 head , tail; if (idev != NULL) idev->idle_calls++; head = i830_readl(idrv->mmio_base, LP_RING + RING_HEAD) & I830_HEAD_MASK; tail = i830_readl(idrv->mmio_base, LP_RING + RING_TAIL) & I830_TAIL_MASK; while ((head != tail) && (count++ < TIMER_LOOP)) { if (idev != NULL) idev->idle_waitcycles++; head = i830_readl(idrv->mmio_base, LP_RING + RING_HEAD) & I830_HEAD_MASK; tail = i830_readl(idrv->mmio_base, LP_RING + RING_TAIL) & I830_TAIL_MASK; } if (count >= TIMER_LOOP) { if (idev != NULL) idev->idle_timeoutsum++; D_BUG("warning: idle timeout exceeded"); } } static void i830_init_ringbuffer( I830DriverData *idrv, I830DeviceData *idev ) { u32 ring_enabled; D_DEBUG_AT( I830_Ring, "Previous lp ring config: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", i830_readl(idrv->mmio_base, LP_RING), i830_readl(idrv->mmio_base, LP_RING + RING_HEAD), i830_readl(idrv->mmio_base, LP_RING + RING_START), i830_readl(idrv->mmio_base, LP_RING + RING_LEN) ); ring_enabled = i830_readl(idrv->mmio_base, LP_RING + RING_LEN) & 1; if (ring_enabled) i830_wait_for_blit_idle(idrv, idev); i830_lring_enable(idrv, 0); idev->lring1 = i830_readl(idrv->mmio_base, LP_RING); idev->lring2 = i830_readl(idrv->mmio_base, LP_RING + RING_HEAD); idev->lring3 = i830_readl(idrv->mmio_base, LP_RING + RING_START); idev->lring4 = i830_readl(idrv->mmio_base, LP_RING + RING_LEN); D_FLAGS_SET( idrv->flags, I830RES_STATE_SAVE ); i830_writel(idrv->mmio_base, LP_RING + RING_LEN, 0); i830_writel(idrv->mmio_base, LP_RING + RING_HEAD, 0); i830_writel(idrv->mmio_base, LP_RING + RING_TAIL, 0); i830_writel(idrv->mmio_base, LP_RING + RING_START, 0); D_DEBUG_AT( I830_Ring, "INST_DONE: 0x%04x\n", i830_readw(idrv->mmio_base, INST_DONE) ); idev->lp_ring.size = RINGBUFFER_SIZE; idev->lp_ring.tail_mask = idev->lp_ring.size - 1; i830_writel( idrv->mmio_base, LP_RING + RING_START, (idev->lring_bind.pg_start * 4096) & I830_RING_START_MASK ); i830_writel( idrv->mmio_base, LP_RING + RING_LEN, (idev->lp_ring.size - 4096) & I830_RING_NR_PAGES ); i830_lring_enable(idrv, 1); D_DEBUG_AT( I830_Ring, "Wrote lp ring config: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", i830_readl(idrv->mmio_base, LP_RING), i830_readl(idrv->mmio_base, LP_RING + RING_HEAD), i830_readl(idrv->mmio_base, LP_RING + RING_START), i830_readl(idrv->mmio_base, LP_RING + RING_LEN) ); } DFBResult i830_wait_lp_ring( I830DriverData *idrv, I830DeviceData *idev, int space ) { I830RingBuffer *buf = &idev->lp_ring; idev->waitfifo_calls++; idev->waitfifo_sum += space; D_DEBUG_AT( I830_Ring, "Waiting for %d...\n", space ); if (buf->space < space) { int head = 0; int loops = 0; do { idev->fifo_waitcycles++; if (loops++ > 100000000) { D_ERROR( "timeout waiting for ring buffer space\n" ); return DFB_TIMEOUT; } buf->head = i830_readl( idrv->mmio_base, LP_RING + RING_HEAD ) & I830_HEAD_MASK; buf->space = buf->head - (buf->tail + 8); if (buf->space < 0) buf->space += buf->size; //D_DEBUG_AT( I830_Ring, "... have %d space\n", buf->space ); if (buf->head != head) loops = 0; head = buf->head; } while (buf->space < space); } else idev->fifo_cache_hits++; return DFB_OK; } /**************************************************************************************************/ static void i830FlushTextureCache( void *drv, void *dev ) { I830DriverData *idrv = drv; I830DeviceData *idev = dev; I830RingBlock block = { .virt = NULL }; if (i830_begin_lp_ring( idrv, idev, 2, &block )) return; i830_out_ring( &block, MI_FLUSH ); i830_out_ring( &block, MI_NOOP ); i830_advance_lp_ring( idrv, idev, &block ); } static DFBResult i830EngineSync( void *drv, void *dev ) { I830DriverData *idrv = drv; I830DeviceData *idev = dev; i830_wait_for_blit_idle( idrv, idev ); return DFB_OK; } /**************************************************************************************************/ static void i830CheckState(void *drv, void *dev, CardState *state, DFBAccelerationMask accel ) { switch (state->destination->config.format) { default: return; } if (!(accel & ~I830_SUPPORTED_DRAWINGFUNCTIONS) && !(state->drawingflags & ~I830_SUPPORTED_DRAWINGFLAGS)) state->accel |= I830_SUPPORTED_DRAWINGFUNCTIONS; if (!(accel & ~I830_SUPPORTED_BLITTINGFUNCTIONS) && !(state->blittingflags & ~I830_SUPPORTED_BLITTINGFLAGS)) { if (state->source->config.format == state->destination->config.format) state->accel |= I830_SUPPORTED_BLITTINGFUNCTIONS; } } static void i830SetState( void *drv, void *dev, GraphicsDeviceFuncs *funcs, CardState *state, DFBAccelerationMask accel ) { switch (accel) { default: D_BUG("unexpected drawing/blitting function"); } state->mod_hw = 0; } /**************************************************************************************************/ static int driver_probe( CoreGraphicsDevice *device ) { switch (dfb_gfxcard_get_accelerator( device )) { case FB_ACCEL_I830: /* Intel 830 */ return 1; } return 0; } static void driver_get_info( CoreGraphicsDevice *device, GraphicsDriverInfo *info ) { /* fill driver info structure */ snprintf( info->name, DFB_GRAPHICS_DRIVER_INFO_NAME_LENGTH, "Intel 830/845G/852GM/855GM/865G Driver" ); snprintf( info->vendor, DFB_GRAPHICS_DRIVER_INFO_VENDOR_LENGTH, "Denis Oliver Kropp" ); info->version.major = 0; info->version.minor = 1; info->driver_data_size = sizeof (I830DriverData); info->device_data_size = sizeof (I830DeviceData); } static void i830_release_resource( I830DriverData *idrv, I830DeviceData *idev ) { agp_unbind unbind; if (idrv->flags & I830RES_STATE_SAVE) { i830_writel( idrv->mmio_base, LP_RING, idev->lring1 ); i830_writel( idrv->mmio_base, LP_RING + RING_HEAD, idev->lring2 ); i830_writel( idrv->mmio_base, LP_RING + RING_START, idev->lring3 ); i830_writel( idrv->mmio_base, LP_RING + RING_LEN, idev->lring4 ); } if (idrv->flags & I830RES_MMAP) { munmap((void *) idrv->aper_base, idev->info.aper_size * 1024 * 1024); idrv->flags &= ~I830RES_MMAP; } if (idrv->flags & I830RES_LRING_BIND) { unbind.key = idev->lring_bind.key; ioctl(idrv->agpgart, AGPIOC_UNBIND, &unbind); } if (idrv->flags & I830RES_LRING_ACQ) ioctl(idrv->agpgart, AGPIOC_DEALLOCATE, idev->lring_mem.key); if (idrv->flags & I830RES_OVL_BIND) { unbind.key = idev->ovl_bind.key; ioctl(idrv->agpgart, AGPIOC_UNBIND, &unbind); } if (idrv->flags & I830RES_OVL_ACQ) ioctl(idrv->agpgart, AGPIOC_DEALLOCATE, idev->ovl_mem.key); if (idrv->flags & I830RES_GART_ACQ) { ioctl(idrv->agpgart, AGPIOC_RELEASE); idrv->flags &= ~I830RES_GART_ACQ; } if (idrv->flags & I830RES_GART) { close(idrv->agpgart); idrv->flags &= ~I830RES_GART; } } static DFBResult i830_agp_setup( CoreGraphicsDevice *device, I830DriverData *idrv, I830DeviceData *idev ) { idrv->agpgart = open("/dev/agpgart", O_RDWR); if (idrv->agpgart == -1) return DFB_IO; D_FLAGS_SET( idrv->flags, I830RES_GART ); if (ioctl(idrv->agpgart, AGPIOC_ACQUIRE)) { D_PERROR( "I830/AGP: AGPIOC_ACQUIRE failed!\n" ); return DFB_IO; } D_FLAGS_SET( idrv->flags, I830RES_GART_ACQ ); if (!idev->initialized) { agp_setup setup; setup.agp_mode = 0; if (ioctl(idrv->agpgart, AGPIOC_SETUP, &setup)) { D_PERROR( "I830/AGP: AGPIOC_SETUP failed!\n" ); return DFB_IO; } if (ioctl(idrv->agpgart, AGPIOC_INFO, &idev->info)) { D_PERROR( "I830/AGP: AGPIOC_INFO failed!\n" ); return DFB_IO; } } idrv->aper_base = mmap( NULL, idev->info.aper_size * 1024 * 1024, PROT_WRITE, MAP_SHARED, idrv->agpgart, 0 ); if (idrv->aper_base == MAP_FAILED) { D_PERROR( "I830/AGP: mmap() failed!\n" ); i830_release_resource( idrv, idev ); return DFB_IO; } D_FLAGS_SET( idrv->flags, I830RES_MMAP ); if (!idev->initialized) { u32 base; /* We'll attempt to bind at fb_base + fb_len + 1 MB, to be safe */ base = dfb_gfxcard_memory_physical(device, 0) - idev->info.aper_base; base += dfb_gfxcard_memory_length(); base += (1024 * 1024); idev->lring_mem.pg_count = RINGBUFFER_SIZE/4096; idev->lring_mem.type = AGP_NORMAL_MEMORY; if (ioctl(idrv->agpgart, AGPIOC_ALLOCATE, &idev->lring_mem)) { D_PERROR( "I830/AGP: AGPIOC_ALLOCATE failed!\n" ); i830_release_resource( idrv, idev ); return DFB_IO; } D_FLAGS_SET( idrv->flags, I830RES_LRING_ACQ ); idev->lring_bind.key = idev->lring_mem.key; idev->lring_bind.pg_start = base/4096; if (ioctl(idrv->agpgart, AGPIOC_BIND, &idev->lring_bind)) { D_PERROR( "I830/AGP: AGPIOC_BIND failed!\n" ); i830_release_resource( idrv, idev ); return DFB_IO; } D_FLAGS_SET( idrv->flags, I830RES_LRING_BIND ); idev->ovl_mem.pg_count = 1; idev->ovl_mem.type = AGP_PHYSICAL_MEMORY; if (ioctl(idrv->agpgart, AGPIOC_ALLOCATE, &idev->ovl_mem)) { D_PERROR( "I830/AGP: AGPIOC_ALLOCATE failed!\n" ); i830_release_resource( idrv, idev ); return DFB_IO; } D_FLAGS_SET( idrv->flags, I830RES_OVL_ACQ ); idev->ovl_bind.key = idev->ovl_mem.key; idev->ovl_bind.pg_start = (base + RINGBUFFER_SIZE)/4096; if (ioctl(idrv->agpgart, AGPIOC_BIND, &idev->ovl_bind)) { D_PERROR( "I830/AGP: AGPIOC_BIND failed!\n" ); i830_release_resource( idrv, idev ); return DFB_IO; } D_FLAGS_SET( idrv->flags, I830RES_OVL_BIND ); } if (idrv->flags & I830RES_GART_ACQ) { ioctl(idrv->agpgart, AGPIOC_RELEASE); idrv->flags &= ~I830RES_GART_ACQ; } idrv->lring_base = idrv->aper_base + idev->lring_bind.pg_start * 4096; idrv->ovl_base = idrv->aper_base + idev->ovl_bind.pg_start * 4096; idrv->pattern_base = idrv->ovl_base + 1024; if (!idev->initialized) { memset((void *) idrv->lring_base, 0x00, RINGBUFFER_SIZE); memset((void *) idrv->ovl_base, 0xff, 1024); memset((void *) idrv->pattern_base, 0xff, 4096 - 1024); idev->lring1 = 0;//i830_readl(idrv->mmio_base, LP_RING); idev->lring2 = 0;//i830_readl(idrv->mmio_base, LP_RING + RING_HEAD); idev->lring3 = 0;//i830_readl(idrv->mmio_base, LP_RING + RING_START); idev->lring4 = 0;//i830_readl(idrv->mmio_base, LP_RING + RING_LEN); } idev->initialized = true; return DFB_OK; } static DFBResult driver_init_driver( CoreGraphicsDevice *device, GraphicsDeviceFuncs *funcs, void *driver_data, void *device_data, CoreDFB *core ) { DFBResult ret; I830DriverData *idrv = driver_data; I830DeviceData *idev = device_data; idrv->idev = device_data; idrv->mmio_base = (volatile u8*) dfb_gfxcard_map_mmio( device, 0, -1 ); if (!idrv->mmio_base) return DFB_IO; ret = i830_agp_setup( device, idrv, idev ); if (ret) { dfb_gfxcard_unmap_mmio( device, idrv->mmio_base, -1 ); return ret; } idrv->info = idev->info; funcs->CheckState = i830CheckState; funcs->SetState = i830SetState; funcs->EngineSync = i830EngineSync; funcs->FlushTextureCache = i830FlushTextureCache; dfb_layers_register( dfb_screens_at(DSCID_PRIMARY), driver_data, &i830OverlayFuncs ); return DFB_OK; } static DFBResult driver_init_device( CoreGraphicsDevice *device, GraphicsDeviceInfo *device_info, void *driver_data, void *device_data ) { I830DriverData *idrv = driver_data; I830DeviceData *idev = device_data; // int offset; /* fill device info */ snprintf( device_info->name, DFB_GRAPHICS_DEVICE_INFO_NAME_LENGTH, "830/845G/852GM/855GM/865G" ); snprintf( device_info->vendor, DFB_GRAPHICS_DEVICE_INFO_VENDOR_LENGTH, "Intel" ); device_info->caps.flags = 0; device_info->caps.accel = I830_SUPPORTED_DRAWINGFUNCTIONS | I830_SUPPORTED_BLITTINGFUNCTIONS; device_info->caps.drawing = I830_SUPPORTED_DRAWINGFLAGS; device_info->caps.blitting = I830_SUPPORTED_BLITTINGFLAGS; device_info->limits.surface_byteoffset_alignment = 32 * 4; device_info->limits.surface_pixelpitch_alignment = 32; device_info->limits.surface_bytepitch_alignment = 64; dfb_config->pollvsync_after = 1; /* offset = dfb_gfxcard_reserve_memory( device, RINGBUFFER_SIZE ); idrv->lring_mem.physical = dfb_gfxcard_memory_physical( device, offset ); idrv->lring_base = dfb_gfxcard_memory_virtual( device, offset ); offset = dfb_gfxcard_reserve_memory( device, 4096 ); idrv->ovl_mem.physical = dfb_gfxcard_memory_physical( device, offset ); idrv->ovl_base = dfb_gfxcard_memory_virtual( device, offset );*/ /* D_DEBUG_AT( I830_Ring, "lp_ring at 0x%08x (%p)\n", idrv->lring_mem.physical, idrv->lring_base ); D_DEBUG_AT( I830_Ring, "ovl at 0x%08x (%p)\n", idrv->ovl_mem.physical, idrv->ovl_base );*/ i830_init_ringbuffer( idrv, idev ); return DFB_OK; } static void driver_close_device( CoreGraphicsDevice *device, void *driver_data, void *device_data ) { I830DeviceData *idev = device_data; I830DriverData *idrv = driver_data; i830ovlOnOff( idrv, idev, false ); i830_wait_for_blit_idle(idrv, idev); i830_lring_enable(idrv, 0); i830_release_resource( idrv, idev ); D_DEBUG( "DirectFB/I830: DMA Buffer Performance Monitoring:\n"); D_DEBUG( "DirectFB/I830: %9d DMA buffer size in KB\n", RINGBUFFER_SIZE/1024 ); D_DEBUG( "DirectFB/I830: %9d i830_wait_for_blit_idle calls\n", idev->idle_calls ); D_DEBUG( "DirectFB/I830: %9d i830_wait_for_space calls\n", idev->waitfifo_calls ); D_DEBUG( "DirectFB/I830: %9d BUFFER transfers (i830_wait_for_space sum)\n", idev->waitfifo_sum ); D_DEBUG( "DirectFB/I830: %9d BUFFER wait cycles (depends on GPU/CPU)\n", idev->fifo_waitcycles ); D_DEBUG( "DirectFB/I830: %9d IDLE wait cycles (depends on GPU/CPU)\n", idev->idle_waitcycles ); D_DEBUG( "DirectFB/I830: %9d BUFFER space cache hits(depends on BUFFER size)\n", idev->fifo_cache_hits ); D_DEBUG( "DirectFB/I830: %9d BUFFER timeout sum (possible hardware crash)\n", idev->fifo_timeoutsum ); D_DEBUG( "DirectFB/I830: %9d IDLE timeout sum (possible hardware crash)\n", idev->idle_timeoutsum ); D_DEBUG( "DirectFB/I830: Conclusion:\n" ); D_DEBUG( "DirectFB/I830: Average buffer transfers per i830_wait_for_space " "call: %.2f\n", idev->waitfifo_sum/(float)(idev->waitfifo_calls) ); D_DEBUG( "DirectFB/I830: Average wait cycles per i830_wait_for_space call:" " %.2f\n", idev->fifo_waitcycles/(float)(idev->waitfifo_calls) ); D_DEBUG( "DirectFB/I830: Average wait cycles per i830_wait_for_blit_idle call:" " %.2f\n", idev->idle_waitcycles/(float)(idev->idle_calls) ); D_DEBUG( "DirectFB/I830: Average buffer space cache hits: %02d%%\n", (int)(100 * idev->fifo_cache_hits/ (float)(idev->waitfifo_calls)) ); } static void driver_close_driver( CoreGraphicsDevice *device, void *driver_data ) { I830DriverData *idrv = (I830DriverData *) driver_data; dfb_gfxcard_unmap_mmio( device, idrv->mmio_base, -1 ); if (idrv->flags & I830RES_MMAP) { munmap((void *) idrv->aper_base, idrv->info.aper_size * 1024 * 1024); idrv->flags &= ~I830RES_MMAP; } if (idrv->flags & I830RES_GART_ACQ) { ioctl(idrv->agpgart, AGPIOC_RELEASE); idrv->flags &= ~I830RES_GART_ACQ; } if (idrv->flags & I830RES_GART) { close(idrv->agpgart); idrv->flags &= ~I830RES_GART; } }