2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-12-05 14:29:31 -06:00
/*
* OMAP Display Subsystem Base
*
* Copyright ( C ) 2015 - 2017 Texas Instruments Incorporated - http : //www.ti.com/
*/
2015-11-05 17:19:32 +02:00
# include <linux/kernel.h>
2018-02-28 23:49:24 +02:00
# include <linux/list.h>
2015-11-05 17:19:32 +02:00
# include <linux/module.h>
2018-02-28 23:49:24 +02:00
# include <linux/mutex.h>
2016-05-03 22:07:10 +03:00
# include <linux/of.h>
# include <linux/of_graph.h>
2018-09-23 12:58:15 +03:00
# include <linux/platform_device.h>
2018-02-13 14:00:41 +02:00
# include "dss.h"
2016-05-03 22:07:10 +03:00
# include "omapdss.h"
2015-11-05 17:19:32 +02:00
2018-02-13 14:00:40 +02:00
static struct dss_device * dss_device ;
2015-11-05 17:23:14 +02:00
2018-02-13 14:00:40 +02:00
struct dss_device * omapdss_get_dss ( void )
2015-11-05 17:23:14 +02:00
{
2018-02-13 14:00:40 +02:00
return dss_device ;
2015-11-05 17:23:14 +02:00
}
2018-02-13 14:00:40 +02:00
EXPORT_SYMBOL ( omapdss_get_dss ) ;
2015-11-05 17:23:14 +02:00
2018-02-13 14:00:40 +02:00
void omapdss_set_dss ( struct dss_device * dss )
2015-11-05 17:23:14 +02:00
{
2018-02-13 14:00:40 +02:00
dss_device = dss ;
2015-11-05 17:23:14 +02:00
}
2018-02-13 14:00:40 +02:00
EXPORT_SYMBOL ( omapdss_set_dss ) ;
2015-11-05 17:23:14 +02:00
2018-02-13 14:00:42 +02:00
struct dispc_device * dispc_get_dispc ( struct dss_device * dss )
{
return dss - > dispc ;
}
EXPORT_SYMBOL ( dispc_get_dispc ) ;
2018-02-13 14:00:41 +02:00
const struct dispc_ops * dispc_get_ops ( struct dss_device * dss )
2015-11-05 19:36:02 +02:00
{
2018-02-13 14:00:41 +02:00
return dss - > dispc_ops ;
2015-11-05 19:36:02 +02:00
}
EXPORT_SYMBOL ( dispc_get_ops ) ;
2018-02-28 23:49:24 +02:00
/* -----------------------------------------------------------------------------
* OMAP DSS Devices Handling
*/
static LIST_HEAD ( omapdss_devices_list ) ;
static DEFINE_MUTEX ( omapdss_devices_lock ) ;
void omapdss_device_register ( struct omap_dss_device * dssdev )
{
mutex_lock ( & omapdss_devices_lock ) ;
list_add_tail ( & dssdev - > list , & omapdss_devices_list ) ;
mutex_unlock ( & omapdss_devices_lock ) ;
}
2018-03-02 01:25:32 +02:00
EXPORT_SYMBOL_GPL ( omapdss_device_register ) ;
2018-02-28 23:49:24 +02:00
void omapdss_device_unregister ( struct omap_dss_device * dssdev )
{
mutex_lock ( & omapdss_devices_lock ) ;
list_del ( & dssdev - > list ) ;
mutex_unlock ( & omapdss_devices_lock ) ;
}
2018-03-02 01:25:32 +02:00
EXPORT_SYMBOL_GPL ( omapdss_device_unregister ) ;
2018-02-28 23:49:24 +02:00
2018-02-28 23:53:16 +02:00
static bool omapdss_device_is_registered ( struct device_node * node )
{
struct omap_dss_device * dssdev ;
bool found = false ;
mutex_lock ( & omapdss_devices_lock ) ;
list_for_each_entry ( dssdev , & omapdss_devices_list , list ) {
if ( dssdev - > dev - > of_node = = node ) {
found = true ;
break ;
}
}
mutex_unlock ( & omapdss_devices_lock ) ;
return found ;
}
2018-03-02 02:43:45 +02:00
struct omap_dss_device * omapdss_device_get ( struct omap_dss_device * dssdev )
{
if ( ! try_module_get ( dssdev - > owner ) )
return NULL ;
if ( get_device ( dssdev - > dev ) = = NULL ) {
module_put ( dssdev - > owner ) ;
return NULL ;
}
return dssdev ;
}
EXPORT_SYMBOL ( omapdss_device_get ) ;
void omapdss_device_put ( struct omap_dss_device * dssdev )
{
put_device ( dssdev - > dev ) ;
module_put ( dssdev - > owner ) ;
}
EXPORT_SYMBOL ( omapdss_device_put ) ;
2018-09-21 23:32:58 +03:00
struct omap_dss_device * omapdss_find_device_by_node ( struct device_node * node )
2018-03-01 23:35:55 +02:00
{
struct omap_dss_device * dssdev ;
list_for_each_entry ( dssdev , & omapdss_devices_list , list ) {
2018-09-21 23:32:58 +03:00
if ( dssdev - > dev - > of_node = = node )
2018-03-02 02:43:45 +02:00
return omapdss_device_get ( dssdev ) ;
2018-03-01 23:35:55 +02:00
}
return NULL ;
}
2018-03-02 02:07:34 +02:00
/*
2018-08-24 19:38:07 +03:00
* Search for the next output device starting at @ from . Release the reference to
* the @ from device , and acquire a reference to the returned device if found .
2018-03-02 02:07:34 +02:00
*/
2018-08-24 19:38:07 +03:00
struct omap_dss_device * omapdss_device_next_output ( struct omap_dss_device * from )
2018-03-02 02:07:34 +02:00
{
struct omap_dss_device * dssdev ;
struct list_head * list ;
mutex_lock ( & omapdss_devices_lock ) ;
if ( list_empty ( & omapdss_devices_list ) ) {
dssdev = NULL ;
goto done ;
}
/*
* Start from the from entry if given or from omapdss_devices_list
* otherwise .
*/
list = from ? & from - > list : & omapdss_devices_list ;
list_for_each_entry ( dssdev , list , list ) {
/*
* Stop if we reach the omapdss_devices_list , that ' s the end of
* the list .
*/
if ( & dssdev - > list = = & omapdss_devices_list ) {
dssdev = NULL ;
goto done ;
}
2018-12-07 23:08:35 +02:00
if ( dssdev - > id & &
( dssdev - > next | | dssdev - > bridge | | dssdev - > panel ) )
2018-03-02 02:07:34 +02:00
goto done ;
}
dssdev = NULL ;
done :
if ( from )
2018-03-02 02:43:45 +02:00
omapdss_device_put ( from ) ;
2018-03-02 02:07:34 +02:00
if ( dssdev )
2018-03-02 02:43:45 +02:00
omapdss_device_get ( dssdev ) ;
2018-03-02 02:07:34 +02:00
mutex_unlock ( & omapdss_devices_lock ) ;
return dssdev ;
}
2018-08-24 19:38:07 +03:00
EXPORT_SYMBOL ( omapdss_device_next_output ) ;
2018-03-02 02:07:34 +02:00
2018-09-04 23:53:34 +03:00
static bool omapdss_device_is_connected ( struct omap_dss_device * dssdev )
{
2018-09-13 03:48:02 +03:00
return dssdev - > dss ;
2018-09-04 23:53:34 +03:00
}
2018-03-02 02:54:16 +02:00
int omapdss_device_connect ( struct dss_device * dss ,
struct omap_dss_device * src ,
2018-02-28 17:30:30 +02:00
struct omap_dss_device * dst )
{
2018-02-28 17:30:30 +02:00
int ret ;
2018-09-23 12:58:15 +03:00
dev_dbg ( & dss - > pdev - > dev , " connect(%s, %s) \n " ,
src ? dev_name ( src - > dev ) : " NULL " ,
dst ? dev_name ( dst - > dev ) : " NULL " ) ;
if ( ! dst ) {
/*
* The destination is NULL when the source is connected to a
2018-12-07 23:08:35 +02:00
* bridge or panel instead of a DSS device . Stop here , we will
* attach the bridge or panel later when we will have a DRM
* encoder .
2018-09-23 12:58:15 +03:00
*/
2018-12-07 23:08:35 +02:00
return src & & ( src - > bridge | | src - > panel ) ? 0 : - EINVAL ;
2018-09-23 12:58:15 +03:00
}
2018-02-28 17:30:30 +02:00
2018-03-04 23:42:36 +02:00
if ( omapdss_device_is_connected ( dst ) )
2018-02-28 17:30:30 +02:00
return - EBUSY ;
2018-03-04 23:42:36 +02:00
dst - > dss = dss ;
2018-03-02 02:54:16 +02:00
2018-06-01 19:45:01 +03:00
ret = dst - > ops - > connect ( src , dst ) ;
2018-03-02 02:54:16 +02:00
if ( ret < 0 ) {
2018-03-04 23:42:36 +02:00
dst - > dss = NULL ;
2018-02-28 17:30:30 +02:00
return ret ;
2018-03-02 02:54:16 +02:00
}
2018-02-28 17:30:30 +02:00
return 0 ;
2018-02-28 17:30:30 +02:00
}
EXPORT_SYMBOL_GPL ( omapdss_device_connect ) ;
void omapdss_device_disconnect ( struct omap_dss_device * src ,
struct omap_dss_device * dst )
{
2018-09-23 12:58:15 +03:00
struct dss_device * dss = src ? src - > dss : dst - > dss ;
dev_dbg ( & dss - > pdev - > dev , " disconnect(%s, %s) \n " ,
src ? dev_name ( src - > dev ) : " NULL " ,
dst ? dev_name ( dst - > dev ) : " NULL " ) ;
if ( ! dst ) {
2018-12-07 23:08:35 +02:00
WARN_ON ( ! src - > bridge & & ! src - > panel ) ;
2018-09-23 12:58:15 +03:00
return ;
}
2018-02-28 17:30:30 +02:00
2018-03-04 23:42:36 +02:00
if ( ! dst - > id & & ! omapdss_device_is_connected ( dst ) ) {
2018-12-10 14:00:38 +02:00
WARN_ON ( ! dst - > display ) ;
2018-02-28 17:30:30 +02:00
return ;
}
2018-03-06 01:51:31 +02:00
WARN_ON ( dst - > state ! = OMAP_DSS_DISPLAY_DISABLED ) ;
2018-06-01 19:45:01 +03:00
dst - > ops - > disconnect ( src , dst ) ;
2018-03-04 23:42:36 +02:00
dst - > dss = NULL ;
2018-02-28 17:30:30 +02:00
}
EXPORT_SYMBOL_GPL ( omapdss_device_disconnect ) ;
2018-08-24 19:38:07 +03:00
void omapdss_device_pre_enable ( struct omap_dss_device * dssdev )
{
if ( ! dssdev )
return ;
omapdss_device_pre_enable ( dssdev - > next ) ;
if ( dssdev - > ops - > pre_enable )
dssdev - > ops - > pre_enable ( dssdev ) ;
}
EXPORT_SYMBOL_GPL ( omapdss_device_pre_enable ) ;
void omapdss_device_enable ( struct omap_dss_device * dssdev )
{
if ( ! dssdev )
return ;
if ( dssdev - > ops - > enable )
dssdev - > ops - > enable ( dssdev ) ;
omapdss_device_enable ( dssdev - > next ) ;
dssdev - > state = OMAP_DSS_DISPLAY_ACTIVE ;
}
EXPORT_SYMBOL_GPL ( omapdss_device_enable ) ;
void omapdss_device_disable ( struct omap_dss_device * dssdev )
{
if ( ! dssdev )
return ;
omapdss_device_disable ( dssdev - > next ) ;
if ( dssdev - > ops - > disable )
dssdev - > ops - > disable ( dssdev ) ;
}
EXPORT_SYMBOL_GPL ( omapdss_device_disable ) ;
void omapdss_device_post_disable ( struct omap_dss_device * dssdev )
{
if ( ! dssdev )
return ;
if ( dssdev - > ops - > post_disable )
dssdev - > ops - > post_disable ( dssdev ) ;
omapdss_device_post_disable ( dssdev - > next ) ;
dssdev - > state = OMAP_DSS_DISPLAY_DISABLED ;
}
EXPORT_SYMBOL_GPL ( omapdss_device_post_disable ) ;
2018-02-28 23:49:24 +02:00
/* -----------------------------------------------------------------------------
* Components Handling
*/
static struct list_head omapdss_comp_list ;
struct omapdss_comp_node {
struct list_head list ;
struct device_node * node ;
bool dss_core_component ;
2018-09-23 15:05:10 +03:00
const char * compat ;
2018-02-28 23:49:24 +02:00
} ;
2016-05-03 22:07:10 +03:00
static bool omapdss_list_contains ( const struct device_node * node )
{
struct omapdss_comp_node * comp ;
list_for_each_entry ( comp , & omapdss_comp_list , list ) {
if ( comp - > node = = node )
return true ;
}
return false ;
}
static void omapdss_walk_device ( struct device * dev , struct device_node * node ,
bool dss_core )
{
2018-09-23 15:05:10 +03:00
struct omapdss_comp_node * comp ;
2016-05-03 22:07:10 +03:00
struct device_node * n ;
2018-09-23 15:05:10 +03:00
const char * compat ;
int ret ;
ret = of_property_read_string ( node , " compatible " , & compat ) ;
if ( ret < 0 )
return ;
2016-05-03 22:07:10 +03:00
2018-09-23 15:05:10 +03:00
comp = devm_kzalloc ( dev , sizeof ( * comp ) , GFP_KERNEL ) ;
2016-05-03 22:07:10 +03:00
if ( comp ) {
comp - > node = node ;
comp - > dss_core_component = dss_core ;
2018-09-23 15:05:10 +03:00
comp - > compat = compat ;
2016-05-03 22:07:10 +03:00
list_add ( & comp - > list , & omapdss_comp_list ) ;
}
/*
* of_graph_get_remote_port_parent ( ) prints an error if there is no
* port / ports node . To avoid that , check first that there ' s the node .
*/
n = of_get_child_by_name ( node , " ports " ) ;
if ( ! n )
n = of_get_child_by_name ( node , " port " ) ;
if ( ! n )
return ;
of_node_put ( n ) ;
n = NULL ;
while ( ( n = of_graph_get_next_endpoint ( node , n ) ) ! = NULL ) {
struct device_node * pn = of_graph_get_remote_port_parent ( n ) ;
if ( ! pn )
continue ;
if ( ! of_device_is_available ( pn ) | | omapdss_list_contains ( pn ) ) {
of_node_put ( pn ) ;
continue ;
}
omapdss_walk_device ( dev , pn , false ) ;
}
}
void omapdss_gather_components ( struct device * dev )
{
struct device_node * child ;
INIT_LIST_HEAD ( & omapdss_comp_list ) ;
omapdss_walk_device ( dev , dev - > of_node , true ) ;
2018-09-23 15:05:10 +03:00
for_each_available_child_of_node ( dev - > of_node , child )
2016-05-03 22:07:10 +03:00
omapdss_walk_device ( dev , child , true ) ;
}
EXPORT_SYMBOL ( omapdss_gather_components ) ;
static bool omapdss_component_is_loaded ( struct omapdss_comp_node * comp )
{
if ( comp - > dss_core_component )
return true ;
2018-09-23 15:05:10 +03:00
if ( ! strstarts ( comp - > compat , " omapdss, " ) )
return true ;
2018-02-28 23:53:16 +02:00
if ( omapdss_device_is_registered ( comp - > node ) )
2016-05-03 22:07:10 +03:00
return true ;
return false ;
}
bool omapdss_stack_is_ready ( void )
{
struct omapdss_comp_node * comp ;
list_for_each_entry ( comp , & omapdss_comp_list , list ) {
if ( ! omapdss_component_is_loaded ( comp ) )
return false ;
}
return true ;
}
EXPORT_SYMBOL ( omapdss_stack_is_ready ) ;
2015-11-05 17:19:32 +02:00
MODULE_AUTHOR ( " Tomi Valkeinen <tomi.valkeinen@ti.com> " ) ;
MODULE_DESCRIPTION ( " OMAP Display Subsystem Base " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;