2011-10-04 14:19:01 +04:00
/* exynos_drm_fimd.c
*
* Copyright ( C ) 2011 Samsung Electronics Co . Ltd
* Authors :
* Joonyoung Shim < jy0922 . shim @ samsung . com >
* Inki Dae < inki . dae @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
2011-10-04 14:19:01 +04:00
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
2013-08-14 15:08:01 +04:00
# include <linux/of.h>
2012-12-14 10:48:25 +04:00
# include <linux/of_device.h>
2011-12-09 11:52:11 +04:00
# include <linux/pm_runtime.h>
2014-05-09 09:25:20 +04:00
# include <linux/component.h>
2014-07-17 13:01:21 +04:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2011-10-04 14:19:01 +04:00
2013-03-07 10:45:21 +04:00
# include <video/of_display_timing.h>
2013-08-21 18:22:01 +04:00
# include <video/of_videomode.h>
2012-08-08 04:44:49 +04:00
# include <video/samsung_fimd.h>
2011-10-04 14:19:01 +04:00
# include <drm/exynos_drm.h>
# include "exynos_drm_drv.h"
2015-11-30 16:53:21 +03:00
# include "exynos_drm_fb.h"
2011-10-04 14:19:01 +04:00
# include "exynos_drm_crtc.h"
2015-04-03 15:03:40 +03:00
# include "exynos_drm_plane.h"
2012-10-19 12:16:36 +04:00
# include "exynos_drm_iommu.h"
2011-10-04 14:19:01 +04:00
/*
2013-09-19 09:09:44 +04:00
* FIMD stands for Fully Interactive Mobile Display and
2011-10-04 14:19:01 +04:00
* as a display controller , it transfers contents drawn on memory
* to a LCD Panel through Display Interfaces such as RGB or
* CPU Interface .
*/
2014-05-07 15:25:22 +04:00
# define MIN_FB_WIDTH_FOR_16WORD_BURST 128
2013-08-21 18:22:01 +04:00
2011-10-04 14:19:01 +04:00
/* position control register for hardware window 0, 2 ~ 4.*/
# define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
# define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
2013-03-08 08:28:52 +04:00
/*
* size control register for hardware windows 0 and alpha control register
* for hardware windows 1 ~ 4
*/
# define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16)
/* size control register for hardware windows 1 ~ 2. */
2011-10-04 14:19:01 +04:00
# define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
2015-04-01 19:02:05 +03:00
# define VIDWnALPHA0(win) (VIDW_ALPHA + 0x00 + (win) * 8)
# define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8)
2011-10-04 14:19:01 +04:00
# define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
2015-08-15 19:26:16 +03:00
# define VIDWx_BUF_START_S(win, buf) (VIDW_BUF_START_S(buf) + (win) * 8)
2011-10-04 14:19:01 +04:00
# define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
# define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
/* color key control register for hardware window 1 ~ 4. */
2013-03-08 08:28:52 +04:00
# define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8))
2011-10-04 14:19:01 +04:00
/* color key value register for hardware window 1 ~ 4. */
2013-03-08 08:28:52 +04:00
# define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
2011-10-04 14:19:01 +04:00
2016-04-12 03:59:11 +03:00
/* I80 trigger control register */
2014-07-17 13:01:21 +04:00
# define TRIGCON 0x1A4
2016-04-12 03:59:11 +03:00
# define TRGMODE_ENABLE (1 << 0)
# define SWTRGCMD_ENABLE (1 << 1)
2016-04-18 11:54:39 +03:00
/* Exynos3250, 3472, 4415, 5260 5410, 5420 and 5422 only supported. */
2016-04-12 03:59:11 +03:00
# define HWTRGEN_ENABLE (1 << 3)
# define HWTRGMASK_ENABLE (1 << 4)
2016-04-18 11:54:39 +03:00
/* Exynos3250, 3472, 4415, 5260, 5420 and 5422 only supported. */
2016-04-12 03:59:11 +03:00
# define HWTRIGEN_PER_ENABLE (1 << 31)
2014-07-17 13:01:21 +04:00
/* display mode change control register except exynos4 */
# define VIDOUT_CON 0x000
# define VIDOUT_CON_F_I80_LDI0 (0x2 << 8)
/* I80 interface control for main LDI register */
# define I80IFCONFAx(x) (0x1B0 + (x) * 4)
# define I80IFCONFBx(x) (0x1B8 + (x) * 4)
# define LCD_CS_SETUP(x) ((x) << 16)
# define LCD_WR_SETUP(x) ((x) << 12)
# define LCD_WR_ACTIVE(x) ((x) << 8)
# define LCD_WR_HOLD(x) ((x) << 4)
# define I80IFEN_ENABLE (1 << 0)
2011-10-04 14:19:01 +04:00
/* FIMD has totally five hardware windows. */
# define WINDOWS_NR 5
2016-04-18 11:54:39 +03:00
/* HW trigger flag on i80 panel. */
# define I80_HW_TRG (1 << 1)
2012-09-21 15:22:15 +04:00
struct fimd_driver_data {
unsigned int timing_base ;
2014-07-17 13:01:21 +04:00
unsigned int lcdblk_offset ;
unsigned int lcdblk_vt_shift ;
unsigned int lcdblk_bypass_shift ;
2016-02-12 16:31:39 +03:00
unsigned int lcdblk_mic_bypass_shift ;
2016-04-18 11:54:39 +03:00
unsigned int trg_type ;
2013-05-01 23:02:27 +04:00
unsigned int has_shadowcon : 1 ;
2013-05-01 23:02:28 +04:00
unsigned int has_clksel : 1 ;
2013-08-20 09:28:56 +04:00
unsigned int has_limited_fmt : 1 ;
2014-07-17 13:01:21 +04:00
unsigned int has_vidoutcon : 1 ;
2014-11-14 05:36:02 +03:00
unsigned int has_vtsel : 1 ;
2016-02-12 16:31:39 +03:00
unsigned int has_mic_bypass : 1 ;
2016-04-29 19:39:08 +03:00
unsigned int has_dp_clk : 1 ;
2016-04-18 11:54:39 +03:00
unsigned int has_hw_trigger : 1 ;
unsigned int has_trigger_per_te : 1 ;
2012-09-21 15:22:15 +04:00
} ;
2013-05-01 23:02:29 +04:00
static struct fimd_driver_data s3c64xx_fimd_driver_data = {
. timing_base = 0x0 ,
. has_clksel = 1 ,
2013-08-20 09:28:56 +04:00
. has_limited_fmt = 1 ,
2013-05-01 23:02:29 +04:00
} ;
2014-08-18 11:53:19 +04:00
static struct fimd_driver_data exynos3_fimd_driver_data = {
. timing_base = 0x20000 ,
. lcdblk_offset = 0x210 ,
. lcdblk_bypass_shift = 1 ,
2016-04-18 11:54:39 +03:00
. trg_type = I80_HW_TRG ,
2014-08-18 11:53:19 +04:00
. has_shadowcon = 1 ,
. has_vidoutcon = 1 ,
2016-04-18 11:54:39 +03:00
. has_trigger_per_te = 1 ,
2014-08-18 11:53:19 +04:00
} ;
2012-11-19 13:52:54 +04:00
static struct fimd_driver_data exynos4_fimd_driver_data = {
2012-09-21 15:22:15 +04:00
. timing_base = 0x0 ,
2014-07-17 13:01:21 +04:00
. lcdblk_offset = 0x210 ,
. lcdblk_vt_shift = 10 ,
. lcdblk_bypass_shift = 1 ,
2013-05-01 23:02:27 +04:00
. has_shadowcon = 1 ,
2014-11-14 05:36:02 +03:00
. has_vtsel = 1 ,
2012-09-21 15:22:15 +04:00
} ;
2014-11-07 09:12:25 +03:00
static struct fimd_driver_data exynos4415_fimd_driver_data = {
. timing_base = 0x20000 ,
. lcdblk_offset = 0x210 ,
. lcdblk_vt_shift = 10 ,
. lcdblk_bypass_shift = 1 ,
2016-04-18 11:54:39 +03:00
. trg_type = I80_HW_TRG ,
2014-11-07 09:12:25 +03:00
. has_shadowcon = 1 ,
. has_vidoutcon = 1 ,
2014-11-14 05:36:02 +03:00
. has_vtsel = 1 ,
2016-04-18 11:54:39 +03:00
. has_trigger_per_te = 1 ,
2014-11-07 09:12:25 +03:00
} ;
2012-11-19 13:52:54 +04:00
static struct fimd_driver_data exynos5_fimd_driver_data = {
2012-09-21 15:22:15 +04:00
. timing_base = 0x20000 ,
2014-07-17 13:01:21 +04:00
. lcdblk_offset = 0x214 ,
. lcdblk_vt_shift = 24 ,
. lcdblk_bypass_shift = 15 ,
2013-05-01 23:02:27 +04:00
. has_shadowcon = 1 ,
2014-07-17 13:01:21 +04:00
. has_vidoutcon = 1 ,
2014-11-14 05:36:02 +03:00
. has_vtsel = 1 ,
2016-04-29 19:39:08 +03:00
. has_dp_clk = 1 ,
2012-09-21 15:22:15 +04:00
} ;
2016-02-12 16:31:39 +03:00
static struct fimd_driver_data exynos5420_fimd_driver_data = {
. timing_base = 0x20000 ,
. lcdblk_offset = 0x214 ,
. lcdblk_vt_shift = 24 ,
. lcdblk_bypass_shift = 15 ,
. lcdblk_mic_bypass_shift = 11 ,
. has_shadowcon = 1 ,
. has_vidoutcon = 1 ,
. has_vtsel = 1 ,
. has_mic_bypass = 1 ,
2016-04-29 19:39:08 +03:00
. has_dp_clk = 1 ,
2016-02-12 16:31:39 +03:00
} ;
2011-10-04 14:19:01 +04:00
struct fimd_context {
2014-01-31 01:19:06 +04:00
struct device * dev ;
2014-01-31 01:19:04 +04:00
struct drm_device * drm_dev ;
2015-01-18 12:16:23 +03:00
struct exynos_drm_crtc * crtc ;
2015-04-03 15:03:40 +03:00
struct exynos_drm_plane planes [ WINDOWS_NR ] ;
2015-11-30 16:53:25 +03:00
struct exynos_drm_plane_config configs [ WINDOWS_NR ] ;
2011-10-04 14:19:01 +04:00
struct clk * bus_clk ;
struct clk * lcd_clk ;
void __iomem * regs ;
2014-07-17 13:01:21 +04:00
struct regmap * sysreg ;
2011-10-04 14:19:01 +04:00
unsigned long irq_flags ;
2014-07-17 13:01:21 +04:00
u32 vidcon0 ;
2011-10-04 14:19:01 +04:00
u32 vidcon1 ;
2014-07-17 13:01:21 +04:00
u32 vidout_con ;
u32 i80ifcon ;
bool i80_if ;
2011-12-09 11:52:11 +04:00
bool suspended ;
2014-02-19 16:02:55 +04:00
int pipe ;
2012-12-06 18:46:04 +04:00
wait_queue_head_t wait_vsync_queue ;
atomic_t wait_vsync_event ;
2014-07-17 13:01:21 +04:00
atomic_t win_updated ;
atomic_t triggering ;
2016-09-23 13:43:29 +03:00
u32 clkdiv ;
2011-10-04 14:19:01 +04:00
2016-04-18 11:38:27 +03:00
const struct fimd_driver_data * driver_data ;
2015-08-15 18:14:08 +03:00
struct drm_encoder * encoder ;
2016-04-29 19:39:08 +03:00
struct exynos_drm_clk dp_clk ;
2011-10-04 14:19:01 +04:00
} ;
2012-12-14 10:48:25 +04:00
static const struct of_device_id fimd_driver_dt_match [ ] = {
2013-05-01 23:02:29 +04:00
{ . compatible = " samsung,s3c6400-fimd " ,
. data = & s3c64xx_fimd_driver_data } ,
2014-08-18 11:53:19 +04:00
{ . compatible = " samsung,exynos3250-fimd " ,
. data = & exynos3_fimd_driver_data } ,
2013-02-27 14:32:58 +04:00
{ . compatible = " samsung,exynos4210-fimd " ,
2012-12-14 10:48:25 +04:00
. data = & exynos4_fimd_driver_data } ,
2014-11-07 09:12:25 +03:00
{ . compatible = " samsung,exynos4415-fimd " ,
. data = & exynos4415_fimd_driver_data } ,
2013-02-27 14:32:58 +04:00
{ . compatible = " samsung,exynos5250-fimd " ,
2012-12-14 10:48:25 +04:00
. data = & exynos5_fimd_driver_data } ,
2016-02-12 16:31:39 +03:00
{ . compatible = " samsung,exynos5420-fimd " ,
. data = & exynos5420_fimd_driver_data } ,
2012-12-14 10:48:25 +04:00
{ } ,
} ;
2014-07-30 06:28:31 +04:00
MODULE_DEVICE_TABLE ( of , fimd_driver_dt_match ) ;
2012-12-14 10:48:25 +04:00
2015-11-30 16:53:25 +03:00
static const enum drm_plane_type fimd_win_types [ WINDOWS_NR ] = {
DRM_PLANE_TYPE_PRIMARY ,
DRM_PLANE_TYPE_OVERLAY ,
DRM_PLANE_TYPE_OVERLAY ,
DRM_PLANE_TYPE_OVERLAY ,
DRM_PLANE_TYPE_CURSOR ,
} ;
2015-08-30 18:53:57 +03:00
static const uint32_t fimd_formats [ ] = {
DRM_FORMAT_C8 ,
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_ARGB8888 ,
} ;
2015-06-12 12:07:17 +03:00
static int fimd_enable_vblank ( struct exynos_drm_crtc * crtc )
{
struct fimd_context * ctx = crtc - > ctx ;
u32 val ;
if ( ctx - > suspended )
return - EPERM ;
if ( ! test_and_set_bit ( 0 , & ctx - > irq_flags ) ) {
val = readl ( ctx - > regs + VIDINTCON0 ) ;
val | = VIDINTCON0_INT_ENABLE ;
if ( ctx - > i80_if ) {
val | = VIDINTCON0_INT_I80IFDONE ;
val | = VIDINTCON0_INT_SYSMAINCON ;
val & = ~ VIDINTCON0_INT_SYSSUBCON ;
} else {
val | = VIDINTCON0_INT_FRAME ;
val & = ~ VIDINTCON0_FRAMESEL0_MASK ;
val | = VIDINTCON0_FRAMESEL0_VSYNC ;
val & = ~ VIDINTCON0_FRAMESEL1_MASK ;
val | = VIDINTCON0_FRAMESEL1_NONE ;
}
writel ( val , ctx - > regs + VIDINTCON0 ) ;
}
return 0 ;
}
static void fimd_disable_vblank ( struct exynos_drm_crtc * crtc )
{
struct fimd_context * ctx = crtc - > ctx ;
u32 val ;
if ( ctx - > suspended )
return ;
if ( test_and_clear_bit ( 0 , & ctx - > irq_flags ) ) {
val = readl ( ctx - > regs + VIDINTCON0 ) ;
val & = ~ VIDINTCON0_INT_ENABLE ;
if ( ctx - > i80_if ) {
val & = ~ VIDINTCON0_INT_I80IFDONE ;
val & = ~ VIDINTCON0_INT_SYSMAINCON ;
val & = ~ VIDINTCON0_INT_SYSSUBCON ;
} else
val & = ~ VIDINTCON0_INT_FRAME ;
writel ( val , ctx - > regs + VIDINTCON0 ) ;
}
}
2015-01-18 12:16:23 +03:00
static void fimd_wait_for_vblank ( struct exynos_drm_crtc * crtc )
2014-04-28 16:26:39 +04:00
{
2015-01-18 12:16:23 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2014-04-28 16:26:39 +04:00
if ( ctx - > suspended )
return ;
atomic_set ( & ctx - > wait_vsync_event , 1 ) ;
/*
* wait for FIMD to signal VSYNC interrupt or return after
* timeout which is set to 50 ms ( refresh rate of 20 ) .
*/
if ( ! wait_event_timeout ( ctx - > wait_vsync_queue ,
! atomic_read ( & ctx - > wait_vsync_event ) ,
HZ / 20 ) )
DRM_DEBUG_KMS ( " vblank wait timed out. \n " ) ;
}
2015-05-06 15:10:22 +03:00
static void fimd_enable_video_output ( struct fimd_context * ctx , unsigned int win ,
2014-11-17 16:00:10 +03:00
bool enable )
{
u32 val = readl ( ctx - > regs + WINCON ( win ) ) ;
if ( enable )
val | = WINCONx_ENWIN ;
else
val & = ~ WINCONx_ENWIN ;
writel ( val , ctx - > regs + WINCON ( win ) ) ;
}
2015-05-06 15:10:22 +03:00
static void fimd_enable_shadow_channel_path ( struct fimd_context * ctx ,
unsigned int win ,
2014-11-17 16:00:11 +03:00
bool enable )
{
u32 val = readl ( ctx - > regs + SHADOWCON ) ;
if ( enable )
val | = SHADOWCON_CHx_ENABLE ( win ) ;
else
val & = ~ SHADOWCON_CHx_ENABLE ( win ) ;
writel ( val , ctx - > regs + SHADOWCON ) ;
}
2015-06-22 13:05:04 +03:00
static void fimd_clear_channels ( struct exynos_drm_crtc * crtc )
2014-04-28 16:26:39 +04:00
{
2015-06-22 13:05:04 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2015-05-06 15:10:22 +03:00
unsigned int win , ch_enabled = 0 ;
2014-04-28 16:26:39 +04:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
2015-06-12 12:07:17 +03:00
/* Hardware is in unknown state, so ensure it gets enabled properly */
pm_runtime_get_sync ( ctx - > dev ) ;
clk_prepare_enable ( ctx - > bus_clk ) ;
clk_prepare_enable ( ctx - > lcd_clk ) ;
2014-04-28 16:26:39 +04:00
/* Check if any channel is enabled. */
for ( win = 0 ; win < WINDOWS_NR ; win + + ) {
2014-09-01 17:27:10 +04:00
u32 val = readl ( ctx - > regs + WINCON ( win ) ) ;
if ( val & WINCONx_ENWIN ) {
2014-11-17 16:00:10 +03:00
fimd_enable_video_output ( ctx , win , false ) ;
2014-09-01 17:27:10 +04:00
2014-11-17 16:00:11 +03:00
if ( ctx - > driver_data - > has_shadowcon )
fimd_enable_shadow_channel_path ( ctx , win ,
false ) ;
2014-04-28 16:26:39 +04:00
ch_enabled = 1 ;
}
}
/* Wait for vsync, as disable channel takes effect at next vsync */
2014-09-01 17:27:10 +04:00
if ( ch_enabled ) {
2015-06-12 12:07:17 +03:00
int pipe = ctx - > pipe ;
/* ensure that vblank interrupt won't be reported to core */
ctx - > suspended = false ;
ctx - > pipe = - 1 ;
2014-09-01 17:27:10 +04:00
2015-06-12 12:07:17 +03:00
fimd_enable_vblank ( ctx - > crtc ) ;
2015-01-30 10:43:02 +03:00
fimd_wait_for_vblank ( ctx - > crtc ) ;
2015-06-12 12:07:17 +03:00
fimd_disable_vblank ( ctx - > crtc ) ;
ctx - > suspended = true ;
ctx - > pipe = pipe ;
2014-09-01 17:27:10 +04:00
}
2015-06-12 12:07:17 +03:00
clk_disable_unprepare ( ctx - > lcd_clk ) ;
clk_disable_unprepare ( ctx - > bus_clk ) ;
pm_runtime_put ( ctx - > dev ) ;
2014-04-28 16:26:39 +04:00
}
2016-09-23 13:43:29 +03:00
static int fimd_atomic_check ( struct exynos_drm_crtc * crtc ,
struct drm_crtc_state * state )
2014-01-31 01:19:20 +04:00
{
2016-09-23 13:43:29 +03:00
struct drm_display_mode * mode = & state - > adjusted_mode ;
struct fimd_context * ctx = crtc - > ctx ;
unsigned long ideal_clk , lcd_rate ;
2014-01-31 01:19:20 +04:00
u32 clkdiv ;
2016-05-05 19:23:38 +03:00
if ( mode - > clock = = 0 ) {
2016-09-23 13:43:29 +03:00
DRM_INFO ( " Mode has zero clock value. \n " ) ;
return - EINVAL ;
2016-05-05 19:23:38 +03:00
}
ideal_clk = mode - > clock * 1000 ;
2014-07-17 13:01:21 +04:00
if ( ctx - > i80_if ) {
/*
* The frame done interrupt should be occurred prior to the
* next TE signal .
*/
ideal_clk * = 2 ;
}
2016-09-23 13:43:29 +03:00
lcd_rate = clk_get_rate ( ctx - > lcd_clk ) ;
if ( 2 * lcd_rate < ideal_clk ) {
DRM_INFO ( " sclk_fimd clock too low(%lu) for requested pixel clock(%lu) \n " ,
lcd_rate , ideal_clk ) ;
return - EINVAL ;
}
2014-01-31 01:19:20 +04:00
/* Find the clock divider value that gets us closest to ideal_clk */
2016-09-23 13:43:29 +03:00
clkdiv = DIV_ROUND_CLOSEST ( lcd_rate , ideal_clk ) ;
if ( clkdiv > = 0x200 ) {
DRM_INFO ( " requested pixel clock(%lu) too low \n " , ideal_clk ) ;
return - EINVAL ;
}
ctx - > clkdiv = ( clkdiv < 0x100 ) ? clkdiv : 0xff ;
2014-01-31 01:19:20 +04:00
2016-09-23 13:43:29 +03:00
return 0 ;
2014-01-31 01:19:20 +04:00
}
2016-04-18 11:54:39 +03:00
static void fimd_setup_trigger ( struct fimd_context * ctx )
{
void __iomem * timing_base = ctx - > regs + ctx - > driver_data - > timing_base ;
u32 trg_type = ctx - > driver_data - > trg_type ;
u32 val = readl ( timing_base + TRIGCON ) ;
2016-04-12 03:59:11 +03:00
val & = ~ ( TRGMODE_ENABLE ) ;
2016-04-18 11:54:39 +03:00
if ( trg_type = = I80_HW_TRG ) {
if ( ctx - > driver_data - > has_hw_trigger )
2016-04-12 03:59:11 +03:00
val | = HWTRGEN_ENABLE | HWTRGMASK_ENABLE ;
2016-04-18 11:54:39 +03:00
if ( ctx - > driver_data - > has_trigger_per_te )
2016-04-12 03:59:11 +03:00
val | = HWTRIGEN_PER_ENABLE ;
2016-04-18 11:54:39 +03:00
} else {
2016-04-12 03:59:11 +03:00
val | = TRGMODE_ENABLE ;
2016-04-18 11:54:39 +03:00
}
writel ( val , timing_base + TRIGCON ) ;
}
2015-01-18 12:16:23 +03:00
static void fimd_commit ( struct exynos_drm_crtc * crtc )
2011-10-04 14:19:01 +04:00
{
2015-01-18 12:16:23 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2015-06-02 15:04:42 +03:00
struct drm_display_mode * mode = & crtc - > base . state - > adjusted_mode ;
2016-04-18 11:38:27 +03:00
const struct fimd_driver_data * driver_data = ctx - > driver_data ;
2014-07-17 13:01:21 +04:00
void * timing_base = ctx - > regs + driver_data - > timing_base ;
2016-09-23 13:43:29 +03:00
u32 val ;
2011-10-04 14:19:01 +04:00
2011-12-12 11:35:20 +04:00
if ( ctx - > suspended )
return ;
2014-01-31 01:19:20 +04:00
/* nothing to do if we haven't set the mode yet */
if ( mode - > htotal = = 0 | | mode - > vtotal = = 0 )
return ;
2014-07-17 13:01:21 +04:00
if ( ctx - > i80_if ) {
val = ctx - > i80ifcon | I80IFEN_ENABLE ;
writel ( val , timing_base + I80IFCONFAx ( 0 ) ) ;
/* disable auto frame rate */
writel ( 0 , timing_base + I80IFCONFBx ( 0 ) ) ;
/* set video type selection to I80 interface */
2014-11-14 05:36:02 +03:00
if ( driver_data - > has_vtsel & & ctx - > sysreg & &
regmap_update_bits ( ctx - > sysreg ,
2014-07-17 13:01:21 +04:00
driver_data - > lcdblk_offset ,
0x3 < < driver_data - > lcdblk_vt_shift ,
0x1 < < driver_data - > lcdblk_vt_shift ) ) {
DRM_ERROR ( " Failed to update sysreg for I80 i/f. \n " ) ;
return ;
}
} else {
int vsync_len , vbpd , vfpd , hsync_len , hbpd , hfpd ;
u32 vidcon1 ;
/* setup polarity values */
vidcon1 = ctx - > vidcon1 ;
if ( mode - > flags & DRM_MODE_FLAG_NVSYNC )
vidcon1 | = VIDCON1_INV_VSYNC ;
if ( mode - > flags & DRM_MODE_FLAG_NHSYNC )
vidcon1 | = VIDCON1_INV_HSYNC ;
writel ( vidcon1 , ctx - > regs + driver_data - > timing_base + VIDCON1 ) ;
/* setup vertical timing values. */
vsync_len = mode - > crtc_vsync_end - mode - > crtc_vsync_start ;
vbpd = mode - > crtc_vtotal - mode - > crtc_vsync_end ;
vfpd = mode - > crtc_vsync_start - mode - > crtc_vdisplay ;
val = VIDTCON0_VBPD ( vbpd - 1 ) |
VIDTCON0_VFPD ( vfpd - 1 ) |
VIDTCON0_VSPW ( vsync_len - 1 ) ;
writel ( val , ctx - > regs + driver_data - > timing_base + VIDTCON0 ) ;
/* setup horizontal timing values. */
hsync_len = mode - > crtc_hsync_end - mode - > crtc_hsync_start ;
hbpd = mode - > crtc_htotal - mode - > crtc_hsync_end ;
hfpd = mode - > crtc_hsync_start - mode - > crtc_hdisplay ;
val = VIDTCON1_HBPD ( hbpd - 1 ) |
VIDTCON1_HFPD ( hfpd - 1 ) |
VIDTCON1_HSPW ( hsync_len - 1 ) ;
writel ( val , ctx - > regs + driver_data - > timing_base + VIDTCON1 ) ;
}
if ( driver_data - > has_vidoutcon )
writel ( ctx - > vidout_con , timing_base + VIDOUT_CON ) ;
/* set bypass selection */
if ( ctx - > sysreg & & regmap_update_bits ( ctx - > sysreg ,
driver_data - > lcdblk_offset ,
0x1 < < driver_data - > lcdblk_bypass_shift ,
0x1 < < driver_data - > lcdblk_bypass_shift ) ) {
DRM_ERROR ( " Failed to update sysreg for bypass setting. \n " ) ;
return ;
}
2011-10-04 14:19:01 +04:00
2016-02-12 16:31:39 +03:00
/* TODO: When MIC is enabled for display path, the lcdblk_mic_bypass
* bit should be cleared .
*/
if ( driver_data - > has_mic_bypass & & ctx - > sysreg & &
regmap_update_bits ( ctx - > sysreg ,
driver_data - > lcdblk_offset ,
0x1 < < driver_data - > lcdblk_mic_bypass_shift ,
0x1 < < driver_data - > lcdblk_mic_bypass_shift ) ) {
DRM_ERROR ( " Failed to update sysreg for bypass mic. \n " ) ;
return ;
}
2011-10-04 14:19:01 +04:00
/* setup horizontal and vertical display size. */
2014-01-31 01:19:20 +04:00
val = VIDTCON2_LINEVAL ( mode - > vdisplay - 1 ) |
VIDTCON2_HOZVAL ( mode - > hdisplay - 1 ) |
VIDTCON2_LINEVAL_E ( mode - > vdisplay - 1 ) |
VIDTCON2_HOZVAL_E ( mode - > hdisplay - 1 ) ;
2012-09-21 15:22:15 +04:00
writel ( val , ctx - > regs + driver_data - > timing_base + VIDTCON2 ) ;
2011-10-04 14:19:01 +04:00
2016-04-18 11:54:39 +03:00
fimd_setup_trigger ( ctx ) ;
2014-03-20 12:09:00 +04:00
/*
* fields of register with prefix ' _F ' would be updated
* at vsync ( same as dma start )
*/
2014-07-17 13:01:21 +04:00
val = ctx - > vidcon0 ;
val | = VIDCON0_ENVID | VIDCON0_ENVID_F ;
2011-10-04 14:19:01 +04:00
2014-03-20 12:09:00 +04:00
if ( ctx - > driver_data - > has_clksel )
2013-05-01 23:02:28 +04:00
val | = VIDCON0_CLKSEL_LCD ;
2016-09-23 13:43:29 +03:00
if ( ctx - > clkdiv > 1 )
val | = VIDCON0_CLKVAL_F ( ctx - > clkdiv - 1 ) | VIDCON0_CLKDIR ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + VIDCON0 ) ;
}
2015-08-03 08:40:44 +03:00
static void fimd_win_set_pixfmt ( struct fimd_context * ctx , unsigned int win ,
2015-11-30 16:53:29 +03:00
uint32_t pixel_format , int width )
2011-10-04 14:19:01 +04:00
{
unsigned long val ;
val = WINCONx_ENWIN ;
2013-08-20 09:28:56 +04:00
/*
* In case of s3c64xx , window 0 doesn ' t support alpha channel .
* So the request format is ARGB8888 then change it to XRGB8888 .
*/
if ( ctx - > driver_data - > has_limited_fmt & & ! win ) {
2015-11-30 16:53:29 +03:00
if ( pixel_format = = DRM_FORMAT_ARGB8888 )
pixel_format = DRM_FORMAT_XRGB8888 ;
2013-08-20 09:28:56 +04:00
}
2015-11-30 16:53:29 +03:00
switch ( pixel_format ) {
2013-08-20 08:51:02 +04:00
case DRM_FORMAT_C8 :
2011-10-04 14:19:01 +04:00
val | = WINCON0_BPPMODE_8BPP_PALETTE ;
val | = WINCONx_BURSTLEN_8WORD ;
val | = WINCONx_BYTSWP ;
break ;
2013-08-20 08:51:02 +04:00
case DRM_FORMAT_XRGB1555 :
val | = WINCON0_BPPMODE_16BPP_1555 ;
val | = WINCONx_HAWSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
case DRM_FORMAT_RGB565 :
2011-10-04 14:19:01 +04:00
val | = WINCON0_BPPMODE_16BPP_565 ;
val | = WINCONx_HAWSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
2013-08-20 08:51:02 +04:00
case DRM_FORMAT_XRGB8888 :
2011-10-04 14:19:01 +04:00
val | = WINCON0_BPPMODE_24BPP_888 ;
val | = WINCONx_WSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
2013-08-20 08:51:02 +04:00
case DRM_FORMAT_ARGB8888 :
val | = WINCON1_BPPMODE_25BPP_A1888
2011-10-04 14:19:01 +04:00
| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL ;
val | = WINCONx_WSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
default :
DRM_DEBUG_KMS ( " invalid pixel size so using unpacked 24bpp. \n " ) ;
val | = WINCON0_BPPMODE_24BPP_888 ;
val | = WINCONx_WSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
}
2014-05-07 15:25:22 +04:00
/*
2015-11-30 16:53:29 +03:00
* Setting dma - burst to 16 Word causes permanent tearing for very small
* buffers , e . g . cursor buffer . Burst Mode switching which based on
* plane size is not recommended as plane size varies alot towards the
* end of the screen and rapid movement causes unstable DMA , but it is
* still better to change dma - burst than displaying garbage .
2014-05-07 15:25:22 +04:00
*/
2015-11-30 16:53:29 +03:00
if ( width < MIN_FB_WIDTH_FOR_16WORD_BURST ) {
2014-05-07 15:25:22 +04:00
val & = ~ WINCONx_BURSTLEN_MASK ;
val | = WINCONx_BURSTLEN_4WORD ;
}
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + WINCON ( win ) ) ;
2015-04-01 19:02:05 +03:00
/* hardware window 0 doesn't support alpha channel. */
if ( win ! = 0 ) {
/* OSD alpha */
val = VIDISD14C_ALPHA0_R ( 0xf ) |
VIDISD14C_ALPHA0_G ( 0xf ) |
VIDISD14C_ALPHA0_B ( 0xf ) |
VIDISD14C_ALPHA1_R ( 0xf ) |
VIDISD14C_ALPHA1_G ( 0xf ) |
VIDISD14C_ALPHA1_B ( 0xf ) ;
writel ( val , ctx - > regs + VIDOSD_C ( win ) ) ;
val = VIDW_ALPHA_R ( 0xf ) | VIDW_ALPHA_G ( 0xf ) |
VIDW_ALPHA_G ( 0xf ) ;
writel ( val , ctx - > regs + VIDWnALPHA0 ( win ) ) ;
writel ( val , ctx - > regs + VIDWnALPHA1 ( win ) ) ;
}
2011-10-04 14:19:01 +04:00
}
2014-01-31 01:19:06 +04:00
static void fimd_win_set_colkey ( struct fimd_context * ctx , unsigned int win )
2011-10-04 14:19:01 +04:00
{
unsigned int keycon0 = 0 , keycon1 = 0 ;
keycon0 = ~ ( WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
WxKEYCON0_DIRCON ) | WxKEYCON0_COMPKEY ( 0 ) ;
keycon1 = WxKEYCON1_COLVAL ( 0xffffffff ) ;
writel ( keycon0 , ctx - > regs + WKEYCON0_BASE ( win ) ) ;
writel ( keycon1 , ctx - > regs + WKEYCON1_BASE ( win ) ) ;
}
2013-05-01 23:02:27 +04:00
/**
* shadow_protect_win ( ) - disable updating values from shadow registers at vsync
*
* @ win : window to protect registers for
* @ protect : 1 to protect ( disable updates )
*/
static void fimd_shadow_protect_win ( struct fimd_context * ctx ,
2015-04-03 15:05:52 +03:00
unsigned int win , bool protect )
2013-05-01 23:02:27 +04:00
{
u32 reg , bits , val ;
2015-08-15 19:26:13 +03:00
/*
* SHADOWCON / PRTCON register is used for enabling timing .
*
* for example , once only width value of a register is set ,
* if the dma is started then fimd hardware could malfunction so
* with protect window setting , the register fields with prefix ' _F '
* wouldn ' t be updated at vsync also but updated once unprotect window
* is set .
*/
2013-05-01 23:02:27 +04:00
if ( ctx - > driver_data - > has_shadowcon ) {
reg = SHADOWCON ;
bits = SHADOWCON_WINx_PROTECT ( win ) ;
} else {
reg = PRTCON ;
bits = PRTCON_PROTECT ;
}
val = readl ( ctx - > regs + reg ) ;
if ( protect )
val | = bits ;
else
val & = ~ bits ;
writel ( val , ctx - > regs + reg ) ;
}
2016-01-05 15:52:51 +03:00
static void fimd_atomic_begin ( struct exynos_drm_crtc * crtc )
2015-08-15 19:26:13 +03:00
{
struct fimd_context * ctx = crtc - > ctx ;
2016-01-05 15:52:51 +03:00
int i ;
2015-08-15 19:26:13 +03:00
if ( ctx - > suspended )
return ;
2016-01-05 15:52:51 +03:00
for ( i = 0 ; i < WINDOWS_NR ; i + + )
fimd_shadow_protect_win ( ctx , i , true ) ;
2015-08-15 19:26:13 +03:00
}
2016-01-05 15:52:51 +03:00
static void fimd_atomic_flush ( struct exynos_drm_crtc * crtc )
2015-08-15 19:26:13 +03:00
{
struct fimd_context * ctx = crtc - > ctx ;
2016-01-05 15:52:51 +03:00
int i ;
2015-08-15 19:26:13 +03:00
if ( ctx - > suspended )
return ;
2016-01-05 15:52:51 +03:00
for ( i = 0 ; i < WINDOWS_NR ; i + + )
fimd_shadow_protect_win ( ctx , i , false ) ;
2015-08-15 19:26:13 +03:00
}
2015-08-03 08:39:36 +03:00
static void fimd_update_plane ( struct exynos_drm_crtc * crtc ,
struct exynos_drm_plane * plane )
2011-10-04 14:19:01 +04:00
{
2015-11-30 16:53:22 +03:00
struct exynos_drm_plane_state * state =
to_exynos_plane_state ( plane - > base . state ) ;
2015-01-18 12:16:23 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2015-11-30 16:53:22 +03:00
struct drm_framebuffer * fb = state - > base . fb ;
2015-04-03 15:03:40 +03:00
dma_addr_t dma_addr ;
unsigned long val , size , offset ;
unsigned int last_x , last_y , buf_offsize , line_size ;
2015-12-16 15:21:42 +03:00
unsigned int win = plane - > index ;
2016-12-15 00:32:20 +03:00
unsigned int bpp = fb - > format - > cpp [ 0 ] ;
2015-11-30 16:53:21 +03:00
unsigned int pitch = fb - > pitches [ 0 ] ;
2011-10-04 14:19:01 +04:00
2011-12-12 11:35:20 +04:00
if ( ctx - > suspended )
return ;
2015-11-30 16:53:22 +03:00
offset = state - > src . x * bpp ;
offset + = state - > src . y * pitch ;
2015-04-03 15:03:40 +03:00
2011-10-04 14:19:01 +04:00
/* buffer start address */
2015-11-30 16:53:21 +03:00
dma_addr = exynos_drm_fb_dma_addr ( fb , 0 ) + offset ;
2015-04-03 15:03:40 +03:00
val = ( unsigned long ) dma_addr ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + VIDWx_BUF_START ( win , 0 ) ) ;
/* buffer end address */
2015-11-30 16:53:22 +03:00
size = pitch * state - > crtc . h ;
2015-04-03 15:03:40 +03:00
val = ( unsigned long ) ( dma_addr + size ) ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + VIDWx_BUF_END ( win , 0 ) ) ;
DRM_DEBUG_KMS ( " start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx \n " ,
2015-04-03 15:03:40 +03:00
( unsigned long ) dma_addr , val , size ) ;
2011-10-14 08:29:46 +04:00
DRM_DEBUG_KMS ( " ovl_width = %d, ovl_height = %d \n " ,
2015-11-30 16:53:22 +03:00
state - > crtc . w , state - > crtc . h ) ;
2011-10-04 14:19:01 +04:00
/* buffer size */
2015-11-30 16:53:22 +03:00
buf_offsize = pitch - ( state - > crtc . w * bpp ) ;
line_size = state - > crtc . w * bpp ;
2015-04-03 15:03:40 +03:00
val = VIDW_BUF_SIZE_OFFSET ( buf_offsize ) |
VIDW_BUF_SIZE_PAGEWIDTH ( line_size ) |
VIDW_BUF_SIZE_OFFSET_E ( buf_offsize ) |
VIDW_BUF_SIZE_PAGEWIDTH_E ( line_size ) ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + VIDWx_BUF_SIZE ( win , 0 ) ) ;
/* OSD position */
2015-11-30 16:53:22 +03:00
val = VIDOSDxA_TOPLEFT_X ( state - > crtc . x ) |
VIDOSDxA_TOPLEFT_Y ( state - > crtc . y ) |
VIDOSDxA_TOPLEFT_X_E ( state - > crtc . x ) |
VIDOSDxA_TOPLEFT_Y_E ( state - > crtc . y ) ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + VIDOSD_A ( win ) ) ;
2015-11-30 16:53:22 +03:00
last_x = state - > crtc . x + state - > crtc . w ;
2012-12-14 10:48:23 +04:00
if ( last_x )
last_x - - ;
2015-11-30 16:53:22 +03:00
last_y = state - > crtc . y + state - > crtc . h ;
2012-12-14 10:48:23 +04:00
if ( last_y )
last_y - - ;
2012-12-14 10:48:24 +04:00
val = VIDOSDxB_BOTRIGHT_X ( last_x ) | VIDOSDxB_BOTRIGHT_Y ( last_y ) |
VIDOSDxB_BOTRIGHT_X_E ( last_x ) | VIDOSDxB_BOTRIGHT_Y_E ( last_y ) ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + VIDOSD_B ( win ) ) ;
2011-10-14 08:29:46 +04:00
DRM_DEBUG_KMS ( " osd pos: tx = %d, ty = %d, bx = %d, by = %d \n " ,
2015-11-30 16:53:22 +03:00
state - > crtc . x , state - > crtc . y , last_x , last_y ) ;
2011-10-04 14:19:01 +04:00
/* OSD size */
if ( win ! = 3 & & win ! = 4 ) {
u32 offset = VIDOSD_D ( win ) ;
if ( win = = 0 )
2013-03-08 08:28:52 +04:00
offset = VIDOSD_C ( win ) ;
2015-11-30 16:53:22 +03:00
val = state - > crtc . w * state - > crtc . h ;
2011-10-04 14:19:01 +04:00
writel ( val , ctx - > regs + offset ) ;
DRM_DEBUG_KMS ( " osd size = 0x%x \n " , ( unsigned int ) val ) ;
}
2016-12-15 00:32:55 +03:00
fimd_win_set_pixfmt ( ctx , win , fb - > format - > format , state - > src . w ) ;
2011-10-04 14:19:01 +04:00
/* hardware window 0 doesn't support color key. */
if ( win ! = 0 )
2014-01-31 01:19:06 +04:00
fimd_win_set_colkey ( ctx , win ) ;
2011-10-04 14:19:01 +04:00
2014-11-17 16:00:10 +03:00
fimd_enable_video_output ( ctx , win , true ) ;
2011-12-06 06:06:54 +04:00
2014-11-17 16:00:11 +03:00
if ( ctx - > driver_data - > has_shadowcon )
fimd_enable_shadow_channel_path ( ctx , win , true ) ;
2011-12-06 06:06:54 +04:00
2014-07-17 13:01:21 +04:00
if ( ctx - > i80_if )
atomic_set ( & ctx - > win_updated , 1 ) ;
2011-10-04 14:19:01 +04:00
}
2015-08-03 08:39:36 +03:00
static void fimd_disable_plane ( struct exynos_drm_crtc * crtc ,
struct exynos_drm_plane * plane )
2011-10-04 14:19:01 +04:00
{
2015-01-18 12:16:23 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2015-12-16 15:21:42 +03:00
unsigned int win = plane - > index ;
2011-12-06 06:06:54 +04:00
2015-06-12 14:34:28 +03:00
if ( ctx - > suspended )
2012-12-06 18:46:06 +04:00
return ;
2014-11-17 16:00:10 +03:00
fimd_enable_video_output ( ctx , win , false ) ;
2011-10-04 14:19:01 +04:00
2014-11-17 16:00:11 +03:00
if ( ctx - > driver_data - > has_shadowcon )
fimd_enable_shadow_channel_path ( ctx , win , false ) ;
2014-01-31 01:19:26 +04:00
}
2015-06-01 18:04:55 +03:00
static void fimd_enable ( struct exynos_drm_crtc * crtc )
2014-01-31 01:19:26 +04:00
{
2015-06-01 18:04:55 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2014-01-31 01:19:26 +04:00
if ( ! ctx - > suspended )
2015-06-01 18:04:55 +03:00
return ;
2014-01-31 01:19:26 +04:00
ctx - > suspended = false ;
2014-01-31 01:19:27 +04:00
pm_runtime_get_sync ( ctx - > dev ) ;
2014-01-31 01:19:26 +04:00
/* if vblank was enabled status, enable it again. */
2015-06-01 18:04:55 +03:00
if ( test_and_clear_bit ( 0 , & ctx - > irq_flags ) )
fimd_enable_vblank ( ctx - > crtc ) ;
2014-01-31 01:19:26 +04:00
2015-06-12 14:34:28 +03:00
fimd_commit ( ctx - > crtc ) ;
2014-01-31 01:19:26 +04:00
}
2015-06-01 18:04:55 +03:00
static void fimd_disable ( struct exynos_drm_crtc * crtc )
2014-01-31 01:19:26 +04:00
{
2015-06-01 18:04:55 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2015-06-12 14:34:28 +03:00
int i ;
2015-06-01 18:04:55 +03:00
2014-01-31 01:19:26 +04:00
if ( ctx - > suspended )
2015-06-01 18:04:55 +03:00
return ;
2014-01-31 01:19:26 +04: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-06-12 14:34:28 +03:00
for ( i = 0 ; i < WINDOWS_NR ; i + + )
2015-08-03 08:39:36 +03:00
fimd_disable_plane ( crtc , & ctx - > planes [ i ] ) ;
2014-01-31 01:19:26 +04:00
2015-06-12 16:19:22 +03:00
fimd_enable_vblank ( crtc ) ;
fimd_wait_for_vblank ( crtc ) ;
fimd_disable_vblank ( crtc ) ;
2015-06-12 11:27:16 +03:00
writel ( 0 , ctx - > regs + VIDCON0 ) ;
2014-01-31 01:19:27 +04:00
pm_runtime_put_sync ( ctx - > dev ) ;
2014-01-31 01:19:26 +04:00
ctx - > suspended = true ;
2014-02-19 16:02:55 +04:00
}
2014-07-17 13:01:21 +04:00
static void fimd_trigger ( struct device * dev )
{
2014-11-17 11:54:18 +03:00
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
2016-04-18 11:38:27 +03:00
const struct fimd_driver_data * driver_data = ctx - > driver_data ;
2014-07-17 13:01:21 +04:00
void * timing_base = ctx - > regs + driver_data - > timing_base ;
u32 reg ;
2014-11-17 16:00:08 +03:00
/*
2014-11-17 16:00:12 +03:00
* Skips triggering if in triggering state , because multiple triggering
* requests can cause panel reset .
*/
2014-11-17 16:00:08 +03:00
if ( atomic_read ( & ctx - > triggering ) )
return ;
2014-11-17 16:00:12 +03:00
/* Enters triggering mode */
2014-07-17 13:01:21 +04:00
atomic_set ( & ctx - > triggering , 1 ) ;
reg = readl ( timing_base + TRIGCON ) ;
2016-04-12 03:59:11 +03:00
reg | = ( TRGMODE_ENABLE | SWTRGCMD_ENABLE ) ;
2014-07-17 13:01:21 +04:00
writel ( reg , timing_base + TRIGCON ) ;
2014-11-17 16:00:13 +03:00
/*
* Exits triggering mode if vblank is not enabled yet , because when the
* VIDINTCON0 register is not set , it can not exit from triggering mode .
*/
if ( ! test_bit ( 0 , & ctx - > irq_flags ) )
atomic_set ( & ctx - > triggering , 0 ) ;
2014-07-17 13:01:21 +04:00
}
2015-01-18 12:16:23 +03:00
static void fimd_te_handler ( struct exynos_drm_crtc * crtc )
2014-07-17 13:01:21 +04:00
{
2015-01-18 12:16:23 +03:00
struct fimd_context * ctx = crtc - > ctx ;
2016-04-18 11:54:39 +03:00
u32 trg_type = ctx - > driver_data - > trg_type ;
2014-07-17 13:01:21 +04:00
/* Checks the crtc is detached already from encoder */
if ( ctx - > pipe < 0 | | ! ctx - > drm_dev )
return ;
2016-04-18 11:54:39 +03:00
if ( trg_type = = I80_HW_TRG )
goto out ;
2014-07-17 13:01:21 +04:00
/*
* If there is a page flip request , triggers and handles the page flip
* event so that current fb can be updated into panel GRAM .
*/
if ( atomic_add_unless ( & ctx - > win_updated , - 1 , 0 ) )
fimd_trigger ( ctx - > dev ) ;
2016-04-18 11:54:39 +03:00
out :
2014-07-17 13:01:21 +04:00
/* Wakes up vsync event queue */
if ( atomic_read ( & ctx - > wait_vsync_event ) ) {
atomic_set ( & ctx - > wait_vsync_event , 0 ) ;
wake_up ( & ctx - > wait_vsync_queue ) ;
}
2014-10-01 10:19:10 +04:00
2014-11-17 16:00:14 +03:00
if ( test_bit ( 0 , & ctx - > irq_flags ) )
2015-07-16 18:23:32 +03:00
drm_crtc_handle_vblank ( & ctx - > crtc - > base ) ;
2014-07-17 13:01:21 +04:00
}
2016-04-29 19:39:08 +03:00
static void fimd_dp_clock_enable ( struct exynos_drm_clk * clk , bool enable )
2015-05-07 03:04:44 +03:00
{
2016-04-29 19:39:08 +03:00
struct fimd_context * ctx = container_of ( clk , struct fimd_context ,
dp_clk ) ;
u32 val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE ;
2015-10-01 00:40:54 +03:00
writel ( val , ctx - > regs + DP_MIE_CLKCON ) ;
2015-05-07 03:04:44 +03:00
}
2015-05-07 03:04:45 +03:00
static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
2015-06-01 18:04:55 +03:00
. enable = fimd_enable ,
. disable = fimd_disable ,
2014-01-31 01:19:02 +04:00
. commit = fimd_commit ,
. enable_vblank = fimd_enable_vblank ,
. disable_vblank = fimd_disable_vblank ,
2015-08-15 19:26:13 +03:00
. atomic_begin = fimd_atomic_begin ,
2015-08-03 08:38:05 +03:00
. update_plane = fimd_update_plane ,
. disable_plane = fimd_disable_plane ,
2015-08-15 19:26:13 +03:00
. atomic_flush = fimd_atomic_flush ,
2016-09-23 13:43:29 +03:00
. atomic_check = fimd_atomic_check ,
2014-07-17 13:01:21 +04:00
. te_handler = fimd_te_handler ,
2011-10-04 14:19:01 +04:00
} ;
static irqreturn_t fimd_irq_handler ( int irq , void * dev_id )
{
struct fimd_context * ctx = ( struct fimd_context * ) dev_id ;
2016-09-23 16:21:38 +03:00
u32 val , clear_bit ;
2011-10-04 14:19:01 +04:00
val = readl ( ctx - > regs + VIDINTCON1 ) ;
2014-07-17 13:01:21 +04:00
clear_bit = ctx - > i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME ;
if ( val & clear_bit )
writel ( clear_bit , ctx - > regs + VIDINTCON1 ) ;
2011-10-04 14:19:01 +04:00
2011-12-06 06:06:54 +04:00
/* check the crtc is detached already from encoder */
2014-02-19 16:02:55 +04:00
if ( ctx - > pipe < 0 | | ! ctx - > drm_dev )
2011-12-06 06:06:54 +04:00
goto out ;
2011-11-11 16:28:00 +04:00
2015-08-15 19:26:11 +03:00
if ( ! ctx - > i80_if )
drm_crtc_handle_vblank ( & ctx - > crtc - > base ) ;
if ( ctx - > i80_if ) {
2014-11-17 16:00:12 +03:00
/* Exits triggering mode */
2014-07-17 13:01:21 +04:00
atomic_set ( & ctx - > triggering , 0 ) ;
} else {
/* set wait vsync event to zero and wake up queue. */
if ( atomic_read ( & ctx - > wait_vsync_event ) ) {
atomic_set ( & ctx - > wait_vsync_event , 0 ) ;
wake_up ( & ctx - > wait_vsync_queue ) ;
}
2012-12-06 18:46:04 +04:00
}
2014-07-17 13:01:21 +04:00
2011-12-06 06:06:54 +04:00
out :
2011-10-04 14:19:01 +04:00
return IRQ_HANDLED ;
}
2014-05-09 09:25:20 +04:00
static int fimd_bind ( struct device * dev , struct device * master , void * data )
2013-08-21 18:22:03 +04:00
{
2014-11-17 11:54:18 +03:00
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
2014-05-09 09:25:20 +04:00
struct drm_device * drm_dev = data ;
2015-03-12 07:36:02 +03:00
struct exynos_drm_private * priv = drm_dev - > dev_private ;
2015-04-03 15:03:40 +03:00
struct exynos_drm_plane * exynos_plane ;
2015-11-30 16:53:25 +03:00
unsigned int i ;
2015-04-03 15:05:52 +03:00
int ret ;
2014-04-03 18:26:00 +04:00
2015-03-12 07:36:02 +03:00
ctx - > drm_dev = drm_dev ;
ctx - > pipe = priv - > pipe + + ;
2015-01-11 19:57:07 +03:00
2015-11-30 16:53:25 +03:00
for ( i = 0 ; i < WINDOWS_NR ; i + + ) {
ctx - > configs [ i ] . pixel_formats = fimd_formats ;
ctx - > configs [ i ] . num_pixel_formats = ARRAY_SIZE ( fimd_formats ) ;
ctx - > configs [ i ] . zpos = i ;
ctx - > configs [ i ] . type = fimd_win_types [ i ] ;
2015-12-16 15:21:42 +03:00
ret = exynos_plane_init ( drm_dev , & ctx - > planes [ i ] , i ,
2015-11-30 16:53:25 +03:00
1 < < ctx - > pipe , & ctx - > configs [ i ] ) ;
2015-04-03 15:03:40 +03:00
if ( ret )
return ret ;
}
2015-10-12 16:07:48 +03:00
exynos_plane = & ctx - > planes [ DEFAULT_WIN ] ;
2015-04-03 15:03:40 +03:00
ctx - > crtc = exynos_drm_crtc_create ( drm_dev , & exynos_plane - > base ,
ctx - > pipe , EXYNOS_DISPLAY_TYPE_LCD ,
2015-01-30 10:43:01 +03:00
& fimd_crtc_ops , ctx ) ;
2015-04-07 16:19:43 +03:00
if ( IS_ERR ( ctx - > crtc ) )
return PTR_ERR ( ctx - > crtc ) ;
2015-01-18 12:16:23 +03:00
2016-04-29 19:39:08 +03:00
if ( ctx - > driver_data - > has_dp_clk ) {
ctx - > dp_clk . enable = fimd_dp_clock_enable ;
ctx - > crtc - > pipe_clk = & ctx - > dp_clk ;
}
2015-08-11 11:38:06 +03:00
if ( ctx - > encoder )
2015-08-06 02:24:20 +03:00
exynos_dpi_bind ( drm_dev , ctx - > encoder ) ;
2014-04-03 18:26:00 +04:00
2015-07-28 11:51:02 +03:00
if ( is_drm_iommu_supported ( drm_dev ) )
fimd_clear_channels ( ctx - > crtc ) ;
2015-07-02 15:49:39 +03:00
ret = drm_iommu_attach_device ( drm_dev , dev ) ;
2015-06-22 13:05:04 +03:00
if ( ret )
priv - > pipe - - ;
return ret ;
2014-04-03 18:26:00 +04:00
}
static void fimd_unbind ( struct device * dev , struct device * master ,
void * data )
{
2014-11-17 11:54:18 +03:00
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
2014-04-03 18:26:00 +04:00
2015-06-01 18:04:55 +03:00
fimd_disable ( ctx - > crtc ) ;
2014-04-03 18:26:00 +04:00
2015-07-02 15:49:38 +03:00
drm_iommu_detach_device ( ctx - > drm_dev , ctx - > dev ) ;
2015-03-12 07:36:02 +03:00
2015-08-11 11:38:06 +03:00
if ( ctx - > encoder )
exynos_dpi_remove ( ctx - > encoder ) ;
2014-04-03 18:26:00 +04:00
}
static const struct component_ops fimd_component_ops = {
. bind = fimd_bind ,
. unbind = fimd_unbind ,
} ;
static int fimd_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2013-08-21 18:22:03 +04:00
struct fimd_context * ctx ;
2014-07-17 13:01:21 +04:00
struct device_node * i80_if_timings ;
2013-08-21 18:22:03 +04:00
struct resource * res ;
2014-11-03 23:56:57 +03:00
int ret ;
2011-10-04 14:19:01 +04:00
2014-11-17 11:54:18 +03:00
if ( ! dev - > of_node )
return - ENODEV ;
2013-08-28 09:17:58 +04:00
2013-05-22 16:14:14 +04:00
ctx = devm_kzalloc ( dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
2014-11-17 11:54:18 +03:00
if ( ! ctx )
return - ENOMEM ;
2014-01-31 01:19:06 +04:00
ctx - > dev = dev ;
2014-01-31 01:19:26 +04:00
ctx - > suspended = true ;
2016-04-18 11:38:27 +03:00
ctx - > driver_data = of_device_get_match_data ( dev ) ;
2014-01-31 01:19:06 +04:00
2014-01-31 01:19:23 +04:00
if ( of_property_read_bool ( dev - > of_node , " samsung,invert-vden " ) )
ctx - > vidcon1 | = VIDCON1_INV_VDEN ;
if ( of_property_read_bool ( dev - > of_node , " samsung,invert-vclk " ) )
ctx - > vidcon1 | = VIDCON1_INV_VCLK ;
2013-08-21 18:22:03 +04:00
2014-07-17 13:01:21 +04:00
i80_if_timings = of_get_child_by_name ( dev - > of_node , " i80-if-timings " ) ;
if ( i80_if_timings ) {
u32 val ;
ctx - > i80_if = true ;
if ( ctx - > driver_data - > has_vidoutcon )
ctx - > vidout_con | = VIDOUT_CON_F_I80_LDI0 ;
else
ctx - > vidcon0 | = VIDCON0_VIDOUT_I80_LDI0 ;
/*
* The user manual describes that this " DSI_EN " bit is required
* to enable I80 24 - bit data interface .
*/
ctx - > vidcon0 | = VIDCON0_DSI_EN ;
if ( of_property_read_u32 ( i80_if_timings , " cs-setup " , & val ) )
val = 0 ;
ctx - > i80ifcon = LCD_CS_SETUP ( val ) ;
if ( of_property_read_u32 ( i80_if_timings , " wr-setup " , & val ) )
val = 0 ;
ctx - > i80ifcon | = LCD_WR_SETUP ( val ) ;
if ( of_property_read_u32 ( i80_if_timings , " wr-active " , & val ) )
val = 1 ;
ctx - > i80ifcon | = LCD_WR_ACTIVE ( val ) ;
if ( of_property_read_u32 ( i80_if_timings , " wr-hold " , & val ) )
val = 0 ;
ctx - > i80ifcon | = LCD_WR_HOLD ( val ) ;
}
of_node_put ( i80_if_timings ) ;
ctx - > sysreg = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" samsung,sysreg " ) ;
if ( IS_ERR ( ctx - > sysreg ) ) {
dev_warn ( dev , " failed to get system register. \n " ) ;
ctx - > sysreg = NULL ;
}
2014-01-31 01:19:20 +04:00
ctx - > bus_clk = devm_clk_get ( dev , " fimd " ) ;
if ( IS_ERR ( ctx - > bus_clk ) ) {
dev_err ( dev , " failed to get bus clock \n " ) ;
2015-06-11 17:23:37 +03:00
return PTR_ERR ( ctx - > bus_clk ) ;
2014-01-31 01:19:20 +04:00
}
ctx - > lcd_clk = devm_clk_get ( dev , " sclk_fimd " ) ;
if ( IS_ERR ( ctx - > lcd_clk ) ) {
dev_err ( dev , " failed to get lcd clock \n " ) ;
2015-06-11 17:23:37 +03:00
return PTR_ERR ( ctx - > lcd_clk ) ;
2014-01-31 01:19:20 +04:00
}
2011-10-04 14:19:01 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-05-22 16:14:14 +04:00
ctx - > regs = devm_ioremap_resource ( dev , res ) ;
2015-06-11 17:23:37 +03:00
if ( IS_ERR ( ctx - > regs ) )
return PTR_ERR ( ctx - > regs ) ;
2011-10-04 14:19:01 +04:00
2014-07-17 13:01:21 +04:00
res = platform_get_resource_byname ( pdev , IORESOURCE_IRQ ,
ctx - > i80_if ? " lcd_sys " : " vsync " ) ;
2011-10-04 14:19:01 +04:00
if ( ! res ) {
dev_err ( dev , " irq request failed. \n " ) ;
2015-06-11 17:23:37 +03:00
return - ENXIO ;
2011-10-04 14:19:01 +04:00
}
2014-01-31 01:19:21 +04:00
ret = devm_request_irq ( dev , res - > start , fimd_irq_handler ,
2012-06-19 10:17:39 +04:00
0 , " drm_fimd " , ctx ) ;
if ( ret ) {
2011-10-04 14:19:01 +04:00
dev_err ( dev , " irq request failed. \n " ) ;
2015-06-11 17:23:37 +03:00
return ret ;
2011-10-04 14:19:01 +04:00
}
2013-12-11 14:34:43 +04:00
init_waitqueue_head ( & ctx - > wait_vsync_queue ) ;
2012-12-06 18:46:04 +04:00
atomic_set ( & ctx - > wait_vsync_event , 0 ) ;
2011-10-04 14:19:01 +04:00
2014-11-17 11:54:18 +03:00
platform_set_drvdata ( pdev , ctx ) ;
2014-03-17 16:03:56 +04:00
2015-08-11 11:38:06 +03:00
ctx - > encoder = exynos_dpi_probe ( dev ) ;
if ( IS_ERR ( ctx - > encoder ) )
return PTR_ERR ( ctx - > encoder ) ;
2014-05-09 09:25:20 +04:00
2014-11-17 11:54:18 +03:00
pm_runtime_enable ( dev ) ;
2014-05-09 09:25:20 +04:00
2014-11-17 11:54:18 +03:00
ret = component_add ( dev , & fimd_component_ops ) ;
2014-05-29 13:28:02 +04:00
if ( ret )
goto err_disable_pm_runtime ;
return ret ;
err_disable_pm_runtime :
2014-11-17 11:54:18 +03:00
pm_runtime_disable ( dev ) ;
2014-05-29 13:28:02 +04:00
return ret ;
2014-05-09 09:25:20 +04:00
}
2011-12-09 11:52:11 +04:00
2014-05-09 09:25:20 +04:00
static int fimd_remove ( struct platform_device * pdev )
{
2014-01-31 01:19:27 +04:00
pm_runtime_disable ( & pdev - > dev ) ;
2012-08-17 12:08:04 +04:00
2014-05-29 13:28:02 +04:00
component_del ( & pdev - > dev , & fimd_component_ops ) ;
2012-08-17 12:08:04 +04:00
return 0 ;
2011-12-12 11:35:20 +04:00
}
2015-09-04 23:15:49 +03:00
# ifdef CONFIG_PM
static int exynos_fimd_suspend ( struct device * dev )
{
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( ctx - > lcd_clk ) ;
clk_disable_unprepare ( ctx - > bus_clk ) ;
return 0 ;
}
static int exynos_fimd_resume ( struct device * dev )
{
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
int ret ;
ret = clk_prepare_enable ( ctx - > bus_clk ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to prepare_enable the bus clk [%d] \n " , ret ) ;
return ret ;
}
ret = clk_prepare_enable ( ctx - > lcd_clk ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to prepare_enable the lcd clk [%d] \n " , ret ) ;
return ret ;
}
return 0 ;
}
# endif
static const struct dev_pm_ops exynos_fimd_pm_ops = {
SET_RUNTIME_PM_OPS ( exynos_fimd_suspend , exynos_fimd_resume , NULL )
} ;
2012-03-16 13:47:08 +04:00
struct platform_driver fimd_driver = {
2011-10-04 14:19:01 +04:00
. probe = fimd_probe ,
2012-12-22 03:09:25 +04:00
. remove = fimd_remove ,
2011-10-04 14:19:01 +04:00
. driver = {
. name = " exynos4-fb " ,
. owner = THIS_MODULE ,
2015-09-04 23:15:49 +03:00
. pm = & exynos_fimd_pm_ops ,
2013-08-28 09:17:58 +04:00
. of_match_table = fimd_driver_dt_match ,
2011-10-04 14:19:01 +04:00
} ,
} ;