2015-02-18 11:24:03 +02:00
/*
* Copyright ( C ) 2015 Texas Instruments
* Author : Jyri Sarha < jsarha @ 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 .
*
*/
/*
* To support the old " ti,tilcdc,slave " binding the binding has to be
* transformed to the new external encoder binding .
*/
# include <linux/kernel.h>
# include <linux/of.h>
# include <linux/of_graph.h>
# include <linux/of_fdt.h>
# include <linux/slab.h>
# include <linux/list.h>
# include "tilcdc_slave_compat.h"
struct kfree_table {
int total ;
int num ;
void * * table ;
} ;
static int __init kfree_table_init ( struct kfree_table * kft )
{
kft - > total = 32 ;
kft - > num = 0 ;
kft - > table = kmalloc ( kft - > total * sizeof ( * kft - > table ) ,
GFP_KERNEL ) ;
if ( ! kft - > table )
return - ENOMEM ;
return 0 ;
}
static int __init kfree_table_add ( struct kfree_table * kft , void * p )
{
if ( kft - > num = = kft - > total ) {
void * * old = kft - > table ;
kft - > total * = 2 ;
kft - > table = krealloc ( old , kft - > total * sizeof ( * kft - > table ) ,
GFP_KERNEL ) ;
if ( ! kft - > table ) {
kft - > table = old ;
kfree ( p ) ;
return - ENOMEM ;
}
}
kft - > table [ kft - > num + + ] = p ;
return 0 ;
}
static void __init kfree_table_free ( struct kfree_table * kft )
{
int i ;
for ( i = 0 ; i < kft - > num ; i + + )
kfree ( kft - > table [ i ] ) ;
kfree ( kft - > table ) ;
}
static
struct property * __init tilcdc_prop_dup ( const struct property * prop ,
struct kfree_table * kft )
{
struct property * nprop ;
nprop = kzalloc ( sizeof ( * nprop ) , GFP_KERNEL ) ;
if ( ! nprop | | kfree_table_add ( kft , nprop ) )
return NULL ;
nprop - > name = kstrdup ( prop - > name , GFP_KERNEL ) ;
if ( ! nprop - > name | | kfree_table_add ( kft , nprop - > name ) )
return NULL ;
nprop - > value = kmemdup ( prop - > value , prop - > length , GFP_KERNEL ) ;
if ( ! nprop - > value | | kfree_table_add ( kft , nprop - > value ) )
return NULL ;
nprop - > length = prop - > length ;
return nprop ;
}
static void __init tilcdc_copy_props ( struct device_node * from ,
struct device_node * to ,
const char * const props [ ] ,
struct kfree_table * kft )
{
struct property * prop ;
int i ;
for ( i = 0 ; props [ i ] ; i + + ) {
prop = of_find_property ( from , props [ i ] , NULL ) ;
if ( ! prop )
continue ;
prop = tilcdc_prop_dup ( prop , kft ) ;
if ( ! prop )
continue ;
prop - > next = to - > properties ;
to - > properties = prop ;
}
}
static int __init tilcdc_prop_str_update ( struct property * prop ,
const char * str ,
struct kfree_table * kft )
{
prop - > value = kstrdup ( str , GFP_KERNEL ) ;
if ( kfree_table_add ( kft , prop - > value ) | | ! prop - > value )
return - ENOMEM ;
prop - > length = strlen ( str ) + 1 ;
return 0 ;
}
static void __init tilcdc_node_disable ( struct device_node * node )
{
struct property * prop ;
prop = kzalloc ( sizeof ( * prop ) , GFP_KERNEL ) ;
if ( ! prop )
return ;
prop - > name = " status " ;
prop - > value = " disabled " ;
prop - > length = strlen ( ( char * ) prop - > value ) + 1 ;
of_update_property ( node , prop ) ;
}
2016-09-08 19:29:24 +08:00
static struct device_node * __init tilcdc_get_overlay ( struct kfree_table * kft )
2015-02-18 11:24:03 +02:00
{
const int size = __dtb_tilcdc_slave_compat_end -
__dtb_tilcdc_slave_compat_begin ;
static void * overlay_data ;
struct device_node * overlay ;
int ret ;
if ( ! size ) {
pr_warn ( " %s: No overlay data \n " , __func__ ) ;
return NULL ;
}
overlay_data = kmemdup ( __dtb_tilcdc_slave_compat_begin ,
size , GFP_KERNEL ) ;
if ( ! overlay_data | | kfree_table_add ( kft , overlay_data ) )
return NULL ;
2016-05-03 23:22:50 +10:00
of_fdt_unflatten_tree ( overlay_data , NULL , & overlay ) ;
2015-02-18 11:24:03 +02:00
if ( ! overlay ) {
pr_warn ( " %s: Unfattening overlay tree failed \n " , __func__ ) ;
return NULL ;
}
ret = of_resolve_phandles ( overlay ) ;
if ( ret ) {
pr_err ( " %s: Failed to resolve phandles: %d \n " , __func__ , ret ) ;
return NULL ;
}
return overlay ;
}
static const struct of_device_id tilcdc_slave_of_match [ ] __initconst = {
{ . compatible = " ti,tilcdc,slave " , } ,
{ } ,
} ;
static const struct of_device_id tilcdc_of_match [ ] __initconst = {
{ . compatible = " ti,am33xx-tilcdc " , } ,
{ } ,
} ;
static const struct of_device_id tilcdc_tda998x_of_match [ ] __initconst = {
{ . compatible = " nxp,tda998x " , } ,
{ } ,
} ;
static const char * const tilcdc_slave_props [ ] __initconst = {
" pinctrl-names " ,
" pinctrl-0 " ,
" pinctrl-1 " ,
NULL
} ;
2016-09-08 19:29:24 +08:00
static void __init tilcdc_convert_slave_node ( void )
2015-02-18 11:24:03 +02:00
{
struct device_node * slave = NULL , * lcdc = NULL ;
struct device_node * i2c = NULL , * fragment = NULL ;
struct device_node * overlay , * encoder ;
struct property * prop ;
/* For all memory needed for the overlay tree. This memory can
be freed after the overlay has been applied . */
struct kfree_table kft ;
int ret ;
if ( kfree_table_init ( & kft ) )
2016-09-22 09:29:23 +02:00
return ;
2015-02-18 11:24:03 +02:00
lcdc = of_find_matching_node ( NULL , tilcdc_of_match ) ;
slave = of_find_matching_node ( NULL , tilcdc_slave_of_match ) ;
if ( ! slave | | ! of_device_is_available ( lcdc ) )
goto out ;
i2c = of_parse_phandle ( slave , " i2c " , 0 ) ;
if ( ! i2c ) {
pr_err ( " %s: Can't find i2c node trough phandle \n " , __func__ ) ;
goto out ;
}
overlay = tilcdc_get_overlay ( & kft ) ;
if ( ! overlay )
goto out ;
encoder = of_find_matching_node ( overlay , tilcdc_tda998x_of_match ) ;
if ( ! encoder ) {
pr_err ( " %s: Failed to find tda998x node \n " , __func__ ) ;
goto out ;
}
tilcdc_copy_props ( slave , encoder , tilcdc_slave_props , & kft ) ;
for_each_child_of_node ( overlay , fragment ) {
prop = of_find_property ( fragment , " target-path " , NULL ) ;
if ( ! prop )
continue ;
if ( ! strncmp ( " i2c " , ( char * ) prop - > value , prop - > length ) )
if ( tilcdc_prop_str_update ( prop , i2c - > full_name , & kft ) )
goto out ;
if ( ! strncmp ( " lcdc " , ( char * ) prop - > value , prop - > length ) )
if ( tilcdc_prop_str_update ( prop , lcdc - > full_name , & kft ) )
goto out ;
}
tilcdc_node_disable ( slave ) ;
ret = of_overlay_create ( overlay ) ;
if ( ret )
pr_err ( " %s: Creating overlay failed: %d \n " , __func__ , ret ) ;
else
pr_info ( " %s: ti,tilcdc,slave node successfully converted \n " ,
__func__ ) ;
out :
kfree_table_free ( & kft ) ;
of_node_put ( i2c ) ;
of_node_put ( slave ) ;
of_node_put ( lcdc ) ;
of_node_put ( fragment ) ;
}
2016-09-08 19:29:24 +08:00
static int __init tilcdc_slave_compat_init ( void )
2015-02-18 11:24:03 +02:00
{
tilcdc_convert_slave_node ( ) ;
return 0 ;
}
subsys_initcall ( tilcdc_slave_compat_init ) ;