2019-06-01 10:08:38 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-06-12 21:59:00 +09:00
/* drivers/gpu/drm/exynos5433_drm_decon.c
*
* Copyright ( C ) 2015 Samsung Electronics Co . Ltd
* Authors :
* Joonyoung Shim < jy0922 . shim @ samsung . com >
* Hyungwon Hwang < human . hwang @ samsung . com >
*/
# include <linux/clk.h>
# include <linux/component.h>
2017-08-24 15:33:50 +02:00
# include <linux/iopoll.h>
2017-09-05 10:19:55 +02:00
# include <linux/irq.h>
2017-02-01 15:35:07 +09:00
# include <linux/mfd/syscon.h>
2015-10-20 18:22:41 +09:00
# include <linux/of_device.h>
2015-06-12 21:59:00 +09:00
# include <linux/of_gpio.h>
2019-06-24 22:06:28 +09:00
# include <linux/platform_device.h>
2015-06-12 21:59:00 +09:00
# include <linux/pm_runtime.h>
2017-02-01 15:35:07 +09:00
# include <linux/regmap.h>
2015-06-12 21:59:00 +09:00
2019-06-24 22:06:28 +09:00
# include <drm/drm_fourcc.h>
# include <drm/drm_vblank.h>
2015-06-12 21:59:00 +09:00
# include "exynos_drm_crtc.h"
2019-06-24 22:06:28 +09:00
# include "exynos_drm_drv.h"
2015-11-30 14:53:21 +01:00
# include "exynos_drm_fb.h"
2015-06-12 21:59:00 +09:00
# include "exynos_drm_plane.h"
2017-12-26 14:07:18 +01:00
# include "regs-decon5433.h"
2015-06-12 21:59:00 +09:00
2017-02-01 15:35:07 +09:00
# define DSD_CFG_MUX 0x1004
# define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13)
2018-05-23 13:00:02 +02:00
# define WINDOWS_NR 5
# define PRIMARY_WIN 2
# define CURSON_WIN 4
2015-06-12 21:59:00 +09:00
# define MIN_FB_WIDTH_FOR_16WORD_BURST 128
2017-08-24 15:33:57 +02:00
# define I80_HW_TRG (1 << 0)
# define IFTYPE_HDMI (1 << 1)
2016-04-18 17:59:01 +09:00
2015-10-20 11:22:34 +02:00
static const char * const decon_clks_name [ ] = {
" pclk " ,
" aclk_decon " ,
" aclk_smmu_decon0x " ,
" aclk_xiu_decon0x " ,
" pclk_smmu_decon0x " ,
2018-05-23 13:00:02 +02:00
" aclk_smmu_decon1x " ,
" aclk_xiu_decon1x " ,
" pclk_smmu_decon1x " ,
2015-10-20 11:22:34 +02:00
" sclk_decon_vclk " ,
" sclk_decon_eclk " ,
} ;
2015-06-12 21:59:00 +09:00
struct decon_context {
struct device * dev ;
struct drm_device * drm_dev ;
2020-03-09 12:02:14 +01:00
void * dma_priv ;
2015-06-12 21:59:00 +09:00
struct exynos_drm_crtc * crtc ;
struct exynos_drm_plane planes [ WINDOWS_NR ] ;
2015-11-30 14:53:25 +01:00
struct exynos_drm_plane_config configs [ WINDOWS_NR ] ;
2015-06-12 21:59:00 +09:00
void __iomem * addr ;
2017-02-01 15:35:07 +09:00
struct regmap * sysreg ;
2015-10-20 11:22:34 +02:00
struct clk * clks [ ARRAY_SIZE ( decon_clks_name ) ] ;
2017-04-05 09:28:32 +02:00
unsigned int irq ;
2017-08-24 15:33:55 +02:00
unsigned int irq_vsync ;
unsigned int irq_lcd_sys ;
2017-04-05 09:28:32 +02:00
unsigned int te_irq ;
2016-04-18 17:59:01 +09:00
unsigned long out_type ;
2015-10-20 18:22:41 +09:00
int first_win ;
2017-03-14 09:27:57 +01:00
spinlock_t vblank_lock ;
u32 frame_id ;
2015-06-12 21:59:00 +09:00
} ;
2015-08-31 00:53:57 +09:00
static const uint32_t decon_formats [ ] = {
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_ARGB8888 ,
} ;
2015-11-30 14:53:25 +01:00
static const enum drm_plane_type decon_win_types [ WINDOWS_NR ] = {
2018-05-23 13:00:02 +02:00
[ PRIMARY_WIN ] = DRM_PLANE_TYPE_PRIMARY ,
[ CURSON_WIN ] = DRM_PLANE_TYPE_CURSOR ,
2015-11-30 14:53:25 +01:00
} ;
2018-10-25 18:21:52 +02:00
static const unsigned int capabilities [ WINDOWS_NR ] = {
0 ,
2018-10-25 18:21:53 +02:00
EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND ,
EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND ,
EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND ,
EXYNOS_DRM_PLANE_CAP_WIN_BLEND | EXYNOS_DRM_PLANE_CAP_PIX_BLEND ,
2018-10-25 18:21:52 +02:00
} ;
2015-10-20 11:22:37 +02:00
static inline void decon_set_bits ( struct decon_context * ctx , u32 reg , u32 mask ,
u32 val )
{
val = ( val & mask ) | ( readl ( ctx - > addr + reg ) & ~ mask ) ;
writel ( val , ctx - > addr + reg ) ;
}
2015-06-12 21:59:00 +09:00
static int decon_enable_vblank ( struct exynos_drm_crtc * crtc )
{
struct decon_context * ctx = crtc - > ctx ;
u32 val ;
2017-03-15 15:41:09 +01:00
val = VIDINTCON0_INTEN ;
2017-08-24 15:33:57 +02:00
if ( crtc - > i80_mode )
2017-03-15 15:41:09 +01:00
val | = VIDINTCON0_FRAMEDONE ;
else
val | = VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP ;
2015-06-12 21:59:00 +09:00
2017-03-15 15:41:09 +01:00
writel ( val , ctx - > addr + DECON_VIDINTCON0 ) ;
2017-04-05 09:28:32 +02:00
enable_irq ( ctx - > irq ) ;
if ( ! ( ctx - > out_type & I80_HW_TRG ) )
enable_irq ( ctx - > te_irq ) ;
2015-06-12 21:59:00 +09:00
return 0 ;
}
static void decon_disable_vblank ( struct exynos_drm_crtc * crtc )
{
struct decon_context * ctx = crtc - > ctx ;
2017-04-05 09:28:32 +02:00
if ( ! ( ctx - > out_type & I80_HW_TRG ) )
disable_irq_nosync ( ctx - > te_irq ) ;
disable_irq_nosync ( ctx - > irq ) ;
2017-03-15 15:41:09 +01:00
writel ( 0 , ctx - > addr + DECON_VIDINTCON0 ) ;
2015-06-12 21:59:00 +09:00
}
2017-03-14 09:27:57 +01:00
/* return number of starts/ends of frame transmissions since reset */
static u32 decon_get_frame_count ( struct decon_context * ctx , bool end )
{
u32 frm , pfrm , status , cnt = 2 ;
/* To get consistent result repeat read until frame id is stable.
* Usually the loop will be executed once , in rare cases when the loop
* is executed at frame change time 2 nd pass will be needed .
*/
frm = readl ( ctx - > addr + DECON_CRFMID ) ;
do {
status = readl ( ctx - > addr + DECON_VIDCON1 ) ;
pfrm = frm ;
frm = readl ( ctx - > addr + DECON_CRFMID ) ;
} while ( frm ! = pfrm & & - - cnt ) ;
/* CRFMID is incremented on BPORCH in case of I80 and on VSYNC in case
* of RGB , it should be taken into account .
*/
if ( ! frm )
return 0 ;
switch ( status & ( VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE ) ) {
case VIDCON1_VSTATUS_VS :
2017-08-24 15:33:57 +02:00
if ( ! ( ctx - > crtc - > i80_mode ) )
2017-03-14 09:27:57 +01:00
- - frm ;
break ;
case VIDCON1_VSTATUS_BP :
- - frm ;
break ;
case VIDCON1_I80_ACTIVE :
case VIDCON1_VSTATUS_AC :
if ( end )
- - frm ;
break ;
default :
break ;
}
return frm ;
}
2015-06-12 21:59:00 +09:00
static void decon_setup_trigger ( struct decon_context * ctx )
{
2017-08-24 15:33:57 +02:00
if ( ! ctx - > crtc - > i80_mode & & ! ( ctx - > out_type & I80_HW_TRG ) )
2017-02-01 15:35:07 +09:00
return ;
if ( ! ( ctx - > out_type & I80_HW_TRG ) ) {
2017-03-14 09:28:00 +01:00
writel ( TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN ,
2017-02-01 15:35:07 +09:00
ctx - > addr + DECON_TRIGCON ) ;
return ;
}
writel ( TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | TRIGCON_HWTRIGMASK
| TRIGCON_HWTRIGEN , ctx - > addr + DECON_TRIGCON ) ;
if ( regmap_update_bits ( ctx - > sysreg , DSD_CFG_MUX ,
DSD_CFG_MUX_TE_UNMASK_GLOBAL , ~ 0 ) )
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( ctx - > dev , " Cannot update sysreg. \n " ) ;
2015-06-12 21:59:00 +09:00
}
static void decon_commit ( struct exynos_drm_crtc * crtc )
{
struct decon_context * ctx = crtc - > ctx ;
2015-10-20 11:22:36 +02:00
struct drm_display_mode * m = & crtc - > base . mode ;
2017-01-20 07:52:23 +01:00
bool interlaced = false ;
2015-06-12 21:59:00 +09:00
u32 val ;
2016-04-18 17:59:01 +09:00
if ( ctx - > out_type & IFTYPE_HDMI ) {
2015-10-20 18:22:41 +09:00
m - > crtc_hsync_start = m - > crtc_hdisplay + 10 ;
m - > crtc_hsync_end = m - > crtc_htotal - 92 ;
m - > crtc_vsync_start = m - > crtc_vdisplay + 1 ;
m - > crtc_vsync_end = m - > crtc_vsync_start + 1 ;
2017-01-20 07:52:23 +01:00
if ( m - > flags & DRM_MODE_FLAG_INTERLACE )
interlaced = true ;
2015-10-20 18:22:41 +09:00
}
2017-02-01 15:35:07 +09:00
decon_setup_trigger ( ctx ) ;
2016-04-29 15:42:49 +02:00
2015-06-12 21:59:00 +09:00
/* lcd on and use command if */
val = VIDOUT_LCD_ON ;
2017-01-20 07:52:23 +01:00
if ( interlaced )
val | = VIDOUT_INTERLACE_EN_F ;
2017-08-24 15:33:57 +02:00
if ( crtc - > i80_mode ) {
2015-06-12 21:59:00 +09:00
val | = VIDOUT_COMMAND_IF ;
2016-04-18 17:59:01 +09:00
} else {
2015-06-12 21:59:00 +09:00
val | = VIDOUT_RGB_IF ;
2016-04-18 17:59:01 +09:00
}
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDOUTCON0 ) ;
2017-01-20 07:52:23 +01:00
if ( interlaced )
val = VIDTCON2_LINEVAL ( m - > vdisplay / 2 - 1 ) |
VIDTCON2_HOZVAL ( m - > hdisplay - 1 ) ;
else
val = VIDTCON2_LINEVAL ( m - > vdisplay - 1 ) |
VIDTCON2_HOZVAL ( m - > hdisplay - 1 ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDTCON2 ) ;
2017-08-24 15:33:57 +02:00
if ( ! crtc - > i80_mode ) {
2017-01-20 07:52:23 +01:00
int vbp = m - > crtc_vtotal - m - > crtc_vsync_end ;
int vfp = m - > crtc_vsync_start - m - > crtc_vdisplay ;
if ( interlaced )
vbp = vbp / 2 - 1 ;
val = VIDTCON00_VBPD_F ( vbp - 1 ) | VIDTCON00_VFPD_F ( vfp - 1 ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDTCON00 ) ;
val = VIDTCON01_VSPW_F (
2015-10-20 11:22:36 +02:00
m - > crtc_vsync_end - m - > crtc_vsync_start - 1 ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDTCON01 ) ;
val = VIDTCON10_HBPD_F (
2015-10-20 11:22:36 +02:00
m - > crtc_htotal - m - > crtc_hsync_end - 1 ) |
2015-06-12 21:59:00 +09:00
VIDTCON10_HFPD_F (
2015-10-20 11:22:36 +02:00
m - > crtc_hsync_start - m - > crtc_hdisplay - 1 ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDTCON10 ) ;
val = VIDTCON11_HSPW_F (
2015-10-20 11:22:36 +02:00
m - > crtc_hsync_end - m - > crtc_hsync_start - 1 ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDTCON11 ) ;
}
/* enable output and display signal */
2015-10-20 18:22:41 +09:00
decon_set_bits ( ctx , DECON_VIDCON0 , VIDCON0_ENVID | VIDCON0_ENVID_F , ~ 0 ) ;
2016-03-23 14:15:16 +01:00
decon_set_bits ( ctx , DECON_UPDATE , STANDALONE_UPDATE_F , ~ 0 ) ;
2015-06-12 21:59:00 +09:00
}
2018-10-25 18:21:53 +02:00
static void decon_win_set_bldeq ( struct decon_context * ctx , unsigned int win ,
unsigned int alpha , unsigned int pixel_alpha )
{
u32 mask = BLENDERQ_A_FUNC_F ( 0xf ) | BLENDERQ_B_FUNC_F ( 0xf ) ;
u32 val = 0 ;
switch ( pixel_alpha ) {
case DRM_MODE_BLEND_PIXEL_NONE :
case DRM_MODE_BLEND_COVERAGE :
val | = BLENDERQ_A_FUNC_F ( BLENDERQ_ALPHA_A ) ;
val | = BLENDERQ_B_FUNC_F ( BLENDERQ_ONE_MINUS_ALPHA_A ) ;
break ;
case DRM_MODE_BLEND_PREMULTI :
default :
if ( alpha ! = DRM_BLEND_ALPHA_OPAQUE ) {
val | = BLENDERQ_A_FUNC_F ( BLENDERQ_ALPHA0 ) ;
val | = BLENDERQ_B_FUNC_F ( BLENDERQ_ONE_MINUS_ALPHA_A ) ;
} else {
val | = BLENDERQ_A_FUNC_F ( BLENDERQ_ONE ) ;
val | = BLENDERQ_B_FUNC_F ( BLENDERQ_ONE_MINUS_ALPHA_A ) ;
}
break ;
}
decon_set_bits ( ctx , DECON_BLENDERQx ( win ) , mask , val ) ;
}
2018-10-25 18:21:52 +02:00
static void decon_win_set_bldmod ( struct decon_context * ctx , unsigned int win ,
2018-10-25 18:21:53 +02:00
unsigned int alpha , unsigned int pixel_alpha )
2018-10-25 18:21:52 +02:00
{
u32 win_alpha = alpha > > 8 ;
u32 val = 0 ;
2018-10-25 18:21:53 +02:00
switch ( pixel_alpha ) {
case DRM_MODE_BLEND_PIXEL_NONE :
break ;
case DRM_MODE_BLEND_COVERAGE :
case DRM_MODE_BLEND_PREMULTI :
default :
val | = WINCONx_ALPHA_SEL_F ;
val | = WINCONx_BLD_PIX_F ;
val | = WINCONx_ALPHA_MUL_F ;
break ;
}
decon_set_bits ( ctx , DECON_WINCONx ( win ) , WINCONx_BLEND_MODE_MASK , val ) ;
2018-10-25 18:21:52 +02:00
if ( alpha ! = DRM_BLEND_ALPHA_OPAQUE ) {
val = VIDOSD_Wx_ALPHA_R_F ( win_alpha ) |
VIDOSD_Wx_ALPHA_G_F ( win_alpha ) |
VIDOSD_Wx_ALPHA_B_F ( win_alpha ) ;
decon_set_bits ( ctx , DECON_VIDOSDxC ( win ) ,
VIDOSDxC_ALPHA0_RGB_MASK , val ) ;
decon_set_bits ( ctx , DECON_BLENDCON , BLEND_NEW , BLEND_NEW ) ;
}
}
2015-08-03 14:40:44 +09:00
static void decon_win_set_pixfmt ( struct decon_context * ctx , unsigned int win ,
struct drm_framebuffer * fb )
2015-06-12 21:59:00 +09:00
{
2018-10-25 18:21:52 +02:00
struct exynos_drm_plane plane = ctx - > planes [ win ] ;
struct exynos_drm_plane_state * state =
to_exynos_plane_state ( plane . base . state ) ;
unsigned int alpha = state - > base . alpha ;
2018-10-25 18:21:53 +02:00
unsigned int pixel_alpha ;
2015-06-12 21:59:00 +09:00
unsigned long val ;
2018-10-25 18:21:53 +02:00
if ( fb - > format - > has_alpha )
pixel_alpha = state - > base . pixel_blend_mode ;
else
pixel_alpha = DRM_MODE_BLEND_PIXEL_NONE ;
2015-06-12 21:59:00 +09:00
val = readl ( ctx - > addr + DECON_WINCONx ( win ) ) ;
2018-06-07 13:07:49 +02:00
val & = WINCONx_ENWIN_F ;
2015-06-12 21:59:00 +09:00
2016-12-14 23:32:55 +02:00
switch ( fb - > format - > format ) {
2015-06-12 21:59:00 +09:00
case DRM_FORMAT_XRGB1555 :
val | = WINCONx_BPPMODE_16BPP_I1555 ;
val | = WINCONx_HAWSWP_F ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
case DRM_FORMAT_RGB565 :
val | = WINCONx_BPPMODE_16BPP_565 ;
val | = WINCONx_HAWSWP_F ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
case DRM_FORMAT_XRGB8888 :
val | = WINCONx_BPPMODE_24BPP_888 ;
val | = WINCONx_WSWP_F ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
case DRM_FORMAT_ARGB8888 :
2017-08-22 16:19:44 +02:00
default :
2015-06-12 21:59:00 +09:00
val | = WINCONx_BPPMODE_32BPP_A8888 ;
2018-10-25 18:21:53 +02:00
val | = WINCONx_WSWP_F ;
2015-06-12 21:59:00 +09:00
val | = WINCONx_BURSTLEN_16WORD ;
break ;
}
2019-04-15 16:25:12 +09:00
DRM_DEV_DEBUG_KMS ( ctx - > dev , " cpp = %u \n " , fb - > format - > cpp [ 0 ] ) ;
2015-06-12 21:59:00 +09:00
/*
* In case of exynos , setting dma - burst to 16 Word causes permanent
* tearing for very small buffers , e . g . cursor buffer . Burst Mode
* switching which is based on plane size is not recommended as
* plane size varies a lot towards the end of the screen and rapid
* movement causes unstable DMA which results into iommu crash / tear .
*/
2015-08-03 14:40:44 +09:00
if ( fb - > width < MIN_FB_WIDTH_FOR_16WORD_BURST ) {
2015-06-12 21:59:00 +09:00
val & = ~ WINCONx_BURSTLEN_MASK ;
val | = WINCONx_BURSTLEN_8WORD ;
}
2018-10-25 18:21:53 +02:00
decon_set_bits ( ctx , DECON_WINCONx ( win ) , ~ WINCONx_BLEND_MODE_MASK , val ) ;
2015-06-12 21:59:00 +09:00
2018-10-25 18:21:53 +02:00
if ( win > 0 ) {
decon_win_set_bldmod ( ctx , win , alpha , pixel_alpha ) ;
decon_win_set_bldeq ( ctx , win , alpha , pixel_alpha ) ;
}
2015-06-12 21:59:00 +09:00
}
2017-03-15 15:41:10 +01:00
static void decon_shadow_protect ( struct decon_context * ctx , bool protect )
2015-06-12 21:59:00 +09:00
{
2017-03-15 15:41:10 +01:00
decon_set_bits ( ctx , DECON_SHADOWCON , SHADOWCON_PROTECT_MASK ,
2015-10-20 11:22:37 +02:00
protect ? ~ 0 : 0 ) ;
2015-06-12 21:59:00 +09:00
}
2016-01-05 13:52:51 +01:00
static void decon_atomic_begin ( struct exynos_drm_crtc * crtc )
2015-08-27 18:21:14 +09:00
{
struct decon_context * ctx = crtc - > ctx ;
2017-03-15 15:41:10 +01:00
decon_shadow_protect ( ctx , true ) ;
2015-08-27 18:21:14 +09:00
}
2015-10-20 18:22:41 +09:00
# define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s))
# define COORDINATE_X(x) BIT_VAL((x), 23, 12)
# define COORDINATE_Y(x) BIT_VAL((x), 11, 0)
2015-08-03 14:39:36 +09:00
static void decon_update_plane ( struct exynos_drm_crtc * crtc ,
struct exynos_drm_plane * plane )
2015-06-12 21:59:00 +09:00
{
2015-11-30 14:53:22 +01:00
struct exynos_drm_plane_state * state =
to_exynos_plane_state ( plane - > base . state ) ;
2015-06-12 21:59:00 +09:00
struct decon_context * ctx = crtc - > ctx ;
2015-11-30 14:53:22 +01:00
struct drm_framebuffer * fb = state - > base . fb ;
2015-12-16 13:21:42 +01:00
unsigned int win = plane - > index ;
2017-08-22 16:19:43 +02:00
unsigned int cpp = fb - > format - > cpp [ 0 ] ;
2015-11-30 14:53:21 +01:00
unsigned int pitch = fb - > pitches [ 0 ] ;
dma_addr_t dma_addr = exynos_drm_fb_dma_addr ( fb , 0 ) ;
2015-06-12 21:59:00 +09:00
u32 val ;
2017-01-20 07:52:23 +01:00
if ( crtc - > base . mode . flags & DRM_MODE_FLAG_INTERLACE ) {
val = COORDINATE_X ( state - > crtc . x ) |
COORDINATE_Y ( state - > crtc . y / 2 ) ;
writel ( val , ctx - > addr + DECON_VIDOSDxA ( win ) ) ;
val = COORDINATE_X ( state - > crtc . x + state - > crtc . w - 1 ) |
COORDINATE_Y ( ( state - > crtc . y + state - > crtc . h ) / 2 - 1 ) ;
writel ( val , ctx - > addr + DECON_VIDOSDxB ( win ) ) ;
} else {
val = COORDINATE_X ( state - > crtc . x ) | COORDINATE_Y ( state - > crtc . y ) ;
writel ( val , ctx - > addr + DECON_VIDOSDxA ( win ) ) ;
2015-06-12 21:59:00 +09:00
2017-01-20 07:52:23 +01:00
val = COORDINATE_X ( state - > crtc . x + state - > crtc . w - 1 ) |
COORDINATE_Y ( state - > crtc . y + state - > crtc . h - 1 ) ;
writel ( val , ctx - > addr + DECON_VIDOSDxB ( win ) ) ;
}
2015-06-12 21:59:00 +09:00
2018-06-07 13:07:40 +02:00
val = VIDOSD_Wx_ALPHA_R_F ( 0xff ) | VIDOSD_Wx_ALPHA_G_F ( 0xff ) |
VIDOSD_Wx_ALPHA_B_F ( 0xff ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDOSDxC ( win ) ) ;
val = VIDOSD_Wx_ALPHA_R_F ( 0x0 ) | VIDOSD_Wx_ALPHA_G_F ( 0x0 ) |
VIDOSD_Wx_ALPHA_B_F ( 0x0 ) ;
writel ( val , ctx - > addr + DECON_VIDOSDxD ( win ) ) ;
2015-11-30 14:53:21 +01:00
writel ( dma_addr , ctx - > addr + DECON_VIDW0xADD0B0 ( win ) ) ;
2015-06-12 21:59:00 +09:00
2015-11-30 14:53:22 +01:00
val = dma_addr + pitch * state - > src . h ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDW0xADD1B0 ( win ) ) ;
2016-04-18 17:59:01 +09:00
if ( ! ( ctx - > out_type & IFTYPE_HDMI ) )
2017-08-22 16:19:43 +02:00
val = BIT_VAL ( pitch - state - > crtc . w * cpp , 27 , 14 )
| BIT_VAL ( state - > crtc . w * cpp , 13 , 0 ) ;
2015-10-20 18:22:41 +09:00
else
2017-08-22 16:19:43 +02:00
val = BIT_VAL ( pitch - state - > crtc . w * cpp , 29 , 15 )
| BIT_VAL ( state - > crtc . w * cpp , 14 , 0 ) ;
2015-06-12 21:59:00 +09:00
writel ( val , ctx - > addr + DECON_VIDW0xADD2 ( win ) ) ;
2015-11-30 14:53:21 +01:00
decon_win_set_pixfmt ( ctx , win , fb ) ;
2015-06-12 21:59:00 +09:00
/* window enable */
2015-10-20 11:22:37 +02:00
decon_set_bits ( ctx , DECON_WINCONx ( win ) , WINCONx_ENWIN_F , ~ 0 ) ;
2015-06-12 21:59:00 +09:00
}
2015-08-03 14:39:36 +09:00
static void decon_disable_plane ( struct exynos_drm_crtc * crtc ,
struct exynos_drm_plane * plane )
2015-06-12 21:59:00 +09:00
{
struct decon_context * ctx = crtc - > ctx ;
2015-12-16 13:21:42 +01:00
unsigned int win = plane - > index ;
2015-06-12 21:59:00 +09:00
2015-10-20 11:22:37 +02:00
decon_set_bits ( ctx , DECON_WINCONx ( win ) , WINCONx_ENWIN_F , 0 ) ;
2015-06-12 21:59:00 +09:00
}
2016-01-05 13:52:51 +01:00
static void decon_atomic_flush ( struct exynos_drm_crtc * crtc )
2015-08-27 18:21:14 +09:00
{
struct decon_context * ctx = crtc - > ctx ;
2017-03-14 09:27:57 +01:00
unsigned long flags ;
2015-08-27 18:21:14 +09:00
2017-03-14 09:27:57 +01:00
spin_lock_irqsave ( & ctx - > vblank_lock , flags ) ;
2017-03-15 15:41:10 +01:00
decon_shadow_protect ( ctx , false ) ;
2015-08-27 18:21:14 +09:00
2017-03-15 15:41:08 +01:00
decon_set_bits ( ctx , DECON_UPDATE , STANDALONE_UPDATE_F , ~ 0 ) ;
2016-03-23 14:15:16 +01:00
2017-03-14 09:27:57 +01:00
ctx - > frame_id = decon_get_frame_count ( ctx , true ) ;
2017-03-14 09:27:56 +01:00
exynos_crtc_handle_event ( crtc ) ;
2017-03-14 09:27:57 +01:00
spin_unlock_irqrestore ( & ctx - > vblank_lock , flags ) ;
2015-08-27 18:21:14 +09:00
}
2015-06-12 21:59:00 +09:00
static void decon_swreset ( struct decon_context * ctx )
{
2017-03-14 09:27:57 +01:00
unsigned long flags ;
2017-08-24 15:33:50 +02:00
u32 val ;
int ret ;
2015-06-12 21:59:00 +09:00
writel ( 0 , ctx - > addr + DECON_VIDCON0 ) ;
2017-08-24 15:33:50 +02:00
readl_poll_timeout ( ctx - > addr + DECON_VIDCON0 , val ,
~ val & VIDCON0_STOP_STATUS , 12 , 20000 ) ;
2015-06-12 21:59:00 +09:00
writel ( VIDCON0_SWRESET , ctx - > addr + DECON_VIDCON0 ) ;
2017-08-24 15:33:50 +02:00
ret = readl_poll_timeout ( ctx - > addr + DECON_VIDCON0 , val ,
~ val & VIDCON0_SWRESET , 12 , 20000 ) ;
2015-06-12 21:59:00 +09:00
2017-08-24 15:33:50 +02:00
WARN ( ret < 0 , " failed to software reset DECON \n " ) ;
2015-10-20 18:22:41 +09:00
2017-03-14 09:27:57 +01:00
spin_lock_irqsave ( & ctx - > vblank_lock , flags ) ;
ctx - > frame_id = 0 ;
spin_unlock_irqrestore ( & ctx - > vblank_lock , flags ) ;
2016-04-18 17:59:01 +09:00
if ( ! ( ctx - > out_type & IFTYPE_HDMI ) )
2015-10-20 18:22:41 +09:00
return ;
writel ( VIDCON0_CLKVALUP | VIDCON0_VLCKFREE , ctx - > addr + DECON_VIDCON0 ) ;
decon_set_bits ( ctx , DECON_CMU ,
CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F , ~ 0 ) ;
writel ( VIDCON1_VCLK_RUN_VDEN_DISABLE , ctx - > addr + DECON_VIDCON1 ) ;
writel ( CRCCTRL_CRCEN | CRCCTRL_CRCSTART_F | CRCCTRL_CRCCLKEN ,
ctx - > addr + DECON_CRCCTRL ) ;
2015-06-12 21:59:00 +09:00
}
2019-12-19 11:07:53 +09:00
static void decon_atomic_enable ( struct exynos_drm_crtc * crtc )
2015-06-12 21:59:00 +09:00
{
struct decon_context * ctx = crtc - > ctx ;
pm_runtime_get_sync ( ctx - > dev ) ;
2016-03-23 14:26:00 +01:00
exynos_drm_pipe_clk_enable ( crtc , true ) ;
2016-03-23 14:15:17 +01:00
decon_swreset ( ctx ) ;
2015-06-12 21:59:00 +09:00
decon_commit ( ctx - > crtc ) ;
}
2019-12-19 11:07:53 +09:00
static void decon_atomic_disable ( struct exynos_drm_crtc * crtc )
2015-06-12 21:59:00 +09:00
{
struct decon_context * ctx = crtc - > ctx ;
int i ;
2017-04-05 09:28:32 +02:00
if ( ! ( ctx - > out_type & I80_HW_TRG ) )
synchronize_irq ( ctx - > te_irq ) ;
synchronize_irq ( ctx - > irq ) ;
2015-06-12 21:59:00 +09:00
/*
* We need to make sure that all windows are disabled before we
* suspend that connector . Otherwise we might try to scan from
* a destroyed buffer later .
*/
2015-10-20 18:22:41 +09:00
for ( i = ctx - > first_win ; i < WINDOWS_NR ; i + + )
2015-08-03 14:39:36 +09:00
decon_disable_plane ( crtc , & ctx - > planes [ i ] ) ;
2015-06-12 21:59:00 +09:00
decon_swreset ( ctx ) ;
2016-03-23 14:26:00 +01:00
exynos_drm_pipe_clk_enable ( crtc , false ) ;
2015-06-12 21:59:00 +09:00
pm_runtime_put_sync ( ctx - > dev ) ;
}
2017-04-05 09:28:32 +02:00
static irqreturn_t decon_te_irq_handler ( int irq , void * dev_id )
2015-06-12 21:59:00 +09:00
{
2017-04-05 09:28:32 +02:00
struct decon_context * ctx = dev_id ;
2015-06-12 21:59:00 +09:00
2017-04-05 09:28:33 +02:00
decon_set_bits ( ctx , DECON_TRIGCON , TRIGCON_SWTRIGCMD , ~ 0 ) ;
2017-04-05 09:28:32 +02:00
return IRQ_HANDLED ;
2015-06-12 21:59:00 +09:00
}
static void decon_clear_channels ( struct exynos_drm_crtc * crtc )
{
struct decon_context * ctx = crtc - > ctx ;
int win , i , ret ;
for ( i = 0 ; i < ARRAY_SIZE ( decon_clks_name ) ; i + + ) {
ret = clk_prepare_enable ( ctx - > clks [ i ] ) ;
if ( ret < 0 )
goto err ;
}
2017-03-15 15:41:10 +01:00
decon_shadow_protect ( ctx , true ) ;
for ( win = 0 ; win < WINDOWS_NR ; win + + )
2015-10-20 11:22:37 +02:00
decon_set_bits ( ctx , DECON_WINCONx ( win ) , WINCONx_ENWIN_F , 0 ) ;
2017-03-15 15:41:10 +01:00
decon_shadow_protect ( ctx , false ) ;
2016-03-23 14:15:16 +01:00
decon_set_bits ( ctx , DECON_UPDATE , STANDALONE_UPDATE_F , ~ 0 ) ;
2015-06-12 21:59:00 +09:00
/* TODO: wait for possible vsync */
msleep ( 50 ) ;
err :
while ( - - i > = 0 )
clk_disable_unprepare ( ctx - > clks [ i ] ) ;
}
2017-08-24 15:33:57 +02:00
static enum drm_mode_status decon_mode_valid ( struct exynos_drm_crtc * crtc ,
const struct drm_display_mode * mode )
{
struct decon_context * ctx = crtc - > ctx ;
ctx - > irq = crtc - > i80_mode ? ctx - > irq_lcd_sys : ctx - > irq_vsync ;
if ( ctx - > irq )
return MODE_OK ;
dev_info ( ctx - > dev , " Sink requires %s mode, but appropriate interrupt is not provided. \n " ,
crtc - > i80_mode ? " command " : " video " ) ;
return MODE_BAD ;
}
2017-01-09 23:24:53 +05:30
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
2019-12-19 11:07:53 +09:00
. atomic_enable = decon_atomic_enable ,
. atomic_disable = decon_atomic_disable ,
2015-06-12 21:59:00 +09:00
. enable_vblank = decon_enable_vblank ,
. disable_vblank = decon_disable_vblank ,
2015-08-27 18:21:14 +09:00
. atomic_begin = decon_atomic_begin ,
2015-08-03 14:38:05 +09:00
. update_plane = decon_update_plane ,
. disable_plane = decon_disable_plane ,
2017-08-24 15:33:57 +02:00
. mode_valid = decon_mode_valid ,
2015-08-27 18:21:14 +09:00
. atomic_flush = decon_atomic_flush ,
2015-06-12 21:59:00 +09:00
} ;
static int decon_bind ( struct device * dev , struct device * master , void * data )
{
struct decon_context * ctx = dev_get_drvdata ( dev ) ;
struct drm_device * drm_dev = data ;
struct exynos_drm_plane * exynos_plane ;
2015-10-20 18:22:41 +09:00
enum exynos_drm_output_type out_type ;
unsigned int win ;
2015-06-12 21:59:00 +09:00
int ret ;
ctx - > drm_dev = drm_dev ;
2015-10-20 18:22:41 +09:00
for ( win = ctx - > first_win ; win < WINDOWS_NR ; win + + ) {
2015-11-30 14:53:25 +01:00
ctx - > configs [ win ] . pixel_formats = decon_formats ;
ctx - > configs [ win ] . num_pixel_formats = ARRAY_SIZE ( decon_formats ) ;
2018-05-23 13:00:02 +02:00
ctx - > configs [ win ] . zpos = win - ctx - > first_win ;
ctx - > configs [ win ] . type = decon_win_types [ win ] ;
2018-10-25 18:21:52 +02:00
ctx - > configs [ win ] . capabilities = capabilities [ win ] ;
2015-11-30 14:53:25 +01:00
2015-12-16 13:21:42 +01:00
ret = exynos_plane_init ( drm_dev , & ctx - > planes [ win ] , win ,
2017-03-15 15:41:05 +01:00
& ctx - > configs [ win ] ) ;
2015-06-12 21:59:00 +09:00
if ( ret )
return ret ;
}
2018-05-23 13:00:02 +02:00
exynos_plane = & ctx - > planes [ PRIMARY_WIN ] ;
2016-04-18 17:59:01 +09:00
out_type = ( ctx - > out_type & IFTYPE_HDMI ) ? EXYNOS_DISPLAY_TYPE_HDMI
2015-10-20 18:22:41 +09:00
: EXYNOS_DISPLAY_TYPE_LCD ;
2015-06-12 21:59:00 +09:00
ctx - > crtc = exynos_drm_crtc_create ( drm_dev , & exynos_plane - > base ,
2017-05-29 10:05:25 +09:00
out_type , & decon_crtc_ops , ctx ) ;
2017-03-15 15:41:04 +01:00
if ( IS_ERR ( ctx - > crtc ) )
return PTR_ERR ( ctx - > crtc ) ;
2015-06-12 21:59:00 +09:00
2015-07-02 21:49:39 +09:00
decon_clear_channels ( ctx - > crtc ) ;
2020-03-09 12:02:14 +01:00
return exynos_drm_register_dma ( drm_dev , dev , & ctx - > dma_priv ) ;
2015-06-12 21:59:00 +09:00
}
static void decon_unbind ( struct device * dev , struct device * master , void * data )
{
struct decon_context * ctx = dev_get_drvdata ( dev ) ;
2019-12-19 11:07:53 +09:00
decon_atomic_disable ( ctx - > crtc ) ;
2015-06-12 21:59:00 +09:00
/* detach this sub driver from iommu mapping if supported. */
2020-03-09 12:02:14 +01:00
exynos_drm_unregister_dma ( ctx - > drm_dev , ctx - > dev , & ctx - > dma_priv ) ;
2015-06-12 21:59:00 +09:00
}
static const struct component_ops decon_component_ops = {
. bind = decon_bind ,
. unbind = decon_unbind ,
} ;
2017-03-14 09:27:57 +01:00
static void decon_handle_vblank ( struct decon_context * ctx )
{
u32 frm ;
spin_lock ( & ctx - > vblank_lock ) ;
frm = decon_get_frame_count ( ctx , true ) ;
if ( frm ! = ctx - > frame_id ) {
/* handle only if incremented, take care of wrap-around */
if ( ( s32 ) ( frm - ctx - > frame_id ) > 0 )
drm_crtc_handle_vblank ( & ctx - > crtc - > base ) ;
ctx - > frame_id = frm ;
}
spin_unlock ( & ctx - > vblank_lock ) ;
}
2015-10-20 18:22:41 +09:00
static irqreturn_t decon_irq_handler ( int irq , void * dev_id )
2015-06-12 21:59:00 +09:00
{
struct decon_context * ctx = dev_id ;
u32 val ;
val = readl ( ctx - > addr + DECON_VIDINTCON1 ) ;
2015-10-20 18:22:41 +09:00
val & = VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND ;
if ( val ) {
writel ( val , ctx - > addr + DECON_VIDINTCON1 ) ;
2017-01-20 07:52:24 +01:00
if ( ctx - > out_type & IFTYPE_HDMI ) {
val = readl ( ctx - > addr + DECON_VIDOUTCON0 ) ;
val & = VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F ;
if ( val = =
( VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F ) )
return IRQ_HANDLED ;
}
2017-03-14 09:27:57 +01:00
decon_handle_vblank ( ctx ) ;
2015-06-12 21:59:00 +09:00
}
return IRQ_HANDLED ;
}
2015-11-02 20:54:55 +09:00
# ifdef CONFIG_PM
static int exynos5433_decon_suspend ( struct device * dev )
{
struct decon_context * ctx = dev_get_drvdata ( dev ) ;
2016-02-11 12:25:04 +01:00
int i = ARRAY_SIZE ( decon_clks_name ) ;
2015-11-02 20:54:55 +09:00
2016-02-11 12:25:04 +01:00
while ( - - i > = 0 )
2015-11-02 20:54:55 +09:00
clk_disable_unprepare ( ctx - > clks [ i ] ) ;
return 0 ;
}
static int exynos5433_decon_resume ( struct device * dev )
{
struct decon_context * ctx = dev_get_drvdata ( dev ) ;
int i , ret ;
for ( i = 0 ; i < ARRAY_SIZE ( decon_clks_name ) ; i + + ) {
ret = clk_prepare_enable ( ctx - > clks [ i ] ) ;
if ( ret < 0 )
goto err ;
}
return 0 ;
err :
while ( - - i > = 0 )
clk_disable_unprepare ( ctx - > clks [ i ] ) ;
return ret ;
}
# endif
static const struct dev_pm_ops exynos5433_decon_pm_ops = {
SET_RUNTIME_PM_OPS ( exynos5433_decon_suspend , exynos5433_decon_resume ,
NULL )
2018-06-11 14:25:00 +02:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2015-11-02 20:54:55 +09:00
} ;
2015-10-20 18:22:41 +09:00
static const struct of_device_id exynos5433_decon_driver_dt_match [ ] = {
{
. compatible = " samsung,exynos5433-decon " ,
2016-04-18 17:59:01 +09:00
. data = ( void * ) I80_HW_TRG
2015-10-20 18:22:41 +09:00
} ,
{
. compatible = " samsung,exynos5433-decon-tv " ,
2016-04-18 17:59:01 +09:00
. data = ( void * ) ( I80_HW_TRG | IFTYPE_HDMI )
2015-10-20 18:22:41 +09:00
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , exynos5433_decon_driver_dt_match ) ;
2017-04-05 09:28:32 +02:00
static int decon_conf_irq ( struct decon_context * ctx , const char * name ,
2017-08-24 15:33:55 +02:00
irq_handler_t handler , unsigned long int flags )
2017-04-05 09:28:32 +02:00
{
struct platform_device * pdev = to_platform_device ( ctx - > dev ) ;
int ret , irq = platform_get_irq_byname ( pdev , name ) ;
if ( irq < 0 ) {
2017-08-24 15:33:55 +02:00
switch ( irq ) {
case - EPROBE_DEFER :
2017-04-05 09:28:32 +02:00
return irq ;
2017-08-24 15:33:55 +02:00
case - ENODATA :
case - ENXIO :
return 0 ;
default :
dev_err ( ctx - > dev , " IRQ %s get failed, %d \n " , name , irq ) ;
return irq ;
}
2017-04-05 09:28:32 +02:00
}
irq_set_status_flags ( irq , IRQ_NOAUTOEN ) ;
ret = devm_request_irq ( ctx - > dev , irq , handler , flags , " drm_decon " , ctx ) ;
if ( ret < 0 ) {
dev_err ( ctx - > dev , " IRQ %s request failed \n " , name ) ;
return ret ;
}
return irq ;
}
2015-06-12 21:59:00 +09:00
static int exynos5433_decon_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct decon_context * ctx ;
struct resource * res ;
int ret ;
int i ;
ctx = devm_kzalloc ( dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
ctx - > dev = dev ;
2016-04-18 17:59:01 +09:00
ctx - > out_type = ( unsigned long ) of_device_get_match_data ( dev ) ;
2017-03-14 09:27:57 +01:00
spin_lock_init ( & ctx - > vblank_lock ) ;
2015-10-20 18:22:41 +09:00
2017-08-24 15:33:57 +02:00
if ( ctx - > out_type & IFTYPE_HDMI )
2015-10-20 18:22:41 +09:00
ctx - > first_win = 1 ;
2015-06-12 21:59:00 +09:00
for ( i = 0 ; i < ARRAY_SIZE ( decon_clks_name ) ; i + + ) {
struct clk * clk ;
clk = devm_clk_get ( ctx - > dev , decon_clks_name [ i ] ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
ctx - > clks [ i ] = clk ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
ctx - > addr = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( ctx - > addr ) ) {
dev_err ( dev , " ioremap failed \n " ) ;
return PTR_ERR ( ctx - > addr ) ;
}
2017-08-24 15:33:55 +02:00
ret = decon_conf_irq ( ctx , " vsync " , decon_irq_handler , 0 ) ;
if ( ret < 0 )
return ret ;
ctx - > irq_vsync = ret ;
2017-04-05 09:28:32 +02:00
2017-08-24 15:33:55 +02:00
ret = decon_conf_irq ( ctx , " lcd_sys " , decon_irq_handler , 0 ) ;
if ( ret < 0 )
return ret ;
ctx - > irq_lcd_sys = ret ;
ret = decon_conf_irq ( ctx , " te " , decon_te_irq_handler ,
IRQF_TRIGGER_RISING ) ;
if ( ret < 0 )
2017-04-05 09:28:32 +02:00
return ret ;
2017-08-24 15:33:55 +02:00
if ( ret ) {
ctx - > te_irq = ret ;
ctx - > out_type & = ~ I80_HW_TRG ;
2015-06-12 21:59:00 +09:00
}
2017-04-05 09:28:32 +02:00
if ( ctx - > out_type & I80_HW_TRG ) {
ctx - > sysreg = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" samsung,disp-sysreg " ) ;
if ( IS_ERR ( ctx - > sysreg ) ) {
dev_err ( dev , " failed to get system register \n " ) ;
return PTR_ERR ( ctx - > sysreg ) ;
}
2015-06-12 21:59:00 +09:00
}
platform_set_drvdata ( pdev , ctx ) ;
pm_runtime_enable ( dev ) ;
ret = component_add ( dev , & decon_component_ops ) ;
if ( ret )
goto err_disable_pm_runtime ;
return 0 ;
err_disable_pm_runtime :
pm_runtime_disable ( dev ) ;
return ret ;
}
static int exynos5433_decon_remove ( struct platform_device * pdev )
{
pm_runtime_disable ( & pdev - > dev ) ;
component_del ( & pdev - > dev , & decon_component_ops ) ;
return 0 ;
}
struct platform_driver exynos5433_decon_driver = {
. probe = exynos5433_decon_probe ,
. remove = exynos5433_decon_remove ,
. driver = {
. name = " exynos5433-decon " ,
2015-11-02 20:54:55 +09:00
. pm = & exynos5433_decon_pm_ops ,
2015-06-12 21:59:00 +09:00
. of_match_table = exynos5433_decon_driver_dt_match ,
} ,
} ;