2014-12-05 14:25:05 +08:00
/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
*
2014-12-05 14:26:31 +08:00
* derived from imx - hdmi . c ( renamed to bridge / dw_hdmi . c now )
2014-12-05 14:25:05 +08:00
*
* 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 <linux/module.h>
# include <linux/platform_device.h>
# include <linux/component.h>
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
2014-12-05 14:26:31 +08:00
# include <drm/bridge/dw_hdmi.h>
2014-12-05 14:25:05 +08:00
# include <video/imx-ipu-v3.h>
# include <linux/regmap.h>
# include <drm/drm_of.h>
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_edid.h>
# include <drm/drm_encoder_slave.h>
# include "imx-drm.h"
2014-12-05 14:26:31 +08:00
struct imx_hdmi {
2014-12-05 14:25:05 +08:00
struct device * dev ;
2016-07-06 14:49:24 +02:00
struct drm_encoder encoder ;
2014-12-05 14:25:05 +08:00
struct regmap * regmap ;
} ;
2016-07-06 14:49:24 +02:00
static inline struct imx_hdmi * enc_to_imx_hdmi ( struct drm_encoder * e )
{
return container_of ( e , struct imx_hdmi , encoder ) ;
}
2014-12-05 14:26:31 +08:00
static const struct dw_hdmi_mpll_config imx_mpll_cfg [ ] = {
2014-12-05 14:25:50 +08:00
{
45250000 , {
{ 0x01e0 , 0x0000 } ,
{ 0x21e1 , 0x0000 } ,
{ 0x41e2 , 0x0000 }
} ,
} , {
92500000 , {
{ 0x0140 , 0x0005 } ,
{ 0x2141 , 0x0005 } ,
{ 0x4142 , 0x0005 } ,
} ,
} , {
148500000 , {
{ 0x00a0 , 0x000a } ,
{ 0x20a1 , 0x000a } ,
{ 0x40a2 , 0x000a } ,
} ,
} , {
2015-10-15 15:42:17 +02:00
216000000 , {
2014-12-05 14:25:50 +08:00
{ 0x00a0 , 0x000a } ,
{ 0x2001 , 0x000f } ,
{ 0x4002 , 0x000f } ,
} ,
2015-10-15 15:42:17 +02:00
} , {
~ 0UL , {
{ 0x0000 , 0x0000 } ,
{ 0x0000 , 0x0000 } ,
{ 0x0000 , 0x0000 } ,
} ,
2014-12-05 14:25:50 +08:00
}
} ;
2014-12-05 14:26:31 +08:00
static const struct dw_hdmi_curr_ctrl imx_cur_ctr [ ] = {
2014-12-05 14:25:50 +08:00
/* pixelclk bpp8 bpp10 bpp12 */
{
54000000 , { 0x091c , 0x091c , 0x06dc } ,
} , {
58400000 , { 0x091c , 0x06dc , 0x06dc } ,
} , {
72000000 , { 0x06dc , 0x06dc , 0x091c } ,
} , {
74250000 , { 0x06dc , 0x0b5c , 0x091c } ,
} , {
118800000 , { 0x091c , 0x091c , 0x06dc } ,
} , {
216000000 , { 0x06dc , 0x0b5c , 0x091c } ,
2015-01-07 23:49:41 +01:00
} , {
~ 0UL , { 0x0000 , 0x0000 , 0x0000 } ,
} ,
2014-12-05 14:25:50 +08:00
} ;
2015-03-31 18:23:16 +01:00
/*
* Resistance term 133 Ohm Cfg
* PREEMP config 0.00
* TX / CK level 10
*/
2015-03-31 23:56:10 -04:00
static const struct dw_hdmi_phy_config imx_phy_config [ ] = {
/*pixelclk symbol term vlev */
2015-10-15 15:42:17 +02:00
{ 216000000 , 0x800d , 0x0005 , 0x01ad } ,
2015-03-31 23:56:10 -04:00
{ ~ 0UL , 0x0000 , 0x0000 , 0x0000 }
2014-12-05 14:25:50 +08:00
} ;
2014-12-05 14:26:31 +08:00
static int dw_hdmi_imx_parse_dt ( struct imx_hdmi * hdmi )
2014-12-05 14:25:05 +08:00
{
struct device_node * np = hdmi - > dev - > of_node ;
hdmi - > regmap = syscon_regmap_lookup_by_phandle ( np , " gpr " ) ;
if ( IS_ERR ( hdmi - > regmap ) ) {
dev_err ( hdmi - > dev , " Unable to get gpr \n " ) ;
return PTR_ERR ( hdmi - > regmap ) ;
}
return 0 ;
}
2014-12-05 14:26:31 +08:00
static void dw_hdmi_imx_encoder_disable ( struct drm_encoder * encoder )
2014-12-05 14:25:05 +08:00
{
}
2016-07-08 17:41:01 +08:00
static void dw_hdmi_imx_encoder_enable ( struct drm_encoder * encoder )
2014-12-05 14:25:05 +08:00
{
2016-07-06 14:49:24 +02:00
struct imx_hdmi * hdmi = enc_to_imx_hdmi ( encoder ) ;
2015-02-24 11:41:28 +01:00
int mux = drm_of_encoder_active_port_id ( hdmi - > dev - > of_node , encoder ) ;
2014-12-05 14:25:05 +08:00
regmap_update_bits ( hdmi - > regmap , IOMUXC_GPR3 ,
IMX6Q_GPR3_HDMI_MUX_CTL_MASK ,
mux < < IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT ) ;
}
2016-07-06 14:49:24 +02:00
static int dw_hdmi_imx_atomic_check ( struct drm_encoder * encoder ,
struct drm_crtc_state * crtc_state ,
struct drm_connector_state * conn_state )
{
struct imx_crtc_state * imx_crtc_state = to_imx_crtc_state ( crtc_state ) ;
imx_crtc_state - > bus_format = MEDIA_BUS_FMT_RGB888_1X24 ;
imx_crtc_state - > di_hsync_pin = 2 ;
imx_crtc_state - > di_vsync_pin = 3 ;
return 0 ;
}
2015-12-15 12:21:09 +01:00
static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
2016-07-08 17:41:01 +08:00
. enable = dw_hdmi_imx_encoder_enable ,
2014-12-05 14:26:31 +08:00
. disable = dw_hdmi_imx_encoder_disable ,
2016-07-06 14:49:24 +02:00
. atomic_check = dw_hdmi_imx_atomic_check ,
2014-12-05 14:25:05 +08:00
} ;
2015-12-15 12:21:09 +01:00
static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
2014-12-05 14:25:05 +08:00
. destroy = drm_encoder_cleanup ,
} ;
2015-01-07 23:52:15 +01:00
static enum drm_mode_status imx6q_hdmi_mode_valid ( struct drm_connector * con ,
struct drm_display_mode * mode )
{
if ( mode - > clock < 13500 )
return MODE_CLOCK_LOW ;
2015-10-15 15:42:17 +02:00
/* FIXME: Hardware is capable of 266MHz, but setup data is missing. */
if ( mode - > clock > 216000 )
2015-01-07 23:52:15 +01:00
return MODE_CLOCK_HIGH ;
return MODE_OK ;
}
static enum drm_mode_status imx6dl_hdmi_mode_valid ( struct drm_connector * con ,
struct drm_display_mode * mode )
{
if ( mode - > clock < 13500 )
return MODE_CLOCK_LOW ;
2015-10-15 15:42:17 +02:00
/* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
if ( mode - > clock > 216000 )
2015-01-07 23:52:15 +01:00
return MODE_CLOCK_HIGH ;
return MODE_OK ;
}
2014-12-05 14:26:31 +08:00
static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {
2015-01-07 23:52:15 +01:00
. mpll_cfg = imx_mpll_cfg ,
. cur_ctr = imx_cur_ctr ,
2015-03-31 23:56:10 -04:00
. phy_config = imx_phy_config ,
2015-01-07 23:52:15 +01:00
. mode_valid = imx6q_hdmi_mode_valid ,
2014-12-05 14:25:05 +08:00
} ;
2014-12-05 14:26:31 +08:00
static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = {
2014-12-05 14:25:50 +08:00
. mpll_cfg = imx_mpll_cfg ,
. cur_ctr = imx_cur_ctr ,
2015-03-31 23:56:10 -04:00
. phy_config = imx_phy_config ,
2015-01-07 23:52:15 +01:00
. mode_valid = imx6dl_hdmi_mode_valid ,
2014-12-05 14:25:05 +08:00
} ;
2014-12-05 14:26:31 +08:00
static const struct of_device_id dw_hdmi_imx_dt_ids [ ] = {
2014-12-05 14:25:05 +08:00
{ . compatible = " fsl,imx6q-hdmi " ,
. data = & imx6q_hdmi_drv_data
} , {
. compatible = " fsl,imx6dl-hdmi " ,
. data = & imx6dl_hdmi_drv_data
} ,
{ } ,
} ;
2014-12-05 14:26:31 +08:00
MODULE_DEVICE_TABLE ( of , dw_hdmi_imx_dt_ids ) ;
2014-12-05 14:25:05 +08:00
2014-12-05 14:26:31 +08:00
static int dw_hdmi_imx_bind ( struct device * dev , struct device * master ,
void * data )
2014-12-05 14:25:05 +08:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
2014-12-05 14:26:31 +08:00
const struct dw_hdmi_plat_data * plat_data ;
2014-12-05 14:25:05 +08:00
const struct of_device_id * match ;
struct drm_device * drm = data ;
struct drm_encoder * encoder ;
2014-12-05 14:26:31 +08:00
struct imx_hdmi * hdmi ;
2014-12-05 14:25:05 +08:00
int ret ;
if ( ! pdev - > dev . of_node )
return - ENODEV ;
hdmi = devm_kzalloc ( & pdev - > dev , sizeof ( * hdmi ) , GFP_KERNEL ) ;
if ( ! hdmi )
return - ENOMEM ;
2014-12-05 14:26:31 +08:00
match = of_match_node ( dw_hdmi_imx_dt_ids , pdev - > dev . of_node ) ;
2014-12-05 14:25:05 +08:00
plat_data = match - > data ;
hdmi - > dev = & pdev - > dev ;
2016-07-06 14:49:24 +02:00
encoder = & hdmi - > encoder ;
2014-12-05 14:25:05 +08:00
encoder - > possible_crtcs = drm_of_find_possible_crtcs ( drm , dev - > of_node ) ;
/*
* If we failed to find the CRTC ( s ) which this encoder is
* supposed to be connected to , it ' s because the CRTC has
* not been registered yet . Defer probing , and hope that
* the required CRTC is added later .
*/
if ( encoder - > possible_crtcs = = 0 )
return - EPROBE_DEFER ;
2014-12-05 14:26:31 +08:00
ret = dw_hdmi_imx_parse_dt ( hdmi ) ;
2014-12-05 14:25:05 +08:00
if ( ret < 0 )
return ret ;
2014-12-05 14:26:31 +08:00
drm_encoder_helper_add ( encoder , & dw_hdmi_imx_encoder_helper_funcs ) ;
drm_encoder_init ( drm , encoder , & dw_hdmi_imx_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 ) ;
2014-12-05 14:25:05 +08:00
2017-01-17 10:28:57 +02:00
ret = dw_hdmi_bind ( pdev , encoder , plat_data ) ;
2016-03-07 14:00:51 -08:00
/*
* If dw_hdmi_bind ( ) fails we ' ll never call dw_hdmi_unbind ( ) ,
* which would have called the encoder cleanup . Do it manually .
*/
if ( ret )
drm_encoder_cleanup ( encoder ) ;
return ret ;
2014-12-05 14:25:05 +08:00
}
2014-12-05 14:26:31 +08:00
static void dw_hdmi_imx_unbind ( struct device * dev , struct device * master ,
void * data )
2014-12-05 14:25:05 +08:00
{
2017-01-17 10:28:52 +02:00
return dw_hdmi_unbind ( dev ) ;
2014-12-05 14:25:05 +08:00
}
2014-12-05 14:26:31 +08:00
static const struct component_ops dw_hdmi_imx_ops = {
. bind = dw_hdmi_imx_bind ,
. unbind = dw_hdmi_imx_unbind ,
2014-12-05 14:25:05 +08:00
} ;
2014-12-05 14:26:31 +08:00
static int dw_hdmi_imx_probe ( struct platform_device * pdev )
2014-12-05 14:25:05 +08:00
{
2014-12-05 14:26:31 +08:00
return component_add ( & pdev - > dev , & dw_hdmi_imx_ops ) ;
2014-12-05 14:25:05 +08:00
}
2014-12-05 14:26:31 +08:00
static int dw_hdmi_imx_remove ( struct platform_device * pdev )
2014-12-05 14:25:05 +08:00
{
2014-12-05 14:26:31 +08:00
component_del ( & pdev - > dev , & dw_hdmi_imx_ops ) ;
2014-12-05 14:25:05 +08:00
return 0 ;
}
2014-12-05 14:26:31 +08:00
static struct platform_driver dw_hdmi_imx_platform_driver = {
. probe = dw_hdmi_imx_probe ,
. remove = dw_hdmi_imx_remove ,
2014-12-05 14:25:05 +08:00
. driver = {
2014-12-05 14:26:31 +08:00
. name = " dwhdmi-imx " ,
. of_match_table = dw_hdmi_imx_dt_ids ,
2014-12-05 14:25:05 +08:00
} ,
} ;
2014-12-05 14:26:31 +08:00
module_platform_driver ( dw_hdmi_imx_platform_driver ) ;
2014-12-05 14:25:05 +08:00
MODULE_AUTHOR ( " Andy Yan <andy.yan@rock-chips.com> " ) ;
MODULE_AUTHOR ( " Yakir Yang <ykk@rock-chips.com> " ) ;
MODULE_DESCRIPTION ( " IMX6 Specific DW-HDMI Driver Extension " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-12-05 14:26:31 +08:00
MODULE_ALIAS ( " platform:dwhdmi-imx " ) ;