2014-07-31 09:39:11 +02: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>
2015-03-19 13:35:16 +01:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2014-07-31 09:39:11 +02:00
# include <drm/drm_crtc_helper.h>
2014-10-29 10:03:57 +01:00
# include <drm/drm_plane_helper.h>
2014-07-31 09:39:11 +02:00
# include "sti_compositor.h"
2015-07-31 11:32:34 +02:00
# include "sti_crtc.h"
# include "sti_drv.h"
2015-08-03 14:22:16 +02:00
# include "sti_vid.h"
2014-07-31 09:39:11 +02:00
# include "sti_vtg.h"
2015-08-03 14:22:16 +02:00
static void sti_crtc_enable ( struct drm_crtc * crtc )
2014-07-31 09:39:11 +02:00
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
2015-08-03 14:22:16 +02:00
DRM_DEBUG_DRIVER ( " \n " ) ;
mixer - > status = STI_MIXER_READY ;
2014-07-31 09:39:11 +02: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 11:17:32 +01:00
2015-08-03 14:22:16 +02:00
drm_crtc_vblank_on ( crtc ) ;
2014-07-31 09:39:11 +02:00
}
2015-08-03 14:22:16 +02:00
static void sti_crtc_disabling ( struct drm_crtc * crtc )
2014-07-31 09:39:11 +02:00
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
2015-08-03 14:22:16 +02:00
DRM_DEBUG_DRIVER ( " \n " ) ;
2014-12-04 11:27:45 +01:00
2015-08-03 14:22:16 +02:00
mixer - > status = STI_MIXER_DISABLING ;
2014-07-31 09:39:11 +02:00
}
2015-07-31 11:32:34 +02:00
static bool sti_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
2014-07-31 09:39:11 +02:00
{
/* accept the provided drm_display_mode, do not fix it up */
return true ;
}
static int
2015-07-31 11:32:34 +02:00
sti_crtc_mode_set ( struct drm_crtc * crtc , struct drm_display_mode * mode )
2014-07-31 09:39:11 +02:00
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
struct clk * clk ;
int rate = mode - > clock * 1000 ;
int res ;
2015-03-19 13:35:16 +01:00
DRM_DEBUG_KMS ( " CRTC:%d (%s) mode:%d (%s) \n " ,
2014-07-31 09:39:11 +02:00
crtc - > base . id , sti_mixer_to_str ( mixer ) ,
2015-03-19 13:35:16 +01:00
mode - > base . id , mode - > name ) ;
2014-07-31 09:39:11 +02:00
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 ) ;
res = sti_mixer_active_video_area ( mixer , & crtc - > mode ) ;
if ( res ) {
2015-07-31 11:32:13 +02:00
DRM_ERROR ( " Can't set active video area \n " ) ;
2014-07-31 09:39:11 +02:00
return - EINVAL ;
}
2015-03-19 13:35:16 +01:00
return res ;
2014-07-31 09:39:11 +02:00
}
2015-07-31 11:32:34 +02:00
static void sti_crtc_disable ( struct drm_crtc * crtc )
2014-07-31 09:39:11 +02:00
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct device * dev = mixer - > dev ;
struct sti_compositor * compo = dev_get_drvdata ( dev ) ;
DRM_DEBUG_KMS ( " CRTC:%d (%s) \n " , crtc - > base . id , sti_mixer_to_str ( mixer ) ) ;
/* Disable Background */
sti_mixer_set_background_status ( mixer , false ) ;
2014-12-04 11:27:45 +01:00
drm_crtc_vblank_off ( crtc ) ;
2014-07-31 09:39:11 +02: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 ) ;
}
2015-08-03 14:22:16 +02:00
mixer - > status = STI_MIXER_DISABLED ;
2014-07-31 09:39:11 +02:00
}
2015-03-19 13:35:16 +01:00
static void
2015-07-31 11:32:34 +02:00
sti_crtc_mode_set_nofb ( struct drm_crtc * crtc )
2015-03-19 13:35:16 +01:00
{
2015-08-03 14:22:16 +02:00
sti_crtc_enable ( crtc ) ;
2015-07-31 11:32:34 +02:00
sti_crtc_mode_set ( crtc , & crtc - > state - > adjusted_mode ) ;
2015-03-19 13:35:16 +01:00
}
2014-07-31 09:39:11 +02:00
2015-08-14 10:14:23 +10:00
static void sti_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2014-07-31 09:39:11 +02:00
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
2015-03-19 13:35:16 +01:00
if ( crtc - > state - > event ) {
crtc - > state - > event - > pipe = drm_crtc_index ( crtc ) ;
2014-07-31 09:39:11 +02:00
2015-03-19 13:35:16 +01:00
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
2014-07-31 09:39:11 +02:00
2015-03-19 13:35:16 +01:00
mixer - > pending_event = crtc - > state - > event ;
crtc - > state - > event = NULL ;
2014-07-31 09:39:11 +02:00
}
2015-03-19 13:35:16 +01:00
}
2014-07-31 09:39:11 +02:00
2015-08-14 10:14:23 +10:00
static void sti_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2015-03-19 13:35:16 +01:00
{
2015-08-03 14:22:16 +02:00
struct drm_device * drm_dev = crtc - > dev ;
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
struct sti_compositor * compo = dev_get_drvdata ( mixer - > dev ) ;
struct drm_plane * p ;
DRM_DEBUG_DRIVER ( " \n " ) ;
/* perform plane actions */
list_for_each_entry ( p , & drm_dev - > mode_config . plane_list , head ) {
struct sti_plane * plane = to_sti_plane ( p ) ;
switch ( plane - > status ) {
case STI_PLANE_UPDATED :
/* update planes tag as updated */
DRM_DEBUG_DRIVER ( " update plane %s \n " ,
sti_plane_to_str ( plane ) ) ;
if ( sti_mixer_set_plane_depth ( mixer , plane ) ) {
DRM_ERROR ( " Cannot set plane %s depth \n " ,
sti_plane_to_str ( plane ) ) ;
break ;
}
if ( sti_mixer_set_plane_status ( mixer , plane , true ) ) {
DRM_ERROR ( " Cannot enable plane %s at mixer \n " ,
sti_plane_to_str ( plane ) ) ;
break ;
}
/* if plane is HQVDP_0 then commit the vid[0] */
if ( plane - > desc = = STI_HQVDP_0 )
sti_vid_commit ( compo - > vid [ 0 ] , p - > state ) ;
plane - > status = STI_PLANE_READY ;
break ;
case STI_PLANE_DISABLING :
/* disabling sequence for planes tag as disabling */
DRM_DEBUG_DRIVER ( " disable plane %s from mixer \n " ,
sti_plane_to_str ( plane ) ) ;
if ( sti_mixer_set_plane_status ( mixer , plane , false ) ) {
DRM_ERROR ( " Cannot disable plane %s at mixer \n " ,
sti_plane_to_str ( plane ) ) ;
continue ;
}
if ( plane - > desc = = STI_CURSOR )
/* tag plane status for disabled */
plane - > status = STI_PLANE_DISABLED ;
else
/* tag plane status for flushing */
plane - > status = STI_PLANE_FLUSHING ;
/* if plane is HQVDP_0 then disable the vid[0] */
if ( plane - > desc = = STI_HQVDP_0 )
sti_vid_disable ( compo - > vid [ 0 ] ) ;
break ;
default :
/* Other status case are not handled */
break ;
}
}
2014-07-31 09:39:11 +02:00
}
2015-03-19 13:35:16 +01:00
static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
2015-08-03 14:22:16 +02:00
. enable = sti_crtc_enable ,
. disable = sti_crtc_disabling ,
2015-07-31 11:32:34 +02:00
. mode_fixup = sti_crtc_mode_fixup ,
2015-03-19 13:35:16 +01:00
. mode_set = drm_helper_crtc_mode_set ,
2015-07-31 11:32:34 +02:00
. mode_set_nofb = sti_crtc_mode_set_nofb ,
2015-03-19 13:35:16 +01:00
. mode_set_base = drm_helper_crtc_mode_set_base ,
2015-07-31 11:32:34 +02:00
. atomic_begin = sti_crtc_atomic_begin ,
. atomic_flush = sti_crtc_atomic_flush ,
2015-03-19 13:35:16 +01:00
} ;
2015-07-31 11:32:34 +02:00
static void sti_crtc_destroy ( struct drm_crtc * crtc )
2014-07-31 09:39:11 +02:00
{
DRM_DEBUG_KMS ( " \n " ) ;
drm_crtc_cleanup ( crtc ) ;
}
2015-07-31 11:32:34 +02:00
static int sti_crtc_set_property ( struct drm_crtc * crtc ,
struct drm_property * property ,
uint64_t val )
2014-07-31 09:39:11 +02:00
{
DRM_DEBUG_KMS ( " \n " ) ;
return 0 ;
}
2015-07-31 11:32:34 +02:00
int sti_crtc_vblank_cb ( struct notifier_block * nb ,
unsigned long event , void * data )
2014-07-31 09:39:11 +02:00
{
struct drm_device * drm_dev ;
struct sti_compositor * compo =
container_of ( nb , struct sti_compositor , vtg_vblank_nb ) ;
int * crtc = data ;
unsigned long flags ;
2015-07-31 11:32:34 +02:00
struct sti_private * priv ;
2014-07-31 09:39:11 +02:00
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 ,
2015-07-31 11:32:34 +02:00
compo - > mixer [ * crtc ] - > pending_event ) ;
2014-07-31 09:39:11 +02:00
drm_vblank_put ( drm_dev , * crtc ) ;
compo - > mixer [ * crtc ] - > pending_event = NULL ;
}
spin_unlock_irqrestore ( & drm_dev - > event_lock , flags ) ;
2015-08-03 14:22:16 +02:00
if ( compo - > mixer [ * crtc ] - > status = = STI_MIXER_DISABLING ) {
struct drm_plane * p ;
/* Disable mixer only if all overlay planes (GDP and VDP)
* are disabled */
list_for_each_entry ( p , & drm_dev - > mode_config . plane_list , head ) {
struct sti_plane * plane = to_sti_plane ( p ) ;
if ( ( plane - > desc & STI_PLANE_TYPE_MASK ) < = STI_VDP )
if ( plane - > status ! = STI_PLANE_DISABLED )
return 0 ;
}
sti_crtc_disable ( & compo - > mixer [ * crtc ] - > drm_crtc ) ;
}
2014-07-31 09:39:11 +02:00
return 0 ;
}
2015-07-31 11:32:34 +02:00
int sti_crtc_enable_vblank ( struct drm_device * dev , int crtc )
2014-07-31 09:39:11 +02:00
{
2015-07-31 11:32:34 +02:00
struct sti_private * dev_priv = dev - > dev_private ;
2014-07-31 09:39:11 +02:00
struct sti_compositor * compo = dev_priv - > compo ;
struct notifier_block * vtg_vblank_nb = & compo - > vtg_vblank_nb ;
2015-07-31 11:32:13 +02:00
DRM_DEBUG_DRIVER ( " \n " ) ;
2014-07-31 09:39:11 +02:00
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 ;
}
2015-07-31 11:32:34 +02:00
EXPORT_SYMBOL ( sti_crtc_enable_vblank ) ;
2014-07-31 09:39:11 +02:00
2015-08-03 14:22:16 +02:00
void sti_crtc_disable_vblank ( struct drm_device * drm_dev , int crtc )
2014-07-31 09:39:11 +02:00
{
2015-08-03 14:22:16 +02:00
struct sti_private * priv = drm_dev - > dev_private ;
2014-07-31 09:39:11 +02:00
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 ) {
2015-08-03 14:22:16 +02:00
drm_vblank_put ( drm_dev , crtc ) ;
2014-07-31 09:39:11 +02:00
compo - > mixer [ crtc ] - > pending_event = NULL ;
}
}
2015-07-31 11:32:34 +02:00
EXPORT_SYMBOL ( sti_crtc_disable_vblank ) ;
2014-07-31 09:39:11 +02:00
static struct drm_crtc_funcs sti_crtc_funcs = {
2015-03-19 13:35:16 +01:00
. set_config = drm_atomic_helper_set_config ,
. page_flip = drm_atomic_helper_page_flip ,
2015-07-31 11:32:34 +02:00
. destroy = sti_crtc_destroy ,
. set_property = sti_crtc_set_property ,
2015-03-19 13:35:16 +01: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 ,
2014-07-31 09:39:11 +02:00
} ;
2015-07-31 11:32:34 +02:00
bool sti_crtc_is_main ( struct drm_crtc * crtc )
2014-07-31 09:39:11 +02:00
{
struct sti_mixer * mixer = to_sti_mixer ( crtc ) ;
if ( mixer - > id = = STI_MIXER_MAIN )
return true ;
return false ;
}
2015-07-31 11:32:34 +02:00
EXPORT_SYMBOL ( sti_crtc_is_main ) ;
2014-07-31 09:39:11 +02:00
2015-07-31 11:32:34 +02:00
int sti_crtc_init ( struct drm_device * drm_dev , struct sti_mixer * mixer ,
struct drm_plane * primary , struct drm_plane * cursor )
2014-07-31 09:39:11 +02:00
{
struct drm_crtc * crtc = & mixer - > drm_crtc ;
int res ;
res = drm_crtc_init_with_planes ( drm_dev , crtc , primary , cursor ,
2015-07-31 11:32:34 +02:00
& sti_crtc_funcs ) ;
2014-07-31 09:39:11 +02:00
if ( res ) {
2015-07-31 11:32:13 +02:00
DRM_ERROR ( " Can't initialze CRTC \n " ) ;
2014-07-31 09:39:11 +02:00
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 ;
}