2014-07-31 11:39:11 +04:00
/*
* Copyright ( C ) STMicroelectronics SA 2014
* Authors : Benjamin Gaignard < benjamin . gaignard @ st . com >
* Fabien Dessenne < fabien . dessenne @ st . com >
* for STMicroelectronics .
* License terms : GNU General Public License ( GPL ) , version 2
*/
# include <linux/clk.h>
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2014-10-29 12:03:57 +03:00
# include <drm/drm_plane_helper.h>
2014-07-31 11:39:11 +04:00
# include "sti_compositor.h"
# include "sti_drm_drv.h"
# include "sti_drm_crtc.h"
# include "sti_vtg.h"
static void sti_drm_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
DRM_DEBUG_KMS ( " \n " ) ;
}
static void sti_drm_crtc_prepare ( struct drm_crtc * crtc )
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
2014-12-08 19:32:36 +03:00
mixer - > enabled = true ;
2014-07-31 11:39:11 +04:00
/* Prepare and enable the compo IP clock */
if ( mixer - > id = = STI_MIXER_MAIN ) {
if ( clk_prepare_enable ( compo - > clk_compo_main ) )
DRM_INFO ( " Failed to prepare/enable compo_main clk \n " ) ;
} else {
if ( clk_prepare_enable ( compo - > clk_compo_aux ) )
DRM_INFO ( " Failed to prepare/enable compo_aux clk \n " ) ;
}
2014-12-04 13:17:32 +03:00
sti_mixer_clear_all_layers ( mixer ) ;
2014-07-31 11:39:11 +04:00
}
static void sti_drm_crtc_commit ( struct drm_crtc * crtc )
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
struct sti_layer * layer ;
if ( ( ! mixer | | ! compo ) ) {
DRM_ERROR ( " Can not find mixer or compositor) \n " ) ;
return ;
}
/* get GDP which is reserved to the CRTC FB */
layer = to_sti_layer ( crtc - > primary ) ;
if ( layer )
sti_layer_commit ( layer ) ;
else
DRM_ERROR ( " Can not find CRTC dedicated plane (GDP0) \n " ) ;
/* Enable layer on mixer */
if ( sti_mixer_set_layer_status ( mixer , layer , true ) )
DRM_ERROR ( " Can not enable layer at mixer \n " ) ;
2014-12-04 13:27:45 +03:00
drm_crtc_vblank_on ( crtc ) ;
2014-07-31 11:39:11 +04:00
}
static bool sti_drm_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
/* accept the provided drm_display_mode, do not fix it up */
return true ;
}
static int
sti_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 )
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
struct sti_layer * layer ;
struct clk * clk ;
int rate = mode - > clock * 1000 ;
int res ;
unsigned int w , h ;
DRM_DEBUG_KMS ( " CRTC:%d (%s) fb:%d mode:%d (%s) \n " ,
crtc - > base . id , sti_mixer_to_str ( mixer ) ,
crtc - > primary - > fb - > base . id , mode - > base . id , mode - > name ) ;
DRM_DEBUG_KMS ( " %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x \n " ,
mode - > vrefresh , mode - > clock ,
mode - > hdisplay ,
mode - > hsync_start , mode - > hsync_end ,
mode - > htotal ,
mode - > vdisplay ,
mode - > vsync_start , mode - > vsync_end ,
mode - > vtotal , mode - > type , mode - > flags ) ;
/* Set rate and prepare/enable pixel clock */
if ( mixer - > id = = STI_MIXER_MAIN )
clk = compo - > clk_pix_main ;
else
clk = compo - > clk_pix_aux ;
res = clk_set_rate ( clk , rate ) ;
if ( res < 0 ) {
DRM_ERROR ( " Cannot set rate (%dHz) for pix clk \n " , rate ) ;
return - EINVAL ;
}
if ( clk_prepare_enable ( clk ) ) {
DRM_ERROR ( " Failed to prepare/enable pix clk \n " ) ;
return - EINVAL ;
}
sti_vtg_set_config ( mixer - > id = = STI_MIXER_MAIN ?
compo - > vtg_main : compo - > vtg_aux , & crtc - > mode ) ;
/* a GDP is reserved to the CRTC FB */
layer = to_sti_layer ( crtc - > primary ) ;
if ( ! layer ) {
DRM_ERROR ( " Can not find GDP0) \n " ) ;
return - EINVAL ;
}
/* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be set to proper mode
*/
memcpy ( & crtc - > mode , adjusted_mode , sizeof ( * adjusted_mode ) ) ;
res = sti_mixer_set_layer_depth ( mixer , layer ) ;
if ( res ) {
DRM_ERROR ( " Can not set layer depth \n " ) ;
return - EINVAL ;
}
res = sti_mixer_active_video_area ( mixer , & crtc - > mode ) ;
if ( res ) {
DRM_ERROR ( " Can not set active video area \n " ) ;
return - EINVAL ;
}
w = crtc - > primary - > fb - > width - x ;
h = crtc - > primary - > fb - > height - y ;
2014-12-11 13:38:59 +03:00
return sti_layer_prepare ( layer , crtc ,
crtc - > primary - > fb , & crtc - > mode ,
2014-07-31 11:39:11 +04:00
mixer - > id , 0 , 0 , w , h , x , y , w , h ) ;
}
static int sti_drm_crtc_mode_set_base ( struct drm_crtc * crtc , int x , int y ,
struct drm_framebuffer * old_fb )
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct sti_layer * layer ;
unsigned int w , h ;
int ret ;
DRM_DEBUG_KMS ( " CRTC:%d (%s) fb:%d (%d,%d) \n " ,
crtc - > base . id , sti_mixer_to_str ( mixer ) ,
crtc - > primary - > fb - > base . id , x , y ) ;
/* GDP is reserved to the CRTC FB */
layer = to_sti_layer ( crtc - > primary ) ;
if ( ! layer ) {
DRM_ERROR ( " Can not find GDP0) \n " ) ;
ret = - EINVAL ;
goto out ;
}
w = crtc - > primary - > fb - > width - crtc - > x ;
h = crtc - > primary - > fb - > height - crtc - > y ;
2014-12-11 13:38:59 +03:00
ret = sti_layer_prepare ( layer , crtc ,
crtc - > primary - > fb , & crtc - > mode ,
2014-07-31 11:39:11 +04:00
mixer - > id , 0 , 0 , w , h ,
crtc - > x , crtc - > y , w , h ) ;
if ( ret ) {
DRM_ERROR ( " Can not prepare layer \n " ) ;
goto out ;
}
sti_drm_crtc_commit ( crtc ) ;
out :
return ret ;
}
static void sti_drm_crtc_load_lut ( struct drm_crtc * crtc )
{
/* do nothing */
}
static void sti_drm_crtc_disable ( struct drm_crtc * crtc )
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
struct sti_layer * layer ;
2014-12-08 19:32:36 +03:00
if ( ! mixer - > enabled )
2014-07-31 11:39:11 +04:00
return ;
DRM_DEBUG_KMS ( " CRTC:%d (%s) \n " , crtc - > base . id , sti_mixer_to_str ( mixer ) ) ;
/* Disable Background */
sti_mixer_set_background_status ( mixer , false ) ;
/* Disable GDP */
layer = to_sti_layer ( crtc - > primary ) ;
if ( ! layer ) {
DRM_ERROR ( " Cannot find GDP0 \n " ) ;
return ;
}
/* Disable layer at mixer level */
if ( sti_mixer_set_layer_status ( mixer , layer , false ) )
DRM_ERROR ( " Can not disable %s layer at mixer \n " ,
sti_layer_to_str ( layer ) ) ;
/* Wait a while to be sure that a Vsync event is received */
msleep ( WAIT_NEXT_VSYNC_MS ) ;
/* Then disable layer itself */
sti_layer_disable ( layer ) ;
2014-12-04 13:27:45 +03:00
drm_crtc_vblank_off ( crtc ) ;
2014-07-31 11:39:11 +04:00
/* Disable pixel clock and compo IP clocks */
if ( mixer - > id = = STI_MIXER_MAIN ) {
clk_disable_unprepare ( compo - > clk_pix_main ) ;
clk_disable_unprepare ( compo - > clk_compo_main ) ;
} else {
clk_disable_unprepare ( compo - > clk_pix_aux ) ;
clk_disable_unprepare ( compo - > clk_compo_aux ) ;
}
2014-12-08 19:32:36 +03:00
mixer - > enabled = false ;
2014-07-31 11:39:11 +04:00
}
static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
. dpms = sti_drm_crtc_dpms ,
. prepare = sti_drm_crtc_prepare ,
. commit = sti_drm_crtc_commit ,
. mode_fixup = sti_drm_crtc_mode_fixup ,
. mode_set = sti_drm_crtc_mode_set ,
. mode_set_base = sti_drm_crtc_mode_set_base ,
. load_lut = sti_drm_crtc_load_lut ,
. disable = sti_drm_crtc_disable ,
} ;
static int sti_drm_crtc_page_flip ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
struct drm_pending_vblank_event * event ,
uint32_t page_flip_flags )
{
struct drm_device * drm_dev = crtc - > dev ;
struct drm_framebuffer * old_fb ;
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
unsigned long flags ;
int ret ;
DRM_DEBUG_KMS ( " fb %d --> fb %d \n " ,
crtc - > primary - > fb - > base . id , fb - > base . id ) ;
mutex_lock ( & drm_dev - > struct_mutex ) ;
old_fb = crtc - > primary - > fb ;
crtc - > primary - > fb = fb ;
ret = sti_drm_crtc_mode_set_base ( crtc , crtc - > x , crtc - > y , old_fb ) ;
if ( ret ) {
DRM_ERROR ( " failed \n " ) ;
crtc - > primary - > fb = old_fb ;
goto out ;
}
if ( event ) {
event - > pipe = mixer - > id ;
ret = drm_vblank_get ( drm_dev , event - > pipe ) ;
if ( ret ) {
DRM_ERROR ( " Cannot get vblank \n " ) ;
goto out ;
}
spin_lock_irqsave ( & drm_dev - > event_lock , flags ) ;
if ( mixer - > pending_event ) {
drm_vblank_put ( drm_dev , event - > pipe ) ;
ret = - EBUSY ;
} else {
mixer - > pending_event = event ;
}
spin_unlock_irqrestore ( & drm_dev - > event_lock , flags ) ;
}
out :
mutex_unlock ( & drm_dev - > struct_mutex ) ;
return ret ;
}
static void sti_drm_crtc_destroy ( struct drm_crtc * crtc )
{
DRM_DEBUG_KMS ( " \n " ) ;
drm_crtc_cleanup ( crtc ) ;
}
static int sti_drm_crtc_set_property ( struct drm_crtc * crtc ,
struct drm_property * property ,
uint64_t val )
{
DRM_DEBUG_KMS ( " \n " ) ;
return 0 ;
}
int sti_drm_crtc_vblank_cb ( struct notifier_block * nb ,
unsigned long event , void * data )
{
struct drm_device * drm_dev ;
struct sti_compositor * compo =
container_of ( nb , struct sti_compositor , vtg_vblank_nb ) ;
int * crtc = data ;
unsigned long flags ;
struct sti_drm_private * priv ;
drm_dev = compo - > mixer [ * crtc ] - > drm_crtc . dev ;
priv = drm_dev - > dev_private ;
if ( ( event ! = VTG_TOP_FIELD_EVENT ) & &
( event ! = VTG_BOTTOM_FIELD_EVENT ) ) {
DRM_ERROR ( " unknown event: %lu \n " , event ) ;
return - EINVAL ;
}
drm_handle_vblank ( drm_dev , * crtc ) ;
spin_lock_irqsave ( & drm_dev - > event_lock , flags ) ;
if ( compo - > mixer [ * crtc ] - > pending_event ) {
drm_send_vblank_event ( drm_dev , - 1 ,
compo - > mixer [ * crtc ] - > pending_event ) ;
drm_vblank_put ( drm_dev , * crtc ) ;
compo - > mixer [ * crtc ] - > pending_event = NULL ;
}
spin_unlock_irqrestore ( & drm_dev - > event_lock , flags ) ;
return 0 ;
}
int sti_drm_crtc_enable_vblank ( struct drm_device * dev , int crtc )
{
struct sti_drm_private * dev_priv = dev - > dev_private ;
struct sti_compositor * compo = dev_priv - > compo ;
struct notifier_block * vtg_vblank_nb = & compo - > vtg_vblank_nb ;
if ( sti_vtg_register_client ( crtc = = STI_MIXER_MAIN ?
compo - > vtg_main : compo - > vtg_aux ,
vtg_vblank_nb , crtc ) ) {
DRM_ERROR ( " Cannot register VTG notifier \n " ) ;
return - EINVAL ;
}
return 0 ;
}
EXPORT_SYMBOL ( sti_drm_crtc_enable_vblank ) ;
void sti_drm_crtc_disable_vblank ( struct drm_device * dev , int crtc )
{
struct sti_drm_private * priv = dev - > dev_private ;
struct sti_compositor * compo = priv - > compo ;
struct notifier_block * vtg_vblank_nb = & compo - > vtg_vblank_nb ;
DRM_DEBUG_DRIVER ( " \n " ) ;
if ( sti_vtg_unregister_client ( crtc = = STI_MIXER_MAIN ?
compo - > vtg_main : compo - > vtg_aux , vtg_vblank_nb ) )
DRM_DEBUG_DRIVER ( " Warning: cannot unregister VTG notifier \n " ) ;
/* free the resources of the pending requests */
if ( compo - > mixer [ crtc ] - > pending_event ) {
drm_vblank_put ( dev , crtc ) ;
compo - > mixer [ crtc ] - > pending_event = NULL ;
}
}
EXPORT_SYMBOL ( sti_drm_crtc_disable_vblank ) ;
static struct drm_crtc_funcs sti_crtc_funcs = {
. set_config = drm_crtc_helper_set_config ,
. page_flip = sti_drm_crtc_page_flip ,
. destroy = sti_drm_crtc_destroy ,
. set_property = sti_drm_crtc_set_property ,
} ;
bool sti_drm_crtc_is_main ( struct drm_crtc * crtc )
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
if ( mixer - > id = = STI_MIXER_MAIN )
return true ;
return false ;
}
2014-12-08 19:32:36 +03:00
EXPORT_SYMBOL ( sti_drm_crtc_is_main ) ;
2014-07-31 11:39:11 +04:00
int sti_drm_crtc_init ( struct drm_device * drm_dev , struct sti_mixer * mixer ,
struct drm_plane * primary , struct drm_plane * cursor )
{
struct drm_crtc * crtc = & mixer - > drm_crtc ;
int res ;
res = drm_crtc_init_with_planes ( drm_dev , crtc , primary , cursor ,
& sti_crtc_funcs ) ;
if ( res ) {
DRM_ERROR ( " Can not initialze CRTC \n " ) ;
return - EINVAL ;
}
drm_crtc_helper_add ( crtc , & sti_crtc_helper_funcs ) ;
DRM_DEBUG_DRIVER ( " drm CRTC:%d mapped to %s \n " ,
crtc - > base . id , sti_mixer_to_str ( mixer ) ) ;
return 0 ;
}