2005-04-17 02:20:36 +04:00
/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */
/**
* \ file drm_pci . c
* \ brief Functions and ioctls to manage PCI memory
*
* \ warning These interfaces aren ' t stable yet .
*
* \ todo Implement the remaining ioctl ' s for the PCI pools .
* \ todo The wrappers here are so thin that they would be better off inlined . .
*
* \ author Jose Fonseca < jrfonseca @ tungstengraphics . com >
* \ author Leif Delgass < ldelgass @ retinalburn . net >
*/
/*
* Copyright 2003 Jos <EFBFBD> Fonseca .
* Copyright 2003 Leif Delgass .
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
# include <linux/pci.h>
2006-04-05 12:12:18 +04:00
# include <linux/dma-mapping.h>
2005-04-17 02:20:36 +04:00
# include "drmP.h"
/**********************************************************************/
/** \name PCI memory */
/*@{*/
/**
* \ brief Allocate a PCI consistent memory block , for DMA .
*/
2005-07-10 09:38:56 +04:00
drm_dma_handle_t * drm_pci_alloc ( drm_device_t * dev , size_t size , size_t align ,
dma_addr_t maxaddr )
2005-04-17 02:20:36 +04:00
{
2005-07-10 09:38:56 +04:00
drm_dma_handle_t * dmah ;
2006-03-19 10:56:12 +03:00
# if 1
unsigned long addr ;
size_t sz ;
# endif
2005-08-05 17:09:14 +04:00
# ifdef DRM_DEBUG_MEMORY
2005-04-17 02:20:36 +04:00
int area = DRM_MEM_DMA ;
spin_lock ( & drm_mem_lock ) ;
if ( ( drm_ram_used > > PAGE_SHIFT )
> ( DRM_RAM_PERCENT * drm_ram_available ) / 100 ) {
spin_unlock ( & drm_mem_lock ) ;
return 0 ;
}
spin_unlock ( & drm_mem_lock ) ;
# endif
/* pci_alloc_consistent only guarantees alignment to the smallest
* PAGE_SIZE order which is greater than or equal to the requested size .
* Return NULL here for now to make sure nobody tries for larger alignment
*/
if ( align > size )
return NULL ;
if ( pci_set_dma_mask ( dev - > pdev , maxaddr ) ! = 0 ) {
DRM_ERROR ( " Setting pci dma mask failed \n " ) ;
return NULL ;
}
2005-07-10 09:38:56 +04:00
dmah = kmalloc ( sizeof ( drm_dma_handle_t ) , GFP_KERNEL ) ;
if ( ! dmah )
return NULL ;
2005-09-25 08:28:13 +04:00
2005-07-10 09:38:56 +04:00
dmah - > size = size ;
2006-03-19 10:56:12 +03:00
dmah - > vaddr = dma_alloc_coherent ( & dev - > pdev - > dev , size , & dmah - > busaddr , GFP_KERNEL | __GFP_COMP ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 17:09:14 +04:00
# ifdef DRM_DEBUG_MEMORY
2005-07-10 09:38:56 +04:00
if ( dmah - > vaddr = = NULL ) {
2005-04-17 02:20:36 +04:00
spin_lock ( & drm_mem_lock ) ;
+ + drm_mem_stats [ area ] . fail_count ;
spin_unlock ( & drm_mem_lock ) ;
2005-07-10 09:38:56 +04:00
kfree ( dmah ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
spin_lock ( & drm_mem_lock ) ;
+ + drm_mem_stats [ area ] . succeed_count ;
drm_mem_stats [ area ] . bytes_allocated + = size ;
drm_ram_used + = size ;
spin_unlock ( & drm_mem_lock ) ;
# else
2005-07-10 09:38:56 +04:00
if ( dmah - > vaddr = = NULL ) {
kfree ( dmah ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
2005-07-10 09:38:56 +04:00
}
2005-04-17 02:20:36 +04:00
# endif
2005-07-10 09:38:56 +04:00
memset ( dmah - > vaddr , 0 , size ) ;
2005-04-17 02:20:36 +04:00
2006-03-19 10:56:12 +03:00
/* XXX - Is virt_to_page() legal for consistent mem? */
/* Reserve */
for ( addr = ( unsigned long ) dmah - > vaddr , sz = size ;
sz > 0 ; addr + = PAGE_SIZE , sz - = PAGE_SIZE ) {
SetPageReserved ( virt_to_page ( addr ) ) ;
}
2005-07-10 09:38:56 +04:00
return dmah ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( drm_pci_alloc ) ;
/**
2006-03-19 10:56:12 +03:00
* \ brief Free a PCI consistent memory block without freeing its descriptor .
2005-07-10 09:38:56 +04:00
*
* This function is for internal use in the Linux - specific DRM core code .
2005-04-17 02:20:36 +04:00
*/
2005-09-25 08:28:13 +04:00
void __drm_pci_free ( drm_device_t * dev , drm_dma_handle_t * dmah )
2005-04-17 02:20:36 +04:00
{
2006-03-19 10:56:12 +03:00
# if 1
unsigned long addr ;
size_t sz ;
# endif
2005-08-05 17:09:14 +04:00
# ifdef DRM_DEBUG_MEMORY
2005-04-17 02:20:36 +04:00
int area = DRM_MEM_DMA ;
int alloc_count ;
int free_count ;
# endif
2005-07-10 09:38:56 +04:00
if ( ! dmah - > vaddr ) {
2005-08-05 17:09:14 +04:00
# ifdef DRM_DEBUG_MEMORY
2005-04-17 02:20:36 +04:00
DRM_MEM_ERROR ( area , " Attempt to free address 0 \n " ) ;
# endif
} else {
2006-03-19 10:56:12 +03:00
/* XXX - Is virt_to_page() legal for consistent mem? */
/* Unreserve */
for ( addr = ( unsigned long ) dmah - > vaddr , sz = dmah - > size ;
sz > 0 ; addr + = PAGE_SIZE , sz - = PAGE_SIZE ) {
ClearPageReserved ( virt_to_page ( addr ) ) ;
}
dma_free_coherent ( & dev - > pdev - > dev , dmah - > size , dmah - > vaddr ,
dmah - > busaddr ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 17:09:14 +04:00
# ifdef DRM_DEBUG_MEMORY
2005-04-17 02:20:36 +04:00
spin_lock ( & drm_mem_lock ) ;
free_count = + + drm_mem_stats [ area ] . free_count ;
alloc_count = drm_mem_stats [ area ] . succeed_count ;
drm_mem_stats [ area ] . bytes_freed + = size ;
drm_ram_used - = size ;
spin_unlock ( & drm_mem_lock ) ;
if ( free_count > alloc_count ) {
DRM_MEM_ERROR ( area ,
" Excess frees: %d frees, %d allocs \n " ,
free_count , alloc_count ) ;
}
# endif
}
2005-07-10 09:38:56 +04:00
/**
* \ brief Free a PCI consistent memory block
*/
2005-09-25 08:28:13 +04:00
void drm_pci_free ( drm_device_t * dev , drm_dma_handle_t * dmah )
2005-07-10 09:38:56 +04:00
{
__drm_pci_free ( dev , dmah ) ;
kfree ( dmah ) ;
}
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( drm_pci_free ) ;
/*@}*/