2011-10-04 19:19:01 +09:00
/* exynos_drm_buf.c
*
* Copyright ( c ) 2011 Samsung Electronics Co . , Ltd .
* Author : Inki Dae < inki . dae @ samsung . com >
*
* 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
* VA LINUX SYSTEMS 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 .
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/exynos_drm.h>
2011-10-04 19:19:01 +09:00
# include "exynos_drm_drv.h"
2011-11-12 15:23:32 +09:00
# include "exynos_drm_gem.h"
2011-10-04 19:19:01 +09:00
# include "exynos_drm_buf.h"
static int lowlevel_buffer_allocate ( struct drm_device * dev ,
2012-03-16 18:47:05 +09:00
unsigned int flags , struct exynos_drm_gem_buf * buf )
2011-10-04 19:19:01 +09:00
{
2012-04-03 21:49:15 +09:00
dma_addr_t start_addr ;
2012-04-23 21:01:28 +09:00
unsigned int npages , i = 0 ;
2012-03-16 18:47:05 +09:00
struct scatterlist * sgl ;
int ret = 0 ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2012-04-03 21:27:58 +09:00
if ( IS_NONCONTIG_BUFFER ( flags ) ) {
2012-03-16 18:47:05 +09:00
DRM_DEBUG_KMS ( " not support allocation type. \n " ) ;
return - EINVAL ;
}
if ( buf - > dma_addr ) {
DRM_DEBUG_KMS ( " already allocated. \n " ) ;
return 0 ;
}
if ( buf - > size > = SZ_1M ) {
2012-04-03 21:27:58 +09:00
npages = buf - > size > > SECTION_SHIFT ;
2012-04-23 21:01:28 +09:00
buf - > page_size = SECTION_SIZE ;
2012-03-16 18:47:05 +09:00
} else if ( buf - > size > = SZ_64K ) {
2012-04-03 21:27:58 +09:00
npages = buf - > size > > 16 ;
2012-04-23 21:01:28 +09:00
buf - > page_size = SZ_64K ;
2012-03-16 18:47:05 +09:00
} else {
2012-04-03 21:27:58 +09:00
npages = buf - > size > > PAGE_SHIFT ;
2012-04-23 21:01:28 +09:00
buf - > page_size = PAGE_SIZE ;
2012-03-16 18:47:05 +09:00
}
buf - > sgt = kzalloc ( sizeof ( struct sg_table ) , GFP_KERNEL ) ;
if ( ! buf - > sgt ) {
DRM_ERROR ( " failed to allocate sg table. \n " ) ;
2011-10-04 19:19:01 +09:00
return - ENOMEM ;
}
2012-03-16 18:47:05 +09:00
ret = sg_alloc_table ( buf - > sgt , npages , GFP_KERNEL ) ;
if ( ret < 0 ) {
DRM_ERROR ( " failed to initialize sg table. \n " ) ;
kfree ( buf - > sgt ) ;
buf - > sgt = NULL ;
return - ENOMEM ;
}
2011-10-04 19:19:01 +09:00
2012-04-03 21:49:15 +09:00
buf - > kvaddr = dma_alloc_writecombine ( dev - > dev , buf - > size ,
& buf - > dma_addr , GFP_KERNEL ) ;
if ( ! buf - > kvaddr ) {
DRM_ERROR ( " failed to allocate buffer. \n " ) ;
ret = - ENOMEM ;
goto err1 ;
}
2012-03-16 18:47:05 +09:00
buf - > pages = kzalloc ( sizeof ( struct page ) * npages , GFP_KERNEL ) ;
if ( ! buf - > pages ) {
DRM_ERROR ( " failed to allocate pages. \n " ) ;
ret = - ENOMEM ;
goto err2 ;
}
sgl = buf - > sgt - > sgl ;
2012-04-03 21:49:15 +09:00
start_addr = buf - > dma_addr ;
2012-03-16 18:47:05 +09:00
while ( i < npages ) {
buf - > pages [ i ] = phys_to_page ( start_addr ) ;
2012-04-23 21:01:28 +09:00
sg_set_page ( sgl , buf - > pages [ i ] , buf - > page_size , 0 ) ;
2012-03-16 18:47:05 +09:00
sg_dma_address ( sgl ) = start_addr ;
2012-04-23 21:01:28 +09:00
start_addr + = buf - > page_size ;
2012-03-16 18:47:05 +09:00
sgl = sg_next ( sgl ) ;
i + + ;
}
DRM_DEBUG_KMS ( " vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx) \n " ,
( unsigned long ) buf - > kvaddr ,
( unsigned long ) buf - > dma_addr ,
buf - > size ) ;
return ret ;
err2 :
dma_free_writecombine ( dev - > dev , buf - > size , buf - > kvaddr ,
( dma_addr_t ) buf - > dma_addr ) ;
buf - > dma_addr = ( dma_addr_t ) NULL ;
err1 :
sg_free_table ( buf - > sgt ) ;
kfree ( buf - > sgt ) ;
buf - > sgt = NULL ;
return ret ;
2011-10-04 19:19:01 +09:00
}
static void lowlevel_buffer_deallocate ( struct drm_device * dev ,
2012-03-16 18:47:05 +09:00
unsigned int flags , struct exynos_drm_gem_buf * buf )
2011-10-04 19:19:01 +09:00
{
DRM_DEBUG_KMS ( " %s. \n " , __FILE__ ) ;
2012-03-16 18:47:05 +09:00
/*
* release only physically continuous memory and
* non - continuous memory would be released by exynos
* gem framework .
*/
2012-04-03 21:27:58 +09:00
if ( IS_NONCONTIG_BUFFER ( flags ) ) {
2012-03-16 18:47:05 +09:00
DRM_DEBUG_KMS ( " not support allocation type. \n " ) ;
return ;
}
if ( ! buf - > dma_addr ) {
DRM_DEBUG_KMS ( " dma_addr is invalid. \n " ) ;
return ;
}
DRM_DEBUG_KMS ( " vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx) \n " ,
( unsigned long ) buf - > kvaddr ,
( unsigned long ) buf - > dma_addr ,
buf - > size ) ;
sg_free_table ( buf - > sgt ) ;
kfree ( buf - > sgt ) ;
buf - > sgt = NULL ;
kfree ( buf - > pages ) ;
buf - > pages = NULL ;
dma_free_writecombine ( dev - > dev , buf - > size , buf - > kvaddr ,
( dma_addr_t ) buf - > dma_addr ) ;
buf - > dma_addr = ( dma_addr_t ) NULL ;
2011-10-04 19:19:01 +09:00
}
2012-03-16 18:47:05 +09:00
struct exynos_drm_gem_buf * exynos_drm_init_buf ( struct drm_device * dev ,
unsigned int size )
2011-10-04 19:19:01 +09:00
{
2011-11-12 15:23:32 +09:00
struct exynos_drm_gem_buf * buffer ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " %s. \n " , __FILE__ ) ;
2011-11-12 15:23:32 +09:00
DRM_DEBUG_KMS ( " desired size = 0x%x \n " , size ) ;
2011-10-04 19:19:01 +09:00
2011-11-12 15:23:32 +09:00
buffer = kzalloc ( sizeof ( * buffer ) , GFP_KERNEL ) ;
if ( ! buffer ) {
DRM_ERROR ( " failed to allocate exynos_drm_gem_buf. \n " ) ;
2011-12-13 14:20:23 +09:00
return NULL ;
2011-10-04 19:19:01 +09:00
}
2011-11-12 15:23:32 +09:00
buffer - > size = size ;
return buffer ;
2011-10-04 19:19:01 +09:00
}
2012-03-16 18:47:05 +09:00
void exynos_drm_fini_buf ( struct drm_device * dev ,
struct exynos_drm_gem_buf * buffer )
2011-10-04 19:19:01 +09:00
{
DRM_DEBUG_KMS ( " %s. \n " , __FILE__ ) ;
2011-11-12 15:23:32 +09:00
if ( ! buffer ) {
DRM_DEBUG_KMS ( " buffer is null. \n " ) ;
2011-10-04 19:19:01 +09:00
return ;
}
2011-11-12 15:23:32 +09:00
kfree ( buffer ) ;
buffer = NULL ;
2011-10-04 19:19:01 +09:00
}
2012-03-16 18:47:05 +09:00
int exynos_drm_alloc_buf ( struct drm_device * dev ,
struct exynos_drm_gem_buf * buf , unsigned int flags )
{
/*
* allocate memory region and set the memory information
* to vaddr and dma_addr of a buffer object .
*/
if ( lowlevel_buffer_allocate ( dev , flags , buf ) < 0 )
return - ENOMEM ;
return 0 ;
}
void exynos_drm_free_buf ( struct drm_device * dev ,
unsigned int flags , struct exynos_drm_gem_buf * buffer )
{
lowlevel_buffer_deallocate ( dev , flags , buffer ) ;
}