2016-07-24 14:57:44 +08:00
/*
* Copyright ( C ) Fuzhou Rockchip Electronics Co . Ltd
* Author : Yakir Yang < ykk @ rock - chips . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <drm/drmP.h>
2018-12-05 10:33:10 -02:00
# include <drm/drm_atomic.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2016-07-24 14:57:44 +08:00
# include "rockchip_drm_drv.h"
# include "rockchip_drm_psr.h"
2018-03-05 23:22:54 +01:00
# define PSR_FLUSH_TIMEOUT_MS 100
2016-07-24 14:57:44 +08:00
struct psr_drv {
struct list_head list ;
struct drm_encoder * encoder ;
2018-03-05 23:22:54 +01:00
struct mutex lock ;
2018-04-23 12:50:01 +02:00
int inhibit_count ;
2018-04-23 12:50:00 +02:00
bool enabled ;
2016-07-24 14:57:44 +08:00
2018-03-05 23:22:54 +01:00
struct delayed_work flush_work ;
2016-07-24 14:57:44 +08:00
2018-04-23 12:49:49 +02:00
int ( * set ) ( struct drm_encoder * encoder , bool enable ) ;
2016-07-24 14:57:44 +08:00
} ;
2018-03-05 23:22:53 +01:00
static struct psr_drv * find_psr_by_encoder ( struct drm_encoder * encoder )
{
struct rockchip_drm_private * drm_drv = encoder - > dev - > dev_private ;
struct psr_drv * psr ;
2018-03-05 23:22:54 +01:00
mutex_lock ( & drm_drv - > psr_list_lock ) ;
2018-03-05 23:22:53 +01:00
list_for_each_entry ( psr , & drm_drv - > psr_list , list ) {
if ( psr - > encoder = = encoder )
goto out ;
}
psr = ERR_PTR ( - ENODEV ) ;
out :
2018-03-05 23:22:54 +01:00
mutex_unlock ( & drm_drv - > psr_list_lock ) ;
2018-03-05 23:22:53 +01:00
return psr ;
}
2018-04-23 12:50:00 +02:00
static int psr_set_state_locked ( struct psr_drv * psr , bool enable )
2016-07-24 14:57:44 +08:00
{
2018-04-23 12:50:00 +02:00
int ret ;
2016-09-06 14:11:53 -04:00
2018-04-23 12:50:01 +02:00
if ( psr - > inhibit_count > 0 )
2018-04-23 12:50:00 +02:00
return - EINVAL ;
2018-04-23 12:49:49 +02:00
2018-04-23 12:50:00 +02:00
if ( enable = = psr - > enabled )
return 0 ;
2016-07-24 14:57:44 +08:00
2018-04-23 12:50:00 +02:00
ret = psr - > set ( psr - > encoder , enable ) ;
if ( ret )
return ret ;
psr - > enabled = enable ;
return 0 ;
2016-08-16 16:28:31 -07:00
}
2018-03-05 23:22:54 +01:00
static void psr_flush_handler ( struct work_struct * work )
2016-07-24 14:57:44 +08:00
{
2018-03-05 23:22:54 +01:00
struct psr_drv * psr = container_of ( to_delayed_work ( work ) ,
struct psr_drv , flush_work ) ;
2016-07-24 14:57:44 +08:00
2018-03-05 23:22:54 +01:00
mutex_lock ( & psr - > lock ) ;
2018-04-23 12:50:00 +02:00
psr_set_state_locked ( psr , true ) ;
2018-03-05 23:22:54 +01:00
mutex_unlock ( & psr - > lock ) ;
2016-07-24 14:57:44 +08:00
}
/**
2018-04-23 12:50:01 +02:00
* rockchip_drm_psr_inhibit_put - release PSR inhibit on given encoder
2018-03-05 23:22:53 +01:00
* @ encoder : encoder to obtain the PSR encoder
2016-07-24 14:57:44 +08:00
*
2018-04-23 12:50:01 +02:00
* Decrements PSR inhibit count on given encoder . Should be called only
* for a PSR inhibit count increment done before . If PSR inhibit counter
* reaches zero , PSR flush work is scheduled to make the hardware enter
* PSR mode in PSR_FLUSH_TIMEOUT_MS .
*
2016-07-24 14:57:44 +08:00
* Returns :
* Zero on success , negative errno on failure .
*/
2018-04-23 12:50:01 +02:00
int rockchip_drm_psr_inhibit_put ( struct drm_encoder * encoder )
2016-07-24 14:57:44 +08:00
{
2018-03-05 23:22:53 +01:00
struct psr_drv * psr = find_psr_by_encoder ( encoder ) ;
2016-07-24 14:57:44 +08:00
if ( IS_ERR ( psr ) )
return PTR_ERR ( psr ) ;
2018-03-05 23:22:54 +01:00
mutex_lock ( & psr - > lock ) ;
2018-04-23 12:50:01 +02:00
- - psr - > inhibit_count ;
WARN_ON ( psr - > inhibit_count < 0 ) ;
if ( ! psr - > inhibit_count )
mod_delayed_work ( system_wq , & psr - > flush_work ,
PSR_FLUSH_TIMEOUT_MS ) ;
2018-03-05 23:22:54 +01:00
mutex_unlock ( & psr - > lock ) ;
2016-08-18 12:01:46 -07:00
2016-07-24 14:57:44 +08:00
return 0 ;
}
2018-04-23 12:50:01 +02:00
EXPORT_SYMBOL ( rockchip_drm_psr_inhibit_put ) ;
2016-07-24 14:57:44 +08:00
2018-12-05 10:33:10 -02:00
void rockchip_drm_psr_inhibit_get_state ( struct drm_atomic_state * state )
{
struct drm_crtc * crtc ;
struct drm_crtc_state * crtc_state ;
struct drm_encoder * encoder ;
u32 encoder_mask = 0 ;
int i ;
for_each_old_crtc_in_state ( state , crtc , crtc_state , i ) {
encoder_mask | = crtc_state - > encoder_mask ;
encoder_mask | = crtc - > state - > encoder_mask ;
}
drm_for_each_encoder_mask ( encoder , state - > dev , encoder_mask )
rockchip_drm_psr_inhibit_get ( encoder ) ;
}
EXPORT_SYMBOL ( rockchip_drm_psr_inhibit_get_state ) ;
void rockchip_drm_psr_inhibit_put_state ( struct drm_atomic_state * state )
{
struct drm_crtc * crtc ;
struct drm_crtc_state * crtc_state ;
struct drm_encoder * encoder ;
u32 encoder_mask = 0 ;
int i ;
for_each_old_crtc_in_state ( state , crtc , crtc_state , i ) {
encoder_mask | = crtc_state - > encoder_mask ;
encoder_mask | = crtc - > state - > encoder_mask ;
}
drm_for_each_encoder_mask ( encoder , state - > dev , encoder_mask )
rockchip_drm_psr_inhibit_put ( encoder ) ;
}
EXPORT_SYMBOL ( rockchip_drm_psr_inhibit_put_state ) ;
2016-07-24 14:57:44 +08:00
/**
2018-04-23 12:50:01 +02:00
* rockchip_drm_psr_inhibit_get - acquire PSR inhibit on given encoder
2018-03-05 23:22:53 +01:00
* @ encoder : encoder to obtain the PSR encoder
2016-07-24 14:57:44 +08:00
*
2018-04-23 12:50:01 +02:00
* Increments PSR inhibit count on given encoder . This function guarantees
* that after it returns PSR is turned off on given encoder and no PSR - related
* hardware state change occurs at least until a matching call to
* rockchip_drm_psr_inhibit_put ( ) is done .
*
2016-07-24 14:57:44 +08:00
* Returns :
* Zero on success , negative errno on failure .
*/
2018-04-23 12:50:01 +02:00
int rockchip_drm_psr_inhibit_get ( struct drm_encoder * encoder )
2016-07-24 14:57:44 +08:00
{
2018-03-05 23:22:53 +01:00
struct psr_drv * psr = find_psr_by_encoder ( encoder ) ;
2016-08-18 12:01:46 -07:00
if ( IS_ERR ( psr ) )
return PTR_ERR ( psr ) ;
2018-03-05 23:22:54 +01:00
mutex_lock ( & psr - > lock ) ;
2018-04-23 12:50:01 +02:00
psr_set_state_locked ( psr , false ) ;
+ + psr - > inhibit_count ;
2018-03-05 23:22:54 +01:00
mutex_unlock ( & psr - > lock ) ;
cancel_delayed_work_sync ( & psr - > flush_work ) ;
2016-08-18 12:01:46 -07:00
return 0 ;
}
2018-04-23 12:50:01 +02:00
EXPORT_SYMBOL ( rockchip_drm_psr_inhibit_get ) ;
2016-08-18 12:01:46 -07:00
static void rockchip_drm_do_flush ( struct psr_drv * psr )
{
2018-04-23 12:50:00 +02:00
cancel_delayed_work_sync ( & psr - > flush_work ) ;
mutex_lock ( & psr - > lock ) ;
if ( ! psr_set_state_locked ( psr , false ) )
mod_delayed_work ( system_wq , & psr - > flush_work ,
PSR_FLUSH_TIMEOUT_MS ) ;
mutex_unlock ( & psr - > lock ) ;
2016-08-18 12:01:46 -07:00
}
2016-07-24 14:57:44 +08:00
/**
2016-08-18 12:01:46 -07:00
* rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
2016-07-24 14:57:44 +08:00
* @ dev : drm device
*
* Disable the PSR function for all registered encoders , and then enable the
* PSR function back after PSR_FLUSH_TIMEOUT . If encoder PSR state have been
* changed during flush time , then keep the state no change after flush
* timeout .
*
* Returns :
* Zero on success , negative errno on failure .
*/
2016-08-18 12:01:46 -07:00
void rockchip_drm_psr_flush_all ( struct drm_device * dev )
2016-07-24 14:57:44 +08:00
{
struct rockchip_drm_private * drm_drv = dev - > dev_private ;
struct psr_drv * psr ;
2018-03-05 23:22:54 +01:00
mutex_lock ( & drm_drv - > psr_list_lock ) ;
2016-08-18 12:01:46 -07:00
list_for_each_entry ( psr , & drm_drv - > psr_list , list )
rockchip_drm_do_flush ( psr ) ;
2018-03-05 23:22:54 +01:00
mutex_unlock ( & drm_drv - > psr_list_lock ) ;
2016-07-24 14:57:44 +08:00
}
2016-08-18 12:01:46 -07:00
EXPORT_SYMBOL ( rockchip_drm_psr_flush_all ) ;
2016-07-24 14:57:44 +08:00
/**
* rockchip_drm_psr_register - register encoder to psr driver
* @ encoder : encoder that obtain the PSR function
* @ psr_set : call back to set PSR state
*
2018-04-23 12:50:01 +02:00
* The function returns with PSR inhibit counter initialized with one
* and the caller ( typically encoder driver ) needs to call
* rockchip_drm_psr_inhibit_put ( ) when it becomes ready to accept PSR
* enable request .
*
2016-07-24 14:57:44 +08:00
* Returns :
* Zero on success , negative errno on failure .
*/
int rockchip_drm_psr_register ( struct drm_encoder * encoder ,
2018-04-23 12:49:49 +02:00
int ( * psr_set ) ( struct drm_encoder * , bool enable ) )
2016-07-24 14:57:44 +08:00
{
2018-10-13 12:56:54 +02:00
struct rockchip_drm_private * drm_drv ;
2016-07-24 14:57:44 +08:00
struct psr_drv * psr ;
if ( ! encoder | | ! psr_set )
return - EINVAL ;
2018-10-13 12:56:54 +02:00
drm_drv = encoder - > dev - > dev_private ;
2016-07-24 14:57:44 +08:00
psr = kzalloc ( sizeof ( struct psr_drv ) , GFP_KERNEL ) ;
if ( ! psr )
return - ENOMEM ;
2018-03-05 23:22:54 +01:00
INIT_DELAYED_WORK ( & psr - > flush_work , psr_flush_handler ) ;
mutex_init ( & psr - > lock ) ;
2016-07-24 14:57:44 +08:00
2018-04-23 12:50:01 +02:00
psr - > inhibit_count = 1 ;
2018-04-23 12:50:00 +02:00
psr - > enabled = false ;
2016-07-24 14:57:44 +08:00
psr - > encoder = encoder ;
psr - > set = psr_set ;
2018-03-05 23:22:54 +01:00
mutex_lock ( & drm_drv - > psr_list_lock ) ;
2016-07-24 14:57:44 +08:00
list_add_tail ( & psr - > list , & drm_drv - > psr_list ) ;
2018-03-05 23:22:54 +01:00
mutex_unlock ( & drm_drv - > psr_list_lock ) ;
2016-07-24 14:57:44 +08:00
return 0 ;
}
EXPORT_SYMBOL ( rockchip_drm_psr_register ) ;
/**
* rockchip_drm_psr_unregister - unregister encoder to psr driver
* @ encoder : encoder that obtain the PSR function
* @ psr_set : call back to set PSR state
*
2018-04-23 12:50:01 +02:00
* It is expected that the PSR inhibit counter is 1 when this function is
* called , which corresponds to a state when related encoder has been
* disconnected from any CRTCs and its driver called
* rockchip_drm_psr_inhibit_get ( ) to stop the PSR logic .
*
2016-07-24 14:57:44 +08:00
* Returns :
* Zero on success , negative errno on failure .
*/
void rockchip_drm_psr_unregister ( struct drm_encoder * encoder )
{
struct rockchip_drm_private * drm_drv = encoder - > dev - > dev_private ;
struct psr_drv * psr , * n ;
2018-03-05 23:22:54 +01:00
mutex_lock ( & drm_drv - > psr_list_lock ) ;
2016-07-24 14:57:44 +08:00
list_for_each_entry_safe ( psr , n , & drm_drv - > psr_list , list ) {
if ( psr - > encoder = = encoder ) {
2018-04-23 12:50:01 +02:00
/*
* Any other value would mean that the encoder
* is still in use .
*/
WARN_ON ( psr - > inhibit_count ! = 1 ) ;
2016-07-24 14:57:44 +08:00
list_del ( & psr - > list ) ;
kfree ( psr ) ;
}
}
2018-03-05 23:22:54 +01:00
mutex_unlock ( & drm_drv - > psr_list_lock ) ;
2016-07-24 14:57:44 +08:00
}
EXPORT_SYMBOL ( rockchip_drm_psr_unregister ) ;