2005-04-16 15:20:36 -07:00
/**
2005-09-25 14:28:13 +10:00
* \ file drm_scatter . c
2005-04-16 15:20:36 -07:00
* IOCTLs to manage scatter / gather memory
*
* \ author Gareth Hughes < gareth @ valinux . com >
*/
/*
* Created : Mon Dec 18 23 : 20 : 54 2000 by gareth @ valinux . com
*
* Copyright 2000 VA Linux Systems , Inc . , Sunnyvale , California .
* 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
* PRECISION INSIGHT AND / OR ITS SUPPLIERS 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/vmalloc.h>
# include "drmP.h"
# define DEBUG_SCATTER 0
2008-03-28 14:23:07 -07:00
static inline void * drm_vmalloc_dma ( unsigned long size )
{
# if defined(__powerpc__) && defined(CONFIG_NOT_COHERENT_CACHE)
return __vmalloc ( size , GFP_KERNEL , PAGE_KERNEL | _PAGE_NO_CACHE ) ;
# else
return vmalloc_32 ( size ) ;
# endif
}
2007-07-11 16:53:40 +10:00
void drm_sg_cleanup ( struct drm_sg_mem * entry )
2005-04-16 15:20:36 -07:00
{
struct page * page ;
int i ;
2005-09-25 14:28:13 +10:00
for ( i = 0 ; i < entry - > pages ; i + + ) {
2005-04-16 15:20:36 -07:00
page = entry - > pagelist [ i ] ;
2005-09-25 14:28:13 +10:00
if ( page )
ClearPageReserved ( page ) ;
2005-04-16 15:20:36 -07:00
}
2005-09-25 14:28:13 +10:00
vfree ( entry - > virtual ) ;
drm_free ( entry - > busaddr ,
entry - > pages * sizeof ( * entry - > busaddr ) , DRM_MEM_PAGES ) ;
drm_free ( entry - > pagelist ,
entry - > pages * sizeof ( * entry - > pagelist ) , DRM_MEM_PAGES ) ;
drm_free ( entry , sizeof ( * entry ) , DRM_MEM_SGLISTS ) ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 22:11:22 +10:00
# ifdef _LP64
# define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1)))
# else
# define ScatterHandle(x) (unsigned int)(x)
# endif
2007-09-03 12:06:45 +10:00
int drm_sg_alloc ( struct drm_device * dev , struct drm_scatter_gather * request )
2005-04-16 15:20:36 -07:00
{
2007-07-11 16:53:40 +10:00
struct drm_sg_mem * entry ;
2005-04-16 15:20:36 -07:00
unsigned long pages , i , j ;
2008-01-24 15:58:57 +10:00
DRM_DEBUG ( " \n " ) ;
2005-04-16 15:20:36 -07:00
if ( ! drm_core_check_feature ( dev , DRIVER_SG ) )
return - EINVAL ;
2005-09-25 14:28:13 +10:00
if ( dev - > sg )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2005-09-25 14:28:13 +10:00
entry = drm_alloc ( sizeof ( * entry ) , DRM_MEM_SGLISTS ) ;
if ( ! entry )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
2005-09-25 14:28:13 +10:00
memset ( entry , 0 , sizeof ( * entry ) ) ;
2007-09-03 12:06:45 +10:00
pages = ( request - > size + PAGE_SIZE - 1 ) / PAGE_SIZE ;
2008-01-24 15:58:57 +10:00
DRM_DEBUG ( " size=%ld pages=%ld \n " , request - > size , pages ) ;
2005-04-16 15:20:36 -07:00
entry - > pages = pages ;
2005-09-25 14:28:13 +10:00
entry - > pagelist = drm_alloc ( pages * sizeof ( * entry - > pagelist ) ,
DRM_MEM_PAGES ) ;
if ( ! entry - > pagelist ) {
drm_free ( entry , sizeof ( * entry ) , DRM_MEM_SGLISTS ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
memset ( entry - > pagelist , 0 , pages * sizeof ( * entry - > pagelist ) ) ;
2005-09-25 14:28:13 +10:00
entry - > busaddr = drm_alloc ( pages * sizeof ( * entry - > busaddr ) ,
DRM_MEM_PAGES ) ;
if ( ! entry - > busaddr ) {
drm_free ( entry - > pagelist ,
entry - > pages * sizeof ( * entry - > pagelist ) ,
DRM_MEM_PAGES ) ;
drm_free ( entry , sizeof ( * entry ) , DRM_MEM_SGLISTS ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
2005-09-25 14:28:13 +10:00
memset ( ( void * ) entry - > busaddr , 0 , pages * sizeof ( * entry - > busaddr ) ) ;
2008-03-28 14:23:07 -07:00
entry - > virtual = drm_vmalloc_dma ( pages < < PAGE_SHIFT ) ;
2005-09-25 14:28:13 +10:00
if ( ! entry - > virtual ) {
drm_free ( entry - > busaddr ,
entry - > pages * sizeof ( * entry - > busaddr ) , DRM_MEM_PAGES ) ;
drm_free ( entry - > pagelist ,
entry - > pages * sizeof ( * entry - > pagelist ) ,
DRM_MEM_PAGES ) ;
drm_free ( entry , sizeof ( * entry ) , DRM_MEM_SGLISTS ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
/* This also forces the mapping of COW pages, so our page list
* will be valid . Please don ' t remove it . . .
*/
2005-09-25 14:28:13 +10:00
memset ( entry - > virtual , 0 , pages < < PAGE_SHIFT ) ;
2005-04-16 15:20:36 -07:00
2005-08-05 22:11:22 +10:00
entry - > handle = ScatterHandle ( ( unsigned long ) entry - > virtual ) ;
2005-04-16 15:20:36 -07:00
2008-01-24 15:58:57 +10:00
DRM_DEBUG ( " handle = %08lx \n " , entry - > handle ) ;
DRM_DEBUG ( " virtual = %p \n " , entry - > virtual ) ;
2005-04-16 15:20:36 -07:00
2005-09-25 14:28:13 +10:00
for ( i = ( unsigned long ) entry - > virtual , j = 0 ; j < pages ;
i + = PAGE_SIZE , j + + ) {
2005-04-16 15:20:36 -07:00
entry - > pagelist [ j ] = vmalloc_to_page ( ( void * ) i ) ;
if ( ! entry - > pagelist [ j ] )
goto failed ;
SetPageReserved ( entry - > pagelist [ j ] ) ;
}
2007-09-03 12:06:45 +10:00
request - > handle = entry - > handle ;
2005-04-16 15:20:36 -07:00
dev - > sg = entry ;
# if DEBUG_SCATTER
/* Verify that each page points to its virtual address, and vice
* versa .
*/
{
2005-09-25 14:28:13 +10:00
int error = 0 ;
2005-04-16 15:20:36 -07:00
2005-09-25 14:28:13 +10:00
for ( i = 0 ; i < pages ; i + + ) {
unsigned long * tmp ;
2005-04-16 15:20:36 -07:00
2005-09-25 14:28:13 +10:00
tmp = page_address ( entry - > pagelist [ i ] ) ;
for ( j = 0 ;
j < PAGE_SIZE / sizeof ( unsigned long ) ;
j + + , tmp + + ) {
* tmp = 0xcafebabe ;
}
tmp = ( unsigned long * ) ( ( u8 * ) entry - > virtual +
( PAGE_SIZE * i ) ) ;
for ( j = 0 ;
j < PAGE_SIZE / sizeof ( unsigned long ) ;
j + + , tmp + + ) {
if ( * tmp ! = 0xcafebabe & & error = = 0 ) {
error = 1 ;
DRM_ERROR ( " Scatter allocation error, "
" pagelist does not match "
" virtual mapping \n " ) ;
}
}
tmp = page_address ( entry - > pagelist [ i ] ) ;
for ( j = 0 ;
j < PAGE_SIZE / sizeof ( unsigned long ) ;
j + + , tmp + + ) {
* tmp = 0 ;
2005-04-16 15:20:36 -07:00
}
}
2005-09-25 14:28:13 +10:00
if ( error = = 0 )
DRM_ERROR ( " Scatter allocation matches pagelist \n " ) ;
2005-04-16 15:20:36 -07:00
}
# endif
return 0 ;
2005-09-25 14:28:13 +10:00
failed :
drm_sg_cleanup ( entry ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
2007-09-03 12:06:45 +10:00
EXPORT_SYMBOL ( drm_sg_alloc ) ;
2005-04-16 15:20:36 -07:00
2007-09-03 12:06:45 +10:00
int drm_sg_alloc_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
2005-04-16 15:20:36 -07:00
{
2007-09-03 12:06:45 +10:00
struct drm_scatter_gather * request = data ;
return drm_sg_alloc ( dev , request ) ;
}
int drm_sg_free ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_scatter_gather * request = data ;
2007-07-11 16:53:40 +10:00
struct drm_sg_mem * entry ;
2005-04-16 15:20:36 -07:00
if ( ! drm_core_check_feature ( dev , DRIVER_SG ) )
return - EINVAL ;
entry = dev - > sg ;
dev - > sg = NULL ;
2007-09-03 12:06:45 +10:00
if ( ! entry | | entry - > handle ! = request - > handle )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2008-01-24 15:58:57 +10:00
DRM_DEBUG ( " virtual = %p \n " , entry - > virtual ) ;
2005-04-16 15:20:36 -07:00
2005-09-25 14:28:13 +10:00
drm_sg_cleanup ( entry ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}