2014-05-15 10:41:34 +03:00
/*
2017-12-05 14:29:31 -06:00
* Copyright ( C ) 2014 Texas Instruments Incorporated - http : //www.ti.com/
2014-05-15 10:41:34 +03:00
* Author : Tomi Valkeinen < tomi . valkeinen @ ti . com >
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
/*
* As omapdss panel drivers are omapdss specific , but we want to define the
* DT - data in generic manner , we convert the compatible strings of the panel and
* encoder nodes from " panel-foo " to " omapdss,panel-foo " . This way we can have
* both correct DT data and omapdss specific drivers .
*
* When we get generic panel drivers to the kernel , this file will be removed .
*/
# include <linux/kernel.h>
# include <linux/of.h>
# include <linux/of_graph.h>
# include <linux/slab.h>
# include <linux/list.h>
static struct list_head dss_conv_list __initdata ;
static const char prefix [ ] __initconst = " omapdss, " ;
struct dss_conv_node {
struct list_head list ;
struct device_node * node ;
bool root ;
} ;
static int __init omapdss_count_strings ( const struct property * prop )
{
const char * p = prop - > value ;
int l = 0 , total = 0 ;
int i ;
for ( i = 0 ; total < prop - > length ; total + = l , p + = l , i + + )
l = strlen ( p ) + 1 ;
return i ;
}
static void __init omapdss_update_prop ( struct device_node * node , char * compat ,
int len )
{
struct property * prop ;
prop = kzalloc ( sizeof ( * prop ) , GFP_KERNEL ) ;
if ( ! prop )
return ;
prop - > name = " compatible " ;
prop - > value = compat ;
prop - > length = len ;
of_update_property ( node , prop ) ;
}
static void __init omapdss_prefix_strcpy ( char * dst , int dst_len ,
const char * src , int src_len )
{
size_t total = 0 ;
while ( total < src_len ) {
size_t l = strlen ( src ) + 1 ;
strcpy ( dst , prefix ) ;
dst + = strlen ( prefix ) ;
strcpy ( dst , src ) ;
dst + = l ;
src + = l ;
total + = l ;
}
}
/* prepend compatible property strings with "omapdss," */
static void __init omapdss_omapify_node ( struct device_node * node )
{
struct property * prop ;
char * new_compat ;
int num_strs ;
int new_len ;
prop = of_find_property ( node , " compatible " , NULL ) ;
if ( ! prop | | ! prop - > value )
return ;
if ( strnlen ( prop - > value , prop - > length ) > = prop - > length )
return ;
/* is it already prefixed? */
if ( strncmp ( prefix , prop - > value , strlen ( prefix ) ) = = 0 )
return ;
num_strs = omapdss_count_strings ( prop ) ;
new_len = prop - > length + strlen ( prefix ) * num_strs ;
new_compat = kmalloc ( new_len , GFP_KERNEL ) ;
omapdss_prefix_strcpy ( new_compat , new_len , prop - > value , prop - > length ) ;
omapdss_update_prop ( node , new_compat , new_len ) ;
}
static void __init omapdss_add_to_list ( struct device_node * node , bool root )
{
2016-12-12 11:57:24 +02:00
struct dss_conv_node * n = kmalloc ( sizeof ( * n ) , GFP_KERNEL ) ;
2014-05-30 15:56:34 +05:30
if ( n ) {
n - > node = node ;
n - > root = root ;
list_add ( & n - > list , & dss_conv_list ) ;
}
2014-05-15 10:41:34 +03:00
}
static bool __init omapdss_list_contains ( const struct device_node * node )
{
struct dss_conv_node * n ;
list_for_each_entry ( n , & dss_conv_list , list ) {
if ( n - > node = = node )
return true ;
}
return false ;
}
static void __init omapdss_walk_device ( struct device_node * node , bool root )
{
struct device_node * n ;
omapdss_add_to_list ( node , root ) ;
/*
* 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 ;
pn = of_graph_get_remote_port_parent ( n ) ;
2014-12-01 13:32:32 +01:00
if ( ! pn )
2014-05-15 10:41:34 +03:00
continue ;
if ( ! of_device_is_available ( pn ) | | omapdss_list_contains ( pn ) ) {
of_node_put ( pn ) ;
continue ;
}
omapdss_walk_device ( pn , false ) ;
}
}
static const struct of_device_id omapdss_of_match [ ] __initconst = {
{ . compatible = " ti,omap2-dss " , } ,
{ . compatible = " ti,omap3-dss " , } ,
{ . compatible = " ti,omap4-dss " , } ,
{ . compatible = " ti,omap5-dss " , } ,
2014-12-31 11:23:31 +02:00
{ . compatible = " ti,dra7-dss " , } ,
2014-05-15 10:41:34 +03:00
{ } ,
} ;
2018-09-23 15:05:10 +03:00
static const struct of_device_id omapdss_of_fixups_whitelist [ ] __initconst = {
{ . compatible = " composite-video-connector " } ,
{ . compatible = " hdmi-connector " } ,
{ . compatible = " lgphilips,lb035q02 " } ,
{ . compatible = " nec,nl8048hl11 " } ,
{ . compatible = " panel-dsi-cm " } ,
{ . compatible = " sharp,ls037v7dw01 " } ,
{ . compatible = " sony,acx565akm " } ,
{ . compatible = " svideo-connector " } ,
{ . compatible = " ti,opa362 " } ,
{ . compatible = " ti,tpd12s015 " } ,
{ . compatible = " toppoly,td028ttec1 " } ,
{ . compatible = " tpo,td028ttec1 " } ,
{ . compatible = " tpo,td043mtea1 " } ,
} ;
2014-05-15 10:41:34 +03:00
static int __init omapdss_boot_init ( void )
{
struct device_node * dss , * child ;
INIT_LIST_HEAD ( & dss_conv_list ) ;
dss = of_find_matching_node ( NULL , omapdss_of_match ) ;
if ( dss = = NULL | | ! of_device_is_available ( dss ) )
return 0 ;
omapdss_walk_device ( dss , true ) ;
for_each_available_child_of_node ( dss , child ) {
2015-10-12 22:43:14 +02:00
if ( ! of_find_property ( child , " compatible " , NULL ) )
2014-05-15 10:41:34 +03:00
continue ;
omapdss_walk_device ( child , true ) ;
}
while ( ! list_empty ( & dss_conv_list ) ) {
struct dss_conv_node * n ;
n = list_first_entry ( & dss_conv_list , struct dss_conv_node ,
list ) ;
2018-09-23 15:05:10 +03:00
if ( of_match_node ( omapdss_of_fixups_whitelist , n - > node ) )
2014-05-15 10:41:34 +03:00
omapdss_omapify_node ( n - > node ) ;
list_del ( & n - > list ) ;
of_node_put ( n - > node ) ;
kfree ( n ) ;
}
return 0 ;
}
subsys_initcall ( omapdss_boot_init ) ;