2016-09-30 17:37:06 +03:00
/*
* Copyright ( C ) 2015 - 2016 Free Electrons
* Copyright ( C ) 2015 - 2016 NextThing Co
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*
* 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>
2018-01-12 10:48:53 +03:00
# include <linux/of_device.h>
2016-09-30 17:37:06 +03:00
# include <linux/of_graph.h>
2016-11-16 18:42:31 +03:00
# include <linux/regulator/consumer.h>
2016-09-30 17:37:06 +03:00
# include <drm/drmP.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
struct dumb_vga {
struct drm_bridge bridge ;
struct drm_connector connector ;
struct i2c_adapter * ddc ;
2016-11-16 18:42:31 +03:00
struct regulator * vdd ;
2016-09-30 17:37:06 +03:00
} ;
static inline struct dumb_vga *
drm_bridge_to_dumb_vga ( struct drm_bridge * bridge )
{
return container_of ( bridge , struct dumb_vga , bridge ) ;
}
static inline struct dumb_vga *
drm_connector_to_dumb_vga ( struct drm_connector * connector )
{
return container_of ( connector , struct dumb_vga , connector ) ;
}
static int dumb_vga_get_modes ( struct drm_connector * connector )
{
struct dumb_vga * vga = drm_connector_to_dumb_vga ( connector ) ;
struct edid * edid ;
int ret ;
if ( IS_ERR ( vga - > ddc ) )
goto fallback ;
edid = drm_get_edid ( connector , vga - > ddc ) ;
if ( ! edid ) {
DRM_INFO ( " EDID readout failed, falling back to standard modes \n " ) ;
goto fallback ;
}
drm_mode_connector_update_edid_property ( connector , edid ) ;
2018-04-20 21:59:59 +03:00
ret = drm_add_edid_modes ( connector , edid ) ;
kfree ( edid ) ;
return ret ;
2016-09-30 17:37:06 +03:00
fallback :
/*
* In case we cannot retrieve the EDIDs ( broken or missing i2c
* bus ) , fallback on the XGA standards
*/
ret = drm_add_modes_noedid ( connector , 1920 , 1200 ) ;
/* And prefer a mode pretty much anyone can handle */
drm_set_preferred_mode ( connector , 1024 , 768 ) ;
return ret ;
}
static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = {
. get_modes = dumb_vga_get_modes ,
} ;
static enum drm_connector_status
dumb_vga_connector_detect ( struct drm_connector * connector , bool force )
{
struct dumb_vga * vga = drm_connector_to_dumb_vga ( connector ) ;
/*
* Even if we have an I2C bus , we can ' t assume that the cable
* is disconnected if drm_probe_ddc fails . Some cables don ' t
* wire the DDC pins , or the I2C bus might not be working at
* all .
*/
if ( ! IS_ERR ( vga - > ddc ) & & drm_probe_ddc ( vga - > ddc ) )
return connector_status_connected ;
return connector_status_unknown ;
}
static const struct drm_connector_funcs dumb_vga_con_funcs = {
. detect = dumb_vga_connector_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = drm_connector_cleanup ,
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
} ;
static int dumb_vga_attach ( struct drm_bridge * bridge )
{
struct dumb_vga * vga = drm_bridge_to_dumb_vga ( bridge ) ;
int ret ;
if ( ! bridge - > encoder ) {
DRM_ERROR ( " Missing encoder \n " ) ;
return - ENODEV ;
}
drm_connector_helper_add ( & vga - > connector ,
& dumb_vga_con_helper_funcs ) ;
ret = drm_connector_init ( bridge - > dev , & vga - > connector ,
& dumb_vga_con_funcs , DRM_MODE_CONNECTOR_VGA ) ;
if ( ret ) {
DRM_ERROR ( " Failed to initialize connector \n " ) ;
return ret ;
}
drm_mode_connector_attach_encoder ( & vga - > connector ,
bridge - > encoder ) ;
return 0 ;
}
2016-11-16 18:42:31 +03:00
static void dumb_vga_enable ( struct drm_bridge * bridge )
{
struct dumb_vga * vga = drm_bridge_to_dumb_vga ( bridge ) ;
int ret = 0 ;
if ( vga - > vdd )
ret = regulator_enable ( vga - > vdd ) ;
if ( ret )
DRM_ERROR ( " Failed to enable vdd regulator: %d \n " , ret ) ;
}
static void dumb_vga_disable ( struct drm_bridge * bridge )
{
struct dumb_vga * vga = drm_bridge_to_dumb_vga ( bridge ) ;
if ( vga - > vdd )
regulator_disable ( vga - > vdd ) ;
}
2016-09-30 17:37:06 +03:00
static const struct drm_bridge_funcs dumb_vga_bridge_funcs = {
. attach = dumb_vga_attach ,
2016-11-16 18:42:31 +03:00
. enable = dumb_vga_enable ,
. disable = dumb_vga_disable ,
2016-09-30 17:37:06 +03:00
} ;
static struct i2c_adapter * dumb_vga_retrieve_ddc ( struct device * dev )
{
2017-03-22 16:26:06 +03:00
struct device_node * phandle , * remote ;
2016-09-30 17:37:06 +03:00
struct i2c_adapter * ddc ;
2017-03-22 16:26:06 +03:00
remote = of_graph_get_remote_node ( dev - > of_node , 1 , - 1 ) ;
if ( ! remote )
2016-09-30 17:37:06 +03:00
return ERR_PTR ( - EINVAL ) ;
phandle = of_parse_phandle ( remote , " ddc-i2c-bus " , 0 ) ;
of_node_put ( remote ) ;
if ( ! phandle )
return ERR_PTR ( - ENODEV ) ;
ddc = of_get_i2c_adapter_by_node ( phandle ) ;
of_node_put ( phandle ) ;
if ( ! ddc )
return ERR_PTR ( - EPROBE_DEFER ) ;
return ddc ;
}
static int dumb_vga_probe ( struct platform_device * pdev )
{
struct dumb_vga * vga ;
vga = devm_kzalloc ( & pdev - > dev , sizeof ( * vga ) , GFP_KERNEL ) ;
if ( ! vga )
return - ENOMEM ;
platform_set_drvdata ( pdev , vga ) ;
2016-11-16 18:42:31 +03:00
vga - > vdd = devm_regulator_get_optional ( & pdev - > dev , " vdd " ) ;
if ( IS_ERR ( vga - > vdd ) ) {
2017-07-03 11:42:20 +03:00
int ret = PTR_ERR ( vga - > vdd ) ;
2016-11-16 18:42:31 +03:00
if ( ret = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
vga - > vdd = NULL ;
dev_dbg ( & pdev - > dev , " No vdd regulator found: %d \n " , ret ) ;
}
2016-09-30 17:37:06 +03:00
vga - > ddc = dumb_vga_retrieve_ddc ( & pdev - > dev ) ;
if ( IS_ERR ( vga - > ddc ) ) {
if ( PTR_ERR ( vga - > ddc ) = = - ENODEV ) {
dev_dbg ( & pdev - > dev ,
" No i2c bus specified. Disabling EDID readout \n " ) ;
} else {
dev_err ( & pdev - > dev , " Couldn't retrieve i2c bus \n " ) ;
return PTR_ERR ( vga - > ddc ) ;
}
}
vga - > bridge . funcs = & dumb_vga_bridge_funcs ;
vga - > bridge . of_node = pdev - > dev . of_node ;
2018-01-12 10:48:53 +03:00
vga - > bridge . timings = of_device_get_match_data ( & pdev - > dev ) ;
2016-09-30 17:37:06 +03:00
2017-07-03 11:42:20 +03:00
drm_bridge_add ( & vga - > bridge ) ;
2016-09-30 17:37:06 +03:00
2017-07-03 11:42:20 +03:00
return 0 ;
2016-09-30 17:37:06 +03:00
}
static int dumb_vga_remove ( struct platform_device * pdev )
{
struct dumb_vga * vga = platform_get_drvdata ( pdev ) ;
drm_bridge_remove ( & vga - > bridge ) ;
if ( ! IS_ERR ( vga - > ddc ) )
i2c_put_adapter ( vga - > ddc ) ;
return 0 ;
}
2018-01-12 10:48:53 +03:00
/*
* We assume the ADV7123 DAC is the " default " for historical reasons
* Information taken from the ADV7123 datasheet , revision D .
* NOTE : the ADV7123EP seems to have other timings and need a new timings
* set if used .
*/
static const struct drm_bridge_timings default_dac_timings = {
/* Timing specifications, datasheet page 7 */
. sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE ,
. setup_time_ps = 500 ,
. hold_time_ps = 1500 ,
} ;
/*
* Information taken from the THS8134 , THS8134A , THS8134B datasheet named
* " SLVS205D " , dated May 1990 , revised March 2000.
*/
static const struct drm_bridge_timings ti_ths8134_dac_timings = {
/* From timing diagram, datasheet page 9 */
. sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE ,
/* From datasheet, page 12 */
. setup_time_ps = 3000 ,
/* I guess this means latched input */
. hold_time_ps = 0 ,
} ;
/*
* Information taken from the THS8135 datasheet named " SLAS343B " , dated
* May 2001 , revised April 2013.
*/
static const struct drm_bridge_timings ti_ths8135_dac_timings = {
/* From timing diagram, datasheet page 14 */
. sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE ,
/* From datasheet, page 16 */
. setup_time_ps = 2000 ,
. hold_time_ps = 500 ,
} ;
2016-09-30 17:37:06 +03:00
static const struct of_device_id dumb_vga_match [ ] = {
2018-01-12 10:48:53 +03:00
{
. compatible = " dumb-vga-dac " ,
. data = NULL ,
} ,
{
. compatible = " adi,adv7123 " ,
. data = & default_dac_timings ,
} ,
{
. compatible = " ti,ths8135 " ,
. data = & ti_ths8135_dac_timings ,
} ,
{
. compatible = " ti,ths8134 " ,
. data = & ti_ths8134_dac_timings ,
} ,
2016-09-30 17:37:06 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dumb_vga_match ) ;
static struct platform_driver dumb_vga_driver = {
. probe = dumb_vga_probe ,
. remove = dumb_vga_remove ,
. driver = {
. name = " dumb-vga-dac " ,
. of_match_table = dumb_vga_match ,
} ,
} ;
module_platform_driver ( dumb_vga_driver ) ;
MODULE_AUTHOR ( " Maxime Ripard <maxime.ripard@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Dumb VGA DAC bridge driver " ) ;
MODULE_LICENSE ( " GPL " ) ;