2012-11-15 21:28:22 +00:00
/*
* Copyright ( C ) 2012 Avionic Design GmbH
* Copyright ( C ) 2012 NVIDIA CORPORATION . All rights reserved .
*
* 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 Foundation .
*/
# include <linux/clk.h>
2013-09-24 16:32:47 +02:00
# include <linux/debugfs.h>
2014-06-26 21:41:53 +02:00
# include <linux/iommu.h>
2017-08-21 16:35:17 +02:00
# include <linux/of_device.h>
2015-08-03 13:20:49 +02:00
# include <linux/pm_runtime.h>
2013-11-06 16:20:54 -07:00
# include <linux/reset.h>
2012-11-15 21:28:22 +00:00
2014-07-07 15:32:53 +02:00
# include <soc/tegra/pmc.h>
2013-03-22 16:34:08 +02:00
# include "dc.h"
# include "drm.h"
# include "gem.h"
2012-11-15 21:28:22 +00:00
2014-11-24 17:02:53 +01:00
# include <drm/drm_atomic.h>
2014-11-24 16:27:13 +01:00
# include <drm/drm_atomic_helper.h>
2014-10-29 10:03:57 +01:00
# include <drm/drm_plane_helper.h>
2012-11-04 21:47:13 +01:00
struct tegra_plane {
struct drm_plane base ;
unsigned int index ;
2012-11-15 21:28:22 +00:00
} ;
2012-11-04 21:47:13 +01:00
static inline struct tegra_plane * to_tegra_plane ( struct drm_plane * plane )
{
return container_of ( plane , struct tegra_plane , base ) ;
}
2014-12-08 16:14:45 +01:00
struct tegra_dc_state {
struct drm_crtc_state base ;
struct clk * clk ;
unsigned long pclk ;
unsigned int div ;
2014-11-26 12:28:39 +01:00
u32 planes ;
2014-12-08 16:14:45 +01:00
} ;
static inline struct tegra_dc_state * to_dc_state ( struct drm_crtc_state * state )
{
if ( state )
return container_of ( state , struct tegra_dc_state , base ) ;
return NULL ;
}
2014-11-28 13:14:55 +01:00
struct tegra_plane_state {
struct drm_plane_state base ;
struct tegra_bo_tiling tiling ;
u32 format ;
u32 swap ;
} ;
static inline struct tegra_plane_state *
to_tegra_plane_state ( struct drm_plane_state * state )
{
if ( state )
return container_of ( state , struct tegra_plane_state , base ) ;
return NULL ;
}
2015-07-28 21:27:05 +02:00
static void tegra_dc_stats_reset ( struct tegra_dc_stats * stats )
{
stats - > frames = 0 ;
stats - > vblank = 0 ;
stats - > underflow = 0 ;
stats - > overflow = 0 ;
}
2014-12-08 16:03:53 +01:00
/*
* Reads the active copy of a register . This takes the dc - > lock spinlock to
* prevent races with the VBLANK processing which also needs access to the
* active copy of some registers .
*/
static u32 tegra_dc_readl_active ( struct tegra_dc * dc , unsigned long offset )
{
unsigned long flags ;
u32 value ;
spin_lock_irqsave ( & dc - > lock , flags ) ;
tegra_dc_writel ( dc , READ_MUX , DC_CMD_STATE_ACCESS ) ;
value = tegra_dc_readl ( dc , offset ) ;
tegra_dc_writel ( dc , 0 , DC_CMD_STATE_ACCESS ) ;
spin_unlock_irqrestore ( & dc - > lock , flags ) ;
return value ;
}
2014-12-08 15:50:04 +01:00
/*
* Double - buffered registers have two copies : ASSEMBLY and ACTIVE . When the
* * _ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy .
* Latching happens mmediately if the display controller is in STOP mode or
* on the next frame boundary otherwise .
*
* Triple - buffered registers have three copies : ASSEMBLY , ARM and ACTIVE . The
* ASSEMBLY copy is latched into the ARM copy immediately after * _UPDATE bits
* are written . When the * _ACT_REQ bits are written , the ARM copy is latched
* into the ACTIVE copy , either immediately if the display controller is in
* STOP mode , or at the next frame boundary otherwise .
*/
2014-11-21 17:33:33 +01:00
void tegra_dc_commit ( struct tegra_dc * dc )
2014-10-21 13:41:46 +02:00
{
tegra_dc_writel ( dc , GENERAL_ACT_REQ < < 8 , DC_CMD_STATE_CONTROL ) ;
tegra_dc_writel ( dc , GENERAL_ACT_REQ , DC_CMD_STATE_CONTROL ) ;
}
2014-11-28 13:14:55 +01:00
static int tegra_dc_format ( u32 fourcc , u32 * format , u32 * swap )
2014-03-14 09:54:58 +01:00
{
/* assume no swapping of fetched data */
if ( swap )
* swap = BYTE_SWAP_NOSWAP ;
2014-11-28 13:14:55 +01:00
switch ( fourcc ) {
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_XBGR8888 :
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_R8G8B8A8 ;
break ;
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_XRGB8888 :
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_B8G8R8A8 ;
break ;
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_RGB565 :
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_B5G6R5 ;
break ;
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_UYVY :
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_YCbCr422 ;
break ;
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_YUYV :
if ( swap )
* swap = BYTE_SWAP_SWAP2 ;
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_YCbCr422 ;
break ;
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_YUV420 :
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_YCbCr420P ;
break ;
2014-03-14 09:54:58 +01:00
case DRM_FORMAT_YUV422 :
2014-11-28 13:14:55 +01:00
* format = WIN_COLOR_DEPTH_YCbCr422P ;
break ;
2014-03-14 09:54:58 +01:00
default :
2014-11-28 13:14:55 +01:00
return - EINVAL ;
2014-03-14 09:54:58 +01:00
}
2014-11-28 13:14:55 +01:00
return 0 ;
2014-03-14 09:54:58 +01:00
}
static bool tegra_dc_format_is_yuv ( unsigned int format , bool * planar )
{
switch ( format ) {
case WIN_COLOR_DEPTH_YCbCr422 :
case WIN_COLOR_DEPTH_YUV422 :
if ( planar )
* planar = false ;
return true ;
case WIN_COLOR_DEPTH_YCbCr420P :
case WIN_COLOR_DEPTH_YUV420P :
case WIN_COLOR_DEPTH_YCbCr422P :
case WIN_COLOR_DEPTH_YUV422P :
case WIN_COLOR_DEPTH_YCbCr422R :
case WIN_COLOR_DEPTH_YUV422R :
case WIN_COLOR_DEPTH_YCbCr422RA :
case WIN_COLOR_DEPTH_YUV422RA :
if ( planar )
* planar = true ;
return true ;
}
2014-12-08 15:55:28 +01:00
if ( planar )
* planar = false ;
2014-03-14 09:54:58 +01:00
return false ;
}
static inline u32 compute_dda_inc ( unsigned int in , unsigned int out , bool v ,
unsigned int bpp )
{
fixed20_12 outf = dfixed_init ( out ) ;
fixed20_12 inf = dfixed_init ( in ) ;
u32 dda_inc ;
int max ;
if ( v )
max = 15 ;
else {
switch ( bpp ) {
case 2 :
max = 8 ;
break ;
default :
WARN_ON_ONCE ( 1 ) ;
/* fallthrough */
case 4 :
max = 4 ;
break ;
}
}
outf . full = max_t ( u32 , outf . full - dfixed_const ( 1 ) , dfixed_const ( 1 ) ) ;
inf . full - = dfixed_const ( 1 ) ;
dda_inc = dfixed_div ( inf , outf ) ;
dda_inc = min_t ( u32 , dda_inc , dfixed_const ( max ) ) ;
return dda_inc ;
}
static inline u32 compute_initial_dda ( unsigned int in )
{
fixed20_12 inf = dfixed_init ( in ) ;
return dfixed_frac ( inf ) ;
}
2014-11-24 16:27:13 +01:00
static void tegra_dc_setup_window ( struct tegra_dc * dc , unsigned int index ,
const struct tegra_dc_window * window )
2014-03-14 09:54:58 +01:00
{
unsigned h_offset , v_offset , h_size , v_size , h_dda , v_dda , bpp ;
2014-11-19 13:04:49 -05:00
unsigned long value , flags ;
2014-03-14 09:54:58 +01:00
bool yuv , planar ;
/*
* For YUV planar modes , the number of bytes per pixel takes into
* account only the luma component and therefore is 1.
*/
yuv = tegra_dc_format_is_yuv ( window - > format , & planar ) ;
if ( ! yuv )
bpp = window - > bits_per_pixel / 8 ;
else
bpp = planar ? 1 : 2 ;
2014-11-19 13:04:49 -05:00
spin_lock_irqsave ( & dc - > lock , flags ) ;
2014-03-14 09:54:58 +01:00
value = WINDOW_A_SELECT < < index ;
tegra_dc_writel ( dc , value , DC_CMD_DISPLAY_WINDOW_HEADER ) ;
tegra_dc_writel ( dc , window - > format , DC_WIN_COLOR_DEPTH ) ;
tegra_dc_writel ( dc , window - > swap , DC_WIN_BYTE_SWAP ) ;
value = V_POSITION ( window - > dst . y ) | H_POSITION ( window - > dst . x ) ;
tegra_dc_writel ( dc , value , DC_WIN_POSITION ) ;
value = V_SIZE ( window - > dst . h ) | H_SIZE ( window - > dst . w ) ;
tegra_dc_writel ( dc , value , DC_WIN_SIZE ) ;
h_offset = window - > src . x * bpp ;
v_offset = window - > src . y ;
h_size = window - > src . w * bpp ;
v_size = window - > src . h ;
value = V_PRESCALED_SIZE ( v_size ) | H_PRESCALED_SIZE ( h_size ) ;
tegra_dc_writel ( dc , value , DC_WIN_PRESCALED_SIZE ) ;
/*
* For DDA computations the number of bytes per pixel for YUV planar
* modes needs to take into account all Y , U and V components .
*/
if ( yuv & & planar )
bpp = 2 ;
h_dda = compute_dda_inc ( window - > src . w , window - > dst . w , false , bpp ) ;
v_dda = compute_dda_inc ( window - > src . h , window - > dst . h , true , bpp ) ;
value = V_DDA_INC ( v_dda ) | H_DDA_INC ( h_dda ) ;
tegra_dc_writel ( dc , value , DC_WIN_DDA_INC ) ;
h_dda = compute_initial_dda ( window - > src . x ) ;
v_dda = compute_initial_dda ( window - > src . y ) ;
tegra_dc_writel ( dc , h_dda , DC_WIN_H_INITIAL_DDA ) ;
tegra_dc_writel ( dc , v_dda , DC_WIN_V_INITIAL_DDA ) ;
tegra_dc_writel ( dc , 0 , DC_WIN_UV_BUF_STRIDE ) ;
tegra_dc_writel ( dc , 0 , DC_WIN_BUF_STRIDE ) ;
tegra_dc_writel ( dc , window - > base [ 0 ] , DC_WINBUF_START_ADDR ) ;
if ( yuv & & planar ) {
tegra_dc_writel ( dc , window - > base [ 1 ] , DC_WINBUF_START_ADDR_U ) ;
tegra_dc_writel ( dc , window - > base [ 2 ] , DC_WINBUF_START_ADDR_V ) ;
value = window - > stride [ 1 ] < < 16 | window - > stride [ 0 ] ;
tegra_dc_writel ( dc , value , DC_WIN_LINE_STRIDE ) ;
} else {
tegra_dc_writel ( dc , window - > stride [ 0 ] , DC_WIN_LINE_STRIDE ) ;
}
if ( window - > bottom_up )
v_offset + = window - > src . h - 1 ;
tegra_dc_writel ( dc , h_offset , DC_WINBUF_ADDR_H_OFFSET ) ;
tegra_dc_writel ( dc , v_offset , DC_WINBUF_ADDR_V_OFFSET ) ;
2014-06-03 14:48:12 +02:00
if ( dc - > soc - > supports_block_linear ) {
unsigned long height = window - > tiling . value ;
switch ( window - > tiling . mode ) {
case TEGRA_BO_TILING_MODE_PITCH :
value = DC_WINBUF_SURFACE_KIND_PITCH ;
break ;
case TEGRA_BO_TILING_MODE_TILED :
value = DC_WINBUF_SURFACE_KIND_TILED ;
break ;
case TEGRA_BO_TILING_MODE_BLOCK :
value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT ( height ) |
DC_WINBUF_SURFACE_KIND_BLOCK ;
break ;
}
tegra_dc_writel ( dc , value , DC_WINBUF_SURFACE_KIND ) ;
2014-03-14 09:54:58 +01:00
} else {
2014-06-03 14:48:12 +02:00
switch ( window - > tiling . mode ) {
case TEGRA_BO_TILING_MODE_PITCH :
value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
DC_WIN_BUFFER_ADDR_MODE_LINEAR ;
break ;
2014-03-14 09:54:58 +01:00
2014-06-03 14:48:12 +02:00
case TEGRA_BO_TILING_MODE_TILED :
value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
DC_WIN_BUFFER_ADDR_MODE_TILE ;
break ;
case TEGRA_BO_TILING_MODE_BLOCK :
2014-11-24 16:27:13 +01:00
/*
* No need to handle this here because - > atomic_check
* will already have filtered it out .
*/
break ;
2014-06-03 14:48:12 +02:00
}
tegra_dc_writel ( dc , value , DC_WIN_BUFFER_ADDR_MODE ) ;
}
2014-03-14 09:54:58 +01:00
value = WIN_ENABLE ;
if ( yuv ) {
/* setup default colorspace conversion coefficients */
tegra_dc_writel ( dc , 0x00f0 , DC_WIN_CSC_YOF ) ;
tegra_dc_writel ( dc , 0x012a , DC_WIN_CSC_KYRGB ) ;
tegra_dc_writel ( dc , 0x0000 , DC_WIN_CSC_KUR ) ;
tegra_dc_writel ( dc , 0x0198 , DC_WIN_CSC_KVR ) ;
tegra_dc_writel ( dc , 0x039b , DC_WIN_CSC_KUG ) ;
tegra_dc_writel ( dc , 0x032f , DC_WIN_CSC_KVG ) ;
tegra_dc_writel ( dc , 0x0204 , DC_WIN_CSC_KUB ) ;
tegra_dc_writel ( dc , 0x0000 , DC_WIN_CSC_KVB ) ;
value | = CSC_ENABLE ;
} else if ( window - > bits_per_pixel < 24 ) {
value | = COLOR_EXPAND ;
}
if ( window - > bottom_up )
value | = V_DIRECTION ;
tegra_dc_writel ( dc , value , DC_WIN_WIN_OPTIONS ) ;
/*
* Disable blending and assume Window A is the bottom - most window ,
* Window C is the top - most window and Window B is in the middle .
*/
tegra_dc_writel ( dc , 0xffff00 , DC_WIN_BLEND_NOKEY ) ;
tegra_dc_writel ( dc , 0xffff00 , DC_WIN_BLEND_1WIN ) ;
switch ( index ) {
case 0 :
tegra_dc_writel ( dc , 0x000000 , DC_WIN_BLEND_2WIN_X ) ;
tegra_dc_writel ( dc , 0x000000 , DC_WIN_BLEND_2WIN_Y ) ;
tegra_dc_writel ( dc , 0x000000 , DC_WIN_BLEND_3WIN_XY ) ;
break ;
case 1 :
tegra_dc_writel ( dc , 0xffff00 , DC_WIN_BLEND_2WIN_X ) ;
tegra_dc_writel ( dc , 0x000000 , DC_WIN_BLEND_2WIN_Y ) ;
tegra_dc_writel ( dc , 0x000000 , DC_WIN_BLEND_3WIN_XY ) ;
break ;
case 2 :
tegra_dc_writel ( dc , 0xffff00 , DC_WIN_BLEND_2WIN_X ) ;
tegra_dc_writel ( dc , 0xffff00 , DC_WIN_BLEND_2WIN_Y ) ;
tegra_dc_writel ( dc , 0xffff00 , DC_WIN_BLEND_3WIN_XY ) ;
break ;
}
2014-11-19 13:04:49 -05:00
spin_unlock_irqrestore ( & dc - > lock , flags ) ;
2014-10-21 13:51:53 +02:00
}
static void tegra_plane_destroy ( struct drm_plane * plane )
{
struct tegra_plane * p = to_tegra_plane ( plane ) ;
drm_plane_cleanup ( plane ) ;
kfree ( p ) ;
}
static const u32 tegra_primary_plane_formats [ ] = {
DRM_FORMAT_XBGR8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_RGB565 ,
} ;
2014-11-24 16:27:13 +01:00
static void tegra_primary_plane_destroy ( struct drm_plane * plane )
2014-10-21 13:51:53 +02:00
{
2014-11-24 16:27:13 +01:00
tegra_plane_destroy ( plane ) ;
}
2014-11-28 13:14:55 +01:00
static void tegra_plane_reset ( struct drm_plane * plane )
{
struct tegra_plane_state * state ;
2015-01-28 15:01:22 +01:00
if ( plane - > state )
2016-05-09 16:34:10 +02:00
__drm_atomic_helper_plane_destroy_state ( plane - > state ) ;
2014-11-28 13:14:55 +01:00
kfree ( plane - > state ) ;
plane - > state = NULL ;
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( state ) {
plane - > state = & state - > base ;
plane - > state - > plane = plane ;
}
}
static struct drm_plane_state * tegra_plane_atomic_duplicate_state ( struct drm_plane * plane )
{
struct tegra_plane_state * state = to_tegra_plane_state ( plane - > state ) ;
struct tegra_plane_state * copy ;
2015-01-28 15:01:22 +01:00
copy = kmalloc ( sizeof ( * copy ) , GFP_KERNEL ) ;
2014-11-28 13:14:55 +01:00
if ( ! copy )
return NULL ;
2015-01-28 15:01:22 +01:00
__drm_atomic_helper_plane_duplicate_state ( plane , & copy - > base ) ;
copy - > tiling = state - > tiling ;
copy - > format = state - > format ;
copy - > swap = state - > swap ;
2014-11-28 13:14:55 +01:00
return & copy - > base ;
}
static void tegra_plane_atomic_destroy_state ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
2016-05-09 16:34:10 +02:00
__drm_atomic_helper_plane_destroy_state ( state ) ;
2014-11-28 13:14:55 +01:00
kfree ( state ) ;
}
2014-11-24 16:27:13 +01:00
static const struct drm_plane_funcs tegra_primary_plane_funcs = {
2014-11-24 17:08:06 +01:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2014-11-24 16:27:13 +01:00
. destroy = tegra_primary_plane_destroy ,
2014-11-28 13:14:55 +01:00
. reset = tegra_plane_reset ,
. atomic_duplicate_state = tegra_plane_atomic_duplicate_state ,
. atomic_destroy_state = tegra_plane_atomic_destroy_state ,
2014-11-24 16:27:13 +01:00
} ;
2014-11-26 12:28:39 +01:00
static int tegra_plane_state_add ( struct tegra_plane * plane ,
struct drm_plane_state * state )
{
struct drm_crtc_state * crtc_state ;
struct tegra_dc_state * tegra ;
2017-06-15 02:18:30 +03:00
struct drm_rect clip ;
int err ;
2014-11-26 12:28:39 +01:00
/* Propagate errors from allocation or locking failures. */
crtc_state = drm_atomic_get_crtc_state ( state - > state , state - > crtc ) ;
if ( IS_ERR ( crtc_state ) )
return PTR_ERR ( crtc_state ) ;
2017-06-15 02:18:30 +03:00
clip . x1 = 0 ;
clip . y1 = 0 ;
clip . x2 = crtc_state - > mode . hdisplay ;
clip . y2 = crtc_state - > mode . vdisplay ;
/* Check plane state for visibility and calculate clipping bounds */
2017-11-01 22:16:19 +02:00
err = drm_atomic_helper_check_plane_state ( state , crtc_state , & clip ,
0 , INT_MAX , true , true ) ;
2017-06-15 02:18:30 +03:00
if ( err < 0 )
return err ;
2014-11-26 12:28:39 +01:00
tegra = to_dc_state ( crtc_state ) ;
tegra - > planes | = WIN_A_ACT_REQ < < plane - > index ;
return 0 ;
}
2014-11-24 16:27:13 +01:00
static int tegra_plane_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
2014-11-28 13:14:55 +01:00
struct tegra_plane_state * plane_state = to_tegra_plane_state ( state ) ;
struct tegra_bo_tiling * tiling = & plane_state - > tiling ;
2014-11-26 12:28:39 +01:00
struct tegra_plane * tegra = to_tegra_plane ( plane ) ;
2014-11-24 16:27:13 +01:00
struct tegra_dc * dc = to_tegra_dc ( state - > crtc ) ;
int err ;
/* no need for further checks if the plane is being disabled */
if ( ! state - > crtc )
return 0 ;
2016-12-14 23:32:55 +02:00
err = tegra_dc_format ( state - > fb - > format - > format , & plane_state - > format ,
2014-11-28 13:14:55 +01:00
& plane_state - > swap ) ;
2014-11-24 16:27:13 +01:00
if ( err < 0 )
return err ;
2014-11-28 13:14:55 +01:00
err = tegra_fb_get_tiling ( state - > fb , tiling ) ;
if ( err < 0 )
return err ;
if ( tiling - > mode = = TEGRA_BO_TILING_MODE_BLOCK & &
2014-11-24 16:27:13 +01:00
! dc - > soc - > supports_block_linear ) {
DRM_ERROR ( " hardware doesn't support block linear mode \n " ) ;
return - EINVAL ;
}
/*
* Tegra doesn ' t support different strides for U and V planes so we
* error out if the user tries to display a framebuffer with such a
* configuration .
*/
2016-12-14 23:30:22 +02:00
if ( state - > fb - > format - > num_planes > 2 ) {
2014-11-24 16:27:13 +01:00
if ( state - > fb - > pitches [ 2 ] ! = state - > fb - > pitches [ 1 ] ) {
DRM_ERROR ( " unsupported UV-plane configuration \n " ) ;
return - EINVAL ;
}
}
2014-11-26 12:28:39 +01:00
err = tegra_plane_state_add ( tegra , state ) ;
if ( err < 0 )
return err ;
2014-11-24 16:27:13 +01:00
return 0 ;
}
2017-08-30 17:34:10 +02:00
static void tegra_plane_atomic_disable ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
2017-06-15 02:18:31 +03:00
{
2017-08-30 17:34:10 +02:00
struct tegra_dc * dc = to_tegra_dc ( old_state - > crtc ) ;
struct tegra_plane * p = to_tegra_plane ( plane ) ;
2017-06-15 02:18:31 +03:00
unsigned long flags ;
u32 value ;
2017-08-30 17:34:10 +02:00
/* rien ne va plus */
if ( ! old_state | | ! old_state - > crtc )
return ;
2017-06-15 02:18:31 +03:00
spin_lock_irqsave ( & dc - > lock , flags ) ;
2017-08-30 17:34:10 +02:00
value = WINDOW_A_SELECT < < p - > index ;
2017-06-15 02:18:31 +03:00
tegra_dc_writel ( dc , value , DC_CMD_DISPLAY_WINDOW_HEADER ) ;
value = tegra_dc_readl ( dc , DC_WIN_WIN_OPTIONS ) ;
value & = ~ WIN_ENABLE ;
tegra_dc_writel ( dc , value , DC_WIN_WIN_OPTIONS ) ;
spin_unlock_irqrestore ( & dc - > lock , flags ) ;
}
2014-11-24 16:27:13 +01:00
static void tegra_plane_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
2014-11-28 13:14:55 +01:00
struct tegra_plane_state * state = to_tegra_plane_state ( plane - > state ) ;
2014-11-24 16:27:13 +01:00
struct tegra_dc * dc = to_tegra_dc ( plane - > state - > crtc ) ;
struct drm_framebuffer * fb = plane - > state - > fb ;
2014-10-21 13:51:53 +02:00
struct tegra_plane * p = to_tegra_plane ( plane ) ;
struct tegra_dc_window window ;
2014-11-24 16:27:13 +01:00
unsigned int i ;
2014-10-21 13:51:53 +02:00
2014-11-24 16:27:13 +01:00
/* rien ne va plus */
if ( ! plane - > state - > crtc | | ! plane - > state - > fb )
return ;
2017-06-15 02:18:31 +03:00
if ( ! plane - > state - > visible )
2017-08-30 17:34:10 +02:00
return tegra_plane_atomic_disable ( plane , old_state ) ;
2017-06-15 02:18:31 +03:00
2014-10-21 13:51:53 +02:00
memset ( & window , 0 , sizeof ( window ) ) ;
2017-06-15 02:18:30 +03:00
window . src . x = plane - > state - > src . x1 > > 16 ;
window . src . y = plane - > state - > src . y1 > > 16 ;
window . src . w = drm_rect_width ( & plane - > state - > src ) > > 16 ;
window . src . h = drm_rect_height ( & plane - > state - > src ) > > 16 ;
window . dst . x = plane - > state - > dst . x1 ;
window . dst . y = plane - > state - > dst . y1 ;
window . dst . w = drm_rect_width ( & plane - > state - > dst ) ;
window . dst . h = drm_rect_height ( & plane - > state - > dst ) ;
2016-12-14 23:32:20 +02:00
window . bits_per_pixel = fb - > format - > cpp [ 0 ] * 8 ;
2014-10-21 13:51:53 +02:00
window . bottom_up = tegra_fb_is_bottom_up ( fb ) ;
2014-11-28 13:14:55 +01:00
/* copy from state */
window . tiling = state - > tiling ;
window . format = state - > format ;
window . swap = state - > swap ;
2014-10-21 13:51:53 +02:00
2016-12-14 23:30:22 +02:00
for ( i = 0 ; i < fb - > format - > num_planes ; i + + ) {
2014-11-24 16:27:13 +01:00
struct tegra_bo * bo = tegra_fb_get_plane ( fb , i ) ;
2014-10-21 13:51:53 +02:00
2014-11-24 16:27:13 +01:00
window . base [ i ] = bo - > paddr + fb - > offsets [ i ] ;
2016-08-21 11:57:58 +03:00
/*
* Tegra uses a shared stride for UV planes . Framebuffers are
* already checked for this in the tegra_plane_atomic_check ( )
* function , so it ' s safe to ignore the V - plane pitch here .
*/
if ( i < 2 )
window . stride [ i ] = fb - > pitches [ i ] ;
2014-11-24 16:27:13 +01:00
}
2014-03-14 09:54:58 +01:00
2014-11-24 16:27:13 +01:00
tegra_dc_setup_window ( dc , p - > index , & window ) ;
2014-03-14 09:54:58 +01:00
}
2017-08-30 17:34:10 +02:00
static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
2014-11-24 16:27:13 +01:00
. atomic_check = tegra_plane_atomic_check ,
. atomic_disable = tegra_plane_atomic_disable ,
2017-08-30 17:34:10 +02:00
. atomic_update = tegra_plane_atomic_update ,
2014-10-21 13:51:53 +02:00
} ;
static struct drm_plane * tegra_dc_primary_plane_create ( struct drm_device * drm ,
struct tegra_dc * dc )
{
2014-12-16 18:04:08 +01:00
/*
* Ideally this would use drm_crtc_mask ( ) , but that would require the
* CRTC to already be in the mode_config ' s list of CRTCs . However , it
* will only be added to that list in the drm_crtc_init_with_planes ( )
* ( in tegra_dc_init ( ) ) , which in turn requires registration of these
* planes . So we have ourselves a nice little chicken and egg problem
* here .
*
* We work around this by manually creating the mask from the number
* of CRTCs that have been registered , and should therefore always be
* the same as drm_crtc_index ( ) after registration .
*/
unsigned long possible_crtcs = 1 < < drm - > mode_config . num_crtc ;
2014-10-21 13:51:53 +02:00
struct tegra_plane * plane ;
unsigned int num_formats ;
const u32 * formats ;
int err ;
plane = kzalloc ( sizeof ( * plane ) , GFP_KERNEL ) ;
if ( ! plane )
return ERR_PTR ( - ENOMEM ) ;
num_formats = ARRAY_SIZE ( tegra_primary_plane_formats ) ;
formats = tegra_primary_plane_formats ;
2014-12-16 18:04:08 +01:00
err = drm_universal_plane_init ( drm , & plane - > base , possible_crtcs ,
2014-10-21 13:51:53 +02:00
& tegra_primary_plane_funcs , formats ,
2017-07-23 20:46:38 -07:00
num_formats , NULL ,
DRM_PLANE_TYPE_PRIMARY , NULL ) ;
2014-10-21 13:51:53 +02:00
if ( err < 0 ) {
kfree ( plane ) ;
return ERR_PTR ( err ) ;
}
2017-08-30 17:34:10 +02:00
drm_plane_helper_add ( & plane - > base , & tegra_plane_helper_funcs ) ;
2014-11-24 16:27:13 +01:00
2014-10-21 13:51:53 +02:00
return & plane - > base ;
}
static const u32 tegra_cursor_plane_formats [ ] = {
DRM_FORMAT_RGBA8888 ,
} ;
2014-11-24 16:27:13 +01:00
static int tegra_cursor_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
2014-10-21 13:51:53 +02:00
{
2014-11-26 12:28:39 +01:00
struct tegra_plane * tegra = to_tegra_plane ( plane ) ;
int err ;
2014-11-24 16:27:13 +01:00
/* no need for further checks if the plane is being disabled */
if ( ! state - > crtc )
return 0 ;
2014-10-21 13:51:53 +02:00
/* scaling not supported for cursor */
2014-11-24 16:27:13 +01:00
if ( ( state - > src_w > > 16 ! = state - > crtc_w ) | |
( state - > src_h > > 16 ! = state - > crtc_h ) )
2014-10-21 13:51:53 +02:00
return - EINVAL ;
/* only square cursors supported */
2014-11-24 16:27:13 +01:00
if ( state - > src_w ! = state - > src_h )
return - EINVAL ;
if ( state - > crtc_w ! = 32 & & state - > crtc_w ! = 64 & &
state - > crtc_w ! = 128 & & state - > crtc_w ! = 256 )
2014-10-21 13:51:53 +02:00
return - EINVAL ;
2014-11-26 12:28:39 +01:00
err = tegra_plane_state_add ( tegra , state ) ;
if ( err < 0 )
return err ;
2014-11-24 16:27:13 +01:00
return 0 ;
}
static void tegra_cursor_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct tegra_bo * bo = tegra_fb_get_plane ( plane - > state - > fb , 0 ) ;
struct tegra_dc * dc = to_tegra_dc ( plane - > state - > crtc ) ;
struct drm_plane_state * state = plane - > state ;
u32 value = CURSOR_CLIP_DISPLAY ;
/* rien ne va plus */
if ( ! plane - > state - > crtc | | ! plane - > state - > fb )
return ;
switch ( state - > crtc_w ) {
2014-10-21 13:51:53 +02:00
case 32 :
value | = CURSOR_SIZE_32x32 ;
break ;
case 64 :
value | = CURSOR_SIZE_64x64 ;
break ;
case 128 :
value | = CURSOR_SIZE_128x128 ;
break ;
case 256 :
value | = CURSOR_SIZE_256x256 ;
break ;
default :
2014-11-24 16:27:13 +01:00
WARN ( 1 , " cursor size %ux%u not supported \n " , state - > crtc_w ,
state - > crtc_h ) ;
return ;
2014-10-21 13:51:53 +02:00
}
value | = ( bo - > paddr > > 10 ) & 0x3fffff ;
tegra_dc_writel ( dc , value , DC_DISP_CURSOR_START_ADDR ) ;
# ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
value = ( bo - > paddr > > 32 ) & 0x3 ;
tegra_dc_writel ( dc , value , DC_DISP_CURSOR_START_ADDR_HI ) ;
# endif
/* enable cursor and set blend mode */
value = tegra_dc_readl ( dc , DC_DISP_DISP_WIN_OPTIONS ) ;
value | = CURSOR_ENABLE ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_WIN_OPTIONS ) ;
value = tegra_dc_readl ( dc , DC_DISP_BLEND_CURSOR_CONTROL ) ;
value & = ~ CURSOR_DST_BLEND_MASK ;
value & = ~ CURSOR_SRC_BLEND_MASK ;
value | = CURSOR_MODE_NORMAL ;
value | = CURSOR_DST_BLEND_NEG_K1_TIMES_SRC ;
value | = CURSOR_SRC_BLEND_K1_TIMES_SRC ;
value | = CURSOR_ALPHA ;
tegra_dc_writel ( dc , value , DC_DISP_BLEND_CURSOR_CONTROL ) ;
/* position the cursor */
2014-11-24 16:27:13 +01:00
value = ( state - > crtc_y & 0x3fff ) < < 16 | ( state - > crtc_x & 0x3fff ) ;
2014-10-21 13:51:53 +02:00
tegra_dc_writel ( dc , value , DC_DISP_CURSOR_POSITION ) ;
}
2014-11-24 16:27:13 +01:00
static void tegra_cursor_atomic_disable ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
2014-10-21 13:51:53 +02:00
{
2014-11-24 16:27:13 +01:00
struct tegra_dc * dc ;
2014-10-21 13:51:53 +02:00
u32 value ;
2014-11-24 16:27:13 +01:00
/* rien ne va plus */
if ( ! old_state | | ! old_state - > crtc )
return ;
dc = to_tegra_dc ( old_state - > crtc ) ;
2014-10-21 13:51:53 +02:00
value = tegra_dc_readl ( dc , DC_DISP_DISP_WIN_OPTIONS ) ;
value & = ~ CURSOR_ENABLE ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_WIN_OPTIONS ) ;
}
static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
2014-11-24 17:08:06 +01:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2014-10-21 13:51:53 +02:00
. destroy = tegra_plane_destroy ,
2014-11-28 13:14:55 +01:00
. reset = tegra_plane_reset ,
. atomic_duplicate_state = tegra_plane_atomic_duplicate_state ,
. atomic_destroy_state = tegra_plane_atomic_destroy_state ,
2014-11-24 16:27:13 +01:00
} ;
static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
. atomic_check = tegra_cursor_atomic_check ,
. atomic_update = tegra_cursor_atomic_update ,
. atomic_disable = tegra_cursor_atomic_disable ,
2014-10-21 13:51:53 +02:00
} ;
static struct drm_plane * tegra_dc_cursor_plane_create ( struct drm_device * drm ,
struct tegra_dc * dc )
{
struct tegra_plane * plane ;
unsigned int num_formats ;
const u32 * formats ;
int err ;
plane = kzalloc ( sizeof ( * plane ) , GFP_KERNEL ) ;
if ( ! plane )
return ERR_PTR ( - ENOMEM ) ;
2014-11-26 12:28:39 +01:00
/*
2015-07-21 16:42:30 +02:00
* This index is kind of fake . The cursor isn ' t a regular plane , but
* its update and activation request bits in DC_CMD_STATE_CONTROL do
* use the same programming . Setting this fake index here allows the
* code in tegra_add_plane_state ( ) to do the right thing without the
* need to special - casing the cursor plane .
2014-11-26 12:28:39 +01:00
*/
plane - > index = 6 ;
2014-10-21 13:51:53 +02:00
num_formats = ARRAY_SIZE ( tegra_cursor_plane_formats ) ;
formats = tegra_cursor_plane_formats ;
err = drm_universal_plane_init ( drm , & plane - > base , 1 < < dc - > pipe ,
& tegra_cursor_plane_funcs , formats ,
2017-07-23 20:46:38 -07:00
num_formats , NULL ,
DRM_PLANE_TYPE_CURSOR , NULL ) ;
2014-10-21 13:51:53 +02:00
if ( err < 0 ) {
kfree ( plane ) ;
return ERR_PTR ( err ) ;
}
2014-11-24 16:27:13 +01:00
drm_plane_helper_add ( & plane - > base , & tegra_cursor_plane_helper_funcs ) ;
2012-11-04 21:47:13 +01:00
2014-11-24 16:27:13 +01:00
return & plane - > base ;
2012-11-04 21:47:13 +01:00
}
2014-10-21 13:51:53 +02:00
static void tegra_overlay_plane_destroy ( struct drm_plane * plane )
2012-11-04 21:47:13 +01:00
{
2014-10-21 13:51:53 +02:00
tegra_plane_destroy ( plane ) ;
2012-11-04 21:47:13 +01:00
}
2014-10-21 13:51:53 +02:00
static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
2014-11-24 17:08:06 +01:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2014-10-21 13:51:53 +02:00
. destroy = tegra_overlay_plane_destroy ,
2014-11-28 13:14:55 +01:00
. reset = tegra_plane_reset ,
. atomic_duplicate_state = tegra_plane_atomic_duplicate_state ,
. atomic_destroy_state = tegra_plane_atomic_destroy_state ,
2012-11-04 21:47:13 +01:00
} ;
2014-10-21 13:51:53 +02:00
static const uint32_t tegra_overlay_plane_formats [ ] = {
2013-03-22 15:37:30 +01:00
DRM_FORMAT_XBGR8888 ,
2012-11-04 21:47:13 +01:00
DRM_FORMAT_XRGB8888 ,
2013-03-22 15:37:30 +01:00
DRM_FORMAT_RGB565 ,
2012-11-04 21:47:13 +01:00
DRM_FORMAT_UYVY ,
2014-01-29 20:31:17 +01:00
DRM_FORMAT_YUYV ,
2012-11-04 21:47:13 +01:00
DRM_FORMAT_YUV420 ,
DRM_FORMAT_YUV422 ,
} ;
2014-10-21 13:51:53 +02:00
static struct drm_plane * tegra_dc_overlay_plane_create ( struct drm_device * drm ,
struct tegra_dc * dc ,
unsigned int index )
2012-11-04 21:47:13 +01:00
{
2014-10-21 13:51:53 +02:00
struct tegra_plane * plane ;
unsigned int num_formats ;
const u32 * formats ;
int err ;
2012-11-04 21:47:13 +01:00
2014-10-21 13:51:53 +02:00
plane = kzalloc ( sizeof ( * plane ) , GFP_KERNEL ) ;
if ( ! plane )
return ERR_PTR ( - ENOMEM ) ;
2012-11-04 21:47:13 +01:00
2014-10-21 13:51:53 +02:00
plane - > index = index ;
2012-11-04 21:47:13 +01:00
2014-10-21 13:51:53 +02:00
num_formats = ARRAY_SIZE ( tegra_overlay_plane_formats ) ;
formats = tegra_overlay_plane_formats ;
2012-11-04 21:47:13 +01:00
2014-10-21 13:51:53 +02:00
err = drm_universal_plane_init ( drm , & plane - > base , 1 < < dc - > pipe ,
& tegra_overlay_plane_funcs , formats ,
2017-07-23 20:46:38 -07:00
num_formats , NULL ,
DRM_PLANE_TYPE_OVERLAY , NULL ) ;
2014-10-21 13:51:53 +02:00
if ( err < 0 ) {
kfree ( plane ) ;
return ERR_PTR ( err ) ;
}
2017-08-30 17:34:10 +02:00
drm_plane_helper_add ( & plane - > base , & tegra_plane_helper_funcs ) ;
2014-11-24 16:27:13 +01:00
2014-10-21 13:51:53 +02:00
return & plane - > base ;
}
static int tegra_dc_add_planes ( struct drm_device * drm , struct tegra_dc * dc )
{
struct drm_plane * plane ;
unsigned int i ;
for ( i = 0 ; i < 2 ; i + + ) {
plane = tegra_dc_overlay_plane_create ( drm , dc , 1 + i ) ;
if ( IS_ERR ( plane ) )
return PTR_ERR ( plane ) ;
2012-11-04 21:47:13 +01:00
}
return 0 ;
}
2017-02-07 17:16:32 +08:00
static u32 tegra_dc_get_vblank_counter ( struct drm_crtc * crtc )
2015-01-28 14:43:05 +01:00
{
2017-02-07 17:16:32 +08:00
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2015-01-28 14:43:05 +01:00
if ( dc - > syncpt )
return host1x_syncpt_read ( dc - > syncpt ) ;
/* fallback to software emulated VBLANK counter */
return drm_crtc_vblank_count ( & dc - > base ) ;
}
2017-02-07 17:16:32 +08:00
static int tegra_dc_enable_vblank ( struct drm_crtc * crtc )
2012-11-28 11:45:47 +01:00
{
2017-02-07 17:16:32 +08:00
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2012-11-28 11:45:47 +01:00
unsigned long value , flags ;
spin_lock_irqsave ( & dc - > lock , flags ) ;
value = tegra_dc_readl ( dc , DC_CMD_INT_MASK ) ;
value | = VBLANK_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_MASK ) ;
spin_unlock_irqrestore ( & dc - > lock , flags ) ;
2017-02-07 17:16:32 +08:00
return 0 ;
2012-11-28 11:45:47 +01:00
}
2017-02-07 17:16:32 +08:00
static void tegra_dc_disable_vblank ( struct drm_crtc * crtc )
2012-11-28 11:45:47 +01:00
{
2017-02-07 17:16:32 +08:00
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2012-11-28 11:45:47 +01:00
unsigned long value , flags ;
spin_lock_irqsave ( & dc - > lock , flags ) ;
value = tegra_dc_readl ( dc , DC_CMD_INT_MASK ) ;
value & = ~ VBLANK_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_MASK ) ;
spin_unlock_irqrestore ( & dc - > lock , flags ) ;
}
2012-11-28 12:00:18 +01:00
static void tegra_dc_finish_page_flip ( struct tegra_dc * dc )
{
struct drm_device * drm = dc - > base . dev ;
struct drm_crtc * crtc = & dc - > base ;
unsigned long flags , base ;
2013-03-22 16:34:08 +02:00
struct tegra_bo * bo ;
2012-11-28 12:00:18 +01:00
2014-12-16 16:33:27 +01:00
spin_lock_irqsave ( & drm - > event_lock , flags ) ;
if ( ! dc - > event ) {
spin_unlock_irqrestore ( & drm - > event_lock , flags ) ;
2012-11-28 12:00:18 +01:00
return ;
2014-12-16 16:33:27 +01:00
}
2012-11-28 12:00:18 +01:00
2014-04-01 15:22:40 -07:00
bo = tegra_fb_get_plane ( crtc - > primary - > fb , 0 ) ;
2012-11-28 12:00:18 +01:00
2015-01-07 14:01:26 +03:00
spin_lock ( & dc - > lock ) ;
2014-11-19 13:04:49 -05:00
2012-11-28 12:00:18 +01:00
/* check if new start address has been latched */
2014-11-19 13:04:49 -05:00
tegra_dc_writel ( dc , WINDOW_A_SELECT , DC_CMD_DISPLAY_WINDOW_HEADER ) ;
2012-11-28 12:00:18 +01:00
tegra_dc_writel ( dc , READ_MUX , DC_CMD_STATE_ACCESS ) ;
base = tegra_dc_readl ( dc , DC_WINBUF_START_ADDR ) ;
tegra_dc_writel ( dc , 0 , DC_CMD_STATE_ACCESS ) ;
2015-01-07 14:01:26 +03:00
spin_unlock ( & dc - > lock ) ;
2014-11-19 13:04:49 -05:00
2014-04-01 15:22:40 -07:00
if ( base = = bo - > paddr + crtc - > primary - > fb - > offsets [ 0 ] ) {
2014-12-16 16:03:13 +01:00
drm_crtc_send_vblank_event ( crtc , dc - > event ) ;
drm_crtc_vblank_put ( crtc ) ;
2012-11-28 12:00:18 +01:00
dc - > event = NULL ;
}
2014-12-16 16:33:27 +01:00
spin_unlock_irqrestore ( & drm - > event_lock , flags ) ;
2012-11-28 12:00:18 +01:00
}
2013-10-14 14:06:02 +02:00
static void tegra_dc_destroy ( struct drm_crtc * crtc )
{
drm_crtc_cleanup ( crtc ) ;
}
2014-12-08 16:14:45 +01:00
static void tegra_crtc_reset ( struct drm_crtc * crtc )
{
struct tegra_dc_state * state ;
2015-01-28 15:01:22 +01:00
if ( crtc - > state )
2016-05-09 16:34:09 +02:00
__drm_atomic_helper_crtc_destroy_state ( crtc - > state ) ;
2015-01-28 15:01:22 +01:00
2014-12-08 16:14:45 +01:00
kfree ( crtc - > state ) ;
crtc - > state = NULL ;
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
2015-01-28 15:03:31 +01:00
if ( state ) {
2014-12-08 16:14:45 +01:00
crtc - > state = & state - > base ;
2015-01-28 15:03:31 +01:00
crtc - > state - > crtc = crtc ;
}
2015-07-02 17:04:06 +02:00
drm_crtc_vblank_reset ( crtc ) ;
2014-12-08 16:14:45 +01:00
}
static struct drm_crtc_state *
tegra_crtc_atomic_duplicate_state ( struct drm_crtc * crtc )
{
struct tegra_dc_state * state = to_dc_state ( crtc - > state ) ;
struct tegra_dc_state * copy ;
2015-01-28 15:01:22 +01:00
copy = kmalloc ( sizeof ( * copy ) , GFP_KERNEL ) ;
2014-12-08 16:14:45 +01:00
if ( ! copy )
return NULL ;
2015-01-28 15:01:22 +01:00
__drm_atomic_helper_crtc_duplicate_state ( crtc , & copy - > base ) ;
copy - > clk = state - > clk ;
copy - > pclk = state - > pclk ;
copy - > div = state - > div ;
copy - > planes = state - > planes ;
2014-12-08 16:14:45 +01:00
return & copy - > base ;
}
static void tegra_crtc_atomic_destroy_state ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
2016-05-09 16:34:09 +02:00
__drm_atomic_helper_crtc_destroy_state ( state ) ;
2014-12-08 16:14:45 +01:00
kfree ( state ) ;
}
2012-11-15 21:28:22 +00:00
static const struct drm_crtc_funcs tegra_crtc_funcs = {
2014-11-24 17:41:23 +01:00
. page_flip = drm_atomic_helper_page_flip ,
2014-11-24 17:08:20 +01:00
. set_config = drm_atomic_helper_set_config ,
2013-10-14 14:06:02 +02:00
. destroy = tegra_dc_destroy ,
2014-12-08 16:14:45 +01:00
. reset = tegra_crtc_reset ,
. atomic_duplicate_state = tegra_crtc_atomic_duplicate_state ,
. atomic_destroy_state = tegra_crtc_atomic_destroy_state ,
2017-02-07 17:16:32 +08:00
. get_vblank_counter = tegra_dc_get_vblank_counter ,
. enable_vblank = tegra_dc_enable_vblank ,
. disable_vblank = tegra_dc_disable_vblank ,
2012-11-15 21:28:22 +00:00
} ;
static int tegra_dc_set_timings ( struct tegra_dc * dc ,
struct drm_display_mode * mode )
{
2014-04-16 09:22:38 +02:00
unsigned int h_ref_to_sync = 1 ;
unsigned int v_ref_to_sync = 1 ;
2012-11-15 21:28:22 +00:00
unsigned long value ;
tegra_dc_writel ( dc , 0x0 , DC_DISP_DISP_TIMING_OPTIONS ) ;
value = ( v_ref_to_sync < < 16 ) | h_ref_to_sync ;
tegra_dc_writel ( dc , value , DC_DISP_REF_TO_SYNC ) ;
value = ( ( mode - > vsync_end - mode - > vsync_start ) < < 16 ) |
( ( mode - > hsync_end - mode - > hsync_start ) < < 0 ) ;
tegra_dc_writel ( dc , value , DC_DISP_SYNC_WIDTH ) ;
value = ( ( mode - > vtotal - mode - > vsync_end ) < < 16 ) |
( ( mode - > htotal - mode - > hsync_end ) < < 0 ) ;
2012-12-19 21:38:52 +00:00
tegra_dc_writel ( dc , value , DC_DISP_BACK_PORCH ) ;
value = ( ( mode - > vsync_start - mode - > vdisplay ) < < 16 ) |
( ( mode - > hsync_start - mode - > hdisplay ) < < 0 ) ;
2012-11-15 21:28:22 +00:00
tegra_dc_writel ( dc , value , DC_DISP_FRONT_PORCH ) ;
value = ( mode - > vdisplay < < 16 ) | mode - > hdisplay ;
tegra_dc_writel ( dc , value , DC_DISP_ACTIVE ) ;
return 0 ;
}
2015-01-28 15:25:54 +01:00
/**
* tegra_dc_state_setup_clock - check clock settings and store them in atomic
* state
* @ dc : display controller
* @ crtc_state : CRTC atomic state
* @ clk : parent clock for display controller
* @ pclk : pixel clock
* @ div : shift clock divider
*
* Returns :
* 0 on success or a negative error - code on failure .
*/
2014-12-08 16:14:45 +01:00
int tegra_dc_state_setup_clock ( struct tegra_dc * dc ,
struct drm_crtc_state * crtc_state ,
struct clk * clk , unsigned long pclk ,
unsigned int div )
{
struct tegra_dc_state * state = to_dc_state ( crtc_state ) ;
2015-01-22 08:48:25 +01:00
if ( ! clk_has_parent ( dc - > clk , clk ) )
return - EINVAL ;
2014-12-08 16:14:45 +01:00
state - > clk = clk ;
state - > pclk = pclk ;
state - > div = div ;
return 0 ;
}
2014-12-19 15:09:16 +01:00
static void tegra_dc_commit_state ( struct tegra_dc * dc ,
struct tegra_dc_state * state )
{
u32 value ;
int err ;
err = clk_set_parent ( dc - > clk , state - > clk ) ;
if ( err < 0 )
dev_err ( dc - > dev , " failed to set parent clock: %d \n " , err ) ;
/*
* Outputs may not want to change the parent clock rate . This is only
* relevant to Tegra20 where only a single display PLL is available .
* Since that PLL would typically be used for HDMI , an internal LVDS
* panel would need to be driven by some other clock such as PLL_P
* which is shared with other peripherals . Changing the clock rate
* should therefore be avoided .
*/
if ( state - > pclk > 0 ) {
err = clk_set_rate ( state - > clk , state - > pclk ) ;
if ( err < 0 )
dev_err ( dc - > dev ,
" failed to set clock rate to %lu Hz \n " ,
state - > pclk ) ;
}
DRM_DEBUG_KMS ( " rate: %lu, div: %u \n " , clk_get_rate ( dc - > clk ) ,
state - > div ) ;
DRM_DEBUG_KMS ( " pclk: %lu \n " , state - > pclk ) ;
value = SHIFT_CLK_DIVIDER ( state - > div ) | PIXEL_CLK_DIVIDER_PCD1 ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_CLOCK_CONTROL ) ;
2017-08-30 17:38:39 +02:00
err = clk_set_rate ( dc - > clk , state - > pclk ) ;
if ( err < 0 )
dev_err ( dc - > dev , " failed to set clock %pC to %lu Hz: %d \n " ,
dc - > clk , state - > pclk , err ) ;
2014-12-19 15:09:16 +01:00
}
2015-08-03 13:16:26 +02:00
static void tegra_dc_stop ( struct tegra_dc * dc )
{
u32 value ;
/* stop the display controller */
value = tegra_dc_readl ( dc , DC_CMD_DISPLAY_COMMAND ) ;
value & = ~ DISP_CTRL_MODE_MASK ;
tegra_dc_writel ( dc , value , DC_CMD_DISPLAY_COMMAND ) ;
tegra_dc_commit ( dc ) ;
}
static bool tegra_dc_idle ( struct tegra_dc * dc )
{
u32 value ;
value = tegra_dc_readl_active ( dc , DC_CMD_DISPLAY_COMMAND ) ;
return ( value & DISP_CTRL_MODE_MASK ) = = 0 ;
}
static int tegra_dc_wait_idle ( struct tegra_dc * dc , unsigned long timeout )
{
timeout = jiffies + msecs_to_jiffies ( timeout ) ;
while ( time_before ( jiffies , timeout ) ) {
if ( tegra_dc_idle ( dc ) )
return 0 ;
usleep_range ( 1000 , 2000 ) ;
}
dev_dbg ( dc - > dev , " timeout waiting for DC to become idle \n " ) ;
return - ETIMEDOUT ;
}
2017-06-30 12:36:45 +03:00
static void tegra_crtc_atomic_disable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2015-08-03 13:16:26 +02:00
{
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
u32 value ;
if ( ! tegra_dc_idle ( dc ) ) {
tegra_dc_stop ( dc ) ;
/*
* Ignore the return value , there isn ' t anything useful to do
* in case this fails .
*/
tegra_dc_wait_idle ( dc , 100 ) ;
}
/*
* This should really be part of the RGB encoder driver , but clearing
* these bits has the side - effect of stopping the display controller .
* When that happens no VBLANK interrupts will be raised . At the same
* time the encoder is disabled before the display controller , so the
* above code is always going to timeout waiting for the controller
* to go idle .
*
* Given the close coupling between the RGB encoder and the display
* controller doing it here is still kind of okay . None of the other
* encoder drivers require these bits to be cleared .
*
* XXX : Perhaps given that the display controller is switched off at
* this point anyway maybe clearing these bits isn ' t even useful for
* the RGB encoder ?
*/
if ( dc - > rgb ) {
value = tegra_dc_readl ( dc , DC_CMD_DISPLAY_POWER_CONTROL ) ;
value & = ~ ( PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE ) ;
tegra_dc_writel ( dc , value , DC_CMD_DISPLAY_POWER_CONTROL ) ;
}
tegra_dc_stats_reset ( & dc - > stats ) ;
drm_crtc_vblank_off ( crtc ) ;
2015-08-03 13:20:49 +02:00
pm_runtime_put_sync ( dc - > dev ) ;
2015-08-03 13:16:26 +02:00
}
2017-06-30 12:36:44 +03:00
static void tegra_crtc_atomic_enable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2012-11-15 21:28:22 +00:00
{
2014-11-24 16:27:13 +01:00
struct drm_display_mode * mode = & crtc - > state - > adjusted_mode ;
2014-12-19 15:09:16 +01:00
struct tegra_dc_state * state = to_dc_state ( crtc - > state ) ;
2012-11-15 21:28:22 +00:00
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2014-03-26 12:32:14 +01:00
u32 value ;
2012-11-15 21:28:22 +00:00
2015-08-03 13:20:49 +02:00
pm_runtime_get_sync ( dc - > dev ) ;
/* initialize display controller */
if ( dc - > syncpt ) {
u32 syncpt = host1x_syncpt_id ( dc - > syncpt ) ;
value = SYNCPT_CNTRL_NO_STALL ;
tegra_dc_writel ( dc , value , DC_CMD_GENERAL_INCR_SYNCPT_CNTRL ) ;
value = SYNCPT_VSYNC_ENABLE | syncpt ;
tegra_dc_writel ( dc , value , DC_CMD_CONT_SYNCPT_VSYNC ) ;
}
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_TYPE ) ;
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_POLARITY ) ;
/* initialize timer */
value = CURSOR_THRESHOLD ( 0 ) | WINDOW_A_THRESHOLD ( 0x20 ) |
WINDOW_B_THRESHOLD ( 0x20 ) | WINDOW_C_THRESHOLD ( 0x20 ) ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_MEM_HIGH_PRIORITY ) ;
value = CURSOR_THRESHOLD ( 0 ) | WINDOW_A_THRESHOLD ( 1 ) |
WINDOW_B_THRESHOLD ( 1 ) | WINDOW_C_THRESHOLD ( 1 ) ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER ) ;
value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_ENABLE ) ;
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_MASK ) ;
if ( dc - > soc - > supports_border_color )
tegra_dc_writel ( dc , 0 , DC_DISP_BORDER_COLOR ) ;
/* apply PLL and pixel clock changes */
2014-12-19 15:09:16 +01:00
tegra_dc_commit_state ( dc , state ) ;
2012-11-15 21:28:22 +00:00
/* program display mode */
tegra_dc_set_timings ( dc , mode ) ;
2013-12-12 11:03:59 +01:00
/* interlacing isn't supported yet, so disable it */
if ( dc - > soc - > supports_interlacing ) {
value = tegra_dc_readl ( dc , DC_DISP_INTERLACE_CONTROL ) ;
value & = ~ INTERLACE_ENABLE ;
tegra_dc_writel ( dc , value , DC_DISP_INTERLACE_CONTROL ) ;
}
2014-12-08 16:32:47 +01:00
value = tegra_dc_readl ( dc , DC_CMD_DISPLAY_COMMAND ) ;
value & = ~ DISP_CTRL_MODE_MASK ;
value | = DISP_CTRL_MODE_C_DISPLAY ;
tegra_dc_writel ( dc , value , DC_CMD_DISPLAY_COMMAND ) ;
value = tegra_dc_readl ( dc , DC_CMD_DISPLAY_POWER_CONTROL ) ;
value | = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE ;
tegra_dc_writel ( dc , value , DC_CMD_DISPLAY_POWER_CONTROL ) ;
tegra_dc_commit ( dc ) ;
2012-11-15 21:28:22 +00:00
2014-10-08 14:48:51 +02:00
drm_crtc_vblank_on ( crtc ) ;
2012-11-15 21:28:22 +00:00
}
2014-11-24 16:27:13 +01:00
static int tegra_crtc_atomic_check ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
return 0 ;
}
2015-07-21 13:28:58 +02:00
static void tegra_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2014-11-24 16:27:13 +01:00
{
2014-11-24 17:41:23 +01:00
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
if ( crtc - > state - > event ) {
crtc - > state - > event - > pipe = drm_crtc_index ( crtc ) ;
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
dc - > event = crtc - > state - > event ;
crtc - > state - > event = NULL ;
}
2014-11-24 16:27:13 +01:00
}
2015-07-21 13:28:58 +02:00
static void tegra_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2014-11-24 16:27:13 +01:00
{
2014-11-26 12:28:39 +01:00
struct tegra_dc_state * state = to_dc_state ( crtc - > state ) ;
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
tegra_dc_writel ( dc , state - > planes < < 8 , DC_CMD_STATE_CONTROL ) ;
tegra_dc_writel ( dc , state - > planes , DC_CMD_STATE_CONTROL ) ;
2014-11-24 16:27:13 +01:00
}
2012-11-15 21:28:22 +00:00
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
2014-11-24 16:27:13 +01:00
. atomic_check = tegra_crtc_atomic_check ,
. atomic_begin = tegra_crtc_atomic_begin ,
. atomic_flush = tegra_crtc_atomic_flush ,
2017-06-30 12:36:44 +03:00
. atomic_enable = tegra_crtc_atomic_enable ,
2017-06-30 12:36:45 +03:00
. atomic_disable = tegra_crtc_atomic_disable ,
2012-11-15 21:28:22 +00:00
} ;
2012-11-28 11:45:47 +01:00
static irqreturn_t tegra_dc_irq ( int irq , void * data )
2012-11-15 21:28:22 +00:00
{
struct tegra_dc * dc = data ;
unsigned long status ;
status = tegra_dc_readl ( dc , DC_CMD_INT_STATUS ) ;
tegra_dc_writel ( dc , status , DC_CMD_INT_STATUS ) ;
if ( status & FRAME_END_INT ) {
/*
dev_dbg ( dc - > dev , " %s(): frame end \n " , __func__ ) ;
*/
2015-07-28 21:27:05 +02:00
dc - > stats . frames + + ;
2012-11-15 21:28:22 +00:00
}
if ( status & VBLANK_INT ) {
/*
dev_dbg ( dc - > dev , " %s(): vertical blank \n " , __func__ ) ;
*/
2014-12-16 16:03:13 +01:00
drm_crtc_handle_vblank ( & dc - > base ) ;
2012-11-28 12:00:18 +01:00
tegra_dc_finish_page_flip ( dc ) ;
2015-07-28 21:27:05 +02:00
dc - > stats . vblank + + ;
2012-11-15 21:28:22 +00:00
}
if ( status & ( WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT ) ) {
/*
dev_dbg ( dc - > dev , " %s(): underflow \n " , __func__ ) ;
*/
2015-07-28 21:27:05 +02:00
dc - > stats . underflow + + ;
}
if ( status & ( WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT ) ) {
/*
dev_dbg ( dc - > dev , " %s(): overflow \n " , __func__ ) ;
*/
dc - > stats . overflow + + ;
2012-11-15 21:28:22 +00:00
}
return IRQ_HANDLED ;
}
static int tegra_dc_show_regs ( struct seq_file * s , void * data )
{
struct drm_info_node * node = s - > private ;
struct tegra_dc * dc = node - > info_ent - > data ;
2015-08-03 13:16:26 +02:00
int err = 0 ;
2017-03-22 22:50:46 +01:00
drm_modeset_lock ( & dc - > base . mutex , NULL ) ;
2015-08-03 13:16:26 +02:00
if ( ! dc - > base . state - > active ) {
err = - EBUSY ;
goto unlock ;
}
2012-11-15 21:28:22 +00:00
# define DUMP_REG(name) \
2014-10-21 13:48:48 +02:00
seq_printf ( s , " %-40s %#05x %08x \n " , # name , name , \
2012-11-15 21:28:22 +00:00
tegra_dc_readl ( dc , name ) )
DUMP_REG ( DC_CMD_GENERAL_INCR_SYNCPT ) ;
DUMP_REG ( DC_CMD_GENERAL_INCR_SYNCPT_CNTRL ) ;
DUMP_REG ( DC_CMD_GENERAL_INCR_SYNCPT_ERROR ) ;
DUMP_REG ( DC_CMD_WIN_A_INCR_SYNCPT ) ;
DUMP_REG ( DC_CMD_WIN_A_INCR_SYNCPT_CNTRL ) ;
DUMP_REG ( DC_CMD_WIN_A_INCR_SYNCPT_ERROR ) ;
DUMP_REG ( DC_CMD_WIN_B_INCR_SYNCPT ) ;
DUMP_REG ( DC_CMD_WIN_B_INCR_SYNCPT_CNTRL ) ;
DUMP_REG ( DC_CMD_WIN_B_INCR_SYNCPT_ERROR ) ;
DUMP_REG ( DC_CMD_WIN_C_INCR_SYNCPT ) ;
DUMP_REG ( DC_CMD_WIN_C_INCR_SYNCPT_CNTRL ) ;
DUMP_REG ( DC_CMD_WIN_C_INCR_SYNCPT_ERROR ) ;
DUMP_REG ( DC_CMD_CONT_SYNCPT_VSYNC ) ;
DUMP_REG ( DC_CMD_DISPLAY_COMMAND_OPTION0 ) ;
DUMP_REG ( DC_CMD_DISPLAY_COMMAND ) ;
DUMP_REG ( DC_CMD_SIGNAL_RAISE ) ;
DUMP_REG ( DC_CMD_DISPLAY_POWER_CONTROL ) ;
DUMP_REG ( DC_CMD_INT_STATUS ) ;
DUMP_REG ( DC_CMD_INT_MASK ) ;
DUMP_REG ( DC_CMD_INT_ENABLE ) ;
DUMP_REG ( DC_CMD_INT_TYPE ) ;
DUMP_REG ( DC_CMD_INT_POLARITY ) ;
DUMP_REG ( DC_CMD_SIGNAL_RAISE1 ) ;
DUMP_REG ( DC_CMD_SIGNAL_RAISE2 ) ;
DUMP_REG ( DC_CMD_SIGNAL_RAISE3 ) ;
DUMP_REG ( DC_CMD_STATE_ACCESS ) ;
DUMP_REG ( DC_CMD_STATE_CONTROL ) ;
DUMP_REG ( DC_CMD_DISPLAY_WINDOW_HEADER ) ;
DUMP_REG ( DC_CMD_REG_ACT_CONTROL ) ;
DUMP_REG ( DC_COM_CRC_CONTROL ) ;
DUMP_REG ( DC_COM_CRC_CHECKSUM ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_ENABLE ( 0 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_ENABLE ( 1 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_ENABLE ( 2 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_ENABLE ( 3 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_POLARITY ( 0 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_POLARITY ( 1 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_POLARITY ( 2 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_POLARITY ( 3 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_DATA ( 0 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_DATA ( 1 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_DATA ( 2 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_DATA ( 3 ) ) ;
DUMP_REG ( DC_COM_PIN_INPUT_ENABLE ( 0 ) ) ;
DUMP_REG ( DC_COM_PIN_INPUT_ENABLE ( 1 ) ) ;
DUMP_REG ( DC_COM_PIN_INPUT_ENABLE ( 2 ) ) ;
DUMP_REG ( DC_COM_PIN_INPUT_ENABLE ( 3 ) ) ;
DUMP_REG ( DC_COM_PIN_INPUT_DATA ( 0 ) ) ;
DUMP_REG ( DC_COM_PIN_INPUT_DATA ( 1 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 0 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 1 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 2 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 3 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 4 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 5 ) ) ;
DUMP_REG ( DC_COM_PIN_OUTPUT_SELECT ( 6 ) ) ;
DUMP_REG ( DC_COM_PIN_MISC_CONTROL ) ;
DUMP_REG ( DC_COM_PIN_PM0_CONTROL ) ;
DUMP_REG ( DC_COM_PIN_PM0_DUTY_CYCLE ) ;
DUMP_REG ( DC_COM_PIN_PM1_CONTROL ) ;
DUMP_REG ( DC_COM_PIN_PM1_DUTY_CYCLE ) ;
DUMP_REG ( DC_COM_SPI_CONTROL ) ;
DUMP_REG ( DC_COM_SPI_START_BYTE ) ;
DUMP_REG ( DC_COM_HSPI_WRITE_DATA_AB ) ;
DUMP_REG ( DC_COM_HSPI_WRITE_DATA_CD ) ;
DUMP_REG ( DC_COM_HSPI_CS_DC ) ;
DUMP_REG ( DC_COM_SCRATCH_REGISTER_A ) ;
DUMP_REG ( DC_COM_SCRATCH_REGISTER_B ) ;
DUMP_REG ( DC_COM_GPIO_CTRL ) ;
DUMP_REG ( DC_COM_GPIO_DEBOUNCE_COUNTER ) ;
DUMP_REG ( DC_COM_CRC_CHECKSUM_LATCHED ) ;
DUMP_REG ( DC_DISP_DISP_SIGNAL_OPTIONS0 ) ;
DUMP_REG ( DC_DISP_DISP_SIGNAL_OPTIONS1 ) ;
DUMP_REG ( DC_DISP_DISP_WIN_OPTIONS ) ;
DUMP_REG ( DC_DISP_DISP_MEM_HIGH_PRIORITY ) ;
DUMP_REG ( DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER ) ;
DUMP_REG ( DC_DISP_DISP_TIMING_OPTIONS ) ;
DUMP_REG ( DC_DISP_REF_TO_SYNC ) ;
DUMP_REG ( DC_DISP_SYNC_WIDTH ) ;
DUMP_REG ( DC_DISP_BACK_PORCH ) ;
DUMP_REG ( DC_DISP_ACTIVE ) ;
DUMP_REG ( DC_DISP_FRONT_PORCH ) ;
DUMP_REG ( DC_DISP_H_PULSE0_CONTROL ) ;
DUMP_REG ( DC_DISP_H_PULSE0_POSITION_A ) ;
DUMP_REG ( DC_DISP_H_PULSE0_POSITION_B ) ;
DUMP_REG ( DC_DISP_H_PULSE0_POSITION_C ) ;
DUMP_REG ( DC_DISP_H_PULSE0_POSITION_D ) ;
DUMP_REG ( DC_DISP_H_PULSE1_CONTROL ) ;
DUMP_REG ( DC_DISP_H_PULSE1_POSITION_A ) ;
DUMP_REG ( DC_DISP_H_PULSE1_POSITION_B ) ;
DUMP_REG ( DC_DISP_H_PULSE1_POSITION_C ) ;
DUMP_REG ( DC_DISP_H_PULSE1_POSITION_D ) ;
DUMP_REG ( DC_DISP_H_PULSE2_CONTROL ) ;
DUMP_REG ( DC_DISP_H_PULSE2_POSITION_A ) ;
DUMP_REG ( DC_DISP_H_PULSE2_POSITION_B ) ;
DUMP_REG ( DC_DISP_H_PULSE2_POSITION_C ) ;
DUMP_REG ( DC_DISP_H_PULSE2_POSITION_D ) ;
DUMP_REG ( DC_DISP_V_PULSE0_CONTROL ) ;
DUMP_REG ( DC_DISP_V_PULSE0_POSITION_A ) ;
DUMP_REG ( DC_DISP_V_PULSE0_POSITION_B ) ;
DUMP_REG ( DC_DISP_V_PULSE0_POSITION_C ) ;
DUMP_REG ( DC_DISP_V_PULSE1_CONTROL ) ;
DUMP_REG ( DC_DISP_V_PULSE1_POSITION_A ) ;
DUMP_REG ( DC_DISP_V_PULSE1_POSITION_B ) ;
DUMP_REG ( DC_DISP_V_PULSE1_POSITION_C ) ;
DUMP_REG ( DC_DISP_V_PULSE2_CONTROL ) ;
DUMP_REG ( DC_DISP_V_PULSE2_POSITION_A ) ;
DUMP_REG ( DC_DISP_V_PULSE3_CONTROL ) ;
DUMP_REG ( DC_DISP_V_PULSE3_POSITION_A ) ;
DUMP_REG ( DC_DISP_M0_CONTROL ) ;
DUMP_REG ( DC_DISP_M1_CONTROL ) ;
DUMP_REG ( DC_DISP_DI_CONTROL ) ;
DUMP_REG ( DC_DISP_PP_CONTROL ) ;
DUMP_REG ( DC_DISP_PP_SELECT_A ) ;
DUMP_REG ( DC_DISP_PP_SELECT_B ) ;
DUMP_REG ( DC_DISP_PP_SELECT_C ) ;
DUMP_REG ( DC_DISP_PP_SELECT_D ) ;
DUMP_REG ( DC_DISP_DISP_CLOCK_CONTROL ) ;
DUMP_REG ( DC_DISP_DISP_INTERFACE_CONTROL ) ;
DUMP_REG ( DC_DISP_DISP_COLOR_CONTROL ) ;
DUMP_REG ( DC_DISP_SHIFT_CLOCK_OPTIONS ) ;
DUMP_REG ( DC_DISP_DATA_ENABLE_OPTIONS ) ;
DUMP_REG ( DC_DISP_SERIAL_INTERFACE_OPTIONS ) ;
DUMP_REG ( DC_DISP_LCD_SPI_OPTIONS ) ;
DUMP_REG ( DC_DISP_BORDER_COLOR ) ;
DUMP_REG ( DC_DISP_COLOR_KEY0_LOWER ) ;
DUMP_REG ( DC_DISP_COLOR_KEY0_UPPER ) ;
DUMP_REG ( DC_DISP_COLOR_KEY1_LOWER ) ;
DUMP_REG ( DC_DISP_COLOR_KEY1_UPPER ) ;
DUMP_REG ( DC_DISP_CURSOR_FOREGROUND ) ;
DUMP_REG ( DC_DISP_CURSOR_BACKGROUND ) ;
DUMP_REG ( DC_DISP_CURSOR_START_ADDR ) ;
DUMP_REG ( DC_DISP_CURSOR_START_ADDR_NS ) ;
DUMP_REG ( DC_DISP_CURSOR_POSITION ) ;
DUMP_REG ( DC_DISP_CURSOR_POSITION_NS ) ;
DUMP_REG ( DC_DISP_INIT_SEQ_CONTROL ) ;
DUMP_REG ( DC_DISP_SPI_INIT_SEQ_DATA_A ) ;
DUMP_REG ( DC_DISP_SPI_INIT_SEQ_DATA_B ) ;
DUMP_REG ( DC_DISP_SPI_INIT_SEQ_DATA_C ) ;
DUMP_REG ( DC_DISP_SPI_INIT_SEQ_DATA_D ) ;
DUMP_REG ( DC_DISP_DC_MCCIF_FIFOCTRL ) ;
DUMP_REG ( DC_DISP_MCCIF_DISPLAY0A_HYST ) ;
DUMP_REG ( DC_DISP_MCCIF_DISPLAY0B_HYST ) ;
DUMP_REG ( DC_DISP_MCCIF_DISPLAY1A_HYST ) ;
DUMP_REG ( DC_DISP_MCCIF_DISPLAY1B_HYST ) ;
DUMP_REG ( DC_DISP_DAC_CRT_CTRL ) ;
DUMP_REG ( DC_DISP_DISP_MISC_CONTROL ) ;
DUMP_REG ( DC_DISP_SD_CONTROL ) ;
DUMP_REG ( DC_DISP_SD_CSC_COEFF ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 0 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 1 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 2 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 3 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 4 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 5 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 6 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 7 ) ) ;
DUMP_REG ( DC_DISP_SD_LUT ( 8 ) ) ;
DUMP_REG ( DC_DISP_SD_FLICKER_CONTROL ) ;
DUMP_REG ( DC_DISP_DC_PIXEL_COUNT ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 0 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 1 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 2 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 3 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 4 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 5 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 6 ) ) ;
DUMP_REG ( DC_DISP_SD_HISTOGRAM ( 7 ) ) ;
DUMP_REG ( DC_DISP_SD_BL_TF ( 0 ) ) ;
DUMP_REG ( DC_DISP_SD_BL_TF ( 1 ) ) ;
DUMP_REG ( DC_DISP_SD_BL_TF ( 2 ) ) ;
DUMP_REG ( DC_DISP_SD_BL_TF ( 3 ) ) ;
DUMP_REG ( DC_DISP_SD_BL_CONTROL ) ;
DUMP_REG ( DC_DISP_SD_HW_K_VALUES ) ;
DUMP_REG ( DC_DISP_SD_MAN_K_VALUES ) ;
2013-12-20 13:58:33 +01:00
DUMP_REG ( DC_DISP_CURSOR_START_ADDR_HI ) ;
DUMP_REG ( DC_DISP_BLEND_CURSOR_CONTROL ) ;
2012-11-15 21:28:22 +00:00
DUMP_REG ( DC_WIN_WIN_OPTIONS ) ;
DUMP_REG ( DC_WIN_BYTE_SWAP ) ;
DUMP_REG ( DC_WIN_BUFFER_CONTROL ) ;
DUMP_REG ( DC_WIN_COLOR_DEPTH ) ;
DUMP_REG ( DC_WIN_POSITION ) ;
DUMP_REG ( DC_WIN_SIZE ) ;
DUMP_REG ( DC_WIN_PRESCALED_SIZE ) ;
DUMP_REG ( DC_WIN_H_INITIAL_DDA ) ;
DUMP_REG ( DC_WIN_V_INITIAL_DDA ) ;
DUMP_REG ( DC_WIN_DDA_INC ) ;
DUMP_REG ( DC_WIN_LINE_STRIDE ) ;
DUMP_REG ( DC_WIN_BUF_STRIDE ) ;
DUMP_REG ( DC_WIN_UV_BUF_STRIDE ) ;
DUMP_REG ( DC_WIN_BUFFER_ADDR_MODE ) ;
DUMP_REG ( DC_WIN_DV_CONTROL ) ;
DUMP_REG ( DC_WIN_BLEND_NOKEY ) ;
DUMP_REG ( DC_WIN_BLEND_1WIN ) ;
DUMP_REG ( DC_WIN_BLEND_2WIN_X ) ;
DUMP_REG ( DC_WIN_BLEND_2WIN_Y ) ;
2012-11-04 21:47:13 +01:00
DUMP_REG ( DC_WIN_BLEND_3WIN_XY ) ;
2012-11-15 21:28:22 +00:00
DUMP_REG ( DC_WIN_HP_FETCH_CONTROL ) ;
DUMP_REG ( DC_WINBUF_START_ADDR ) ;
DUMP_REG ( DC_WINBUF_START_ADDR_NS ) ;
DUMP_REG ( DC_WINBUF_START_ADDR_U ) ;
DUMP_REG ( DC_WINBUF_START_ADDR_U_NS ) ;
DUMP_REG ( DC_WINBUF_START_ADDR_V ) ;
DUMP_REG ( DC_WINBUF_START_ADDR_V_NS ) ;
DUMP_REG ( DC_WINBUF_ADDR_H_OFFSET ) ;
DUMP_REG ( DC_WINBUF_ADDR_H_OFFSET_NS ) ;
DUMP_REG ( DC_WINBUF_ADDR_V_OFFSET ) ;
DUMP_REG ( DC_WINBUF_ADDR_V_OFFSET_NS ) ;
DUMP_REG ( DC_WINBUF_UFLOW_STATUS ) ;
DUMP_REG ( DC_WINBUF_AD_UFLOW_STATUS ) ;
DUMP_REG ( DC_WINBUF_BD_UFLOW_STATUS ) ;
DUMP_REG ( DC_WINBUF_CD_UFLOW_STATUS ) ;
# undef DUMP_REG
2015-08-03 13:16:26 +02:00
unlock :
2017-03-22 22:50:46 +01:00
drm_modeset_unlock ( & dc - > base . mutex ) ;
2015-08-03 13:16:26 +02:00
return err ;
2012-11-15 21:28:22 +00:00
}
2015-04-01 14:59:40 +02:00
static int tegra_dc_show_crc ( struct seq_file * s , void * data )
{
struct drm_info_node * node = s - > private ;
struct tegra_dc * dc = node - > info_ent - > data ;
2015-08-03 13:16:26 +02:00
int err = 0 ;
2015-04-01 14:59:40 +02:00
u32 value ;
2017-03-22 22:50:46 +01:00
drm_modeset_lock ( & dc - > base . mutex , NULL ) ;
2015-08-03 13:16:26 +02:00
if ( ! dc - > base . state - > active ) {
err = - EBUSY ;
goto unlock ;
}
2015-04-01 14:59:40 +02:00
value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE ;
tegra_dc_writel ( dc , value , DC_COM_CRC_CONTROL ) ;
tegra_dc_commit ( dc ) ;
drm_crtc_wait_one_vblank ( & dc - > base ) ;
drm_crtc_wait_one_vblank ( & dc - > base ) ;
value = tegra_dc_readl ( dc , DC_COM_CRC_CHECKSUM ) ;
seq_printf ( s , " %08x \n " , value ) ;
tegra_dc_writel ( dc , 0 , DC_COM_CRC_CONTROL ) ;
2015-08-03 13:16:26 +02:00
unlock :
2017-03-22 22:50:46 +01:00
drm_modeset_unlock ( & dc - > base . mutex ) ;
2015-08-03 13:16:26 +02:00
return err ;
2015-04-01 14:59:40 +02:00
}
2015-07-28 21:27:05 +02:00
static int tegra_dc_show_stats ( struct seq_file * s , void * data )
{
struct drm_info_node * node = s - > private ;
struct tegra_dc * dc = node - > info_ent - > data ;
seq_printf ( s , " frames: %lu \n " , dc - > stats . frames ) ;
seq_printf ( s , " vblank: %lu \n " , dc - > stats . vblank ) ;
seq_printf ( s , " underflow: %lu \n " , dc - > stats . underflow ) ;
seq_printf ( s , " overflow: %lu \n " , dc - > stats . overflow ) ;
2012-11-15 21:28:22 +00:00
return 0 ;
}
static struct drm_info_list debugfs_files [ ] = {
{ " regs " , tegra_dc_show_regs , 0 , NULL } ,
2015-04-01 14:59:40 +02:00
{ " crc " , tegra_dc_show_crc , 0 , NULL } ,
2015-07-28 21:27:05 +02:00
{ " stats " , tegra_dc_show_stats , 0 , NULL } ,
2012-11-15 21:28:22 +00:00
} ;
static int tegra_dc_debugfs_init ( struct tegra_dc * dc , struct drm_minor * minor )
{
unsigned int i ;
char * name ;
int err ;
name = kasprintf ( GFP_KERNEL , " dc.%d " , dc - > pipe ) ;
dc - > debugfs = debugfs_create_dir ( name , minor - > debugfs_root ) ;
kfree ( name ) ;
if ( ! dc - > debugfs )
return - ENOMEM ;
dc - > debugfs_files = kmemdup ( debugfs_files , sizeof ( debugfs_files ) ,
GFP_KERNEL ) ;
if ( ! dc - > debugfs_files ) {
err = - ENOMEM ;
goto remove ;
}
for ( i = 0 ; i < ARRAY_SIZE ( debugfs_files ) ; i + + )
dc - > debugfs_files [ i ] . data = dc ;
err = drm_debugfs_create_files ( dc - > debugfs_files ,
ARRAY_SIZE ( debugfs_files ) ,
dc - > debugfs , minor ) ;
if ( err < 0 )
goto free ;
dc - > minor = minor ;
return 0 ;
free :
kfree ( dc - > debugfs_files ) ;
dc - > debugfs_files = NULL ;
remove :
debugfs_remove ( dc - > debugfs ) ;
dc - > debugfs = NULL ;
return err ;
}
static int tegra_dc_debugfs_exit ( struct tegra_dc * dc )
{
drm_debugfs_remove_files ( dc - > debugfs_files , ARRAY_SIZE ( debugfs_files ) ,
dc - > minor ) ;
dc - > minor = NULL ;
kfree ( dc - > debugfs_files ) ;
dc - > debugfs_files = NULL ;
debugfs_remove ( dc - > debugfs ) ;
dc - > debugfs = NULL ;
return 0 ;
}
2013-09-24 15:35:40 +02:00
static int tegra_dc_init ( struct host1x_client * client )
2012-11-15 21:28:22 +00:00
{
2014-05-22 09:57:15 +02:00
struct drm_device * drm = dev_get_drvdata ( client - > parent ) ;
2015-08-24 14:47:10 +02:00
unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED ;
2013-10-14 14:43:22 +02:00
struct tegra_dc * dc = host1x_client_to_dc ( client ) ;
2014-07-11 08:29:14 +02:00
struct tegra_drm * tegra = drm - > dev_private ;
2014-10-21 13:51:53 +02:00
struct drm_plane * primary = NULL ;
struct drm_plane * cursor = NULL ;
2012-11-15 21:28:22 +00:00
int err ;
2017-08-30 12:48:31 +02:00
dc - > syncpt = host1x_syncpt_request ( client , flags ) ;
2015-08-24 14:47:10 +02:00
if ( ! dc - > syncpt )
dev_warn ( dc - > dev , " failed to allocate syncpoint \n " ) ;
2014-06-26 21:41:53 +02:00
if ( tegra - > domain ) {
err = iommu_attach_device ( tegra - > domain , dc - > dev ) ;
if ( err < 0 ) {
dev_err ( dc - > dev , " failed to attach to domain: %d \n " ,
err ) ;
return err ;
}
dc - > domain = tegra - > domain ;
}
2014-10-21 13:51:53 +02:00
primary = tegra_dc_primary_plane_create ( drm , dc ) ;
if ( IS_ERR ( primary ) ) {
err = PTR_ERR ( primary ) ;
goto cleanup ;
}
if ( dc - > soc - > supports_cursor ) {
cursor = tegra_dc_cursor_plane_create ( drm , dc ) ;
if ( IS_ERR ( cursor ) ) {
err = PTR_ERR ( cursor ) ;
goto cleanup ;
}
}
err = drm_crtc_init_with_planes ( drm , & dc - > base , primary , cursor ,
drm: Pass 'name' to drm_crtc_init_with_planes()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
I didn't convert drm_crtc_init() since passing the varargs through
would mean either cpp macros or va_list, and I figured we don't
care about these legacy functions enough to warrant the extra pain.
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4, E5;
@@
drm_crtc_init_with_planes(E1, E2, E3, E4, E5
+ ,NULL
)
v2: Split crtc and plane changes apart
Pass NULL for no-name instead of ""
Leave drm_crtc_init() alone
v3: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670771-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:19:31 +02:00
& tegra_crtc_funcs , NULL ) ;
2014-10-21 13:51:53 +02:00
if ( err < 0 )
goto cleanup ;
2012-11-15 21:28:22 +00:00
drm_crtc_helper_add ( & dc - > base , & tegra_crtc_helper_funcs ) ;
2014-07-11 08:29:14 +02:00
/*
* Keep track of the minimum pitch alignment across all display
* controllers .
*/
if ( dc - > soc - > pitch_align > tegra - > pitch_align )
tegra - > pitch_align = dc - > soc - > pitch_align ;
2014-05-22 09:57:15 +02:00
err = tegra_dc_rgb_init ( drm , dc ) ;
2012-11-15 21:28:22 +00:00
if ( err < 0 & & err ! = - ENODEV ) {
dev_err ( dc - > dev , " failed to initialize RGB output: %d \n " , err ) ;
2014-10-21 13:51:53 +02:00
goto cleanup ;
2012-11-15 21:28:22 +00:00
}
2014-05-22 09:57:15 +02:00
err = tegra_dc_add_planes ( drm , dc ) ;
2012-11-04 21:47:13 +01:00
if ( err < 0 )
2014-10-21 13:51:53 +02:00
goto cleanup ;
2012-11-04 21:47:13 +01:00
2012-11-15 21:28:22 +00:00
if ( IS_ENABLED ( CONFIG_DEBUG_FS ) ) {
2014-05-22 09:57:15 +02:00
err = tegra_dc_debugfs_init ( dc , drm - > primary ) ;
2012-11-15 21:28:22 +00:00
if ( err < 0 )
dev_err ( dc - > dev , " debugfs setup failed: %d \n " , err ) ;
}
2012-11-28 11:45:47 +01:00
err = devm_request_irq ( dc - > dev , dc - > irq , tegra_dc_irq , 0 ,
2012-11-15 21:28:22 +00:00
dev_name ( dc - > dev ) , dc ) ;
if ( err < 0 ) {
dev_err ( dc - > dev , " failed to request IRQ#%u: %d \n " , dc - > irq ,
err ) ;
2014-10-21 13:51:53 +02:00
goto cleanup ;
2012-11-15 21:28:22 +00:00
}
return 0 ;
2014-10-21 13:51:53 +02:00
cleanup :
if ( cursor )
drm_plane_cleanup ( cursor ) ;
if ( primary )
drm_plane_cleanup ( primary ) ;
if ( tegra - > domain ) {
iommu_detach_device ( tegra - > domain , dc - > dev ) ;
dc - > domain = NULL ;
}
return err ;
2012-11-15 21:28:22 +00:00
}
2013-09-24 15:35:40 +02:00
static int tegra_dc_exit ( struct host1x_client * client )
2012-11-15 21:28:22 +00:00
{
2013-10-14 14:43:22 +02:00
struct tegra_dc * dc = host1x_client_to_dc ( client ) ;
2012-11-15 21:28:22 +00:00
int err ;
devm_free_irq ( dc - > dev , dc - > irq , dc ) ;
if ( IS_ENABLED ( CONFIG_DEBUG_FS ) ) {
err = tegra_dc_debugfs_exit ( dc ) ;
if ( err < 0 )
dev_err ( dc - > dev , " debugfs cleanup failed: %d \n " , err ) ;
}
err = tegra_dc_rgb_exit ( dc ) ;
if ( err ) {
dev_err ( dc - > dev , " failed to shutdown RGB output: %d \n " , err ) ;
return err ;
}
2014-06-26 21:41:53 +02:00
if ( dc - > domain ) {
iommu_detach_device ( dc - > domain , dc - > dev ) ;
dc - > domain = NULL ;
}
2015-08-24 14:47:10 +02:00
host1x_syncpt_free ( dc - > syncpt ) ;
2012-11-15 21:28:22 +00:00
return 0 ;
}
static const struct host1x_client_ops dc_client_ops = {
2013-09-24 15:35:40 +02:00
. init = tegra_dc_init ,
. exit = tegra_dc_exit ,
2012-11-15 21:28:22 +00:00
} ;
2013-12-12 11:03:59 +01:00
static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
2014-12-08 15:45:39 +01:00
. supports_border_color = true ,
2013-12-12 11:03:59 +01:00
. supports_interlacing = false ,
2013-12-20 13:58:33 +01:00
. supports_cursor = false ,
2014-06-03 14:48:12 +02:00
. supports_block_linear = false ,
2014-07-11 08:29:14 +02:00
. pitch_align = 8 ,
2014-07-07 15:32:53 +02:00
. has_powergate = false ,
2017-06-15 02:18:29 +03:00
. broken_reset = true ,
2013-12-12 11:03:59 +01:00
} ;
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
2014-12-08 15:45:39 +01:00
. supports_border_color = true ,
2013-12-12 11:03:59 +01:00
. supports_interlacing = false ,
2013-12-20 13:58:33 +01:00
. supports_cursor = false ,
2014-06-03 14:48:12 +02:00
. supports_block_linear = false ,
2014-07-11 08:29:14 +02:00
. pitch_align = 8 ,
2014-07-07 15:32:53 +02:00
. has_powergate = false ,
2017-06-15 02:18:29 +03:00
. broken_reset = false ,
2014-07-11 08:29:14 +02:00
} ;
static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
2014-12-08 15:45:39 +01:00
. supports_border_color = true ,
2014-07-11 08:29:14 +02:00
. supports_interlacing = false ,
. supports_cursor = false ,
. supports_block_linear = false ,
. pitch_align = 64 ,
2014-07-07 15:32:53 +02:00
. has_powergate = true ,
2017-06-15 02:18:29 +03:00
. broken_reset = false ,
2013-12-12 11:03:59 +01:00
} ;
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
2014-12-08 15:45:39 +01:00
. supports_border_color = false ,
2013-12-12 11:03:59 +01:00
. supports_interlacing = true ,
2013-12-20 13:58:33 +01:00
. supports_cursor = true ,
2014-06-03 14:48:12 +02:00
. supports_block_linear = true ,
2014-07-11 08:29:14 +02:00
. pitch_align = 64 ,
2014-07-07 15:32:53 +02:00
. has_powergate = true ,
2017-06-15 02:18:29 +03:00
. broken_reset = false ,
2013-12-12 11:03:59 +01:00
} ;
2015-03-27 10:31:58 +01:00
static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
. supports_border_color = false ,
. supports_interlacing = true ,
. supports_cursor = true ,
. supports_block_linear = true ,
. pitch_align = 64 ,
. has_powergate = true ,
2017-06-15 02:18:29 +03:00
. broken_reset = false ,
2015-03-27 10:31:58 +01:00
} ;
2013-12-12 11:03:59 +01:00
static const struct of_device_id tegra_dc_of_match [ ] = {
{
2015-03-27 10:31:58 +01:00
. compatible = " nvidia,tegra210-dc " ,
. data = & tegra210_dc_soc_info ,
} , {
2013-12-12 11:03:59 +01:00
. compatible = " nvidia,tegra124-dc " ,
. data = & tegra124_dc_soc_info ,
2014-07-07 15:32:53 +02:00
} , {
. compatible = " nvidia,tegra114-dc " ,
. data = & tegra114_dc_soc_info ,
2013-12-12 11:03:59 +01:00
} , {
. compatible = " nvidia,tegra30-dc " ,
. data = & tegra30_dc_soc_info ,
} , {
. compatible = " nvidia,tegra20-dc " ,
. data = & tegra20_dc_soc_info ,
} , {
/* sentinel */
}
} ;
2014-06-18 16:21:55 -06:00
MODULE_DEVICE_TABLE ( of , tegra_dc_of_match ) ;
2013-12-12 11:03:59 +01:00
2014-01-09 17:08:36 +01:00
static int tegra_dc_parse_dt ( struct tegra_dc * dc )
{
struct device_node * np ;
u32 value = 0 ;
int err ;
err = of_property_read_u32 ( dc - > dev - > of_node , " nvidia,head " , & value ) ;
if ( err < 0 ) {
dev_err ( dc - > dev , " missing \" nvidia,head \" property \n " ) ;
/*
* If the nvidia , head property isn ' t present , try to find the
* correct head number by looking up the position of this
* display controller ' s node within the device tree . Assuming
* that the nodes are ordered properly in the DTS file and
* that the translation into a flattened device tree blob
* preserves that ordering this will actually yield the right
* head number .
*
* If those assumptions don ' t hold , this will still work for
* cases where only a single display controller is used .
*/
for_each_matching_node ( np , tegra_dc_of_match ) {
2015-10-24 16:42:31 +02:00
if ( np = = dc - > dev - > of_node ) {
of_node_put ( np ) ;
2014-01-09 17:08:36 +01:00
break ;
2015-10-24 16:42:31 +02:00
}
2014-01-09 17:08:36 +01:00
value + + ;
}
}
dc - > pipe = value ;
return 0 ;
}
2012-11-15 21:28:22 +00:00
static int tegra_dc_probe ( struct platform_device * pdev )
{
struct resource * regs ;
struct tegra_dc * dc ;
int err ;
dc = devm_kzalloc ( & pdev - > dev , sizeof ( * dc ) , GFP_KERNEL ) ;
if ( ! dc )
return - ENOMEM ;
2017-08-21 16:35:17 +02:00
dc - > soc = of_device_get_match_data ( & pdev - > dev ) ;
2013-12-12 11:03:59 +01:00
2012-11-28 11:45:47 +01:00
spin_lock_init ( & dc - > lock ) ;
2012-11-15 21:28:22 +00:00
INIT_LIST_HEAD ( & dc - > list ) ;
dc - > dev = & pdev - > dev ;
2014-01-09 17:08:36 +01:00
err = tegra_dc_parse_dt ( dc ) ;
if ( err < 0 )
return err ;
2012-11-15 21:28:22 +00:00
dc - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( dc - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
return PTR_ERR ( dc - > clk ) ;
}
2013-11-06 16:20:54 -07:00
dc - > rst = devm_reset_control_get ( & pdev - > dev , " dc " ) ;
if ( IS_ERR ( dc - > rst ) ) {
dev_err ( & pdev - > dev , " failed to get reset \n " ) ;
return PTR_ERR ( dc - > rst ) ;
}
2017-08-30 17:41:00 +02:00
/* assert reset and disable clock */
if ( ! dc - > soc - > broken_reset ) {
err = clk_prepare_enable ( dc - > clk ) ;
if ( err < 0 )
return err ;
usleep_range ( 2000 , 4000 ) ;
err = reset_control_assert ( dc - > rst ) ;
if ( err < 0 )
return err ;
usleep_range ( 2000 , 4000 ) ;
clk_disable_unprepare ( dc - > clk ) ;
}
2015-08-03 13:20:49 +02:00
2014-07-07 15:32:53 +02:00
if ( dc - > soc - > has_powergate ) {
if ( dc - > pipe = = 0 )
dc - > powergate = TEGRA_POWERGATE_DIS ;
else
dc - > powergate = TEGRA_POWERGATE_DISB ;
2015-08-03 13:20:49 +02:00
tegra_powergate_power_off ( dc - > powergate ) ;
2014-07-07 15:32:53 +02:00
}
2012-11-15 21:28:22 +00:00
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 11:09:02 +01:00
dc - > regs = devm_ioremap_resource ( & pdev - > dev , regs ) ;
if ( IS_ERR ( dc - > regs ) )
return PTR_ERR ( dc - > regs ) ;
2012-11-15 21:28:22 +00:00
dc - > irq = platform_get_irq ( pdev , 0 ) ;
if ( dc - > irq < 0 ) {
dev_err ( & pdev - > dev , " failed to get IRQ \n " ) ;
return - ENXIO ;
}
err = tegra_dc_rgb_probe ( dc ) ;
if ( err < 0 & & err ! = - ENODEV ) {
dev_err ( & pdev - > dev , " failed to probe RGB output: %d \n " , err ) ;
return err ;
}
2015-08-03 13:20:49 +02:00
platform_set_drvdata ( pdev , dc ) ;
pm_runtime_enable ( & pdev - > dev ) ;
INIT_LIST_HEAD ( & dc - > client . list ) ;
dc - > client . ops = & dc_client_ops ;
dc - > client . dev = & pdev - > dev ;
2013-10-14 14:43:22 +02:00
err = host1x_client_register ( & dc - > client ) ;
2012-11-15 21:28:22 +00:00
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to register host1x client: %d \n " ,
err ) ;
return err ;
}
return 0 ;
}
static int tegra_dc_remove ( struct platform_device * pdev )
{
struct tegra_dc * dc = platform_get_drvdata ( pdev ) ;
int err ;
2013-10-14 14:43:22 +02:00
err = host1x_client_unregister ( & dc - > client ) ;
2012-11-15 21:28:22 +00:00
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to unregister host1x client: %d \n " ,
err ) ;
return err ;
}
2013-10-14 14:26:42 +02:00
err = tegra_dc_rgb_remove ( dc ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to remove RGB output: %d \n " , err ) ;
return err ;
}
2015-08-03 13:20:49 +02:00
pm_runtime_disable ( & pdev - > dev ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int tegra_dc_suspend ( struct device * dev )
{
struct tegra_dc * dc = dev_get_drvdata ( dev ) ;
int err ;
2017-06-15 02:18:29 +03:00
if ( ! dc - > soc - > broken_reset ) {
err = reset_control_assert ( dc - > rst ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to assert reset: %d \n " , err ) ;
return err ;
}
2015-08-03 13:20:49 +02:00
}
2014-07-07 15:32:53 +02:00
if ( dc - > soc - > has_powergate )
tegra_powergate_power_off ( dc - > powergate ) ;
2012-11-15 21:28:22 +00:00
clk_disable_unprepare ( dc - > clk ) ;
return 0 ;
}
2015-08-03 13:20:49 +02:00
static int tegra_dc_resume ( struct device * dev )
{
struct tegra_dc * dc = dev_get_drvdata ( dev ) ;
int err ;
if ( dc - > soc - > has_powergate ) {
err = tegra_powergate_sequence_power_up ( dc - > powergate , dc - > clk ,
dc - > rst ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to power partition: %d \n " , err ) ;
return err ;
}
} else {
err = clk_prepare_enable ( dc - > clk ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to enable clock: %d \n " , err ) ;
return err ;
}
2017-06-15 02:18:29 +03:00
if ( ! dc - > soc - > broken_reset ) {
err = reset_control_deassert ( dc - > rst ) ;
if ( err < 0 ) {
dev_err ( dev ,
" failed to deassert reset: %d \n " , err ) ;
return err ;
}
2015-08-03 13:20:49 +02:00
}
}
return 0 ;
}
# endif
static const struct dev_pm_ops tegra_dc_pm_ops = {
SET_RUNTIME_PM_OPS ( tegra_dc_suspend , tegra_dc_resume , NULL )
} ;
2012-11-15 21:28:22 +00:00
struct platform_driver tegra_dc_driver = {
. driver = {
. name = " tegra-dc " ,
. of_match_table = tegra_dc_of_match ,
2015-08-03 13:20:49 +02:00
. pm = & tegra_dc_pm_ops ,
2012-11-15 21:28:22 +00:00
} ,
. probe = tegra_dc_probe ,
. remove = tegra_dc_remove ,
} ;