2018-09-28 16:13:25 -03:00
// SPDX-License-Identifier: GPL-2.0
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
*/
2019-07-16 08:42:18 +02:00
2014-12-05 14:25:05 +08:00
# include <linux/component.h>
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
2019-07-16 08:42:18 +02:00
# include <linux/module.h>
# include <linux/platform_device.h>
2014-12-05 14:25:05 +08:00
# include <linux/regmap.h>
2019-07-16 08:42:18 +02:00
# include <video/imx-ipu-v3.h>
# include <drm/bridge/dw_hdmi.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_atomic_helper.h>
2014-12-05 14:25:05 +08:00
# include <drm/drm_edid.h>
2018-01-17 15:54:15 +02:00
# include <drm/drm_encoder.h>
2019-07-16 08:42:18 +02:00
# include <drm/drm_of.h>
2020-03-05 16:59:36 +01:00
# include <drm/drm_simple_kms_helper.h>
2014-12-05 14:25:05 +08:00
# 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 ;
2018-02-14 21:08:59 +01:00
struct dw_hdmi * hdmi ;
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
} ;
2017-05-25 15:19:19 +01:00
static enum drm_mode_status
2020-05-26 04:14:49 +03:00
imx6q_hdmi_mode_valid ( struct dw_hdmi * hdmi , void * data ,
2020-05-26 04:14:53 +03:00
const struct drm_display_info * info ,
2017-05-25 15:19:19 +01:00
const struct drm_display_mode * mode )
2015-01-07 23:52:15 +01:00
{
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 ;
}
2017-05-25 15:19:19 +01:00
static enum drm_mode_status
2020-05-26 04:14:49 +03:00
imx6dl_hdmi_mode_valid ( struct dw_hdmi * hdmi , void * data ,
2020-05-26 04:14:53 +03:00
const struct drm_display_info * info ,
2017-05-25 15:19:19 +01:00
const struct drm_display_mode * mode )
2015-01-07 23:52:15 +01:00
{
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 ;
2020-06-11 14:43:31 +02:00
hdmi = dev_get_drvdata ( dev ) ;
memset ( hdmi , 0 , sizeof ( * hdmi ) ) ;
2014-12-05 14:25:05 +08:00
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
2020-07-15 17:28:01 +02:00
ret = imx_drm_encoder_parse_of ( drm , encoder , dev - > of_node ) ;
if ( ret )
return ret ;
2014-12-05 14:25:05 +08:00
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 ) ;
2020-03-05 16:59:36 +01:00
drm_simple_encoder_init ( drm , encoder , DRM_MODE_ENCODER_TMDS ) ;
2014-12-05 14:25:05 +08:00
2018-02-14 21:08:59 +01:00
hdmi - > hdmi = 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 .
*/
2018-02-14 21:08:59 +01:00
if ( IS_ERR ( hdmi - > hdmi ) ) {
ret = PTR_ERR ( hdmi - > hdmi ) ;
2016-03-07 14:00:51 -08:00
drm_encoder_cleanup ( encoder ) ;
2018-02-14 21:08:59 +01:00
}
2016-03-07 14:00:51 -08:00
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
{
2018-02-14 21:08:59 +01:00
struct imx_hdmi * hdmi = dev_get_drvdata ( dev ) ;
dw_hdmi_unbind ( hdmi - > hdmi ) ;
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
{
2020-06-11 14:43:31 +02:00
struct imx_hdmi * hdmi ;
hdmi = devm_kzalloc ( & pdev - > dev , sizeof ( * hdmi ) , GFP_KERNEL ) ;
if ( ! hdmi )
return - ENOMEM ;
platform_set_drvdata ( pdev , hdmi ) ;
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 " ) ;