2015-01-06 11:13:28 +01:00
/*
* Copyright ( C ) 2014 Free Electrons
* Copyright ( C ) 2014 Atmel
*
* Author : Boris BREZILLON < boris . brezillon @ free - electrons . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "atmel_hlcdc_dc.h"
2015-02-05 16:32:33 +01:00
/**
* Atmel HLCDC Plane state structure .
*
* @ base : DRM plane state
* @ crtc_x : x position of the plane relative to the CRTC
* @ crtc_y : y position of the plane relative to the CRTC
* @ crtc_w : visible width of the plane
* @ crtc_h : visible height of the plane
* @ src_x : x buffer position
* @ src_y : y buffer position
* @ src_w : buffer width
* @ src_h : buffer height
* @ alpha : alpha blending of the plane
* @ bpp : bytes per pixel deduced from pixel_format
* @ offsets : offsets to apply to the GEM buffers
* @ xstride : value to add to the pixel pointer between each line
* @ pstride : value to add to the pixel pointer between each pixel
* @ nplanes : number of planes ( deduced from pixel_format )
*/
struct atmel_hlcdc_plane_state {
struct drm_plane_state base ;
int crtc_x ;
int crtc_y ;
unsigned int crtc_w ;
unsigned int crtc_h ;
uint32_t src_x ;
uint32_t src_y ;
uint32_t src_w ;
uint32_t src_h ;
u8 alpha ;
2015-02-06 16:25:06 +01:00
bool disc_updated ;
int disc_x ;
int disc_y ;
int disc_w ;
int disc_h ;
2015-02-05 16:32:33 +01:00
/* These fields are private and should not be touched */
int bpp [ ATMEL_HLCDC_MAX_PLANES ] ;
unsigned int offsets [ ATMEL_HLCDC_MAX_PLANES ] ;
int xstride [ ATMEL_HLCDC_MAX_PLANES ] ;
int pstride [ ATMEL_HLCDC_MAX_PLANES ] ;
int nplanes ;
} ;
static inline struct atmel_hlcdc_plane_state *
drm_plane_state_to_atmel_hlcdc_plane_state ( struct drm_plane_state * s )
{
return container_of ( s , struct atmel_hlcdc_plane_state , base ) ;
}
2015-01-06 11:13:28 +01:00
# define SUBPIXEL_MASK 0xffff
static uint32_t rgb_formats [ ] = {
DRM_FORMAT_XRGB4444 ,
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_RGBA4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGB888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_ARGB8888 ,
DRM_FORMAT_RGBA8888 ,
} ;
struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
. formats = rgb_formats ,
. nformats = ARRAY_SIZE ( rgb_formats ) ,
} ;
static uint32_t rgb_and_yuv_formats [ ] = {
DRM_FORMAT_XRGB4444 ,
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_RGBA4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGB888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_ARGB8888 ,
DRM_FORMAT_RGBA8888 ,
DRM_FORMAT_AYUV ,
DRM_FORMAT_YUYV ,
DRM_FORMAT_UYVY ,
DRM_FORMAT_YVYU ,
DRM_FORMAT_VYUY ,
DRM_FORMAT_NV21 ,
DRM_FORMAT_NV61 ,
DRM_FORMAT_YUV422 ,
DRM_FORMAT_YUV420 ,
} ;
struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
. formats = rgb_and_yuv_formats ,
. nformats = ARRAY_SIZE ( rgb_and_yuv_formats ) ,
} ;
static int atmel_hlcdc_format_to_plane_mode ( u32 format , u32 * mode )
{
switch ( format ) {
case DRM_FORMAT_XRGB4444 :
* mode = ATMEL_HLCDC_XRGB4444_MODE ;
break ;
case DRM_FORMAT_ARGB4444 :
* mode = ATMEL_HLCDC_ARGB4444_MODE ;
break ;
case DRM_FORMAT_RGBA4444 :
* mode = ATMEL_HLCDC_RGBA4444_MODE ;
break ;
case DRM_FORMAT_RGB565 :
* mode = ATMEL_HLCDC_RGB565_MODE ;
break ;
case DRM_FORMAT_RGB888 :
* mode = ATMEL_HLCDC_RGB888_MODE ;
break ;
case DRM_FORMAT_ARGB1555 :
* mode = ATMEL_HLCDC_ARGB1555_MODE ;
break ;
case DRM_FORMAT_XRGB8888 :
* mode = ATMEL_HLCDC_XRGB8888_MODE ;
break ;
case DRM_FORMAT_ARGB8888 :
* mode = ATMEL_HLCDC_ARGB8888_MODE ;
break ;
case DRM_FORMAT_RGBA8888 :
* mode = ATMEL_HLCDC_RGBA8888_MODE ;
break ;
case DRM_FORMAT_AYUV :
* mode = ATMEL_HLCDC_AYUV_MODE ;
break ;
case DRM_FORMAT_YUYV :
* mode = ATMEL_HLCDC_YUYV_MODE ;
break ;
case DRM_FORMAT_UYVY :
* mode = ATMEL_HLCDC_UYVY_MODE ;
break ;
case DRM_FORMAT_YVYU :
* mode = ATMEL_HLCDC_YVYU_MODE ;
break ;
case DRM_FORMAT_VYUY :
* mode = ATMEL_HLCDC_VYUY_MODE ;
break ;
case DRM_FORMAT_NV21 :
* mode = ATMEL_HLCDC_NV21_MODE ;
break ;
case DRM_FORMAT_NV61 :
* mode = ATMEL_HLCDC_NV61_MODE ;
break ;
case DRM_FORMAT_YUV420 :
* mode = ATMEL_HLCDC_YUV420_MODE ;
break ;
case DRM_FORMAT_YUV422 :
* mode = ATMEL_HLCDC_YUV422_MODE ;
break ;
default :
return - ENOTSUPP ;
}
return 0 ;
}
2015-02-05 16:32:33 +01:00
static bool atmel_hlcdc_format_embeds_alpha ( u32 format )
2015-01-06 11:13:28 +01:00
{
int i ;
for ( i = 0 ; i < sizeof ( format ) ; i + + ) {
char tmp = ( format > > ( 8 * i ) ) & 0xff ;
if ( tmp = = ' A ' )
return true ;
}
return false ;
}
static u32 heo_downscaling_xcoef [ ] = {
0x11343311 ,
0x000000f7 ,
0x1635300c ,
0x000000f9 ,
0x1b362c08 ,
0x000000fb ,
0x1f372804 ,
0x000000fe ,
0x24382400 ,
0x00000000 ,
0x28371ffe ,
0x00000004 ,
0x2c361bfb ,
0x00000008 ,
0x303516f9 ,
0x0000000c ,
} ;
static u32 heo_downscaling_ycoef [ ] = {
0x00123737 ,
0x00173732 ,
0x001b382d ,
0x001f3928 ,
0x00243824 ,
0x0028391f ,
0x002d381b ,
0x00323717 ,
} ;
static u32 heo_upscaling_xcoef [ ] = {
0xf74949f7 ,
0x00000000 ,
0xf55f33fb ,
0x000000fe ,
0xf5701efe ,
0x000000ff ,
0xf87c0dff ,
0x00000000 ,
0x00800000 ,
0x00000000 ,
0x0d7cf800 ,
0x000000ff ,
0x1e70f5ff ,
0x000000fe ,
0x335ff5fe ,
0x000000fb ,
} ;
static u32 heo_upscaling_ycoef [ ] = {
0x00004040 ,
0x00075920 ,
0x00056f0c ,
0x00027b03 ,
0x00008000 ,
0x00037b02 ,
0x000c6f05 ,
0x00205907 ,
} ;
static void
atmel_hlcdc_plane_update_pos_and_size ( struct atmel_hlcdc_plane * plane ,
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane_state * state )
2015-01-06 11:13:28 +01:00
{
const struct atmel_hlcdc_layer_cfg_layout * layout =
& plane - > layer . desc - > layout ;
if ( layout - > size )
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > size ,
0xffffffff ,
2015-02-05 16:32:33 +01:00
( state - > crtc_w - 1 ) |
( ( state - > crtc_h - 1 ) < < 16 ) ) ;
2015-01-06 11:13:28 +01:00
if ( layout - > memsize )
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > memsize ,
0xffffffff ,
2015-02-05 16:32:33 +01:00
( state - > src_w - 1 ) |
( ( state - > src_h - 1 ) < < 16 ) ) ;
2015-01-06 11:13:28 +01:00
if ( layout - > pos )
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > pos ,
0xffffffff ,
2015-02-05 16:32:33 +01:00
state - > crtc_x |
( state - > crtc_y < < 16 ) ) ;
2015-01-06 11:13:28 +01:00
/* TODO: rework the rescaling part */
2015-02-05 16:32:33 +01:00
if ( state - > crtc_w ! = state - > src_w | | state - > crtc_h ! = state - > src_h ) {
2015-01-06 11:13:28 +01:00
u32 factor_reg = 0 ;
2015-02-05 16:32:33 +01:00
if ( state - > crtc_w ! = state - > src_w ) {
2015-01-06 11:13:28 +01:00
int i ;
u32 factor ;
u32 * coeff_tab = heo_upscaling_xcoef ;
u32 max_memsize ;
2015-02-05 16:32:33 +01:00
if ( state - > crtc_w < state - > src_w )
2015-01-06 11:13:28 +01:00
coeff_tab = heo_downscaling_xcoef ;
for ( i = 0 ; i < ARRAY_SIZE ( heo_upscaling_xcoef ) ; i + + )
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
17 + i ,
0xffffffff ,
coeff_tab [ i ] ) ;
2015-02-05 16:32:33 +01:00
factor = ( ( 8 * 256 * state - > src_w ) - ( 256 * 4 ) ) /
state - > crtc_w ;
2015-01-06 11:13:28 +01:00
factor + + ;
2015-02-05 16:32:33 +01:00
max_memsize = ( ( factor * state - > crtc_w ) + ( 256 * 4 ) ) /
2015-01-06 11:13:28 +01:00
2048 ;
2015-02-05 16:32:33 +01:00
if ( max_memsize > state - > src_w )
2015-01-06 11:13:28 +01:00
factor - - ;
factor_reg | = factor | 0x80000000 ;
}
2015-02-05 16:32:33 +01:00
if ( state - > crtc_h ! = state - > src_h ) {
2015-01-06 11:13:28 +01:00
int i ;
u32 factor ;
u32 * coeff_tab = heo_upscaling_ycoef ;
u32 max_memsize ;
2015-02-05 16:32:33 +01:00
if ( state - > crtc_w < state - > src_w )
2015-01-06 11:13:28 +01:00
coeff_tab = heo_downscaling_ycoef ;
for ( i = 0 ; i < ARRAY_SIZE ( heo_upscaling_ycoef ) ; i + + )
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
33 + i ,
0xffffffff ,
coeff_tab [ i ] ) ;
2015-02-05 16:32:33 +01:00
factor = ( ( 8 * 256 * state - > src_w ) - ( 256 * 4 ) ) /
state - > crtc_w ;
2015-01-06 11:13:28 +01:00
factor + + ;
2015-02-05 16:32:33 +01:00
max_memsize = ( ( factor * state - > crtc_w ) + ( 256 * 4 ) ) /
2015-01-06 11:13:28 +01:00
2048 ;
2015-02-05 16:32:33 +01:00
if ( max_memsize > state - > src_w )
2015-01-06 11:13:28 +01:00
factor - - ;
factor_reg | = ( factor < < 16 ) | 0x80000000 ;
}
atmel_hlcdc_layer_update_cfg ( & plane - > layer , 13 , 0xffffffff ,
factor_reg ) ;
}
}
static void
atmel_hlcdc_plane_update_general_settings ( struct atmel_hlcdc_plane * plane ,
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane_state * state )
2015-01-06 11:13:28 +01:00
{
const struct atmel_hlcdc_layer_cfg_layout * layout =
& plane - > layer . desc - > layout ;
unsigned int cfg = ATMEL_HLCDC_LAYER_DMA ;
if ( plane - > base . type ! = DRM_PLANE_TYPE_PRIMARY ) {
cfg | = ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
ATMEL_HLCDC_LAYER_ITER ;
2015-02-05 16:32:33 +01:00
if ( atmel_hlcdc_format_embeds_alpha ( state - > base . fb - > pixel_format ) )
2015-01-06 11:13:28 +01:00
cfg | = ATMEL_HLCDC_LAYER_LAEN ;
else
2015-02-05 16:32:33 +01:00
cfg | = ATMEL_HLCDC_LAYER_GAEN |
ATMEL_HLCDC_LAYER_GA ( state - > alpha ) ;
2015-01-06 11:13:28 +01:00
}
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
ATMEL_HLCDC_LAYER_DMA_CFG_ID ,
ATMEL_HLCDC_LAYER_DMA_BLEN_MASK ,
ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 ) ;
atmel_hlcdc_layer_update_cfg ( & plane - > layer , layout - > general_config ,
ATMEL_HLCDC_LAYER_ITER2BL |
ATMEL_HLCDC_LAYER_ITER |
ATMEL_HLCDC_LAYER_GAEN |
2015-02-05 16:32:33 +01:00
ATMEL_HLCDC_LAYER_GA_MASK |
2015-01-06 11:13:28 +01:00
ATMEL_HLCDC_LAYER_LAEN |
ATMEL_HLCDC_LAYER_OVR |
ATMEL_HLCDC_LAYER_DMA , cfg ) ;
}
static void atmel_hlcdc_plane_update_format ( struct atmel_hlcdc_plane * plane ,
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane_state * state )
2015-01-06 11:13:28 +01:00
{
u32 cfg ;
int ret ;
2015-02-05 16:32:33 +01:00
ret = atmel_hlcdc_format_to_plane_mode ( state - > base . fb - > pixel_format ,
& cfg ) ;
2015-01-06 11:13:28 +01:00
if ( ret )
return ;
2015-02-05 16:32:33 +01:00
if ( ( state - > base . fb - > pixel_format = = DRM_FORMAT_YUV422 | |
state - > base . fb - > pixel_format = = DRM_FORMAT_NV61 ) & &
( state - > base . rotation & ( BIT ( DRM_ROTATE_90 ) | BIT ( DRM_ROTATE_270 ) ) ) )
2015-01-06 11:13:28 +01:00
cfg | = ATMEL_HLCDC_YUV422ROT ;
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
ATMEL_HLCDC_LAYER_FORMAT_CFG_ID ,
0xffffffff ,
cfg ) ;
/*
* Rotation optimization is not working on RGB888 ( rotation is still
* working but without any optimization ) .
*/
2015-02-05 16:32:33 +01:00
if ( state - > base . fb - > pixel_format = = DRM_FORMAT_RGB888 )
2015-01-06 11:13:28 +01:00
cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS ;
else
cfg = 0 ;
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
ATMEL_HLCDC_LAYER_DMA_CFG_ID ,
ATMEL_HLCDC_LAYER_DMA_ROTDIS , cfg ) ;
}
static void atmel_hlcdc_plane_update_buffers ( struct atmel_hlcdc_plane * plane ,
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane_state * state )
2015-01-06 11:13:28 +01:00
{
struct atmel_hlcdc_layer * layer = & plane - > layer ;
const struct atmel_hlcdc_layer_cfg_layout * layout =
& layer - > desc - > layout ;
int i ;
2015-02-05 16:32:33 +01:00
atmel_hlcdc_layer_update_set_fb ( & plane - > layer , state - > base . fb ,
state - > offsets ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
for ( i = 0 ; i < state - > nplanes ; i + + ) {
2015-01-06 11:13:28 +01:00
if ( layout - > xstride [ i ] ) {
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > xstride [ i ] ,
0xffffffff ,
2015-02-05 16:32:33 +01:00
state - > xstride [ i ] ) ;
2015-01-06 11:13:28 +01:00
}
if ( layout - > pstride [ i ] ) {
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > pstride [ i ] ,
0xffffffff ,
2015-02-05 16:32:33 +01:00
state - > pstride [ i ] ) ;
2015-01-06 11:13:28 +01:00
}
}
}
2015-02-06 16:25:06 +01:00
int
atmel_hlcdc_plane_prepare_disc_area ( struct drm_crtc_state * c_state )
{
int disc_x = 0 , disc_y = 0 , disc_w = 0 , disc_h = 0 ;
const struct atmel_hlcdc_layer_cfg_layout * layout ;
struct atmel_hlcdc_plane_state * primary_state ;
struct drm_plane_state * primary_s ;
struct atmel_hlcdc_plane * primary ;
struct drm_plane * ovl ;
primary = drm_plane_to_atmel_hlcdc_plane ( c_state - > crtc - > primary ) ;
layout = & primary - > layer . desc - > layout ;
if ( ! layout - > disc_pos | | ! layout - > disc_size )
return 0 ;
primary_s = drm_atomic_get_plane_state ( c_state - > state ,
& primary - > base ) ;
if ( IS_ERR ( primary_s ) )
return PTR_ERR ( primary_s ) ;
primary_state = drm_plane_state_to_atmel_hlcdc_plane_state ( primary_s ) ;
drm_atomic_crtc_state_for_each_plane ( ovl , c_state ) {
struct atmel_hlcdc_plane_state * ovl_state ;
struct drm_plane_state * ovl_s ;
if ( ovl = = c_state - > crtc - > primary )
continue ;
ovl_s = drm_atomic_get_plane_state ( c_state - > state , ovl ) ;
if ( IS_ERR ( ovl_s ) )
return PTR_ERR ( ovl_s ) ;
ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state ( ovl_s ) ;
if ( ! ovl_s - > fb | |
atmel_hlcdc_format_embeds_alpha ( ovl_s - > fb - > pixel_format ) | |
ovl_state - > alpha ! = 255 )
continue ;
/* TODO: implement a smarter hidden area detection */
if ( ovl_state - > crtc_h * ovl_state - > crtc_w < disc_h * disc_w )
continue ;
disc_x = ovl_state - > crtc_x ;
disc_y = ovl_state - > crtc_y ;
disc_h = ovl_state - > crtc_h ;
disc_w = ovl_state - > crtc_w ;
}
if ( disc_x = = primary_state - > disc_x & &
disc_y = = primary_state - > disc_y & &
disc_w = = primary_state - > disc_w & &
disc_h = = primary_state - > disc_h )
return 0 ;
primary_state - > disc_x = disc_x ;
primary_state - > disc_y = disc_y ;
primary_state - > disc_w = disc_w ;
primary_state - > disc_h = disc_h ;
primary_state - > disc_updated = true ;
return 0 ;
}
static void
atmel_hlcdc_plane_update_disc_area ( struct atmel_hlcdc_plane * plane ,
struct atmel_hlcdc_plane_state * state )
{
const struct atmel_hlcdc_layer_cfg_layout * layout =
& plane - > layer . desc - > layout ;
int disc_surface = 0 ;
if ( ! state - > disc_updated )
return ;
disc_surface = state - > disc_h * state - > disc_w ;
atmel_hlcdc_layer_update_cfg ( & plane - > layer , layout - > general_config ,
ATMEL_HLCDC_LAYER_DISCEN ,
disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0 ) ;
if ( ! disc_surface )
return ;
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > disc_pos ,
0xffffffff ,
state - > disc_x | ( state - > disc_y < < 16 ) ) ;
atmel_hlcdc_layer_update_cfg ( & plane - > layer ,
layout - > disc_size ,
0xffffffff ,
( state - > disc_w - 1 ) |
( ( state - > disc_h - 1 ) < < 16 ) ) ;
}
2015-02-05 16:32:33 +01:00
static int atmel_hlcdc_plane_atomic_check ( struct drm_plane * p ,
struct drm_plane_state * s )
2015-01-06 11:13:28 +01:00
{
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane_state * state =
drm_plane_state_to_atmel_hlcdc_plane_state ( s ) ;
2015-01-06 11:13:28 +01:00
const struct atmel_hlcdc_layer_cfg_layout * layout =
& plane - > layer . desc - > layout ;
2015-02-05 16:32:33 +01:00
struct drm_framebuffer * fb = state - > base . fb ;
const struct drm_display_mode * mode ;
struct drm_crtc_state * crtc_state ;
2015-01-06 11:13:28 +01:00
unsigned int patched_crtc_w ;
unsigned int patched_crtc_h ;
unsigned int patched_src_w ;
unsigned int patched_src_h ;
unsigned int tmp ;
int x_offset = 0 ;
int y_offset = 0 ;
int hsub = 1 ;
int vsub = 1 ;
int i ;
2015-02-05 16:32:33 +01:00
if ( ! state - > base . crtc | | ! fb )
return 0 ;
crtc_state = s - > state - > crtc_states [ drm_crtc_index ( s - > crtc ) ] ;
mode = & crtc_state - > adjusted_mode ;
state - > src_x = s - > src_x ;
state - > src_y = s - > src_y ;
state - > src_h = s - > src_h ;
state - > src_w = s - > src_w ;
state - > crtc_x = s - > crtc_x ;
state - > crtc_y = s - > crtc_y ;
state - > crtc_h = s - > crtc_h ;
state - > crtc_w = s - > crtc_w ;
if ( ( state - > src_x | state - > src_y | state - > src_w | state - > src_h ) &
2015-01-06 11:13:28 +01:00
SUBPIXEL_MASK )
return - EINVAL ;
2015-02-05 16:32:33 +01:00
state - > src_x > > = 16 ;
state - > src_y > > = 16 ;
state - > src_w > > = 16 ;
state - > src_h > > = 16 ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
state - > nplanes = drm_format_num_planes ( fb - > pixel_format ) ;
if ( state - > nplanes > ATMEL_HLCDC_MAX_PLANES )
2015-01-06 11:13:28 +01:00
return - EINVAL ;
/*
* Swap width and size in case of 90 or 270 degrees rotation
*/
2015-02-05 16:32:33 +01:00
if ( state - > base . rotation & ( BIT ( DRM_ROTATE_90 ) | BIT ( DRM_ROTATE_270 ) ) ) {
tmp = state - > crtc_w ;
state - > crtc_w = state - > crtc_h ;
state - > crtc_h = tmp ;
tmp = state - > src_w ;
state - > src_w = state - > src_h ;
state - > src_h = tmp ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
if ( state - > crtc_x + state - > crtc_w > mode - > hdisplay )
patched_crtc_w = mode - > hdisplay - state - > crtc_x ;
2015-01-06 11:13:28 +01:00
else
2015-02-05 16:32:33 +01:00
patched_crtc_w = state - > crtc_w ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( state - > crtc_x < 0 ) {
patched_crtc_w + = state - > crtc_x ;
x_offset = - state - > crtc_x ;
state - > crtc_x = 0 ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
if ( state - > crtc_y + state - > crtc_h > mode - > vdisplay )
patched_crtc_h = mode - > vdisplay - state - > crtc_y ;
2015-01-06 11:13:28 +01:00
else
2015-02-05 16:32:33 +01:00
patched_crtc_h = state - > crtc_h ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( state - > crtc_y < 0 ) {
patched_crtc_h + = state - > crtc_y ;
y_offset = - state - > crtc_y ;
state - > crtc_y = 0 ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
patched_src_w = DIV_ROUND_CLOSEST ( patched_crtc_w * state - > src_w ,
state - > crtc_w ) ;
patched_src_h = DIV_ROUND_CLOSEST ( patched_crtc_h * state - > src_h ,
state - > crtc_h ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
hsub = drm_format_horz_chroma_subsampling ( fb - > pixel_format ) ;
vsub = drm_format_vert_chroma_subsampling ( fb - > pixel_format ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
for ( i = 0 ; i < state - > nplanes ; i + + ) {
2015-01-06 11:13:28 +01:00
unsigned int offset = 0 ;
int xdiv = i ? hsub : 1 ;
int ydiv = i ? vsub : 1 ;
2015-02-05 16:32:33 +01:00
state - > bpp [ i ] = drm_format_plane_cpp ( fb - > pixel_format , i ) ;
if ( ! state - > bpp [ i ] )
2015-01-06 11:13:28 +01:00
return - EINVAL ;
2015-02-05 16:32:33 +01:00
switch ( state - > base . rotation & 0xf ) {
2015-01-06 11:13:28 +01:00
case BIT ( DRM_ROTATE_90 ) :
2015-02-05 16:32:33 +01:00
offset = ( ( y_offset + state - > src_y + patched_src_w - 1 ) /
ydiv ) * fb - > pitches [ i ] ;
offset + = ( ( x_offset + state - > src_x ) / xdiv ) *
state - > bpp [ i ] ;
state - > xstride [ i ] = ( ( patched_src_w - 1 ) / ydiv ) *
fb - > pitches [ i ] ;
state - > pstride [ i ] = - fb - > pitches [ i ] - state - > bpp [ i ] ;
2015-01-06 11:13:28 +01:00
break ;
case BIT ( DRM_ROTATE_180 ) :
2015-02-05 16:32:33 +01:00
offset = ( ( y_offset + state - > src_y + patched_src_h - 1 ) /
ydiv ) * fb - > pitches [ i ] ;
offset + = ( ( x_offset + state - > src_x + patched_src_w - 1 ) /
xdiv ) * state - > bpp [ i ] ;
state - > xstride [ i ] = ( ( ( ( patched_src_w - 1 ) / xdiv ) - 1 ) *
state - > bpp [ i ] ) - fb - > pitches [ i ] ;
state - > pstride [ i ] = - 2 * state - > bpp [ i ] ;
2015-01-06 11:13:28 +01:00
break ;
case BIT ( DRM_ROTATE_270 ) :
2015-02-05 16:32:33 +01:00
offset = ( ( y_offset + state - > src_y ) / ydiv ) *
fb - > pitches [ i ] ;
offset + = ( ( x_offset + state - > src_x + patched_src_h - 1 ) /
xdiv ) * state - > bpp [ i ] ;
state - > xstride [ i ] = - ( ( ( patched_src_w - 1 ) / ydiv ) *
fb - > pitches [ i ] ) -
( 2 * state - > bpp [ i ] ) ;
state - > pstride [ i ] = fb - > pitches [ i ] - state - > bpp [ i ] ;
2015-01-06 11:13:28 +01:00
break ;
case BIT ( DRM_ROTATE_0 ) :
default :
2015-02-05 16:32:33 +01:00
offset = ( ( y_offset + state - > src_y ) / ydiv ) *
fb - > pitches [ i ] ;
offset + = ( ( x_offset + state - > src_x ) / xdiv ) *
state - > bpp [ i ] ;
state - > xstride [ i ] = fb - > pitches [ i ] -
2015-01-06 11:13:28 +01:00
( ( patched_src_w / xdiv ) *
2015-02-05 16:32:33 +01:00
state - > bpp [ i ] ) ;
state - > pstride [ i ] = 0 ;
2015-01-06 11:13:28 +01:00
break ;
}
2015-02-05 16:32:33 +01:00
state - > offsets [ i ] = offset + fb - > offsets [ i ] ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
state - > src_w = patched_src_w ;
state - > src_h = patched_src_h ;
state - > crtc_w = patched_crtc_w ;
state - > crtc_h = patched_crtc_h ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( ! layout - > size & &
( mode - > hdisplay ! = state - > crtc_w | |
mode - > vdisplay ! = state - > crtc_h ) )
return - EINVAL ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( plane - > layer . desc - > max_height & &
state - > crtc_h > plane - > layer . desc - > max_height )
return - EINVAL ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( plane - > layer . desc - > max_width & &
state - > crtc_w > plane - > layer . desc - > max_width )
return - EINVAL ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( ( state - > crtc_h ! = state - > src_h | | state - > crtc_w ! = state - > src_w ) & &
( ! layout - > memsize | |
atmel_hlcdc_format_embeds_alpha ( state - > base . fb - > pixel_format ) ) )
return - EINVAL ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( state - > crtc_x < 0 | | state - > crtc_y < 0 )
return - EINVAL ;
if ( state - > crtc_w + state - > crtc_x > mode - > hdisplay | |
state - > crtc_h + state - > crtc_y > mode - > vdisplay )
return - EINVAL ;
2015-01-06 11:13:28 +01:00
return 0 ;
}
2015-02-05 16:32:33 +01:00
static int atmel_hlcdc_plane_prepare_fb ( struct drm_plane * p ,
2015-03-03 14:22:31 +00:00
const struct drm_plane_state * new_state )
2015-01-06 11:13:28 +01:00
{
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
2015-09-02 10:42:40 +02:00
if ( ! new_state - > fb )
return 0 ;
2015-02-05 16:32:33 +01:00
return atmel_hlcdc_layer_update_start ( & plane - > layer ) ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
static void atmel_hlcdc_plane_atomic_update ( struct drm_plane * p ,
struct drm_plane_state * old_s )
2015-01-06 11:13:28 +01:00
{
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
struct atmel_hlcdc_plane_state * state =
drm_plane_state_to_atmel_hlcdc_plane_state ( p - > state ) ;
if ( ! p - > state - > crtc | | ! p - > state - > fb )
return ;
atmel_hlcdc_plane_update_pos_and_size ( plane , state ) ;
atmel_hlcdc_plane_update_general_settings ( plane , state ) ;
atmel_hlcdc_plane_update_format ( plane , state ) ;
atmel_hlcdc_plane_update_buffers ( plane , state ) ;
2015-02-06 16:25:06 +01:00
atmel_hlcdc_plane_update_disc_area ( plane , state ) ;
2015-02-05 16:32:33 +01:00
atmel_hlcdc_layer_update_commit ( & plane - > layer ) ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
static void atmel_hlcdc_plane_atomic_disable ( struct drm_plane * p ,
struct drm_plane_state * old_state )
2015-01-06 11:13:28 +01:00
{
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
2015-02-05 16:32:33 +01:00
atmel_hlcdc_layer_disable ( & plane - > layer ) ;
2015-01-06 11:13:28 +01:00
}
static void atmel_hlcdc_plane_destroy ( struct drm_plane * p )
{
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
if ( plane - > base . fb )
drm_framebuffer_unreference ( plane - > base . fb ) ;
atmel_hlcdc_layer_cleanup ( p - > dev , & plane - > layer ) ;
drm_plane_cleanup ( p ) ;
devm_kfree ( p - > dev - > dev , plane ) ;
}
2015-02-05 16:32:33 +01:00
static int atmel_hlcdc_plane_atomic_set_property ( struct drm_plane * p ,
struct drm_plane_state * s ,
struct drm_property * property ,
uint64_t val )
2015-01-06 11:13:28 +01:00
{
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
struct atmel_hlcdc_plane_properties * props = plane - > properties ;
struct atmel_hlcdc_plane_state * state =
drm_plane_state_to_atmel_hlcdc_plane_state ( s ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( property = = props - > alpha )
state - > alpha = val ;
else
return - EINVAL ;
2015-01-06 11:13:28 +01:00
return 0 ;
}
2015-02-05 16:32:33 +01:00
static int atmel_hlcdc_plane_atomic_get_property ( struct drm_plane * p ,
const struct drm_plane_state * s ,
struct drm_property * property ,
uint64_t * val )
2015-01-06 11:13:28 +01:00
{
struct atmel_hlcdc_plane * plane = drm_plane_to_atmel_hlcdc_plane ( p ) ;
struct atmel_hlcdc_plane_properties * props = plane - > properties ;
2015-02-05 16:32:33 +01:00
const struct atmel_hlcdc_plane_state * state =
container_of ( s , const struct atmel_hlcdc_plane_state , base ) ;
2015-01-06 11:13:28 +01:00
if ( property = = props - > alpha )
2015-02-05 16:32:33 +01:00
* val = state - > alpha ;
2015-01-06 11:13:28 +01:00
else
return - EINVAL ;
return 0 ;
}
static void atmel_hlcdc_plane_init_properties ( struct atmel_hlcdc_plane * plane ,
const struct atmel_hlcdc_layer_desc * desc ,
struct atmel_hlcdc_plane_properties * props )
{
struct regmap * regmap = plane - > layer . hlcdc - > regmap ;
if ( desc - > type = = ATMEL_HLCDC_OVERLAY_LAYER | |
desc - > type = = ATMEL_HLCDC_CURSOR_LAYER ) {
drm_object_attach_property ( & plane - > base . base ,
props - > alpha , 255 ) ;
/* Set default alpha value */
regmap_update_bits ( regmap ,
desc - > regs_offset +
ATMEL_HLCDC_LAYER_GENERAL_CFG ( & plane - > layer ) ,
ATMEL_HLCDC_LAYER_GA_MASK ,
ATMEL_HLCDC_LAYER_GA_MASK ) ;
}
if ( desc - > layout . xstride & & desc - > layout . pstride )
drm_object_attach_property ( & plane - > base . base ,
2015-02-05 16:32:33 +01:00
plane - > base . dev - > mode_config . rotation_property ,
BIT ( DRM_ROTATE_0 ) ) ;
2015-01-06 11:13:28 +01:00
if ( desc - > layout . csc ) {
/*
* TODO : decare a " yuv-to-rgb-conv-factors " property to let
* userspace modify these factors ( using a BLOB property ? ) .
*/
regmap_write ( regmap ,
desc - > regs_offset +
ATMEL_HLCDC_LAYER_CSC_CFG ( & plane - > layer , 0 ) ,
0x4c900091 ) ;
regmap_write ( regmap ,
desc - > regs_offset +
ATMEL_HLCDC_LAYER_CSC_CFG ( & plane - > layer , 1 ) ,
0x7a5f5090 ) ;
regmap_write ( regmap ,
desc - > regs_offset +
ATMEL_HLCDC_LAYER_CSC_CFG ( & plane - > layer , 2 ) ,
0x40040890 ) ;
}
}
2015-02-05 16:32:33 +01:00
static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
. prepare_fb = atmel_hlcdc_plane_prepare_fb ,
. atomic_check = atmel_hlcdc_plane_atomic_check ,
. atomic_update = atmel_hlcdc_plane_atomic_update ,
. atomic_disable = atmel_hlcdc_plane_atomic_disable ,
} ;
static void atmel_hlcdc_plane_reset ( struct drm_plane * p )
{
struct atmel_hlcdc_plane_state * state ;
if ( p - > state ) {
state = drm_plane_state_to_atmel_hlcdc_plane_state ( p - > state ) ;
if ( state - > base . fb )
drm_framebuffer_unreference ( state - > base . fb ) ;
kfree ( state ) ;
p - > state = NULL ;
}
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( state ) {
state - > alpha = 255 ;
p - > state = & state - > base ;
p - > state - > plane = p ;
}
}
static struct drm_plane_state *
atmel_hlcdc_plane_atomic_duplicate_state ( struct drm_plane * p )
{
struct atmel_hlcdc_plane_state * state =
drm_plane_state_to_atmel_hlcdc_plane_state ( p - > state ) ;
struct atmel_hlcdc_plane_state * copy ;
copy = kmemdup ( state , sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! copy )
return NULL ;
2015-02-06 16:25:06 +01:00
copy - > disc_updated = false ;
2015-02-05 16:32:33 +01:00
if ( copy - > base . fb )
drm_framebuffer_reference ( copy - > base . fb ) ;
return & copy - > base ;
}
static void atmel_hlcdc_plane_atomic_destroy_state ( struct drm_plane * plane ,
struct drm_plane_state * s )
{
struct atmel_hlcdc_plane_state * state =
drm_plane_state_to_atmel_hlcdc_plane_state ( s ) ;
if ( s - > fb )
drm_framebuffer_unreference ( s - > fb ) ;
kfree ( state ) ;
}
2015-01-06 11:13:28 +01:00
static struct drm_plane_funcs layer_plane_funcs = {
2015-02-05 16:32:33 +01:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
. set_property = drm_atomic_helper_plane_set_property ,
2015-01-06 11:13:28 +01:00
. destroy = atmel_hlcdc_plane_destroy ,
2015-02-05 16:32:33 +01:00
. reset = atmel_hlcdc_plane_reset ,
. atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state ,
. atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state ,
. atomic_set_property = atmel_hlcdc_plane_atomic_set_property ,
. atomic_get_property = atmel_hlcdc_plane_atomic_get_property ,
2015-01-06 11:13:28 +01:00
} ;
static struct atmel_hlcdc_plane *
atmel_hlcdc_plane_create ( struct drm_device * dev ,
const struct atmel_hlcdc_layer_desc * desc ,
struct atmel_hlcdc_plane_properties * props )
{
struct atmel_hlcdc_plane * plane ;
enum drm_plane_type type ;
int ret ;
plane = devm_kzalloc ( dev - > dev , sizeof ( * plane ) , GFP_KERNEL ) ;
if ( ! plane )
return ERR_PTR ( - ENOMEM ) ;
ret = atmel_hlcdc_layer_init ( dev , & plane - > layer , desc ) ;
if ( ret )
return ERR_PTR ( ret ) ;
if ( desc - > type = = ATMEL_HLCDC_BASE_LAYER )
type = DRM_PLANE_TYPE_PRIMARY ;
else if ( desc - > type = = ATMEL_HLCDC_CURSOR_LAYER )
type = DRM_PLANE_TYPE_CURSOR ;
else
type = DRM_PLANE_TYPE_OVERLAY ;
ret = drm_universal_plane_init ( dev , & plane - > base , 0 ,
& layer_plane_funcs ,
desc - > formats - > formats ,
desc - > formats - > nformats , type ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2015-02-05 16:32:33 +01:00
drm_plane_helper_add ( & plane - > base ,
& atmel_hlcdc_layer_plane_helper_funcs ) ;
2015-01-06 11:13:28 +01:00
/* Set default property values*/
atmel_hlcdc_plane_init_properties ( plane , desc , props ) ;
return plane ;
}
static struct atmel_hlcdc_plane_properties *
atmel_hlcdc_plane_create_properties ( struct drm_device * dev )
{
struct atmel_hlcdc_plane_properties * props ;
props = devm_kzalloc ( dev - > dev , sizeof ( * props ) , GFP_KERNEL ) ;
if ( ! props )
return ERR_PTR ( - ENOMEM ) ;
props - > alpha = drm_property_create_range ( dev , 0 , " alpha " , 0 , 255 ) ;
if ( ! props - > alpha )
return ERR_PTR ( - ENOMEM ) ;
2015-02-05 16:32:33 +01:00
dev - > mode_config . rotation_property =
drm_mode_create_rotation_property ( dev ,
BIT ( DRM_ROTATE_0 ) |
BIT ( DRM_ROTATE_90 ) |
BIT ( DRM_ROTATE_180 ) |
BIT ( DRM_ROTATE_270 ) ) ;
if ( ! dev - > mode_config . rotation_property )
2015-01-06 11:13:28 +01:00
return ERR_PTR ( - ENOMEM ) ;
return props ;
}
struct atmel_hlcdc_planes *
atmel_hlcdc_create_planes ( struct drm_device * dev )
{
struct atmel_hlcdc_dc * dc = dev - > dev_private ;
struct atmel_hlcdc_plane_properties * props ;
struct atmel_hlcdc_planes * planes ;
const struct atmel_hlcdc_layer_desc * descs = dc - > desc - > layers ;
int nlayers = dc - > desc - > nlayers ;
int i ;
planes = devm_kzalloc ( dev - > dev , sizeof ( * planes ) , GFP_KERNEL ) ;
if ( ! planes )
return ERR_PTR ( - ENOMEM ) ;
for ( i = 0 ; i < nlayers ; i + + ) {
if ( descs [ i ] . type = = ATMEL_HLCDC_OVERLAY_LAYER )
planes - > noverlays + + ;
}
if ( planes - > noverlays ) {
planes - > overlays = devm_kzalloc ( dev - > dev ,
planes - > noverlays *
sizeof ( * planes - > overlays ) ,
GFP_KERNEL ) ;
if ( ! planes - > overlays )
return ERR_PTR ( - ENOMEM ) ;
}
props = atmel_hlcdc_plane_create_properties ( dev ) ;
if ( IS_ERR ( props ) )
return ERR_CAST ( props ) ;
planes - > noverlays = 0 ;
for ( i = 0 ; i < nlayers ; i + + ) {
struct atmel_hlcdc_plane * plane ;
if ( descs [ i ] . type = = ATMEL_HLCDC_PP_LAYER )
continue ;
plane = atmel_hlcdc_plane_create ( dev , & descs [ i ] , props ) ;
if ( IS_ERR ( plane ) )
return ERR_CAST ( plane ) ;
plane - > properties = props ;
switch ( descs [ i ] . type ) {
case ATMEL_HLCDC_BASE_LAYER :
if ( planes - > primary )
return ERR_PTR ( - EINVAL ) ;
planes - > primary = plane ;
break ;
case ATMEL_HLCDC_OVERLAY_LAYER :
planes - > overlays [ planes - > noverlays + + ] = plane ;
break ;
case ATMEL_HLCDC_CURSOR_LAYER :
if ( planes - > cursor )
return ERR_PTR ( - EINVAL ) ;
planes - > cursor = plane ;
break ;
default :
break ;
}
}
return planes ;
}