2006-12-19 09:49:08 +03:00
/* radeon_mem.c -- Simple GART/fb memory manager for radeon -*- linux-c -*- */
/*
2005-04-17 02:20:36 +04:00
* Copyright ( C ) The Weather Channel , Inc . 2002. All Rights Reserved .
2005-09-25 08:28:13 +04:00
*
2005-04-17 02:20:36 +04:00
* The Weather Channel ( TM ) funded Tungsten Graphics to develop the
* initial release of the Radeon 8500 driver under the XFree86 license .
* This notice must be preserved .
*
* 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 .
*
* Authors :
* Keith Whitwell < keith @ tungstengraphics . com >
2013-01-21 16:58:46 +04:00
*
* - - - - - - - - - - - - - - - - - - - - - - - - This file is DEPRECATED ! - - - - - - - - - - - - - - - - - - - - - - - - -
2005-04-17 02:20:36 +04:00
*/
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
# include <drm/radeon_drm.h>
2005-04-17 02:20:36 +04:00
# include "radeon_drv.h"
/* Very simple allocator for GART memory, working on a static range
2005-09-25 08:28:13 +04:00
* already mapped into each client ' s address space .
2005-04-17 02:20:36 +04:00
*/
static struct mem_block * split_block ( struct mem_block * p , int start , int size ,
2007-08-25 14:23:09 +04:00
struct drm_file * file_priv )
2005-04-17 02:20:36 +04:00
{
/* Maybe cut off the start of an existing block */
if ( start > p - > start ) {
2009-03-24 22:23:04 +03:00
struct mem_block * newblock = kmalloc ( sizeof ( * newblock ) ,
GFP_KERNEL ) ;
2005-09-25 08:28:13 +04:00
if ( ! newblock )
2005-04-17 02:20:36 +04:00
goto out ;
newblock - > start = start ;
newblock - > size = p - > size - ( start - p - > start ) ;
2007-08-25 14:23:09 +04:00
newblock - > file_priv = NULL ;
2005-04-17 02:20:36 +04:00
newblock - > next = p - > next ;
newblock - > prev = p ;
p - > next - > prev = newblock ;
p - > next = newblock ;
p - > size - = newblock - > size ;
p = newblock ;
}
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
/* Maybe cut off the end of an existing block */
if ( size < p - > size ) {
2009-03-24 22:23:04 +03:00
struct mem_block * newblock = kmalloc ( sizeof ( * newblock ) ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! newblock )
goto out ;
newblock - > start = start + size ;
newblock - > size = p - > size - size ;
2007-08-25 14:23:09 +04:00
newblock - > file_priv = NULL ;
2005-04-17 02:20:36 +04:00
newblock - > next = p - > next ;
newblock - > prev = p ;
p - > next - > prev = newblock ;
p - > next = newblock ;
p - > size = size ;
}
2005-09-25 08:28:13 +04:00
out :
2005-04-17 02:20:36 +04:00
/* Our block is in the middle */
2007-08-25 14:23:09 +04:00
p - > file_priv = file_priv ;
2005-04-17 02:20:36 +04:00
return p ;
}
2005-09-25 08:28:13 +04:00
static struct mem_block * alloc_block ( struct mem_block * heap , int size ,
2007-08-25 14:23:09 +04:00
int align2 , struct drm_file * file_priv )
2005-04-17 02:20:36 +04:00
{
struct mem_block * p ;
2005-09-25 08:28:13 +04:00
int mask = ( 1 < < align2 ) - 1 ;
2005-04-17 02:20:36 +04:00
list_for_each ( p , heap ) {
int start = ( p - > start + mask ) & ~ mask ;
2008-03-29 00:23:06 +03:00
if ( p - > file_priv = = NULL & & start + size < = p - > start + p - > size )
2007-08-25 14:23:09 +04:00
return split_block ( p , start , size , file_priv ) ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
2005-09-25 08:28:13 +04:00
static struct mem_block * find_block ( struct mem_block * heap , int start )
2005-04-17 02:20:36 +04:00
{
struct mem_block * p ;
list_for_each ( p , heap )
2005-09-25 08:28:13 +04:00
if ( p - > start = = start )
return p ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2005-09-25 08:28:13 +04:00
static void free_block ( struct mem_block * p )
2005-04-17 02:20:36 +04:00
{
2007-08-25 14:23:09 +04:00
p - > file_priv = NULL ;
2005-04-17 02:20:36 +04:00
2007-08-25 14:23:09 +04:00
/* Assumes a single contiguous range. Needs a special file_priv in
2005-04-17 02:20:36 +04:00
* ' heap ' to stop it being subsumed .
*/
2008-03-29 00:23:06 +03:00
if ( p - > next - > file_priv = = NULL ) {
2005-04-17 02:20:36 +04:00
struct mem_block * q = p - > next ;
p - > size + = q - > size ;
p - > next = q - > next ;
p - > next - > prev = p ;
2009-03-24 22:23:04 +03:00
kfree ( q ) ;
2005-04-17 02:20:36 +04:00
}
2008-03-29 00:23:06 +03:00
if ( p - > prev - > file_priv = = NULL ) {
2005-04-17 02:20:36 +04:00
struct mem_block * q = p - > prev ;
q - > size + = p - > size ;
q - > next = p - > next ;
q - > next - > prev = q ;
2009-03-24 22:23:04 +03:00
kfree ( p ) ;
2005-04-17 02:20:36 +04:00
}
}
/* Initialize. How to check for an uninitialized heap?
*/
static int init_heap ( struct mem_block * * heap , int start , int size )
{
2009-03-24 22:23:04 +03:00
struct mem_block * blocks = kmalloc ( sizeof ( * blocks ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
if ( ! blocks )
2007-08-25 13:22:43 +04:00
return - ENOMEM ;
2005-09-25 08:28:13 +04:00
2011-07-26 04:12:56 +04:00
* heap = kzalloc ( sizeof ( * * heap ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! * heap ) {
2009-03-24 22:23:04 +03:00
kfree ( blocks ) ;
2007-08-25 13:22:43 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
blocks - > start = start ;
blocks - > size = size ;
2007-08-25 14:23:09 +04:00
blocks - > file_priv = NULL ;
2005-04-17 02:20:36 +04:00
blocks - > next = blocks - > prev = * heap ;
2007-08-25 14:23:09 +04:00
( * heap ) - > file_priv = ( struct drm_file * ) - 1 ;
2005-04-17 02:20:36 +04:00
( * heap ) - > next = ( * heap ) - > prev = blocks ;
return 0 ;
}
/* Free all blocks associated with the releasing file.
*/
2007-08-25 14:23:09 +04:00
void radeon_mem_release ( struct drm_file * file_priv , struct mem_block * heap )
2005-04-17 02:20:36 +04:00
{
struct mem_block * p ;
if ( ! heap | | ! heap - > next )
return ;
list_for_each ( p , heap ) {
2007-08-25 14:23:09 +04:00
if ( p - > file_priv = = file_priv )
p - > file_priv = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-08-25 14:23:09 +04:00
/* Assumes a single contiguous range. Needs a special file_priv in
2005-04-17 02:20:36 +04:00
* ' heap ' to stop it being subsumed .
*/
list_for_each ( p , heap ) {
2008-03-29 00:23:06 +03:00
while ( p - > file_priv = = NULL & & p - > next - > file_priv = = NULL ) {
2005-04-17 02:20:36 +04:00
struct mem_block * q = p - > next ;
p - > size + = q - > size ;
p - > next = q - > next ;
p - > next - > prev = p ;
2009-03-24 22:23:04 +03:00
kfree ( q ) ;
2005-04-17 02:20:36 +04:00
}
}
}
/* Shutdown.
*/
2005-09-25 08:28:13 +04:00
void radeon_mem_takedown ( struct mem_block * * heap )
2005-04-17 02:20:36 +04:00
{
struct mem_block * p ;
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
if ( ! * heap )
return ;
2005-09-25 08:28:13 +04:00
for ( p = ( * heap ) - > next ; p ! = * heap ; ) {
2005-04-17 02:20:36 +04:00
struct mem_block * q = p ;
p = p - > next ;
2009-03-24 22:23:04 +03:00
kfree ( q ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-24 22:23:04 +03:00
kfree ( * heap ) ;
2005-04-17 02:20:36 +04:00
* heap = NULL ;
}
/* IOCTL HANDLERS */
2005-09-25 08:28:13 +04:00
static struct mem_block * * get_heap ( drm_radeon_private_t * dev_priv , int region )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
switch ( region ) {
2005-04-17 02:20:36 +04:00
case RADEON_MEM_REGION_GART :
2005-09-25 08:28:13 +04:00
return & dev_priv - > gart_heap ;
2005-04-17 02:20:36 +04:00
case RADEON_MEM_REGION_FB :
return & dev_priv - > fb_heap ;
default :
return NULL ;
}
}
2007-09-03 06:06:45 +04:00
int radeon_mem_alloc ( struct drm_device * dev , void * data , struct drm_file * file_priv )
2005-04-17 02:20:36 +04:00
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2007-09-03 06:06:45 +04:00
drm_radeon_mem_alloc_t * alloc = data ;
2005-04-17 02:20:36 +04:00
struct mem_block * block , * * heap ;
2005-09-25 08:28:13 +04:00
if ( ! dev_priv ) {
2008-01-24 08:58:57 +03:00
DRM_ERROR ( " called with no initialization \n " ) ;
2007-08-25 13:22:43 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-09-03 06:06:45 +04:00
heap = get_heap ( dev_priv , alloc - > region ) ;
2005-04-17 02:20:36 +04:00
if ( ! heap | | ! * heap )
2007-08-25 13:22:43 +04:00
return - EFAULT ;
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
/* Make things easier on ourselves: all allocations at least
* 4 k aligned .
*/
2007-09-03 06:06:45 +04:00
if ( alloc - > alignment < 12 )
alloc - > alignment = 12 ;
2005-04-17 02:20:36 +04:00
2007-09-03 06:06:45 +04:00
block = alloc_block ( * heap , alloc - > size , alloc - > alignment , file_priv ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
if ( ! block )
2007-08-25 13:22:43 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2013-12-11 14:34:44 +04:00
if ( copy_to_user ( alloc - > region_offset , & block - > start ,
2007-09-03 06:06:45 +04:00
sizeof ( int ) ) ) {
2005-09-25 08:28:13 +04:00
DRM_ERROR ( " copy_to_user \n " ) ;
2007-08-25 13:22:43 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-09-03 06:06:45 +04:00
int radeon_mem_free ( struct drm_device * dev , void * data , struct drm_file * file_priv )
2005-04-17 02:20:36 +04:00
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2007-09-03 06:06:45 +04:00
drm_radeon_mem_free_t * memfree = data ;
2005-04-17 02:20:36 +04:00
struct mem_block * block , * * heap ;
2005-09-25 08:28:13 +04:00
if ( ! dev_priv ) {
2008-01-24 08:58:57 +03:00
DRM_ERROR ( " called with no initialization \n " ) ;
2007-08-25 13:22:43 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-09-03 06:06:45 +04:00
heap = get_heap ( dev_priv , memfree - > region ) ;
2005-04-17 02:20:36 +04:00
if ( ! heap | | ! * heap )
2007-08-25 13:22:43 +04:00
return - EFAULT ;
2005-09-25 08:28:13 +04:00
2007-09-03 06:06:45 +04:00
block = find_block ( * heap , memfree - > region_offset ) ;
2005-04-17 02:20:36 +04:00
if ( ! block )
2007-08-25 13:22:43 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2007-08-25 14:23:09 +04:00
if ( block - > file_priv ! = file_priv )
2007-08-25 13:22:43 +04:00
return - EPERM ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
free_block ( block ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-09-03 06:06:45 +04:00
int radeon_mem_init_heap ( struct drm_device * dev , void * data , struct drm_file * file_priv )
2005-04-17 02:20:36 +04:00
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2007-09-03 06:06:45 +04:00
drm_radeon_mem_init_heap_t * initheap = data ;
2005-04-17 02:20:36 +04:00
struct mem_block * * heap ;
2005-09-25 08:28:13 +04:00
if ( ! dev_priv ) {
2008-01-24 08:58:57 +03:00
DRM_ERROR ( " called with no initialization \n " ) ;
2007-08-25 13:22:43 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-09-03 06:06:45 +04:00
heap = get_heap ( dev_priv , initheap - > region ) ;
2005-09-25 08:28:13 +04:00
if ( ! heap )
2007-08-25 13:22:43 +04:00
return - EFAULT ;
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
if ( * heap ) {
DRM_ERROR ( " heap already initialized? " ) ;
2007-08-25 13:22:43 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
2007-09-03 06:06:45 +04:00
return init_heap ( heap , initheap - > start , initheap - > size ) ;
2005-09-25 08:28:13 +04:00
}