2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-11-08 13:21:06 -05:00
/*
* Copyright ( C ) 2014 Red Hat
* Author : Rob Clark < robdclark @ gmail . com >
*/
2018-09-05 15:57:11 +02:00
# include <drm/drm_atomic_uapi.h>
2019-06-25 22:42:03 +02:00
# include <drm/drm_gem_framebuffer_helper.h>
2019-08-04 08:55:51 +02:00
# include <drm/drm_vblank.h>
2018-09-05 15:57:11 +02:00
2019-08-29 09:45:18 -07:00
# include "msm_atomic_trace.h"
2014-11-08 13:21:06 -05:00
# include "msm_drv.h"
2018-04-03 10:42:23 -04:00
# include "msm_gem.h"
2014-11-08 13:21:06 -05:00
# include "msm_kms.h"
2018-04-03 10:42:23 -04:00
int msm_atomic_prepare_fb ( struct drm_plane * plane ,
struct drm_plane_state * new_state )
{
struct msm_drm_private * priv = plane - > dev - > dev_private ;
struct msm_kms * kms = priv - > kms ;
if ( ! new_state - > fb )
return 0 ;
2019-06-25 22:42:03 +02:00
drm_gem_fb_prepare_fb ( plane , new_state ) ;
2018-04-03 10:42:23 -04:00
return msm_framebuffer_prepare ( new_state - > fb , kms - > aspace ) ;
}
2019-08-29 09:45:16 -07:00
static void msm_atomic_async_commit ( struct msm_kms * kms , int crtc_idx )
{
unsigned crtc_mask = BIT ( crtc_idx ) ;
2019-08-29 09:45:18 -07:00
trace_msm_atomic_async_commit_start ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
mutex_lock ( & kms - > commit_lock ) ;
if ( ! ( kms - > pending_crtc_mask & crtc_mask ) ) {
mutex_unlock ( & kms - > commit_lock ) ;
2019-08-29 09:45:18 -07:00
goto out ;
2019-08-29 09:45:16 -07:00
}
kms - > pending_crtc_mask & = ~ crtc_mask ;
kms - > funcs - > enable_commit ( kms ) ;
/*
* Flush hardware updates :
*/
2019-08-29 09:45:18 -07:00
trace_msm_atomic_flush_commit ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
kms - > funcs - > flush_commit ( kms , crtc_mask ) ;
mutex_unlock ( & kms - > commit_lock ) ;
/*
* Wait for flush to complete :
*/
2019-08-29 09:45:18 -07:00
trace_msm_atomic_wait_flush_start ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
kms - > funcs - > wait_flush ( kms , crtc_mask ) ;
2019-08-29 09:45:18 -07:00
trace_msm_atomic_wait_flush_finish ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
mutex_lock ( & kms - > commit_lock ) ;
kms - > funcs - > complete_commit ( kms , crtc_mask ) ;
mutex_unlock ( & kms - > commit_lock ) ;
kms - > funcs - > disable_commit ( kms ) ;
2019-08-29 09:45:18 -07:00
out :
trace_msm_atomic_async_commit_finish ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
}
static enum hrtimer_restart msm_atomic_pending_timer ( struct hrtimer * t )
{
struct msm_pending_timer * timer = container_of ( t ,
struct msm_pending_timer , timer ) ;
struct msm_drm_private * priv = timer - > kms - > dev - > dev_private ;
queue_work ( priv - > wq , & timer - > work ) ;
return HRTIMER_NORESTART ;
}
static void msm_atomic_pending_work ( struct work_struct * work )
{
struct msm_pending_timer * timer = container_of ( work ,
struct msm_pending_timer , work ) ;
msm_atomic_async_commit ( timer - > kms , timer - > crtc_idx ) ;
}
void msm_atomic_init_pending_timer ( struct msm_pending_timer * timer ,
struct msm_kms * kms , int crtc_idx )
{
timer - > kms = kms ;
timer - > crtc_idx = crtc_idx ;
hrtimer_init ( & timer - > timer , CLOCK_MONOTONIC , HRTIMER_MODE_ABS ) ;
timer - > timer . function = msm_atomic_pending_timer ;
INIT_WORK ( & timer - > work , msm_atomic_pending_work ) ;
}
static bool can_do_async ( struct drm_atomic_state * state ,
struct drm_crtc * * async_crtc )
{
struct drm_connector_state * connector_state ;
struct drm_connector * connector ;
struct drm_crtc_state * crtc_state ;
struct drm_crtc * crtc ;
int i , num_crtcs = 0 ;
if ( ! ( state - > legacy_cursor_update | | state - > async_update ) )
return false ;
/* any connector change, means slow path: */
for_each_new_connector_in_state ( state , connector , connector_state , i )
return false ;
for_each_new_crtc_in_state ( state , crtc , crtc_state , i ) {
if ( drm_atomic_crtc_needs_modeset ( crtc_state ) )
return false ;
if ( + + num_crtcs > 1 )
return false ;
* async_crtc = crtc ;
}
return true ;
}
2019-08-29 09:45:12 -07:00
/* Get bitmask of crtcs that will need to be flushed. The bitmask
* can be used with for_each_crtc_mask ( ) iterator , to iterate
* effected crtcs without needing to preserve the atomic state .
*/
static unsigned get_crtc_mask ( struct drm_atomic_state * state )
{
struct drm_crtc_state * crtc_state ;
struct drm_crtc * crtc ;
unsigned i , mask = 0 ;
for_each_new_crtc_in_state ( state , crtc , crtc_state , i )
mask | = drm_crtc_mask ( crtc ) ;
return mask ;
}
2018-02-28 14:19:05 -05:00
void msm_atomic_commit_tail ( struct drm_atomic_state * state )
2014-11-08 13:21:06 -05:00
{
struct drm_device * dev = state - > dev ;
2015-01-30 17:04:45 -05:00
struct msm_drm_private * priv = dev - > dev_private ;
struct msm_kms * kms = priv - > kms ;
2019-08-29 09:45:16 -07:00
struct drm_crtc * async_crtc = NULL ;
2019-08-29 09:45:12 -07:00
unsigned crtc_mask = get_crtc_mask ( state ) ;
2019-08-29 09:45:16 -07:00
bool async = kms - > funcs - > vsync_time & &
can_do_async ( state , & async_crtc ) ;
2015-01-30 17:04:45 -05:00
2019-08-29 09:45:18 -07:00
trace_msm_atomic_commit_tail_start ( async , crtc_mask ) ;
2019-08-29 09:45:15 -07:00
kms - > funcs - > enable_commit ( kms ) ;
2019-08-29 09:45:16 -07:00
/*
* Ensure any previous ( potentially async ) commit has
* completed :
*/
2019-08-29 09:45:18 -07:00
trace_msm_atomic_wait_flush_start ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
kms - > funcs - > wait_flush ( kms , crtc_mask ) ;
2019-08-29 09:45:18 -07:00
trace_msm_atomic_wait_flush_finish ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
mutex_lock ( & kms - > commit_lock ) ;
/*
* Now that there is no in - progress flush , prepare the
* current update :
*/
2015-01-30 17:04:45 -05:00
kms - > funcs - > prepare_commit ( kms , state ) ;
2014-11-08 13:21:06 -05:00
2019-08-29 09:45:14 -07:00
/*
* Push atomic updates down to hardware :
*/
2015-02-22 12:24:19 +01:00
drm_atomic_helper_commit_modeset_disables ( dev , state ) ;
2016-08-29 17:12:03 +08:00
drm_atomic_helper_commit_planes ( dev , state , 0 ) ;
2015-02-22 12:24:19 +01:00
drm_atomic_helper_commit_modeset_enables ( dev , state ) ;
2014-11-08 13:21:06 -05:00
2019-08-29 09:45:16 -07:00
if ( async ) {
struct msm_pending_timer * timer =
& kms - > pending_timers [ drm_crtc_index ( async_crtc ) ] ;
/* async updates are limited to single-crtc updates: */
WARN_ON ( crtc_mask ! = drm_crtc_mask ( async_crtc ) ) ;
/*
* Start timer if we don ' t already have an update pending
* on this crtc :
*/
if ( ! ( kms - > pending_crtc_mask & crtc_mask ) ) {
ktime_t vsync_time , wakeup_time ;
kms - > pending_crtc_mask | = crtc_mask ;
vsync_time = kms - > funcs - > vsync_time ( kms , async_crtc ) ;
wakeup_time = ktime_sub ( vsync_time , ms_to_ktime ( 1 ) ) ;
hrtimer_start ( & timer - > timer , wakeup_time ,
HRTIMER_MODE_ABS ) ;
}
kms - > funcs - > disable_commit ( kms ) ;
mutex_unlock ( & kms - > commit_lock ) ;
/*
* At this point , from drm core ' s perspective , we
* are done with the atomic update , so we can just
* go ahead and signal that it is done :
*/
drm_atomic_helper_commit_hw_done ( state ) ;
drm_atomic_helper_cleanup_planes ( dev , state ) ;
2019-08-29 09:45:18 -07:00
trace_msm_atomic_commit_tail_finish ( async , crtc_mask ) ;
2019-08-29 09:45:16 -07:00
return ;
}
/*
* If there is any async flush pending on updated crtcs , fold
* them into the current flush .
*/
kms - > pending_crtc_mask & = ~ crtc_mask ;
2019-08-29 09:45:14 -07:00
/*
* Flush hardware updates :
*/
2019-08-29 09:45:18 -07:00
trace_msm_atomic_flush_commit ( crtc_mask ) ;
2019-08-29 09:45:14 -07:00
kms - > funcs - > flush_commit ( kms , crtc_mask ) ;
2019-08-29 09:45:16 -07:00
mutex_unlock ( & kms - > commit_lock ) ;
2018-06-27 13:49:23 -04:00
2019-08-29 09:45:16 -07:00
/*
* Wait for flush to complete :
*/
2019-08-29 09:45:18 -07:00
trace_msm_atomic_wait_flush_start ( crtc_mask ) ;
2019-08-29 09:45:12 -07:00
kms - > funcs - > wait_flush ( kms , crtc_mask ) ;
2019-08-29 09:45:18 -07:00
trace_msm_atomic_wait_flush_finish ( crtc_mask ) ;
2019-08-29 09:45:16 -07:00
mutex_lock ( & kms - > commit_lock ) ;
2019-08-29 09:45:13 -07:00
kms - > funcs - > complete_commit ( kms , crtc_mask ) ;
2019-08-29 09:45:16 -07:00
mutex_unlock ( & kms - > commit_lock ) ;
2019-08-29 09:45:15 -07:00
kms - > funcs - > disable_commit ( kms ) ;
2018-02-28 14:19:01 -05:00
drm_atomic_helper_commit_hw_done ( state ) ;
drm_atomic_helper_cleanup_planes ( dev , state ) ;
2019-08-29 09:45:18 -07:00
trace_msm_atomic_commit_tail_finish ( async , crtc_mask ) ;
2018-02-28 14:18:58 -05:00
}