2015-01-07 15:48:27 +08:00
/*
* Copyright ( c ) 2014 , Fuzhou Rockchip Electronics Co . , Ltd
*
* 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/mfd/syscon.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 <drm/bridge/dw_hdmi.h>
# include "rockchip_drm_drv.h"
# include "rockchip_drm_vop.h"
# define GRF_SOC_CON6 0x025c
# define HDMI_SEL_VOP_LIT (1 << 4)
struct rockchip_hdmi {
struct device * dev ;
struct regmap * regmap ;
struct drm_encoder encoder ;
} ;
# define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x)
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg [ ] = {
{
27000000 , {
{ 0x00b3 , 0x0000 } ,
{ 0x2153 , 0x0000 } ,
{ 0x40f3 , 0x0000 }
} ,
} , {
36000000 , {
{ 0x00b3 , 0x0000 } ,
{ 0x2153 , 0x0000 } ,
{ 0x40f3 , 0x0000 }
} ,
} , {
40000000 , {
{ 0x00b3 , 0x0000 } ,
{ 0x2153 , 0x0000 } ,
{ 0x40f3 , 0x0000 }
} ,
} , {
54000000 , {
{ 0x0072 , 0x0001 } ,
{ 0x2142 , 0x0001 } ,
{ 0x40a2 , 0x0001 } ,
} ,
} , {
65000000 , {
{ 0x0072 , 0x0001 } ,
{ 0x2142 , 0x0001 } ,
{ 0x40a2 , 0x0001 } ,
} ,
} , {
66000000 , {
{ 0x013e , 0x0003 } ,
{ 0x217e , 0x0002 } ,
{ 0x4061 , 0x0002 }
} ,
} , {
74250000 , {
{ 0x0072 , 0x0001 } ,
{ 0x2145 , 0x0002 } ,
{ 0x4061 , 0x0002 }
} ,
} , {
83500000 , {
{ 0x0072 , 0x0001 } ,
} ,
} , {
108000000 , {
{ 0x0051 , 0x0002 } ,
{ 0x2145 , 0x0002 } ,
{ 0x4061 , 0x0002 }
} ,
} , {
106500000 , {
{ 0x0051 , 0x0002 } ,
{ 0x2145 , 0x0002 } ,
{ 0x4061 , 0x0002 }
} ,
} , {
146250000 , {
{ 0x0051 , 0x0002 } ,
{ 0x2145 , 0x0002 } ,
{ 0x4061 , 0x0002 }
} ,
} , {
148500000 , {
{ 0x0051 , 0x0003 } ,
{ 0x214c , 0x0003 } ,
{ 0x4064 , 0x0003 }
} ,
} , {
~ 0UL , {
{ 0x00a0 , 0x000a } ,
{ 0x2001 , 0x000f } ,
{ 0x4002 , 0x000f } ,
} ,
}
} ;
static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr [ ] = {
/* pixelclk bpp8 bpp10 bpp12 */
{
40000000 , { 0x0018 , 0x0018 , 0x0018 } ,
} , {
65000000 , { 0x0028 , 0x0028 , 0x0028 } ,
} , {
66000000 , { 0x0038 , 0x0038 , 0x0038 } ,
} , {
74250000 , { 0x0028 , 0x0038 , 0x0038 } ,
} , {
83500000 , { 0x0028 , 0x0038 , 0x0038 } ,
} , {
146250000 , { 0x0038 , 0x0038 , 0x0038 } ,
} , {
148500000 , { 0x0000 , 0x0038 , 0x0038 } ,
} , {
~ 0UL , { 0x0000 , 0x0000 , 0x0000 } ,
}
} ;
2015-03-31 23:56:10 -04:00
static const struct dw_hdmi_phy_config rockchip_phy_config [ ] = {
/*pixelclk symbol term vlev*/
2015-03-31 23:57:10 -04:00
{ 74250000 , 0x8009 , 0x0004 , 0x0272 } ,
{ 148500000 , 0x802b , 0x0004 , 0x028d } ,
{ 297000000 , 0x8039 , 0x0005 , 0x028d } ,
2015-03-31 23:56:10 -04:00
{ ~ 0UL , 0x0000 , 0x0000 , 0x0000 }
2015-01-07 15:48:27 +08:00
} ;
static int rockchip_hdmi_parse_dt ( struct rockchip_hdmi * hdmi )
{
struct device_node * np = hdmi - > dev - > of_node ;
hdmi - > regmap = syscon_regmap_lookup_by_phandle ( np , " rockchip,grf " ) ;
if ( IS_ERR ( hdmi - > regmap ) ) {
dev_err ( hdmi - > dev , " Unable to get rockchip,grf \n " ) ;
return PTR_ERR ( hdmi - > regmap ) ;
}
return 0 ;
}
static enum drm_mode_status
dw_hdmi_rockchip_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
const struct dw_hdmi_mpll_config * mpll_cfg = rockchip_mpll_cfg ;
int pclk = mode - > clock * 1000 ;
bool valid = false ;
int i ;
for ( i = 0 ; mpll_cfg [ i ] . mpixelclock ! = ( ~ 0UL ) ; i + + ) {
if ( pclk = = mpll_cfg [ i ] . mpixelclock ) {
valid = true ;
break ;
}
}
return ( valid ) ? MODE_OK : MODE_BAD ;
}
2015-12-15 12:21:12 +01:00
static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
2015-01-07 15:48:27 +08:00
. destroy = drm_encoder_cleanup ,
} ;
static void dw_hdmi_rockchip_encoder_disable ( struct drm_encoder * encoder )
{
}
static bool
dw_hdmi_rockchip_encoder_mode_fixup ( struct drm_encoder * encoder ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adj_mode )
{
return true ;
}
static void dw_hdmi_rockchip_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adj_mode )
{
}
2015-11-30 18:41:06 +08:00
static void dw_hdmi_rockchip_encoder_enable ( struct drm_encoder * encoder )
2015-01-07 15:48:27 +08:00
{
struct rockchip_hdmi * hdmi = to_rockchip_hdmi ( encoder ) ;
u32 val ;
int mux ;
2015-02-24 11:42:08 +01:00
mux = drm_of_encoder_active_endpoint_id ( hdmi - > dev - > of_node , encoder ) ;
2015-01-07 15:48:27 +08:00
if ( mux )
val = HDMI_SEL_VOP_LIT | ( HDMI_SEL_VOP_LIT < < 16 ) ;
else
val = HDMI_SEL_VOP_LIT < < 16 ;
regmap_write ( hdmi - > regmap , GRF_SOC_CON6 , val ) ;
dev_dbg ( hdmi - > dev , " vop %s output to hdmi \n " ,
( mux ) ? " LIT " : " BIG " ) ;
}
2016-04-20 10:41:42 +08:00
static int
dw_hdmi_rockchip_encoder_atomic_check ( struct drm_encoder * encoder ,
struct drm_crtc_state * crtc_state ,
struct drm_connector_state * conn_state )
{
struct rockchip_crtc_state * s = to_rockchip_crtc_state ( crtc_state ) ;
s - > output_mode = ROCKCHIP_OUT_MODE_AAAA ;
s - > output_type = DRM_MODE_CONNECTOR_HDMIA ;
return 0 ;
}
2015-12-15 12:21:12 +01:00
static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
2015-01-07 15:48:27 +08:00
. mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup ,
. mode_set = dw_hdmi_rockchip_encoder_mode_set ,
2015-11-30 18:41:06 +08:00
. enable = dw_hdmi_rockchip_encoder_enable ,
2015-01-07 15:48:27 +08:00
. disable = dw_hdmi_rockchip_encoder_disable ,
2016-04-20 10:41:42 +08:00
. atomic_check = dw_hdmi_rockchip_encoder_atomic_check ,
2015-01-07 15:48:27 +08:00
} ;
static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
. mode_valid = dw_hdmi_rockchip_mode_valid ,
. mpll_cfg = rockchip_mpll_cfg ,
. cur_ctr = rockchip_cur_ctr ,
2015-03-31 23:56:10 -04:00
. phy_config = rockchip_phy_config ,
2015-01-07 15:48:27 +08:00
. dev_type = RK3288_HDMI ,
} ;
static const struct of_device_id dw_hdmi_rockchip_dt_ids [ ] = {
{ . compatible = " rockchip,rk3288-dw-hdmi " ,
. data = & rockchip_hdmi_drv_data
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dw_hdmi_rockchip_dt_ids ) ;
static int dw_hdmi_rockchip_bind ( struct device * dev , struct device * master ,
void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
const struct dw_hdmi_plat_data * plat_data ;
const struct of_device_id * match ;
struct drm_device * drm = data ;
struct drm_encoder * encoder ;
struct rockchip_hdmi * hdmi ;
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 ;
match = of_match_node ( dw_hdmi_rockchip_dt_ids , pdev - > dev . of_node ) ;
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 ;
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 ;
ret = rockchip_hdmi_parse_dt ( hdmi ) ;
if ( ret ) {
dev_err ( hdmi - > dev , " Unable to parse OF data \n " ) ;
return ret ;
}
drm_encoder_helper_add ( encoder , & dw_hdmi_rockchip_encoder_helper_funcs ) ;
drm_encoder_init ( drm , encoder , & dw_hdmi_rockchip_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-01-07 15:48:27 +08:00
2016-03-07 14:00:50 -08:00
ret = dw_hdmi_bind ( dev , master , data , encoder , iores , irq , plat_data ) ;
/*
* 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 ;
2015-01-07 15:48:27 +08:00
}
static void dw_hdmi_rockchip_unbind ( struct device * dev , struct device * master ,
void * data )
{
return dw_hdmi_unbind ( dev , master , data ) ;
}
static const struct component_ops dw_hdmi_rockchip_ops = {
. bind = dw_hdmi_rockchip_bind ,
. unbind = dw_hdmi_rockchip_unbind ,
} ;
static int dw_hdmi_rockchip_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & dw_hdmi_rockchip_ops ) ;
}
static int dw_hdmi_rockchip_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & dw_hdmi_rockchip_ops ) ;
return 0 ;
}
static struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
. probe = dw_hdmi_rockchip_probe ,
. remove = dw_hdmi_rockchip_remove ,
. driver = {
. name = " dwhdmi-rockchip " ,
. of_match_table = dw_hdmi_rockchip_dt_ids ,
} ,
} ;
module_platform_driver ( dw_hdmi_rockchip_pltfm_driver ) ;
MODULE_AUTHOR ( " Andy Yan <andy.yan@rock-chips.com> " ) ;
MODULE_AUTHOR ( " Yakir Yang <ykk@rock-chips.com> " ) ;
MODULE_DESCRIPTION ( " Rockchip Specific DW-HDMI Driver Extension " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:dwhdmi-rockchip " ) ;