2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2012-11-15 21:28:22 +00:00
/*
* Copyright ( C ) 2012 Avionic Design GmbH
* Copyright ( C ) 2012 NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/clk.h>
2013-09-24 16:32:47 +02:00
# include <linux/debugfs.h>
2019-08-04 11:41:30 +02:00
# include <linux/delay.h>
2014-06-26 21:41:53 +02:00
# include <linux/iommu.h>
2019-08-04 11:41:30 +02:00
# include <linux/module.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>
2019-08-04 11:41:30 +02:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_debugfs.h>
# include <drm/drm_fourcc.h>
# include <drm/drm_plane_helper.h>
# include <drm/drm_vblank.h>
2013-03-22 16:34:08 +02:00
# include "dc.h"
# include "drm.h"
# include "gem.h"
2017-08-30 17:42:54 +02:00
# include "hub.h"
2017-11-10 15:27:25 +01:00
# include "plane.h"
2012-11-15 21:28:22 +00:00
2019-03-01 13:56:24 +01:00
static void tegra_crtc_atomic_destroy_state ( struct drm_crtc * crtc ,
struct drm_crtc_state * state ) ;
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 ;
}
2017-12-14 13:37:53 +01:00
/* Reads the active copy of a register. */
2014-12-08 16:03:53 +01:00
static u32 tegra_dc_readl_active ( struct tegra_dc * dc , unsigned long offset )
{
u32 value ;
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 ) ;
return value ;
}
2017-12-14 13:37:53 +01:00
static inline unsigned int tegra_plane_offset ( struct tegra_plane * plane ,
unsigned int offset )
{
if ( offset > = 0x500 & & offset < = 0x638 ) {
offset = 0x000 + ( offset - 0x500 ) ;
return plane - > offset + offset ;
}
if ( offset > = 0x700 & & offset < = 0x719 ) {
offset = 0x180 + ( offset - 0x700 ) ;
return plane - > offset + offset ;
}
if ( offset > = 0x800 & & offset < = 0x839 ) {
offset = 0x1c0 + ( offset - 0x800 ) ;
return plane - > offset + offset ;
}
dev_WARN ( plane - > dc - > dev , " invalid offset: %x \n " , offset ) ;
return plane - > offset + offset ;
}
static inline u32 tegra_plane_readl ( struct tegra_plane * plane ,
unsigned int offset )
{
return tegra_dc_readl ( plane - > dc , tegra_plane_offset ( plane , offset ) ) ;
}
static inline void tegra_plane_writel ( struct tegra_plane * plane , u32 value ,
unsigned int offset )
{
tegra_dc_writel ( plane - > dc , value , tegra_plane_offset ( plane , offset ) ) ;
}
2017-10-12 19:12:57 +02:00
bool tegra_dc_has_output ( struct tegra_dc * dc , struct device * dev )
{
struct device_node * np = dc - > dev - > of_node ;
struct of_phandle_iterator it ;
int err ;
of_for_each_phandle ( & it , err , np , " nvidia,outputs " , NULL , 0 )
if ( it . node = = dev - > of_node )
return true ;
return false ;
}
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-03-14 09:54:58 +01:00
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 ) ;
}
2017-12-14 13:46:20 +01:00
static void tegra_plane_setup_blending_legacy ( struct tegra_plane * plane )
{
2017-12-20 09:39:14 +01:00
u32 background [ 3 ] = {
BLEND_WEIGHT1 ( 0 ) | BLEND_WEIGHT0 ( 0 ) | BLEND_COLOR_KEY_NONE ,
BLEND_WEIGHT1 ( 0 ) | BLEND_WEIGHT0 ( 0 ) | BLEND_COLOR_KEY_NONE ,
BLEND_WEIGHT1 ( 0 ) | BLEND_WEIGHT0 ( 0 ) | BLEND_COLOR_KEY_NONE ,
} ;
u32 foreground = BLEND_WEIGHT1 ( 255 ) | BLEND_WEIGHT0 ( 255 ) |
BLEND_COLOR_KEY_NONE ;
u32 blendnokey = BLEND_WEIGHT1 ( 255 ) | BLEND_WEIGHT0 ( 255 ) ;
struct tegra_plane_state * state ;
2018-05-04 17:39:59 +03:00
u32 blending [ 2 ] ;
2017-12-20 09:39:14 +01:00
unsigned int i ;
2018-05-04 17:39:59 +03:00
/* disable blending for non-overlapping case */
tegra_plane_writel ( plane , blendnokey , DC_WIN_BLEND_NOKEY ) ;
tegra_plane_writel ( plane , foreground , DC_WIN_BLEND_1WIN ) ;
2017-12-20 09:39:14 +01:00
state = to_tegra_plane_state ( plane - > base . state ) ;
2018-05-04 17:39:59 +03:00
if ( state - > opaque ) {
/*
* Since custom fix - weight blending isn ' t utilized and weight
* of top window is set to max , we can enforce dependent
* blending which in this case results in transparent bottom
* window if top window is opaque and if top window enables
* alpha blending , then bottom window is getting alpha value
* of 1 minus the sum of alpha components of the overlapping
* plane .
*/
background [ 0 ] | = BLEND_CONTROL_DEPENDENT ;
background [ 1 ] | = BLEND_CONTROL_DEPENDENT ;
2017-12-20 09:39:14 +01:00
2018-05-04 17:39:59 +03:00
/*
* The region where three windows overlap is the intersection
* of the two regions where two windows overlap . It contributes
* to the area if all of the windows on top of it have an alpha
* component .
*/
switch ( state - > base . normalized_zpos ) {
case 0 :
if ( state - > blending [ 0 ] . alpha & &
state - > blending [ 1 ] . alpha )
background [ 2 ] | = BLEND_CONTROL_DEPENDENT ;
break ;
case 1 :
background [ 2 ] | = BLEND_CONTROL_DEPENDENT ;
break ;
}
} else {
/*
* Enable alpha blending if pixel format has an alpha
* component .
*/
2017-12-20 09:39:14 +01:00
foreground | = BLEND_CONTROL_ALPHA ;
2018-05-04 17:39:59 +03:00
/*
* If any of the windows on top of this window is opaque , it
* will completely conceal this window within that area . If
* top window has an alpha component , it is blended over the
* bottom window .
*/
for ( i = 0 ; i < 2 ; i + + ) {
if ( state - > blending [ i ] . alpha & &
state - > blending [ i ] . top )
background [ i ] | = BLEND_CONTROL_DEPENDENT ;
}
switch ( state - > base . normalized_zpos ) {
case 0 :
if ( state - > blending [ 0 ] . alpha & &
state - > blending [ 1 ] . alpha )
background [ 2 ] | = BLEND_CONTROL_DEPENDENT ;
break ;
2017-12-14 13:46:20 +01:00
2018-05-04 17:39:59 +03:00
case 1 :
/*
* When both middle and topmost windows have an alpha ,
* these windows a mixed together and then the result
* is blended over the bottom window .
*/
if ( state - > blending [ 0 ] . alpha & &
state - > blending [ 0 ] . top )
background [ 2 ] | = BLEND_CONTROL_ALPHA ;
if ( state - > blending [ 1 ] . alpha & &
state - > blending [ 1 ] . top )
background [ 2 ] | = BLEND_CONTROL_ALPHA ;
break ;
}
}
switch ( state - > base . normalized_zpos ) {
2017-12-14 13:46:20 +01:00
case 0 :
2017-12-20 09:39:14 +01:00
tegra_plane_writel ( plane , background [ 0 ] , DC_WIN_BLEND_2WIN_X ) ;
tegra_plane_writel ( plane , background [ 1 ] , DC_WIN_BLEND_2WIN_Y ) ;
tegra_plane_writel ( plane , background [ 2 ] , DC_WIN_BLEND_3WIN_XY ) ;
2017-12-14 13:46:20 +01:00
break ;
case 1 :
2018-05-04 17:39:59 +03:00
/*
* If window B / C is topmost , then X / Y registers are
* matching the order of blending [ . . . ] state indices ,
* otherwise a swap is required .
*/
if ( ! state - > blending [ 0 ] . top & & state - > blending [ 1 ] . top ) {
blending [ 0 ] = foreground ;
blending [ 1 ] = background [ 1 ] ;
} else {
blending [ 0 ] = background [ 0 ] ;
blending [ 1 ] = foreground ;
}
tegra_plane_writel ( plane , blending [ 0 ] , DC_WIN_BLEND_2WIN_X ) ;
tegra_plane_writel ( plane , blending [ 1 ] , DC_WIN_BLEND_2WIN_Y ) ;
2017-12-20 09:39:14 +01:00
tegra_plane_writel ( plane , background [ 2 ] , DC_WIN_BLEND_3WIN_XY ) ;
2017-12-14 13:46:20 +01:00
break ;
case 2 :
2017-12-20 09:39:14 +01:00
tegra_plane_writel ( plane , foreground , DC_WIN_BLEND_2WIN_X ) ;
tegra_plane_writel ( plane , foreground , DC_WIN_BLEND_2WIN_Y ) ;
tegra_plane_writel ( plane , foreground , DC_WIN_BLEND_3WIN_XY ) ;
2017-12-14 13:46:20 +01:00
break ;
}
}
static void tegra_plane_setup_blending ( struct tegra_plane * plane ,
const struct tegra_dc_window * window )
{
u32 value ;
value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC ;
tegra_plane_writel ( plane , value , DC_WIN_BLEND_MATCH_SELECT ) ;
value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC ;
tegra_plane_writel ( plane , value , DC_WIN_BLEND_NOMATCH_SELECT ) ;
value = K2 ( 255 ) | K1 ( 255 ) | WINDOW_LAYER_DEPTH ( 255 - window - > zpos ) ;
tegra_plane_writel ( plane , value , DC_WIN_BLEND_LAYER_CONTROL ) ;
}
2018-05-04 17:39:58 +03:00
static bool
tegra_plane_use_horizontal_filtering ( struct tegra_plane * plane ,
const struct tegra_dc_window * window )
{
struct tegra_dc * dc = plane - > dc ;
if ( window - > src . w = = window - > dst . w )
return false ;
if ( plane - > index = = 0 & & dc - > soc - > has_win_a_without_filters )
return false ;
return true ;
}
static bool
tegra_plane_use_vertical_filtering ( struct tegra_plane * plane ,
const struct tegra_dc_window * window )
{
struct tegra_dc * dc = plane - > dc ;
if ( window - > src . h = = window - > dst . h )
return false ;
if ( plane - > index = = 0 & & dc - > soc - > has_win_a_without_filters )
return false ;
if ( plane - > index = = 2 & & dc - > soc - > has_win_c_without_vert_filter )
return false ;
return true ;
}
2017-12-14 13:37:53 +01:00
static void tegra_dc_setup_window ( struct tegra_plane * plane ,
2014-11-24 16:27:13 +01:00
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 ;
2017-12-14 13:37:53 +01:00
struct tegra_dc * dc = plane - > dc ;
2014-03-14 09:54:58 +01:00
bool yuv , planar ;
2017-12-14 13:37:53 +01:00
u32 value ;
2014-03-14 09:54:58 +01:00
/*
* For YUV planar modes , the number of bytes per pixel takes into
* account only the luma component and therefore is 1.
*/
2017-11-10 15:27:25 +01:00
yuv = tegra_plane_format_is_yuv ( window - > format , & planar ) ;
2014-03-14 09:54:58 +01:00
if ( ! yuv )
bpp = window - > bits_per_pixel / 8 ;
else
bpp = planar ? 1 : 2 ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , window - > format , DC_WIN_COLOR_DEPTH ) ;
tegra_plane_writel ( plane , window - > swap , DC_WIN_BYTE_SWAP ) ;
2014-03-14 09:54:58 +01:00
value = V_POSITION ( window - > dst . y ) | H_POSITION ( window - > dst . x ) ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_POSITION ) ;
2014-03-14 09:54:58 +01:00
value = V_SIZE ( window - > dst . h ) | H_SIZE ( window - > dst . w ) ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_SIZE ) ;
2014-03-14 09:54:58 +01:00
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 ) ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_PRESCALED_SIZE ) ;
2014-03-14 09:54:58 +01:00
/*
* 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 ) ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_DDA_INC ) ;
2014-03-14 09:54:58 +01:00
h_dda = compute_initial_dda ( window - > src . x ) ;
v_dda = compute_initial_dda ( window - > src . y ) ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , h_dda , DC_WIN_H_INITIAL_DDA ) ;
tegra_plane_writel ( plane , v_dda , DC_WIN_V_INITIAL_DDA ) ;
2014-03-14 09:54:58 +01:00
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , 0 , DC_WIN_UV_BUF_STRIDE ) ;
tegra_plane_writel ( plane , 0 , DC_WIN_BUF_STRIDE ) ;
2014-03-14 09:54:58 +01:00
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , window - > base [ 0 ] , DC_WINBUF_START_ADDR ) ;
2014-03-14 09:54:58 +01:00
if ( yuv & & planar ) {
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , window - > base [ 1 ] , DC_WINBUF_START_ADDR_U ) ;
tegra_plane_writel ( plane , window - > base [ 2 ] , DC_WINBUF_START_ADDR_V ) ;
2014-03-14 09:54:58 +01:00
value = window - > stride [ 1 ] < < 16 | window - > stride [ 0 ] ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_LINE_STRIDE ) ;
2014-03-14 09:54:58 +01:00
} else {
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , window - > stride [ 0 ] , DC_WIN_LINE_STRIDE ) ;
2014-03-14 09:54:58 +01:00
}
if ( window - > bottom_up )
v_offset + = window - > src . h - 1 ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , h_offset , DC_WINBUF_ADDR_H_OFFSET ) ;
tegra_plane_writel ( plane , v_offset , DC_WINBUF_ADDR_V_OFFSET ) ;
2014-03-14 09:54:58 +01:00
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 ;
}
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , 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
}
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_BUFFER_ADDR_MODE ) ;
2014-06-03 14:48:12 +02:00
}
2014-03-14 09:54:58 +01:00
value = WIN_ENABLE ;
if ( yuv ) {
/* setup default colorspace conversion coefficients */
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , 0x00f0 , DC_WIN_CSC_YOF ) ;
tegra_plane_writel ( plane , 0x012a , DC_WIN_CSC_KYRGB ) ;
tegra_plane_writel ( plane , 0x0000 , DC_WIN_CSC_KUR ) ;
tegra_plane_writel ( plane , 0x0198 , DC_WIN_CSC_KVR ) ;
tegra_plane_writel ( plane , 0x039b , DC_WIN_CSC_KUG ) ;
tegra_plane_writel ( plane , 0x032f , DC_WIN_CSC_KVG ) ;
tegra_plane_writel ( plane , 0x0204 , DC_WIN_CSC_KUB ) ;
tegra_plane_writel ( plane , 0x0000 , DC_WIN_CSC_KVB ) ;
2014-03-14 09:54:58 +01:00
value | = CSC_ENABLE ;
} else if ( window - > bits_per_pixel < 24 ) {
value | = COLOR_EXPAND ;
}
if ( window - > bottom_up )
value | = V_DIRECTION ;
2018-05-04 17:39:58 +03:00
if ( tegra_plane_use_horizontal_filtering ( plane , window ) ) {
/*
* Enable horizontal 6 - tap filter and set filtering
* coefficients to the default values defined in TRM .
*/
tegra_plane_writel ( plane , 0x00008000 , DC_WIN_H_FILTER_P ( 0 ) ) ;
tegra_plane_writel ( plane , 0x3e087ce1 , DC_WIN_H_FILTER_P ( 1 ) ) ;
tegra_plane_writel ( plane , 0x3b117ac1 , DC_WIN_H_FILTER_P ( 2 ) ) ;
tegra_plane_writel ( plane , 0x591b73aa , DC_WIN_H_FILTER_P ( 3 ) ) ;
tegra_plane_writel ( plane , 0x57256d9a , DC_WIN_H_FILTER_P ( 4 ) ) ;
tegra_plane_writel ( plane , 0x552f668b , DC_WIN_H_FILTER_P ( 5 ) ) ;
tegra_plane_writel ( plane , 0x73385e8b , DC_WIN_H_FILTER_P ( 6 ) ) ;
tegra_plane_writel ( plane , 0x72435583 , DC_WIN_H_FILTER_P ( 7 ) ) ;
tegra_plane_writel ( plane , 0x714c4c8b , DC_WIN_H_FILTER_P ( 8 ) ) ;
tegra_plane_writel ( plane , 0x70554393 , DC_WIN_H_FILTER_P ( 9 ) ) ;
tegra_plane_writel ( plane , 0x715e389b , DC_WIN_H_FILTER_P ( 10 ) ) ;
tegra_plane_writel ( plane , 0x71662faa , DC_WIN_H_FILTER_P ( 11 ) ) ;
tegra_plane_writel ( plane , 0x536d25ba , DC_WIN_H_FILTER_P ( 12 ) ) ;
tegra_plane_writel ( plane , 0x55731bca , DC_WIN_H_FILTER_P ( 13 ) ) ;
tegra_plane_writel ( plane , 0x387a11d9 , DC_WIN_H_FILTER_P ( 14 ) ) ;
tegra_plane_writel ( plane , 0x3c7c08f1 , DC_WIN_H_FILTER_P ( 15 ) ) ;
value | = H_FILTER ;
}
if ( tegra_plane_use_vertical_filtering ( plane , window ) ) {
unsigned int i , k ;
/*
* Enable vertical 2 - tap filter and set filtering
* coefficients to the default values defined in TRM .
*/
for ( i = 0 , k = 128 ; i < 16 ; i + + , k - = 8 )
tegra_plane_writel ( plane , k , DC_WIN_V_FILTER_P ( i ) ) ;
value | = V_FILTER ;
}
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( plane , value , DC_WIN_WIN_OPTIONS ) ;
2014-03-14 09:54:58 +01:00
2018-05-04 17:40:00 +03:00
if ( dc - > soc - > has_legacy_blending )
2017-12-14 13:46:20 +01:00
tegra_plane_setup_blending_legacy ( plane ) ;
2018-05-04 17:40:00 +03:00
else
tegra_plane_setup_blending ( plane , window ) ;
2014-10-21 13:51:53 +02:00
}
2017-11-14 16:07:40 +01:00
static const u32 tegra20_primary_formats [ ] = {
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGBA5551 ,
2017-10-12 17:30:55 +02:00
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_ARGB8888 ,
2017-12-20 09:39:14 +01:00
/* non-native formats */
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGBX5551 ,
DRM_FORMAT_XBGR8888 ,
DRM_FORMAT_XRGB8888 ,
2017-11-14 16:07:40 +01:00
} ;
2018-03-15 16:44:04 +01:00
static const u64 tegra20_modifiers [ ] = {
DRM_FORMAT_MOD_LINEAR ,
DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED ,
DRM_FORMAT_MOD_INVALID
} ;
2017-11-14 16:07:40 +01:00
static const u32 tegra114_primary_formats [ ] = {
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGBA5551 ,
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_ARGB8888 ,
/* new on Tegra114 */
DRM_FORMAT_ABGR4444 ,
DRM_FORMAT_ABGR1555 ,
DRM_FORMAT_BGRA5551 ,
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGBX5551 ,
DRM_FORMAT_XBGR1555 ,
DRM_FORMAT_BGRX5551 ,
DRM_FORMAT_BGR565 ,
DRM_FORMAT_BGRA8888 ,
DRM_FORMAT_RGBA8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_XBGR8888 ,
} ;
static const u32 tegra124_primary_formats [ ] = {
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_ARGB1555 ,
2014-10-21 13:51:53 +02:00
DRM_FORMAT_RGB565 ,
2017-11-14 16:07:40 +01:00
DRM_FORMAT_RGBA5551 ,
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_ARGB8888 ,
/* new on Tegra114 */
DRM_FORMAT_ABGR4444 ,
DRM_FORMAT_ABGR1555 ,
DRM_FORMAT_BGRA5551 ,
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGBX5551 ,
DRM_FORMAT_XBGR1555 ,
DRM_FORMAT_BGRX5551 ,
DRM_FORMAT_BGR565 ,
DRM_FORMAT_BGRA8888 ,
DRM_FORMAT_RGBA8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_XBGR8888 ,
/* new on Tegra124 */
DRM_FORMAT_RGBX8888 ,
DRM_FORMAT_BGRX8888 ,
2014-10-21 13:51:53 +02:00
} ;
2018-03-15 16:44:04 +01:00
static const u64 tegra124_modifiers [ ] = {
DRM_FORMAT_MOD_LINEAR ,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK ( 0 ) ,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK ( 1 ) ,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK ( 2 ) ,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK ( 3 ) ,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK ( 4 ) ,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK ( 5 ) ,
DRM_FORMAT_MOD_INVALID
} ;
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 ) ;
2018-03-19 17:20:46 +01:00
unsigned int rotation = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y ;
2014-11-28 13:14:55 +01:00
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 ;
2018-05-04 17:39:59 +03:00
err = tegra_plane_format ( state - > fb - > format - > format ,
& plane_state - > format ,
2017-11-10 15:27:25 +01:00
& plane_state - > swap ) ;
2014-11-24 16:27:13 +01:00
if ( err < 0 )
return err ;
2017-12-20 09:39:14 +01:00
/*
* Tegra20 and Tegra30 are special cases here because they support
* only variants of specific formats with an alpha component , but not
* the corresponding opaque formats . However , the opaque formats can
* be emulated by disabling alpha blending for the plane .
*/
2018-05-04 17:40:00 +03:00
if ( dc - > soc - > has_legacy_blending ) {
2018-05-04 17:39:59 +03:00
err = tegra_plane_setup_legacy_state ( tegra , plane_state ) ;
if ( err < 0 )
return err ;
2017-12-20 09:39:14 +01:00
}
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 ;
}
2018-03-19 17:20:46 +01:00
rotation = drm_rotation_simplify ( state - > rotation , rotation ) ;
if ( rotation & DRM_MODE_REFLECT_Y )
plane_state - > bottom_up = true ;
else
plane_state - > bottom_up = false ;
2014-11-24 16:27:13 +01:00
/*
* 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_plane * p = to_tegra_plane ( plane ) ;
2017-06-15 02:18:31 +03:00
u32 value ;
2017-08-30 17:34:10 +02:00
/* rien ne va plus */
if ( ! old_state | | ! old_state - > crtc )
return ;
2017-12-14 13:37:53 +01:00
value = tegra_plane_readl ( p , DC_WIN_WIN_OPTIONS ) ;
2017-06-15 02:18:31 +03:00
value & = ~ WIN_ENABLE ;
2017-12-14 13:37:53 +01:00
tegra_plane_writel ( p , value , DC_WIN_WIN_OPTIONS ) ;
2017-06-15 02:18:31 +03:00
}
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 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 ;
2018-03-19 17:20:46 +01:00
window . bottom_up = tegra_fb_is_bottom_up ( fb ) | | state - > bottom_up ;
2014-10-21 13:51:53 +02:00
2014-11-28 13:14:55 +01:00
/* copy from state */
2017-12-14 13:46:20 +01:00
window . zpos = plane - > state - > normalized_zpos ;
2014-11-28 13:14:55 +01:00
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 + + ) {
2019-10-28 13:37:17 +01:00
window . base [ i ] = state - > iova [ i ] + 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
2017-12-14 13:37:53 +01:00
tegra_dc_setup_window ( p , & 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 = {
2019-10-28 13:37:17 +01:00
. prepare_fb = tegra_plane_prepare_fb ,
. cleanup_fb = tegra_plane_cleanup_fb ,
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
} ;
2018-01-08 16:11:00 +01:00
static unsigned long tegra_plane_get_possible_crtcs ( struct drm_device * drm )
2014-10-21 13:51:53 +02:00
{
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 .
*/
2018-01-08 16:11:00 +01:00
return 1 < < drm - > mode_config . num_crtc ;
}
static struct drm_plane * tegra_primary_plane_create ( struct drm_device * drm ,
struct tegra_dc * dc )
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs ( drm ) ;
2017-08-30 17:42:54 +02:00
enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY ;
2014-10-21 13:51:53 +02:00
struct tegra_plane * plane ;
unsigned int num_formats ;
2018-03-15 16:44:04 +01:00
const u64 * modifiers ;
2014-10-21 13:51:53 +02:00
const u32 * formats ;
int err ;
plane = kzalloc ( sizeof ( * plane ) , GFP_KERNEL ) ;
if ( ! plane )
return ERR_PTR ( - ENOMEM ) ;
2017-12-14 13:37:53 +01:00
/* Always use window A as primary window */
plane - > offset = 0xa00 ;
2017-11-13 11:08:13 +01:00
plane - > index = 0 ;
2017-12-14 13:37:53 +01:00
plane - > dc = dc ;
num_formats = dc - > soc - > num_primary_formats ;
formats = dc - > soc - > primary_formats ;
2018-03-15 16:44:04 +01:00
modifiers = dc - > soc - > modifiers ;
2017-11-13 11:08:13 +01:00
2014-12-16 18:04:08 +01:00
err = drm_universal_plane_init ( drm , & plane - > base , possible_crtcs ,
2017-08-30 18:04:12 +02:00
& tegra_plane_funcs , formats ,
2018-03-15 16:44:04 +01:00
num_formats , modifiers , type , 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 ) ;
2018-05-04 17:39:59 +03:00
drm_plane_create_zpos_property ( & plane - > base , plane - > index , 0 , 255 ) ;
2017-12-14 13:46:20 +01:00
2018-03-19 17:20:46 +01:00
err = drm_plane_create_rotation_property ( & plane - > base ,
DRM_MODE_ROTATE_0 ,
DRM_MODE_ROTATE_0 |
DRM_MODE_REFLECT_Y ) ;
if ( err < 0 )
dev_err ( dc - > dev , " failed to create rotation property: %d \n " ,
err ) ;
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 )
{
2019-12-03 17:19:09 +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 ) ;
u32 value = CURSOR_CLIP_DISPLAY ;
/* rien ne va plus */
if ( ! plane - > state - > crtc | | ! plane - > state - > fb )
return ;
2019-12-03 17:19:09 +01:00
switch ( plane - > 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 :
2019-12-03 17:19:09 +01:00
WARN ( 1 , " cursor size %ux%u not supported \n " ,
plane - > state - > crtc_w , plane - > state - > crtc_h ) ;
2014-11-24 16:27:13 +01:00
return ;
2014-10-21 13:51:53 +02:00
}
2019-12-03 17:19:09 +01:00
value | = ( state - > iova [ 0 ] > > 10 ) & 0x3fffff ;
2014-10-21 13:51:53 +02:00
tegra_dc_writel ( dc , value , DC_DISP_CURSOR_START_ADDR ) ;
# ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
2019-12-03 17:19:09 +01:00
value = ( state - > iova [ 0 ] > > 32 ) & 0x3 ;
2014-10-21 13:51:53 +02:00
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 */
2019-12-03 17:19:09 +01:00
value = ( plane - > state - > crtc_y & 0x3fff ) < < 16 |
( plane - > 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 ) ;
}
2014-11-24 16:27:13 +01:00
static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
2019-10-28 13:37:17 +01:00
. prepare_fb = tegra_plane_prepare_fb ,
. cleanup_fb = tegra_plane_cleanup_fb ,
2014-11-24 16:27:13 +01:00
. 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 )
{
2018-01-08 16:11:00 +01:00
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs ( drm ) ;
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 ) ;
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 ;
2017-12-14 13:37:53 +01:00
plane - > dc = dc ;
2014-11-26 12:28:39 +01:00
2014-10-21 13:51:53 +02:00
num_formats = ARRAY_SIZE ( tegra_cursor_plane_formats ) ;
formats = tegra_cursor_plane_formats ;
2018-01-08 16:11:00 +01:00
err = drm_universal_plane_init ( drm , & plane - > base , possible_crtcs ,
2017-08-30 18:04:12 +02:00
& tegra_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
}
2017-11-14 16:07:40 +01:00
static const u32 tegra20_overlay_formats [ ] = {
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGBA5551 ,
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_ARGB8888 ,
2017-12-20 09:39:14 +01:00
/* non-native formats */
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGBX5551 ,
DRM_FORMAT_XBGR8888 ,
DRM_FORMAT_XRGB8888 ,
2017-11-14 16:07:40 +01:00
/* planar formats */
DRM_FORMAT_UYVY ,
DRM_FORMAT_YUYV ,
DRM_FORMAT_YUV420 ,
DRM_FORMAT_YUV422 ,
} ;
static const u32 tegra114_overlay_formats [ ] = {
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGBA5551 ,
2017-10-12 17:30:55 +02:00
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_ARGB8888 ,
2017-11-14 16:07:40 +01:00
/* new on Tegra114 */
DRM_FORMAT_ABGR4444 ,
DRM_FORMAT_ABGR1555 ,
DRM_FORMAT_BGRA5551 ,
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGBX5551 ,
DRM_FORMAT_XBGR1555 ,
DRM_FORMAT_BGRX5551 ,
DRM_FORMAT_BGR565 ,
DRM_FORMAT_BGRA8888 ,
DRM_FORMAT_RGBA8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_XBGR8888 ,
/* planar formats */
DRM_FORMAT_UYVY ,
DRM_FORMAT_YUYV ,
DRM_FORMAT_YUV420 ,
DRM_FORMAT_YUV422 ,
} ;
static const u32 tegra124_overlay_formats [ ] = {
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_ARGB1555 ,
2013-03-22 15:37:30 +01:00
DRM_FORMAT_RGB565 ,
2017-11-14 16:07:40 +01:00
DRM_FORMAT_RGBA5551 ,
DRM_FORMAT_ABGR8888 ,
DRM_FORMAT_ARGB8888 ,
/* new on Tegra114 */
DRM_FORMAT_ABGR4444 ,
DRM_FORMAT_ABGR1555 ,
DRM_FORMAT_BGRA5551 ,
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGBX5551 ,
DRM_FORMAT_XBGR1555 ,
DRM_FORMAT_BGRX5551 ,
DRM_FORMAT_BGR565 ,
DRM_FORMAT_BGRA8888 ,
DRM_FORMAT_RGBA8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_XBGR8888 ,
/* new on Tegra124 */
DRM_FORMAT_RGBX8888 ,
DRM_FORMAT_BGRX8888 ,
/* planar formats */
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 ,
2018-03-15 04:00:25 +03:00
unsigned int index ,
bool cursor )
2012-11-04 21:47:13 +01:00
{
2018-01-08 16:11:00 +01:00
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs ( drm ) ;
2014-10-21 13:51:53 +02:00
struct tegra_plane * plane ;
unsigned int num_formats ;
2018-03-15 04:00:25 +03:00
enum drm_plane_type type ;
2014-10-21 13:51:53 +02:00
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
2017-12-14 13:37:53 +01:00
plane - > offset = 0xa00 + 0x200 * index ;
2014-10-21 13:51:53 +02:00
plane - > index = index ;
2017-12-14 13:37:53 +01:00
plane - > dc = dc ;
2012-11-04 21:47:13 +01:00
2017-11-14 16:07:40 +01:00
num_formats = dc - > soc - > num_overlay_formats ;
formats = dc - > soc - > overlay_formats ;
2012-11-04 21:47:13 +01:00
2018-03-15 04:00:25 +03:00
if ( ! cursor )
type = DRM_PLANE_TYPE_OVERLAY ;
else
type = DRM_PLANE_TYPE_CURSOR ;
2018-01-08 16:11:00 +01:00
err = drm_universal_plane_init ( drm , & plane - > base , possible_crtcs ,
2017-08-30 18:04:12 +02:00
& tegra_plane_funcs , formats ,
2018-03-15 04:00:25 +03:00
num_formats , NULL , type , 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 ) ;
2018-05-04 17:39:59 +03:00
drm_plane_create_zpos_property ( & plane - > base , plane - > index , 0 , 255 ) ;
2017-12-14 13:46:20 +01:00
2018-03-19 17:20:46 +01:00
err = drm_plane_create_rotation_property ( & plane - > base ,
DRM_MODE_ROTATE_0 ,
DRM_MODE_ROTATE_0 |
DRM_MODE_REFLECT_Y ) ;
if ( err < 0 )
dev_err ( dc - > dev , " failed to create rotation property: %d \n " ,
err ) ;
2014-10-21 13:51:53 +02:00
return & plane - > base ;
}
2017-08-30 17:42:54 +02:00
static struct drm_plane * tegra_dc_add_shared_planes ( struct drm_device * drm ,
struct tegra_dc * dc )
{
struct drm_plane * plane , * primary = NULL ;
unsigned int i , j ;
for ( i = 0 ; i < dc - > soc - > num_wgrps ; i + + ) {
const struct tegra_windowgroup_soc * wgrp = & dc - > soc - > wgrps [ i ] ;
if ( wgrp - > dc = = dc - > pipe ) {
for ( j = 0 ; j < wgrp - > num_windows ; j + + ) {
unsigned int index = wgrp - > windows [ j ] ;
plane = tegra_shared_plane_create ( drm , dc ,
wgrp - > index ,
index ) ;
if ( IS_ERR ( plane ) )
return plane ;
/*
* Choose the first shared plane owned by this
* head as the primary plane .
*/
if ( ! primary ) {
plane - > type = DRM_PLANE_TYPE_PRIMARY ;
primary = plane ;
}
}
}
}
return primary ;
}
static struct drm_plane * tegra_dc_add_planes ( struct drm_device * drm ,
struct tegra_dc * dc )
2014-10-21 13:51:53 +02:00
{
2018-01-08 16:16:06 +01:00
struct drm_plane * planes [ 2 ] , * primary ;
2018-03-15 04:00:25 +03:00
unsigned int planes_num ;
2014-10-21 13:51:53 +02:00
unsigned int i ;
2018-01-08 16:16:06 +01:00
int err ;
2014-10-21 13:51:53 +02:00
2017-08-30 17:42:54 +02:00
primary = tegra_primary_plane_create ( drm , dc ) ;
if ( IS_ERR ( primary ) )
return primary ;
2018-03-15 04:00:25 +03:00
if ( dc - > soc - > supports_cursor )
planes_num = 2 ;
else
planes_num = 1 ;
for ( i = 0 ; i < planes_num ; i + + ) {
planes [ i ] = tegra_dc_overlay_plane_create ( drm , dc , 1 + i ,
false ) ;
2018-01-08 16:16:06 +01:00
if ( IS_ERR ( planes [ i ] ) ) {
err = PTR_ERR ( planes [ i ] ) ;
while ( i - - )
tegra_plane_funcs . destroy ( planes [ i ] ) ;
tegra_plane_funcs . destroy ( primary ) ;
return ERR_PTR ( err ) ;
2017-08-30 17:42:54 +02:00
}
2012-11-04 21:47:13 +01:00
}
2017-08-30 17:42:54 +02:00
return primary ;
2012-11-04 21:47:13 +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 )
{
2019-03-01 13:56:24 +01:00
struct tegra_dc_state * state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
2014-12-08 16:14:45 +01:00
2015-01-28 15:01:22 +01:00
if ( crtc - > state )
2019-03-01 13:56:24 +01:00
tegra_crtc_atomic_destroy_state ( crtc , crtc - > state ) ;
2015-07-02 17:04:06 +02:00
2019-03-01 13:56:24 +01:00
__drm_atomic_helper_crtc_reset ( crtc , & state - > base ) ;
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 ) ;
}
2017-11-08 13:40:54 +01:00
# define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
2017-11-08 13:32:05 +01:00
2017-11-08 13:40:54 +01:00
static const struct debugfs_reg32 tegra_dc_regs [ ] = {
DEBUGFS_REG32 ( DC_CMD_GENERAL_INCR_SYNCPT ) ,
DEBUGFS_REG32 ( DC_CMD_GENERAL_INCR_SYNCPT_CNTRL ) ,
DEBUGFS_REG32 ( DC_CMD_GENERAL_INCR_SYNCPT_ERROR ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_A_INCR_SYNCPT ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_A_INCR_SYNCPT_CNTRL ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_A_INCR_SYNCPT_ERROR ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_B_INCR_SYNCPT ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_B_INCR_SYNCPT_CNTRL ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_B_INCR_SYNCPT_ERROR ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_C_INCR_SYNCPT ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_C_INCR_SYNCPT_CNTRL ) ,
DEBUGFS_REG32 ( DC_CMD_WIN_C_INCR_SYNCPT_ERROR ) ,
DEBUGFS_REG32 ( DC_CMD_CONT_SYNCPT_VSYNC ) ,
DEBUGFS_REG32 ( DC_CMD_DISPLAY_COMMAND_OPTION0 ) ,
DEBUGFS_REG32 ( DC_CMD_DISPLAY_COMMAND ) ,
DEBUGFS_REG32 ( DC_CMD_SIGNAL_RAISE ) ,
DEBUGFS_REG32 ( DC_CMD_DISPLAY_POWER_CONTROL ) ,
DEBUGFS_REG32 ( DC_CMD_INT_STATUS ) ,
DEBUGFS_REG32 ( DC_CMD_INT_MASK ) ,
DEBUGFS_REG32 ( DC_CMD_INT_ENABLE ) ,
DEBUGFS_REG32 ( DC_CMD_INT_TYPE ) ,
DEBUGFS_REG32 ( DC_CMD_INT_POLARITY ) ,
DEBUGFS_REG32 ( DC_CMD_SIGNAL_RAISE1 ) ,
DEBUGFS_REG32 ( DC_CMD_SIGNAL_RAISE2 ) ,
DEBUGFS_REG32 ( DC_CMD_SIGNAL_RAISE3 ) ,
DEBUGFS_REG32 ( DC_CMD_STATE_ACCESS ) ,
DEBUGFS_REG32 ( DC_CMD_STATE_CONTROL ) ,
DEBUGFS_REG32 ( DC_CMD_DISPLAY_WINDOW_HEADER ) ,
DEBUGFS_REG32 ( DC_CMD_REG_ACT_CONTROL ) ,
DEBUGFS_REG32 ( DC_COM_CRC_CONTROL ) ,
DEBUGFS_REG32 ( DC_COM_CRC_CHECKSUM ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_ENABLE ( 0 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_ENABLE ( 1 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_ENABLE ( 2 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_ENABLE ( 3 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_POLARITY ( 0 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_POLARITY ( 1 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_POLARITY ( 2 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_POLARITY ( 3 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_DATA ( 0 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_DATA ( 1 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_DATA ( 2 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_DATA ( 3 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_INPUT_ENABLE ( 0 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_INPUT_ENABLE ( 1 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_INPUT_ENABLE ( 2 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_INPUT_ENABLE ( 3 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_INPUT_DATA ( 0 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_INPUT_DATA ( 1 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 0 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 1 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 2 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 3 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 4 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 5 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_OUTPUT_SELECT ( 6 ) ) ,
DEBUGFS_REG32 ( DC_COM_PIN_MISC_CONTROL ) ,
DEBUGFS_REG32 ( DC_COM_PIN_PM0_CONTROL ) ,
DEBUGFS_REG32 ( DC_COM_PIN_PM0_DUTY_CYCLE ) ,
DEBUGFS_REG32 ( DC_COM_PIN_PM1_CONTROL ) ,
DEBUGFS_REG32 ( DC_COM_PIN_PM1_DUTY_CYCLE ) ,
DEBUGFS_REG32 ( DC_COM_SPI_CONTROL ) ,
DEBUGFS_REG32 ( DC_COM_SPI_START_BYTE ) ,
DEBUGFS_REG32 ( DC_COM_HSPI_WRITE_DATA_AB ) ,
DEBUGFS_REG32 ( DC_COM_HSPI_WRITE_DATA_CD ) ,
DEBUGFS_REG32 ( DC_COM_HSPI_CS_DC ) ,
DEBUGFS_REG32 ( DC_COM_SCRATCH_REGISTER_A ) ,
DEBUGFS_REG32 ( DC_COM_SCRATCH_REGISTER_B ) ,
DEBUGFS_REG32 ( DC_COM_GPIO_CTRL ) ,
DEBUGFS_REG32 ( DC_COM_GPIO_DEBOUNCE_COUNTER ) ,
DEBUGFS_REG32 ( DC_COM_CRC_CHECKSUM_LATCHED ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_SIGNAL_OPTIONS0 ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_SIGNAL_OPTIONS1 ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_WIN_OPTIONS ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_MEM_HIGH_PRIORITY ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_TIMING_OPTIONS ) ,
DEBUGFS_REG32 ( DC_DISP_REF_TO_SYNC ) ,
DEBUGFS_REG32 ( DC_DISP_SYNC_WIDTH ) ,
DEBUGFS_REG32 ( DC_DISP_BACK_PORCH ) ,
DEBUGFS_REG32 ( DC_DISP_ACTIVE ) ,
DEBUGFS_REG32 ( DC_DISP_FRONT_PORCH ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE0_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE0_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE0_POSITION_B ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE0_POSITION_C ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE0_POSITION_D ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE1_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE1_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE1_POSITION_B ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE1_POSITION_C ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE1_POSITION_D ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE2_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE2_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE2_POSITION_B ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE2_POSITION_C ) ,
DEBUGFS_REG32 ( DC_DISP_H_PULSE2_POSITION_D ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE0_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE0_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE0_POSITION_B ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE0_POSITION_C ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE1_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE1_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE1_POSITION_B ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE1_POSITION_C ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE2_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE2_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE3_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_V_PULSE3_POSITION_A ) ,
DEBUGFS_REG32 ( DC_DISP_M0_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_M1_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_DI_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_PP_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_PP_SELECT_A ) ,
DEBUGFS_REG32 ( DC_DISP_PP_SELECT_B ) ,
DEBUGFS_REG32 ( DC_DISP_PP_SELECT_C ) ,
DEBUGFS_REG32 ( DC_DISP_PP_SELECT_D ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_CLOCK_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_INTERFACE_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_COLOR_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_SHIFT_CLOCK_OPTIONS ) ,
DEBUGFS_REG32 ( DC_DISP_DATA_ENABLE_OPTIONS ) ,
DEBUGFS_REG32 ( DC_DISP_SERIAL_INTERFACE_OPTIONS ) ,
DEBUGFS_REG32 ( DC_DISP_LCD_SPI_OPTIONS ) ,
DEBUGFS_REG32 ( DC_DISP_BORDER_COLOR ) ,
DEBUGFS_REG32 ( DC_DISP_COLOR_KEY0_LOWER ) ,
DEBUGFS_REG32 ( DC_DISP_COLOR_KEY0_UPPER ) ,
DEBUGFS_REG32 ( DC_DISP_COLOR_KEY1_LOWER ) ,
DEBUGFS_REG32 ( DC_DISP_COLOR_KEY1_UPPER ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_FOREGROUND ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_BACKGROUND ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_START_ADDR ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_START_ADDR_NS ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_POSITION ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_POSITION_NS ) ,
DEBUGFS_REG32 ( DC_DISP_INIT_SEQ_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_SPI_INIT_SEQ_DATA_A ) ,
DEBUGFS_REG32 ( DC_DISP_SPI_INIT_SEQ_DATA_B ) ,
DEBUGFS_REG32 ( DC_DISP_SPI_INIT_SEQ_DATA_C ) ,
DEBUGFS_REG32 ( DC_DISP_SPI_INIT_SEQ_DATA_D ) ,
DEBUGFS_REG32 ( DC_DISP_DC_MCCIF_FIFOCTRL ) ,
DEBUGFS_REG32 ( DC_DISP_MCCIF_DISPLAY0A_HYST ) ,
DEBUGFS_REG32 ( DC_DISP_MCCIF_DISPLAY0B_HYST ) ,
DEBUGFS_REG32 ( DC_DISP_MCCIF_DISPLAY1A_HYST ) ,
DEBUGFS_REG32 ( DC_DISP_MCCIF_DISPLAY1B_HYST ) ,
DEBUGFS_REG32 ( DC_DISP_DAC_CRT_CTRL ) ,
DEBUGFS_REG32 ( DC_DISP_DISP_MISC_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_SD_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_SD_CSC_COEFF ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 0 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 1 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 2 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 3 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 4 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 5 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 6 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 7 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_LUT ( 8 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_FLICKER_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_DC_PIXEL_COUNT ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 0 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 1 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 2 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 3 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 4 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 5 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 6 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HISTOGRAM ( 7 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_BL_TF ( 0 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_BL_TF ( 1 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_BL_TF ( 2 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_BL_TF ( 3 ) ) ,
DEBUGFS_REG32 ( DC_DISP_SD_BL_CONTROL ) ,
DEBUGFS_REG32 ( DC_DISP_SD_HW_K_VALUES ) ,
DEBUGFS_REG32 ( DC_DISP_SD_MAN_K_VALUES ) ,
DEBUGFS_REG32 ( DC_DISP_CURSOR_START_ADDR_HI ) ,
DEBUGFS_REG32 ( DC_DISP_BLEND_CURSOR_CONTROL ) ,
DEBUGFS_REG32 ( DC_WIN_WIN_OPTIONS ) ,
DEBUGFS_REG32 ( DC_WIN_BYTE_SWAP ) ,
DEBUGFS_REG32 ( DC_WIN_BUFFER_CONTROL ) ,
DEBUGFS_REG32 ( DC_WIN_COLOR_DEPTH ) ,
DEBUGFS_REG32 ( DC_WIN_POSITION ) ,
DEBUGFS_REG32 ( DC_WIN_SIZE ) ,
DEBUGFS_REG32 ( DC_WIN_PRESCALED_SIZE ) ,
DEBUGFS_REG32 ( DC_WIN_H_INITIAL_DDA ) ,
DEBUGFS_REG32 ( DC_WIN_V_INITIAL_DDA ) ,
DEBUGFS_REG32 ( DC_WIN_DDA_INC ) ,
DEBUGFS_REG32 ( DC_WIN_LINE_STRIDE ) ,
DEBUGFS_REG32 ( DC_WIN_BUF_STRIDE ) ,
DEBUGFS_REG32 ( DC_WIN_UV_BUF_STRIDE ) ,
DEBUGFS_REG32 ( DC_WIN_BUFFER_ADDR_MODE ) ,
DEBUGFS_REG32 ( DC_WIN_DV_CONTROL ) ,
DEBUGFS_REG32 ( DC_WIN_BLEND_NOKEY ) ,
DEBUGFS_REG32 ( DC_WIN_BLEND_1WIN ) ,
DEBUGFS_REG32 ( DC_WIN_BLEND_2WIN_X ) ,
DEBUGFS_REG32 ( DC_WIN_BLEND_2WIN_Y ) ,
DEBUGFS_REG32 ( DC_WIN_BLEND_3WIN_XY ) ,
DEBUGFS_REG32 ( DC_WIN_HP_FETCH_CONTROL ) ,
DEBUGFS_REG32 ( DC_WINBUF_START_ADDR ) ,
DEBUGFS_REG32 ( DC_WINBUF_START_ADDR_NS ) ,
DEBUGFS_REG32 ( DC_WINBUF_START_ADDR_U ) ,
DEBUGFS_REG32 ( DC_WINBUF_START_ADDR_U_NS ) ,
DEBUGFS_REG32 ( DC_WINBUF_START_ADDR_V ) ,
DEBUGFS_REG32 ( DC_WINBUF_START_ADDR_V_NS ) ,
DEBUGFS_REG32 ( DC_WINBUF_ADDR_H_OFFSET ) ,
DEBUGFS_REG32 ( DC_WINBUF_ADDR_H_OFFSET_NS ) ,
DEBUGFS_REG32 ( DC_WINBUF_ADDR_V_OFFSET ) ,
DEBUGFS_REG32 ( DC_WINBUF_ADDR_V_OFFSET_NS ) ,
DEBUGFS_REG32 ( DC_WINBUF_UFLOW_STATUS ) ,
DEBUGFS_REG32 ( DC_WINBUF_AD_UFLOW_STATUS ) ,
DEBUGFS_REG32 ( DC_WINBUF_BD_UFLOW_STATUS ) ,
DEBUGFS_REG32 ( DC_WINBUF_CD_UFLOW_STATUS ) ,
} ;
2017-11-08 13:32:05 +01:00
2017-11-08 13:40:54 +01:00
static int tegra_dc_show_regs ( struct seq_file * s , void * data )
2017-11-08 13:32:05 +01:00
{
2017-11-08 13:40:54 +01:00
struct drm_info_node * node = s - > private ;
struct tegra_dc * dc = node - > info_ent - > data ;
unsigned int i ;
int err = 0 ;
2017-11-08 13:32:05 +01:00
2017-11-08 13:40:54 +01:00
drm_modeset_lock ( & dc - > base . mutex , NULL ) ;
2017-11-08 13:32:05 +01:00
2017-11-08 13:40:54 +01:00
if ( ! dc - > base . state - > active ) {
err = - EBUSY ;
goto unlock ;
}
2017-11-08 13:32:05 +01:00
2017-11-08 13:40:54 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( tegra_dc_regs ) ; i + + ) {
unsigned int offset = tegra_dc_regs [ i ] . offset ;
2017-11-08 13:32:05 +01:00
2017-11-08 13:40:54 +01:00
seq_printf ( s , " %-40s %#05x %08x \n " , tegra_dc_regs [ i ] . name ,
offset , tegra_dc_readl ( dc , offset ) ) ;
}
unlock :
drm_modeset_unlock ( & dc - > base . mutex ) ;
return err ;
2017-11-08 13:32:05 +01:00
}
2017-11-08 13:40:54 +01:00
static int tegra_dc_show_crc ( struct seq_file * s , void * data )
2017-11-08 13:32:05 +01:00
{
2017-11-08 13:40:54 +01:00
struct drm_info_node * node = s - > private ;
struct tegra_dc * dc = node - > info_ent - > data ;
int err = 0 ;
u32 value ;
drm_modeset_lock ( & dc - > base . mutex , NULL ) ;
if ( ! dc - > base . state - > active ) {
err = - EBUSY ;
goto unlock ;
}
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 ) ;
unlock :
drm_modeset_unlock ( & dc - > base . mutex ) ;
return err ;
}
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 ) ;
return 0 ;
}
static struct drm_info_list debugfs_files [ ] = {
{ " regs " , tegra_dc_show_regs , 0 , NULL } ,
{ " crc " , tegra_dc_show_crc , 0 , NULL } ,
{ " stats " , tegra_dc_show_stats , 0 , NULL } ,
} ;
static int tegra_dc_late_register ( struct drm_crtc * crtc )
{
unsigned int i , count = ARRAY_SIZE ( debugfs_files ) ;
struct drm_minor * minor = crtc - > dev - > primary ;
2017-12-18 14:44:46 +01:00
struct dentry * root ;
2017-11-08 13:40:54 +01:00
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
int err ;
2017-12-18 14:44:46 +01:00
# ifdef CONFIG_DEBUG_FS
root = crtc - > debugfs_entry ;
# else
root = NULL ;
# endif
2017-11-08 13:40:54 +01:00
dc - > debugfs_files = kmemdup ( debugfs_files , sizeof ( debugfs_files ) ,
GFP_KERNEL ) ;
if ( ! dc - > debugfs_files )
return - ENOMEM ;
for ( i = 0 ; i < count ; i + + )
dc - > debugfs_files [ i ] . data = dc ;
err = drm_debugfs_create_files ( dc - > debugfs_files , count , root , minor ) ;
if ( err < 0 )
goto free ;
return 0 ;
free :
kfree ( dc - > debugfs_files ) ;
dc - > debugfs_files = NULL ;
return err ;
}
static void tegra_dc_early_unregister ( struct drm_crtc * crtc )
{
unsigned int count = ARRAY_SIZE ( debugfs_files ) ;
struct drm_minor * minor = crtc - > dev - > primary ;
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
drm_debugfs_remove_files ( dc - > debugfs_files , count , minor ) ;
kfree ( dc - > debugfs_files ) ;
dc - > debugfs_files = NULL ;
}
static u32 tegra_dc_get_vblank_counter ( struct drm_crtc * crtc )
{
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2017-08-30 17:42:54 +02:00
/* XXX vblank syncpoints don't work with nvdisplay yet */
if ( dc - > syncpt & & ! dc - > soc - > has_nvdisplay )
2017-11-08 13:40:54 +01:00
return host1x_syncpt_read ( dc - > syncpt ) ;
/* fallback to software emulated VBLANK counter */
2018-02-02 21:12:58 -08:00
return ( u32 ) drm_crtc_vblank_count ( & dc - > base ) ;
2017-11-08 13:40:54 +01:00
}
static int tegra_dc_enable_vblank ( struct drm_crtc * crtc )
{
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2017-12-14 13:50:19 +01:00
u32 value ;
2017-11-08 13:40:54 +01:00
value = tegra_dc_readl ( dc , DC_CMD_INT_MASK ) ;
value | = VBLANK_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_MASK ) ;
return 0 ;
}
static void tegra_dc_disable_vblank ( struct drm_crtc * crtc )
{
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2017-12-14 13:50:19 +01:00
u32 value ;
2017-11-08 13:32:05 +01:00
value = tegra_dc_readl ( dc , DC_CMD_INT_MASK ) ;
value & = ~ VBLANK_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_MASK ) ;
}
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-11-08 13:40:54 +01:00
. late_register = tegra_dc_late_register ,
. early_unregister = tegra_dc_early_unregister ,
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 ;
2017-08-30 17:42:54 +02:00
if ( ! dc - > soc - > has_nvdisplay ) {
tegra_dc_writel ( dc , 0x0 , DC_DISP_DISP_TIMING_OPTIONS ) ;
2012-11-15 21:28:22 +00:00
2017-08-30 17:42:54 +02:00
value = ( v_ref_to_sync < < 16 ) | h_ref_to_sync ;
tegra_dc_writel ( dc , value , DC_DISP_REF_TO_SYNC ) ;
}
2012-11-15 21:28:22 +00:00
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 ) ;
2017-08-30 17:42:54 +02:00
if ( ! dc - > soc - > has_nvdisplay ) {
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 )
2017-11-08 13:40:54 +01:00
{
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
u32 value ;
2019-12-02 15:29:03 +01:00
int err ;
2015-08-03 13:16:26 +02:00
2017-11-08 13:40:54 +01:00
if ( ! tegra_dc_idle ( dc ) ) {
tegra_dc_stop ( dc ) ;
2015-08-03 13:16:26 +02:00
2017-11-08 13:40:54 +01:00
/*
* Ignore the return value , there isn ' t anything useful to do
* in case this fails .
*/
tegra_dc_wait_idle ( dc , 100 ) ;
2015-08-03 13:16:26 +02:00
}
2012-11-15 21:28:22 +00:00
2017-11-08 13:40:54 +01:00
/*
* 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 ) ;
2017-11-10 12:13:22 +01:00
}
2012-11-15 21:28:22 +00:00
2017-11-08 13:40:54 +01:00
tegra_dc_stats_reset ( & dc - > stats ) ;
drm_crtc_vblank_off ( crtc ) ;
2017-10-12 17:40:46 +02:00
spin_lock_irq ( & crtc - > dev - > event_lock ) ;
if ( crtc - > state - > event ) {
drm_crtc_send_vblank_event ( crtc , crtc - > state - > event ) ;
crtc - > state - > event = NULL ;
}
spin_unlock_irq ( & crtc - > dev - > event_lock ) ;
2019-12-02 15:29:03 +01:00
err = host1x_client_suspend ( & dc - > client ) ;
if ( err < 0 )
dev_err ( dc - > dev , " failed to suspend: %d \n " , err ) ;
2012-11-15 21:28:22 +00:00
}
2017-11-08 13:40:54 +01:00
static void tegra_crtc_atomic_enable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2015-04-01 14:59:40 +02:00
{
2017-11-08 13:40:54 +01:00
struct drm_display_mode * mode = & crtc - > state - > adjusted_mode ;
struct tegra_dc_state * state = to_dc_state ( crtc - > state ) ;
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2015-04-01 14:59:40 +02:00
u32 value ;
2019-12-02 15:29:03 +01:00
int err ;
2015-04-01 14:59:40 +02:00
2019-12-02 15:29:03 +01:00
err = host1x_client_resume ( & dc - > client ) ;
if ( err < 0 ) {
dev_err ( dc - > dev , " failed to resume: %d \n " , err ) ;
return ;
}
2015-08-03 13:16:26 +02:00
2017-11-08 13:40:54 +01:00
/* initialize display controller */
if ( dc - > syncpt ) {
2017-08-30 17:42:54 +02:00
u32 syncpt = host1x_syncpt_id ( dc - > syncpt ) , enable ;
if ( dc - > soc - > has_nvdisplay )
enable = 1 < < 31 ;
else
enable = 1 < < 8 ;
2017-11-08 13:40:54 +01:00
value = SYNCPT_CNTRL_NO_STALL ;
tegra_dc_writel ( dc , value , DC_CMD_GENERAL_INCR_SYNCPT_CNTRL ) ;
2017-08-30 17:42:54 +02:00
value = enable | syncpt ;
2017-11-08 13:40:54 +01:00
tegra_dc_writel ( dc , value , DC_CMD_CONT_SYNCPT_VSYNC ) ;
2015-08-03 13:16:26 +02:00
}
2017-08-30 17:42:54 +02:00
if ( dc - > soc - > has_nvdisplay ) {
value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
DSC_OBUF_UF_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_TYPE ) ;
2015-04-01 14:59:40 +02:00
2017-08-30 17:42:54 +02:00
value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
VBLANK_INT | FRAME_END_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_POLARITY ) ;
2015-04-01 14:59:40 +02:00
2017-08-30 17:42:54 +02:00
value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
FRAME_END_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_ENABLE ) ;
2015-04-01 14:59:40 +02:00
2017-08-30 17:42:54 +02:00
value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT ;
tegra_dc_writel ( dc , value , DC_CMD_INT_MASK ) ;
2017-11-08 13:40:54 +01:00
2017-08-30 17:42:54 +02:00
tegra_dc_writel ( dc , READ_MUX , DC_CMD_STATE_ACCESS ) ;
} else {
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 ) ;
}
2017-11-08 13:40:54 +01:00
2017-11-13 11:20:48 +01:00
if ( dc - > soc - > supports_background_color )
tegra_dc_writel ( dc , 0 , DC_DISP_BLEND_BACKGROUND_COLOR ) ;
else
2017-11-08 13:40:54 +01:00
tegra_dc_writel ( dc , 0 , DC_DISP_BORDER_COLOR ) ;
/* apply PLL and pixel clock changes */
tegra_dc_commit_state ( dc , state ) ;
/* program display mode */
tegra_dc_set_timings ( dc , mode ) ;
/* 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 ) ;
}
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 ) ;
2017-08-30 17:42:54 +02:00
if ( ! dc - > soc - > has_nvdisplay ) {
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 ) ;
}
/* enable underflow reporting and display red for missing pixels */
if ( dc - > soc - > has_nvdisplay ) {
value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE ;
tegra_dc_writel ( dc , value , DC_COM_RG_UNDERFLOW ) ;
}
2017-11-08 13:40:54 +01:00
tegra_dc_commit ( dc ) ;
drm_crtc_vblank_on ( crtc ) ;
2015-04-01 14:59:40 +02:00
}
2017-11-08 13:40:54 +01:00
static void tegra_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
{
2017-10-12 17:40:46 +02:00
unsigned long flags ;
2015-07-28 21:27:05 +02:00
2017-11-08 13:40:54 +01:00
if ( crtc - > state - > event ) {
2017-10-12 17:40:46 +02:00
spin_lock_irqsave ( & crtc - > dev - > event_lock , flags ) ;
if ( drm_crtc_vblank_get ( crtc ) ! = 0 )
drm_crtc_send_vblank_event ( crtc , crtc - > state - > event ) ;
else
drm_crtc_arm_vblank_event ( crtc , crtc - > state - > event ) ;
2017-11-08 13:40:54 +01:00
2017-10-12 17:40:46 +02:00
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
2017-11-08 13:40:54 +01:00
crtc - > state - > event = NULL ;
}
2012-11-15 21:28:22 +00:00
}
2017-11-08 13:40:54 +01:00
static void tegra_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
{
struct tegra_dc_state * state = to_dc_state ( crtc - > state ) ;
struct tegra_dc * dc = to_tegra_dc ( crtc ) ;
2017-08-30 17:42:54 +02:00
u32 value ;
value = state - > planes < < 8 | GENERAL_UPDATE ;
tegra_dc_writel ( dc , value , DC_CMD_STATE_CONTROL ) ;
value = tegra_dc_readl ( dc , DC_CMD_STATE_CONTROL ) ;
2017-11-08 13:40:54 +01:00
2017-08-30 17:42:54 +02:00
value = state - > planes | GENERAL_ACT_REQ ;
tegra_dc_writel ( dc , value , DC_CMD_STATE_CONTROL ) ;
value = tegra_dc_readl ( dc , DC_CMD_STATE_CONTROL ) ;
2017-11-08 13:40:54 +01:00
}
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
. atomic_begin = tegra_crtc_atomic_begin ,
. atomic_flush = tegra_crtc_atomic_flush ,
. atomic_enable = tegra_crtc_atomic_enable ,
. atomic_disable = tegra_crtc_atomic_disable ,
2012-11-15 21:28:22 +00:00
} ;
2017-11-08 13:40:54 +01:00
static irqreturn_t tegra_dc_irq ( int irq , void * data )
2012-11-15 21:28:22 +00:00
{
2017-11-08 13:40:54 +01:00
struct tegra_dc * dc = data ;
unsigned long status ;
2012-11-15 21:28:22 +00:00
2017-11-08 13:40:54 +01:00
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__ ) ;
*/
dc - > stats . frames + + ;
}
2012-11-15 21:28:22 +00:00
2017-11-08 13:40:54 +01:00
if ( status & VBLANK_INT ) {
/*
dev_dbg ( dc - > dev , " %s(): vertical blank \n " , __func__ ) ;
*/
drm_crtc_handle_vblank ( & dc - > base ) ;
dc - > stats . vblank + + ;
}
2012-11-15 21:28:22 +00:00
2017-11-08 13:40:54 +01:00
if ( status & ( WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT ) ) {
/*
dev_dbg ( dc - > dev , " %s(): underflow \n " , __func__ ) ;
*/
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 + + ;
}
2017-08-30 17:42:54 +02:00
if ( status & HEAD_UF_INT ) {
dev_dbg_ratelimited ( dc - > dev , " %s(): head underflow \n " , __func__ ) ;
dc - > stats . underflow + + ;
}
2017-11-08 13:40:54 +01:00
return IRQ_HANDLED ;
2012-11-15 21:28:22 +00:00
}
2018-11-29 17:24:35 +01:00
static bool tegra_dc_has_window_groups ( struct tegra_dc * dc )
{
unsigned int i ;
if ( ! dc - > soc - > wgrps )
return true ;
for ( i = 0 ; i < dc - > soc - > num_wgrps ; i + + ) {
const struct tegra_windowgroup_soc * wgrp = & dc - > soc - > wgrps [ i ] ;
if ( wgrp - > dc = = dc - > pipe & & wgrp - > num_windows > 0 )
return true ;
}
return false ;
}
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
{
2019-12-02 10:51:58 +01:00
struct drm_device * drm = dev_get_drvdata ( client - > host ) ;
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 ;
2018-09-21 12:27:42 +02:00
/*
* XXX do not register DCs with no window groups because we cannot
* assign a primary plane to them , which in turn will cause KMS to
* crash .
*/
2018-11-29 17:24:35 +01:00
if ( ! tegra_dc_has_window_groups ( dc ) )
return 0 ;
2018-09-21 12:27:42 +02:00
2019-12-02 15:29:03 +01:00
/*
* Set the display hub as the host1x client parent for the display
* controller . This is needed for the runtime reference counting that
* ensures the display hub is always powered when any of the display
* controllers are .
*/
if ( dc - > soc - > has_nvdisplay )
client - > parent = & tegra - > hub - > client ;
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 " ) ;
2019-10-28 13:37:08 +01:00
err = host1x_client_iommu_attach ( client ) ;
2019-12-03 17:19:12 +01:00
if ( err < 0 & & err ! = - ENODEV ) {
2018-05-04 15:02:24 +02:00
dev_err ( client - > dev , " failed to attach to domain: %d \n " , err ) ;
return err ;
2014-06-26 21:41:53 +02:00
}
2017-08-30 17:42:54 +02:00
if ( dc - > soc - > wgrps )
primary = tegra_dc_add_shared_planes ( drm , dc ) ;
else
primary = tegra_dc_add_planes ( drm , dc ) ;
2014-10-21 13:51:53 +02:00
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 ;
}
2018-03-15 04:00:25 +03:00
} else {
/* dedicate one overlay to mouse cursor */
cursor = tegra_dc_overlay_plane_create ( drm , dc , 2 , true ) ;
if ( IS_ERR ( cursor ) ) {
err = PTR_ERR ( cursor ) ;
goto cleanup ;
}
2014-10-21 13:51:53 +02:00
}
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
}
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
}
2019-09-09 14:25:45 +02:00
/*
* Inherit the DMA parameters ( such as maximum segment size ) from the
2019-12-02 10:51:58 +01:00
* parent host1x device .
2019-09-09 14:25:45 +02:00
*/
2019-12-02 10:51:58 +01:00
client - > dev - > dma_parms = client - > host - > dma_parms ;
2019-09-09 14:25:45 +02:00
2012-11-15 21:28:22 +00:00
return 0 ;
2014-10-21 13:51:53 +02:00
cleanup :
2017-08-30 17:42:54 +02:00
if ( ! IS_ERR_OR_NULL ( cursor ) )
2014-10-21 13:51:53 +02:00
drm_plane_cleanup ( cursor ) ;
2017-08-30 17:42:54 +02:00
if ( ! IS_ERR ( primary ) )
2014-10-21 13:51:53 +02:00
drm_plane_cleanup ( primary ) ;
2019-02-08 14:35:13 +01:00
host1x_client_iommu_detach ( client ) ;
2018-05-04 15:00:54 +02:00
host1x_syncpt_free ( dc - > syncpt ) ;
2014-10-21 13:51:53 +02:00
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 ;
2018-11-29 17:24:35 +01:00
if ( ! tegra_dc_has_window_groups ( dc ) )
return 0 ;
2019-09-09 14:25:45 +02:00
/* avoid a dangling pointer just in case this disappears */
client - > dev - > dma_parms = NULL ;
2012-11-15 21:28:22 +00:00
devm_free_irq ( dc - > dev , dc - > irq , dc ) ;
err = tegra_dc_rgb_exit ( dc ) ;
if ( err ) {
dev_err ( dc - > dev , " failed to shutdown RGB output: %d \n " , err ) ;
return err ;
}
2019-02-08 14:35:13 +01:00
host1x_client_iommu_detach ( client ) ;
2015-08-24 14:47:10 +02:00
host1x_syncpt_free ( dc - > syncpt ) ;
2012-11-15 21:28:22 +00:00
return 0 ;
}
2019-12-02 15:29:03 +01:00
static int tegra_dc_runtime_suspend ( struct host1x_client * client )
{
struct tegra_dc * dc = host1x_client_to_dc ( client ) ;
struct device * dev = client - > dev ;
int err ;
err = reset_control_assert ( dc - > rst ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to assert reset: %d \n " , err ) ;
return err ;
}
if ( dc - > soc - > has_powergate )
tegra_powergate_power_off ( dc - > powergate ) ;
clk_disable_unprepare ( dc - > clk ) ;
pm_runtime_put_sync ( dev ) ;
return 0 ;
}
static int tegra_dc_runtime_resume ( struct host1x_client * client )
{
struct tegra_dc * dc = host1x_client_to_dc ( client ) ;
struct device * dev = client - > dev ;
int err ;
err = pm_runtime_get_sync ( dev ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to get runtime PM: %d \n " , err ) ;
return 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 ) ;
goto put_rpm ;
}
} else {
err = clk_prepare_enable ( dc - > clk ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to enable clock: %d \n " , err ) ;
goto put_rpm ;
}
err = reset_control_deassert ( dc - > rst ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to deassert reset: %d \n " , err ) ;
goto disable_clk ;
}
}
return 0 ;
disable_clk :
clk_disable_unprepare ( dc - > clk ) ;
put_rpm :
pm_runtime_put_sync ( dev ) ;
return err ;
}
2012-11-15 21:28:22 +00:00
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 ,
2019-12-02 15:29:03 +01:00
. suspend = tegra_dc_runtime_suspend ,
. resume = tegra_dc_runtime_resume ,
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 = {
2017-11-13 11:20:48 +01:00
. supports_background_color = false ,
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 ,
2018-05-04 17:40:00 +03:00
. has_legacy_blending = true ,
2014-07-11 08:29:14 +02:00
. pitch_align = 8 ,
2014-07-07 15:32:53 +02:00
. has_powergate = false ,
2017-12-20 18:46:10 +03:00
. coupled_pm = true ,
2017-08-30 17:42:54 +02:00
. has_nvdisplay = false ,
2017-11-14 16:07:40 +01:00
. num_primary_formats = ARRAY_SIZE ( tegra20_primary_formats ) ,
. primary_formats = tegra20_primary_formats ,
. num_overlay_formats = ARRAY_SIZE ( tegra20_overlay_formats ) ,
. overlay_formats = tegra20_overlay_formats ,
2018-03-15 16:44:04 +01:00
. modifiers = tegra20_modifiers ,
2018-05-04 17:39:58 +03:00
. has_win_a_without_filters = true ,
. has_win_c_without_vert_filter = true ,
2013-12-12 11:03:59 +01:00
} ;
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
2017-11-13 11:20:48 +01:00
. supports_background_color = false ,
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 ,
2018-05-04 17:40:00 +03:00
. has_legacy_blending = true ,
2014-07-11 08:29:14 +02:00
. pitch_align = 8 ,
2014-07-07 15:32:53 +02:00
. has_powergate = false ,
2017-12-20 18:46:10 +03:00
. coupled_pm = false ,
2017-08-30 17:42:54 +02:00
. has_nvdisplay = false ,
2017-11-14 16:07:40 +01:00
. num_primary_formats = ARRAY_SIZE ( tegra20_primary_formats ) ,
. primary_formats = tegra20_primary_formats ,
. num_overlay_formats = ARRAY_SIZE ( tegra20_overlay_formats ) ,
. overlay_formats = tegra20_overlay_formats ,
2018-03-15 16:44:04 +01:00
. modifiers = tegra20_modifiers ,
2018-05-04 17:39:58 +03:00
. has_win_a_without_filters = false ,
. has_win_c_without_vert_filter = false ,
2014-07-11 08:29:14 +02:00
} ;
static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
2017-11-13 11:20:48 +01:00
. supports_background_color = false ,
2014-07-11 08:29:14 +02:00
. supports_interlacing = false ,
. supports_cursor = false ,
. supports_block_linear = false ,
2018-05-04 17:40:00 +03:00
. has_legacy_blending = true ,
2014-07-11 08:29:14 +02:00
. pitch_align = 64 ,
2014-07-07 15:32:53 +02:00
. has_powergate = true ,
2017-12-20 18:46:10 +03:00
. coupled_pm = false ,
2017-08-30 17:42:54 +02:00
. has_nvdisplay = false ,
2017-11-14 16:07:40 +01:00
. num_primary_formats = ARRAY_SIZE ( tegra114_primary_formats ) ,
. primary_formats = tegra114_primary_formats ,
. num_overlay_formats = ARRAY_SIZE ( tegra114_overlay_formats ) ,
. overlay_formats = tegra114_overlay_formats ,
2018-03-15 16:44:04 +01:00
. modifiers = tegra20_modifiers ,
2018-05-04 17:39:58 +03:00
. has_win_a_without_filters = false ,
. has_win_c_without_vert_filter = false ,
2013-12-12 11:03:59 +01:00
} ;
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
2017-11-13 11:20:48 +01:00
. supports_background_color = true ,
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 ,
2018-05-04 17:40:00 +03:00
. has_legacy_blending = false ,
2014-07-11 08:29:14 +02:00
. pitch_align = 64 ,
2014-07-07 15:32:53 +02:00
. has_powergate = true ,
2017-12-20 18:46:10 +03:00
. coupled_pm = false ,
2017-08-30 17:42:54 +02:00
. has_nvdisplay = false ,
2017-11-14 16:07:40 +01:00
. num_primary_formats = ARRAY_SIZE ( tegra124_primary_formats ) ,
2018-03-20 10:11:44 +01:00
. primary_formats = tegra124_primary_formats ,
2017-11-14 16:07:40 +01:00
. num_overlay_formats = ARRAY_SIZE ( tegra124_overlay_formats ) ,
2018-03-20 10:11:44 +01:00
. overlay_formats = tegra124_overlay_formats ,
2018-03-15 16:44:04 +01:00
. modifiers = tegra124_modifiers ,
2018-05-04 17:39:58 +03:00
. has_win_a_without_filters = false ,
. has_win_c_without_vert_filter = 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 = {
2017-11-13 11:20:48 +01:00
. supports_background_color = true ,
2015-03-27 10:31:58 +01:00
. supports_interlacing = true ,
. supports_cursor = true ,
. supports_block_linear = true ,
2018-05-04 17:40:00 +03:00
. has_legacy_blending = false ,
2015-03-27 10:31:58 +01:00
. pitch_align = 64 ,
. has_powergate = true ,
2017-12-20 18:46:10 +03:00
. coupled_pm = false ,
2017-08-30 17:42:54 +02:00
. has_nvdisplay = false ,
2017-11-14 16:07:40 +01:00
. num_primary_formats = ARRAY_SIZE ( tegra114_primary_formats ) ,
. primary_formats = tegra114_primary_formats ,
. num_overlay_formats = ARRAY_SIZE ( tegra114_overlay_formats ) ,
. overlay_formats = tegra114_overlay_formats ,
2018-03-15 16:44:04 +01:00
. modifiers = tegra124_modifiers ,
2018-05-04 17:39:58 +03:00
. has_win_a_without_filters = false ,
. has_win_c_without_vert_filter = false ,
2017-08-30 17:42:54 +02:00
} ;
static const struct tegra_windowgroup_soc tegra186_dc_wgrps [ ] = {
{
. index = 0 ,
. dc = 0 ,
. windows = ( const unsigned int [ ] ) { 0 } ,
. num_windows = 1 ,
} , {
. index = 1 ,
. dc = 1 ,
. windows = ( const unsigned int [ ] ) { 1 } ,
. num_windows = 1 ,
} , {
. index = 2 ,
. dc = 1 ,
. windows = ( const unsigned int [ ] ) { 2 } ,
. num_windows = 1 ,
} , {
. index = 3 ,
. dc = 2 ,
. windows = ( const unsigned int [ ] ) { 3 } ,
. num_windows = 1 ,
} , {
. index = 4 ,
. dc = 2 ,
. windows = ( const unsigned int [ ] ) { 4 } ,
. num_windows = 1 ,
} , {
. index = 5 ,
. dc = 2 ,
. windows = ( const unsigned int [ ] ) { 5 } ,
. num_windows = 1 ,
} ,
} ;
static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
. supports_background_color = true ,
. supports_interlacing = true ,
. supports_cursor = true ,
. supports_block_linear = true ,
2018-05-04 17:40:00 +03:00
. has_legacy_blending = false ,
2017-08-30 17:42:54 +02:00
. pitch_align = 64 ,
. has_powergate = false ,
2017-12-20 18:46:10 +03:00
. coupled_pm = false ,
2017-08-30 17:42:54 +02:00
. has_nvdisplay = true ,
. wgrps = tegra186_dc_wgrps ,
. num_wgrps = ARRAY_SIZE ( tegra186_dc_wgrps ) ,
2015-03-27 10:31:58 +01:00
} ;
2018-09-21 12:27:44 +02:00
static const struct tegra_windowgroup_soc tegra194_dc_wgrps [ ] = {
{
. index = 0 ,
. dc = 0 ,
. windows = ( const unsigned int [ ] ) { 0 } ,
. num_windows = 1 ,
} , {
. index = 1 ,
. dc = 1 ,
. windows = ( const unsigned int [ ] ) { 1 } ,
. num_windows = 1 ,
} , {
. index = 2 ,
. dc = 1 ,
. windows = ( const unsigned int [ ] ) { 2 } ,
. num_windows = 1 ,
} , {
. index = 3 ,
. dc = 2 ,
. windows = ( const unsigned int [ ] ) { 3 } ,
. num_windows = 1 ,
} , {
. index = 4 ,
. dc = 2 ,
. windows = ( const unsigned int [ ] ) { 4 } ,
. num_windows = 1 ,
} , {
. index = 5 ,
. dc = 2 ,
. windows = ( const unsigned int [ ] ) { 5 } ,
. num_windows = 1 ,
} ,
} ;
static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
. supports_background_color = true ,
. supports_interlacing = true ,
. supports_cursor = true ,
. supports_block_linear = true ,
. has_legacy_blending = false ,
. pitch_align = 64 ,
. has_powergate = false ,
. coupled_pm = false ,
. has_nvdisplay = true ,
. wgrps = tegra194_dc_wgrps ,
. num_wgrps = ARRAY_SIZE ( tegra194_dc_wgrps ) ,
} ;
2013-12-12 11:03:59 +01:00
static const struct of_device_id tegra_dc_of_match [ ] = {
{
2018-09-21 12:27:44 +02:00
. compatible = " nvidia,tegra194-dc " ,
. data = & tegra194_dc_soc_info ,
} , {
2017-08-30 17:42:54 +02:00
. compatible = " nvidia,tegra186-dc " ,
. data = & tegra186_dc_soc_info ,
} , {
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 ;
}
2019-06-14 18:54:00 +01:00
static int tegra_dc_match_by_pipe ( struct device * dev , const void * data )
2017-12-20 18:46:10 +03:00
{
struct tegra_dc * dc = dev_get_drvdata ( dev ) ;
2019-06-14 18:54:00 +01:00
unsigned int pipe = ( unsigned long ) ( void * ) data ;
2017-12-20 18:46:10 +03:00
return dc - > pipe = = pipe ;
}
static int tegra_dc_couple ( struct tegra_dc * dc )
{
/*
* On Tegra20 , DC1 requires DC0 to be taken out of reset in order to
* be enabled , otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND /
* POWER_CONTROL registers during CRTC enabling .
*/
if ( dc - > soc - > coupled_pm & & dc - > pipe = = 1 ) {
2018-06-27 18:20:55 +05:30
u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER ;
2017-12-20 18:46:10 +03:00
struct device_link * link ;
struct device * partner ;
2018-03-28 12:52:10 +00:00
partner = driver_find_device ( dc - > dev - > driver , NULL , NULL ,
2017-12-20 18:46:10 +03:00
tegra_dc_match_by_pipe ) ;
if ( ! partner )
return - EPROBE_DEFER ;
link = device_link_add ( dc - > dev , partner , flags ) ;
if ( ! link ) {
dev_err ( dc - > dev , " failed to link controllers \n " ) ;
return - EINVAL ;
}
dev_dbg ( dc - > dev , " coupled to %s \n " , dev_name ( partner ) ) ;
}
return 0 ;
}
2012-11-15 21:28:22 +00:00
static int tegra_dc_probe ( struct platform_device * pdev )
{
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-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 ;
2017-12-20 18:46:10 +03:00
err = tegra_dc_couple ( 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 */
2017-12-20 18:46:10 +03:00
err = clk_prepare_enable ( dc - > clk ) ;
if ( err < 0 )
return err ;
2017-08-30 17:41:00 +02:00
2017-12-20 18:46:10 +03:00
usleep_range ( 2000 , 4000 ) ;
2017-08-30 17:41:00 +02:00
2017-12-20 18:46:10 +03:00
err = reset_control_assert ( dc - > rst ) ;
if ( err < 0 )
return err ;
2017-08-30 17:41:00 +02:00
2017-12-20 18:46:10 +03:00
usleep_range ( 2000 , 4000 ) ;
2017-08-30 17:41:00 +02:00
2017-12-20 18:46:10 +03:00
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
2020-03-09 01:38:07 +03:00
dc - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-01-21 11:09:02 +01:00
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 ) {
2020-03-09 01:38:09 +03:00
const char * level = KERN_ERR ;
if ( err = = - EPROBE_DEFER )
level = KERN_DEBUG ;
dev_printk ( level , dc - > dev , " failed to probe RGB output: %d \n " ,
err ) ;
2012-11-15 21:28:22 +00:00
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 ) ;
2020-03-09 01:38:08 +03:00
goto disable_pm ;
2012-11-15 21:28:22 +00:00
}
return 0 ;
2020-03-09 01:38:08 +03:00
disable_pm :
pm_runtime_disable ( & pdev - > dev ) ;
tegra_dc_rgb_remove ( dc ) ;
return err ;
2012-11-15 21:28:22 +00:00
}
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 ;
}
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 ,
} ,
. probe = tegra_dc_probe ,
. remove = tegra_dc_remove ,
} ;