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 >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundationr
*/
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/component.h>
2017-08-24 15:33:50 +02:00
# include <linux/iopoll.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>
# 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
# include <video/exynos5433_decon.h>
# include "exynos_drm_drv.h"
# include "exynos_drm_crtc.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"
# include "exynos_drm_iommu.h"
2017-02-01 15:35:07 +09:00
# define DSD_CFG_MUX 0x1004
# define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13)
2015-06-12 21:59:00 +09:00
# define WINDOWS_NR 3
# define MIN_FB_WIDTH_FOR_16WORD_BURST 128
2016-04-18 17:59:01 +09:00
# define IFTYPE_I80 (1 << 0)
# define I80_HW_TRG (1 << 1)
# define IFTYPE_HDMI (1 << 2)
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 " ,
" 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 ;
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 ;
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 ] = {
DRM_PLANE_TYPE_PRIMARY ,
DRM_PLANE_TYPE_OVERLAY ,
DRM_PLANE_TYPE_CURSOR ,
} ;
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 ;
if ( ctx - > out_type & IFTYPE_I80 )
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 :
if ( ! ( ctx - > out_type & IFTYPE_I80 ) )
- - frm ;
break ;
case VIDCON1_VSTATUS_BP :
- - frm ;
break ;
case VIDCON1_I80_ACTIVE :
case VIDCON1_VSTATUS_AC :
if ( end )
- - frm ;
break ;
default :
break ;
}
return frm ;
}
2017-03-15 15:41:02 +01:00
static u32 decon_get_vblank_counter ( struct exynos_drm_crtc * crtc )
{
struct decon_context * ctx = crtc - > ctx ;
return decon_get_frame_count ( ctx , false ) ;
}
2015-06-12 21:59:00 +09:00
static void decon_setup_trigger ( struct decon_context * ctx )
{
2017-02-01 15:35:07 +09:00
if ( ! ( ctx - > out_type & ( IFTYPE_I80 | I80_HW_TRG ) ) )
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 ) )
DRM_ERROR ( " 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 ;
2016-04-18 17:59:01 +09:00
if ( ctx - > out_type & IFTYPE_I80 ) {
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 ) ;
2016-04-18 17:59:01 +09:00
if ( ! ( ctx - > out_type & IFTYPE_I80 ) ) {
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
}
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
{
unsigned long val ;
val = readl ( ctx - > addr + DECON_WINCONx ( win ) ) ;
val & = ~ WINCONx_BPPMODE_MASK ;
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 :
val | = WINCONx_BPPMODE_32BPP_A8888 ;
val | = WINCONx_WSWP_F | WINCONx_BLD_PIX_F | WINCONx_ALPHA_SEL_F ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
default :
DRM_ERROR ( " Proper pixel format is not set \n " ) ;
return ;
}
2016-12-14 23:32:20 +02:00
DRM_DEBUG_KMS ( " bpp = %u \n " , fb - > format - > cpp [ 0 ] * 8 ) ;
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 ;
}
writel ( val , ctx - > addr + DECON_WINCONx ( win ) ) ;
}
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 ;
2016-12-14 23:32:20 +02:00
unsigned int bpp = 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
val = VIDOSD_Wx_ALPHA_R_F ( 0x0 ) | VIDOSD_Wx_ALPHA_G_F ( 0x0 ) |
VIDOSD_Wx_ALPHA_B_F ( 0x0 ) ;
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 ) )
2015-11-30 14:53:22 +01:00
val = BIT_VAL ( pitch - state - > crtc . w * bpp , 27 , 14 )
| BIT_VAL ( state - > crtc . w * bpp , 13 , 0 ) ;
2015-10-20 18:22:41 +09:00
else
2015-11-30 14:53:22 +01:00
val = BIT_VAL ( pitch - state - > crtc . w * bpp , 29 , 15 )
| BIT_VAL ( state - > crtc . w * bpp , 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
}
static void decon_enable ( struct exynos_drm_crtc * crtc )
{
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 ) ;
}
static void decon_disable ( struct exynos_drm_crtc * crtc )
{
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 ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
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-01-09 23:24:53 +05:30
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
2015-06-12 21:59:00 +09:00
. enable = decon_enable ,
. disable = decon_disable ,
. enable_vblank = decon_enable_vblank ,
. disable_vblank = decon_disable_vblank ,
2017-03-15 15:41:02 +01:00
. get_vblank_counter = decon_get_vblank_counter ,
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 ,
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 ;
2017-03-15 15:41:02 +01:00
drm_dev - > max_vblank_count = 0xffffffff ;
2015-06-12 21:59:00 +09:00
2015-10-20 18:22:41 +09:00
for ( win = ctx - > first_win ; win < WINDOWS_NR ; win + + ) {
int tmp = ( win = = ctx - > first_win ) ? 0 : 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 ) ;
ctx - > configs [ win ] . zpos = win ;
ctx - > configs [ win ] . type = decon_win_types [ tmp ] ;
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 ;
}
2015-10-20 18:22:41 +09:00
exynos_plane = & ctx - > planes [ ctx - > first_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 ) ;
2017-03-15 15:41:04 +01:00
return drm_iommu_attach_device ( drm_dev , dev ) ;
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 ) ;
decon_disable ( ctx - > crtc ) ;
/* detach this sub driver from iommu mapping if supported. */
2015-07-02 21:49:38 +09:00
drm_iommu_detach_device ( ctx - > drm_dev , ctx - > dev ) ;
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 )
} ;
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 ,
irq_handler_t handler , unsigned long int flags , bool required )
{
struct platform_device * pdev = to_platform_device ( ctx - > dev ) ;
int ret , irq = platform_get_irq_byname ( pdev , name ) ;
if ( irq < 0 ) {
if ( irq = = - EPROBE_DEFER )
return irq ;
if ( required )
dev_err ( ctx - > dev , " cannot get %s IRQ \n " , name ) ;
else
irq = 0 ;
return irq ;
}
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
2016-04-18 17:59:01 +09:00
if ( ctx - > out_type & IFTYPE_HDMI ) {
2015-10-20 18:22:41 +09:00
ctx - > first_win = 1 ;
2016-04-18 17:59:01 +09:00
} else if ( of_get_child_by_name ( dev - > of_node , " i80-if-timings " ) ) {
2016-04-29 15:42:49 +02:00
ctx - > out_type | = IFTYPE_I80 ;
2016-04-18 17:59:01 +09:00
}
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 ) ;
if ( ! res ) {
dev_err ( dev , " cannot find IO resource \n " ) ;
return - ENXIO ;
}
ctx - > addr = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( ctx - > addr ) ) {
dev_err ( dev , " ioremap failed \n " ) ;
return PTR_ERR ( ctx - > addr ) ;
}
2017-04-05 09:28:32 +02:00
if ( ctx - > out_type & IFTYPE_I80 ) {
ret = decon_conf_irq ( ctx , " lcd_sys " , decon_irq_handler , 0 , true ) ;
if ( ret < 0 )
return ret ;
ctx - > irq = ret ;
ret = decon_conf_irq ( ctx , " te " , decon_te_irq_handler ,
IRQF_TRIGGER_RISING , false ) ;
if ( ret < 0 )
return ret ;
if ( ret ) {
ctx - > te_irq = ret ;
ctx - > out_type & = ~ I80_HW_TRG ;
}
} else {
ret = decon_conf_irq ( ctx , " vsync " , decon_irq_handler , 0 , true ) ;
if ( ret < 0 )
return ret ;
ctx - > irq = ret ;
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 ,
} ,
} ;