2012-02-03 13:01:55 +04:00
/*
* Samsung SoC DP ( Display Port ) interface driver .
*
* Copyright ( C ) 2012 Samsung Electronics Co . , Ltd .
* Author : Jingoo Han < jg1 . han @ samsung . 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 <linux/module.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/clk.h>
2015-01-20 19:38:46 +03:00
# include <linux/of_graph.h>
2014-05-09 09:25:20 +04:00
# include <linux/component.h>
2014-01-31 01:19:23 +04:00
# include <video/of_display_timing.h>
# include <video/of_videomode.h>
2016-03-29 04:57:03 +03:00
# include <video/videomode.h>
2012-02-03 13:01:55 +04:00
2014-01-31 01:19:23 +04:00
# include <drm/drmP.h>
2014-01-31 01:19:30 +04:00
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
2017-03-29 21:55:46 +03:00
# include <drm/drm_of.h>
2014-07-31 21:42:14 +04:00
# include <drm/drm_panel.h>
2014-01-31 01:19:23 +04:00
2016-03-29 04:57:03 +03:00
# include <drm/bridge/analogix_dp.h>
# include <drm/exynos_drm.h>
2015-08-15 18:14:08 +03:00
# include "exynos_drm_crtc.h"
2012-02-03 13:01:55 +04:00
2016-03-29 04:57:03 +03:00
# define to_dp(nm) container_of(nm, struct exynos_dp_device, nm)
2014-01-31 01:19:30 +04:00
2016-03-29 04:57:03 +03:00
struct exynos_dp_device {
struct drm_encoder encoder ;
2016-06-08 17:13:27 +03:00
struct drm_connector * connector ;
2016-03-29 04:57:03 +03:00
struct drm_bridge * ptn_bridge ;
struct drm_device * drm_dev ;
struct device * dev ;
2015-04-07 16:28:50 +03:00
2016-03-29 04:57:03 +03:00
struct videomode vm ;
struct analogix_dp_plat_data plat_data ;
2014-02-24 14:20:15 +04:00
} ;
2016-09-25 10:54:59 +03:00
static int exynos_dp_crtc_clock_enable ( struct analogix_dp_plat_data * plat_data ,
2016-03-29 04:57:03 +03:00
bool enable )
2012-02-03 13:01:55 +04:00
{
2016-03-29 04:57:03 +03:00
struct exynos_dp_device * dp = to_dp ( plat_data ) ;
struct drm_encoder * encoder = & dp - > encoder ;
2012-02-03 13:01:55 +04:00
2016-04-29 19:39:08 +03:00
if ( ! encoder - > crtc )
return - EPERM ;
2012-02-03 13:01:55 +04:00
2016-04-29 19:39:08 +03:00
exynos_drm_pipe_clk_enable ( to_exynos_crtc ( encoder - > crtc ) , enable ) ;
2012-07-19 08:52:59 +04:00
2012-02-03 13:01:55 +04:00
return 0 ;
}
2016-03-29 04:57:03 +03:00
static int exynos_dp_poweron ( struct analogix_dp_plat_data * plat_data )
2012-02-03 13:01:55 +04:00
{
2016-03-29 04:57:03 +03:00
return exynos_dp_crtc_clock_enable ( plat_data , true ) ;
2012-02-03 13:01:55 +04:00
}
2016-03-29 04:57:03 +03:00
static int exynos_dp_poweroff ( struct analogix_dp_plat_data * plat_data )
2012-02-03 13:01:55 +04:00
{
2016-03-29 04:57:03 +03:00
return exynos_dp_crtc_clock_enable ( plat_data , false ) ;
2012-02-03 13:01:55 +04:00
}
2016-06-29 12:15:35 +03:00
static int exynos_dp_get_modes ( struct analogix_dp_plat_data * plat_data ,
struct drm_connector * connector )
2012-02-03 13:01:55 +04:00
{
2016-03-29 04:57:03 +03:00
struct exynos_dp_device * dp = to_dp ( plat_data ) ;
2014-01-31 01:19:30 +04:00
struct drm_display_mode * mode ;
2016-03-29 04:57:03 +03:00
int num_modes = 0 ;
2014-01-31 01:19:30 +04:00
2016-03-29 04:57:03 +03:00
if ( dp - > plat_data . panel )
return num_modes ;
2014-07-31 21:42:14 +04:00
2014-01-31 01:19:30 +04:00
mode = drm_mode_create ( connector - > dev ) ;
if ( ! mode ) {
DRM_ERROR ( " failed to create a new display mode. \n " ) ;
2016-03-29 04:57:03 +03:00
return num_modes ;
2014-01-31 01:19:30 +04:00
}
2016-02-12 15:13:56 +03:00
drm_display_mode_from_videomode ( & dp - > vm , mode ) ;
2014-01-31 01:19:30 +04:00
connector - > display_info . width_mm = mode - > width_mm ;
connector - > display_info . height_mm = mode - > height_mm ;
mode - > type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ;
drm_mode_set_name ( mode ) ;
drm_mode_probed_add ( connector , mode ) ;
2014-01-31 01:19:23 +04:00
2016-03-29 04:57:03 +03:00
return num_modes + 1 ;
2014-02-24 14:20:15 +04:00
}
2016-03-29 04:57:03 +03:00
static int exynos_dp_bridge_attach ( struct analogix_dp_plat_data * plat_data ,
struct drm_bridge * bridge ,
struct drm_connector * connector )
2014-01-31 01:19:30 +04:00
{
2016-03-29 04:57:03 +03:00
struct exynos_dp_device * dp = to_dp ( plat_data ) ;
2014-01-31 01:19:30 +04:00
int ret ;
2016-06-08 17:13:27 +03:00
dp - > connector = connector ;
2015-08-11 03:37:04 +03:00
2016-03-29 04:57:03 +03:00
/* Pre-empt DP connector creation if there's a bridge */
if ( dp - > ptn_bridge ) {
2016-11-28 18:59:08 +03:00
ret = drm_bridge_attach ( & dp - > encoder , dp - > ptn_bridge , bridge ) ;
2016-03-29 04:57:03 +03:00
if ( ret ) {
DRM_ERROR ( " Failed to attach bridge to drm \n " ) ;
bridge - > next = NULL ;
return ret ;
2014-07-31 21:42:14 +04:00
}
}
2015-11-02 14:00:03 +03:00
return 0 ;
}
static void exynos_dp_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
}
2016-03-29 04:57:03 +03:00
static void exynos_dp_nop ( struct drm_encoder * encoder )
2015-11-02 14:00:03 +03:00
{
2016-03-29 04:57:03 +03:00
/* do nothing */
2015-11-02 14:00:03 +03:00
}
2015-12-15 14:21:06 +03:00
static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
2015-08-15 18:14:08 +03:00
. mode_set = exynos_dp_mode_set ,
2016-03-29 04:57:03 +03:00
. enable = exynos_dp_nop ,
. disable = exynos_dp_nop ,
2014-01-31 01:19:23 +04:00
} ;
2015-12-15 14:21:06 +03:00
static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
2015-08-15 18:14:08 +03:00
. destroy = drm_encoder_cleanup ,
} ;
2014-01-31 01:19:23 +04:00
static int exynos_dp_dt_parse_panel ( struct exynos_dp_device * dp )
{
int ret ;
2016-02-12 15:13:56 +03:00
ret = of_get_videomode ( dp - > dev - > of_node , & dp - > vm , OF_USE_NATIVE_MODE ) ;
2014-01-31 01:19:23 +04:00
if ( ret ) {
DRM_ERROR ( " failed: of_get_videomode() : %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2014-05-09 09:25:20 +04:00
static int exynos_dp_bind ( struct device * dev , struct device * master , void * data )
2012-02-03 13:01:55 +04:00
{
2014-11-17 11:54:24 +03:00
struct exynos_dp_device * dp = dev_get_drvdata ( dev ) ;
2015-08-15 18:14:08 +03:00
struct drm_encoder * encoder = & dp - > encoder ;
2016-03-29 04:57:03 +03:00
struct drm_device * drm_dev = data ;
int pipe , ret ;
2012-02-03 13:01:55 +04:00
2016-03-29 04:57:03 +03:00
/*
* Just like the probe function said , we don ' t need the
* device drvrate anymore , we should leave the charge to
* analogix dp driver , set the device drvdata to NULL .
*/
dev_set_drvdata ( dev , NULL ) ;
2012-02-03 13:01:55 +04:00
2016-03-29 04:57:03 +03:00
dp - > dev = dev ;
dp - > drm_dev = drm_dev ;
2012-10-13 00:48:00 +04:00
2016-03-29 04:57:03 +03:00
dp - > plat_data . dev_type = EXYNOS_DP ;
dp - > plat_data . power_on = exynos_dp_poweron ;
dp - > plat_data . power_off = exynos_dp_poweroff ;
dp - > plat_data . attach = exynos_dp_bridge_attach ;
dp - > plat_data . get_modes = exynos_dp_get_modes ;
2012-10-13 00:48:00 +04:00
2016-03-29 04:57:03 +03:00
if ( ! dp - > plat_data . panel & & ! dp - > ptn_bridge ) {
2014-07-31 21:42:14 +04:00
ret = exynos_dp_dt_parse_panel ( dp ) ;
if ( ret )
return ret ;
}
2014-01-31 01:19:23 +04:00
2015-08-15 18:14:08 +03:00
pipe = exynos_drm_crtc_get_pipe_from_type ( drm_dev ,
EXYNOS_DISPLAY_TYPE_LCD ) ;
if ( pipe < 0 )
return pipe ;
encoder - > possible_crtcs = 1 < < pipe ;
DRM_DEBUG_KMS ( " possible_crtcs = 0x%x \n " , encoder - > possible_crtcs ) ;
drm_encoder_init ( drm_dev , encoder , & exynos_dp_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 17:20:18 +03:00
DRM_MODE_ENCODER_TMDS , NULL ) ;
2015-08-15 18:14:08 +03:00
drm_encoder_helper_add ( encoder , & exynos_dp_encoder_helper_funcs ) ;
2015-08-06 02:24:20 +03:00
2016-03-29 04:57:03 +03:00
dp - > plat_data . encoder = encoder ;
2015-08-06 02:24:20 +03:00
2016-03-29 04:57:03 +03:00
return analogix_dp_bind ( dev , dp - > drm_dev , & dp - > plat_data ) ;
2012-02-03 13:01:55 +04:00
}
2014-05-09 09:25:20 +04:00
static void exynos_dp_unbind ( struct device * dev , struct device * master ,
2016-03-29 04:57:19 +03:00
void * data )
2012-02-03 13:01:55 +04:00
{
2016-03-29 04:57:03 +03:00
return analogix_dp_unbind ( dev , master , data ) ;
2014-05-09 09:25:20 +04:00
}
static const struct component_ops exynos_dp_ops = {
. bind = exynos_dp_bind ,
. unbind = exynos_dp_unbind ,
} ;
static int exynos_dp_probe ( struct platform_device * pdev )
{
2014-07-31 21:42:14 +04:00
struct device * dev = & pdev - > dev ;
2017-03-29 21:55:46 +03:00
struct device_node * np ;
2014-07-31 21:42:14 +04:00
struct exynos_dp_device * dp ;
2017-03-29 21:55:46 +03:00
struct drm_panel * panel ;
struct drm_bridge * bridge ;
int ret ;
2014-05-29 13:28:02 +04:00
2014-07-31 21:42:14 +04:00
dp = devm_kzalloc ( & pdev - > dev , sizeof ( struct exynos_dp_device ) ,
2016-03-29 04:57:19 +03:00
GFP_KERNEL ) ;
2014-07-31 21:42:14 +04:00
if ( ! dp )
return - ENOMEM ;
2016-03-29 04:57:03 +03:00
/*
* We just use the drvdata until driver run into component
* add function , and then we would set drvdata to null , so
* that analogix dp driver would take charge of the drvdata .
*/
2014-11-17 11:54:24 +03:00
platform_set_drvdata ( pdev , dp ) ;
2015-11-26 15:34:18 +03:00
/* This is for the backward compatibility. */
2016-01-29 18:09:31 +03:00
np = of_parse_phandle ( dev - > of_node , " panel " , 0 ) ;
if ( np ) {
2016-03-29 04:57:03 +03:00
dp - > plat_data . panel = of_drm_find_panel ( np ) ;
2016-01-29 18:09:31 +03:00
of_node_put ( np ) ;
2016-03-29 04:57:03 +03:00
if ( ! dp - > plat_data . panel )
2014-07-31 21:42:14 +04:00
return - EPROBE_DEFER ;
2015-11-26 15:34:18 +03:00
goto out ;
2016-01-29 18:09:31 +03:00
}
2015-11-26 15:34:18 +03:00
2017-03-29 21:55:46 +03:00
ret = drm_of_find_panel_or_bridge ( dev - > of_node , 0 , 0 , & panel , & bridge ) ;
if ( ret )
return ret ;
/* The remote port can be either a panel or a bridge */
dp - > plat_data . panel = panel ;
dp - > ptn_bridge = bridge ;
2015-01-20 19:38:46 +03:00
2015-11-26 15:34:18 +03:00
out :
2016-03-29 04:57:03 +03:00
return component_add ( & pdev - > dev , & exynos_dp_ops ) ;
2014-05-09 09:25:20 +04:00
}
static int exynos_dp_remove ( struct platform_device * pdev )
{
2014-05-29 13:28:02 +04:00
component_del ( & pdev - > dev , & exynos_dp_ops ) ;
2012-02-03 13:01:55 +04:00
return 0 ;
}
2015-11-02 14:32:36 +03:00
# ifdef CONFIG_PM
static int exynos_dp_suspend ( struct device * dev )
{
2016-03-29 04:57:03 +03:00
return analogix_dp_suspend ( dev ) ;
2015-11-02 14:32:36 +03:00
}
static int exynos_dp_resume ( struct device * dev )
{
2016-03-29 04:57:03 +03:00
return analogix_dp_resume ( dev ) ;
2015-11-02 14:32:36 +03:00
}
# endif
static const struct dev_pm_ops exynos_dp_pm_ops = {
SET_RUNTIME_PM_OPS ( exynos_dp_suspend , exynos_dp_resume , NULL )
} ;
2012-10-13 00:48:00 +04:00
static const struct of_device_id exynos_dp_match [ ] = {
{ . compatible = " samsung,exynos5-dp " } ,
{ } ,
} ;
2014-07-30 06:29:41 +04:00
MODULE_DEVICE_TABLE ( of , exynos_dp_match ) ;
2012-10-13 00:48:00 +04:00
2014-01-31 01:19:23 +04:00
struct platform_driver dp_driver = {
2012-02-03 13:01:55 +04:00
. probe = exynos_dp_probe ,
2012-12-22 01:07:39 +04:00
. remove = exynos_dp_remove ,
2012-02-03 13:01:55 +04:00
. driver = {
. name = " exynos-dp " ,
. owner = THIS_MODULE ,
2015-11-02 14:32:36 +03:00
. pm = & exynos_dp_pm_ops ,
2013-10-16 20:28:15 +04:00
. of_match_table = exynos_dp_match ,
2012-02-03 13:01:55 +04:00
} ,
} ;
MODULE_AUTHOR ( " Jingoo Han <jg1.han@samsung.com> " ) ;
2016-03-29 04:57:03 +03:00
MODULE_DESCRIPTION ( " Samsung Specific Analogix-DP Driver Extension " ) ;
2014-06-03 16:46:11 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;