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 ;
struct drm_encoder encoder ;
struct regmap * regmap ;
} ;
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 } ,
} ,
} , {
~ 0UL , {
{ 0x00a0 , 0x000a } ,
{ 0x2001 , 0x000f } ,
{ 0x4002 , 0x000f } ,
} ,
}
} ;
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 } ,
}
} ;
2014-12-05 14:26:31 +08:00
static const struct dw_hdmi_sym_term imx_sym_term [ ] = {
2014-12-05 14:25:50 +08:00
/*pixelclk symbol term*/
{ 148500000 , 0x800d , 0x0005 } ,
{ ~ 0UL , 0x0000 , 0x0000 }
} ;
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
{
}
2014-12-05 14:26:31 +08:00
static bool dw_hdmi_imx_encoder_mode_fixup ( struct drm_encoder * encoder ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adj_mode )
2014-12-05 14:25:05 +08:00
{
return true ;
}
2014-12-05 14:26:31 +08:00
static void dw_hdmi_imx_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adj_mode )
2014-12-05 14:25:05 +08:00
{
}
2014-12-05 14:26:31 +08:00
static void dw_hdmi_imx_encoder_commit ( struct drm_encoder * encoder )
2014-12-05 14:25:05 +08:00
{
2014-12-05 14:26:31 +08:00
struct imx_hdmi * hdmi = container_of ( encoder , struct imx_hdmi , encoder ) ;
2014-12-05 14:25:05 +08:00
int mux = imx_drm_encoder_get_mux_id ( hdmi - > dev - > of_node , encoder ) ;
regmap_update_bits ( hdmi - > regmap , IOMUXC_GPR3 ,
IMX6Q_GPR3_HDMI_MUX_CTL_MASK ,
mux < < IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT ) ;
}
2014-12-05 14:26:31 +08:00
static void dw_hdmi_imx_encoder_prepare ( struct drm_encoder * encoder )
2014-12-05 14:25:05 +08:00
{
imx_drm_panel_format ( encoder , V4L2_PIX_FMT_RGB24 ) ;
}
2014-12-05 14:26:31 +08:00
static struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
. mode_fixup = dw_hdmi_imx_encoder_mode_fixup ,
. mode_set = dw_hdmi_imx_encoder_mode_set ,
. prepare = dw_hdmi_imx_encoder_prepare ,
. commit = dw_hdmi_imx_encoder_commit ,
. disable = dw_hdmi_imx_encoder_disable ,
2014-12-05 14:25:05 +08:00
} ;
2014-12-05 14:26:31 +08:00
static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
2014-12-05 14:25:05 +08:00
. destroy = drm_encoder_cleanup ,
} ;
2014-12-05 14:26:31 +08:00
static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {
2014-12-05 14:25:50 +08:00
. mpll_cfg = imx_mpll_cfg ,
. cur_ctr = imx_cur_ctr ,
. sym_term = imx_sym_term ,
2014-12-05 14:25:05 +08:00
. dev_type = IMX6Q_HDMI ,
} ;
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 ,
. sym_term = imx_sym_term ,
2014-12-05 14:25:05 +08:00
. dev_type = IMX6DL_HDMI ,
} ;
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
struct resource * iores ;
int irq ;
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 ;
encoder = & hdmi - > encoder ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
iores = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! iores )
return - ENXIO ;
platform_set_drvdata ( pdev , hdmi ) ;
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 ,
2014-12-05 14:25:05 +08:00
DRM_MODE_ENCODER_TMDS ) ;
2014-12-05 14:26:31 +08:00
return dw_hdmi_bind ( dev , master , data , encoder , iores , irq , plat_data ) ;
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
{
2014-12-05 14:26:31 +08:00
return dw_hdmi_unbind ( dev , master , data ) ;
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 " ) ;