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_encoder.h"
2012-06-27 14:27:04 +09:00
# include "exynos_drm_plane.h"
2011-10-04 19:19:01 +09:00
# define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
drm_crtc )
2012-06-27 14:27:11 +09:00
enum exynos_crtc_mode {
CRTC_MODE_NORMAL , /* normal mode */
CRTC_MODE_BLANK , /* The private plane of crtc is blank */
} ;
2011-10-04 19:19:01 +09:00
/*
* Exynos specific crtc structure .
*
* @ drm_crtc : crtc object .
2012-06-27 14:27:04 +09:00
* @ drm_plane : pointer of private plane object for this crtc
2011-10-04 19:19:01 +09:00
* @ 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 .
2011-12-06 11:06:54 +09:00
* @ dpms : store the crtc dpms value
2012-06-27 14:27:11 +09:00
* @ mode : store the crtc mode value
2011-10-04 19:19:01 +09:00
*/
struct exynos_drm_crtc {
struct drm_crtc drm_crtc ;
2012-06-27 14:27:04 +09:00
struct drm_plane * plane ;
2011-10-04 19:19:01 +09:00
unsigned int pipe ;
2011-12-06 11:06:54 +09:00
unsigned int dpms ;
2012-06-27 14:27:11 +09:00
enum exynos_crtc_mode mode ;
2011-10-04 19:19:01 +09:00
} ;
static void exynos_drm_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
2011-11-04 17:04:45 +09:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2011-10-04 19:19:01 +09:00
2011-11-04 17:04:45 +09:00
DRM_DEBUG_KMS ( " crtc[%d] mode[%d] \n " , crtc - > base . id , mode ) ;
2011-12-06 11:06:54 +09:00
if ( exynos_crtc - > dpms = = mode ) {
DRM_DEBUG_KMS ( " desired dpms mode is same as previous one. \n " ) ;
return ;
}
2012-06-27 14:27:09 +09:00
exynos_drm_fn_encoder ( crtc , & mode , exynos_drm_encoder_crtc_dpms ) ;
exynos_crtc - > dpms = mode ;
2011-10-04 19:19:01 +09:00
}
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 )
{
2011-11-04 17:04:45 +09:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2012-08-20 21:29:25 +09:00
exynos_drm_crtc_dpms ( crtc , DRM_MODE_DPMS_ON ) ;
2012-06-27 14:27:05 +09:00
exynos_plane_commit ( exynos_crtc - > plane ) ;
2012-06-27 14:27:09 +09:00
exynos_plane_dpms ( exynos_crtc - > plane , DRM_MODE_DPMS_ON ) ;
2011-10-04 19:19:01 +09:00
}
static bool
exynos_drm_crtc_mode_fixup ( struct drm_crtc * crtc ,
2012-07-17 17:56:50 +02:00
const struct drm_display_mode * mode ,
2011-10-04 19:19:01 +09:00
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 )
{
2012-06-27 14:27:01 +09:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2012-06-27 14:27:05 +09:00
struct drm_plane * plane = exynos_crtc - > plane ;
unsigned int crtc_w ;
unsigned int crtc_h ;
2012-06-27 14:27:02 +09:00
int pipe = exynos_crtc - > pipe ;
2012-06-27 14:27:01 +09:00
int ret ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2012-03-16 18:47:04 +09:00
/*
* copy the mode data adjusted by mode_fixup ( ) into crtc - > mode
* so that hardware can be seet to proper mode .
*/
memcpy ( & crtc - > mode , adjusted_mode , sizeof ( * adjusted_mode ) ) ;
2011-10-04 19:19:01 +09:00
2012-06-27 14:27:05 +09:00
crtc_w = crtc - > fb - > width - x ;
crtc_h = crtc - > fb - > height - y ;
ret = exynos_plane_mode_set ( plane , crtc , crtc - > fb , 0 , 0 , crtc_w , crtc_h ,
x , y , crtc_w , crtc_h ) ;
2012-06-27 14:27:01 +09:00
if ( ret )
return ret ;
2012-06-27 14:27:05 +09:00
plane - > crtc = crtc ;
plane - > fb = crtc - > fb ;
2012-06-27 14:27:02 +09:00
exynos_drm_fn_encoder ( crtc , & pipe , exynos_drm_encoder_crtc_pipe ) ;
2012-06-27 14:27:01 +09:00
return 0 ;
2011-10-04 19:19:01 +09:00
}
static int exynos_drm_crtc_mode_set_base ( struct drm_crtc * crtc , int x , int y ,
struct drm_framebuffer * old_fb )
{
2012-06-27 14:27:05 +09:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
struct drm_plane * plane = exynos_crtc - > plane ;
unsigned int crtc_w ;
unsigned int crtc_h ;
2011-10-04 19:19:01 +09:00
int ret ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2012-06-27 14:27:05 +09:00
crtc_w = crtc - > fb - > width - x ;
crtc_h = crtc - > fb - > height - y ;
ret = exynos_plane_mode_set ( plane , crtc , crtc - > fb , 0 , 0 , crtc_w , crtc_h ,
x , y , crtc_w , crtc_h ) ;
2011-10-04 19:19:01 +09:00
if ( ret )
return ret ;
2012-06-27 14:27:07 +09:00
exynos_drm_crtc_commit ( crtc ) ;
2011-10-04 19:19:01 +09:00
2012-06-27 14:27:05 +09:00
return 0 ;
2011-10-04 19:19:01 +09:00
}
static void exynos_drm_crtc_load_lut ( struct drm_crtc * crtc )
{
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* drm framework doesn't check NULL */
}
2012-06-27 14:27:10 +09:00
static void exynos_drm_crtc_disable ( struct drm_crtc * crtc )
{
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
exynos_plane_dpms ( exynos_crtc - > plane , DRM_MODE_DPMS_OFF ) ;
exynos_drm_crtc_dpms ( crtc , DRM_MODE_DPMS_OFF ) ;
}
2011-10-04 19:19:01 +09:00
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 ,
2012-06-27 14:27:10 +09:00
. disable = exynos_drm_crtc_disable ,
2011-10-04 19:19:01 +09:00
} ;
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__ ) ;
2012-09-11 18:25:21 +09:00
/* when the page flip is requested, crtc's dpms should be on */
if ( exynos_crtc - > dpms > DRM_MODE_DPMS_ON ) {
DRM_ERROR ( " failed page flip request. \n " ) ;
return - EINVAL ;
}
2011-10-04 19:19:01 +09:00
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
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 ;
}
2012-02-15 11:25:20 +09:00
list_add_tail ( & event - > base . link ,
& dev_priv - > pageflip_event_list ) ;
2011-10-04 19:19:01 +09:00
crtc - > fb = fb ;
2012-06-27 14:27:05 +09:00
ret = exynos_drm_crtc_mode_set_base ( crtc , crtc - > x , crtc - > y ,
NULL ) ;
2011-10-04 19:19:01 +09:00
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 ;
}
}
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 ) ;
}
2012-06-27 14:27:11 +09:00
static int exynos_drm_crtc_set_property ( struct drm_crtc * crtc ,
struct drm_property * property ,
uint64_t val )
{
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 ) ;
DRM_DEBUG_KMS ( " %s \n " , __func__ ) ;
if ( property = = dev_priv - > crtc_mode_property ) {
enum exynos_crtc_mode mode = val ;
if ( mode = = exynos_crtc - > mode )
return 0 ;
exynos_crtc - > mode = mode ;
switch ( mode ) {
case CRTC_MODE_NORMAL :
exynos_drm_crtc_commit ( crtc ) ;
break ;
case CRTC_MODE_BLANK :
exynos_plane_dpms ( exynos_crtc - > plane ,
DRM_MODE_DPMS_OFF ) ;
break ;
default :
break ;
}
return 0 ;
}
return - EINVAL ;
}
2011-10-04 19:19:01 +09:00
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 ,
2012-06-27 14:27:11 +09:00
. set_property = exynos_drm_crtc_set_property ,
} ;
static const struct drm_prop_enum_list mode_names [ ] = {
{ CRTC_MODE_NORMAL , " normal " } ,
{ CRTC_MODE_BLANK , " blank " } ,
2011-10-04 19:19:01 +09:00
} ;
2012-06-27 14:27:11 +09:00
static void exynos_drm_crtc_attach_mode_property ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct exynos_drm_private * dev_priv = dev - > dev_private ;
struct drm_property * prop ;
DRM_DEBUG_KMS ( " %s \n " , __func__ ) ;
prop = dev_priv - > crtc_mode_property ;
if ( ! prop ) {
prop = drm_property_create_enum ( dev , 0 , " mode " , mode_names ,
ARRAY_SIZE ( mode_names ) ) ;
if ( ! prop )
return ;
dev_priv - > crtc_mode_property = prop ;
}
drm_object_attach_property ( & crtc - > base , prop , 0 ) ;
}
2011-10-04 19:19:01 +09:00
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 ;
2011-12-06 11:06:54 +09:00
exynos_crtc - > dpms = DRM_MODE_DPMS_OFF ;
2012-06-27 14:27:04 +09:00
exynos_crtc - > plane = exynos_plane_init ( dev , 1 < < nr , true ) ;
if ( ! exynos_crtc - > plane ) {
kfree ( exynos_crtc ) ;
return - ENOMEM ;
}
2011-10-04 19:19:01 +09:00
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 ) ;
2012-06-27 14:27:11 +09:00
exynos_drm_crtc_attach_mode_property ( crtc ) ;
2011-10-04 19:19:01 +09:00
return 0 ;
}
int exynos_drm_crtc_enable_vblank ( struct drm_device * dev , int crtc )
{
struct exynos_drm_private * private = dev - > dev_private ;
2011-12-06 11:06:54 +09:00
struct exynos_drm_crtc * exynos_crtc =
to_exynos_crtc ( private - > crtc [ crtc ] ) ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2011-12-06 11:06:54 +09:00
if ( exynos_crtc - > dpms ! = DRM_MODE_DPMS_ON )
return - EPERM ;
2011-10-04 19:19:01 +09:00
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 ;
2011-12-06 11:06:54 +09:00
struct exynos_drm_crtc * exynos_crtc =
to_exynos_crtc ( private - > crtc [ crtc ] ) ;
2011-10-04 19:19:01 +09:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2011-12-06 11:06:54 +09:00
if ( exynos_crtc - > dpms ! = DRM_MODE_DPMS_ON )
return ;
2011-10-04 19:19:01 +09:00
exynos_drm_fn_encoder ( private - > crtc [ crtc ] , & crtc ,
exynos_drm_disable_vblank ) ;
}