2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-02-03 18:01:55 +09:00
/*
* Samsung SoC DP ( Display Port ) interface driver .
*
* Copyright ( C ) 2012 Samsung Electronics Co . , Ltd .
* Author : Jingoo Han < jg1 . han @ samsung . com >
*/
# include <linux/clk.h>
2014-05-09 14:25:20 +09:00
# include <linux/component.h>
2019-06-24 22:06:28 +09:00
# include <linux/err.h>
# include <linux/module.h>
# include <linux/of_graph.h>
# include <linux/platform_device.h>
2018-06-11 14:25:00 +02:00
# include <linux/pm_runtime.h>
2014-01-30 16:19:23 -05:00
# include <video/of_display_timing.h>
# include <video/of_videomode.h>
2016-03-29 09:57:03 +08:00
# include <video/videomode.h>
2012-02-03 18:01:55 +09:00
2019-06-24 22:06:28 +09:00
# include <drm/bridge/analogix_dp.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_atomic_helper.h>
2014-01-30 16:19:30 -05:00
# include <drm/drm_crtc.h>
2017-03-29 13:55:46 -05:00
# include <drm/drm_of.h>
2014-07-31 23:12:14 +05:30
# include <drm/drm_panel.h>
2019-06-24 22:06:28 +09:00
# include <drm/drm_print.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2016-03-29 09:57:03 +08:00
# include <drm/exynos_drm.h>
2015-08-15 12:14:08 -03:00
# include "exynos_drm_crtc.h"
2012-02-03 18:01:55 +09:00
2016-03-29 09:57:03 +08:00
# define to_dp(nm) container_of(nm, struct exynos_dp_device, nm)
2014-01-30 16:19:30 -05:00
2016-03-29 09:57:03 +08:00
struct exynos_dp_device {
struct drm_encoder encoder ;
2016-06-08 10:13:27 -04:00
struct drm_connector * connector ;
2016-03-29 09:57:03 +08:00
struct drm_bridge * ptn_bridge ;
struct drm_device * drm_dev ;
struct device * dev ;
2015-04-07 22:28:50 +09:00
2016-03-29 09:57:03 +08:00
struct videomode vm ;
2018-01-10 17:23:41 +01:00
struct analogix_dp_device * adp ;
2016-03-29 09:57:03 +08:00
struct analogix_dp_plat_data plat_data ;
2014-02-24 19:20:15 +09:00
} ;
2016-09-25 15:54:59 +08:00
static int exynos_dp_crtc_clock_enable ( struct analogix_dp_plat_data * plat_data ,
2016-03-29 09:57:03 +08:00
bool enable )
2012-02-03 18:01:55 +09:00
{
2016-03-29 09:57:03 +08:00
struct exynos_dp_device * dp = to_dp ( plat_data ) ;
struct drm_encoder * encoder = & dp - > encoder ;
2012-02-03 18:01:55 +09:00
2016-04-30 01:39:08 +09:00
if ( ! encoder - > crtc )
return - EPERM ;
2012-02-03 18:01:55 +09:00
2016-04-30 01:39:08 +09:00
exynos_drm_pipe_clk_enable ( to_exynos_crtc ( encoder - > crtc ) , enable ) ;
2012-07-19 13:52:59 +09:00
2012-02-03 18:01:55 +09:00
return 0 ;
}
2016-03-29 09:57:03 +08:00
static int exynos_dp_poweron ( struct analogix_dp_plat_data * plat_data )
2012-02-03 18:01:55 +09:00
{
2016-03-29 09:57:03 +08:00
return exynos_dp_crtc_clock_enable ( plat_data , true ) ;
2012-02-03 18:01:55 +09:00
}
2016-03-29 09:57:03 +08:00
static int exynos_dp_poweroff ( struct analogix_dp_plat_data * plat_data )
2012-02-03 18:01:55 +09:00
{
2016-03-29 09:57:03 +08:00
return exynos_dp_crtc_clock_enable ( plat_data , false ) ;
2012-02-03 18:01:55 +09:00
}
2016-06-29 17:15:35 +08:00
static int exynos_dp_get_modes ( struct analogix_dp_plat_data * plat_data ,
struct drm_connector * connector )
2012-02-03 18:01:55 +09:00
{
2016-03-29 09:57:03 +08:00
struct exynos_dp_device * dp = to_dp ( plat_data ) ;
2014-01-30 16:19:30 -05:00
struct drm_display_mode * mode ;
2016-03-29 09:57:03 +08:00
int num_modes = 0 ;
2014-01-30 16:19:30 -05:00
2016-03-29 09:57:03 +08:00
if ( dp - > plat_data . panel )
return num_modes ;
2014-07-31 23:12:14 +05:30
2014-01-30 16:19:30 -05:00
mode = drm_mode_create ( connector - > dev ) ;
if ( ! mode ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dp - > dev ,
" failed to create a new display mode. \n " ) ;
2016-03-29 09:57:03 +08:00
return num_modes ;
2014-01-30 16:19:30 -05:00
}
2016-02-12 13:13:56 +01:00
drm_display_mode_from_videomode ( & dp - > vm , mode ) ;
2014-01-30 16:19:30 -05: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-30 16:19:23 -05:00
2016-03-29 09:57:03 +08:00
return num_modes + 1 ;
2014-02-24 19:20:15 +09:00
}
2016-03-29 09:57:03 +08:00
static int exynos_dp_bridge_attach ( struct analogix_dp_plat_data * plat_data ,
struct drm_bridge * bridge ,
struct drm_connector * connector )
2014-01-30 16:19:30 -05:00
{
2016-03-29 09:57:03 +08:00
struct exynos_dp_device * dp = to_dp ( plat_data ) ;
2014-01-30 16:19:30 -05:00
int ret ;
2016-06-08 10:13:27 -04:00
dp - > connector = connector ;
2015-08-10 21:37:04 -03:00
2016-03-29 09:57:03 +08:00
/* Pre-empt DP connector creation if there's a bridge */
if ( dp - > ptn_bridge ) {
2016-11-28 17:59:08 +02:00
ret = drm_bridge_attach ( & dp - > encoder , dp - > ptn_bridge , bridge ) ;
2016-03-29 09:57:03 +08:00
if ( ret ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dp - > dev ,
" Failed to attach bridge to drm \n " ) ;
2016-03-29 09:57:03 +08:00
bridge - > next = NULL ;
return ret ;
2014-07-31 23:12:14 +05:30
}
}
2015-11-02 20:00:03 +09: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 09:57:03 +08:00
static void exynos_dp_nop ( struct drm_encoder * encoder )
2015-11-02 20:00:03 +09:00
{
2016-03-29 09:57:03 +08:00
/* do nothing */
2015-11-02 20:00:03 +09:00
}
2015-12-15 12:21:06 +01:00
static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
2015-08-15 12:14:08 -03:00
. mode_set = exynos_dp_mode_set ,
2016-03-29 09:57:03 +08:00
. enable = exynos_dp_nop ,
. disable = exynos_dp_nop ,
2014-01-30 16:19:23 -05:00
} ;
2015-12-15 12:21:06 +01:00
static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
2015-08-15 12:14:08 -03:00
. destroy = drm_encoder_cleanup ,
} ;
2014-01-30 16:19:23 -05:00
static int exynos_dp_dt_parse_panel ( struct exynos_dp_device * dp )
{
int ret ;
2016-02-12 13:13:56 +01:00
ret = of_get_videomode ( dp - > dev - > of_node , & dp - > vm , OF_USE_NATIVE_MODE ) ;
2014-01-30 16:19:23 -05:00
if ( ret ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dp - > dev ,
" failed: of_get_videomode() : %d \n " , ret ) ;
2014-01-30 16:19:23 -05:00
return ret ;
}
return 0 ;
}
2014-05-09 14:25:20 +09:00
static int exynos_dp_bind ( struct device * dev , struct device * master , void * data )
2012-02-03 18:01:55 +09:00
{
2014-11-17 09:54:24 +01:00
struct exynos_dp_device * dp = dev_get_drvdata ( dev ) ;
2015-08-15 12:14:08 -03:00
struct drm_encoder * encoder = & dp - > encoder ;
2016-03-29 09:57:03 +08:00
struct drm_device * drm_dev = data ;
2017-08-24 15:33:51 +02:00
int ret ;
2012-02-03 18:01:55 +09:00
2016-03-29 09:57:03 +08:00
dp - > dev = dev ;
dp - > drm_dev = drm_dev ;
2012-10-13 05:48:00 +09:00
2016-03-29 09:57:03 +08:00
dp - > plat_data . dev_type = EXYNOS_DP ;
2018-04-23 12:49:58 +02:00
dp - > plat_data . power_on_start = exynos_dp_poweron ;
2016-03-29 09:57:03 +08:00
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 05:48:00 +09:00
2016-03-29 09:57:03 +08:00
if ( ! dp - > plat_data . panel & & ! dp - > ptn_bridge ) {
2014-07-31 23:12:14 +05:30
ret = exynos_dp_dt_parse_panel ( dp ) ;
if ( ret )
return ret ;
}
2014-01-30 16:19:23 -05:00
2015-08-15 12:14:08 -03:00
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 16:20:18 +02:00
DRM_MODE_ENCODER_TMDS , NULL ) ;
2015-08-15 12:14:08 -03:00
drm_encoder_helper_add ( encoder , & exynos_dp_encoder_helper_funcs ) ;
2015-08-05 20:24:20 -03:00
2017-08-24 15:33:51 +02:00
ret = exynos_drm_set_possible_crtcs ( encoder , EXYNOS_DISPLAY_TYPE_LCD ) ;
if ( ret < 0 )
return ret ;
2016-03-29 09:57:03 +08:00
dp - > plat_data . encoder = encoder ;
2015-08-05 20:24:20 -03:00
2018-01-10 17:23:41 +01:00
dp - > adp = analogix_dp_bind ( dev , dp - > drm_dev , & dp - > plat_data ) ;
2018-01-10 17:23:42 +01:00
if ( IS_ERR ( dp - > adp ) ) {
dp - > encoder . funcs - > destroy ( & dp - > encoder ) ;
2018-01-10 17:23:41 +01:00
return PTR_ERR ( dp - > adp ) ;
2018-01-10 17:23:42 +01:00
}
2018-01-10 17:23:41 +01:00
return 0 ;
2012-02-03 18:01:55 +09:00
}
2014-05-09 14:25:20 +09:00
static void exynos_dp_unbind ( struct device * dev , struct device * master ,
2016-03-29 09:57:19 +08:00
void * data )
2012-02-03 18:01:55 +09:00
{
2018-01-10 17:23:41 +01:00
struct exynos_dp_device * dp = dev_get_drvdata ( dev ) ;
2018-01-10 17:23:42 +01:00
analogix_dp_unbind ( dp - > adp ) ;
dp - > encoder . funcs - > destroy ( & dp - > encoder ) ;
2014-05-09 14:25:20 +09: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 23:12:14 +05:30
struct device * dev = & pdev - > dev ;
2017-03-29 13:55:46 -05:00
struct device_node * np ;
2014-07-31 23:12:14 +05:30
struct exynos_dp_device * dp ;
2017-03-29 13:55:46 -05:00
struct drm_panel * panel ;
struct drm_bridge * bridge ;
int ret ;
2014-05-29 18:28:02 +09:00
2014-07-31 23:12:14 +05:30
dp = devm_kzalloc ( & pdev - > dev , sizeof ( struct exynos_dp_device ) ,
2016-03-29 09:57:19 +08:00
GFP_KERNEL ) ;
2014-07-31 23:12:14 +05:30
if ( ! dp )
return - ENOMEM ;
2016-03-29 09:57:03 +08: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 09:54:24 +01:00
platform_set_drvdata ( pdev , dp ) ;
2015-11-26 21:34:18 +09:00
/* This is for the backward compatibility. */
2016-01-29 12:09:31 -03:00
np = of_parse_phandle ( dev - > of_node , " panel " , 0 ) ;
if ( np ) {
2016-03-29 09:57:03 +08:00
dp - > plat_data . panel = of_drm_find_panel ( np ) ;
2018-05-09 15:00:39 +02:00
2016-01-29 12:09:31 -03:00
of_node_put ( np ) ;
2018-05-09 15:00:39 +02:00
if ( IS_ERR ( dp - > plat_data . panel ) )
return PTR_ERR ( dp - > plat_data . panel ) ;
2015-11-26 21:34:18 +09:00
goto out ;
2016-01-29 12:09:31 -03:00
}
2015-11-26 21:34:18 +09:00
2017-03-29 13:55:46 -05: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 ;
2018-03-05 09:57:41 +01:00
dp - > plat_data . skip_connector = ! ! bridge ;
2017-03-29 13:55:46 -05:00
dp - > ptn_bridge = bridge ;
2015-01-20 22:08:46 +05:30
2015-11-26 21:34:18 +09:00
out :
2016-03-29 09:57:03 +08:00
return component_add ( & pdev - > dev , & exynos_dp_ops ) ;
2014-05-09 14:25:20 +09:00
}
static int exynos_dp_remove ( struct platform_device * pdev )
{
2014-05-29 18:28:02 +09:00
component_del ( & pdev - > dev , & exynos_dp_ops ) ;
2012-02-03 18:01:55 +09:00
return 0 ;
}
2015-11-02 20:32:36 +09:00
# ifdef CONFIG_PM
static int exynos_dp_suspend ( struct device * dev )
{
2018-01-10 17:23:41 +01:00
struct exynos_dp_device * dp = dev_get_drvdata ( dev ) ;
return analogix_dp_suspend ( dp - > adp ) ;
2015-11-02 20:32:36 +09:00
}
static int exynos_dp_resume ( struct device * dev )
{
2018-01-10 17:23:41 +01:00
struct exynos_dp_device * dp = dev_get_drvdata ( dev ) ;
return analogix_dp_resume ( dp - > adp ) ;
2015-11-02 20:32:36 +09:00
}
# endif
static const struct dev_pm_ops exynos_dp_pm_ops = {
SET_RUNTIME_PM_OPS ( exynos_dp_suspend , exynos_dp_resume , NULL )
2018-06-11 14:25:00 +02:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2015-11-02 20:32:36 +09:00
} ;
2012-10-13 05:48:00 +09:00
static const struct of_device_id exynos_dp_match [ ] = {
{ . compatible = " samsung,exynos5-dp " } ,
{ } ,
} ;
2014-07-30 11:29:41 +09:00
MODULE_DEVICE_TABLE ( of , exynos_dp_match ) ;
2012-10-13 05:48:00 +09:00
2014-01-30 16:19:23 -05:00
struct platform_driver dp_driver = {
2012-02-03 18:01:55 +09:00
. probe = exynos_dp_probe ,
2012-12-21 13:07:39 -08:00
. remove = exynos_dp_remove ,
2012-02-03 18:01:55 +09:00
. driver = {
. name = " exynos-dp " ,
. owner = THIS_MODULE ,
2015-11-02 20:32:36 +09:00
. pm = & exynos_dp_pm_ops ,
2013-10-16 21:58:15 +05:30
. of_match_table = exynos_dp_match ,
2012-02-03 18:01:55 +09:00
} ,
} ;
MODULE_AUTHOR ( " Jingoo Han <jg1.han@samsung.com> " ) ;
2016-03-29 09:57:03 +08:00
MODULE_DESCRIPTION ( " Samsung Specific Analogix-DP Driver Extension " ) ;
2014-06-03 21:46:11 +09:00
MODULE_LICENSE ( " GPL v2 " ) ;