2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-01-04 18:36:34 +01:00
/*
* Copyright ( c ) 2015 MediaTek Inc .
*/
2019-07-16 08:42:20 +02:00
# include <linux/clk.h>
# include <linux/pm_runtime.h>
2019-12-10 13:05:26 +08:00
# include <linux/soc/mediatek/mtk-cmdq.h>
2019-07-16 08:42:20 +02:00
2016-01-04 18:36:34 +01:00
# include <asm/barrier.h>
2019-07-16 08:42:20 +02:00
# include <soc/mediatek/smi.h>
2016-01-04 18:36:34 +01:00
# include <drm/drm_atomic_helper.h>
# include <drm/drm_plane_helper.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2019-07-16 08:42:20 +02:00
# include <drm/drm_vblank.h>
2016-01-04 18:36:34 +01:00
# include "mtk_drm_drv.h"
# include "mtk_drm_crtc.h"
# include "mtk_drm_ddp.h"
# include "mtk_drm_ddp_comp.h"
# include "mtk_drm_gem.h"
# include "mtk_drm_plane.h"
/**
* struct mtk_drm_crtc - MediaTek specific crtc structure .
* @ base : crtc object .
* @ enabled : records whether crtc_enable succeeded
2016-08-04 10:59:53 +08:00
* @ planes : array of 4 drm_plane structures , one for each overlay plane
2016-01-04 18:36:34 +01:00
* @ pending_planes : whether any plane has pending changes to be applied
* @ config_regs : memory mapped mmsys configuration register space
* @ mutex : handle to one of the ten disp_mutex streams
* @ ddp_comp_nr : number of components in ddp_comp
* @ ddp_comp : array of pointers the mtk_ddp_comp structures used by this crtc
*/
struct mtk_drm_crtc {
struct drm_crtc base ;
bool enabled ;
bool pending_needs_vblank ;
struct drm_pending_vblank_event * event ;
2018-08-09 10:15:47 +08:00
struct drm_plane * planes ;
unsigned int layer_nr ;
2016-01-04 18:36:34 +01:00
bool pending_planes ;
2019-12-10 13:05:22 +08:00
bool pending_async_planes ;
2016-01-04 18:36:34 +01:00
2019-12-10 13:05:26 +08:00
# if IS_REACHABLE(CONFIG_MTK_CMDQ)
struct cmdq_client * cmdq_client ;
u32 cmdq_event ;
# endif
2016-01-04 18:36:34 +01:00
void __iomem * config_regs ;
struct mtk_disp_mutex * mutex ;
unsigned int ddp_comp_nr ;
struct mtk_ddp_comp * * ddp_comp ;
2019-12-10 13:05:22 +08:00
/* lock for display hardware access */
struct mutex hw_lock ;
2016-01-04 18:36:34 +01:00
} ;
struct mtk_crtc_state {
struct drm_crtc_state base ;
bool pending_config ;
unsigned int pending_width ;
unsigned int pending_height ;
unsigned int pending_vrefresh ;
} ;
static inline struct mtk_drm_crtc * to_mtk_crtc ( struct drm_crtc * c )
{
return container_of ( c , struct mtk_drm_crtc , base ) ;
}
static inline struct mtk_crtc_state * to_mtk_crtc_state ( struct drm_crtc_state * s )
{
return container_of ( s , struct mtk_crtc_state , base ) ;
}
static void mtk_drm_crtc_finish_page_flip ( struct mtk_drm_crtc * mtk_crtc )
{
struct drm_crtc * crtc = & mtk_crtc - > base ;
unsigned long flags ;
spin_lock_irqsave ( & crtc - > dev - > event_lock , flags ) ;
drm_crtc_send_vblank_event ( crtc , mtk_crtc - > event ) ;
drm_crtc_vblank_put ( crtc ) ;
mtk_crtc - > event = NULL ;
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
}
static void mtk_drm_finish_page_flip ( struct mtk_drm_crtc * mtk_crtc )
{
drm_crtc_handle_vblank ( & mtk_crtc - > base ) ;
if ( mtk_crtc - > pending_needs_vblank ) {
mtk_drm_crtc_finish_page_flip ( mtk_crtc ) ;
mtk_crtc - > pending_needs_vblank = false ;
}
}
static void mtk_drm_crtc_destroy ( struct drm_crtc * crtc )
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
mtk_disp_mutex_put ( mtk_crtc - > mutex ) ;
drm_crtc_cleanup ( crtc ) ;
}
static void mtk_drm_crtc_reset ( struct drm_crtc * crtc )
{
struct mtk_crtc_state * state ;
if ( crtc - > state ) {
2016-08-04 10:59:54 +08:00
__drm_atomic_helper_crtc_destroy_state ( crtc - > state ) ;
2016-01-04 18:36:34 +01:00
state = to_mtk_crtc_state ( crtc - > state ) ;
memset ( state , 0 , sizeof ( * state ) ) ;
} else {
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
return ;
crtc - > state = & state - > base ;
}
state - > base . crtc = crtc ;
}
static struct drm_crtc_state * mtk_drm_crtc_duplicate_state ( struct drm_crtc * crtc )
{
struct mtk_crtc_state * state ;
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
return NULL ;
__drm_atomic_helper_crtc_duplicate_state ( crtc , & state - > base ) ;
WARN_ON ( state - > base . crtc ! = crtc ) ;
state - > base . crtc = crtc ;
return & state - > base ;
}
static void mtk_drm_crtc_destroy_state ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
2016-05-09 16:34:09 +02:00
__drm_atomic_helper_crtc_destroy_state ( state ) ;
2016-01-04 18:36:34 +01:00
kfree ( to_mtk_crtc_state ( state ) ) ;
}
static bool mtk_drm_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
/* Nothing to do here, but this callback is mandatory. */
return true ;
}
static void mtk_drm_crtc_mode_set_nofb ( struct drm_crtc * crtc )
{
struct mtk_crtc_state * state = to_mtk_crtc_state ( crtc - > state ) ;
state - > pending_width = crtc - > mode . hdisplay ;
state - > pending_height = crtc - > mode . vdisplay ;
state - > pending_vrefresh = crtc - > mode . vrefresh ;
wmb ( ) ; /* Make sure the above parameters are set before update */
state - > pending_config = true ;
}
2017-02-07 17:16:25 +08:00
static int mtk_drm_crtc_enable_vblank ( struct drm_crtc * crtc )
2016-01-04 18:36:34 +01:00
{
2017-01-09 19:25:42 +08:00
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
2018-08-09 10:15:48 +08:00
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ 0 ] ;
2016-01-04 18:36:34 +01:00
2018-08-09 10:15:48 +08:00
mtk_ddp_comp_enable_vblank ( comp , & mtk_crtc - > base ) ;
2016-01-04 18:36:34 +01:00
return 0 ;
}
2017-02-07 17:16:25 +08:00
static void mtk_drm_crtc_disable_vblank ( struct drm_crtc * crtc )
2016-01-04 18:36:34 +01:00
{
2017-01-09 19:25:42 +08:00
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
2018-08-09 10:15:48 +08:00
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ 0 ] ;
2016-01-04 18:36:34 +01:00
2018-08-09 10:15:48 +08:00
mtk_ddp_comp_disable_vblank ( comp ) ;
2016-01-04 18:36:34 +01:00
}
static int mtk_crtc_ddp_clk_enable ( struct mtk_drm_crtc * mtk_crtc )
{
int ret ;
int i ;
DRM_DEBUG_DRIVER ( " %s \n " , __func__ ) ;
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
2019-03-27 14:19:18 +08:00
ret = clk_prepare_enable ( mtk_crtc - > ddp_comp [ i ] - > clk ) ;
2016-01-04 18:36:34 +01:00
if ( ret ) {
DRM_ERROR ( " Failed to enable clock %d: %d \n " , i , ret ) ;
goto err ;
}
}
return 0 ;
err :
while ( - - i > = 0 )
2019-03-27 14:19:18 +08:00
clk_disable_unprepare ( mtk_crtc - > ddp_comp [ i ] - > clk ) ;
2016-01-04 18:36:34 +01:00
return ret ;
}
static void mtk_crtc_ddp_clk_disable ( struct mtk_drm_crtc * mtk_crtc )
{
int i ;
DRM_DEBUG_DRIVER ( " %s \n " , __func__ ) ;
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + )
2019-03-27 14:19:18 +08:00
clk_disable_unprepare ( mtk_crtc - > ddp_comp [ i ] - > clk ) ;
2016-01-04 18:36:34 +01:00
}
2019-11-05 16:10:19 -05:00
static
struct mtk_ddp_comp * mtk_drm_ddp_comp_for_plane ( struct drm_crtc * crtc ,
struct drm_plane * plane ,
unsigned int * local_layer )
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
struct mtk_ddp_comp * comp ;
int i , count = 0 ;
2019-11-27 18:04:19 +08:00
unsigned int local_index = plane - mtk_crtc - > planes ;
2019-11-05 16:10:19 -05:00
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
comp = mtk_crtc - > ddp_comp [ i ] ;
2019-11-27 18:04:19 +08:00
if ( local_index < ( count + mtk_ddp_comp_layer_nr ( comp ) ) ) {
* local_layer = local_index - count ;
2019-11-05 16:10:19 -05:00
return comp ;
}
count + = mtk_ddp_comp_layer_nr ( comp ) ;
}
WARN ( 1 , " Failed to find component for plane %d \n " , plane - > index ) ;
return NULL ;
}
2019-12-10 13:05:26 +08:00
# if IS_REACHABLE(CONFIG_MTK_CMDQ)
static void ddp_cmdq_cb ( struct cmdq_cb_data data )
{
cmdq_pkt_destroy ( data . data ) ;
}
# endif
2016-01-04 18:36:34 +01:00
static int mtk_crtc_ddp_hw_init ( struct mtk_drm_crtc * mtk_crtc )
{
struct drm_crtc * crtc = & mtk_crtc - > base ;
2016-07-28 10:22:55 +08:00
struct drm_connector * connector ;
struct drm_encoder * encoder ;
2017-05-11 16:10:45 -03:00
struct drm_connector_list_iter conn_iter ;
2016-07-28 10:22:55 +08:00
unsigned int width , height , vrefresh , bpc = MTK_MAX_BPC ;
2016-01-04 18:36:34 +01:00
int ret ;
int i ;
DRM_DEBUG_DRIVER ( " %s \n " , __func__ ) ;
if ( WARN_ON ( ! crtc - > state ) )
return - EINVAL ;
width = crtc - > state - > adjusted_mode . hdisplay ;
height = crtc - > state - > adjusted_mode . vdisplay ;
vrefresh = crtc - > state - > adjusted_mode . vrefresh ;
2016-07-28 10:22:55 +08:00
drm_for_each_encoder ( encoder , crtc - > dev ) {
if ( encoder - > crtc ! = crtc )
continue ;
2017-05-11 16:10:45 -03:00
drm_connector_list_iter_begin ( crtc - > dev , & conn_iter ) ;
drm_for_each_connector_iter ( connector , & conn_iter ) {
2016-07-28 10:22:55 +08:00
if ( connector - > encoder ! = encoder )
continue ;
if ( connector - > display_info . bpc ! = 0 & &
bpc > connector - > display_info . bpc )
bpc = connector - > display_info . bpc ;
}
2017-05-11 16:10:45 -03:00
drm_connector_list_iter_end ( & conn_iter ) ;
2016-07-28 10:22:55 +08:00
}
2016-01-04 18:36:34 +01:00
ret = pm_runtime_get_sync ( crtc - > dev - > dev ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to enable power domain: %d \n " , ret ) ;
return ret ;
}
ret = mtk_disp_mutex_prepare ( mtk_crtc - > mutex ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to enable mutex clock: %d \n " , ret ) ;
goto err_pm_runtime_put ;
}
ret = mtk_crtc_ddp_clk_enable ( mtk_crtc ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to enable component clocks: %d \n " , ret ) ;
goto err_mutex_unprepare ;
}
DRM_DEBUG_DRIVER ( " mediatek_ddp_ddp_path_setup \n " ) ;
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr - 1 ; i + + ) {
mtk_ddp_add_comp_to_path ( mtk_crtc - > config_regs ,
mtk_crtc - > ddp_comp [ i ] - > id ,
mtk_crtc - > ddp_comp [ i + 1 ] - > id ) ;
mtk_disp_mutex_add_comp ( mtk_crtc - > mutex ,
mtk_crtc - > ddp_comp [ i ] - > id ) ;
}
mtk_disp_mutex_add_comp ( mtk_crtc - > mutex , mtk_crtc - > ddp_comp [ i ] - > id ) ;
mtk_disp_mutex_enable ( mtk_crtc - > mutex ) ;
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ i ] ;
2019-08-29 22:50:44 +08:00
if ( i = = 1 )
mtk_ddp_comp_bgclr_in_on ( comp ) ;
2019-12-10 13:05:25 +08:00
mtk_ddp_comp_config ( comp , width , height , vrefresh , bpc , NULL ) ;
2016-01-04 18:36:34 +01:00
mtk_ddp_comp_start ( comp ) ;
}
/* Initially configure all planes */
2018-08-09 10:15:47 +08:00
for ( i = 0 ; i < mtk_crtc - > layer_nr ; i + + ) {
2016-08-04 10:59:53 +08:00
struct drm_plane * plane = & mtk_crtc - > planes [ i ] ;
2016-01-04 18:36:34 +01:00
struct mtk_plane_state * plane_state ;
2019-11-05 16:10:19 -05:00
struct mtk_ddp_comp * comp ;
2019-08-29 22:50:44 +08:00
unsigned int local_layer ;
2016-01-04 18:36:34 +01:00
plane_state = to_mtk_plane_state ( plane - > state ) ;
2019-11-05 16:10:19 -05:00
comp = mtk_drm_ddp_comp_for_plane ( crtc , plane , & local_layer ) ;
2019-11-18 14:18:05 +08:00
if ( comp )
mtk_ddp_comp_layer_config ( comp , local_layer ,
2019-12-10 13:05:25 +08:00
plane_state , NULL ) ;
2016-01-04 18:36:34 +01:00
}
return 0 ;
err_mutex_unprepare :
mtk_disp_mutex_unprepare ( mtk_crtc - > mutex ) ;
err_pm_runtime_put :
pm_runtime_put ( crtc - > dev - > dev ) ;
return ret ;
}
static void mtk_crtc_ddp_hw_fini ( struct mtk_drm_crtc * mtk_crtc )
{
struct drm_device * drm = mtk_crtc - > base . dev ;
2019-12-10 13:05:21 +08:00
struct drm_crtc * crtc = & mtk_crtc - > base ;
2016-01-04 18:36:34 +01:00
int i ;
DRM_DEBUG_DRIVER ( " %s \n " , __func__ ) ;
2019-08-29 22:50:44 +08:00
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
2016-01-04 18:36:34 +01:00
mtk_ddp_comp_stop ( mtk_crtc - > ddp_comp [ i ] ) ;
2019-08-29 22:50:44 +08:00
if ( i = = 1 )
mtk_ddp_comp_bgclr_in_off ( mtk_crtc - > ddp_comp [ i ] ) ;
}
2016-01-04 18:36:34 +01:00
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + )
mtk_disp_mutex_remove_comp ( mtk_crtc - > mutex ,
mtk_crtc - > ddp_comp [ i ] - > id ) ;
mtk_disp_mutex_disable ( mtk_crtc - > mutex ) ;
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr - 1 ; i + + ) {
mtk_ddp_remove_comp_from_path ( mtk_crtc - > config_regs ,
mtk_crtc - > ddp_comp [ i ] - > id ,
mtk_crtc - > ddp_comp [ i + 1 ] - > id ) ;
mtk_disp_mutex_remove_comp ( mtk_crtc - > mutex ,
mtk_crtc - > ddp_comp [ i ] - > id ) ;
}
mtk_disp_mutex_remove_comp ( mtk_crtc - > mutex , mtk_crtc - > ddp_comp [ i ] - > id ) ;
mtk_crtc_ddp_clk_disable ( mtk_crtc ) ;
mtk_disp_mutex_unprepare ( mtk_crtc - > mutex ) ;
pm_runtime_put ( drm - > dev ) ;
2019-12-10 13:05:21 +08:00
if ( crtc - > state - > event & & ! crtc - > state - > active ) {
spin_lock_irq ( & crtc - > dev - > event_lock ) ;
drm_crtc_send_vblank_event ( crtc , crtc - > state - > event ) ;
crtc - > state - > event = NULL ;
spin_unlock_irq ( & crtc - > dev - > event_lock ) ;
}
2016-01-04 18:36:34 +01:00
}
2019-12-10 13:05:26 +08:00
static void mtk_crtc_ddp_config ( struct drm_crtc * crtc ,
struct cmdq_pkt * cmdq_handle )
2017-03-31 19:30:31 +08:00
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
struct mtk_crtc_state * state = to_mtk_crtc_state ( mtk_crtc - > base . state ) ;
2018-08-09 10:15:48 +08:00
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ 0 ] ;
2017-03-31 19:30:31 +08:00
unsigned int i ;
2019-08-29 22:50:44 +08:00
unsigned int local_layer ;
2017-03-31 19:30:31 +08:00
/*
* TODO : instead of updating the registers here , we should prepare
* working registers in atomic_commit and let the hardware command
* queue update module registers on vblank .
*/
if ( state - > pending_config ) {
2018-08-09 10:15:48 +08:00
mtk_ddp_comp_config ( comp , state - > pending_width ,
2017-03-31 19:30:31 +08:00
state - > pending_height ,
2019-12-10 13:05:26 +08:00
state - > pending_vrefresh , 0 ,
cmdq_handle ) ;
2017-03-31 19:30:31 +08:00
state - > pending_config = false ;
}
if ( mtk_crtc - > pending_planes ) {
2018-08-09 10:15:47 +08:00
for ( i = 0 ; i < mtk_crtc - > layer_nr ; i + + ) {
2017-03-31 19:30:31 +08:00
struct drm_plane * plane = & mtk_crtc - > planes [ i ] ;
struct mtk_plane_state * plane_state ;
plane_state = to_mtk_plane_state ( plane - > state ) ;
2019-11-05 16:10:19 -05:00
if ( ! plane_state - > pending . config )
continue ;
comp = mtk_drm_ddp_comp_for_plane ( crtc , plane ,
& local_layer ) ;
2019-11-18 14:18:05 +08:00
if ( comp )
mtk_ddp_comp_layer_config ( comp , local_layer ,
2019-12-10 13:05:26 +08:00
plane_state ,
cmdq_handle ) ;
2019-11-05 16:10:19 -05:00
plane_state - > pending . config = false ;
2017-03-31 19:30:31 +08:00
}
mtk_crtc - > pending_planes = false ;
}
2019-12-10 13:05:22 +08:00
if ( mtk_crtc - > pending_async_planes ) {
for ( i = 0 ; i < mtk_crtc - > layer_nr ; i + + ) {
struct drm_plane * plane = & mtk_crtc - > planes [ i ] ;
struct mtk_plane_state * plane_state ;
plane_state = to_mtk_plane_state ( plane - > state ) ;
if ( ! plane_state - > pending . async_config )
continue ;
comp = mtk_drm_ddp_comp_for_plane ( crtc , plane ,
& local_layer ) ;
if ( comp )
mtk_ddp_comp_layer_config ( comp , local_layer ,
2019-12-10 13:05:26 +08:00
plane_state ,
cmdq_handle ) ;
2019-12-10 13:05:22 +08:00
plane_state - > pending . async_config = false ;
}
mtk_crtc - > pending_async_planes = false ;
}
}
static void mtk_drm_crtc_hw_config ( struct mtk_drm_crtc * mtk_crtc )
{
2019-12-10 13:05:26 +08:00
# if IS_REACHABLE(CONFIG_MTK_CMDQ)
struct cmdq_pkt * cmdq_handle ;
# endif
2019-12-10 13:05:22 +08:00
struct drm_crtc * crtc = & mtk_crtc - > base ;
struct mtk_drm_private * priv = crtc - > dev - > dev_private ;
unsigned int pending_planes = 0 , pending_async_planes = 0 ;
int i ;
mutex_lock ( & mtk_crtc - > hw_lock ) ;
for ( i = 0 ; i < mtk_crtc - > layer_nr ; i + + ) {
struct drm_plane * plane = & mtk_crtc - > planes [ i ] ;
struct mtk_plane_state * plane_state ;
plane_state = to_mtk_plane_state ( plane - > state ) ;
if ( plane_state - > pending . dirty ) {
plane_state - > pending . config = true ;
plane_state - > pending . dirty = false ;
pending_planes | = BIT ( i ) ;
} else if ( plane_state - > pending . async_dirty ) {
plane_state - > pending . async_config = true ;
plane_state - > pending . async_dirty = false ;
pending_async_planes | = BIT ( i ) ;
}
}
if ( pending_planes )
mtk_crtc - > pending_planes = true ;
if ( pending_async_planes )
mtk_crtc - > pending_async_planes = true ;
if ( priv - > data - > shadow_register ) {
mtk_disp_mutex_acquire ( mtk_crtc - > mutex ) ;
2019-12-10 13:05:26 +08:00
mtk_crtc_ddp_config ( crtc , NULL ) ;
2019-12-10 13:05:22 +08:00
mtk_disp_mutex_release ( mtk_crtc - > mutex ) ;
}
2019-12-10 13:05:26 +08:00
# if IS_REACHABLE(CONFIG_MTK_CMDQ)
if ( mtk_crtc - > cmdq_client ) {
2020-02-17 17:10:19 +08:00
mbox_flush ( mtk_crtc - > cmdq_client - > chan , 2000 ) ;
2019-12-10 13:05:26 +08:00
cmdq_handle = cmdq_pkt_create ( mtk_crtc - > cmdq_client , PAGE_SIZE ) ;
cmdq_pkt_clear_event ( cmdq_handle , mtk_crtc - > cmdq_event ) ;
cmdq_pkt_wfe ( cmdq_handle , mtk_crtc - > cmdq_event ) ;
mtk_crtc_ddp_config ( crtc , cmdq_handle ) ;
cmdq_pkt_flush_async ( cmdq_handle , ddp_cmdq_cb , cmdq_handle ) ;
}
# endif
2019-12-10 13:05:22 +08:00
mutex_unlock ( & mtk_crtc - > hw_lock ) ;
2017-03-31 19:30:31 +08:00
}
2019-11-05 16:10:20 -05:00
int mtk_drm_crtc_plane_check ( struct drm_crtc * crtc , struct drm_plane * plane ,
struct mtk_plane_state * state )
{
unsigned int local_layer ;
struct mtk_ddp_comp * comp ;
comp = mtk_drm_ddp_comp_for_plane ( crtc , plane , & local_layer ) ;
2019-11-18 14:18:05 +08:00
if ( comp )
return mtk_ddp_comp_layer_check ( comp , local_layer , state ) ;
return 0 ;
2019-11-05 16:10:20 -05:00
}
2019-12-10 13:05:22 +08:00
void mtk_drm_crtc_async_update ( struct drm_crtc * crtc , struct drm_plane * plane ,
struct drm_plane_state * new_state )
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
const struct drm_plane_helper_funcs * plane_helper_funcs =
plane - > helper_private ;
if ( ! mtk_crtc - > enabled )
return ;
plane_helper_funcs - > atomic_update ( plane , new_state ) ;
mtk_drm_crtc_hw_config ( mtk_crtc ) ;
}
2017-06-30 12:36:44 +03:00
static void mtk_drm_crtc_atomic_enable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2016-01-04 18:36:34 +01:00
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
2018-08-09 10:15:48 +08:00
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ 0 ] ;
2016-01-04 18:36:34 +01:00
int ret ;
DRM_DEBUG_DRIVER ( " %s %d \n " , __func__ , crtc - > base . id ) ;
2018-08-09 10:15:48 +08:00
ret = mtk_smi_larb_get ( comp - > larb_dev ) ;
2016-01-04 18:36:34 +01:00
if ( ret ) {
DRM_ERROR ( " Failed to get larb: %d \n " , ret ) ;
return ;
}
ret = mtk_crtc_ddp_hw_init ( mtk_crtc ) ;
if ( ret ) {
2018-08-09 10:15:48 +08:00
mtk_smi_larb_put ( comp - > larb_dev ) ;
2016-01-04 18:36:34 +01:00
return ;
}
drm_crtc_vblank_on ( crtc ) ;
mtk_crtc - > enabled = true ;
}
2017-06-30 12:36:45 +03:00
static void mtk_drm_crtc_atomic_disable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2016-01-04 18:36:34 +01:00
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
2018-08-09 10:15:48 +08:00
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ 0 ] ;
2016-01-04 18:36:34 +01:00
int i ;
DRM_DEBUG_DRIVER ( " %s %d \n " , __func__ , crtc - > base . id ) ;
if ( ! mtk_crtc - > enabled )
return ;
/* Set all pending plane state to disabled */
2018-08-09 10:15:47 +08:00
for ( i = 0 ; i < mtk_crtc - > layer_nr ; i + + ) {
2016-08-04 10:59:53 +08:00
struct drm_plane * plane = & mtk_crtc - > planes [ i ] ;
2016-01-04 18:36:34 +01:00
struct mtk_plane_state * plane_state ;
plane_state = to_mtk_plane_state ( plane - > state ) ;
plane_state - > pending . enable = false ;
plane_state - > pending . config = true ;
}
mtk_crtc - > pending_planes = true ;
2019-12-10 13:05:23 +08:00
mtk_drm_crtc_hw_config ( mtk_crtc ) ;
2016-01-04 18:36:34 +01:00
/* Wait for planes to be disabled */
drm_crtc_wait_one_vblank ( crtc ) ;
drm_crtc_vblank_off ( crtc ) ;
mtk_crtc_ddp_hw_fini ( mtk_crtc ) ;
2018-08-09 10:15:48 +08:00
mtk_smi_larb_put ( comp - > larb_dev ) ;
2016-01-04 18:36:34 +01:00
mtk_crtc - > enabled = false ;
}
static void mtk_drm_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
{
struct mtk_crtc_state * state = to_mtk_crtc_state ( crtc - > state ) ;
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
if ( mtk_crtc - > event & & state - > base . event )
DRM_ERROR ( " new event while there is still a pending event \n " ) ;
if ( state - > base . event ) {
state - > base . event - > pipe = drm_crtc_index ( crtc ) ;
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
mtk_crtc - > event = state - > base . event ;
state - > base . event = NULL ;
}
}
static void mtk_drm_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
int i ;
if ( mtk_crtc - > event )
mtk_crtc - > pending_needs_vblank = true ;
2016-07-28 10:22:54 +08:00
if ( crtc - > state - > color_mgmt_changed )
2019-12-13 15:28:52 +08:00
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
2016-07-28 10:22:54 +08:00
mtk_ddp_gamma_set ( mtk_crtc - > ddp_comp [ i ] , crtc - > state ) ;
2019-12-13 15:28:52 +08:00
mtk_ddp_ctm_set ( mtk_crtc - > ddp_comp [ i ] , crtc - > state ) ;
}
2019-12-10 13:05:22 +08:00
mtk_drm_crtc_hw_config ( mtk_crtc ) ;
2016-01-04 18:36:34 +01:00
}
static const struct drm_crtc_funcs mtk_crtc_funcs = {
. set_config = drm_atomic_helper_set_config ,
. page_flip = drm_atomic_helper_page_flip ,
. destroy = mtk_drm_crtc_destroy ,
. reset = mtk_drm_crtc_reset ,
. atomic_duplicate_state = mtk_drm_crtc_duplicate_state ,
. atomic_destroy_state = mtk_drm_crtc_destroy_state ,
2016-07-28 10:22:54 +08:00
. gamma_set = drm_atomic_helper_legacy_gamma_set ,
2017-02-07 17:16:25 +08:00
. enable_vblank = mtk_drm_crtc_enable_vblank ,
. disable_vblank = mtk_drm_crtc_disable_vblank ,
2016-01-04 18:36:34 +01:00
} ;
static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
. mode_fixup = mtk_drm_crtc_mode_fixup ,
. mode_set_nofb = mtk_drm_crtc_mode_set_nofb ,
. atomic_begin = mtk_drm_crtc_atomic_begin ,
. atomic_flush = mtk_drm_crtc_atomic_flush ,
2017-06-30 12:36:44 +03:00
. atomic_enable = mtk_drm_crtc_atomic_enable ,
2017-06-30 12:36:45 +03:00
. atomic_disable = mtk_drm_crtc_atomic_disable ,
2016-01-04 18:36:34 +01:00
} ;
static int mtk_drm_crtc_init ( struct drm_device * drm ,
struct mtk_drm_crtc * mtk_crtc ,
2020-02-07 15:23:51 +11:00
unsigned int pipe )
2016-01-04 18:36:34 +01:00
{
2020-02-07 15:23:51 +11:00
struct drm_plane * primary = NULL ;
struct drm_plane * cursor = NULL ;
int i , ret ;
for ( i = 0 ; i < mtk_crtc - > layer_nr ; i + + ) {
if ( mtk_crtc - > planes [ i ] . type = = DRM_PLANE_TYPE_PRIMARY )
primary = & mtk_crtc - > planes [ i ] ;
else if ( mtk_crtc - > planes [ i ] . type = = DRM_PLANE_TYPE_CURSOR )
cursor = & mtk_crtc - > planes [ i ] ;
}
2016-01-04 18:36:34 +01:00
ret = drm_crtc_init_with_planes ( drm , & mtk_crtc - > base , primary , cursor ,
& mtk_crtc_funcs , NULL ) ;
if ( ret )
goto err_cleanup_crtc ;
drm_crtc_helper_add ( & mtk_crtc - > base , & mtk_crtc_helper_funcs ) ;
return 0 ;
err_cleanup_crtc :
drm_crtc_cleanup ( & mtk_crtc - > base ) ;
return ret ;
}
2018-08-09 10:15:48 +08:00
void mtk_crtc_ddp_irq ( struct drm_crtc * crtc , struct mtk_ddp_comp * comp )
2016-01-04 18:36:34 +01:00
{
struct mtk_drm_crtc * mtk_crtc = to_mtk_crtc ( crtc ) ;
2017-03-31 19:30:31 +08:00
struct mtk_drm_private * priv = crtc - > dev - > dev_private ;
2016-01-04 18:36:34 +01:00
2019-12-10 13:05:26 +08:00
# if IS_REACHABLE(CONFIG_MTK_CMDQ)
if ( ! priv - > data - > shadow_register & & ! mtk_crtc - > cmdq_client )
# else
2017-03-31 19:30:31 +08:00
if ( ! priv - > data - > shadow_register )
2019-12-10 13:05:26 +08:00
# endif
mtk_crtc_ddp_config ( crtc , NULL ) ;
2016-01-04 18:36:34 +01:00
mtk_drm_finish_page_flip ( mtk_crtc ) ;
}
2019-11-05 16:10:18 -05:00
static int mtk_drm_crtc_num_comp_planes ( struct mtk_drm_crtc * mtk_crtc ,
int comp_idx )
{
struct mtk_ddp_comp * comp ;
if ( comp_idx > 1 )
return 0 ;
comp = mtk_crtc - > ddp_comp [ comp_idx ] ;
if ( ! comp - > funcs )
return 0 ;
if ( comp_idx = = 1 & & ! comp - > funcs - > bgclr_in_on )
return 0 ;
return mtk_ddp_comp_layer_nr ( comp ) ;
}
static inline
2020-01-30 14:24:55 -05:00
enum drm_plane_type mtk_drm_crtc_plane_type ( unsigned int plane_idx ,
unsigned int num_planes )
2019-11-05 16:10:18 -05:00
{
if ( plane_idx = = 0 )
return DRM_PLANE_TYPE_PRIMARY ;
2020-01-30 14:24:55 -05:00
else if ( plane_idx = = ( num_planes - 1 ) )
2019-11-05 16:10:18 -05:00
return DRM_PLANE_TYPE_CURSOR ;
else
return DRM_PLANE_TYPE_OVERLAY ;
}
static int mtk_drm_crtc_init_comp_planes ( struct drm_device * drm_dev ,
struct mtk_drm_crtc * mtk_crtc ,
int comp_idx , int pipe )
{
int num_planes = mtk_drm_crtc_num_comp_planes ( mtk_crtc , comp_idx ) ;
2019-11-05 16:10:21 -05:00
struct mtk_ddp_comp * comp = mtk_crtc - > ddp_comp [ comp_idx ] ;
2019-11-05 16:10:18 -05:00
int i , ret ;
for ( i = 0 ; i < num_planes ; i + + ) {
ret = mtk_plane_init ( drm_dev ,
& mtk_crtc - > planes [ mtk_crtc - > layer_nr ] ,
BIT ( pipe ) ,
2020-01-30 14:24:55 -05:00
mtk_drm_crtc_plane_type ( mtk_crtc - > layer_nr ,
num_planes ) ,
2019-11-05 16:10:21 -05:00
mtk_ddp_comp_supported_rotations ( comp ) ) ;
2019-11-05 16:10:18 -05:00
if ( ret )
return ret ;
mtk_crtc - > layer_nr + + ;
}
return 0 ;
}
2016-01-04 18:36:34 +01:00
int mtk_drm_crtc_create ( struct drm_device * drm_dev ,
const enum mtk_ddp_comp_id * path , unsigned int path_len )
{
struct mtk_drm_private * priv = drm_dev - > dev_private ;
struct device * dev = drm_dev - > dev ;
struct mtk_drm_crtc * mtk_crtc ;
2019-11-05 16:10:18 -05:00
unsigned int num_comp_planes = 0 ;
2016-01-04 18:36:34 +01:00
int pipe = priv - > num_pipes ;
int ret ;
int i ;
2019-12-13 15:28:52 +08:00
bool has_ctm = false ;
2019-12-13 15:28:51 +08:00
uint gamma_lut_size = 0 ;
2016-01-04 18:36:34 +01:00
2018-06-20 16:19:30 +08:00
if ( ! path )
return 0 ;
2016-01-04 18:36:34 +01:00
for ( i = 0 ; i < path_len ; i + + ) {
enum mtk_ddp_comp_id comp_id = path [ i ] ;
struct device_node * node ;
node = priv - > comp_node [ comp_id ] ;
if ( ! node ) {
dev_info ( dev ,
" Not creating crtc %d because component %d is disabled or missing \n " ,
pipe , comp_id ) ;
return 0 ;
}
}
mtk_crtc = devm_kzalloc ( dev , sizeof ( * mtk_crtc ) , GFP_KERNEL ) ;
if ( ! mtk_crtc )
return - ENOMEM ;
mtk_crtc - > config_regs = priv - > config_regs ;
mtk_crtc - > ddp_comp_nr = path_len ;
mtk_crtc - > ddp_comp = devm_kmalloc_array ( dev , mtk_crtc - > ddp_comp_nr ,
sizeof ( * mtk_crtc - > ddp_comp ) ,
GFP_KERNEL ) ;
2017-06-09 21:27:12 +02:00
if ( ! mtk_crtc - > ddp_comp )
return - ENOMEM ;
2016-01-04 18:36:34 +01:00
mtk_crtc - > mutex = mtk_disp_mutex_get ( priv - > mutex_dev , pipe ) ;
if ( IS_ERR ( mtk_crtc - > mutex ) ) {
ret = PTR_ERR ( mtk_crtc - > mutex ) ;
dev_err ( dev , " Failed to get mutex: %d \n " , ret ) ;
return ret ;
}
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
enum mtk_ddp_comp_id comp_id = path [ i ] ;
struct mtk_ddp_comp * comp ;
struct device_node * node ;
node = priv - > comp_node [ comp_id ] ;
comp = priv - > ddp_comp [ comp_id ] ;
if ( ! comp ) {
2017-07-18 16:43:04 -05:00
dev_err ( dev , " Component %pOF not initialized \n " , node ) ;
2016-01-04 18:36:34 +01:00
ret = - ENODEV ;
2019-03-27 14:19:18 +08:00
return ret ;
2016-01-04 18:36:34 +01:00
}
mtk_crtc - > ddp_comp [ i ] = comp ;
2019-12-13 15:28:51 +08:00
2019-12-13 15:28:52 +08:00
if ( comp - > funcs ) {
if ( comp - > funcs - > gamma_set )
gamma_lut_size = MTK_LUT_SIZE ;
if ( comp - > funcs - > ctm_set )
has_ctm = true ;
}
2016-01-04 18:36:34 +01:00
}
2019-11-05 16:10:18 -05:00
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + )
num_comp_planes + = mtk_drm_crtc_num_comp_planes ( mtk_crtc , i ) ;
mtk_crtc - > planes = devm_kcalloc ( dev , num_comp_planes ,
sizeof ( struct drm_plane ) , GFP_KERNEL ) ;
2019-08-29 22:50:44 +08:00
2019-11-05 16:10:18 -05:00
for ( i = 0 ; i < mtk_crtc - > ddp_comp_nr ; i + + ) {
ret = mtk_drm_crtc_init_comp_planes ( drm_dev , mtk_crtc , i ,
pipe ) ;
2016-01-04 18:36:34 +01:00
if ( ret )
2019-03-27 14:19:18 +08:00
return ret ;
2016-01-04 18:36:34 +01:00
}
2020-02-07 15:23:51 +11:00
ret = mtk_drm_crtc_init ( drm_dev , mtk_crtc , pipe ) ;
2016-01-04 18:36:34 +01:00
if ( ret < 0 )
2019-03-27 14:19:18 +08:00
return ret ;
2019-12-13 15:28:51 +08:00
if ( gamma_lut_size )
drm_mode_crtc_set_gamma_size ( & mtk_crtc - > base , gamma_lut_size ) ;
2019-12-13 15:28:52 +08:00
drm_crtc_enable_color_mgmt ( & mtk_crtc - > base , 0 , has_ctm , gamma_lut_size ) ;
2016-01-04 18:36:34 +01:00
priv - > num_pipes + + ;
2019-12-10 13:05:22 +08:00
mutex_init ( & mtk_crtc - > hw_lock ) ;
2016-01-04 18:36:34 +01:00
2019-12-10 13:05:26 +08:00
# if IS_REACHABLE(CONFIG_MTK_CMDQ)
mtk_crtc - > cmdq_client =
cmdq_mbox_create ( dev , drm_crtc_index ( & mtk_crtc - > base ) ,
2000 ) ;
if ( IS_ERR ( mtk_crtc - > cmdq_client ) ) {
dev_dbg ( dev , " mtk_crtc %d failed to create mailbox client, writing register by CPU now \n " ,
drm_crtc_index ( & mtk_crtc - > base ) ) ;
mtk_crtc - > cmdq_client = NULL ;
}
2020-02-17 17:10:20 +08:00
ret = of_property_read_u32_index ( priv - > mutex_node ,
" mediatek,gce-events " ,
2019-12-10 13:05:26 +08:00
drm_crtc_index ( & mtk_crtc - > base ) ,
& mtk_crtc - > cmdq_event ) ;
if ( ret )
dev_dbg ( dev , " mtk_crtc %d failed to get mediatek,gce-events property \n " ,
drm_crtc_index ( & mtk_crtc - > base ) ) ;
# endif
2016-01-04 18:36:34 +01:00
return 0 ;
}