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 )
*
* 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 .
*/
# include <drm/drmP.h>
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>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_fb_cma_helper.h>
# include <drm/drm_gem_cma_helper.h>
2014-01-21 15:57:26 +01:00
# include <linux/of_graph.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"
2013-06-17 13:48:27 +02:00
# include "rcar_du_lvdsenc.h"
2013-06-19 13:54:11 +02:00
# include "rcar_du_regs.h"
2015-09-07 17:14:58 +03:00
# include "rcar_du_vsp.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 ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_ARGB1555 ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_XRGB1555 ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_XRGB8888 ,
. bpp = 32 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_RGB888 ,
} , {
. fourcc = DRM_FORMAT_ARGB8888 ,
. bpp = 32 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP ,
. edf = PnDDCR4_EDF_ARGB8888 ,
} , {
. fourcc = DRM_FORMAT_UYVY ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_YUYV ,
. bpp = 16 ,
. planes = 1 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV12 ,
. bpp = 12 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV21 ,
. bpp = 12 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} , {
. fourcc = DRM_FORMAT_NV16 ,
. bpp = 16 ,
. planes = 2 ,
. pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC ,
. edf = PnDDCR4_EDF_NONE ,
} ,
2015-11-12 02:03:47 +02:00
/* The following formats are not supported on Gen2 and thus have no
* associated . pnmr or . edf settings .
*/
{
. fourcc = DRM_FORMAT_NV61 ,
. bpp = 16 ,
. planes = 2 ,
} , {
. fourcc = DRM_FORMAT_YUV420 ,
. bpp = 12 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YVU420 ,
. bpp = 12 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YUV422 ,
. bpp = 16 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YVU422 ,
. bpp = 16 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YUV444 ,
. bpp = 24 ,
. planes = 3 ,
} , {
. fourcc = DRM_FORMAT_YVU444 ,
. 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 ;
2013-06-14 20:52:52 +02:00
/* The R8A7779 DU requires a 16 pixels pitch alignment as documented,
* 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 ;
2014-07-28 20:18:36 +02:00
unsigned int bpp ;
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 ) ;
}
2014-07-28 20:18:36 +02:00
/*
* The pitch and alignment constraints are expressed in pixels on the
* hardware side and in bytes in the DRM API .
*/
2015-11-12 02:03:47 +02:00
bpp = format - > planes = = 1 ? format - > bpp / 8 : 1 ;
2014-07-28 20:18:36 +02:00
max_pitch = 4096 * bpp ;
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
2014-07-28 20:18:36 +02:00
align = 16 * bpp ;
2013-07-04 20:05:51 +02:00
if ( mode_cmd - > pitches [ 0 ] & ( align - 1 ) | |
2014-07-28 20:18:36 +02: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 ) ;
}
}
return drm_fb_cma_create ( dev , file_priv , mode_cmd ) ;
}
2013-03-14 22:45:22 +01:00
static void rcar_du_output_poll_changed ( struct drm_device * dev )
{
struct rcar_du_device * rcdu = dev - > dev_private ;
drm_fbdev_cma_hotplug_event ( rcdu - > fbdev ) ;
}
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 ;
ret = drm_atomic_helper_check ( dev , state ) ;
if ( ret < 0 )
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
}
2015-02-23 01:02:15 +02:00
struct rcar_du_commit {
struct work_struct work ;
struct drm_device * dev ;
struct drm_atomic_state * state ;
u32 crtcs ;
} ;
static void rcar_du_atomic_complete ( struct rcar_du_commit * commit )
{
struct drm_device * dev = commit - > dev ;
struct rcar_du_device * rcdu = dev - > dev_private ;
struct drm_atomic_state * old_state = commit - > state ;
/* Apply the atomic update. */
drm_atomic_helper_commit_modeset_disables ( dev , old_state ) ;
drm_atomic_helper_commit_modeset_enables ( dev , old_state ) ;
2015-09-10 18:52:55 +03:00
drm_atomic_helper_commit_planes ( dev , old_state , true ) ;
2015-02-23 01:02:15 +02:00
drm_atomic_helper_wait_for_vblanks ( dev , old_state ) ;
drm_atomic_helper_cleanup_planes ( dev , old_state ) ;
drm_atomic_state_free ( old_state ) ;
/* Complete the commit, wake up any waiter. */
spin_lock ( & rcdu - > commit . wait . lock ) ;
rcdu - > commit . pending & = ~ commit - > crtcs ;
wake_up_all_locked ( & rcdu - > commit . wait ) ;
spin_unlock ( & rcdu - > commit . wait . lock ) ;
kfree ( commit ) ;
}
static void rcar_du_atomic_work ( struct work_struct * work )
{
struct rcar_du_commit * commit =
container_of ( work , struct rcar_du_commit , work ) ;
rcar_du_atomic_complete ( commit ) ;
}
static int rcar_du_atomic_commit ( struct drm_device * dev ,
2016-04-26 16:11:40 +02:00
struct drm_atomic_state * state ,
bool nonblock )
2015-02-23 01:02:15 +02:00
{
struct rcar_du_device * rcdu = dev - > dev_private ;
struct rcar_du_commit * commit ;
2016-06-02 00:06:27 +02:00
struct drm_crtc * crtc ;
struct drm_crtc_state * crtc_state ;
2015-02-23 01:02:15 +02:00
unsigned int i ;
int ret ;
ret = drm_atomic_helper_prepare_planes ( dev , state ) ;
if ( ret )
return ret ;
/* Allocate the commit object. */
commit = kzalloc ( sizeof ( * commit ) , GFP_KERNEL ) ;
2015-05-27 02:14:37 +03:00
if ( commit = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
2015-02-23 01:02:15 +02:00
INIT_WORK ( & commit - > work , rcar_du_atomic_work ) ;
commit - > dev = dev ;
commit - > state = state ;
/* Wait until all affected CRTCs have completed previous commits and
* mark them as pending .
*/
2016-06-02 00:06:27 +02:00
for_each_crtc_in_state ( state , crtc , crtc_state , i )
commit - > crtcs | = drm_crtc_mask ( crtc ) ;
2015-02-23 01:02:15 +02:00
spin_lock ( & rcdu - > commit . wait . lock ) ;
ret = wait_event_interruptible_locked ( rcdu - > commit . wait ,
! ( rcdu - > commit . pending & commit - > crtcs ) ) ;
if ( ret = = 0 )
rcdu - > commit . pending | = commit - > crtcs ;
spin_unlock ( & rcdu - > commit . wait . lock ) ;
if ( ret ) {
kfree ( commit ) ;
2015-05-27 02:14:37 +03:00
goto error ;
2015-02-23 01:02:15 +02:00
}
/* Swap the state, this is the point of no return. */
2016-06-10 00:06:32 +02:00
drm_atomic_helper_swap_state ( state , true ) ;
2015-02-23 01:02:15 +02:00
2016-04-26 16:11:40 +02:00
if ( nonblock )
2015-02-23 01:02:15 +02:00
schedule_work ( & commit - > work ) ;
else
rcar_du_atomic_complete ( commit ) ;
return 0 ;
2015-05-27 02:14:37 +03:00
error :
drm_atomic_helper_cleanup_planes ( dev , state ) ;
return ret ;
2015-02-23 01:02:15 +02:00
}
/* -----------------------------------------------------------------------------
* Initialization
*/
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 ,
2013-03-14 22:45:22 +01:00
. output_poll_changed = rcar_du_output_poll_changed ,
2015-02-25 18:27:19 +02:00
. atomic_check = rcar_du_atomic_check ,
2015-02-23 01:02:15 +02:00
. atomic_commit = rcar_du_atomic_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
{
static const struct {
const char * compatible ;
enum rcar_du_encoder_type type ;
} encoders [ ] = {
{ " adi,adv7123 " , RCAR_DU_ENCODER_VGA } ,
2014-03-30 21:55:38 +02:00
{ " adi,adv7511w " , RCAR_DU_ENCODER_HDMI } ,
2014-01-21 15:57:26 +01:00
{ " thine,thc63lvdm83d " , RCAR_DU_ENCODER_LVDS } ,
} ;
enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE ;
struct device_node * connector = NULL ;
struct device_node * encoder = NULL ;
2014-12-01 13:32:32 +01:00
struct device_node * ep_node = NULL ;
2014-01-21 15:57:26 +01:00
struct device_node * entity_ep_node ;
struct device_node * entity ;
int ret ;
/*
* Locate the connected entity and infer its type from the number of
* endpoints .
*/
entity = of_graph_get_remote_port_parent ( ep - > local_node ) ;
if ( ! entity ) {
dev_dbg ( rcdu - > dev , " unconnected endpoint %s, skipping \n " ,
ep - > local_node - > full_name ) ;
2015-05-26 15:07:56 +03:00
return - ENODEV ;
2014-01-21 15:57:26 +01:00
}
entity_ep_node = of_parse_phandle ( ep - > local_node , " remote-endpoint " , 0 ) ;
2014-12-22 11:46:40 +01:00
for_each_endpoint_of_node ( entity , ep_node ) {
2014-01-21 15:57:26 +01:00
if ( ep_node = = entity_ep_node )
continue ;
/*
* We ' ve found one endpoint other than the input , this must
* be an encoder . Locate the connector .
*/
encoder = entity ;
connector = of_graph_get_remote_port_parent ( ep_node ) ;
of_node_put ( ep_node ) ;
if ( ! connector ) {
dev_warn ( rcdu - > dev ,
" no connector for encoder %s, skipping \n " ,
encoder - > full_name ) ;
of_node_put ( entity_ep_node ) ;
of_node_put ( encoder ) ;
2015-05-26 15:07:56 +03:00
return - ENODEV ;
2014-01-21 15:57:26 +01:00
}
break ;
}
of_node_put ( entity_ep_node ) ;
if ( encoder ) {
/*
* If an encoder has been found , get its type based on its
* compatible string .
*/
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( encoders ) ; + + i ) {
if ( of_device_is_compatible ( encoder ,
encoders [ i ] . compatible ) ) {
enc_type = encoders [ i ] . type ;
break ;
}
}
if ( i = = ARRAY_SIZE ( encoders ) ) {
dev_warn ( rcdu - > dev ,
" unknown encoder type for %s, skipping \n " ,
encoder - > full_name ) ;
of_node_put ( encoder ) ;
of_node_put ( connector ) ;
2015-05-26 15:07:56 +03:00
return - EINVAL ;
2014-01-21 15:57:26 +01:00
}
} else {
/*
* If no encoder has been found the entity must be the
* connector .
*/
connector = entity ;
}
2014-09-17 14:41:57 +03:00
ret = rcar_du_encoder_init ( rcdu , enc_type , output , encoder , connector ) ;
2014-01-21 15:57:26 +01:00
of_node_put ( encoder ) ;
of_node_put ( connector ) ;
2015-05-26 14:59:42 +03:00
if ( ret & & ret ! = - EPROBE_DEFER )
dev_warn ( rcdu - > dev ,
" failed to initialize encoder %s (%d), skipping \n " ,
encoder - > full_name , ret ) ;
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 )
{
rcdu - > props . alpha =
drm_property_create_range ( rcdu - > ddev , 0 , " alpha " , 0 , 255 ) ;
if ( rcdu - > props . alpha = = NULL )
return - ENOMEM ;
/* The color key is expressed as an RGB888 triplet stored in a 32-bit
* 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 ;
}
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 ;
2013-03-14 22:45:22 +01:00
struct drm_fbdev_cma * fbdev ;
2014-01-21 15:57:26 +01:00
unsigned int num_encoders ;
2013-06-17 00:29:25 +02:00
unsigned int num_groups ;
2013-06-19 13:54:11 +02:00
unsigned int i ;
int ret ;
2013-03-14 22:45:22 +01:00
drm_mode_config_init ( dev ) ;
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 ;
dev - > mode_config . max_width = 4095 ;
dev - > mode_config . max_height = 2047 ;
dev - > mode_config . funcs = & rcar_du_mode_config_funcs ;
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
rcdu - > num_crtcs = rcdu - > info - > num_crtcs ;
2015-04-28 23:59:29 +03:00
ret = rcar_du_properties_init ( rcdu ) ;
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 ;
2015-04-28 17:36:33 +03:00
rgrp - > num_crtcs = min ( rcdu - > num_crtcs - 2 * i , 2U ) ;
2013-06-19 13:54:11 +02:00
2015-04-28 22:00:28 +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 ) ) {
for ( i = 0 ; i < rcdu - > num_crtcs ; + + i ) {
struct rcar_du_vsp * vsp = & rcdu - > vsps [ i ] ;
vsp - > index = i ;
vsp - > dev = rcdu ;
rcdu - > crtcs [ i ] . vsp = vsp ;
ret = rcar_du_vsp_init ( vsp ) ;
if ( ret < 0 )
return ret ;
}
2013-07-04 20:05:50 +02:00
}
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
/* Create the CRTCs. */
for ( i = 0 ; i < rcdu - > num_crtcs ; + + i ) {
struct rcar_du_group * rgrp = & rcdu - > groups [ i / 2 ] ;
ret = rcar_du_crtc_create ( rgrp , i ) ;
if ( ret < 0 )
return ret ;
}
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
/* Initialize the encoders. */
2013-06-17 13:48:27 +02:00
ret = rcar_du_lvdsenc_init ( rcdu ) ;
if ( ret < 0 )
return ret ;
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
2013-06-17 03:13:11 +02:00
/* Set the possible CRTCs and possible clones. There's always at least
* 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
}
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 ) ;
2015-02-24 03:51:26 +02:00
if ( dev - > mode_config . num_connector ) {
fbdev = drm_fbdev_cma_init ( dev , 32 , dev - > mode_config . num_crtc ,
dev - > mode_config . num_connector ) ;
if ( IS_ERR ( fbdev ) )
return PTR_ERR ( fbdev ) ;
2013-03-14 22:45:22 +01:00
2015-02-24 03:51:26 +02:00
rcdu - > fbdev = fbdev ;
} else {
dev_info ( rcdu - > dev ,
" no connector found, disabling fbdev emulation \n " ) ;
}
2013-06-19 13:54:11 +02:00
return 0 ;
}