2011-10-04 19:19:01 +09:00
/* exynos_drm_crtc.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 >
*
* 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 .
*/
# include "drmP.h"
# include "drm_crtc_helper.h"
# include "exynos_drm_drv.h"
# include "exynos_drm_fb.h"
# include "exynos_drm_encoder.h"
2011-10-14 13:29:46 +09:00
# include "exynos_drm_buf.h"
2011-10-04 19:19:01 +09:00
# define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
drm_crtc )
/*
2011-10-14 13:29:46 +09:00
* Exynos specific crtc postion structure .
*
* @ fb_x : offset x on a framebuffer to be displyed
* - the unit is screen coordinates .
* @ fb_y : offset y on a framebuffer to be displayed
* - the unit is screen coordinates .
* @ crtc_x : offset x on hardware screen .
* @ crtc_y : offset y on hardware screen .
* @ crtc_w : width of hardware screen .
* @ crtc_h : height of hardware screen .
2011-10-04 19:19:01 +09:00
*/
struct exynos_drm_crtc_pos {
unsigned int fb_x ;
unsigned int fb_y ;
2011-10-14 13:29:46 +09:00
unsigned int crtc_x ;
unsigned int crtc_y ;
2011-10-04 19:19:01 +09:00
unsigned int crtc_w ;
unsigned int crtc_h ;
} ;
/*
* Exynos specific crtc structure .
*
* @ drm_crtc : crtc object .
* @ overlay : contain information common to display controller and hdmi and
* contents of this overlay object would be copied to sub driver size .
* @ pipe : a crtc index created at load ( ) with a new crtc object creation
* and the crtc object would be set to private - > crtc array
* to get a crtc object corresponding to this pipe from private - > crtc
* array when irq interrupt occured . the reason of using this pipe is that
* drm framework doesn ' t support multiple irq yet .
* we can refer to the crtc to current hardware interrupt occured through
* this pipe value .
*/
struct exynos_drm_crtc {
struct drm_crtc drm_crtc ;
struct exynos_drm_overlay overlay ;
unsigned int pipe ;
} ;
2011-10-14 13:29:47 +09:00
static void exynos_drm_crtc_apply ( struct drm_crtc * crtc )
2011-10-04 19:19:01 +09:00
{
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
struct exynos_drm_overlay * overlay = & exynos_crtc - > overlay ;
exynos_drm_fn_encoder ( crtc , overlay ,
exynos_drm_encoder_crtc_mode_set ) ;
exynos_drm_fn_encoder ( crtc , NULL , exynos_drm_encoder_crtc_commit ) ;
}
2011-10-14 13:29:46 +09:00
static int exynos_drm_overlay_update ( struct exynos_drm_overlay * overlay ,
2011-10-04 19:19:01 +09:00
struct drm_framebuffer * fb ,
struct drm_display_mode * mode ,
struct exynos_drm_crtc_pos * pos )
{
2011-10-14 13:29:46 +09:00
struct exynos_drm_buf_entry * entry ;
unsigned int actual_w ;
unsigned int actual_h ;
entry = exynos_drm_fb_get_buf ( fb ) ;
if ( ! entry ) {
DRM_LOG_KMS ( " entry is null. \n " ) ;
return - EFAULT ;
}
overlay - > paddr = entry - > paddr ;
overlay - > vaddr = entry - > vaddr ;
DRM_DEBUG_KMS ( " vaddr = 0x%lx, paddr = 0x%lx \n " ,
( unsigned long ) overlay - > vaddr ,
( unsigned long ) overlay - > paddr ) ;
actual_w = min ( ( mode - > hdisplay - pos - > crtc_x ) , pos - > crtc_w ) ;
actual_h = min ( ( mode - > vdisplay - pos - > crtc_y ) , pos - > crtc_h ) ;
/* set drm framebuffer data. */
overlay - > fb_x = pos - > fb_x ;
overlay - > fb_y = pos - > fb_y ;
overlay - > fb_width = fb - > width ;
overlay - > fb_height = fb - > height ;
2011-10-04 19:19:01 +09:00
overlay - > bpp = fb - > bits_per_pixel ;
2011-10-14 13:29:46 +09:00
overlay - > pitch = fb - > pitch ;
/* set overlay range to be displayed. */
overlay - > crtc_x = pos - > crtc_x ;
overlay - > crtc_y = pos - > crtc_y ;
overlay - > crtc_width = actual_w ;
overlay - > crtc_height = actual_h ;
/* set drm mode data. */
overlay - > mode_width = mode - > hdisplay ;
overlay - > mode_height = mode - > vdisplay ;
overlay - > refresh = mode - > vrefresh ;
overlay - > scan_flag = mode - > flags ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " overlay : offset_x/y(%d,%d), width/height(%d,%d) " ,
2011-10-14 13:29:46 +09:00
overlay - > crtc_x , overlay - > crtc_y ,
overlay - > crtc_width , overlay - > crtc_height ) ;
2011-10-04 19:19:01 +09:00
2011-10-14 13:29:46 +09:00
return 0 ;
2011-10-04 19:19:01 +09:00
}
static int exynos_drm_crtc_update ( struct drm_crtc * crtc )
{
struct exynos_drm_crtc * exynos_crtc ;
struct exynos_drm_overlay * overlay ;
struct exynos_drm_crtc_pos pos ;
struct drm_display_mode * mode = & crtc - > mode ;
struct drm_framebuffer * fb = crtc - > fb ;
if ( ! mode | | ! fb )
return - EINVAL ;
exynos_crtc = to_exynos_crtc ( crtc ) ;
overlay = & exynos_crtc - > overlay ;
memset ( & pos , 0 , sizeof ( struct exynos_drm_crtc_pos ) ) ;
2011-10-14 13:29:46 +09:00
/* it means the offset of framebuffer to be displayed. */
2011-10-04 19:19:01 +09:00
pos . fb_x = crtc - > x ;
pos . fb_y = crtc - > y ;
2011-10-14 13:29:46 +09:00
/* OSD position to be displayed. */
pos . crtc_x = 0 ;
pos . crtc_y = 0 ;
2011-10-04 19:19:01 +09:00
pos . crtc_w = fb - > width - crtc - > x ;
pos . crtc_h = fb - > height - crtc - > y ;
2011-10-14 13:29:46 +09:00
return exynos_drm_overlay_update ( overlay , crtc - > fb , mode , & pos ) ;
2011-10-04 19:19:01 +09:00
}
static void exynos_drm_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* TODO */
}
static void exynos_drm_crtc_prepare ( struct drm_crtc * crtc )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* drm framework doesn't check NULL. */
}
static void exynos_drm_crtc_commit ( struct drm_crtc * crtc )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* drm framework doesn't check NULL. */
}
static bool
exynos_drm_crtc_mode_fixup ( struct drm_crtc * crtc ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* drm framework doesn't check NULL */
return true ;
}
static int
exynos_drm_crtc_mode_set ( struct drm_crtc * crtc , struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode , int x , int y ,
struct drm_framebuffer * old_fb )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
mode = adjusted_mode ;
return exynos_drm_crtc_update ( crtc ) ;
}
static int exynos_drm_crtc_mode_set_base ( struct drm_crtc * crtc , int x , int y ,
struct drm_framebuffer * old_fb )
{
int ret ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
ret = exynos_drm_crtc_update ( crtc ) ;
if ( ret )
return ret ;
exynos_drm_crtc_apply ( crtc ) ;
return ret ;
}
static void exynos_drm_crtc_load_lut ( struct drm_crtc * crtc )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* drm framework doesn't check NULL */
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
. dpms = exynos_drm_crtc_dpms ,
. prepare = exynos_drm_crtc_prepare ,
. commit = exynos_drm_crtc_commit ,
. mode_fixup = exynos_drm_crtc_mode_fixup ,
. mode_set = exynos_drm_crtc_mode_set ,
. mode_set_base = exynos_drm_crtc_mode_set_base ,
. load_lut = exynos_drm_crtc_load_lut ,
} ;
static int exynos_drm_crtc_page_flip ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
struct drm_pending_vblank_event * event )
{
struct drm_device * dev = crtc - > dev ;
struct exynos_drm_private * dev_priv = dev - > dev_private ;
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
struct drm_framebuffer * old_fb = crtc - > fb ;
int ret = - EINVAL ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
mutex_lock ( & dev - > struct_mutex ) ;
2011-10-14 13:29:51 +09:00
if ( event ) {
/*
* the pipe from user always is 0 so we can set pipe number
* of current owner to event .
*/
event - > pipe = exynos_crtc - > pipe ;
2011-10-04 19:19:01 +09:00
list_add_tail ( & event - > base . link ,
& dev_priv - > pageflip_event_list ) ;
ret = drm_vblank_get ( dev , exynos_crtc - > pipe ) ;
if ( ret ) {
DRM_DEBUG ( " failed to acquire vblank counter \n " ) ;
2011-10-14 13:29:51 +09:00
list_del ( & event - > base . link ) ;
2011-10-04 19:19:01 +09:00
goto out ;
}
crtc - > fb = fb ;
ret = exynos_drm_crtc_update ( crtc ) ;
if ( ret ) {
crtc - > fb = old_fb ;
drm_vblank_put ( dev , exynos_crtc - > pipe ) ;
2011-10-14 13:29:51 +09:00
list_del ( & event - > base . link ) ;
2011-10-04 19:19:01 +09:00
goto out ;
}
2011-10-14 13:29:50 +09:00
/*
* the values related to a buffer of the drm framebuffer
* to be applied should be set at here . because these values
2011-10-14 13:29:51 +09:00
* first , are set to shadow registers and then to
2011-10-14 13:29:50 +09:00
* real registers at vsync front porch period .
*/
2011-10-14 13:29:47 +09:00
exynos_drm_crtc_apply ( crtc ) ;
2011-10-04 19:19:01 +09:00
}
out :
mutex_unlock ( & dev - > struct_mutex ) ;
return ret ;
}
static void exynos_drm_crtc_destroy ( struct drm_crtc * crtc )
{
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
struct exynos_drm_private * private = crtc - > dev - > dev_private ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
private - > crtc [ exynos_crtc - > pipe ] = NULL ;
drm_crtc_cleanup ( crtc ) ;
kfree ( exynos_crtc ) ;
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
. set_config = drm_crtc_helper_set_config ,
. page_flip = exynos_drm_crtc_page_flip ,
. destroy = exynos_drm_crtc_destroy ,
} ;
struct exynos_drm_overlay * get_exynos_drm_overlay ( struct drm_device * dev ,
struct drm_crtc * crtc )
{
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
return & exynos_crtc - > overlay ;
}
int exynos_drm_crtc_create ( struct drm_device * dev , unsigned int nr )
{
struct exynos_drm_crtc * exynos_crtc ;
struct exynos_drm_private * private = dev - > dev_private ;
struct drm_crtc * crtc ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
exynos_crtc = kzalloc ( sizeof ( * exynos_crtc ) , GFP_KERNEL ) ;
if ( ! exynos_crtc ) {
DRM_ERROR ( " failed to allocate exynos crtc \n " ) ;
return - ENOMEM ;
}
exynos_crtc - > pipe = nr ;
crtc = & exynos_crtc - > drm_crtc ;
private - > crtc [ nr ] = crtc ;
drm_crtc_init ( dev , crtc , & exynos_crtc_funcs ) ;
drm_crtc_helper_add ( crtc , & exynos_crtc_helper_funcs ) ;
return 0 ;
}
int exynos_drm_crtc_enable_vblank ( struct drm_device * dev , int crtc )
{
struct exynos_drm_private * private = dev - > dev_private ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
exynos_drm_fn_encoder ( private - > crtc [ crtc ] , & crtc ,
exynos_drm_enable_vblank ) ;
return 0 ;
}
void exynos_drm_crtc_disable_vblank ( struct drm_device * dev , int crtc )
{
struct exynos_drm_private * private = dev - > dev_private ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
exynos_drm_fn_encoder ( private - > crtc [ crtc ] , & crtc ,
exynos_drm_disable_vblank ) ;
}
MODULE_AUTHOR ( " Inki Dae <inki.dae@samsung.com> " ) ;
MODULE_AUTHOR ( " Joonyoung Shim <jy0922.shim@samsung.com> " ) ;
MODULE_AUTHOR ( " Seung-Woo Kim <sw0312.kim@samsung.com> " ) ;
MODULE_DESCRIPTION ( " Samsung SoC DRM CRTC Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;