2011-10-04 14:19:01 +04: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 >
*
2012-12-17 21:30:17 +04: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 14:19:01 +04:00
*/
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2015-06-01 18:04:44 +03:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2011-10-04 14:19:01 +04:00
2013-08-13 03:46:40 +04:00
# include "exynos_drm_crtc.h"
2011-10-04 14:19:01 +04:00
# include "exynos_drm_drv.h"
# include "exynos_drm_encoder.h"
2012-06-27 09:27:04 +04:00
# include "exynos_drm_plane.h"
2011-10-04 14:19:01 +04:00
2015-06-01 18:04:53 +03:00
static void exynos_drm_crtc_enable ( struct drm_crtc * crtc )
2011-10-04 14:19:01 +04:00
{
2011-11-04 12:04:45 +04:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2011-10-04 14:19:01 +04:00
2015-06-01 18:04:53 +03:00
if ( exynos_crtc - > enabled )
2011-12-06 06:06:54 +04:00
return ;
2013-05-21 11:55:58 +04:00
2015-06-01 18:04:55 +03:00
if ( exynos_crtc - > ops - > enable )
exynos_crtc - > ops - > enable ( exynos_crtc ) ;
2014-02-19 16:02:55 +04:00
2015-06-01 18:04:53 +03:00
exynos_crtc - > enabled = true ;
2014-10-10 16:31:55 +04:00
2015-06-01 18:04:53 +03:00
drm_crtc_vblank_on ( crtc ) ;
2011-10-04 14:19:01 +04:00
}
2015-06-01 18:04:51 +03:00
static void exynos_drm_crtc_disable ( struct drm_crtc * crtc )
{
2015-06-01 18:04:53 +03:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2015-06-01 18:04:51 +03:00
2015-06-01 18:04:53 +03:00
if ( ! exynos_crtc - > enabled )
return ;
/* wait for the completion of page flip. */
if ( ! wait_event_timeout ( exynos_crtc - > pending_flip_queue ,
( exynos_crtc - > event = = NULL ) , HZ / 20 ) )
exynos_crtc - > event = NULL ;
drm_crtc_vblank_off ( crtc ) ;
2015-06-01 18:04:55 +03:00
if ( exynos_crtc - > ops - > disable )
exynos_crtc - > ops - > disable ( exynos_crtc ) ;
2015-06-01 18:04:53 +03:00
exynos_crtc - > enabled = false ;
2015-06-01 18:04:51 +03:00
}
2011-10-04 14:19:01 +04:00
static bool
exynos_drm_crtc_mode_fixup ( struct drm_crtc * crtc ,
2012-07-17 19:56:50 +04:00
const struct drm_display_mode * mode ,
2011-10-04 14:19:01 +04:00
struct drm_display_mode * adjusted_mode )
{
2014-01-31 01:19:19 +04:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2015-01-18 12:16:23 +03:00
if ( exynos_crtc - > ops - > mode_fixup )
return exynos_crtc - > ops - > mode_fixup ( exynos_crtc , mode ,
adjusted_mode ) ;
2014-01-31 01:19:19 +04:00
2011-10-04 14:19:01 +04:00
return true ;
}
2015-06-01 18:04:43 +03:00
static void
exynos_drm_crtc_mode_set_nofb ( struct drm_crtc * crtc )
2011-10-04 14:19:01 +04:00
{
2012-06-27 09:27:05 +04:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2012-09-14 08:29:47 +04:00
2015-06-01 18:04:43 +03:00
if ( exynos_crtc - > ops - > commit )
exynos_crtc - > ops - > commit ( exynos_crtc ) ;
2011-10-04 14:19:01 +04:00
}
2015-07-21 14:28:58 +03:00
static void exynos_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2015-06-01 18:04:48 +03:00
{
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
if ( crtc - > state - > event ) {
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
exynos_crtc - > event = crtc - > state - > event ;
}
}
2015-07-21 14:28:58 +03:00
static void exynos_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2015-06-01 18:04:48 +03:00
{
}
2011-10-04 14:19:01 +04:00
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
2015-06-01 18:04:53 +03:00
. enable = exynos_drm_crtc_enable ,
2015-06-01 18:04:51 +03:00
. disable = exynos_drm_crtc_disable ,
2011-10-04 14:19:01 +04:00
. mode_fixup = exynos_drm_crtc_mode_fixup ,
2015-06-01 18:04:43 +03:00
. mode_set_nofb = exynos_drm_crtc_mode_set_nofb ,
2015-06-01 18:04:48 +03:00
. atomic_begin = exynos_crtc_atomic_begin ,
. atomic_flush = exynos_crtc_atomic_flush ,
2011-10-04 14:19:01 +04:00
} ;
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 ;
private - > crtc [ exynos_crtc - > pipe ] = NULL ;
drm_crtc_cleanup ( crtc ) ;
kfree ( exynos_crtc ) ;
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
2015-06-01 18:04:47 +03:00
. set_config = drm_atomic_helper_set_config ,
2015-06-01 18:04:48 +03:00
. page_flip = drm_atomic_helper_page_flip ,
2011-10-04 14:19:01 +04:00
. destroy = exynos_drm_crtc_destroy ,
2015-06-01 18:04:44 +03:00
. reset = drm_atomic_helper_crtc_reset ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
2011-10-04 14:19:01 +04:00
} ;
2015-01-18 12:16:23 +03:00
struct exynos_drm_crtc * exynos_drm_crtc_create ( struct drm_device * drm_dev ,
2015-05-07 03:04:45 +03:00
struct drm_plane * plane ,
int pipe ,
enum exynos_drm_output_type type ,
const struct exynos_drm_crtc_ops * ops ,
void * ctx )
2011-10-04 14:19:01 +04:00
{
struct exynos_drm_crtc * exynos_crtc ;
2014-11-26 21:43:27 +03:00
struct exynos_drm_private * private = drm_dev - > dev_private ;
2011-10-04 14:19:01 +04:00
struct drm_crtc * crtc ;
2014-09-19 16:58:53 +04:00
int ret ;
2011-10-04 14:19:01 +04:00
exynos_crtc = kzalloc ( sizeof ( * exynos_crtc ) , GFP_KERNEL ) ;
2013-08-19 14:04:55 +04:00
if ( ! exynos_crtc )
2015-01-18 12:16:23 +03:00
return ERR_PTR ( - ENOMEM ) ;
2011-10-04 14:19:01 +04:00
2013-05-21 11:55:58 +04:00
init_waitqueue_head ( & exynos_crtc - > pending_flip_queue ) ;
2014-02-19 16:02:55 +04:00
2014-11-04 23:25:27 +03:00
exynos_crtc - > pipe = pipe ;
2014-11-06 00:51:35 +03:00
exynos_crtc - > type = type ;
2015-01-18 12:16:23 +03:00
exynos_crtc - > ops = ops ;
exynos_crtc - > ctx = ctx ;
2012-06-27 09:27:04 +04:00
2014-11-03 23:20:29 +03:00
crtc = & exynos_crtc - > base ;
2011-10-04 14:19:01 +04:00
2014-11-04 23:25:27 +03:00
private - > crtc [ pipe ] = crtc ;
2011-10-04 14:19:01 +04:00
2014-11-26 21:43:27 +03:00
ret = drm_crtc_init_with_planes ( drm_dev , crtc , plane , NULL ,
2014-09-19 16:58:53 +04:00
& exynos_crtc_funcs ) ;
if ( ret < 0 )
goto err_crtc ;
2011-10-04 14:19:01 +04:00
drm_crtc_helper_add ( crtc , & exynos_crtc_helper_funcs ) ;
2015-01-18 12:16:23 +03:00
return exynos_crtc ;
2014-09-19 16:58:53 +04:00
err_crtc :
plane - > funcs - > destroy ( plane ) ;
kfree ( exynos_crtc ) ;
2015-01-18 12:16:23 +03:00
return ERR_PTR ( ret ) ;
2011-10-04 14:19:01 +04:00
}
2014-02-19 16:02:55 +04:00
int exynos_drm_crtc_enable_vblank ( struct drm_device * dev , int pipe )
2011-10-04 14:19:01 +04:00
{
struct exynos_drm_private * private = dev - > dev_private ;
2011-12-06 06:06:54 +04:00
struct exynos_drm_crtc * exynos_crtc =
2014-02-19 16:02:55 +04:00
to_exynos_crtc ( private - > crtc [ pipe ] ) ;
2011-10-04 14:19:01 +04:00
2015-06-01 18:04:53 +03:00
if ( ! exynos_crtc - > enabled )
2011-12-06 06:06:54 +04:00
return - EPERM ;
2015-01-18 12:16:23 +03:00
if ( exynos_crtc - > ops - > enable_vblank )
exynos_crtc - > ops - > enable_vblank ( exynos_crtc ) ;
2011-10-04 14:19:01 +04:00
return 0 ;
}
2014-02-19 16:02:55 +04:00
void exynos_drm_crtc_disable_vblank ( struct drm_device * dev , int pipe )
2011-10-04 14:19:01 +04:00
{
struct exynos_drm_private * private = dev - > dev_private ;
2011-12-06 06:06:54 +04:00
struct exynos_drm_crtc * exynos_crtc =
2014-02-19 16:02:55 +04:00
to_exynos_crtc ( private - > crtc [ pipe ] ) ;
2011-10-04 14:19:01 +04:00
2015-06-01 18:04:53 +03:00
if ( ! exynos_crtc - > enabled )
2011-12-06 06:06:54 +04:00
return ;
2015-01-18 12:16:23 +03:00
if ( exynos_crtc - > ops - > disable_vblank )
exynos_crtc - > ops - > disable_vblank ( exynos_crtc ) ;
2011-10-04 14:19:01 +04:00
}
2013-01-03 14:44:04 +04:00
2014-02-19 16:02:55 +04:00
void exynos_drm_crtc_finish_pageflip ( struct drm_device * dev , int pipe )
2013-01-03 14:44:04 +04:00
{
struct exynos_drm_private * dev_priv = dev - > dev_private ;
2014-02-19 16:02:55 +04:00
struct drm_crtc * drm_crtc = dev_priv - > crtc [ pipe ] ;
2013-05-21 11:55:58 +04:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( drm_crtc ) ;
2013-01-03 14:44:04 +04:00
unsigned long flags ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
2015-04-01 19:02:12 +03:00
if ( exynos_crtc - > event ) {
2013-01-03 14:44:04 +04:00
2015-07-16 18:23:31 +03:00
drm_send_vblank_event ( dev , pipe , exynos_crtc - > event ) ;
2014-02-19 16:02:55 +04:00
drm_vblank_put ( dev , pipe ) ;
2013-05-21 11:55:58 +04:00
wake_up ( & exynos_crtc - > pending_flip_queue ) ;
2015-04-01 19:02:12 +03:00
2013-01-03 14:44:04 +04:00
}
2015-04-01 19:02:12 +03:00
exynos_crtc - > event = NULL ;
2013-01-03 14:44:04 +04:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
}
2014-02-19 16:02:55 +04:00
void exynos_drm_crtc_complete_scanout ( struct drm_framebuffer * fb )
{
2015-01-18 12:16:23 +03:00
struct exynos_drm_crtc * exynos_crtc ;
2014-02-19 16:02:55 +04:00
struct drm_device * dev = fb - > dev ;
struct drm_crtc * crtc ;
/*
* make sure that overlay data are updated to real hardware
* for all encoders .
*/
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
2015-01-18 12:16:23 +03:00
exynos_crtc = to_exynos_crtc ( crtc ) ;
2014-02-19 16:02:55 +04:00
/*
* wait for vblank interrupt
* - this makes sure that overlay data are updated to
* real hardware .
*/
2015-01-18 12:16:23 +03:00
if ( exynos_crtc - > ops - > wait_for_vblank )
exynos_crtc - > ops - > wait_for_vblank ( exynos_crtc ) ;
2014-02-19 16:02:55 +04:00
}
}
2014-05-09 09:25:20 +04:00
int exynos_drm_crtc_get_pipe_from_type ( struct drm_device * drm_dev ,
unsigned int out_type )
{
struct drm_crtc * crtc ;
list_for_each_entry ( crtc , & drm_dev - > mode_config . crtc_list , head ) {
struct exynos_drm_crtc * exynos_crtc ;
exynos_crtc = to_exynos_crtc ( crtc ) ;
2014-11-06 00:51:35 +03:00
if ( exynos_crtc - > type = = out_type )
2014-11-04 23:44:47 +03:00
return exynos_crtc - > pipe ;
2014-05-09 09:25:20 +04:00
}
return - EPERM ;
}
2014-07-17 13:01:19 +04:00
void exynos_drm_crtc_te_handler ( struct drm_crtc * crtc )
{
2015-01-18 12:16:23 +03:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( crtc ) ;
2014-07-17 13:01:19 +04:00
2015-01-18 12:16:23 +03:00
if ( exynos_crtc - > ops - > te_handler )
exynos_crtc - > ops - > te_handler ( exynos_crtc ) ;
2014-07-17 13:01:19 +04:00
}