2018-07-26 02:37:32 +00:00
// SPDX-License-Identifier: GPL-2.0+
2013-06-19 13:54:11 +02:00
/*
* rcar_du_kms . c - - R - Car Display Unit Mode Setting
*
2015-09-07 17:34:26 +03:00
* Copyright ( C ) 2013 - 2015 Renesas Electronics Corporation
2013-06-19 13:54:11 +02:00
*
* Contact : Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*/
2015-02-23 01:02:15 +02:00
# include <drm/drm_atomic.h>
2015-02-20 13:18:56 +02:00
# include <drm/drm_atomic_helper.h>
2013-06-19 13:54:11 +02:00
# include <drm/drm_crtc.h>
2019-01-26 13:25:25 +01:00
# include <drm/drm_device.h>
2013-06-19 13:54:11 +02:00
# include <drm/drm_fb_cma_helper.h>
# include <drm/drm_gem_cma_helper.h>
2017-09-24 14:26:21 +02:00
# include <drm/drm_gem_framebuffer_helper.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2019-01-26 13:25:25 +01:00
# include <drm/drm_vblank.h>
2013-06-19 13:54:11 +02:00
2019-10-16 10:55:44 +02:00
# include <linux/device.h>
2014-01-21 15:57:26 +01:00
# include <linux/of_graph.h>
2019-10-16 10:55:44 +02:00
# include <linux/of_platform.h>
2015-02-23 01:02:15 +02:00
# include <linux/wait.h>
2014-01-21 15:57:26 +01:00
2013-06-19 13:54:11 +02:00
# include "rcar_du_crtc.h"
# include "rcar_du_drv.h"
2013-06-15 15:02:12 +02:00
# include "rcar_du_encoder.h"
2013-06-19 13:54:11 +02:00
# include "rcar_du_kms.h"
# include "rcar_du_regs.h"
2015-09-07 17:14:58 +03:00
# include "rcar_du_vsp.h"
2019-02-21 03:40:12 +02:00
# include "rcar_du_writeback.h"
2013-06-19 13:54:11 +02:00
/* -----------------------------------------------------------------------------
* Format helpers
*/
static const struct rcar_du_format_info rcar_du_format_infos [ ] = {
{
. fourcc = DRM_FORMAT_RGB565 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_RGB565 ,
2013-06-19 13:54:11 +02:00
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_ARGB1555 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_ARGB555 ,
2013-06-19 13:54:11 +02:00
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_XRGB1555 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_XRGB555 ,
2013-06-19 13:54:11 +02:00
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_XRGB8888 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_XBGR32 ,
2013-06-19 13:54:11 +02:00
. bpp = 32 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_RGB888 ,
} , {
. fourcc = DRM_FORMAT_ARGB8888 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_ABGR32 ,
2013-06-19 13:54:11 +02:00
. bpp = 32 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_ARGB8888 ,
} , {
. fourcc = DRM_FORMAT_UYVY ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_UYVY ,
2013-06-19 13:54:11 +02:00
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_YUYV ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YUYV ,
2013-06-19 13:54:11 +02:00
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV12 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_NV12M ,
2013-06-19 13:54:11 +02:00
. bpp = 12 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV21 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_NV21M ,
2013-06-19 13:54:11 +02:00
. bpp = 12 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV16 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_NV16M ,
2013-06-19 13:54:11 +02:00
. bpp = 16 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} ,
2017-07-11 01:13:20 +03:00
/*
* The following formats are not supported on Gen2 and thus have no
2015-11-12 02:03:47 +02:00
* associated . pnmr or . edf settings .
*/
{
2018-08-31 19:12:58 +01:00
. fourcc = DRM_FORMAT_RGB332 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_RGB332 ,
2018-08-31 19:12:58 +01:00
. bpp = 8 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_ARGB4444 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_ARGB444 ,
2018-08-31 19:12:58 +01:00
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_XRGB4444 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_XRGB444 ,
2018-08-31 19:12:58 +01:00
. bpp = 16 ,
. planes = 1 ,
2019-03-28 07:31:32 +02:00
} , {
. fourcc = DRM_FORMAT_RGBA4444 ,
. v4l2 = V4L2_PIX_FMT_RGBA444 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_RGBX4444 ,
. v4l2 = V4L2_PIX_FMT_RGBX444 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_ABGR4444 ,
. v4l2 = V4L2_PIX_FMT_ABGR444 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_XBGR4444 ,
. v4l2 = V4L2_PIX_FMT_XBGR444 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_BGRA4444 ,
. v4l2 = V4L2_PIX_FMT_BGRA444 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_BGRX4444 ,
. v4l2 = V4L2_PIX_FMT_BGRX444 ,
. bpp = 16 ,
. planes = 1 ,
2019-03-28 07:31:32 +02:00
} , {
. fourcc = DRM_FORMAT_RGBA5551 ,
. v4l2 = V4L2_PIX_FMT_RGBA555 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_RGBX5551 ,
. v4l2 = V4L2_PIX_FMT_RGBX555 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_ABGR1555 ,
. v4l2 = V4L2_PIX_FMT_ABGR555 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_XBGR1555 ,
. v4l2 = V4L2_PIX_FMT_XBGR555 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_BGRA5551 ,
. v4l2 = V4L2_PIX_FMT_BGRA555 ,
. bpp = 16 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_BGRX5551 ,
. v4l2 = V4L2_PIX_FMT_BGRX555 ,
. bpp = 16 ,
. planes = 1 ,
2018-08-31 19:12:58 +01:00
} , {
. fourcc = DRM_FORMAT_BGR888 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_RGB24 ,
2018-08-31 19:12:58 +01:00
. bpp = 24 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_RGB888 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_BGR24 ,
2018-08-31 19:12:58 +01:00
. bpp = 24 ,
. planes = 1 ,
2019-03-28 07:31:32 +02:00
} , {
. fourcc = DRM_FORMAT_RGBA8888 ,
. v4l2 = V4L2_PIX_FMT_BGRA32 ,
. bpp = 32 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_RGBX8888 ,
. v4l2 = V4L2_PIX_FMT_BGRX32 ,
. bpp = 32 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_ABGR8888 ,
. v4l2 = V4L2_PIX_FMT_RGBA32 ,
. bpp = 32 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_XBGR8888 ,
. v4l2 = V4L2_PIX_FMT_RGBX32 ,
. bpp = 32 ,
. planes = 1 ,
2018-08-31 19:12:58 +01:00
} , {
. fourcc = DRM_FORMAT_BGRA8888 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_ARGB32 ,
2018-08-31 19:12:58 +01:00
. bpp = 32 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_BGRX8888 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_XRGB32 ,
2018-08-31 19:12:58 +01:00
. bpp = 32 ,
. planes = 1 ,
} , {
. fourcc = DRM_FORMAT_YVYU ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YVYU ,
2018-08-31 19:12:58 +01:00
. bpp = 16 ,
. planes = 1 ,
} , {
2015-11-12 02:03:47 +02:00
. fourcc = DRM_FORMAT_NV61 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_NV61M ,
2015-11-12 02:03:47 +02:00
. bpp = 16 ,
. planes = 2 ,
} , {
. fourcc = DRM_FORMAT_YUV420 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YUV420M ,
2015-11-12 02:03:47 +02:00
. bpp = 12 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YVU420 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YVU420M ,
2015-11-12 02:03:47 +02:00
. bpp = 12 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YUV422 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YUV422M ,
2015-11-12 02:03:47 +02:00
. bpp = 16 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YVU422 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YVU422M ,
2015-11-12 02:03:47 +02:00
. bpp = 16 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YUV444 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YUV444M ,
2015-11-12 02:03:47 +02:00
. bpp = 24 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YVU444 ,
2019-02-21 03:18:05 +02:00
. v4l2 = V4L2_PIX_FMT_YVU444M ,
2015-11-12 02:03:47 +02:00
. bpp = 24 ,
. planes = 3 ,
} ,
2013-06-19 13:54:11 +02:00
} ;
const struct rcar_du_format_info * rcar_du_format_info ( u32 fourcc )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( rcar_du_format_infos ) ; + + i ) {
if ( rcar_du_format_infos [ i ] . fourcc = = fourcc )
return & rcar_du_format_infos [ i ] ;
}
return NULL ;
}
/* -----------------------------------------------------------------------------
* Frame buffer
*/
2013-07-04 20:05:51 +02:00
int rcar_du_dumb_create ( struct drm_file * file , struct drm_device * dev ,
struct drm_mode_create_dumb * args )
{
2013-06-14 20:52:52 +02:00
struct rcar_du_device * rcdu = dev - > dev_private ;
2013-07-04 20:05:51 +02:00
unsigned int min_pitch = DIV_ROUND_UP ( args - > width * args - > bpp , 8 ) ;
unsigned int align ;
2017-07-11 01:13:20 +03:00
/*
* The R8A7779 DU requires a 16 pixels pitch alignment as documented ,
2013-06-14 20:52:52 +02:00
* but the R8A7790 DU seems to require a 128 bytes pitch alignment .
*/
2013-11-13 13:33:45 +01:00
if ( rcar_du_needs ( rcdu , RCAR_DU_QUIRK_ALIGN_128B ) )
2013-06-14 20:52:52 +02:00
align = 128 ;
else
align = 16 * args - > bpp / 8 ;
2014-11-03 12:08:24 +01:00
args - > pitch = roundup ( min_pitch , align ) ;
2013-07-04 20:05:51 +02:00
2014-11-03 11:48:49 +01:00
return drm_gem_cma_dumb_create_internal ( file , dev , args ) ;
2013-07-04 20:05:51 +02:00
}
2013-06-19 13:54:11 +02:00
static struct drm_framebuffer *
rcar_du_fb_create ( struct drm_device * dev , struct drm_file * file_priv ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd )
2013-06-19 13:54:11 +02:00
{
2013-06-14 20:52:52 +02:00
struct rcar_du_device * rcdu = dev - > dev_private ;
2013-06-19 13:54:11 +02:00
const struct rcar_du_format_info * format ;
2014-07-28 20:18:36 +02:00
unsigned int max_pitch ;
2013-07-04 20:05:51 +02:00
unsigned int align ;
2015-11-12 02:03:47 +02:00
unsigned int i ;
2013-06-19 13:54:11 +02:00
format = rcar_du_format_info ( mode_cmd - > pixel_format ) ;
if ( format = = NULL ) {
dev_dbg ( dev - > dev , " unsupported pixel format %08x \n " ,
mode_cmd - > pixel_format ) ;
return ERR_PTR ( - EINVAL ) ;
}
2018-08-31 19:12:59 +01:00
if ( rcdu - > info - > gen < 3 ) {
/*
* On Gen2 the DU limits the pitch to 4095 pixels and requires
* buffers to be aligned to a 16 pixels boundary ( or 128 bytes
* on some platforms ) .
*/
unsigned int bpp = format - > planes = = 1 ? format - > bpp / 8 : 1 ;
2014-07-28 20:18:36 +02:00
2018-08-31 19:12:59 +01:00
max_pitch = 4095 * bpp ;
if ( rcar_du_needs ( rcdu , RCAR_DU_QUIRK_ALIGN_128B ) )
align = 128 ;
else
align = 16 * bpp ;
} else {
/*
* On Gen3 the memory interface is handled by the VSP that
* limits the pitch to 65535 bytes and has no alignment
* constraint .
*/
max_pitch = 65535 ;
align = 1 ;
}
2013-07-04 20:05:51 +02:00
if ( mode_cmd - > pitches [ 0 ] & ( align - 1 ) | |
2018-08-31 19:12:59 +01:00
mode_cmd - > pitches [ 0 ] > max_pitch ) {
2013-06-19 13:54:11 +02:00
dev_dbg ( dev - > dev , " invalid pitch value %u \n " ,
mode_cmd - > pitches [ 0 ] ) ;
return ERR_PTR ( - EINVAL ) ;
}
2015-11-12 02:03:47 +02:00
for ( i = 1 ; i < format - > planes ; + + i ) {
if ( mode_cmd - > pitches [ i ] ! = mode_cmd - > pitches [ 0 ] ) {
2013-06-19 13:54:11 +02:00
dev_dbg ( dev - > dev ,
" luma and chroma pitches do not match \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
}
2017-09-24 14:26:21 +02:00
return drm_gem_fb_create ( dev , file_priv , mode_cmd ) ;
2013-06-19 13:54:11 +02:00
}
2015-02-23 01:02:15 +02:00
/* -----------------------------------------------------------------------------
2015-02-25 18:27:19 +02:00
* Atomic Check and Update
2015-02-23 01:02:15 +02:00
*/
2015-02-25 18:27:19 +02:00
static int rcar_du_atomic_check ( struct drm_device * dev ,
struct drm_atomic_state * state )
{
2015-09-07 17:14:58 +03:00
struct rcar_du_device * rcdu = dev - > dev_private ;
2015-02-25 18:27:19 +02:00
int ret ;
2018-03-21 12:20:28 +02:00
ret = drm_atomic_helper_check ( dev , state ) ;
2016-10-10 17:50:56 +03:00
if ( ret )
2015-02-25 18:27:19 +02:00
return ret ;
2015-09-07 17:14:58 +03:00
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_VSP1_SOURCE ) )
return 0 ;
2015-07-27 15:34:18 +03:00
return rcar_du_atomic_check_planes ( dev , state ) ;
2015-02-25 18:27:19 +02:00
}
2017-02-10 13:30:35 +02:00
static void rcar_du_atomic_commit_tail ( struct drm_atomic_state * old_state )
2015-02-23 01:02:15 +02:00
{
2017-02-10 13:30:35 +02:00
struct drm_device * dev = old_state - > dev ;
2018-11-24 20:19:52 +02:00
struct rcar_du_device * rcdu = dev - > dev_private ;
struct drm_crtc_state * crtc_state ;
struct drm_crtc * crtc ;
unsigned int i ;
/*
* Store RGB routing to DPAD0 and DPAD1 , the hardware will be configured
* when starting the CRTCs .
*/
rcdu - > dpad1_source = - 1 ;
for_each_new_crtc_in_state ( old_state , crtc , crtc_state , i ) {
struct rcar_du_crtc_state * rcrtc_state =
to_rcar_crtc_state ( crtc_state ) ;
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
if ( rcrtc_state - > outputs & BIT ( RCAR_DU_OUTPUT_DPAD0 ) )
rcdu - > dpad0_source = rcrtc - > index ;
if ( rcrtc_state - > outputs & BIT ( RCAR_DU_OUTPUT_DPAD1 ) )
rcdu - > dpad1_source = rcrtc - > index ;
}
2015-02-23 01:02:15 +02:00
/* Apply the atomic update. */
drm_atomic_helper_commit_modeset_disables ( dev , old_state ) ;
2016-08-29 17:12:03 +08:00
drm_atomic_helper_commit_planes ( dev , old_state ,
DRM_PLANE_COMMIT_ACTIVE_ONLY ) ;
2017-06-27 13:18:38 +03:00
drm_atomic_helper_commit_modeset_enables ( dev , old_state ) ;
2015-02-23 01:02:15 +02:00
2017-02-10 13:30:35 +02:00
drm_atomic_helper_commit_hw_done ( old_state ) ;
2017-07-14 01:26:52 +03:00
drm_atomic_helper_wait_for_flip_done ( dev , old_state ) ;
2015-02-23 01:02:15 +02:00
drm_atomic_helper_cleanup_planes ( dev , old_state ) ;
}
/* -----------------------------------------------------------------------------
* Initialization
*/
2017-02-10 13:30:35 +02:00
static const struct drm_mode_config_helper_funcs rcar_du_mode_config_helper = {
. atomic_commit_tail = rcar_du_atomic_commit_tail ,
} ;
2013-06-19 13:54:11 +02:00
static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
. fb_create = rcar_du_fb_create ,
2015-02-25 18:27:19 +02:00
. atomic_check = rcar_du_atomic_check ,
2017-02-10 13:30:35 +02:00
. atomic_commit = drm_atomic_helper_commit ,
2013-06-19 13:54:11 +02:00
} ;
2014-09-17 02:07:52 +03:00
static int rcar_du_encoders_init_one ( struct rcar_du_device * rcdu ,
enum rcar_du_output output ,
struct of_endpoint * ep )
2014-01-21 15:57:26 +01:00
{
struct device_node * entity ;
int ret ;
2019-01-17 00:40:49 +02:00
/* Locate the connected entity and initialize the encoder. */
2014-01-21 15:57:26 +01:00
entity = of_graph_get_remote_port_parent ( ep - > local_node ) ;
if ( ! entity ) {
2017-07-18 16:43:04 -05:00
dev_dbg ( rcdu - > dev , " unconnected endpoint %pOF, skipping \n " ,
ep - > local_node ) ;
2015-05-26 15:07:56 +03:00
return - ENODEV ;
2014-01-21 15:57:26 +01:00
}
2016-12-01 13:07:49 +02:00
if ( ! of_device_is_available ( entity ) ) {
dev_dbg ( rcdu - > dev ,
2017-07-18 16:43:04 -05:00
" connected entity %pOF is disabled, skipping \n " ,
entity ) ;
2019-01-14 17:44:56 +01:00
of_node_put ( entity ) ;
2016-12-01 13:07:49 +02:00
return - ENODEV ;
}
2019-01-17 00:40:49 +02:00
ret = rcar_du_encoder_init ( rcdu , output , entity ) ;
2019-03-05 00:10:28 +02:00
if ( ret & & ret ! = - EPROBE_DEFER & & ret ! = - ENOLINK )
2015-05-26 14:59:42 +03:00
dev_warn ( rcdu - > dev ,
2017-07-18 16:43:04 -05:00
" failed to initialize encoder %pOF on output %u (%d), skipping \n " ,
2019-01-17 00:40:49 +02:00
entity , output , ret ) ;
2016-10-03 20:03:22 +03:00
2019-01-17 00:40:49 +02:00
of_node_put ( entity ) ;
2015-05-26 14:59:42 +03:00
2015-05-26 15:07:56 +03:00
return ret ;
2014-01-21 15:57:26 +01:00
}
2014-09-17 02:07:52 +03:00
static int rcar_du_encoders_init ( struct rcar_du_device * rcdu )
2014-01-21 15:57:26 +01:00
{
struct device_node * np = rcdu - > dev - > of_node ;
2014-12-22 11:46:40 +01:00
struct device_node * ep_node ;
2014-01-21 15:57:26 +01:00
unsigned int num_encoders = 0 ;
/*
* Iterate over the endpoints and create one encoder for each output
* pipeline .
*/
2014-12-22 11:46:40 +01:00
for_each_endpoint_of_node ( np , ep_node ) {
2014-01-21 15:57:26 +01:00
enum rcar_du_output output ;
struct of_endpoint ep ;
unsigned int i ;
int ret ;
ret = of_graph_parse_endpoint ( ep_node , & ep ) ;
if ( ret < 0 ) {
of_node_put ( ep_node ) ;
return ret ;
}
/* Find the output route corresponding to the port number. */
for ( i = 0 ; i < RCAR_DU_OUTPUT_MAX ; + + i ) {
if ( rcdu - > info - > routes [ i ] . possible_crtcs & &
rcdu - > info - > routes [ i ] . port = = ep . port ) {
output = i ;
break ;
}
}
if ( i = = RCAR_DU_OUTPUT_MAX ) {
dev_warn ( rcdu - > dev ,
" port %u references unexisting output, skipping \n " ,
ep . port ) ;
continue ;
}
/* Process the output pipeline. */
2014-09-17 02:07:52 +03:00
ret = rcar_du_encoders_init_one ( rcdu , output , & ep ) ;
2014-01-21 15:57:26 +01:00
if ( ret < 0 ) {
2014-12-11 01:26:04 +02:00
if ( ret = = - EPROBE_DEFER ) {
of_node_put ( ep_node ) ;
return ret ;
}
continue ;
2014-01-21 15:57:26 +01:00
}
2015-05-26 15:07:56 +03:00
num_encoders + + ;
2014-01-21 15:57:26 +01:00
}
return num_encoders ;
}
2015-04-28 23:59:29 +03:00
static int rcar_du_properties_init ( struct rcar_du_device * rcdu )
{
2017-07-11 01:13:20 +03:00
/*
* The color key is expressed as an RGB888 triplet stored in a 32 - bit
2015-04-28 23:59:29 +03:00
* integer in XRGB8888 format . Bit 24 is used as a flag to disable ( 0 )
* or enable source color keying ( 1 ) .
*/
rcdu - > props . colorkey =
drm_property_create_range ( rcdu - > ddev , 0 , " colorkey " ,
0 , 0x01ffffff ) ;
if ( rcdu - > props . colorkey = = NULL )
return - ENOMEM ;
return 0 ;
}
2017-06-26 13:12:01 +03:00
static int rcar_du_vsps_init ( struct rcar_du_device * rcdu )
{
const struct device_node * np = rcdu - > dev - > of_node ;
2019-11-05 19:35:03 +01:00
const char * vsps_prop_name = " renesas,vsps " ;
2017-06-26 13:12:01 +03:00
struct of_phandle_args args ;
struct {
struct device_node * np ;
unsigned int crtcs_mask ;
2018-04-24 16:40:03 +01:00
} vsps [ RCAR_DU_MAX_VSPS ] = { { NULL , } , } ;
2017-06-26 13:12:01 +03:00
unsigned int vsps_count = 0 ;
unsigned int cells ;
unsigned int i ;
int ret ;
/*
* First parse the DT vsps property to populate the list of VSPs . Each
* entry contains a pointer to the VSP DT node and a bitmask of the
* connected DU CRTCs .
*/
2019-11-05 19:35:03 +01:00
ret = of_property_count_u32_elems ( np , vsps_prop_name ) ;
if ( ret < 0 ) {
/* Backward compatibility with old DTBs. */
vsps_prop_name = " vsps " ;
ret = of_property_count_u32_elems ( np , vsps_prop_name ) ;
}
cells = ret / rcdu - > num_crtcs - 1 ;
2017-06-26 13:12:01 +03:00
if ( cells > 1 )
return - EINVAL ;
for ( i = 0 ; i < rcdu - > num_crtcs ; + + i ) {
unsigned int j ;
2019-11-05 19:35:03 +01:00
ret = of_parse_phandle_with_fixed_args ( np , vsps_prop_name ,
cells , i , & args ) ;
2017-06-26 13:12:01 +03:00
if ( ret < 0 )
goto error ;
/*
* Add the VSP to the list or update the corresponding existing
* entry if the VSP has already been added .
*/
for ( j = 0 ; j < vsps_count ; + + j ) {
if ( vsps [ j ] . np = = args . np )
break ;
}
if ( j < vsps_count )
of_node_put ( args . np ) ;
else
vsps [ vsps_count + + ] . np = args . np ;
vsps [ j ] . crtcs_mask | = BIT ( i ) ;
2019-09-06 15:50:12 +02:00
/*
* Store the VSP pointer and pipe index in the CRTC . If the
2019-11-05 19:35:03 +01:00
* second cell of the ' renesas , vsps ' specifier isn ' t present ,
* default to 0 to remain compatible with older DT bindings .
2019-09-06 15:50:12 +02:00
*/
2017-06-26 13:12:01 +03:00
rcdu - > crtcs [ i ] . vsp = & rcdu - > vsps [ j ] ;
rcdu - > crtcs [ i ] . vsp_pipe = cells > = 1 ? args . args [ 0 ] : 0 ;
}
/*
* Then initialize all the VSPs from the node pointers and CRTCs bitmask
* computed previously .
*/
for ( i = 0 ; i < vsps_count ; + + i ) {
struct rcar_du_vsp * vsp = & rcdu - > vsps [ i ] ;
vsp - > index = i ;
vsp - > dev = rcdu ;
ret = rcar_du_vsp_init ( vsp , vsps [ i ] . np , vsps [ i ] . crtcs_mask ) ;
if ( ret < 0 )
goto error ;
}
return 0 ;
error :
for ( i = 0 ; i < ARRAY_SIZE ( vsps ) ; + + i )
of_node_put ( vsps [ i ] . np ) ;
return ret ;
}
2019-10-16 10:55:44 +02:00
static int rcar_du_cmm_init ( struct rcar_du_device * rcdu )
{
const struct device_node * np = rcdu - > dev - > of_node ;
unsigned int i ;
int cells ;
cells = of_property_count_u32_elems ( np , " renesas,cmms " ) ;
if ( cells = = - EINVAL )
return 0 ;
if ( cells > rcdu - > num_crtcs ) {
dev_err ( rcdu - > dev ,
" Invalid number of entries in 'renesas,cmms' \n " ) ;
return - EINVAL ;
}
for ( i = 0 ; i < cells ; + + i ) {
struct platform_device * pdev ;
struct device_link * link ;
struct device_node * cmm ;
int ret ;
cmm = of_parse_phandle ( np , " renesas,cmms " , i ) ;
if ( IS_ERR ( cmm ) ) {
dev_err ( rcdu - > dev ,
" Failed to parse 'renesas,cmms' property \n " ) ;
return PTR_ERR ( cmm ) ;
}
if ( ! of_device_is_available ( cmm ) ) {
/* It's fine to have a phandle to a non-enabled CMM. */
of_node_put ( cmm ) ;
continue ;
}
pdev = of_find_device_by_node ( cmm ) ;
if ( IS_ERR ( pdev ) ) {
dev_err ( rcdu - > dev , " No device found for CMM%u \n " , i ) ;
of_node_put ( cmm ) ;
return PTR_ERR ( pdev ) ;
}
of_node_put ( cmm ) ;
/*
* - ENODEV is used to report that the CMM config option is
* disabled : return 0 and let the DU continue probing .
*/
ret = rcar_cmm_init ( pdev ) ;
if ( ret )
return ret = = - ENODEV ? 0 : ret ;
/*
* Enforce suspend / resume ordering by making the CMM a provider
* of the DU : CMM is suspended after and resumed before the DU .
*/
link = device_link_add ( rcdu - > dev , & pdev - > dev , DL_FLAG_STATELESS ) ;
if ( ! link ) {
dev_err ( rcdu - > dev ,
" Failed to create device link to CMM%u \n " , i ) ;
return - EINVAL ;
}
rcdu - > cmms [ i ] = pdev ;
}
return 0 ;
}
2013-06-19 13:54:11 +02:00
int rcar_du_modeset_init ( struct rcar_du_device * rcdu )
{
2013-06-17 00:29:25 +02:00
static const unsigned int mmio_offsets [ ] = {
DU0_REG_OFFSET , DU2_REG_OFFSET
} ;
2013-06-19 13:54:11 +02:00
struct drm_device * dev = rcdu - > ddev ;
struct drm_encoder * encoder ;
2018-08-22 00:01:07 +03:00
unsigned int dpad0_sources ;
2014-01-21 15:57:26 +01:00
unsigned int num_encoders ;
2013-06-17 00:29:25 +02:00
unsigned int num_groups ;
2018-04-27 23:21:52 +01:00
unsigned int swindex ;
unsigned int hwindex ;
2013-06-19 13:54:11 +02:00
unsigned int i ;
int ret ;
2020-03-23 15:49:35 +01:00
ret = drmm_mode_config_init ( dev ) ;
if ( ret )
return ret ;
2013-06-19 13:54:11 +02:00
2013-03-14 22:45:22 +01:00
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
2018-03-21 12:20:28 +02:00
dev - > mode_config . normalize_zpos = true ;
2013-03-14 22:45:22 +01:00
dev - > mode_config . funcs = & rcar_du_mode_config_funcs ;
2017-02-10 13:30:35 +02:00
dev - > mode_config . helper_private = & rcar_du_mode_config_helper ;
2013-06-19 13:54:11 +02:00
2018-08-31 19:12:57 +01:00
if ( rcdu - > info - > gen < 3 ) {
dev - > mode_config . max_width = 4095 ;
dev - > mode_config . max_height = 2047 ;
} else {
/*
* The Gen3 DU uses the VSP1 for memory access , and is limited
* to frame sizes of 8190 x8190 .
*/
dev - > mode_config . max_width = 8190 ;
dev - > mode_config . max_height = 8190 ;
}
2018-04-27 23:21:52 +01:00
rcdu - > num_crtcs = hweight8 ( rcdu - > info - > channels_mask ) ;
2013-06-17 00:29:25 +02:00
2015-04-28 23:59:29 +03:00
ret = rcar_du_properties_init ( rcdu ) ;
if ( ret < 0 )
return ret ;
2017-07-11 01:13:20 +03:00
/*
* Initialize vertical blanking interrupts handling . Start with vblank
2016-10-19 00:51:35 +03:00
* disabled for all CRTCs .
*/
2018-10-17 22:08:31 +03:00
ret = drm_vblank_init ( dev , rcdu - > num_crtcs ) ;
2016-10-19 00:51:35 +03:00
if ( ret < 0 )
return ret ;
2013-06-17 00:29:25 +02:00
/* Initialize the groups. */
num_groups = DIV_ROUND_UP ( rcdu - > num_crtcs , 2 ) ;
for ( i = 0 ; i < num_groups ; + + i ) {
struct rcar_du_group * rgrp = & rcdu - > groups [ i ] ;
2013-06-16 21:01:02 +02:00
2015-02-25 18:27:19 +02:00
mutex_init ( & rgrp - > lock ) ;
2013-06-17 00:29:25 +02:00
rgrp - > dev = rcdu ;
rgrp - > mmio_offset = mmio_offsets [ i ] ;
rgrp - > index = i ;
2018-04-27 23:21:53 +01:00
/* Extract the channel mask for this group only. */
rgrp - > channels_mask = ( rcdu - > info - > channels_mask > > ( 2 * i ) )
& GENMASK ( 1 , 0 ) ;
rgrp - > num_crtcs = hweight8 ( rgrp - > channels_mask ) ;
2013-06-19 13:54:11 +02:00
2017-07-11 01:13:20 +03:00
/*
* If we have more than one CRTCs in this group pre - associate
2015-09-07 17:34:26 +03:00
* the low - order planes with CRTC 0 and the high - order planes
* with CRTC 1 to minimize flicker occurring when the
* association is changed .
2015-04-29 00:51:01 +03:00
*/
2015-09-07 17:34:26 +03:00
rgrp - > dptsr_planes = rgrp - > num_crtcs > 1
? ( rcdu - > info - > gen > = 3 ? 0x04 : 0xf0 )
: 0 ;
2015-04-29 00:51:01 +03:00
2015-09-07 17:14:58 +03:00
if ( ! rcar_du_has ( rcdu , RCAR_DU_FEATURE_VSP1_SOURCE ) ) {
ret = rcar_du_planes_init ( rgrp ) ;
if ( ret < 0 )
return ret ;
}
}
/* Initialize the compositors. */
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_VSP1_SOURCE ) ) {
2017-06-26 13:12:01 +03:00
ret = rcar_du_vsps_init ( rcdu ) ;
if ( ret < 0 )
return ret ;
2013-07-04 20:05:50 +02:00
}
2013-06-19 13:54:11 +02:00
2019-10-16 10:55:44 +02:00
/* Initialize the Color Management Modules. */
ret = rcar_du_cmm_init ( rcdu ) ;
if ( ret )
return ret ;
2013-06-17 00:29:25 +02:00
/* Create the CRTCs. */
2018-04-27 23:21:52 +01:00
for ( swindex = 0 , hwindex = 0 ; swindex < rcdu - > num_crtcs ; + + hwindex ) {
struct rcar_du_group * rgrp ;
/* Skip unpopulated DU channels. */
if ( ! ( rcdu - > info - > channels_mask & BIT ( hwindex ) ) )
continue ;
rgrp = & rcdu - > groups [ hwindex / 2 ] ;
2013-06-17 00:29:25 +02:00
2018-04-27 23:21:52 +01:00
ret = rcar_du_crtc_create ( rgrp , swindex + + , hwindex ) ;
2013-06-17 00:29:25 +02:00
if ( ret < 0 )
return ret ;
}
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
/* Initialize the encoders. */
2014-09-17 02:07:52 +03:00
ret = rcar_du_encoders_init ( rcdu ) ;
2014-01-21 15:57:26 +01:00
if ( ret < 0 )
return ret ;
2013-06-19 13:54:11 +02:00
2014-12-11 01:26:04 +02:00
if ( ret = = 0 ) {
dev_err ( rcdu - > dev , " error: no encoder could be initialized \n " ) ;
return - EINVAL ;
}
2014-01-21 15:57:26 +01:00
num_encoders = ret ;
2013-06-19 13:54:11 +02:00
2017-07-11 01:13:20 +03:00
/*
* Set the possible CRTCs and possible clones . There ' s always at least
2013-06-17 03:13:11 +02:00
* one way for all encoders to clone each other , set all bits in the
* possible clones field .
2013-06-19 13:54:11 +02:00
*/
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct rcar_du_encoder * renc = to_rcar_encoder ( encoder ) ;
2013-06-17 03:13:11 +02:00
const struct rcar_du_output_routing * route =
& rcdu - > info - > routes [ renc - > output ] ;
2013-06-19 13:54:11 +02:00
2013-06-17 03:13:11 +02:00
encoder - > possible_crtcs = route - > possible_crtcs ;
2014-01-21 15:57:26 +01:00
encoder - > possible_clones = ( 1 < < num_encoders ) - 1 ;
2013-06-19 13:54:11 +02:00
}
2019-02-21 03:40:12 +02:00
/* Create the writeback connectors. */
if ( rcdu - > info - > gen > = 3 ) {
for ( i = 0 ; i < rcdu - > num_crtcs ; + + i ) {
struct rcar_du_crtc * rcrtc = & rcdu - > crtcs [ i ] ;
ret = rcar_du_writeback_init ( rcdu , rcrtc ) ;
if ( ret < 0 )
return ret ;
}
}
2018-08-22 00:01:07 +03:00
/*
* Initialize the default DPAD0 source to the index of the first DU
* channel that can be connected to DPAD0 . The exact value doesn ' t
* matter as it should be overwritten by mode setting for the RGB
* output , but it is nonetheless required to ensure a valid initial
* hardware configuration on Gen3 where DU0 can ' t always be connected to
* DPAD0 .
*/
dpad0_sources = rcdu - > info - > routes [ RCAR_DU_OUTPUT_DPAD0 ] . possible_crtcs ;
rcdu - > dpad0_source = ffs ( dpad0_sources ) - 1 ;
2015-02-20 11:30:59 +02:00
drm_mode_config_reset ( dev ) ;
2013-03-14 22:45:22 +01:00
drm_kms_helper_poll_init ( dev ) ;
2013-06-19 13:54:11 +02:00
return 0 ;
}