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
2012-10-02 18:01:07 +01:00
# 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-06-27 14:27:03 +09:00
# define to_exynos_plane(x) container_of(x, struct exynos_plane, base)
2011-12-08 17:54:07 +09:00
struct exynos_plane {
struct drm_plane base ;
struct exynos_drm_overlay overlay ;
bool enabled ;
} ;
2012-03-16 18:47:15 +09:00
static const uint32_t formats [ ] = {
DRM_FORMAT_XRGB8888 ,
2012-04-05 11:21:09 +09:00
DRM_FORMAT_ARGB8888 ,
DRM_FORMAT_NV12 ,
DRM_FORMAT_NV12MT ,
2012-03-16 18:47:15 +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 ;
}
2012-06-27 14:27:05 +09:00
int exynos_plane_mode_set ( struct drm_plane * plane , struct drm_crtc * crtc ,
struct drm_framebuffer * fb , 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 )
{
struct exynos_plane * exynos_plane = to_exynos_plane ( plane ) ;
struct exynos_drm_overlay * overlay = & exynos_plane - > overlay ;
unsigned int actual_w ;
unsigned int actual_h ;
int nr ;
int i ;
2012-08-20 20:05:56 +09:00
nr = exynos_drm_fb_get_buf_cnt ( fb ) ;
2012-06-27 14:27:05 +09:00
for ( i = 0 ; i < nr ; i + + ) {
struct exynos_drm_gem_buf * buffer = exynos_drm_fb_buffer ( fb , i ) ;
if ( ! buffer ) {
2014-03-24 15:53:10 +00:00
DRM_DEBUG_KMS ( " buffer is null \n " ) ;
2012-06-27 14:27:05 +09:00
return - EFAULT ;
}
overlay - > dma_addr [ i ] = buffer - > dma_addr ;
2012-12-10 15:44:58 +09:00
DRM_DEBUG_KMS ( " buffer: %d, dma_addr = 0x%lx \n " ,
i , ( unsigned long ) overlay - > dma_addr [ i ] ) ;
2012-06-27 14:27:05 +09:00
}
2012-09-27 19:25:21 +09:00
actual_w = exynos_plane_get_size ( crtc_x , crtc_w , crtc - > mode . hdisplay ) ;
actual_h = exynos_plane_get_size ( crtc_y , crtc_h , crtc - > mode . vdisplay ) ;
if ( crtc_x < 0 ) {
if ( actual_w )
src_x - = crtc_x ;
crtc_x = 0 ;
}
if ( crtc_y < 0 ) {
if ( actual_h )
src_y - = crtc_y ;
crtc_y = 0 ;
}
2012-06-27 14:27:05 +09:00
/* set drm framebuffer data. */
overlay - > fb_x = src_x ;
overlay - > fb_y = src_y ;
overlay - > fb_width = fb - > width ;
overlay - > fb_height = fb - > height ;
overlay - > src_width = src_w ;
overlay - > src_height = src_h ;
overlay - > bpp = fb - > bits_per_pixel ;
overlay - > pitch = fb - > pitches [ 0 ] ;
overlay - > pixel_format = fb - > pixel_format ;
/* set overlay range to be displayed. */
overlay - > crtc_x = crtc_x ;
overlay - > crtc_y = crtc_y ;
overlay - > crtc_width = actual_w ;
overlay - > crtc_height = actual_h ;
/* set drm mode data. */
overlay - > mode_width = crtc - > mode . hdisplay ;
overlay - > mode_height = crtc - > mode . vdisplay ;
overlay - > refresh = crtc - > mode . vrefresh ;
overlay - > scan_flag = crtc - > mode . flags ;
DRM_DEBUG_KMS ( " overlay : offset_x/y(%d,%d), width/height(%d,%d) " ,
overlay - > crtc_x , overlay - > crtc_y ,
overlay - > crtc_width , overlay - > crtc_height ) ;
2014-02-19 21:02:55 +09:00
exynos_drm_crtc_plane_mode_set ( crtc , overlay ) ;
2012-06-27 14:27:05 +09:00
return 0 ;
}
void exynos_plane_commit ( struct drm_plane * plane )
{
struct exynos_plane * exynos_plane = to_exynos_plane ( plane ) ;
struct exynos_drm_overlay * overlay = & exynos_plane - > overlay ;
2014-02-19 21:02:55 +09:00
exynos_drm_crtc_plane_commit ( plane - > crtc , overlay - > zpos ) ;
2012-06-27 14:27:09 +09:00
}
void exynos_plane_dpms ( struct drm_plane * plane , int mode )
{
struct exynos_plane * exynos_plane = to_exynos_plane ( plane ) ;
struct exynos_drm_overlay * overlay = & exynos_plane - > overlay ;
if ( mode = = DRM_MODE_DPMS_ON ) {
if ( exynos_plane - > enabled )
return ;
2014-02-19 21:02:55 +09:00
exynos_drm_crtc_plane_enable ( plane - > crtc , overlay - > zpos ) ;
2012-06-27 14:27:09 +09:00
exynos_plane - > enabled = true ;
} else {
if ( ! exynos_plane - > enabled )
return ;
2014-02-19 21:02:55 +09:00
exynos_drm_crtc_plane_disable ( plane - > crtc , overlay - > zpos ) ;
2012-06-27 14:27:09 +09:00
exynos_plane - > enabled = false ;
}
2012-06-27 14:27:05 +09:00
}
2011-12-08 17:54:07 +09:00
static int
exynos_update_plane ( struct drm_plane * plane , struct drm_crtc * crtc ,
struct drm_framebuffer * fb , 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 )
{
int ret ;
2012-06-27 14:27:05 +09:00
ret = exynos_plane_mode_set ( plane , crtc , fb , crtc_x , crtc_y ,
crtc_w , crtc_h , src_x > > 16 , src_y > > 16 ,
src_w > > 16 , src_h > > 16 ) ;
2011-12-08 17:54:07 +09:00
if ( ret < 0 )
return ret ;
2012-06-27 14:27:05 +09:00
plane - > crtc = crtc ;
2011-12-08 17:54:07 +09:00
2012-06-27 14:27:05 +09:00
exynos_plane_commit ( plane ) ;
2012-06-27 14:27:09 +09:00
exynos_plane_dpms ( plane , DRM_MODE_DPMS_ON ) ;
2011-12-08 17:54:07 +09:00
return 0 ;
}
static int exynos_disable_plane ( struct drm_plane * plane )
{
2012-06-27 14:27:09 +09:00
exynos_plane_dpms ( plane , DRM_MODE_DPMS_OFF ) ;
2011-12-08 17:54:07 +09:00
return 0 ;
}
static void exynos_plane_destroy ( struct drm_plane * plane )
{
2012-06-27 14:27:03 +09:00
struct exynos_plane * exynos_plane = to_exynos_plane ( plane ) ;
2011-12-08 17:54:07 +09:00
exynos_disable_plane ( plane ) ;
drm_plane_cleanup ( plane ) ;
kfree ( exynos_plane ) ;
}
2012-06-27 14:27:06 +09:00
static int exynos_plane_set_property ( struct drm_plane * plane ,
struct drm_property * property ,
uint64_t val )
{
struct drm_device * dev = plane - > dev ;
struct exynos_plane * exynos_plane = to_exynos_plane ( plane ) ;
struct exynos_drm_private * dev_priv = dev - > dev_private ;
if ( property = = dev_priv - > plane_zpos_property ) {
exynos_plane - > overlay . zpos = val ;
return 0 ;
}
return - EINVAL ;
}
2011-12-08 17:54:07 +09:00
static struct drm_plane_funcs exynos_plane_funcs = {
. update_plane = exynos_update_plane ,
. disable_plane = exynos_disable_plane ,
. destroy = exynos_plane_destroy ,
2012-06-27 14:27:06 +09:00
. set_property = exynos_plane_set_property ,
2011-12-08 17:54:07 +09:00
} ;
2012-06-27 14:27:06 +09:00
static void exynos_plane_attach_zpos_property ( struct drm_plane * plane )
{
struct drm_device * dev = plane - > dev ;
struct exynos_drm_private * dev_priv = dev - > dev_private ;
struct drm_property * prop ;
prop = dev_priv - > plane_zpos_property ;
if ( ! prop ) {
prop = drm_property_create_range ( dev , 0 , " zpos " , 0 ,
MAX_PLANE - 1 ) ;
if ( ! prop )
return ;
dev_priv - > plane_zpos_property = prop ;
}
drm_object_attach_property ( & plane - > base , prop , 0 ) ;
}
2012-06-27 14:27:04 +09:00
struct drm_plane * exynos_plane_init ( struct drm_device * dev ,
2014-01-30 16:19:11 -05:00
unsigned long possible_crtcs , bool priv )
2011-12-08 17:54:07 +09:00
{
struct exynos_plane * exynos_plane ;
2012-06-27 14:27:04 +09:00
int err ;
2011-12-08 17:54:07 +09:00
exynos_plane = kzalloc ( sizeof ( struct exynos_plane ) , GFP_KERNEL ) ;
2013-08-19 19:04:55 +09:00
if ( ! exynos_plane )
2012-06-27 14:27:04 +09:00
return NULL ;
2011-12-08 17:54:07 +09:00
2012-06-27 14:27:04 +09:00
err = drm_plane_init ( dev , & exynos_plane - > base , possible_crtcs ,
2012-03-16 18:47:15 +09:00
& exynos_plane_funcs , formats , ARRAY_SIZE ( formats ) ,
2012-06-27 14:27:04 +09:00
priv ) ;
if ( err ) {
DRM_ERROR ( " failed to initialize plane \n " ) ;
kfree ( exynos_plane ) ;
return NULL ;
}
2012-06-27 14:27:06 +09:00
if ( priv )
exynos_plane - > overlay . zpos = DEFAULT_ZPOS ;
else
exynos_plane_attach_zpos_property ( & exynos_plane - > base ) ;
2011-12-08 17:54:07 +09:00
2012-06-27 14:27:06 +09:00
return & exynos_plane - > base ;
2011-12-08 17:54:07 +09:00
}