2011-10-04 19:19:01 +09:00
/* exynos_drm_fb.c
*
* Copyright ( c ) 2011 Samsung Electronics Co . , Ltd .
* Authors :
* Inki Dae < inki . dae @ samsung . com >
* Joonyoung Shim < jy0922 . shim @ samsung . com >
* Seung - Woo Kim < sw0312 . kim @ samsung . com >
*
2012-12-18 02:30:17 +09:00
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
2011-10-04 19:19:01 +09:00
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_fb_helper.h>
2012-10-20 07:53:42 -07:00
# include <uapi/drm/exynos_drm.h>
2011-10-04 19:19:01 +09:00
2011-10-18 16:58:05 +09:00
# include "exynos_drm_drv.h"
2011-10-04 19:19:01 +09:00
# include "exynos_drm_fb.h"
2014-03-17 11:27:17 +01:00
# include "exynos_drm_fbdev.h"
2011-10-04 19:19:01 +09:00
# include "exynos_drm_gem.h"
2012-10-20 07:53:42 -07:00
# include "exynos_drm_iommu.h"
2014-02-19 21:02:55 +09:00
# include "exynos_drm_crtc.h"
2011-10-04 19:19:01 +09:00
# define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
/*
* exynos specific framebuffer structure .
*
* @ fb : drm framebuffer obejct .
2012-08-20 20:05:56 +09:00
* @ buf_cnt : a buffer count to drm framebuffer .
2011-12-15 14:36:22 +09:00
* @ exynos_gem_obj : array of exynos specific gem object containing a gem object .
2011-10-04 19:19:01 +09:00
*/
struct exynos_drm_fb {
struct drm_framebuffer fb ;
2012-08-20 20:05:56 +09:00
unsigned int buf_cnt ;
2011-12-15 14:36:22 +09:00
struct exynos_drm_gem_obj * exynos_gem_obj [ MAX_FB_BUFFER ] ;
2011-10-04 19:19:01 +09:00
} ;
2012-10-20 07:53:42 -07:00
static int check_fb_gem_memory_type ( struct drm_device * drm_dev ,
struct exynos_drm_gem_obj * exynos_gem_obj )
{
unsigned int flags ;
/*
* if exynos drm driver supports iommu then framebuffer can use
* all the buffer types .
*/
if ( is_drm_iommu_supported ( drm_dev ) )
return 0 ;
flags = exynos_gem_obj - > flags ;
/*
* without iommu support , not support physically non - continuous memory
* for framebuffer .
*/
if ( IS_NONCONTIG_BUFFER ( flags ) ) {
DRM_ERROR ( " cannot use this gem memory type for fb. \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2011-10-04 19:19:01 +09:00
static void exynos_drm_fb_destroy ( struct drm_framebuffer * fb )
{
struct exynos_drm_fb * exynos_fb = to_exynos_fb ( fb ) ;
2012-05-16 17:08:56 +02:00
unsigned int i ;
2011-10-04 19:19:01 +09:00
2012-11-22 17:41:23 +09:00
/* make sure that overlay data are updated before relesing fb. */
2014-02-19 21:02:55 +09:00
exynos_drm_crtc_complete_scanout ( fb ) ;
2012-11-22 17:41:23 +09:00
2011-10-04 19:19:01 +09:00
drm_framebuffer_cleanup ( fb ) ;
2012-05-16 17:08:56 +02:00
for ( i = 0 ; i < ARRAY_SIZE ( exynos_fb - > exynos_gem_obj ) ; i + + ) {
struct drm_gem_object * obj ;
if ( exynos_fb - > exynos_gem_obj [ i ] = = NULL )
continue ;
obj = & exynos_fb - > exynos_gem_obj [ i ] - > base ;
drm_gem_object_unreference_unlocked ( obj ) ;
}
2011-10-04 19:19:01 +09:00
kfree ( exynos_fb ) ;
exynos_fb = NULL ;
}
static int exynos_drm_fb_create_handle ( struct drm_framebuffer * fb ,
struct drm_file * file_priv ,
unsigned int * handle )
{
struct exynos_drm_fb * exynos_fb = to_exynos_fb ( fb ) ;
2013-01-29 17:51:09 +09:00
/* This fb should have only one gem object. */
if ( WARN_ON ( exynos_fb - > buf_cnt ! = 1 ) )
return - EINVAL ;
2011-10-04 19:19:01 +09:00
return drm_gem_handle_create ( file_priv ,
2011-12-15 14:36:22 +09:00
& exynos_fb - > exynos_gem_obj [ 0 ] - > base , handle ) ;
2011-10-04 19:19:01 +09:00
}
static int exynos_drm_fb_dirty ( struct drm_framebuffer * fb ,
struct drm_file * file_priv , unsigned flags ,
unsigned color , struct drm_clip_rect * clips ,
unsigned num_clips )
{
/* TODO */
return 0 ;
}
static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
. destroy = exynos_drm_fb_destroy ,
. create_handle = exynos_drm_fb_create_handle ,
. dirty = exynos_drm_fb_dirty ,
} ;
2012-08-20 20:05:56 +09:00
void exynos_drm_fb_set_buf_cnt ( struct drm_framebuffer * fb ,
unsigned int cnt )
{
struct exynos_drm_fb * exynos_fb ;
exynos_fb = to_exynos_fb ( fb ) ;
exynos_fb - > buf_cnt = cnt ;
}
unsigned int exynos_drm_fb_get_buf_cnt ( struct drm_framebuffer * fb )
{
struct exynos_drm_fb * exynos_fb ;
exynos_fb = to_exynos_fb ( fb ) ;
return exynos_fb - > buf_cnt ;
}
2011-12-13 14:46:57 +09:00
struct drm_framebuffer *
exynos_drm_framebuffer_init ( struct drm_device * dev ,
struct drm_mode_fb_cmd2 * mode_cmd ,
struct drm_gem_object * obj )
2011-10-04 19:19:01 +09:00
{
struct exynos_drm_fb * exynos_fb ;
2012-10-20 07:53:42 -07:00
struct exynos_drm_gem_obj * exynos_gem_obj ;
2011-10-04 19:19:01 +09:00
int ret ;
2012-10-20 07:53:42 -07:00
exynos_gem_obj = to_exynos_gem_obj ( obj ) ;
ret = check_fb_gem_memory_type ( dev , exynos_gem_obj ) ;
if ( ret < 0 ) {
DRM_ERROR ( " cannot use this gem memory type for fb. \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2011-10-04 19:19:01 +09:00
exynos_fb = kzalloc ( sizeof ( * exynos_fb ) , GFP_KERNEL ) ;
2013-08-19 19:04:55 +09:00
if ( ! exynos_fb )
2011-10-04 19:19:01 +09:00
return ERR_PTR ( - ENOMEM ) ;
2012-12-14 13:39:03 +09:00
drm_helper_mode_fill_fb_struct ( & exynos_fb - > fb , mode_cmd ) ;
2012-10-20 07:53:42 -07:00
exynos_fb - > exynos_gem_obj [ 0 ] = exynos_gem_obj ;
2011-12-13 14:46:57 +09:00
ret = drm_framebuffer_init ( dev , & exynos_fb - > fb , & exynos_drm_fb_funcs ) ;
2011-10-04 19:19:01 +09:00
if ( ret ) {
2011-12-13 14:46:57 +09:00
DRM_ERROR ( " failed to initialize framebuffer \n " ) ;
return ERR_PTR ( ret ) ;
2011-10-04 19:19:01 +09:00
}
2011-12-13 14:46:57 +09:00
return & exynos_fb - > fb ;
2011-10-04 19:19:01 +09:00
}
2012-08-20 20:05:56 +09:00
static u32 exynos_drm_format_num_buffers ( struct drm_mode_fb_cmd2 * mode_cmd )
{
unsigned int cnt = 0 ;
if ( mode_cmd - > pixel_format ! = DRM_FORMAT_NV12 )
return drm_format_num_planes ( mode_cmd - > pixel_format ) ;
while ( cnt ! = MAX_FB_BUFFER ) {
if ( ! mode_cmd - > handles [ cnt ] )
break ;
cnt + + ;
}
/*
* check if NV12 or NV12M .
*
* NV12
* handles [ 0 ] = base1 , offsets [ 0 ] = 0
* handles [ 1 ] = base1 , offsets [ 1 ] = Y_size
*
* NV12M
* handles [ 0 ] = base1 , offsets [ 0 ] = 0
* handles [ 1 ] = base2 , offsets [ 1 ] = 0
*/
if ( cnt = = 2 ) {
/*
* in case of NV12 format , offsets [ 1 ] is not 0 and
* handles [ 0 ] is same as handles [ 1 ] .
*/
if ( mode_cmd - > offsets [ 1 ] & &
mode_cmd - > handles [ 0 ] = = mode_cmd - > handles [ 1 ] )
cnt = 1 ;
}
return cnt ;
}
2011-12-13 14:46:57 +09:00
static struct drm_framebuffer *
exynos_user_fb_create ( struct drm_device * dev , struct drm_file * file_priv ,
struct drm_mode_fb_cmd2 * mode_cmd )
2011-10-04 19:19:01 +09:00
{
2011-12-13 14:46:57 +09:00
struct drm_gem_object * obj ;
2013-02-12 21:23:54 +09:00
struct exynos_drm_gem_obj * exynos_gem_obj ;
2011-12-15 14:36:22 +09:00
struct exynos_drm_fb * exynos_fb ;
2012-12-14 13:39:03 +09:00
int i , ret ;
2011-12-13 14:46:57 +09:00
2012-12-14 13:39:03 +09:00
exynos_fb = kzalloc ( sizeof ( * exynos_fb ) , GFP_KERNEL ) ;
2013-08-19 19:04:55 +09:00
if ( ! exynos_fb )
2012-12-14 13:39:03 +09:00
return ERR_PTR ( - ENOMEM ) ;
2011-12-15 14:36:22 +09:00
2013-02-12 21:23:54 +09:00
obj = drm_gem_object_lookup ( dev , file_priv , mode_cmd - > handles [ 0 ] ) ;
if ( ! obj ) {
DRM_ERROR ( " failed to lookup gem object \n " ) ;
ret = - ENOENT ;
goto err_free ;
}
2012-12-14 13:39:03 +09:00
drm_helper_mode_fill_fb_struct ( & exynos_fb - > fb , mode_cmd ) ;
exynos_fb - > exynos_gem_obj [ 0 ] = to_exynos_gem_obj ( obj ) ;
2012-08-20 20:05:56 +09:00
exynos_fb - > buf_cnt = exynos_drm_format_num_buffers ( mode_cmd ) ;
DRM_DEBUG_KMS ( " buf_cnt = %d \n " , exynos_fb - > buf_cnt ) ;
2011-12-15 14:36:22 +09:00
2012-08-20 20:05:56 +09:00
for ( i = 1 ; i < exynos_fb - > buf_cnt ; i + + ) {
2011-12-15 14:36:22 +09:00
obj = drm_gem_object_lookup ( dev , file_priv ,
mode_cmd - > handles [ i ] ) ;
if ( ! obj ) {
DRM_ERROR ( " failed to lookup gem object \n " ) ;
2013-02-12 21:23:54 +09:00
ret = - ENOENT ;
exynos_fb - > buf_cnt = i ;
goto err_unreference ;
2011-12-15 14:36:22 +09:00
}
2012-10-20 07:53:42 -07:00
exynos_gem_obj = to_exynos_gem_obj ( obj ) ;
2013-02-12 21:23:54 +09:00
exynos_fb - > exynos_gem_obj [ i ] = exynos_gem_obj ;
2012-10-20 07:53:42 -07:00
ret = check_fb_gem_memory_type ( dev , exynos_gem_obj ) ;
if ( ret < 0 ) {
DRM_ERROR ( " cannot use this gem memory type for fb. \n " ) ;
2013-02-12 21:23:54 +09:00
goto err_unreference ;
2012-10-20 07:53:42 -07:00
}
2011-12-15 14:36:22 +09:00
}
2012-12-14 13:39:03 +09:00
ret = drm_framebuffer_init ( dev , & exynos_fb - > fb , & exynos_drm_fb_funcs ) ;
if ( ret ) {
2013-02-12 21:23:54 +09:00
DRM_ERROR ( " failed to init framebuffer. \n " ) ;
goto err_unreference ;
2012-12-14 13:39:03 +09:00
}
return & exynos_fb - > fb ;
2013-02-12 21:23:54 +09:00
err_unreference :
for ( i = 0 ; i < exynos_fb - > buf_cnt ; i + + ) {
struct drm_gem_object * obj ;
obj = & exynos_fb - > exynos_gem_obj [ i ] - > base ;
if ( obj )
drm_gem_object_unreference_unlocked ( obj ) ;
}
err_free :
kfree ( exynos_fb ) ;
return ERR_PTR ( ret ) ;
2011-10-04 19:19:01 +09:00
}
2011-12-15 14:36:22 +09:00
struct exynos_drm_gem_buf * exynos_drm_fb_buffer ( struct drm_framebuffer * fb ,
int index )
2011-10-04 19:19:01 +09:00
{
struct exynos_drm_fb * exynos_fb = to_exynos_fb ( fb ) ;
2011-11-12 15:23:32 +09:00
struct exynos_drm_gem_buf * buffer ;
2011-10-04 19:19:01 +09:00
2011-12-15 14:36:22 +09:00
if ( index > = MAX_FB_BUFFER )
return NULL ;
buffer = exynos_fb - > exynos_gem_obj [ index ] - > buffer ;
2011-11-12 15:23:32 +09:00
if ( ! buffer )
2011-10-14 13:29:46 +09:00
return NULL ;
2011-10-04 19:19:01 +09:00
2012-12-07 17:51:27 +09:00
DRM_DEBUG_KMS ( " dma_addr = 0x%lx \n " , ( unsigned long ) buffer - > dma_addr ) ;
2011-10-04 19:19:01 +09:00
2011-11-12 15:23:32 +09:00
return buffer ;
2011-10-04 19:19:01 +09:00
}
2011-10-18 16:58:05 +09:00
static void exynos_drm_output_poll_changed ( struct drm_device * dev )
{
struct exynos_drm_private * private = dev - > dev_private ;
struct drm_fb_helper * fb_helper = private - > fb_helper ;
if ( fb_helper )
drm_fb_helper_hotplug_event ( fb_helper ) ;
2014-03-17 11:27:17 +01:00
else
exynos_drm_fbdev_init ( dev ) ;
2011-10-18 16:58:05 +09:00
}
2012-05-17 13:27:23 +02:00
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
2011-12-13 14:46:57 +09:00
. fb_create = exynos_user_fb_create ,
2011-10-18 16:58:05 +09:00
. output_poll_changed = exynos_drm_output_poll_changed ,
2011-10-04 19:19:01 +09:00
} ;
void exynos_drm_mode_config_init ( struct drm_device * dev )
{
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
/*
* set max width and height as default value ( 4096 x4096 ) .
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb ( ) .
*/
dev - > mode_config . max_width = 4096 ;
dev - > mode_config . max_height = 4096 ;
dev - > mode_config . funcs = & exynos_drm_mode_config_funcs ;
}