2017-03-02 12:47:26 +02:00
/*
* Copyright ( C ) 2016 Laurent Pinchart < laurent . pinchart @ ideasonboard . 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 <drm/drmP.h>
2017-06-02 13:25:14 -07:00
# include <drm/drm_bridge.h>
2017-03-02 12:47:26 +02:00
# include <drm/drm_panel.h>
# include <linux/of_graph.h>
2017-11-14 11:16:47 -08:00
struct lvds_encoder {
struct drm_bridge bridge ;
struct drm_bridge * panel_bridge ;
} ;
static int lvds_encoder_attach ( struct drm_bridge * bridge )
{
struct lvds_encoder * lvds_encoder = container_of ( bridge ,
struct lvds_encoder ,
bridge ) ;
return drm_bridge_attach ( bridge - > encoder , lvds_encoder - > panel_bridge ,
bridge ) ;
}
static struct drm_bridge_funcs funcs = {
. attach = lvds_encoder_attach ,
} ;
2017-03-02 12:47:26 +02:00
static int lvds_encoder_probe ( struct platform_device * pdev )
{
2019-01-11 15:19:10 +00:00
struct device * dev = & pdev - > dev ;
2017-03-02 12:47:26 +02:00
struct device_node * port ;
struct device_node * endpoint ;
2017-06-02 13:25:14 -07:00
struct device_node * panel_node ;
struct drm_panel * panel ;
2017-11-14 11:16:47 -08:00
struct lvds_encoder * lvds_encoder ;
2019-01-11 15:19:10 +00:00
lvds_encoder = devm_kzalloc ( dev , sizeof ( * lvds_encoder ) , GFP_KERNEL ) ;
2017-11-14 11:16:47 -08:00
if ( ! lvds_encoder )
return - ENOMEM ;
2017-03-02 12:47:26 +02:00
/* Locate the panel DT node. */
2019-01-11 15:19:10 +00:00
port = of_graph_get_port_by_id ( dev - > of_node , 1 ) ;
2017-03-02 12:47:26 +02:00
if ( ! port ) {
2019-01-11 15:19:10 +00:00
dev_dbg ( dev , " port 1 not found \n " ) ;
2017-03-02 12:47:26 +02:00
return - ENXIO ;
}
endpoint = of_get_child_by_name ( port , " endpoint " ) ;
of_node_put ( port ) ;
if ( ! endpoint ) {
2019-01-11 15:19:10 +00:00
dev_dbg ( dev , " no endpoint for port 1 \n " ) ;
2017-03-02 12:47:26 +02:00
return - ENXIO ;
}
2017-06-02 13:25:14 -07:00
panel_node = of_graph_get_remote_port_parent ( endpoint ) ;
2017-03-02 12:47:26 +02:00
of_node_put ( endpoint ) ;
2017-06-02 13:25:14 -07:00
if ( ! panel_node ) {
2019-01-11 15:19:10 +00:00
dev_dbg ( dev , " no remote endpoint for port 1 \n " ) ;
2017-03-02 12:47:26 +02:00
return - ENXIO ;
}
2017-06-02 13:25:14 -07:00
panel = of_drm_find_panel ( panel_node ) ;
of_node_put ( panel_node ) ;
2018-05-09 15:00:39 +02:00
if ( IS_ERR ( panel ) ) {
2019-01-11 15:19:10 +00:00
dev_dbg ( dev , " panel not found, deferring probe \n " ) ;
2018-05-09 15:00:39 +02:00
return PTR_ERR ( panel ) ;
2017-03-02 12:47:26 +02:00
}
2017-11-14 11:16:47 -08:00
lvds_encoder - > panel_bridge =
2019-01-11 15:19:10 +00:00
devm_drm_panel_bridge_add ( dev , panel , DRM_MODE_CONNECTOR_LVDS ) ;
2017-11-14 11:16:47 -08:00
if ( IS_ERR ( lvds_encoder - > panel_bridge ) )
return PTR_ERR ( lvds_encoder - > panel_bridge ) ;
/* The panel_bridge bridge is attached to the panel's of_node,
* but we need a bridge attached to our of_node for our user
* to look up .
*/
2019-01-11 15:19:10 +00:00
lvds_encoder - > bridge . of_node = dev - > of_node ;
2017-11-14 11:16:47 -08:00
lvds_encoder - > bridge . funcs = & funcs ;
drm_bridge_add ( & lvds_encoder - > bridge ) ;
2017-06-02 13:25:14 -07:00
2017-11-14 11:16:47 -08:00
platform_set_drvdata ( pdev , lvds_encoder ) ;
2017-06-02 13:25:14 -07:00
return 0 ;
2017-03-02 12:47:26 +02:00
}
static int lvds_encoder_remove ( struct platform_device * pdev )
{
2017-11-14 11:16:47 -08:00
struct lvds_encoder * lvds_encoder = platform_get_drvdata ( pdev ) ;
2017-03-02 12:47:26 +02:00
2017-11-14 11:16:47 -08:00
drm_bridge_remove ( & lvds_encoder - > bridge ) ;
2017-03-02 12:47:26 +02:00
return 0 ;
}
static const struct of_device_id lvds_encoder_match [ ] = {
{ . compatible = " lvds-encoder " } ,
2017-03-02 12:47:28 +02:00
{ . compatible = " thine,thc63lvdm83d " } ,
2017-03-02 12:47:26 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , lvds_encoder_match ) ;
static struct platform_driver lvds_encoder_driver = {
. probe = lvds_encoder_probe ,
. remove = lvds_encoder_remove ,
. driver = {
. name = " lvds-encoder " ,
. of_match_table = lvds_encoder_match ,
} ,
} ;
module_platform_driver ( lvds_encoder_driver ) ;
MODULE_AUTHOR ( " Laurent Pinchart <laurent.pinchart@ideasonboard.com> " ) ;
MODULE_DESCRIPTION ( " Transparent parallel to LVDS encoder " ) ;
MODULE_LICENSE ( " GPL " ) ;