2014-03-17 13:03:56 +01:00
/*
* Exynos DRM Parallel output support .
*
* Copyright ( c ) 2014 Samsung Electronics Co . , Ltd
*
* Contacts : Andrzej Hajda < a . hajda @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_panel.h>
2015-06-01 12:04:44 -03:00
# include <drm/drm_atomic_helper.h>
2014-03-17 13:03:56 +01:00
2016-05-03 15:47:24 +02:00
# include <linux/of_graph.h>
2014-03-17 13:03:56 +01:00
# include <linux/regulator/consumer.h>
# include <video/of_videomode.h>
# include <video/videomode.h>
2015-08-05 20:24:20 -03:00
# include "exynos_drm_crtc.h"
2014-03-17 13:03:56 +01:00
struct exynos_dpi {
2015-08-15 12:14:08 -03:00
struct drm_encoder encoder ;
2014-03-17 13:03:56 +01:00
struct device * dev ;
struct device_node * panel_node ;
struct drm_panel * panel ;
struct drm_connector connector ;
struct videomode * vm ;
} ;
# define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
2015-08-15 12:14:08 -03:00
static inline struct exynos_dpi * encoder_to_dpi ( struct drm_encoder * e )
2014-11-17 09:54:26 +01:00
{
2015-08-11 17:38:06 +09:00
return container_of ( e , struct exynos_dpi , encoder ) ;
2014-11-17 09:54:26 +01:00
}
2014-03-17 13:03:56 +01:00
static enum drm_connector_status
exynos_dpi_detect ( struct drm_connector * connector , bool force )
{
struct exynos_dpi * ctx = connector_to_dpi ( connector ) ;
2014-06-10 22:57:57 +02:00
if ( ctx - > panel & & ! ctx - > panel - > connector )
2014-04-03 16:26:00 +02:00
drm_panel_attach ( ctx - > panel , & ctx - > connector ) ;
2014-03-17 13:03:56 +01:00
2014-04-03 16:26:00 +02:00
return connector_status_connected ;
2014-03-17 13:03:56 +01:00
}
static void exynos_dpi_connector_destroy ( struct drm_connector * connector )
{
2014-05-29 16:57:41 +01:00
drm_connector_unregister ( connector ) ;
2014-03-17 13:03:56 +01:00
drm_connector_cleanup ( connector ) ;
}
2015-12-15 12:21:06 +01:00
static const struct drm_connector_funcs exynos_dpi_connector_funcs = {
2014-03-17 13:03:56 +01:00
. detect = exynos_dpi_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = exynos_dpi_connector_destroy ,
2015-06-01 12:04:44 -03:00
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
2014-03-17 13:03:56 +01:00
} ;
static int exynos_dpi_get_modes ( struct drm_connector * connector )
{
struct exynos_dpi * ctx = connector_to_dpi ( connector ) ;
/* fimd timings gets precedence over panel modes */
if ( ctx - > vm ) {
struct drm_display_mode * mode ;
mode = drm_mode_create ( connector - > dev ) ;
if ( ! mode ) {
DRM_ERROR ( " failed to create a new display mode \n " ) ;
return 0 ;
}
drm_display_mode_from_videomode ( ctx - > vm , mode ) ;
mode - > type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ;
drm_mode_probed_add ( connector , mode ) ;
return 1 ;
}
if ( ctx - > panel )
return ctx - > panel - > funcs - > get_modes ( ctx - > panel ) ;
return 0 ;
}
2015-12-15 12:21:06 +01:00
static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
2014-03-17 13:03:56 +01:00
. get_modes = exynos_dpi_get_modes ,
} ;
2015-08-15 12:14:08 -03:00
static int exynos_dpi_create_connector ( struct drm_encoder * encoder )
2014-03-17 13:03:56 +01:00
{
2015-08-15 12:14:08 -03:00
struct exynos_dpi * ctx = encoder_to_dpi ( encoder ) ;
2014-03-17 13:03:56 +01:00
struct drm_connector * connector = & ctx - > connector ;
int ret ;
2014-04-01 19:45:28 +09:00
connector - > polled = DRM_CONNECTOR_POLL_HPD ;
2014-03-17 13:03:56 +01:00
ret = drm_connector_init ( encoder - > dev , connector ,
& exynos_dpi_connector_funcs ,
DRM_MODE_CONNECTOR_VGA ) ;
if ( ret ) {
DRM_ERROR ( " failed to initialize connector with drm \n " ) ;
return ret ;
}
drm_connector_helper_add ( connector , & exynos_dpi_connector_helper_funcs ) ;
drm_mode_connector_attach_encoder ( connector , encoder ) ;
return 0 ;
}
2015-08-15 12:14:08 -03:00
static void exynos_dpi_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
}
static void exynos_dpi_enable ( struct drm_encoder * encoder )
2014-03-17 13:03:56 +01:00
{
2015-08-11 17:38:06 +09:00
struct exynos_dpi * ctx = encoder_to_dpi ( encoder ) ;
2015-08-10 21:37:04 -03:00
2014-07-31 23:12:05 +05:30
if ( ctx - > panel ) {
drm_panel_prepare ( ctx - > panel ) ;
2014-03-17 13:03:56 +01:00
drm_panel_enable ( ctx - > panel ) ;
2014-07-31 23:12:05 +05:30
}
2014-03-17 13:03:56 +01:00
}
2015-08-15 12:14:08 -03:00
static void exynos_dpi_disable ( struct drm_encoder * encoder )
2014-03-17 13:03:56 +01:00
{
2015-08-11 17:38:06 +09:00
struct exynos_dpi * ctx = encoder_to_dpi ( encoder ) ;
2015-08-10 21:37:04 -03:00
2014-07-31 23:12:05 +05:30
if ( ctx - > panel ) {
2014-03-17 13:03:56 +01:00
drm_panel_disable ( ctx - > panel ) ;
2014-07-31 23:12:05 +05:30
drm_panel_unprepare ( ctx - > panel ) ;
}
2014-03-17 13:03:56 +01:00
}
2015-12-15 12:21:06 +01:00
static const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = {
2015-08-15 12:14:08 -03:00
. mode_set = exynos_dpi_mode_set ,
2015-08-10 21:37:04 -03:00
. enable = exynos_dpi_enable ,
. disable = exynos_dpi_disable ,
2014-03-17 13:03:56 +01:00
} ;
2015-12-15 12:21:06 +01:00
static const struct drm_encoder_funcs exynos_dpi_encoder_funcs = {
2015-08-15 12:14:08 -03:00
. destroy = drm_encoder_cleanup ,
} ;
2014-03-17 13:03:56 +01:00
enum {
FIMD_PORT_IN0 ,
FIMD_PORT_IN1 ,
FIMD_PORT_IN2 ,
FIMD_PORT_RGB ,
FIMD_PORT_WRB ,
} ;
static int exynos_dpi_parse_dt ( struct exynos_dpi * ctx )
{
struct device * dev = ctx - > dev ;
struct device_node * dn = dev - > of_node ;
struct device_node * np ;
2017-03-22 08:26:06 -05:00
ctx - > panel_node = of_graph_get_remote_node ( dn , FIMD_PORT_RGB , 0 ) ;
2014-03-17 13:03:56 +01:00
np = of_get_child_by_name ( dn , " display-timings " ) ;
if ( np ) {
struct videomode * vm ;
int ret ;
of_node_put ( np ) ;
vm = devm_kzalloc ( dev , sizeof ( * ctx - > vm ) , GFP_KERNEL ) ;
if ( ! vm )
return - ENOMEM ;
ret = of_get_videomode ( dn , vm , 0 ) ;
2014-04-03 16:26:00 +02:00
if ( ret < 0 ) {
devm_kfree ( dev , vm ) ;
2014-03-17 13:03:56 +01:00
return ret ;
2014-04-03 16:26:00 +02:00
}
2014-03-17 13:03:56 +01:00
ctx - > vm = vm ;
return 0 ;
}
if ( ! ctx - > panel_node )
return - EINVAL ;
return 0 ;
}
2015-08-15 12:14:08 -03:00
int exynos_dpi_bind ( struct drm_device * dev , struct drm_encoder * encoder )
2015-08-05 20:24:20 -03:00
{
int ret ;
2015-08-15 12:14:08 -03:00
drm_encoder_init ( dev , encoder , & exynos_dpi_encoder_funcs ,
drm: Pass 'name' to drm_encoder_init()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4;
@@
drm_encoder_init(E1, E2, E3, E4
+ ,NULL
)
v2: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670818-2966-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:20:18 +02:00
DRM_MODE_ENCODER_TMDS , NULL ) ;
2015-08-15 12:14:08 -03:00
drm_encoder_helper_add ( encoder , & exynos_dpi_encoder_helper_funcs ) ;
2017-08-24 15:33:51 +02:00
ret = exynos_drm_set_possible_crtcs ( encoder , EXYNOS_DISPLAY_TYPE_LCD ) ;
if ( ret < 0 )
return ret ;
2015-08-15 12:14:08 -03:00
ret = exynos_dpi_create_connector ( encoder ) ;
2015-08-05 20:24:20 -03:00
if ( ret ) {
DRM_ERROR ( " failed to create connector ret = %d \n " , ret ) ;
2015-08-15 12:14:08 -03:00
drm_encoder_cleanup ( encoder ) ;
2015-08-05 20:24:20 -03:00
return ret ;
}
return 0 ;
}
2015-08-15 12:14:08 -03:00
struct drm_encoder * exynos_dpi_probe ( struct device * dev )
2014-03-17 13:03:56 +01:00
{
struct exynos_dpi * ctx ;
int ret ;
ctx = devm_kzalloc ( dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
2014-11-17 09:54:26 +01:00
return ERR_PTR ( - ENOMEM ) ;
2014-03-17 13:03:56 +01:00
ctx - > dev = dev ;
ret = exynos_dpi_parse_dt ( ctx ) ;
2014-04-03 16:26:00 +02:00
if ( ret < 0 ) {
devm_kfree ( dev , ctx ) ;
2015-06-11 23:23:37 +09:00
return NULL ;
2014-04-03 16:26:00 +02:00
}
if ( ctx - > panel_node ) {
ctx - > panel = of_drm_find_panel ( ctx - > panel_node ) ;
2015-06-11 23:23:37 +09:00
if ( ! ctx - > panel )
2014-04-03 16:26:00 +02:00
return ERR_PTR ( - EPROBE_DEFER ) ;
}
2014-03-17 13:03:56 +01:00
2015-08-11 17:38:06 +09:00
return & ctx - > encoder ;
2014-03-17 13:03:56 +01:00
}
2015-08-15 12:14:08 -03:00
int exynos_dpi_remove ( struct drm_encoder * encoder )
2014-03-17 13:03:56 +01:00
{
2015-08-11 17:38:06 +09:00
struct exynos_dpi * ctx = encoder_to_dpi ( encoder ) ;
2014-05-09 14:25:20 +09:00
2015-08-11 17:38:06 +09:00
exynos_dpi_disable ( & ctx - > encoder ) ;
2014-09-09 15:16:11 +02:00
if ( ctx - > panel )
drm_panel_detach ( ctx - > panel ) ;
2014-03-17 13:03:56 +01:00
return 0 ;
}