2018-07-26 02:37:32 +00:00
// SPDX-License-Identifier: GPL-2.0+
2015-09-07 17:14:58 +03:00
/*
* rcar_du_vsp . h - - R - Car Display Unit VSP - Based Compositor
*
* Copyright ( C ) 2015 Renesas Electronics Corporation
*
* Contact : Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*/
# include <drm/drm_atomic_helper.h>
# include <drm/drm_crtc.h>
# include <drm/drm_fb_cma_helper.h>
2019-02-21 03:20:42 +02:00
# include <drm/drm_fourcc.h>
2015-09-07 17:14:58 +03:00
# include <drm/drm_gem_cma_helper.h>
2018-04-30 14:02:04 +02:00
# include <drm/drm_gem_framebuffer_helper.h>
2015-09-07 17:14:58 +03:00
# include <drm/drm_plane_helper.h>
2019-01-26 13:25:25 +01:00
# include <drm/drm_vblank.h>
2015-09-07 17:14:58 +03:00
2017-06-26 13:12:01 +03:00
# include <linux/bitops.h>
2017-05-17 02:20:07 +03:00
# include <linux/dma-mapping.h>
2015-09-07 17:14:58 +03:00
# include <linux/of_platform.h>
2017-05-17 02:20:07 +03:00
# include <linux/scatterlist.h>
2015-09-07 17:14:58 +03:00
# include <linux/videodev2.h>
# include <media/vsp1.h>
# include "rcar_du_drv.h"
# include "rcar_du_kms.h"
# include "rcar_du_vsp.h"
2019-02-21 03:40:12 +02:00
# include "rcar_du_writeback.h"
2015-09-07 17:14:58 +03:00
2019-02-21 03:38:06 +02:00
static void rcar_du_vsp_complete ( void * private , unsigned int status , u32 crc )
2017-03-04 02:01:19 +00:00
{
struct rcar_du_crtc * crtc = private ;
2017-06-30 13:14:11 +01:00
if ( crtc - > vblank_enable )
drm_crtc_handle_vblank ( & crtc - > crtc ) ;
2019-02-21 03:38:06 +02:00
if ( status & VSP1_DU_STATUS_COMPLETE )
2017-06-30 13:14:11 +01:00
rcar_du_crtc_finish_page_flip ( crtc ) ;
2019-02-21 03:40:12 +02:00
if ( status & VSP1_DU_STATUS_WRITEBACK )
rcar_du_writeback_complete ( crtc ) ;
2017-12-01 06:59:55 -05:00
drm_crtc_add_crc_entry ( & crtc - > crtc , false , 0 , & crc ) ;
2017-03-04 02:01:19 +00:00
}
2015-09-07 17:14:58 +03:00
void rcar_du_vsp_enable ( struct rcar_du_crtc * crtc )
{
const struct drm_display_mode * mode = & crtc - > crtc . state - > adjusted_mode ;
2019-03-15 17:01:05 +00:00
struct rcar_du_device * rcdu = crtc - > dev ;
2017-03-03 06:31:48 -03:00
struct vsp1_du_lif_config cfg = {
. width = mode - > hdisplay ,
. height = mode - > vdisplay ,
2018-08-03 12:37:30 +01:00
. interlaced = mode - > flags & DRM_MODE_FLAG_INTERLACE ,
2017-03-04 02:01:19 +00:00
. callback = rcar_du_vsp_complete ,
. callback_data = crtc ,
2017-03-03 06:31:48 -03:00
} ;
2015-09-07 17:14:58 +03:00
struct rcar_du_plane_state state = {
. state = {
2018-04-11 09:39:27 +02:00
. alpha = DRM_BLEND_ALPHA_OPAQUE ,
2015-09-07 17:14:58 +03:00
. crtc = & crtc - > crtc ,
2017-08-15 18:52:04 +03:00
. dst . x1 = 0 ,
. dst . y1 = 0 ,
. dst . x2 = mode - > hdisplay ,
. dst . y2 = mode - > vdisplay ,
. src . x1 = 0 ,
. src . y1 = 0 ,
. src . x2 = mode - > hdisplay < < 16 ,
. src . y2 = mode - > vdisplay < < 16 ,
2016-07-22 14:28:27 +02:00
. zpos = 0 ,
2015-09-07 17:14:58 +03:00
} ,
. format = rcar_du_format_info ( DRM_FORMAT_ARGB8888 ) ,
. source = RCAR_DU_PLANE_VSPD1 ,
. colorkey = 0 ,
} ;
2015-09-07 17:34:26 +03:00
if ( rcdu - > info - > gen > = 3 )
state . hwindex = ( crtc - > index % 2 ) ? 2 : 0 ;
else
state . hwindex = crtc - > index % 2 ;
2015-09-07 17:14:58 +03:00
__rcar_du_plane_setup ( crtc - > group , & state ) ;
2017-07-11 01:13:20 +03:00
/*
* Ensure that the plane source configuration takes effect by requesting
2015-09-07 17:14:58 +03:00
* a restart of the group . See rcar_du_plane_atomic_update ( ) for a more
* detailed explanation .
*
* TODO : Check whether this is still needed on Gen3 .
*/
crtc - > group - > need_restart = true ;
2017-06-26 13:12:01 +03:00
vsp1_du_setup_lif ( crtc - > vsp - > vsp , crtc - > vsp_pipe , & cfg ) ;
2015-09-07 17:14:58 +03:00
}
void rcar_du_vsp_disable ( struct rcar_du_crtc * crtc )
{
2017-06-26 13:12:01 +03:00
vsp1_du_setup_lif ( crtc - > vsp - > vsp , crtc - > vsp_pipe , NULL ) ;
2015-09-07 17:14:58 +03:00
}
void rcar_du_vsp_atomic_begin ( struct rcar_du_crtc * crtc )
{
2017-06-26 13:12:01 +03:00
vsp1_du_atomic_begin ( crtc - > vsp - > vsp , crtc - > vsp_pipe ) ;
2015-09-07 17:14:58 +03:00
}
void rcar_du_vsp_atomic_flush ( struct rcar_du_crtc * crtc )
{
2017-12-01 06:47:19 -05:00
struct vsp1_du_atomic_pipe_config cfg = { { 0 , } } ;
2017-12-01 06:59:55 -05:00
struct rcar_du_crtc_state * state ;
state = to_rcar_crtc_state ( crtc - > crtc . state ) ;
cfg . crc = state - > crc ;
2017-12-01 06:47:19 -05:00
2019-02-21 03:40:12 +02:00
rcar_du_writeback_setup ( crtc , & cfg . writeback ) ;
2017-12-01 06:47:19 -05:00
vsp1_du_atomic_flush ( crtc - > vsp - > vsp , crtc - > vsp_pipe , & cfg ) ;
2015-09-07 17:14:58 +03:00
}
2019-02-21 03:18:05 +02:00
static const u32 rcar_du_vsp_formats [ ] = {
2015-09-07 17:14:58 +03:00
DRM_FORMAT_RGB332 ,
DRM_FORMAT_ARGB4444 ,
DRM_FORMAT_XRGB4444 ,
DRM_FORMAT_ARGB1555 ,
DRM_FORMAT_XRGB1555 ,
DRM_FORMAT_RGB565 ,
DRM_FORMAT_BGR888 ,
DRM_FORMAT_RGB888 ,
DRM_FORMAT_BGRA8888 ,
DRM_FORMAT_BGRX8888 ,
DRM_FORMAT_ARGB8888 ,
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_UYVY ,
DRM_FORMAT_YUYV ,
DRM_FORMAT_YVYU ,
DRM_FORMAT_NV12 ,
DRM_FORMAT_NV21 ,
DRM_FORMAT_NV16 ,
DRM_FORMAT_NV61 ,
2015-11-12 02:03:47 +02:00
DRM_FORMAT_YUV420 ,
DRM_FORMAT_YVU420 ,
DRM_FORMAT_YUV422 ,
DRM_FORMAT_YVU422 ,
DRM_FORMAT_YUV444 ,
DRM_FORMAT_YVU444 ,
2015-09-07 17:14:58 +03:00
} ;
static void rcar_du_vsp_plane_setup ( struct rcar_du_vsp_plane * plane )
{
struct rcar_du_vsp_plane_state * state =
to_rcar_vsp_plane_state ( plane - > plane . state ) ;
2017-06-26 13:12:01 +03:00
struct rcar_du_crtc * crtc = to_rcar_crtc ( state - > state . crtc ) ;
2015-09-07 17:14:58 +03:00
struct drm_framebuffer * fb = plane - > plane . state - > fb ;
2019-02-21 03:18:05 +02:00
const struct rcar_du_format_info * format ;
2016-03-24 05:15:59 -03:00
struct vsp1_du_atomic_config cfg = {
. pixelformat = 0 ,
. pitch = fb - > pitches [ 0 ] ,
2018-04-11 09:39:27 +02:00
. alpha = state - > state . alpha > > 8 ,
2016-07-22 14:28:27 +02:00
. zpos = state - > state . zpos ,
2016-03-24 05:15:59 -03:00
} ;
2015-09-07 17:14:58 +03:00
unsigned int i ;
2017-08-15 18:52:04 +03:00
cfg . src . left = state - > state . src . x1 > > 16 ;
cfg . src . top = state - > state . src . y1 > > 16 ;
cfg . src . width = drm_rect_width ( & state - > state . src ) > > 16 ;
cfg . src . height = drm_rect_height ( & state - > state . src ) > > 16 ;
2015-09-07 17:14:58 +03:00
2017-08-15 18:52:04 +03:00
cfg . dst . left = state - > state . dst . x1 ;
cfg . dst . top = state - > state . dst . y1 ;
cfg . dst . width = drm_rect_width ( & state - > state . dst ) ;
cfg . dst . height = drm_rect_height ( & state - > state . dst ) ;
2015-09-07 17:14:58 +03:00
2017-05-17 02:20:07 +03:00
for ( i = 0 ; i < state - > format - > planes ; + + i )
cfg . mem [ i ] = sg_dma_address ( state - > sg_tables [ i ] . sgl )
+ fb - > offsets [ i ] ;
2015-09-07 17:14:58 +03:00
2019-02-21 03:18:05 +02:00
format = rcar_du_format_info ( state - > format - > fourcc ) ;
cfg . pixelformat = format - > v4l2 ;
2015-09-07 17:14:58 +03:00
2017-06-26 13:12:01 +03:00
vsp1_du_atomic_update ( plane - > vsp - > vsp , crtc - > vsp_pipe ,
plane - > index , & cfg ) ;
2015-09-07 17:14:58 +03:00
}
2019-02-21 03:20:42 +02:00
int rcar_du_vsp_map_fb ( struct rcar_du_vsp * vsp , struct drm_framebuffer * fb ,
struct sg_table sg_tables [ 3 ] )
2017-05-17 02:20:07 +03:00
{
struct rcar_du_device * rcdu = vsp - > dev ;
unsigned int i ;
int ret ;
2019-02-21 03:20:42 +02:00
for ( i = 0 ; i < fb - > format - > num_planes ; + + i ) {
struct drm_gem_cma_object * gem = drm_fb_cma_get_gem_obj ( fb , i ) ;
struct sg_table * sgt = & sg_tables [ i ] ;
2017-05-17 02:20:07 +03:00
ret = dma_get_sgtable ( rcdu - > dev , sgt , gem - > vaddr , gem - > paddr ,
gem - > base . size ) ;
if ( ret )
goto fail ;
ret = vsp1_du_map_sg ( vsp - > vsp , sgt ) ;
if ( ! ret ) {
sg_free_table ( sgt ) ;
ret = - ENOMEM ;
goto fail ;
}
}
return 0 ;
fail :
while ( i - - ) {
2019-02-21 03:20:42 +02:00
struct sg_table * sgt = & sg_tables [ i ] ;
2017-05-17 02:20:07 +03:00
vsp1_du_unmap_sg ( vsp - > vsp , sgt ) ;
sg_free_table ( sgt ) ;
}
return ret ;
}
2019-02-21 03:20:42 +02:00
static int rcar_du_vsp_plane_prepare_fb ( struct drm_plane * plane ,
struct drm_plane_state * state )
2017-05-17 02:20:07 +03:00
{
struct rcar_du_vsp_plane_state * rstate = to_rcar_vsp_plane_state ( state ) ;
struct rcar_du_vsp * vsp = to_rcar_vsp_plane ( plane ) - > vsp ;
2019-02-21 03:20:42 +02:00
int ret ;
2017-05-17 02:20:07 +03:00
2019-02-21 03:20:42 +02:00
/*
* There ' s no need to prepare ( and unprepare ) the framebuffer when the
* plane is not visible , as it will not be displayed .
*/
2017-08-15 18:52:04 +03:00
if ( ! state - > visible )
2019-02-21 03:20:42 +02:00
return 0 ;
ret = rcar_du_vsp_map_fb ( vsp , state - > fb , rstate - > sg_tables ) ;
if ( ret < 0 )
return ret ;
return drm_gem_fb_prepare_fb ( plane , state ) ;
}
2017-05-17 02:20:07 +03:00
2019-02-21 03:20:42 +02:00
void rcar_du_vsp_unmap_fb ( struct rcar_du_vsp * vsp , struct drm_framebuffer * fb ,
struct sg_table sg_tables [ 3 ] )
{
unsigned int i ;
for ( i = 0 ; i < fb - > format - > num_planes ; + + i ) {
struct sg_table * sgt = & sg_tables [ i ] ;
2017-05-17 02:20:07 +03:00
vsp1_du_unmap_sg ( vsp - > vsp , sgt ) ;
sg_free_table ( sgt ) ;
}
}
2019-02-21 03:20:42 +02:00
static void rcar_du_vsp_plane_cleanup_fb ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
struct rcar_du_vsp_plane_state * rstate = to_rcar_vsp_plane_state ( state ) ;
struct rcar_du_vsp * vsp = to_rcar_vsp_plane ( plane ) - > vsp ;
if ( ! state - > visible )
return ;
rcar_du_vsp_unmap_fb ( vsp , state - > fb , rstate - > sg_tables ) ;
}
2015-09-07 17:14:58 +03:00
static int rcar_du_vsp_plane_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
struct rcar_du_vsp_plane_state * rstate = to_rcar_vsp_plane_state ( state ) ;
2017-08-15 18:45:21 +03:00
return __rcar_du_plane_atomic_check ( plane , state , & rstate - > format ) ;
2015-09-07 17:14:58 +03:00
}
static void rcar_du_vsp_plane_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct rcar_du_vsp_plane * rplane = to_rcar_vsp_plane ( plane ) ;
2017-06-26 13:12:01 +03:00
struct rcar_du_crtc * crtc = to_rcar_crtc ( old_state - > crtc ) ;
2015-09-07 17:14:58 +03:00
2017-08-15 18:52:04 +03:00
if ( plane - > state - > visible )
2015-09-07 17:14:58 +03:00
rcar_du_vsp_plane_setup ( rplane ) ;
else
2017-06-26 13:12:01 +03:00
vsp1_du_atomic_update ( rplane - > vsp - > vsp , crtc - > vsp_pipe ,
rplane - > index , NULL ) ;
2015-09-07 17:14:58 +03:00
}
static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
2017-05-17 02:20:07 +03:00
. prepare_fb = rcar_du_vsp_plane_prepare_fb ,
. cleanup_fb = rcar_du_vsp_plane_cleanup_fb ,
2015-09-07 17:14:58 +03:00
. atomic_check = rcar_du_vsp_plane_atomic_check ,
. atomic_update = rcar_du_vsp_plane_atomic_update ,
} ;
static struct drm_plane_state *
rcar_du_vsp_plane_atomic_duplicate_state ( struct drm_plane * plane )
{
struct rcar_du_vsp_plane_state * copy ;
if ( WARN_ON ( ! plane - > state ) )
return NULL ;
2018-01-17 22:18:41 +02:00
copy = kzalloc ( sizeof ( * copy ) , GFP_KERNEL ) ;
2015-09-07 17:14:58 +03:00
if ( copy = = NULL )
return NULL ;
__drm_atomic_helper_plane_duplicate_state ( plane , & copy - > state ) ;
return & copy - > state ;
}
static void rcar_du_vsp_plane_atomic_destroy_state ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
2016-05-09 16:34:10 +02:00
__drm_atomic_helper_plane_destroy_state ( state ) ;
2015-09-07 17:14:58 +03:00
kfree ( to_rcar_vsp_plane_state ( state ) ) ;
}
static void rcar_du_vsp_plane_reset ( struct drm_plane * plane )
{
struct rcar_du_vsp_plane_state * state ;
if ( plane - > state ) {
rcar_du_vsp_plane_atomic_destroy_state ( plane , plane - > state ) ;
plane - > state = NULL ;
}
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( state = = NULL )
return ;
2018-08-04 17:15:27 +01:00
__drm_atomic_helper_plane_reset ( plane , & state - > state ) ;
2016-07-22 14:28:27 +02:00
state - > state . zpos = plane - > type = = DRM_PLANE_TYPE_PRIMARY ? 0 : 1 ;
2015-09-07 17:14:58 +03:00
}
static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
. reset = rcar_du_vsp_plane_reset ,
. destroy = drm_plane_cleanup ,
. atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state ,
. atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state ,
} ;
2017-06-26 13:12:01 +03:00
int rcar_du_vsp_init ( struct rcar_du_vsp * vsp , struct device_node * np ,
unsigned int crtcs )
2015-09-07 17:14:58 +03:00
{
struct rcar_du_device * rcdu = vsp - > dev ;
struct platform_device * pdev ;
2017-06-26 13:12:01 +03:00
unsigned int num_crtcs = hweight32 ( crtcs ) ;
2015-09-07 17:14:58 +03:00
unsigned int i ;
int ret ;
/* Find the VSP device and initialize it. */
pdev = of_find_device_by_node ( np ) ;
if ( ! pdev )
return - ENXIO ;
vsp - > vsp = & pdev - > dev ;
ret = vsp1_du_init ( vsp - > vsp ) ;
if ( ret < 0 )
return ret ;
2017-07-11 01:13:20 +03:00
/*
* The VSP2D ( Gen3 ) has 5 RPFs , but the VSP1D ( Gen2 ) is limited to
2015-09-07 17:34:26 +03:00
* 4 RPFs .
2015-09-07 17:14:58 +03:00
*/
2015-09-07 17:34:26 +03:00
vsp - > num_planes = rcdu - > info - > gen > = 3 ? 5 : 4 ;
2015-09-07 17:14:58 +03:00
vsp - > planes = devm_kcalloc ( rcdu - > dev , vsp - > num_planes ,
sizeof ( * vsp - > planes ) , GFP_KERNEL ) ;
if ( ! vsp - > planes )
return - ENOMEM ;
for ( i = 0 ; i < vsp - > num_planes ; + + i ) {
2017-06-26 13:12:01 +03:00
enum drm_plane_type type = i < num_crtcs
? DRM_PLANE_TYPE_PRIMARY
: DRM_PLANE_TYPE_OVERLAY ;
2015-09-07 17:14:58 +03:00
struct rcar_du_vsp_plane * plane = & vsp - > planes [ i ] ;
plane - > vsp = vsp ;
plane - > index = i ;
2017-06-26 13:12:01 +03:00
ret = drm_universal_plane_init ( rcdu - > ddev , & plane - > plane , crtcs ,
2015-09-07 17:14:58 +03:00
& rcar_du_vsp_plane_funcs ,
2019-02-21 03:18:05 +02:00
rcar_du_vsp_formats ,
ARRAY_SIZE ( rcar_du_vsp_formats ) ,
2017-07-23 20:46:38 -07:00
NULL , type , NULL ) ;
2015-09-07 17:14:58 +03:00
if ( ret < 0 )
return ret ;
drm_plane_helper_add ( & plane - > plane ,
& rcar_du_vsp_plane_helper_funcs ) ;
if ( type = = DRM_PLANE_TYPE_PRIMARY )
continue ;
2018-04-11 09:39:27 +02:00
drm_plane_create_alpha_property ( & plane - > plane ) ;
2016-07-22 14:28:27 +02:00
drm_plane_create_zpos_property ( & plane - > plane , 1 , 1 ,
vsp - > num_planes - 1 ) ;
2015-09-07 17:14:58 +03:00
}
return 0 ;
}