2011-12-08 17:54:07 +09:00
/*
* Copyright ( C ) 2011 Samsung Electronics Co . Ltd
* Authors : Joonyoung Shim < jy0922 . shim @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
2011-12-08 17:54:07 +09:00
2016-03-15 12:38:02 +01:00
# include <drm/drm_atomic.h>
2015-06-01 12:04:44 -03:00
# include <drm/drm_atomic_helper.h>
2016-03-15 12:38:02 +01:00
# include <drm/drm_plane_helper.h>
# include <drm/exynos_drm.h>
2011-12-08 17:54:07 +09:00
# include "exynos_drm_drv.h"
2014-02-19 21:02:55 +09:00
# include "exynos_drm_crtc.h"
2012-06-27 14:27:05 +09:00
# include "exynos_drm_fb.h"
# include "exynos_drm_gem.h"
2013-08-13 00:46:40 +01:00
# include "exynos_drm_plane.h"
2011-12-08 17:54:07 +09:00
2012-09-27 19:25:21 +09:00
/*
* This function is to get X or Y size shown via screen . This needs length and
* start position of CRTC .
*
* < - - - length - - - >
* CRTC - - - - - - - - - - - - - - - -
* ^ start ^ end
*
2012-12-14 15:48:22 +09:00
* There are six cases from a to f .
2012-09-27 19:25:21 +09:00
*
* < - - - - - SCREEN - - - - - >
* 0 last
* - - - - - - - - - - | - - - - - - - - - - - - - - - - - - | - - - - - - - - - -
* CRTCs
* a - - - - - - -
* b - - - - - - -
* c - - - - - - - - - - - - - - - - - - - - - - - - - -
* d - - - - - - - -
* e - - - - - - -
* f - - - - - - -
*/
static int exynos_plane_get_size ( int start , unsigned length , unsigned last )
{
int end = start + length ;
int size = 0 ;
if ( start < = 0 ) {
if ( end > 0 )
size = min_t ( unsigned , end , last ) ;
} else if ( start < = last ) {
size = min_t ( unsigned , last - start , length ) ;
}
return size ;
}
2015-11-30 14:53:22 +01:00
static void exynos_plane_mode_set ( struct exynos_drm_plane_state * exynos_state )
2014-11-27 14:56:09 -02:00
{
2015-11-30 14:53:22 +01:00
struct drm_plane_state * state = & exynos_state - > base ;
2016-03-15 12:38:02 +01:00
struct drm_crtc * crtc = state - > crtc ;
struct drm_crtc_state * crtc_state =
drm_atomic_get_existing_crtc_state ( state - > state , crtc ) ;
struct drm_display_mode * mode = & crtc_state - > adjusted_mode ;
2015-11-30 14:53:22 +01:00
int crtc_x , crtc_y ;
unsigned int crtc_w , crtc_h ;
unsigned int src_x , src_y ;
unsigned int src_w , src_h ;
2014-11-27 14:56:09 -02:00
unsigned int actual_w ;
unsigned int actual_h ;
2015-11-30 14:53:22 +01:00
/*
* The original src / dest coordinates are stored in exynos_state - > base ,
* but we want to keep another copy internal to our driver that we can
* clip / modify ourselves .
*/
crtc_x = state - > crtc_x ;
crtc_y = state - > crtc_y ;
crtc_w = state - > crtc_w ;
crtc_h = state - > crtc_h ;
src_x = state - > src_x > > 16 ;
src_y = state - > src_y > > 16 ;
src_w = state - > src_w > > 16 ;
src_h = state - > src_h > > 16 ;
2015-11-30 14:53:28 +01:00
/* set ratio */
exynos_state - > h_ratio = ( src_w < < 16 ) / crtc_w ;
exynos_state - > v_ratio = ( src_h < < 16 ) / crtc_h ;
/* clip to visible area */
2015-06-02 21:04:42 +09:00
actual_w = exynos_plane_get_size ( crtc_x , crtc_w , mode - > hdisplay ) ;
actual_h = exynos_plane_get_size ( crtc_y , crtc_h , mode - > vdisplay ) ;
2012-09-27 19:25:21 +09:00
if ( crtc_x < 0 ) {
if ( actual_w )
2015-11-30 14:53:28 +01:00
src_x + = ( ( - crtc_x ) * exynos_state - > h_ratio ) > > 16 ;
2012-09-27 19:25:21 +09:00
crtc_x = 0 ;
}
if ( crtc_y < 0 ) {
if ( actual_h )
2015-11-30 14:53:28 +01:00
src_y + = ( ( - crtc_y ) * exynos_state - > v_ratio ) > > 16 ;
2012-09-27 19:25:21 +09:00
crtc_y = 0 ;
}
2012-06-27 14:27:05 +09:00
/* set drm framebuffer data. */
2015-11-30 14:53:22 +01:00
exynos_state - > src . x = src_x ;
exynos_state - > src . y = src_y ;
exynos_state - > src . w = ( actual_w * exynos_state - > h_ratio ) > > 16 ;
exynos_state - > src . h = ( actual_h * exynos_state - > v_ratio ) > > 16 ;
2014-11-03 18:13:27 -02:00
/* set plane range to be displayed. */
2015-11-30 14:53:22 +01:00
exynos_state - > crtc . x = crtc_x ;
exynos_state - > crtc . y = crtc_y ;
exynos_state - > crtc . w = actual_w ;
exynos_state - > crtc . h = actual_h ;
2015-07-16 12:23:38 -03:00
2019-04-15 16:25:12 +09:00
DRM_DEV_DEBUG_KMS ( crtc - > dev - > dev ,
" plane : offset_x/y(%d,%d), width/height(%d,%d) " ,
exynos_state - > crtc . x , exynos_state - > crtc . y ,
exynos_state - > crtc . w , exynos_state - > crtc . h ) ;
2015-11-30 14:53:22 +01:00
}
static void exynos_drm_plane_reset ( struct drm_plane * plane )
{
2015-12-16 13:21:43 +01:00
struct exynos_drm_plane * exynos_plane = to_exynos_plane ( plane ) ;
2015-11-30 14:53:22 +01:00
struct exynos_drm_plane_state * exynos_state ;
if ( plane - > state ) {
exynos_state = to_exynos_plane_state ( plane - > state ) ;
2018-09-21 14:24:36 +02:00
__drm_atomic_helper_plane_destroy_state ( plane - > state ) ;
2015-11-30 14:53:22 +01:00
kfree ( exynos_state ) ;
plane - > state = NULL ;
}
exynos_state = kzalloc ( sizeof ( * exynos_state ) , GFP_KERNEL ) ;
if ( exynos_state ) {
2018-09-21 14:24:36 +02:00
__drm_atomic_helper_plane_reset ( plane , & exynos_state - > base ) ;
2016-05-11 17:16:06 +02:00
plane - > state - > zpos = exynos_plane - > config - > zpos ;
2015-11-30 14:53:22 +01:00
}
}
static struct drm_plane_state *
exynos_drm_plane_duplicate_state ( struct drm_plane * plane )
{
struct exynos_drm_plane_state * exynos_state ;
struct exynos_drm_plane_state * copy ;
exynos_state = to_exynos_plane_state ( plane - > state ) ;
copy = kzalloc ( sizeof ( * exynos_state ) , GFP_KERNEL ) ;
if ( ! copy )
return NULL ;
2012-06-27 14:27:05 +09:00
2015-11-30 14:53:22 +01:00
__drm_atomic_helper_plane_duplicate_state ( plane , & copy - > base ) ;
return & copy - > base ;
}
2012-06-27 14:27:05 +09:00
2015-11-30 14:53:22 +01:00
static void exynos_drm_plane_destroy_state ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct exynos_drm_plane_state * old_exynos_state =
to_exynos_plane_state ( old_state ) ;
2016-05-09 16:34:10 +02:00
__drm_atomic_helper_plane_destroy_state ( old_state ) ;
2015-11-30 14:53:22 +01:00
kfree ( old_exynos_state ) ;
2012-06-27 14:27:05 +09:00
}
2011-12-08 17:54:07 +09:00
static struct drm_plane_funcs exynos_plane_funcs = {
2015-06-01 12:04:46 -03:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2015-04-01 13:02:10 -03:00
. destroy = drm_plane_cleanup ,
2015-11-30 14:53:22 +01:00
. reset = exynos_drm_plane_reset ,
. atomic_duplicate_state = exynos_drm_plane_duplicate_state ,
. atomic_destroy_state = exynos_drm_plane_destroy_state ,
2011-12-08 17:54:07 +09:00
} ;
2017-08-22 16:19:37 +02:00
static int
exynos_drm_plane_check_format ( const struct exynos_drm_plane_config * config ,
struct exynos_drm_plane_state * state )
{
struct drm_framebuffer * fb = state - > base . fb ;
2019-04-15 14:24:36 +09:00
struct drm_device * dev = fb - > dev ;
2017-08-22 16:19:37 +02:00
switch ( fb - > modifier ) {
case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE :
if ( ! ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_TILE ) )
return - ENOTSUPP ;
break ;
case DRM_FORMAT_MOD_LINEAR :
break ;
default :
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dev - > dev , " unsupported pixel format modifier " ) ;
2017-08-22 16:19:37 +02:00
return - ENOTSUPP ;
}
return 0 ;
}
2015-11-30 14:53:26 +01:00
static int
exynos_drm_plane_check_size ( const struct exynos_drm_plane_config * config ,
struct exynos_drm_plane_state * state )
{
2019-04-15 16:25:12 +09:00
struct drm_crtc * crtc = state - > base . crtc ;
2015-11-30 14:53:26 +01:00
bool width_ok = false , height_ok = false ;
if ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_SCALE )
return 0 ;
if ( state - > src . w = = state - > crtc . w )
width_ok = true ;
if ( state - > src . h = = state - > crtc . h )
height_ok = true ;
if ( ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE ) & &
state - > h_ratio = = ( 1 < < 15 ) )
width_ok = true ;
if ( ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE ) & &
state - > v_ratio = = ( 1 < < 15 ) )
height_ok = true ;
2016-05-25 14:30:07 +02:00
if ( width_ok & & height_ok )
2015-11-30 14:53:26 +01:00
return 0 ;
2019-04-15 16:25:12 +09:00
DRM_DEV_DEBUG_KMS ( crtc - > dev - > dev , " scaling mode is not supported " ) ;
2015-11-30 14:53:26 +01:00
return - ENOTSUPP ;
}
2015-06-01 12:04:41 -03:00
static int exynos_plane_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
2015-06-01 12:04:49 -03:00
struct exynos_drm_plane * exynos_plane = to_exynos_plane ( plane ) ;
2015-11-30 14:53:22 +01:00
struct exynos_drm_plane_state * exynos_state =
to_exynos_plane_state ( state ) ;
int ret = 0 ;
2015-06-01 12:04:49 -03:00
2015-11-30 14:53:22 +01:00
if ( ! state - > crtc | | ! state - > fb )
2015-06-01 12:04:49 -03:00
return 0 ;
2015-11-30 14:53:22 +01:00
/* translate state into exynos_state */
exynos_plane_mode_set ( exynos_state ) ;
2015-06-01 12:04:49 -03:00
2017-08-22 16:19:37 +02:00
ret = exynos_drm_plane_check_format ( exynos_plane - > config , exynos_state ) ;
if ( ret )
return ret ;
2015-11-30 14:53:26 +01:00
ret = exynos_drm_plane_check_size ( exynos_plane - > config , exynos_state ) ;
2015-11-30 14:53:22 +01:00
return ret ;
2015-06-01 12:04:41 -03:00
}
static void exynos_plane_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct drm_plane_state * state = plane - > state ;
2015-06-01 12:04:49 -03:00
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( state - > crtc ) ;
struct exynos_drm_plane * exynos_plane = to_exynos_plane ( plane ) ;
2015-06-01 12:04:41 -03:00
if ( ! state - > crtc )
return ;
2015-08-03 14:38:05 +09:00
if ( exynos_crtc - > ops - > update_plane )
2015-08-03 14:39:36 +09:00
exynos_crtc - > ops - > update_plane ( exynos_crtc , exynos_plane ) ;
2015-06-01 12:04:41 -03:00
}
2015-06-01 12:04:42 -03:00
static void exynos_plane_atomic_disable ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct exynos_drm_plane * exynos_plane = to_exynos_plane ( plane ) ;
struct exynos_drm_crtc * exynos_crtc = to_exynos_crtc ( old_state - > crtc ) ;
if ( ! old_state - > crtc )
return ;
2015-08-03 14:38:05 +09:00
if ( exynos_crtc - > ops - > disable_plane )
2015-11-30 14:53:22 +01:00
exynos_crtc - > ops - > disable_plane ( exynos_crtc , exynos_plane ) ;
2015-06-01 12:04:42 -03:00
}
2015-06-01 12:04:41 -03:00
static const struct drm_plane_helper_funcs plane_helper_funcs = {
. atomic_check = exynos_plane_atomic_check ,
. atomic_update = exynos_plane_atomic_update ,
2015-06-01 12:04:42 -03:00
. atomic_disable = exynos_plane_atomic_disable ,
2015-06-01 12:04:41 -03:00
} ;
2015-04-03 21:05:52 +09:00
static void exynos_plane_attach_zpos_property ( struct drm_plane * plane ,
2018-05-23 12:15:50 +02:00
int zpos , bool immutable )
2012-06-27 14:27:06 +09:00
{
2016-05-11 17:16:06 +02:00
if ( immutable )
2018-05-23 12:15:50 +02:00
drm_plane_create_zpos_immutable_property ( plane , zpos ) ;
2016-05-11 17:16:06 +02:00
else
2018-05-23 12:15:50 +02:00
drm_plane_create_zpos_property ( plane , zpos , 0 , MAX_PLANE - 1 ) ;
2012-06-27 14:27:06 +09:00
}
2015-04-03 21:03:40 +09:00
int exynos_plane_init ( struct drm_device * dev ,
2017-03-15 15:41:05 +01:00
struct exynos_drm_plane * exynos_plane , unsigned int index ,
2015-11-30 14:53:25 +01:00
const struct exynos_drm_plane_config * config )
2011-12-08 17:54:07 +09:00
{
2012-06-27 14:27:04 +09:00
int err ;
2018-09-21 14:24:37 +02:00
unsigned int supported_modes = BIT ( DRM_MODE_BLEND_PIXEL_NONE ) |
BIT ( DRM_MODE_BLEND_PREMULTI ) |
BIT ( DRM_MODE_BLEND_COVERAGE ) ;
struct drm_plane * plane = & exynos_plane - > base ;
2011-12-08 17:54:07 +09:00
2015-11-30 14:53:25 +01:00
err = drm_universal_plane_init ( dev , & exynos_plane - > base ,
2017-03-15 15:41:05 +01:00
1 < < dev - > mode_config . num_crtc ,
2015-11-30 14:53:25 +01:00
& exynos_plane_funcs ,
config - > pixel_formats ,
config - > num_pixel_formats ,
2017-07-23 20:46:38 -07:00
NULL , config - > type , NULL ) ;
2012-06-27 14:27:04 +09:00
if ( err ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dev - > dev , " failed to initialize plane \n " ) ;
2015-04-03 21:03:40 +09:00
return err ;
2012-06-27 14:27:04 +09:00
}
2015-06-01 12:04:41 -03:00
drm_plane_helper_add ( & exynos_plane - > base , & plane_helper_funcs ) ;
2015-12-16 13:21:42 +01:00
exynos_plane - > index = index ;
2015-11-30 14:53:25 +01:00
exynos_plane - > config = config ;
2015-04-03 21:05:52 +09:00
2018-05-23 12:15:50 +02:00
exynos_plane_attach_zpos_property ( & exynos_plane - > base , config - > zpos ,
2016-05-11 17:16:06 +02:00
! ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS ) ) ;
2011-12-08 17:54:07 +09:00
2018-09-21 14:24:37 +02:00
if ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_PIX_BLEND )
drm_plane_create_blend_mode_property ( plane , supported_modes ) ;
2018-09-21 14:24:38 +02:00
if ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_WIN_BLEND )
drm_plane_create_alpha_property ( plane ) ;
2015-04-03 21:03:40 +09:00
return 0 ;
2011-12-08 17:54:07 +09:00
}