2013-06-19 13:54:11 +02:00
/*
2013-06-15 15:02:12 +02:00
* rcar_du_encoder . c - - R - Car Display Unit Encoder
2013-06-19 13:54:11 +02:00
*
2014-02-06 18:13:52 +01:00
* Copyright ( C ) 2013 - 2014 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 .
*/
2013-06-17 13:48:27 +02:00
# include <linux/export.h>
2013-06-19 13:54:11 +02:00
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include "rcar_du_drv.h"
2013-06-15 15:02:12 +02:00
# include "rcar_du_encoder.h"
2014-03-30 21:55:38 +02:00
# include "rcar_du_hdmicon.h"
# include "rcar_du_hdmienc.h"
2013-06-19 13:54:11 +02:00
# include "rcar_du_kms.h"
2013-06-15 14:21:51 +02:00
# include "rcar_du_lvdscon.h"
2013-06-17 13:48:27 +02:00
# include "rcar_du_lvdsenc.h"
2013-06-15 15:02:12 +02:00
# include "rcar_du_vgacon.h"
2013-06-19 13:54:11 +02:00
2013-06-15 15:02:12 +02:00
/* -----------------------------------------------------------------------------
* Common connector functions
*/
struct drm_encoder *
rcar_du_connector_best_encoder ( struct drm_connector * connector )
2013-06-19 13:54:11 +02:00
{
2013-06-15 15:02:12 +02:00
struct rcar_du_connector * rcon = to_rcar_connector ( connector ) ;
2014-03-31 01:50:16 +02:00
return rcar_encoder_to_drm_encoder ( rcon - > encoder ) ;
2013-06-19 13:54:11 +02:00
}
2013-06-15 15:02:12 +02:00
/* -----------------------------------------------------------------------------
* Encoder
*/
2015-02-20 14:05:21 +02:00
static void rcar_du_encoder_disable ( struct drm_encoder * encoder )
2013-06-15 15:02:12 +02:00
{
2013-06-17 13:48:27 +02:00
struct rcar_du_encoder * renc = to_rcar_encoder ( encoder ) ;
if ( renc - > lvds )
2015-02-20 14:05:21 +02:00
rcar_du_lvdsenc_enable ( renc - > lvds , encoder - > crtc , false ) ;
}
static void rcar_du_encoder_enable ( struct drm_encoder * encoder )
{
struct rcar_du_encoder * renc = to_rcar_encoder ( encoder ) ;
if ( renc - > lvds )
rcar_du_lvdsenc_enable ( renc - > lvds , encoder - > crtc , true ) ;
}
2015-02-22 21:02:39 +02:00
static int rcar_du_encoder_atomic_check ( struct drm_encoder * encoder ,
struct drm_crtc_state * crtc_state ,
struct drm_connector_state * conn_state )
2013-06-19 13:54:11 +02:00
{
2013-06-17 13:48:27 +02:00
struct rcar_du_encoder * renc = to_rcar_encoder ( encoder ) ;
2015-02-22 21:02:39 +02:00
struct drm_display_mode * adjusted_mode = & crtc_state - > adjusted_mode ;
const struct drm_display_mode * mode = & crtc_state - > mode ;
2013-06-19 13:54:11 +02:00
const struct drm_display_mode * panel_mode ;
2015-02-22 21:02:39 +02:00
struct drm_connector * connector = conn_state - > connector ;
2013-06-19 13:54:11 +02:00
struct drm_device * dev = encoder - > dev ;
2013-06-15 15:02:12 +02:00
/* DAC encoders have currently no restriction on the mode. */
if ( encoder - > encoder_type = = DRM_MODE_ENCODER_DAC )
2015-02-22 21:02:39 +02:00
return 0 ;
2013-06-19 13:54:11 +02:00
if ( list_empty ( & connector - > modes ) ) {
2015-02-22 21:02:39 +02:00
dev_dbg ( dev - > dev , " encoder: empty modes list \n " ) ;
return - EINVAL ;
2013-06-19 13:54:11 +02:00
}
panel_mode = list_first_entry ( & connector - > modes ,
struct drm_display_mode , head ) ;
/* We're not allowed to modify the resolution. */
if ( mode - > hdisplay ! = panel_mode - > hdisplay | |
mode - > vdisplay ! = panel_mode - > vdisplay )
2015-02-22 21:02:39 +02:00
return - EINVAL ;
2013-06-19 13:54:11 +02:00
/* The flat panel mode is fixed, just copy it to the adjusted mode. */
drm_mode_copy ( adjusted_mode , panel_mode ) ;
2013-06-17 13:48:27 +02:00
/* The internal LVDS encoder has a clock frequency operating range of
* 30 MHz to 150 MHz . Clamp the clock accordingly .
*/
if ( renc - > lvds )
adjusted_mode - > clock = clamp ( adjusted_mode - > clock ,
30000 , 150000 ) ;
2015-02-22 21:02:39 +02:00
return 0 ;
2013-06-19 13:54:11 +02:00
}
2013-06-15 15:02:12 +02:00
static void rcar_du_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
struct rcar_du_encoder * renc = to_rcar_encoder ( encoder ) ;
rcar_du_crtc_route_output ( encoder - > crtc , renc - > output ) ;
}
2013-06-19 13:54:11 +02:00
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
. mode_set = rcar_du_encoder_mode_set ,
2015-02-20 14:05:21 +02:00
. disable = rcar_du_encoder_disable ,
. enable = rcar_du_encoder_enable ,
2015-02-22 21:02:39 +02:00
. atomic_check = rcar_du_encoder_atomic_check ,
2013-06-19 13:54:11 +02:00
} ;
static const struct drm_encoder_funcs encoder_funcs = {
. destroy = drm_encoder_cleanup ,
} ;
2013-06-15 15:02:12 +02:00
int rcar_du_encoder_init ( struct rcar_du_device * rcdu ,
2013-06-17 03:13:11 +02:00
enum rcar_du_encoder_type type ,
enum rcar_du_output output ,
2014-09-17 14:41:57 +03:00
struct device_node * enc_node ,
struct device_node * con_node )
2013-06-19 13:54:11 +02:00
{
struct rcar_du_encoder * renc ;
2014-03-31 01:50:16 +02:00
struct drm_encoder * encoder ;
2013-06-17 03:13:11 +02:00
unsigned int encoder_type ;
2013-06-19 13:54:11 +02:00
int ret ;
renc = devm_kzalloc ( rcdu - > dev , sizeof ( * renc ) , GFP_KERNEL ) ;
if ( renc = = NULL )
return - ENOMEM ;
renc - > output = output ;
2014-03-31 01:50:16 +02:00
encoder = rcar_encoder_to_drm_encoder ( renc ) ;
2013-06-19 13:54:11 +02:00
2013-06-17 13:48:27 +02:00
switch ( output ) {
case RCAR_DU_OUTPUT_LVDS0 :
renc - > lvds = rcdu - > lvds [ 0 ] ;
break ;
case RCAR_DU_OUTPUT_LVDS1 :
renc - > lvds = rcdu - > lvds [ 1 ] ;
break ;
default :
break ;
}
2013-06-17 03:13:11 +02:00
switch ( type ) {
case RCAR_DU_ENCODER_VGA :
encoder_type = DRM_MODE_ENCODER_DAC ;
break ;
case RCAR_DU_ENCODER_LVDS :
encoder_type = DRM_MODE_ENCODER_LVDS ;
break ;
2014-03-30 21:55:38 +02:00
case RCAR_DU_ENCODER_HDMI :
encoder_type = DRM_MODE_ENCODER_TMDS ;
break ;
2013-06-17 03:13:11 +02:00
case RCAR_DU_ENCODER_NONE :
default :
/* No external encoder, use the internal encoder type. */
encoder_type = rcdu - > info - > routes [ output ] . encoder_type ;
break ;
}
2014-03-30 21:55:38 +02:00
if ( type = = RCAR_DU_ENCODER_HDMI ) {
ret = rcar_du_hdmienc_init ( rcdu , renc , enc_node ) ;
if ( ret < 0 )
2014-12-11 01:26:04 +02:00
goto done ;
2014-03-30 21:55:38 +02:00
} else {
ret = drm_encoder_init ( rcdu - > ddev , encoder , & encoder_funcs ,
encoder_type ) ;
if ( ret < 0 )
2014-12-11 01:26:04 +02:00
goto done ;
2014-03-30 21:55:38 +02:00
drm_encoder_helper_add ( encoder , & encoder_helper_funcs ) ;
}
2013-06-19 13:54:11 +02:00
2013-06-17 03:13:11 +02:00
switch ( encoder_type ) {
2014-09-17 02:07:52 +03:00
case DRM_MODE_ENCODER_LVDS :
2014-12-11 01:26:04 +02:00
ret = rcar_du_lvds_connector_init ( rcdu , renc , con_node ) ;
break ;
2013-06-15 15:02:12 +02:00
2013-06-17 03:13:11 +02:00
case DRM_MODE_ENCODER_DAC :
2014-12-11 01:26:04 +02:00
ret = rcar_du_vga_connector_init ( rcdu , renc ) ;
break ;
2013-06-15 15:02:12 +02:00
2014-03-30 21:55:38 +02:00
case DRM_MODE_ENCODER_TMDS :
2014-12-11 01:26:04 +02:00
ret = rcar_du_hdmi_connector_init ( rcdu , renc ) ;
break ;
2014-03-30 21:55:38 +02:00
2013-06-15 15:02:12 +02:00
default :
2014-12-11 01:26:04 +02:00
ret = - EINVAL ;
break ;
2013-06-15 15:02:12 +02:00
}
2014-12-11 01:26:04 +02:00
done :
if ( ret < 0 ) {
if ( encoder - > name )
encoder - > funcs - > destroy ( encoder ) ;
devm_kfree ( rcdu - > dev , renc ) ;
}
return ret ;
2013-06-19 13:54:11 +02:00
}