2011-10-04 19:19:01 +09: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 18:01:07 +01:00
# include <drm/drmP.h>
2011-10-04 19:19:01 +09:00
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
2013-08-14 16:38:01 +05:30
# include <linux/of.h>
2012-12-14 15:48:25 +09:00
# include <linux/of_device.h>
2011-12-09 16:52:11 +09:00
# include <linux/pm_runtime.h>
2014-05-09 14:25:20 +09:00
# include <linux/component.h>
2014-07-17 18:01:21 +09:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2011-10-04 19:19:01 +09:00
2013-03-07 12:15:21 +05:30
# include <video/of_display_timing.h>
2013-08-21 16:22:01 +02:00
# include <video/of_videomode.h>
2012-08-08 09:44:49 +09:00
# include <video/samsung_fimd.h>
2011-10-04 19:19:01 +09:00
# include <drm/exynos_drm.h>
# include "exynos_drm_drv.h"
# include "exynos_drm_fbdev.h"
# include "exynos_drm_crtc.h"
2015-04-03 21:03:40 +09:00
# include "exynos_drm_plane.h"
2012-10-19 17:16:36 +09:00
# include "exynos_drm_iommu.h"
2011-10-04 19:19:01 +09:00
/*
2013-09-19 10:39:44 +05:30
* FIMD stands for Fully Interactive Mobile Display and
2011-10-04 19:19:01 +09:00
* as a display controller , it transfers contents drawn on memory
* to a LCD Panel through Display Interfaces such as RGB or
* CPU Interface .
*/
2013-08-21 16:22:01 +02:00
# define FIMD_DEFAULT_FRAMERATE 60
2014-05-07 16:55:22 +05:30
# define MIN_FB_WIDTH_FOR_16WORD_BURST 128
2013-08-21 16:22:01 +02:00
2011-10-04 19:19:01 +09: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-07 23:28:52 -05: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 19:19:01 +09:00
# define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
2015-04-01 13:02:05 -03:00
# define VIDWnALPHA0(win) (VIDW_ALPHA + 0x00 + (win) * 8)
# define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8)
2011-10-04 19:19:01 +09:00
# define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
# 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-07 23:28:52 -05:00
# define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8))
2011-10-04 19:19:01 +09:00
/* color key value register for hardware window 1 ~ 4. */
2013-03-07 23:28:52 -05:00
# define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
2011-10-04 19:19:01 +09:00
2014-07-17 18:01:21 +09:00
/* I80 / RGB trigger control register */
# define TRIGCON 0x1A4
# define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0)
# define SWTRGCMD_I80_RGB_ENABLE (1 << 1)
/* 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 19:19:01 +09:00
/* FIMD has totally five hardware windows. */
# define WINDOWS_NR 5
2012-09-21 16:52:15 +05:30
struct fimd_driver_data {
unsigned int timing_base ;
2014-07-17 18:01:21 +09:00
unsigned int lcdblk_offset ;
unsigned int lcdblk_vt_shift ;
unsigned int lcdblk_bypass_shift ;
2013-05-01 21:02:27 +02:00
unsigned int has_shadowcon : 1 ;
2013-05-01 21:02:28 +02:00
unsigned int has_clksel : 1 ;
2013-08-20 14:28:56 +09:00
unsigned int has_limited_fmt : 1 ;
2014-07-17 18:01:21 +09:00
unsigned int has_vidoutcon : 1 ;
2014-11-14 11:36:02 +09:00
unsigned int has_vtsel : 1 ;
2012-09-21 16:52:15 +05:30
} ;
2013-05-01 21:02:29 +02:00
static struct fimd_driver_data s3c64xx_fimd_driver_data = {
. timing_base = 0x0 ,
. has_clksel = 1 ,
2013-08-20 14:28:56 +09:00
. has_limited_fmt = 1 ,
2013-05-01 21:02:29 +02:00
} ;
2014-08-18 16:53:19 +09:00
static struct fimd_driver_data exynos3_fimd_driver_data = {
. timing_base = 0x20000 ,
. lcdblk_offset = 0x210 ,
. lcdblk_bypass_shift = 1 ,
. has_shadowcon = 1 ,
. has_vidoutcon = 1 ,
} ;
2012-11-19 15:22:54 +05:30
static struct fimd_driver_data exynos4_fimd_driver_data = {
2012-09-21 16:52:15 +05:30
. timing_base = 0x0 ,
2014-07-17 18:01:21 +09:00
. lcdblk_offset = 0x210 ,
. lcdblk_vt_shift = 10 ,
. lcdblk_bypass_shift = 1 ,
2013-05-01 21:02:27 +02:00
. has_shadowcon = 1 ,
2014-11-14 11:36:02 +09:00
. has_vtsel = 1 ,
2012-09-21 16:52:15 +05:30
} ;
2014-11-07 15:12:25 +09:00
static struct fimd_driver_data exynos4415_fimd_driver_data = {
. timing_base = 0x20000 ,
. lcdblk_offset = 0x210 ,
. lcdblk_vt_shift = 10 ,
. lcdblk_bypass_shift = 1 ,
. has_shadowcon = 1 ,
. has_vidoutcon = 1 ,
2014-11-14 11:36:02 +09:00
. has_vtsel = 1 ,
2014-11-07 15:12:25 +09:00
} ;
2012-11-19 15:22:54 +05:30
static struct fimd_driver_data exynos5_fimd_driver_data = {
2012-09-21 16:52:15 +05:30
. timing_base = 0x20000 ,
2014-07-17 18:01:21 +09:00
. lcdblk_offset = 0x214 ,
. lcdblk_vt_shift = 24 ,
. lcdblk_bypass_shift = 15 ,
2013-05-01 21:02:27 +02:00
. has_shadowcon = 1 ,
2014-07-17 18:01:21 +09:00
. has_vidoutcon = 1 ,
2014-11-14 11:36:02 +09:00
. has_vtsel = 1 ,
2012-09-21 16:52:15 +05:30
} ;
2011-10-04 19:19:01 +09:00
struct fimd_context {
2014-01-30 16:19:06 -05:00
struct device * dev ;
2014-01-30 16:19:04 -05:00
struct drm_device * drm_dev ;
2015-01-18 18:16:23 +09:00
struct exynos_drm_crtc * crtc ;
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane planes [ WINDOWS_NR ] ;
2011-10-04 19:19:01 +09:00
struct clk * bus_clk ;
struct clk * lcd_clk ;
void __iomem * regs ;
2014-07-17 18:01:21 +09:00
struct regmap * sysreg ;
2011-10-04 19:19:01 +09:00
unsigned int default_win ;
unsigned long irq_flags ;
2014-07-17 18:01:21 +09:00
u32 vidcon0 ;
2011-10-04 19:19:01 +09:00
u32 vidcon1 ;
2014-07-17 18:01:21 +09:00
u32 vidout_con ;
u32 i80ifcon ;
bool i80_if ;
2011-12-09 16:52:11 +09:00
bool suspended ;
2014-02-19 21:02:55 +09:00
int pipe ;
2012-12-06 20:16:04 +05:30
wait_queue_head_t wait_vsync_queue ;
atomic_t wait_vsync_event ;
2014-07-17 18:01:21 +09:00
atomic_t win_updated ;
atomic_t triggering ;
2011-10-04 19:19:01 +09:00
2013-08-21 16:22:03 +02:00
struct exynos_drm_panel_info panel ;
2013-05-01 21:02:26 +02:00
struct fimd_driver_data * driver_data ;
2014-04-03 16:26:00 +02:00
struct exynos_drm_display * display ;
2011-10-04 19:19:01 +09:00
} ;
2012-12-14 15:48:25 +09:00
static const struct of_device_id fimd_driver_dt_match [ ] = {
2013-05-01 21:02:29 +02:00
{ . compatible = " samsung,s3c6400-fimd " ,
. data = & s3c64xx_fimd_driver_data } ,
2014-08-18 16:53:19 +09:00
{ . compatible = " samsung,exynos3250-fimd " ,
. data = & exynos3_fimd_driver_data } ,
2013-02-27 16:02:58 +05:30
{ . compatible = " samsung,exynos4210-fimd " ,
2012-12-14 15:48:25 +09:00
. data = & exynos4_fimd_driver_data } ,
2014-11-07 15:12:25 +09:00
{ . compatible = " samsung,exynos4415-fimd " ,
. data = & exynos4415_fimd_driver_data } ,
2013-02-27 16:02:58 +05:30
{ . compatible = " samsung,exynos5250-fimd " ,
2012-12-14 15:48:25 +09:00
. data = & exynos5_fimd_driver_data } ,
{ } ,
} ;
2014-07-30 11:28:31 +09:00
MODULE_DEVICE_TABLE ( of , fimd_driver_dt_match ) ;
2012-12-14 15:48:25 +09:00
2012-09-21 16:52:15 +05:30
static inline struct fimd_driver_data * drm_fimd_get_driver_data (
struct platform_device * pdev )
{
2012-12-14 15:48:25 +09:00
const struct of_device_id * of_id =
of_match_device ( fimd_driver_dt_match , & pdev - > dev ) ;
2013-08-28 10:47:58 +05:30
return ( struct fimd_driver_data * ) of_id - > data ;
2012-09-21 16:52:15 +05:30
}
2015-01-18 18:16:23 +09:00
static void fimd_wait_for_vblank ( struct exynos_drm_crtc * crtc )
2014-04-28 21:26:39 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2014-04-28 21:26:39 +09: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 " ) ;
}
2014-11-17 22:00:10 +09:00
static void fimd_enable_video_output ( struct fimd_context * ctx , int win ,
bool enable )
{
u32 val = readl ( ctx - > regs + WINCON ( win ) ) ;
if ( enable )
val | = WINCONx_ENWIN ;
else
val & = ~ WINCONx_ENWIN ;
writel ( val , ctx - > regs + WINCON ( win ) ) ;
}
2014-11-17 22:00:11 +09:00
static void fimd_enable_shadow_channel_path ( struct fimd_context * ctx , int win ,
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-01-30 16:43:02 +09:00
static void fimd_clear_channel ( struct fimd_context * ctx )
2014-04-28 21:26:39 +09:00
{
int win , ch_enabled = 0 ;
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
/* Check if any channel is enabled. */
for ( win = 0 ; win < WINDOWS_NR ; win + + ) {
2014-09-01 22:27:10 +09:00
u32 val = readl ( ctx - > regs + WINCON ( win ) ) ;
if ( val & WINCONx_ENWIN ) {
2014-11-17 22:00:10 +09:00
fimd_enable_video_output ( ctx , win , false ) ;
2014-09-01 22:27:10 +09:00
2014-11-17 22:00:11 +09:00
if ( ctx - > driver_data - > has_shadowcon )
fimd_enable_shadow_channel_path ( ctx , win ,
false ) ;
2014-04-28 21:26:39 +09:00
ch_enabled = 1 ;
}
}
/* Wait for vsync, as disable channel takes effect at next vsync */
2014-09-01 22:27:10 +09:00
if ( ch_enabled ) {
unsigned int state = ctx - > suspended ;
ctx - > suspended = 0 ;
2015-01-30 16:43:02 +09:00
fimd_wait_for_vblank ( ctx - > crtc ) ;
2014-09-01 22:27:10 +09:00
ctx - > suspended = state ;
}
2014-04-28 21:26:39 +09:00
}
2015-03-12 13:36:02 +09:00
static int fimd_iommu_attach_devices ( struct fimd_context * ctx ,
2014-05-09 14:25:20 +09:00
struct drm_device * drm_dev )
2014-01-30 16:19:04 -05:00
{
2014-02-19 21:02:55 +09:00
/* attach this sub driver to iommu mapping if supported. */
2014-04-28 21:26:39 +09:00
if ( is_drm_iommu_supported ( ctx - > drm_dev ) ) {
2015-01-12 01:57:07 +09:00
int ret ;
2014-04-28 21:26:39 +09:00
/*
* If any channel is already active , iommu will throw
* a PAGE FAULT when enabled . So clear any channel if enabled .
*/
2015-01-30 16:43:02 +09:00
fimd_clear_channel ( ctx ) ;
2015-01-12 01:57:07 +09:00
ret = drm_iommu_attach_device ( ctx - > drm_dev , ctx - > dev ) ;
if ( ret ) {
DRM_ERROR ( " drm_iommu_attach failed. \n " ) ;
return ret ;
}
2014-04-28 21:26:39 +09:00
}
2011-12-16 21:49:03 +09:00
2014-02-19 21:02:55 +09:00
return 0 ;
2011-12-06 11:06:54 +09:00
}
2015-03-12 13:36:02 +09:00
static void fimd_iommu_detach_devices ( struct fimd_context * ctx )
2011-12-06 11:06:54 +09:00
{
2014-02-19 21:02:55 +09:00
/* detach this sub driver from iommu mapping if supported. */
if ( is_drm_iommu_supported ( ctx - > drm_dev ) )
drm_iommu_detach_device ( ctx - > drm_dev , ctx - > dev ) ;
2011-12-06 11:06:54 +09:00
}
2014-01-30 16:19:20 -05:00
static u32 fimd_calc_clkdiv ( struct fimd_context * ctx ,
const struct drm_display_mode * mode )
{
unsigned long ideal_clk = mode - > htotal * mode - > vtotal * mode - > vrefresh ;
u32 clkdiv ;
2014-07-17 18:01:21 +09:00
if ( ctx - > i80_if ) {
/*
* The frame done interrupt should be occurred prior to the
* next TE signal .
*/
ideal_clk * = 2 ;
}
2014-01-30 16:19:20 -05:00
/* Find the clock divider value that gets us closest to ideal_clk */
clkdiv = DIV_ROUND_UP ( clk_get_rate ( ctx - > lcd_clk ) , ideal_clk ) ;
return ( clkdiv < 0x100 ) ? clkdiv : 0xff ;
}
2015-01-18 18:16:23 +09:00
static bool fimd_mode_fixup ( struct exynos_drm_crtc * crtc ,
2014-01-30 16:19:20 -05:00
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
if ( adjusted_mode - > vrefresh = = 0 )
adjusted_mode - > vrefresh = FIMD_DEFAULT_FRAMERATE ;
return true ;
}
2015-01-18 18:16:23 +09:00
static void fimd_commit ( struct exynos_drm_crtc * crtc )
2011-10-04 19:19:01 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2014-11-27 16:28:44 -02:00
struct drm_display_mode * mode = & crtc - > base . mode ;
2014-07-17 18:01:21 +09:00
struct fimd_driver_data * driver_data = ctx - > driver_data ;
void * timing_base = ctx - > regs + driver_data - > timing_base ;
u32 val , clkdiv ;
2011-10-04 19:19:01 +09:00
2011-12-12 16:35:20 +09:00
if ( ctx - > suspended )
return ;
2014-01-30 16:19:20 -05:00
/* nothing to do if we haven't set the mode yet */
if ( mode - > htotal = = 0 | | mode - > vtotal = = 0 )
return ;
2014-07-17 18:01:21 +09: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 11:36:02 +09:00
if ( driver_data - > has_vtsel & & ctx - > sysreg & &
regmap_update_bits ( ctx - > sysreg ,
2014-07-17 18:01:21 +09: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 19:19:01 +09:00
/* setup horizontal and vertical display size. */
2014-01-30 16:19:20 -05: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 16:52:15 +05:30
writel ( val , ctx - > regs + driver_data - > timing_base + VIDTCON2 ) ;
2011-10-04 19:19:01 +09:00
2014-03-20 17:09:00 +09:00
/*
* fields of register with prefix ' _F ' would be updated
* at vsync ( same as dma start )
*/
2014-07-17 18:01:21 +09:00
val = ctx - > vidcon0 ;
val | = VIDCON0_ENVID | VIDCON0_ENVID_F ;
2011-10-04 19:19:01 +09:00
2014-03-20 17:09:00 +09:00
if ( ctx - > driver_data - > has_clksel )
2013-05-01 21:02:28 +02:00
val | = VIDCON0_CLKSEL_LCD ;
2014-01-30 16:19:20 -05:00
clkdiv = fimd_calc_clkdiv ( ctx , mode ) ;
if ( clkdiv > 1 )
val | = VIDCON0_CLKVAL_F ( clkdiv - 1 ) | VIDCON0_CLKDIR ;
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + VIDCON0 ) ;
}
2015-01-18 18:16:23 +09:00
static int fimd_enable_vblank ( struct exynos_drm_crtc * crtc )
2011-10-04 19:19:01 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2011-10-04 19:19:01 +09:00
u32 val ;
2011-12-09 16:52:11 +09:00
if ( ctx - > suspended )
return - EPERM ;
2011-10-04 19:19:01 +09:00
if ( ! test_and_set_bit ( 0 , & ctx - > irq_flags ) ) {
val = readl ( ctx - > regs + VIDINTCON0 ) ;
val | = VIDINTCON0_INT_ENABLE ;
2014-11-17 22:00:12 +09:00
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 ;
}
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + VIDINTCON0 ) ;
}
return 0 ;
}
2015-01-18 18:16:23 +09:00
static void fimd_disable_vblank ( struct exynos_drm_crtc * crtc )
2011-10-04 19:19:01 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2011-10-04 19:19:01 +09:00
u32 val ;
2011-12-09 16:52:11 +09:00
if ( ctx - > suspended )
return ;
2011-10-04 19:19:01 +09:00
if ( test_and_clear_bit ( 0 , & ctx - > irq_flags ) ) {
val = readl ( ctx - > regs + VIDINTCON0 ) ;
val & = ~ VIDINTCON0_INT_ENABLE ;
2014-11-17 22:00:12 +09:00
if ( ctx - > i80_if ) {
val & = ~ VIDINTCON0_INT_I80IFDONE ;
val & = ~ VIDINTCON0_INT_SYSMAINCON ;
val & = ~ VIDINTCON0_INT_SYSSUBCON ;
} else
val & = ~ VIDINTCON0_INT_FRAME ;
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + VIDINTCON0 ) ;
}
}
2014-01-30 16:19:06 -05:00
static void fimd_win_set_pixfmt ( struct fimd_context * ctx , unsigned int win )
2011-10-04 19:19:01 +09:00
{
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * plane = & ctx - > planes [ win ] ;
2011-10-04 19:19:01 +09:00
unsigned long val ;
val = WINCONx_ENWIN ;
2013-08-20 14:28:56 +09: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-04-03 21:03:40 +09:00
if ( plane - > pixel_format = = DRM_FORMAT_ARGB8888 )
plane - > pixel_format = DRM_FORMAT_XRGB8888 ;
2013-08-20 14:28:56 +09:00
}
2015-04-03 21:03:40 +09:00
switch ( plane - > pixel_format ) {
2013-08-20 13:51:02 +09:00
case DRM_FORMAT_C8 :
2011-10-04 19:19:01 +09:00
val | = WINCON0_BPPMODE_8BPP_PALETTE ;
val | = WINCONx_BURSTLEN_8WORD ;
val | = WINCONx_BYTSWP ;
break ;
2013-08-20 13:51:02 +09: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 19:19:01 +09:00
val | = WINCON0_BPPMODE_16BPP_565 ;
val | = WINCONx_HAWSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
2013-08-20 13:51:02 +09:00
case DRM_FORMAT_XRGB8888 :
2011-10-04 19:19:01 +09:00
val | = WINCON0_BPPMODE_24BPP_888 ;
val | = WINCONx_WSWP ;
val | = WINCONx_BURSTLEN_16WORD ;
break ;
2013-08-20 13:51:02 +09:00
case DRM_FORMAT_ARGB8888 :
val | = WINCON1_BPPMODE_25BPP_A1888
2011-10-04 19:19:01 +09: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 ;
}
2015-04-03 21:03:40 +09:00
DRM_DEBUG_KMS ( " bpp = %d \n " , plane - > bpp ) ;
2011-10-04 19:19:01 +09:00
2014-05-07 16:55:22 +05:30
/*
* In case of exynos , setting dma - burst to 16 Word causes permanent
* tearing for very small buffers , e . g . cursor buffer . Burst Mode
2014-11-03 18:13:27 -02:00
* switching which is based on plane size is not recommended as
* plane size varies alot towards the end of the screen and rapid
2014-05-07 16:55:22 +05:30
* movement causes unstable DMA which results into iommu crash / tear .
*/
2015-04-03 21:03:40 +09:00
if ( plane - > fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST ) {
2014-05-07 16:55:22 +05:30
val & = ~ WINCONx_BURSTLEN_MASK ;
val | = WINCONx_BURSTLEN_4WORD ;
}
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + WINCON ( win ) ) ;
2015-04-01 13: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 19:19:01 +09:00
}
2014-01-30 16:19:06 -05:00
static void fimd_win_set_colkey ( struct fimd_context * ctx , unsigned int win )
2011-10-04 19:19:01 +09: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 21:02:27 +02: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 21:05:52 +09:00
unsigned int win , bool protect )
2013-05-01 21:02:27 +02:00
{
u32 reg , bits , val ;
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 ) ;
}
2015-04-03 21:05:52 +09:00
static void fimd_win_commit ( struct exynos_drm_crtc * crtc , unsigned int win )
2011-10-04 19:19:01 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * plane ;
dma_addr_t dma_addr ;
unsigned long val , size , offset ;
unsigned int last_x , last_y , buf_offsize , line_size ;
2011-10-04 19:19:01 +09:00
2011-12-12 16:35:20 +09:00
if ( ctx - > suspended )
return ;
2013-05-27 11:56:26 +02:00
if ( win < 0 | | win > = WINDOWS_NR )
2011-10-04 19:19:01 +09:00
return ;
2015-04-03 21:03:40 +09:00
plane = & ctx - > planes [ win ] ;
2011-10-04 19:19:01 +09:00
2014-01-30 16:19:26 -05:00
/* If suspended, enable this on resume */
if ( ctx - > suspended ) {
2015-04-03 21:03:40 +09:00
plane - > resume = true ;
2014-01-30 16:19:26 -05:00
return ;
}
2011-10-04 19:19:01 +09:00
/*
2013-05-01 21:02:27 +02:00
* SHADOWCON / PRTCON register is used for enabling timing .
2011-10-04 19:19:01 +09:00
*
* 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 .
*/
/* protect windows */
2013-05-01 21:02:27 +02:00
fimd_shadow_protect_win ( ctx , win , true ) ;
2011-10-04 19:19:01 +09:00
2015-04-03 21:03:40 +09:00
2015-04-07 15:59:38 +09:00
offset = plane - > src_x * ( plane - > bpp > > 3 ) ;
offset + = plane - > src_y * plane - > pitch ;
2015-04-03 21:03:40 +09:00
2011-10-04 19:19:01 +09:00
/* buffer start address */
2015-04-03 21:03:40 +09:00
dma_addr = plane - > dma_addr [ 0 ] + offset ;
val = ( unsigned long ) dma_addr ;
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + VIDWx_BUF_START ( win , 0 ) ) ;
/* buffer end address */
2015-04-08 16:39:06 +01:00
size = plane - > pitch * plane - > crtc_height ;
2015-04-03 21:03:40 +09:00
val = ( unsigned long ) ( dma_addr + size ) ;
2011-10-04 19:19:01 +09: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 21:03:40 +09:00
( unsigned long ) dma_addr , val , size ) ;
2011-10-14 13:29:46 +09:00
DRM_DEBUG_KMS ( " ovl_width = %d, ovl_height = %d \n " ,
2015-04-03 21:03:40 +09:00
plane - > crtc_width , plane - > crtc_height ) ;
2011-10-04 19:19:01 +09:00
/* buffer size */
2015-04-08 16:39:06 +01:00
buf_offsize = plane - > pitch - ( plane - > crtc_width * ( plane - > bpp > > 3 ) ) ;
2015-04-03 21:03:40 +09:00
line_size = plane - > crtc_width * ( plane - > bpp > > 3 ) ;
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 19:19:01 +09:00
writel ( val , ctx - > regs + VIDWx_BUF_SIZE ( win , 0 ) ) ;
/* OSD position */
2015-04-03 21:03:40 +09:00
val = VIDOSDxA_TOPLEFT_X ( plane - > crtc_x ) |
VIDOSDxA_TOPLEFT_Y ( plane - > crtc_y ) |
VIDOSDxA_TOPLEFT_X_E ( plane - > crtc_x ) |
VIDOSDxA_TOPLEFT_Y_E ( plane - > crtc_y ) ;
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + VIDOSD_A ( win ) ) ;
2015-04-03 21:03:40 +09:00
last_x = plane - > crtc_x + plane - > crtc_width ;
2012-12-14 15:48:23 +09:00
if ( last_x )
last_x - - ;
2015-04-03 21:03:40 +09:00
last_y = plane - > crtc_y + plane - > crtc_height ;
2012-12-14 15:48:23 +09:00
if ( last_y )
last_y - - ;
2012-12-14 15:48:24 +09: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 19:19:01 +09:00
writel ( val , ctx - > regs + VIDOSD_B ( win ) ) ;
2011-10-14 13:29:46 +09:00
DRM_DEBUG_KMS ( " osd pos: tx = %d, ty = %d, bx = %d, by = %d \n " ,
2015-04-03 21:03:40 +09:00
plane - > crtc_x , plane - > crtc_y , last_x , last_y ) ;
2011-10-04 19:19:01 +09:00
/* OSD size */
if ( win ! = 3 & & win ! = 4 ) {
u32 offset = VIDOSD_D ( win ) ;
if ( win = = 0 )
2013-03-07 23:28:52 -05:00
offset = VIDOSD_C ( win ) ;
2015-04-03 21:03:40 +09:00
val = plane - > crtc_width * plane - > crtc_height ;
2011-10-04 19:19:01 +09:00
writel ( val , ctx - > regs + offset ) ;
DRM_DEBUG_KMS ( " osd size = 0x%x \n " , ( unsigned int ) val ) ;
}
2014-01-30 16:19:06 -05:00
fimd_win_set_pixfmt ( ctx , win ) ;
2011-10-04 19:19:01 +09:00
/* hardware window 0 doesn't support color key. */
if ( win ! = 0 )
2014-01-30 16:19:06 -05:00
fimd_win_set_colkey ( ctx , win ) ;
2011-10-04 19:19:01 +09:00
2014-11-17 22:00:10 +09:00
fimd_enable_video_output ( ctx , win , true ) ;
2011-12-06 11:06:54 +09:00
2014-11-17 22:00:11 +09:00
if ( ctx - > driver_data - > has_shadowcon )
fimd_enable_shadow_channel_path ( ctx , win , true ) ;
2011-12-06 11:06:54 +09:00
2014-11-17 22:00:09 +09:00
/* Enable DMA channel and unprotect windows */
fimd_shadow_protect_win ( ctx , win , false ) ;
2015-04-03 21:03:40 +09:00
plane - > enabled = true ;
2014-07-17 18:01:21 +09:00
if ( ctx - > i80_if )
atomic_set ( & ctx - > win_updated , 1 ) ;
2011-10-04 19:19:01 +09:00
}
2015-04-03 21:05:52 +09:00
static void fimd_win_disable ( struct exynos_drm_crtc * crtc , unsigned int win )
2011-10-04 19:19:01 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * plane ;
2011-12-08 17:54:07 +09:00
2013-05-27 11:56:26 +02:00
if ( win < 0 | | win > = WINDOWS_NR )
2011-10-04 19:19:01 +09:00
return ;
2015-04-03 21:03:40 +09:00
plane = & ctx - > planes [ win ] ;
2011-12-06 11:06:54 +09:00
2012-12-06 20:16:06 +05:30
if ( ctx - > suspended ) {
/* do not resume this window*/
2015-04-03 21:03:40 +09:00
plane - > resume = false ;
2012-12-06 20:16:06 +05:30
return ;
}
2011-10-04 19:19:01 +09:00
/* protect windows */
2013-05-01 21:02:27 +02:00
fimd_shadow_protect_win ( ctx , win , true ) ;
2011-10-04 19:19:01 +09:00
2014-11-17 22:00:10 +09:00
fimd_enable_video_output ( ctx , win , false ) ;
2011-10-04 19:19:01 +09:00
2014-11-17 22:00:11 +09:00
if ( ctx - > driver_data - > has_shadowcon )
fimd_enable_shadow_channel_path ( ctx , win , false ) ;
2013-05-01 21:02:27 +02:00
2014-11-17 22:00:11 +09:00
/* unprotect windows */
2013-05-01 21:02:27 +02:00
fimd_shadow_protect_win ( ctx , win , false ) ;
2011-12-06 11:06:54 +09:00
2015-04-03 21:03:40 +09:00
plane - > enabled = false ;
2011-10-04 19:19:01 +09:00
}
2015-01-30 16:43:02 +09:00
static void fimd_window_suspend ( struct fimd_context * ctx )
2014-01-30 16:19:26 -05:00
{
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * plane ;
2014-01-30 16:19:26 -05:00
int i ;
for ( i = 0 ; i < WINDOWS_NR ; i + + ) {
2015-04-03 21:03:40 +09:00
plane = & ctx - > planes [ i ] ;
plane - > resume = plane - > enabled ;
if ( plane - > enabled )
2015-01-30 16:43:02 +09:00
fimd_win_disable ( ctx - > crtc , i ) ;
2014-01-30 16:19:26 -05:00
}
}
2015-01-30 16:43:02 +09:00
static void fimd_window_resume ( struct fimd_context * ctx )
2014-01-30 16:19:26 -05:00
{
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * plane ;
2014-01-30 16:19:26 -05:00
int i ;
for ( i = 0 ; i < WINDOWS_NR ; i + + ) {
2015-04-03 21:03:40 +09:00
plane = & ctx - > planes [ i ] ;
plane - > enabled = plane - > resume ;
plane - > resume = false ;
2014-01-30 16:19:26 -05:00
}
}
2015-01-30 16:43:02 +09:00
static void fimd_apply ( struct fimd_context * ctx )
2014-01-30 16:19:26 -05:00
{
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * plane ;
2014-01-30 16:19:26 -05:00
int i ;
for ( i = 0 ; i < WINDOWS_NR ; i + + ) {
2015-04-03 21:03:40 +09:00
plane = & ctx - > planes [ i ] ;
if ( plane - > enabled )
2015-01-30 16:43:02 +09:00
fimd_win_commit ( ctx - > crtc , i ) ;
2014-06-09 16:10:59 +02:00
else
2015-01-30 16:43:02 +09:00
fimd_win_disable ( ctx - > crtc , i ) ;
2014-01-30 16:19:26 -05:00
}
2015-01-30 16:43:02 +09:00
fimd_commit ( ctx - > crtc ) ;
2014-01-30 16:19:26 -05:00
}
2015-01-30 16:43:02 +09:00
static int fimd_poweron ( struct fimd_context * ctx )
2014-01-30 16:19:26 -05:00
{
int ret ;
if ( ! ctx - > suspended )
return 0 ;
ctx - > suspended = false ;
2014-01-30 16:19:27 -05:00
pm_runtime_get_sync ( ctx - > dev ) ;
2014-01-30 16:19:26 -05:00
ret = clk_prepare_enable ( ctx - > bus_clk ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to prepare_enable the bus clk [%d] \n " , ret ) ;
goto bus_clk_err ;
}
ret = clk_prepare_enable ( ctx - > lcd_clk ) ;
if ( ret < 0 ) {
DRM_ERROR ( " Failed to prepare_enable the lcd clk [%d] \n " , ret ) ;
goto lcd_clk_err ;
}
/* if vblank was enabled status, enable it again. */
if ( test_and_clear_bit ( 0 , & ctx - > irq_flags ) ) {
2015-01-30 16:43:02 +09:00
ret = fimd_enable_vblank ( ctx - > crtc ) ;
2014-01-30 16:19:26 -05:00
if ( ret ) {
DRM_ERROR ( " Failed to re-enable vblank [%d] \n " , ret ) ;
goto enable_vblank_err ;
}
}
2015-01-30 16:43:02 +09:00
fimd_window_resume ( ctx ) ;
2014-01-30 16:19:26 -05:00
2015-01-30 16:43:02 +09:00
fimd_apply ( ctx ) ;
2014-01-30 16:19:26 -05:00
return 0 ;
enable_vblank_err :
clk_disable_unprepare ( ctx - > lcd_clk ) ;
lcd_clk_err :
clk_disable_unprepare ( ctx - > bus_clk ) ;
bus_clk_err :
ctx - > suspended = true ;
return ret ;
}
2015-01-30 16:43:02 +09:00
static int fimd_poweroff ( struct fimd_context * ctx )
2014-01-30 16:19:26 -05:00
{
if ( ctx - > suspended )
return 0 ;
/*
* 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-01-30 16:43:02 +09:00
fimd_window_suspend ( ctx ) ;
2014-01-30 16:19:26 -05:00
clk_disable_unprepare ( ctx - > lcd_clk ) ;
clk_disable_unprepare ( ctx - > bus_clk ) ;
2014-01-30 16:19:27 -05:00
pm_runtime_put_sync ( ctx - > dev ) ;
2014-01-30 16:19:26 -05:00
ctx - > suspended = true ;
return 0 ;
}
2015-01-18 18:16:23 +09:00
static void fimd_dpms ( struct exynos_drm_crtc * crtc , int mode )
2014-02-19 21:02:55 +09:00
{
2014-01-30 16:19:27 -05:00
DRM_DEBUG_KMS ( " %s, %d \n " , __FILE__ , mode ) ;
2014-02-19 21:02:55 +09:00
switch ( mode ) {
case DRM_MODE_DPMS_ON :
2015-01-30 16:43:02 +09:00
fimd_poweron ( crtc - > ctx ) ;
2014-02-19 21:02:55 +09:00
break ;
case DRM_MODE_DPMS_STANDBY :
case DRM_MODE_DPMS_SUSPEND :
case DRM_MODE_DPMS_OFF :
2015-01-30 16:43:02 +09:00
fimd_poweroff ( crtc - > ctx ) ;
2014-02-19 21:02:55 +09:00
break ;
default :
DRM_DEBUG_KMS ( " unspecified mode %d \n " , mode ) ;
break ;
}
}
2014-07-17 18:01:21 +09:00
static void fimd_trigger ( struct device * dev )
{
2014-11-17 09:54:18 +01:00
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
2014-07-17 18:01:21 +09:00
struct fimd_driver_data * driver_data = ctx - > driver_data ;
void * timing_base = ctx - > regs + driver_data - > timing_base ;
u32 reg ;
2014-11-17 22:00:08 +09:00
/*
2014-11-17 22:00:12 +09:00
* Skips triggering if in triggering state , because multiple triggering
* requests can cause panel reset .
*/
2014-11-17 22:00:08 +09:00
if ( atomic_read ( & ctx - > triggering ) )
return ;
2014-11-17 22:00:12 +09:00
/* Enters triggering mode */
2014-07-17 18:01:21 +09:00
atomic_set ( & ctx - > triggering , 1 ) ;
reg = readl ( timing_base + TRIGCON ) ;
reg | = ( TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE ) ;
writel ( reg , timing_base + TRIGCON ) ;
2014-11-17 22:00:13 +09: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 18:01:21 +09:00
}
2015-01-18 18:16:23 +09:00
static void fimd_te_handler ( struct exynos_drm_crtc * crtc )
2014-07-17 18:01:21 +09:00
{
2015-01-18 18:16:23 +09:00
struct fimd_context * ctx = crtc - > ctx ;
2014-07-17 18:01:21 +09:00
/* Checks the crtc is detached already from encoder */
if ( ctx - > pipe < 0 | | ! ctx - > drm_dev )
return ;
/*
* 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 ) ;
/* 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 15:19:10 +09:00
2014-11-17 22:00:14 +09:00
if ( test_bit ( 0 , & ctx - > irq_flags ) )
2014-10-01 15:19:10 +09:00
drm_handle_vblank ( ctx - > drm_dev , ctx - > pipe ) ;
2014-07-17 18:01:21 +09:00
}
2015-05-07 09:04:44 +09:00
static void fimd_dp_clock_enable ( struct exynos_drm_crtc * crtc , bool enable )
{
struct fimd_context * ctx = crtc - > ctx ;
u32 val ;
/*
* Only Exynos 5250 , 5260 , 5410 and 542 x requires enabling DP / MIE
* clock . On these SoCs the bootloader may enable it but any
* power domain off / on will reset it to disable state .
*/
if ( ctx - > driver_data ! = & exynos5_fimd_driver_data )
return ;
val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE ;
writel ( DP_MIE_CLK_DP_ENABLE , ctx - > regs + DP_MIE_CLKCON ) ;
}
2015-01-18 18:16:23 +09:00
static struct exynos_drm_crtc_ops fimd_crtc_ops = {
2014-01-30 16:19:02 -05:00
. dpms = fimd_dpms ,
2014-01-30 16:19:20 -05:00
. mode_fixup = fimd_mode_fixup ,
2014-01-30 16:19:02 -05:00
. commit = fimd_commit ,
. enable_vblank = fimd_enable_vblank ,
. disable_vblank = fimd_disable_vblank ,
. wait_for_vblank = fimd_wait_for_vblank ,
. win_commit = fimd_win_commit ,
. win_disable = fimd_win_disable ,
2014-07-17 18:01:21 +09:00
. te_handler = fimd_te_handler ,
2015-05-07 09:04:44 +09:00
. clock_enable = fimd_dp_clock_enable ,
2011-10-04 19:19:01 +09:00
} ;
static irqreturn_t fimd_irq_handler ( int irq , void * dev_id )
{
struct fimd_context * ctx = ( struct fimd_context * ) dev_id ;
2014-07-17 18:01:21 +09:00
u32 val , clear_bit ;
2011-10-04 19:19:01 +09:00
val = readl ( ctx - > regs + VIDINTCON1 ) ;
2014-07-17 18:01:21 +09: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 19:19:01 +09:00
2011-12-06 11:06:54 +09:00
/* check the crtc is detached already from encoder */
2014-02-19 21:02:55 +09:00
if ( ctx - > pipe < 0 | | ! ctx - > drm_dev )
2011-12-06 11:06:54 +09:00
goto out ;
2011-11-11 21:28:00 +09:00
2014-11-17 22:00:12 +09:00
if ( ctx - > i80_if ) {
2014-11-17 22:00:14 +09:00
exynos_drm_crtc_finish_pageflip ( ctx - > drm_dev , ctx - > pipe ) ;
2014-11-17 22:00:12 +09:00
/* Exits triggering mode */
2014-07-17 18:01:21 +09:00
atomic_set ( & ctx - > triggering , 0 ) ;
} else {
2014-11-17 22:00:14 +09:00
drm_handle_vblank ( ctx - > drm_dev , ctx - > pipe ) ;
exynos_drm_crtc_finish_pageflip ( ctx - > drm_dev , ctx - > pipe ) ;
2014-07-17 18:01:21 +09:00
/* 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 20:16:04 +05:30
}
2014-07-17 18:01:21 +09:00
2011-12-06 11:06:54 +09:00
out :
2011-10-04 19:19:01 +09:00
return IRQ_HANDLED ;
}
2014-05-09 14:25:20 +09:00
static int fimd_bind ( struct device * dev , struct device * master , void * data )
2013-08-21 16:22:03 +02:00
{
2014-11-17 09:54:18 +01:00
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
2014-05-09 14:25:20 +09:00
struct drm_device * drm_dev = data ;
2015-03-12 13:36:02 +09:00
struct exynos_drm_private * priv = drm_dev - > dev_private ;
2015-04-03 21:03:40 +09:00
struct exynos_drm_plane * exynos_plane ;
enum drm_plane_type type ;
2015-04-03 21:05:52 +09:00
unsigned int zpos ;
int ret ;
2014-04-03 16:26:00 +02:00
2015-03-12 13:36:02 +09:00
ctx - > drm_dev = drm_dev ;
ctx - > pipe = priv - > pipe + + ;
2015-01-12 01:57:07 +09:00
2015-04-03 21:03:40 +09:00
for ( zpos = 0 ; zpos < WINDOWS_NR ; zpos + + ) {
type = ( zpos = = ctx - > default_win ) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY ;
ret = exynos_plane_init ( drm_dev , & ctx - > planes [ zpos ] ,
2015-04-03 21:05:52 +09:00
1 < < ctx - > pipe , type , zpos ) ;
2015-04-03 21:03:40 +09:00
if ( ret )
return ret ;
}
exynos_plane = & ctx - > planes [ ctx - > default_win ] ;
ctx - > crtc = exynos_drm_crtc_create ( drm_dev , & exynos_plane - > base ,
ctx - > pipe , EXYNOS_DISPLAY_TYPE_LCD ,
2015-01-30 16:43:01 +09:00
& fimd_crtc_ops , ctx ) ;
2015-04-07 22:19:43 +09:00
if ( IS_ERR ( ctx - > crtc ) )
return PTR_ERR ( ctx - > crtc ) ;
2015-01-18 18:16:23 +09:00
2014-04-03 16:26:00 +02:00
if ( ctx - > display )
exynos_drm_create_enc_conn ( drm_dev , ctx - > display ) ;
2015-03-12 13:36:02 +09:00
ret = fimd_iommu_attach_devices ( ctx , drm_dev ) ;
if ( ret )
return ret ;
2014-04-03 16:26:00 +02:00
return 0 ;
}
static void fimd_unbind ( struct device * dev , struct device * master ,
void * data )
{
2014-11-17 09:54:18 +01:00
struct fimd_context * ctx = dev_get_drvdata ( dev ) ;
2014-04-03 16:26:00 +02:00
2015-01-18 18:16:23 +09:00
fimd_dpms ( ctx - > crtc , DRM_MODE_DPMS_OFF ) ;
2014-04-03 16:26:00 +02:00
2015-03-12 13:36:02 +09:00
fimd_iommu_detach_devices ( ctx ) ;
2014-04-03 16:26:00 +02:00
if ( ctx - > display )
2014-11-17 09:54:26 +01:00
exynos_dpi_remove ( ctx - > display ) ;
2014-04-03 16:26:00 +02: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 16:22:03 +02:00
struct fimd_context * ctx ;
2014-07-17 18:01:21 +09:00
struct device_node * i80_if_timings ;
2013-08-21 16:22:03 +02:00
struct resource * res ;
2014-11-03 18:56:57 -02:00
int ret ;
2011-10-04 19:19:01 +09:00
2014-11-17 09:54:18 +01:00
if ( ! dev - > of_node )
return - ENODEV ;
2013-08-28 10:47:58 +05:30
2013-05-22 21:14:14 +09:00
ctx = devm_kzalloc ( dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
2014-11-17 09:54:18 +01:00
if ( ! ctx )
return - ENOMEM ;
ret = exynos_drm_component_add ( dev , EXYNOS_DEVICE_TYPE_CRTC ,
2014-11-05 19:51:35 -02:00
EXYNOS_DISPLAY_TYPE_LCD ) ;
2014-11-17 09:54:18 +01:00
if ( ret )
return ret ;
2011-10-04 19:19:01 +09:00
2014-01-30 16:19:06 -05:00
ctx - > dev = dev ;
2014-01-30 16:19:26 -05:00
ctx - > suspended = true ;
2014-07-17 18:01:21 +09:00
ctx - > driver_data = drm_fimd_get_driver_data ( pdev ) ;
2014-01-30 16:19:06 -05:00
2014-01-30 16:19:23 -05: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 16:22:03 +02:00
2014-07-17 18:01:21 +09: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-30 16:19:20 -05:00
ctx - > bus_clk = devm_clk_get ( dev , " fimd " ) ;
if ( IS_ERR ( ctx - > bus_clk ) ) {
dev_err ( dev , " failed to get bus clock \n " ) ;
2014-05-29 18:28:02 +09:00
ret = PTR_ERR ( ctx - > bus_clk ) ;
goto err_del_component ;
2014-01-30 16:19:20 -05: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 " ) ;
2014-05-29 18:28:02 +09:00
ret = PTR_ERR ( ctx - > lcd_clk ) ;
goto err_del_component ;
2014-01-30 16:19:20 -05:00
}
2011-10-04 19:19:01 +09:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-05-22 21:14:14 +09:00
ctx - > regs = devm_ioremap_resource ( dev , res ) ;
2014-05-29 18:28:02 +09:00
if ( IS_ERR ( ctx - > regs ) ) {
ret = PTR_ERR ( ctx - > regs ) ;
goto err_del_component ;
}
2011-10-04 19:19:01 +09:00
2014-07-17 18:01:21 +09:00
res = platform_get_resource_byname ( pdev , IORESOURCE_IRQ ,
ctx - > i80_if ? " lcd_sys " : " vsync " ) ;
2011-10-04 19:19:01 +09:00
if ( ! res ) {
dev_err ( dev , " irq request failed. \n " ) ;
2014-05-29 18:28:02 +09:00
ret = - ENXIO ;
goto err_del_component ;
2011-10-04 19:19:01 +09:00
}
2014-01-30 16:19:21 -05:00
ret = devm_request_irq ( dev , res - > start , fimd_irq_handler ,
2012-06-19 11:47:39 +05:30
0 , " drm_fimd " , ctx ) ;
if ( ret ) {
2011-10-04 19:19:01 +09:00
dev_err ( dev , " irq request failed. \n " ) ;
2014-05-29 18:28:02 +09:00
goto err_del_component ;
2011-10-04 19:19:01 +09:00
}
2013-12-11 11:34:43 +01:00
init_waitqueue_head ( & ctx - > wait_vsync_queue ) ;
2012-12-06 20:16:04 +05:30
atomic_set ( & ctx - > wait_vsync_event , 0 ) ;
2011-10-04 19:19:01 +09:00
2014-11-17 09:54:18 +01:00
platform_set_drvdata ( pdev , ctx ) ;
2014-03-17 13:03:56 +01:00
2014-04-03 16:26:00 +02:00
ctx - > display = exynos_dpi_probe ( dev ) ;
2014-11-24 16:23:30 -02:00
if ( IS_ERR ( ctx - > display ) ) {
ret = PTR_ERR ( ctx - > display ) ;
goto err_del_component ;
}
2014-05-09 14:25:20 +09:00
2014-11-17 09:54:18 +01:00
pm_runtime_enable ( dev ) ;
2014-05-09 14:25:20 +09:00
2014-11-17 09:54:18 +01:00
ret = component_add ( dev , & fimd_component_ops ) ;
2014-05-29 18:28:02 +09:00
if ( ret )
goto err_disable_pm_runtime ;
return ret ;
err_disable_pm_runtime :
2014-11-17 09:54:18 +01:00
pm_runtime_disable ( dev ) ;
2014-05-29 18:28:02 +09:00
err_del_component :
2014-11-17 09:54:18 +01:00
exynos_drm_component_del ( dev , EXYNOS_DEVICE_TYPE_CRTC ) ;
2014-05-29 18:28:02 +09:00
return ret ;
2014-05-09 14:25:20 +09:00
}
2011-12-09 16:52:11 +09:00
2014-05-09 14:25:20 +09:00
static int fimd_remove ( struct platform_device * pdev )
{
2014-01-30 16:19:27 -05:00
pm_runtime_disable ( & pdev - > dev ) ;
2012-08-17 17:08:04 +09:00
2014-05-29 18:28:02 +09:00
component_del ( & pdev - > dev , & fimd_component_ops ) ;
exynos_drm_component_del ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CRTC ) ;
2012-08-17 17:08:04 +09:00
return 0 ;
2011-12-12 16:35:20 +09:00
}
2012-03-16 18:47:08 +09:00
struct platform_driver fimd_driver = {
2011-10-04 19:19:01 +09:00
. probe = fimd_probe ,
2012-12-21 15:09:25 -08:00
. remove = fimd_remove ,
2011-10-04 19:19:01 +09:00
. driver = {
. name = " exynos4-fb " ,
. owner = THIS_MODULE ,
2013-08-28 10:47:58 +05:30
. of_match_table = fimd_driver_dt_match ,
2011-10-04 19:19:01 +09:00
} ,
} ;