2011-12-08 12:54:07 +04: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 21:01:07 +04:00
# include <drm/drmP.h>
2011-12-08 12:54:07 +04:00
2016-03-15 14:38:02 +03:00
# include <drm/drm_atomic.h>
2015-06-01 18:04:44 +03:00
# include <drm/drm_atomic_helper.h>
2016-03-15 14:38:02 +03:00
# include <drm/drm_plane_helper.h>
# include <drm/exynos_drm.h>
2011-12-08 12:54:07 +04:00
# include "exynos_drm_drv.h"
2014-02-19 16:02:55 +04:00
# include "exynos_drm_crtc.h"
2012-06-27 09:27:05 +04:00
# include "exynos_drm_fb.h"
# include "exynos_drm_gem.h"
2013-08-13 03:46:40 +04:00
# include "exynos_drm_plane.h"
2011-12-08 12:54:07 +04:00
2012-09-27 14:25:21 +04: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 10:48:22 +04:00
* There are six cases from a to f .
2012-09-27 14:25:21 +04: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 16:53:22 +03:00
static void exynos_plane_mode_set ( struct exynos_drm_plane_state * exynos_state )
2014-11-27 19:56:09 +03:00
{
2015-11-30 16:53:22 +03:00
struct drm_plane_state * state = & exynos_state - > base ;
2016-03-15 14:38:02 +03: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 16:53:22 +03: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 19:56:09 +03:00
unsigned int actual_w ;
unsigned int actual_h ;
2015-11-30 16:53:22 +03: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 16:53:28 +03: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 15:04:42 +03: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 14:25:21 +04:00
if ( crtc_x < 0 ) {
if ( actual_w )
2015-11-30 16:53:28 +03:00
src_x + = ( ( - crtc_x ) * exynos_state - > h_ratio ) > > 16 ;
2012-09-27 14:25:21 +04:00
crtc_x = 0 ;
}
if ( crtc_y < 0 ) {
if ( actual_h )
2015-11-30 16:53:28 +03:00
src_y + = ( ( - crtc_y ) * exynos_state - > v_ratio ) > > 16 ;
2012-09-27 14:25:21 +04:00
crtc_y = 0 ;
}
2012-06-27 09:27:05 +04:00
/* set drm framebuffer data. */
2015-11-30 16:53:22 +03: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 23:13:27 +03:00
/* set plane range to be displayed. */
2015-11-30 16:53:22 +03: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 18:23:38 +03:00
2014-11-03 23:13:27 +03:00
DRM_DEBUG_KMS ( " plane : offset_x/y(%d,%d), width/height(%d,%d) " ,
2015-11-30 16:53:22 +03:00
exynos_state - > crtc . x , exynos_state - > crtc . y ,
exynos_state - > crtc . w , exynos_state - > crtc . h ) ;
}
static void exynos_drm_plane_reset ( struct drm_plane * plane )
{
2015-12-16 15:21:43 +03:00
struct exynos_drm_plane * exynos_plane = to_exynos_plane ( plane ) ;
2015-11-30 16:53:22 +03:00
struct exynos_drm_plane_state * exynos_state ;
if ( plane - > state ) {
exynos_state = to_exynos_plane_state ( plane - > state ) ;
if ( exynos_state - > base . fb )
drm_framebuffer_unreference ( exynos_state - > base . fb ) ;
kfree ( exynos_state ) ;
plane - > state = NULL ;
}
exynos_state = kzalloc ( sizeof ( * exynos_state ) , GFP_KERNEL ) ;
if ( exynos_state ) {
plane - > state = & exynos_state - > base ;
plane - > state - > plane = plane ;
2016-05-11 18:16:06 +03:00
plane - > state - > zpos = exynos_plane - > config - > zpos ;
2015-11-30 16:53:22 +03: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 09:27:05 +04:00
2015-11-30 16:53:22 +03:00
__drm_atomic_helper_plane_duplicate_state ( plane , & copy - > base ) ;
return & copy - > base ;
}
2012-06-27 09:27:05 +04:00
2015-11-30 16:53:22 +03: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 17:34:10 +03:00
__drm_atomic_helper_plane_destroy_state ( old_state ) ;
2015-11-30 16:53:22 +03:00
kfree ( old_exynos_state ) ;
2012-06-27 09:27:05 +04:00
}
2011-12-08 12:54:07 +04:00
static struct drm_plane_funcs exynos_plane_funcs = {
2015-06-01 18:04:46 +03:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2015-04-01 19:02:10 +03:00
. destroy = drm_plane_cleanup ,
2015-11-30 16:53:22 +03: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 12:54:07 +04:00
} ;
2017-08-22 17:19:37 +03: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 ;
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 :
DRM_ERROR ( " unsupported pixel format modifier " ) ;
return - ENOTSUPP ;
}
return 0 ;
}
2015-11-30 16:53:26 +03:00
static int
exynos_drm_plane_check_size ( const struct exynos_drm_plane_config * config ,
struct exynos_drm_plane_state * state )
{
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 15:30:07 +03:00
if ( width_ok & & height_ok )
2015-11-30 16:53:26 +03:00
return 0 ;
DRM_DEBUG_KMS ( " scaling mode is not supported " ) ;
return - ENOTSUPP ;
}
2015-06-01 18:04:41 +03:00
static int exynos_plane_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
2015-06-01 18:04:49 +03:00
struct exynos_drm_plane * exynos_plane = to_exynos_plane ( plane ) ;
2015-11-30 16:53:22 +03:00
struct exynos_drm_plane_state * exynos_state =
to_exynos_plane_state ( state ) ;
int ret = 0 ;
2015-06-01 18:04:49 +03:00
2015-11-30 16:53:22 +03:00
if ( ! state - > crtc | | ! state - > fb )
2015-06-01 18:04:49 +03:00
return 0 ;
2015-11-30 16:53:22 +03:00
/* translate state into exynos_state */
exynos_plane_mode_set ( exynos_state ) ;
2015-06-01 18:04:49 +03:00
2017-08-22 17:19:37 +03:00
ret = exynos_drm_plane_check_format ( exynos_plane - > config , exynos_state ) ;
if ( ret )
return ret ;
2015-11-30 16:53:26 +03:00
ret = exynos_drm_plane_check_size ( exynos_plane - > config , exynos_state ) ;
2015-11-30 16:53:22 +03:00
return ret ;
2015-06-01 18: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 18: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 18:04:41 +03:00
if ( ! state - > crtc )
return ;
2015-11-30 16:53:22 +03:00
plane - > crtc = state - > crtc ;
2015-08-15 19:26:14 +03:00
2015-08-03 08:38:05 +03:00
if ( exynos_crtc - > ops - > update_plane )
2015-08-03 08:39:36 +03:00
exynos_crtc - > ops - > update_plane ( exynos_crtc , exynos_plane ) ;
2015-06-01 18:04:41 +03:00
}
2015-06-01 18: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 08:38:05 +03:00
if ( exynos_crtc - > ops - > disable_plane )
2015-11-30 16:53:22 +03:00
exynos_crtc - > ops - > disable_plane ( exynos_crtc , exynos_plane ) ;
2015-06-01 18:04:42 +03:00
}
2015-06-01 18: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 18:04:42 +03:00
. atomic_disable = exynos_plane_atomic_disable ,
2015-06-01 18:04:41 +03:00
} ;
2015-04-03 15:05:52 +03:00
static void exynos_plane_attach_zpos_property ( struct drm_plane * plane ,
2016-05-11 18:16:06 +03:00
bool immutable )
2012-06-27 09:27:06 +04:00
{
2016-05-11 18:16:06 +03:00
/* FIXME */
if ( immutable )
drm_plane_create_zpos_immutable_property ( plane , 0 ) ;
else
drm_plane_create_zpos_property ( plane , 0 , 0 , MAX_PLANE - 1 ) ;
2012-06-27 09:27:06 +04:00
}
2015-04-03 15:03:40 +03:00
int exynos_plane_init ( struct drm_device * dev ,
2017-03-15 17:41:05 +03:00
struct exynos_drm_plane * exynos_plane , unsigned int index ,
2015-11-30 16:53:25 +03:00
const struct exynos_drm_plane_config * config )
2011-12-08 12:54:07 +04:00
{
2012-06-27 09:27:04 +04:00
int err ;
2011-12-08 12:54:07 +04:00
2015-11-30 16:53:25 +03:00
err = drm_universal_plane_init ( dev , & exynos_plane - > base ,
2017-03-15 17:41:05 +03:00
1 < < dev - > mode_config . num_crtc ,
2015-11-30 16:53:25 +03:00
& exynos_plane_funcs ,
config - > pixel_formats ,
config - > num_pixel_formats ,
2017-07-24 06:46:38 +03:00
NULL , config - > type , NULL ) ;
2012-06-27 09:27:04 +04:00
if ( err ) {
DRM_ERROR ( " failed to initialize plane \n " ) ;
2015-04-03 15:03:40 +03:00
return err ;
2012-06-27 09:27:04 +04:00
}
2015-06-01 18:04:41 +03:00
drm_plane_helper_add ( & exynos_plane - > base , & plane_helper_funcs ) ;
2015-12-16 15:21:42 +03:00
exynos_plane - > index = index ;
2015-11-30 16:53:25 +03:00
exynos_plane - > config = config ;
2015-04-03 15:05:52 +03:00
2016-05-11 18:16:06 +03:00
exynos_plane_attach_zpos_property ( & exynos_plane - > base ,
! ( config - > capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS ) ) ;
2011-12-08 12:54:07 +04:00
2015-04-03 15:03:40 +03:00
return 0 ;
2011-12-08 12:54:07 +04:00
}