2016-08-12 08:05:51 -03:00
/*
* V4L2 fwnode binding parsing library
*
* The origins of the V4L2 fwnode library are in V4L2 OF library that
* formerly was located in v4l2 - of . c .
*
* Copyright ( c ) 2016 Intel Corporation .
* Author : Sakari Ailus < sakari . ailus @ linux . intel . com >
*
* Copyright ( C ) 2012 - 2013 Samsung Electronics Co . , Ltd .
* Author : Sylwester Nawrocki < s . nawrocki @ samsung . com >
*
* Copyright ( C ) 2012 Renesas Electronics Corp .
* Author : Guennadi Liakhovetski < g . liakhovetski @ gmx . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*/
# include <linux/acpi.h>
# include <linux/kernel.h>
2017-08-17 11:28:21 -04:00
# include <linux/mm.h>
2016-08-12 08:05:51 -03:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/property.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/types.h>
2017-08-17 11:28:21 -04:00
# include <media/v4l2-async.h>
2016-08-12 08:05:51 -03:00
# include <media/v4l2-fwnode.h>
2017-09-24 18:47:44 -04:00
# include <media/v4l2-subdev.h>
2016-08-12 08:05:51 -03:00
2015-02-25 14:51:01 -05:00
enum v4l2_fwnode_bus_type {
V4L2_FWNODE_BUS_TYPE_GUESS = 0 ,
V4L2_FWNODE_BUS_TYPE_CSI2_CPHY ,
V4L2_FWNODE_BUS_TYPE_CSI1 ,
V4L2_FWNODE_BUS_TYPE_CCP2 ,
2018-07-03 17:40:37 -04:00
V4L2_FWNODE_BUS_TYPE_CSI2_DPHY ,
V4L2_FWNODE_BUS_TYPE_PARALLEL ,
V4L2_FWNODE_BUS_TYPE_BT656 ,
2015-02-25 14:51:01 -05:00
NR_OF_V4L2_FWNODE_BUS_TYPE ,
} ;
2018-07-17 12:17:02 -04:00
static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type ;
enum v4l2_mbus_type mbus_type ;
const char * name ;
2019-02-18 14:29:04 -05:00
} buses [ ] = {
2018-07-17 12:17:02 -04:00
{
V4L2_FWNODE_BUS_TYPE_GUESS ,
V4L2_MBUS_UNKNOWN ,
" not specified " ,
} , {
V4L2_FWNODE_BUS_TYPE_CSI2_CPHY ,
V4L2_MBUS_CSI2_CPHY ,
" MIPI CSI-2 C-PHY " ,
} , {
V4L2_FWNODE_BUS_TYPE_CSI1 ,
V4L2_MBUS_CSI1 ,
" MIPI CSI-1 " ,
} , {
V4L2_FWNODE_BUS_TYPE_CCP2 ,
V4L2_MBUS_CCP2 ,
" compact camera port 2 " ,
} , {
V4L2_FWNODE_BUS_TYPE_CSI2_DPHY ,
V4L2_MBUS_CSI2_DPHY ,
" MIPI CSI-2 D-PHY " ,
} , {
V4L2_FWNODE_BUS_TYPE_PARALLEL ,
V4L2_MBUS_PARALLEL ,
" parallel " ,
} , {
V4L2_FWNODE_BUS_TYPE_BT656 ,
V4L2_MBUS_BT656 ,
" Bt.656 " ,
}
} ;
static const struct v4l2_fwnode_bus_conv *
get_v4l2_fwnode_bus_conv_by_fwnode_bus ( enum v4l2_fwnode_bus_type type )
{
unsigned int i ;
2019-02-18 14:29:04 -05:00
for ( i = 0 ; i < ARRAY_SIZE ( buses ) ; i + + )
if ( buses [ i ] . fwnode_bus_type = = type )
return & buses [ i ] ;
2018-07-17 12:17:02 -04:00
return NULL ;
}
static enum v4l2_mbus_type
v4l2_fwnode_bus_type_to_mbus ( enum v4l2_fwnode_bus_type type )
{
const struct v4l2_fwnode_bus_conv * conv =
get_v4l2_fwnode_bus_conv_by_fwnode_bus ( type ) ;
return conv ? conv - > mbus_type : V4L2_MBUS_UNKNOWN ;
}
2018-01-02 10:44:34 -05:00
static const char *
v4l2_fwnode_bus_type_to_string ( enum v4l2_fwnode_bus_type type )
{
const struct v4l2_fwnode_bus_conv * conv =
get_v4l2_fwnode_bus_conv_by_fwnode_bus ( type ) ;
return conv ? conv - > name : " not found " ;
}
static const struct v4l2_fwnode_bus_conv *
get_v4l2_fwnode_bus_conv_by_mbus ( enum v4l2_mbus_type type )
{
unsigned int i ;
2019-02-18 14:29:04 -05:00
for ( i = 0 ; i < ARRAY_SIZE ( buses ) ; i + + )
if ( buses [ i ] . mbus_type = = type )
return & buses [ i ] ;
2018-01-02 10:44:34 -05:00
return NULL ;
}
static const char *
v4l2_fwnode_mbus_type_to_string ( enum v4l2_mbus_type type )
{
const struct v4l2_fwnode_bus_conv * conv =
get_v4l2_fwnode_bus_conv_by_mbus ( type ) ;
return conv ? conv - > name : " not found " ;
}
2017-02-20 05:42:09 -05:00
static int v4l2_fwnode_endpoint_parse_csi2_bus ( struct fwnode_handle * fwnode ,
2018-07-18 09:33:45 -04:00
struct v4l2_fwnode_endpoint * vep ,
2018-07-17 12:17:02 -04:00
enum v4l2_mbus_type bus_type )
2016-08-12 08:05:51 -03:00
{
struct v4l2_fwnode_bus_mipi_csi2 * bus = & vep - > bus . mipi_csi2 ;
2018-07-20 09:57:48 -04:00
bool have_clk_lane = false , have_data_lanes = false ,
have_lane_polarities = false ;
2016-08-12 08:05:51 -03:00
unsigned int flags = 0 , lanes_used = 0 ;
2018-07-18 09:33:45 -04:00
u32 array [ 1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES ] ;
2018-07-20 09:57:48 -04:00
u32 clock_lane = 0 ;
2018-07-18 09:33:45 -04:00
unsigned int num_data_lanes = 0 ;
2018-07-20 09:57:48 -04:00
bool use_default_lane_mapping = false ;
2016-08-12 08:05:51 -03:00
unsigned int i ;
u32 v ;
int rval ;
2018-07-23 09:09:28 -04:00
if ( bus_type = = V4L2_MBUS_CSI2_DPHY | |
bus_type = = V4L2_MBUS_CSI2_CPHY ) {
2018-07-20 09:57:48 -04:00
use_default_lane_mapping = true ;
2018-07-18 09:33:45 -04:00
num_data_lanes = min_t ( u32 , bus - > num_data_lanes ,
V4L2_FWNODE_CSI2_MAX_DATA_LANES ) ;
2018-07-20 09:57:48 -04:00
clock_lane = bus - > clock_lane ;
if ( clock_lane )
use_default_lane_mapping = false ;
for ( i = 0 ; i < num_data_lanes ; i + + ) {
2018-07-20 06:14:14 -04:00
array [ i ] = bus - > data_lanes [ i ] ;
2018-07-20 09:57:48 -04:00
if ( array [ i ] )
use_default_lane_mapping = false ;
}
if ( use_default_lane_mapping )
2019-03-01 08:48:38 -05:00
pr_debug ( " no lane mapping given, using defaults \n " ) ;
2018-07-20 06:14:14 -04:00
}
2016-08-12 08:05:51 -03:00
rval = fwnode_property_read_u32_array ( fwnode , " data-lanes " , NULL , 0 ) ;
if ( rval > 0 ) {
2018-07-18 09:33:45 -04:00
num_data_lanes =
2017-08-14 06:43:07 -04:00
min_t ( int , V4L2_FWNODE_CSI2_MAX_DATA_LANES , rval ) ;
2016-08-12 08:05:51 -03:00
fwnode_property_read_u32_array ( fwnode , " data-lanes " , array ,
2018-07-18 09:33:45 -04:00
num_data_lanes ) ;
2018-07-20 09:57:48 -04:00
have_data_lanes = true ;
2019-03-01 08:48:38 -05:00
if ( use_default_lane_mapping ) {
pr_debug ( " data-lanes property exists; disabling default mapping \n " ) ;
use_default_lane_mapping = false ;
}
2018-07-20 06:14:14 -04:00
}
2016-08-12 08:05:51 -03:00
2018-07-20 06:14:14 -04:00
for ( i = 0 ; i < num_data_lanes ; i + + ) {
2018-07-20 09:57:48 -04:00
if ( lanes_used & BIT ( array [ i ] ) ) {
if ( have_data_lanes | | ! use_default_lane_mapping )
pr_warn ( " duplicated lane %u in data-lanes, using defaults \n " ,
array [ i ] ) ;
use_default_lane_mapping = true ;
}
2018-07-20 06:14:14 -04:00
lanes_used | = BIT ( array [ i ] ) ;
2016-08-12 08:05:51 -03:00
2018-07-20 09:57:48 -04:00
if ( have_data_lanes )
pr_debug ( " lane %u position %u \n " , i , array [ i ] ) ;
2018-07-18 09:33:45 -04:00
}
2016-08-12 08:05:51 -03:00
2018-07-18 09:33:45 -04:00
rval = fwnode_property_read_u32_array ( fwnode , " lane-polarities " , NULL ,
0 ) ;
if ( rval > 0 ) {
if ( rval ! = 1 + num_data_lanes /* clock+data */ ) {
pr_warn ( " invalid number of lane-polarities entries (need %u, got %u) \n " ,
1 + num_data_lanes , rval ) ;
return - EINVAL ;
2017-06-26 14:07:54 -04:00
}
2017-08-14 06:15:21 -04:00
2018-07-03 07:06:20 -04:00
have_lane_polarities = true ;
2016-08-12 08:05:51 -03:00
}
if ( ! fwnode_property_read_u32 ( fwnode , " clock-lanes " , & v ) ) {
2018-07-20 09:57:48 -04:00
clock_lane = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " clock lane position %u \n " , v ) ;
2018-07-20 09:57:48 -04:00
have_clk_lane = true ;
}
if ( lanes_used & BIT ( clock_lane ) ) {
if ( have_clk_lane | | ! use_default_lane_mapping )
pr_warn ( " duplicated lane %u in clock-lanes, using defaults \n " ,
2018-10-04 17:10:46 -04:00
v ) ;
2018-07-20 09:57:48 -04:00
use_default_lane_mapping = true ;
2016-08-12 08:05:51 -03:00
}
2017-12-04 16:25:06 -05:00
if ( fwnode_property_present ( fwnode , " clock-noncontinuous " ) ) {
2016-08-12 08:05:51 -03:00
flags | = V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ;
2017-12-04 16:25:06 -05:00
pr_debug ( " non-continuous clock \n " ) ;
2018-07-20 05:04:29 -04:00
} else {
2016-08-12 08:05:51 -03:00
flags | = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK ;
2017-12-04 16:25:06 -05:00
}
2016-08-12 08:05:51 -03:00
2018-07-23 09:09:28 -04:00
if ( bus_type = = V4L2_MBUS_CSI2_DPHY | |
bus_type = = V4L2_MBUS_CSI2_CPHY | | lanes_used | |
2018-07-18 09:33:45 -04:00
have_clk_lane | | ( flags & ~ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK ) ) {
2019-03-02 10:23:12 -05:00
/* Only D-PHY has a clock lane. */
unsigned int dfl_data_lane_index =
bus_type = = V4L2_MBUS_CSI2_DPHY ;
2018-07-31 06:43:14 -04:00
bus - > flags = flags ;
2018-07-23 09:09:28 -04:00
if ( bus_type = = V4L2_MBUS_UNKNOWN )
vep - > bus_type = V4L2_MBUS_CSI2_DPHY ;
2018-07-18 09:33:45 -04:00
bus - > num_data_lanes = num_data_lanes ;
2018-07-20 09:57:48 -04:00
if ( use_default_lane_mapping ) {
bus - > clock_lane = 0 ;
for ( i = 0 ; i < num_data_lanes ; i + + )
2019-03-02 10:23:12 -05:00
bus - > data_lanes [ i ] = dfl_data_lane_index + i ;
2018-07-20 09:57:48 -04:00
} else {
bus - > clock_lane = clock_lane ;
for ( i = 0 ; i < num_data_lanes ; i + + )
bus - > data_lanes [ i ] = array [ i ] ;
}
2018-07-03 07:06:20 -04:00
if ( have_lane_polarities ) {
fwnode_property_read_u32_array ( fwnode ,
" lane-polarities " , array ,
1 + num_data_lanes ) ;
for ( i = 0 ; i < 1 + num_data_lanes ; i + + ) {
bus - > lane_polarities [ i ] = array [ i ] ;
pr_debug ( " lane %u polarity %sinverted " ,
i , array [ i ] ? " " : " not " ) ;
}
} else {
pr_debug ( " no lane polarities defined, assuming not inverted \n " ) ;
}
2018-07-31 06:43:14 -04:00
}
2016-08-12 08:05:51 -03:00
return 0 ;
}
2018-01-02 08:14:30 -05:00
# define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
V4L2_MBUS_HSYNC_ACTIVE_LOW | \
V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
V4L2_MBUS_VSYNC_ACTIVE_LOW | \
V4L2_MBUS_FIELD_EVEN_HIGH | \
V4L2_MBUS_FIELD_EVEN_LOW )
2018-10-04 17:10:46 -04:00
static void
v4l2_fwnode_endpoint_parse_parallel_bus ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_endpoint * vep ,
enum v4l2_mbus_type bus_type )
2016-08-12 08:05:51 -03:00
{
struct v4l2_fwnode_bus_parallel * bus = & vep - > bus . parallel ;
unsigned int flags = 0 ;
u32 v ;
2018-07-03 07:09:25 -04:00
if ( bus_type = = V4L2_MBUS_PARALLEL | | bus_type = = V4L2_MBUS_BT656 )
flags = bus - > flags ;
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " hsync-active " , & v ) ) {
2018-07-03 07:09:25 -04:00
flags & = ~ ( V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_HSYNC_ACTIVE_LOW ) ;
2016-08-12 08:05:51 -03:00
flags | = v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
V4L2_MBUS_HSYNC_ACTIVE_LOW ;
2017-12-04 16:25:06 -05:00
pr_debug ( " hsync-active %s \n " , v ? " high " : " low " ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " vsync-active " , & v ) ) {
2018-07-03 07:09:25 -04:00
flags & = ~ ( V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_VSYNC_ACTIVE_LOW ) ;
2016-08-12 08:05:51 -03:00
flags | = v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
V4L2_MBUS_VSYNC_ACTIVE_LOW ;
2017-12-04 16:25:06 -05:00
pr_debug ( " vsync-active %s \n " , v ? " high " : " low " ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " field-even-active " , & v ) ) {
2018-07-03 07:09:25 -04:00
flags & = ~ ( V4L2_MBUS_FIELD_EVEN_HIGH |
V4L2_MBUS_FIELD_EVEN_LOW ) ;
2016-08-12 08:05:51 -03:00
flags | = v ? V4L2_MBUS_FIELD_EVEN_HIGH :
V4L2_MBUS_FIELD_EVEN_LOW ;
2017-12-04 16:25:06 -05:00
pr_debug ( " field-even-active %s \n " , v ? " high " : " low " ) ;
}
if ( ! fwnode_property_read_u32 ( fwnode , " pclk-sample " , & v ) ) {
2018-07-03 07:09:25 -04:00
flags & = ~ ( V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_PCLK_SAMPLE_FALLING ) ;
2016-08-12 08:05:51 -03:00
flags | = v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
V4L2_MBUS_PCLK_SAMPLE_FALLING ;
2017-12-04 16:25:06 -05:00
pr_debug ( " pclk-sample %s \n " , v ? " high " : " low " ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " data-active " , & v ) ) {
2018-12-07 10:56:01 -05:00
flags & = ~ ( V4L2_MBUS_DATA_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_LOW ) ;
2016-08-12 08:05:51 -03:00
flags | = v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
V4L2_MBUS_DATA_ACTIVE_LOW ;
2017-12-04 16:25:06 -05:00
pr_debug ( " data-active %s \n " , v ? " high " : " low " ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( fwnode_property_present ( fwnode , " slave-mode " ) ) {
pr_debug ( " slave mode \n " ) ;
2018-07-03 07:09:25 -04:00
flags & = ~ V4L2_MBUS_MASTER ;
2016-08-12 08:05:51 -03:00
flags | = V4L2_MBUS_SLAVE ;
2017-12-04 16:25:06 -05:00
} else {
2018-07-03 07:09:25 -04:00
flags & = ~ V4L2_MBUS_SLAVE ;
2016-08-12 08:05:51 -03:00
flags | = V4L2_MBUS_MASTER ;
2017-12-04 16:25:06 -05:00
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " bus-width " , & v ) ) {
2016-08-12 08:05:51 -03:00
bus - > bus_width = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " bus-width %u \n " , v ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " data-shift " , & v ) ) {
2016-08-12 08:05:51 -03:00
bus - > data_shift = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " data-shift %u \n " , v ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " sync-on-green-active " , & v ) ) {
2018-07-03 07:09:25 -04:00
flags & = ~ ( V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH |
V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW ) ;
2016-08-12 08:05:51 -03:00
flags | = v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW ;
2017-12-04 16:25:06 -05:00
pr_debug ( " sync-on-green-active %s \n " , v ? " high " : " low " ) ;
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " data-enable-active " , & v ) ) {
2018-07-03 07:09:25 -04:00
flags & = ~ ( V4L2_MBUS_DATA_ENABLE_HIGH |
V4L2_MBUS_DATA_ENABLE_LOW ) ;
2018-07-09 10:19:19 -04:00
flags | = v ? V4L2_MBUS_DATA_ENABLE_HIGH :
V4L2_MBUS_DATA_ENABLE_LOW ;
2017-12-04 16:25:06 -05:00
pr_debug ( " data-enable-active %s \n " , v ? " high " : " low " ) ;
}
2018-07-09 10:19:19 -04:00
2018-01-02 08:14:30 -05:00
switch ( bus_type ) {
default :
bus - > flags = flags ;
if ( flags & PARALLEL_MBUS_FLAGS )
vep - > bus_type = V4L2_MBUS_PARALLEL ;
else
vep - > bus_type = V4L2_MBUS_BT656 ;
break ;
2018-07-17 12:17:02 -04:00
case V4L2_MBUS_PARALLEL :
2018-07-31 06:43:14 -04:00
vep - > bus_type = V4L2_MBUS_PARALLEL ;
2018-01-02 08:14:30 -05:00
bus - > flags = flags ;
break ;
2018-07-17 12:17:02 -04:00
case V4L2_MBUS_BT656 :
2018-07-31 06:43:14 -04:00
vep - > bus_type = V4L2_MBUS_BT656 ;
2018-01-02 08:14:30 -05:00
bus - > flags = flags & ~ PARALLEL_MBUS_FLAGS ;
break ;
}
2016-08-12 08:05:51 -03:00
}
2017-07-20 16:27:27 -04:00
static void
v4l2_fwnode_endpoint_parse_csi1_bus ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_endpoint * vep ,
2018-07-17 12:17:02 -04:00
enum v4l2_mbus_type bus_type )
2015-02-25 14:39:11 -05:00
{
struct v4l2_fwnode_bus_mipi_csi1 * bus = & vep - > bus . mipi_csi1 ;
u32 v ;
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " clock-inv " , & v ) ) {
2015-02-25 14:39:11 -05:00
bus - > clock_inv = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " clock-inv %u \n " , v ) ;
}
2015-02-25 14:39:11 -05:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " strobe " , & v ) ) {
2015-02-25 14:39:11 -05:00
bus - > strobe = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " strobe %u \n " , v ) ;
}
2015-02-25 14:39:11 -05:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " data-lanes " , & v ) ) {
2015-02-25 14:39:11 -05:00
bus - > data_lane = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " data-lanes %u \n " , v ) ;
}
2015-02-25 14:39:11 -05:00
2017-12-04 16:25:06 -05:00
if ( ! fwnode_property_read_u32 ( fwnode , " clock-lanes " , & v ) ) {
2015-02-25 14:39:11 -05:00
bus - > clock_lane = v ;
2017-12-04 16:25:06 -05:00
pr_debug ( " clock-lanes %u \n " , v ) ;
}
2015-02-25 14:39:11 -05:00
2018-07-17 12:17:02 -04:00
if ( bus_type = = V4L2_MBUS_CCP2 )
2015-02-25 14:39:11 -05:00
vep - > bus_type = V4L2_MBUS_CCP2 ;
else
vep - > bus_type = V4L2_MBUS_CSI1 ;
}
2017-12-04 16:25:06 -05:00
static int __v4l2_fwnode_endpoint_parse ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_endpoint * vep )
2016-08-12 08:05:51 -03:00
{
2018-07-19 11:22:49 -04:00
u32 bus_type = V4L2_FWNODE_BUS_TYPE_GUESS ;
2018-07-17 12:17:02 -04:00
enum v4l2_mbus_type mbus_type ;
2016-08-12 08:05:51 -03:00
int rval ;
2018-07-31 05:21:53 -04:00
if ( vep - > bus_type = = V4L2_MBUS_UNKNOWN ) {
/* Zero fields from bus union to until the end */
memset ( & vep - > bus , 0 ,
sizeof ( * vep ) - offsetof ( typeof ( * vep ) , bus ) ) ;
}
2017-12-04 16:25:06 -05:00
pr_debug ( " ===== begin V4L2 endpoint properties \n " ) ;
2018-07-18 09:20:34 -04:00
/*
* Zero the fwnode graph endpoint memory in case we don ' t end up parsing
* the endpoint .
*/
memset ( & vep - > base , 0 , sizeof ( vep - > base ) ) ;
2016-08-12 08:05:51 -03:00
2015-02-25 14:51:01 -05:00
fwnode_property_read_u32 ( fwnode , " bus-type " , & bus_type ) ;
2018-01-02 10:44:34 -05:00
pr_debug ( " fwnode video bus type %s (%u), mbus type %s (%u) \n " ,
v4l2_fwnode_bus_type_to_string ( bus_type ) , bus_type ,
v4l2_fwnode_mbus_type_to_string ( vep - > bus_type ) ,
vep - > bus_type ) ;
2018-07-17 12:17:02 -04:00
mbus_type = v4l2_fwnode_bus_type_to_mbus ( bus_type ) ;
2018-07-19 11:22:49 -04:00
if ( vep - > bus_type ! = V4L2_MBUS_UNKNOWN ) {
if ( mbus_type ! = V4L2_MBUS_UNKNOWN & &
vep - > bus_type ! = mbus_type ) {
pr_debug ( " expecting bus type %s \n " ,
2018-10-04 17:10:46 -04:00
v4l2_fwnode_mbus_type_to_string ( vep - > bus_type ) ) ;
2018-07-19 11:22:49 -04:00
return - ENXIO ;
}
} else {
vep - > bus_type = mbus_type ;
}
switch ( vep - > bus_type ) {
2018-07-17 12:17:02 -04:00
case V4L2_MBUS_UNKNOWN :
2018-07-18 09:33:45 -04:00
rval = v4l2_fwnode_endpoint_parse_csi2_bus ( fwnode , vep ,
2018-07-19 11:22:49 -04:00
V4L2_MBUS_UNKNOWN ) ;
2015-02-25 14:39:11 -05:00
if ( rval )
return rval ;
2018-07-31 06:43:14 -04:00
if ( vep - > bus_type = = V4L2_MBUS_UNKNOWN )
2018-10-04 17:10:46 -04:00
v4l2_fwnode_endpoint_parse_parallel_bus ( fwnode , vep ,
V4L2_MBUS_UNKNOWN ) ;
2015-02-25 14:39:11 -05:00
2018-01-02 10:44:34 -05:00
pr_debug ( " assuming media bus type %s (%u) \n " ,
v4l2_fwnode_mbus_type_to_string ( vep - > bus_type ) ,
vep - > bus_type ) ;
2017-12-04 16:25:06 -05:00
break ;
2018-07-17 12:17:02 -04:00
case V4L2_MBUS_CCP2 :
case V4L2_MBUS_CSI1 :
2018-07-19 11:22:49 -04:00
v4l2_fwnode_endpoint_parse_csi1_bus ( fwnode , vep , vep - > bus_type ) ;
2015-02-25 14:39:11 -05:00
2018-01-02 08:14:30 -05:00
break ;
2018-07-17 12:17:02 -04:00
case V4L2_MBUS_CSI2_DPHY :
2018-07-23 09:09:28 -04:00
case V4L2_MBUS_CSI2_CPHY :
2018-07-18 09:33:45 -04:00
rval = v4l2_fwnode_endpoint_parse_csi2_bus ( fwnode , vep ,
2018-07-19 11:22:49 -04:00
vep - > bus_type ) ;
2018-01-02 08:14:30 -05:00
if ( rval )
return rval ;
break ;
2018-07-17 12:17:02 -04:00
case V4L2_MBUS_PARALLEL :
case V4L2_MBUS_BT656 :
2018-07-19 11:22:49 -04:00
v4l2_fwnode_endpoint_parse_parallel_bus ( fwnode , vep ,
vep - > bus_type ) ;
2018-01-02 08:14:30 -05:00
2017-12-04 16:25:06 -05:00
break ;
2015-02-25 14:39:11 -05:00
default :
2018-07-17 12:17:02 -04:00
pr_warn ( " unsupported bus type %u \n " , mbus_type ) ;
2015-02-25 14:39:11 -05:00
return - EINVAL ;
}
2017-12-04 16:25:06 -05:00
2018-07-18 09:20:34 -04:00
fwnode_graph_parse_endpoint ( fwnode , & vep - > base ) ;
2017-12-04 16:25:06 -05:00
return 0 ;
}
int v4l2_fwnode_endpoint_parse ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_endpoint * vep )
{
int ret ;
ret = __v4l2_fwnode_endpoint_parse ( fwnode , vep ) ;
pr_debug ( " ===== end V4L2 endpoint properties \n " ) ;
return ret ;
2016-08-12 08:05:51 -03:00
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_endpoint_parse ) ;
void v4l2_fwnode_endpoint_free ( struct v4l2_fwnode_endpoint * vep )
{
if ( IS_ERR_OR_NULL ( vep ) )
return ;
kfree ( vep - > link_frequencies ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_endpoint_free ) ;
2018-10-04 17:10:46 -04:00
int v4l2_fwnode_endpoint_alloc_parse ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_endpoint * vep )
2016-08-12 08:05:51 -03:00
{
int rval ;
2017-12-04 16:25:06 -05:00
rval = __v4l2_fwnode_endpoint_parse ( fwnode , vep ) ;
2016-08-12 08:05:51 -03:00
if ( rval < 0 )
2018-06-02 12:19:35 -04:00
return rval ;
2016-08-12 08:05:51 -03:00
rval = fwnode_property_read_u64_array ( fwnode , " link-frequencies " ,
NULL , 0 ) ;
2017-06-20 09:14:43 -04:00
if ( rval > 0 ) {
2017-12-04 16:25:06 -05:00
unsigned int i ;
2017-06-20 09:14:43 -04:00
vep - > link_frequencies =
kmalloc_array ( rval , sizeof ( * vep - > link_frequencies ) ,
GFP_KERNEL ) ;
2018-06-02 12:19:35 -04:00
if ( ! vep - > link_frequencies )
return - ENOMEM ;
2016-08-12 08:05:51 -03:00
2017-06-20 09:14:43 -04:00
vep - > nr_of_link_frequencies = rval ;
2016-08-12 08:05:51 -03:00
2018-10-04 17:10:46 -04:00
rval = fwnode_property_read_u64_array ( fwnode ,
" link-frequencies " ,
vep - > link_frequencies ,
vep - > nr_of_link_frequencies ) ;
2018-06-02 12:19:35 -04:00
if ( rval < 0 ) {
v4l2_fwnode_endpoint_free ( vep ) ;
return rval ;
}
2017-12-04 16:25:06 -05:00
for ( i = 0 ; i < vep - > nr_of_link_frequencies ; i + + )
pr_info ( " link-frequencies %u value %llu \n " , i ,
vep - > link_frequencies [ i ] ) ;
2017-06-20 09:14:43 -04:00
}
2016-08-12 08:05:51 -03:00
2017-12-04 16:25:06 -05:00
pr_debug ( " ===== end V4L2 endpoint properties \n " ) ;
2018-06-02 12:19:35 -04:00
return 0 ;
2016-08-12 08:05:51 -03:00
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_endpoint_alloc_parse ) ;
int v4l2_fwnode_parse_link ( struct fwnode_handle * __fwnode ,
struct v4l2_fwnode_link * link )
{
const char * port_prop = is_of_node ( __fwnode ) ? " reg " : " port " ;
struct fwnode_handle * fwnode ;
memset ( link , 0 , sizeof ( * link ) ) ;
fwnode = fwnode_get_parent ( __fwnode ) ;
fwnode_property_read_u32 ( fwnode , port_prop , & link - > local_port ) ;
fwnode = fwnode_get_next_parent ( fwnode ) ;
2018-12-06 14:35:19 -05:00
if ( is_of_node ( fwnode ) & & of_node_name_eq ( to_of_node ( fwnode ) , " ports " ) )
2016-08-12 08:05:51 -03:00
fwnode = fwnode_get_next_parent ( fwnode ) ;
link - > local_node = fwnode ;
fwnode = fwnode_graph_get_remote_endpoint ( __fwnode ) ;
if ( ! fwnode ) {
fwnode_handle_put ( fwnode ) ;
return - ENOLINK ;
}
fwnode = fwnode_get_parent ( fwnode ) ;
fwnode_property_read_u32 ( fwnode , port_prop , & link - > remote_port ) ;
fwnode = fwnode_get_next_parent ( fwnode ) ;
2018-12-06 14:35:19 -05:00
if ( is_of_node ( fwnode ) & & of_node_name_eq ( to_of_node ( fwnode ) , " ports " ) )
2016-08-12 08:05:51 -03:00
fwnode = fwnode_get_next_parent ( fwnode ) ;
link - > remote_node = fwnode ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_parse_link ) ;
void v4l2_fwnode_put_link ( struct v4l2_fwnode_link * link )
{
fwnode_handle_put ( link - > local_node ) ;
fwnode_handle_put ( link - > remote_node ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_put_link ) ;
2018-10-04 17:10:46 -04:00
static int
v4l2_async_notifier_fwnode_parse_endpoint ( struct device * dev ,
2018-10-04 17:41:16 -04:00
struct v4l2_async_notifier * notifier ,
struct fwnode_handle * endpoint ,
unsigned int asd_struct_size ,
parse_endpoint_func parse_endpoint )
2017-08-17 11:28:21 -04:00
{
2018-06-02 12:19:35 -04:00
struct v4l2_fwnode_endpoint vep = { . bus_type = 0 } ;
2017-08-17 11:28:21 -04:00
struct v4l2_async_subdev * asd ;
2018-06-02 12:19:35 -04:00
int ret ;
2017-08-17 11:28:21 -04:00
asd = kzalloc ( asd_struct_size , GFP_KERNEL ) ;
if ( ! asd )
return - ENOMEM ;
asd - > match_type = V4L2_ASYNC_MATCH_FWNODE ;
2017-09-27 10:12:00 -04:00
asd - > match . fwnode =
2017-08-17 11:28:21 -04:00
fwnode_graph_get_remote_port_parent ( endpoint ) ;
2017-09-27 10:12:00 -04:00
if ( ! asd - > match . fwnode ) {
2018-11-23 07:50:59 -05:00
dev_dbg ( dev , " no remote endpoint found \n " ) ;
2018-09-29 15:54:04 -04:00
ret = - ENOTCONN ;
2017-08-17 11:28:21 -04:00
goto out_err ;
}
2018-06-02 12:19:35 -04:00
ret = v4l2_fwnode_endpoint_alloc_parse ( endpoint , & vep ) ;
if ( ret ) {
2017-08-17 11:28:21 -04:00
dev_warn ( dev , " unable to parse V4L2 fwnode endpoint (%d) \n " ,
ret ) ;
goto out_err ;
}
2018-06-02 12:19:35 -04:00
ret = parse_endpoint ? parse_endpoint ( dev , & vep , asd ) : 0 ;
2017-08-17 11:28:21 -04:00
if ( ret = = - ENOTCONN )
2018-06-02 12:19:35 -04:00
dev_dbg ( dev , " ignoring port@%u/endpoint@%u \n " , vep . base . port ,
vep . base . id ) ;
2017-08-17 11:28:21 -04:00
else if ( ret < 0 )
dev_warn ( dev ,
" driver could not parse port@%u/endpoint@%u (%d) \n " ,
2018-06-02 12:19:35 -04:00
vep . base . port , vep . base . id , ret ) ;
v4l2_fwnode_endpoint_free ( & vep ) ;
2017-08-17 11:28:21 -04:00
if ( ret < 0 )
goto out_err ;
2018-09-29 15:54:08 -04:00
ret = v4l2_async_notifier_add_subdev ( notifier , asd ) ;
if ( ret < 0 ) {
/* not an error if asd already exists */
if ( ret = = - EEXIST )
ret = 0 ;
goto out_err ;
}
2017-08-17 11:28:21 -04:00
return 0 ;
out_err :
2017-09-27 10:12:00 -04:00
fwnode_handle_put ( asd - > match . fwnode ) ;
2017-08-17 11:28:21 -04:00
kfree ( asd ) ;
return ret = = - ENOTCONN ? 0 : ret ;
}
2018-10-04 17:10:46 -04:00
static int
2018-10-04 17:41:16 -04:00
__v4l2_async_notifier_parse_fwnode_ep ( struct device * dev ,
struct v4l2_async_notifier * notifier ,
size_t asd_struct_size ,
unsigned int port ,
bool has_port ,
parse_endpoint_func parse_endpoint )
2017-08-17 11:28:21 -04:00
{
struct fwnode_handle * fwnode ;
2018-09-29 15:54:08 -04:00
int ret = 0 ;
2017-08-17 11:28:21 -04:00
if ( WARN_ON ( asd_struct_size < sizeof ( struct v4l2_async_subdev ) ) )
return - EINVAL ;
2017-12-21 07:11:19 -05:00
fwnode_graph_for_each_endpoint ( dev_fwnode ( dev ) , fwnode ) {
2017-08-17 11:28:21 -04:00
struct fwnode_handle * dev_fwnode ;
bool is_available ;
dev_fwnode = fwnode_graph_get_port_parent ( fwnode ) ;
is_available = fwnode_device_is_available ( dev_fwnode ) ;
fwnode_handle_put ( dev_fwnode ) ;
2017-10-31 14:18:03 -04:00
if ( ! is_available )
2017-08-17 11:28:21 -04:00
continue ;
if ( has_port ) {
struct fwnode_endpoint ep ;
ret = fwnode_graph_parse_endpoint ( fwnode , & ep ) ;
if ( ret )
break ;
if ( ep . port ! = port )
continue ;
}
2018-10-04 17:10:46 -04:00
ret = v4l2_async_notifier_fwnode_parse_endpoint ( dev ,
notifier ,
fwnode ,
asd_struct_size ,
parse_endpoint ) ;
2017-08-17 11:28:21 -04:00
if ( ret < 0 )
break ;
}
fwnode_handle_put ( fwnode ) ;
return ret ;
}
2018-10-04 17:10:46 -04:00
int
v4l2_async_notifier_parse_fwnode_endpoints ( struct device * dev ,
2018-10-04 17:41:16 -04:00
struct v4l2_async_notifier * notifier ,
size_t asd_struct_size ,
parse_endpoint_func parse_endpoint )
2017-08-17 11:28:21 -04:00
{
2018-10-04 17:41:16 -04:00
return __v4l2_async_notifier_parse_fwnode_ep ( dev , notifier ,
asd_struct_size , 0 ,
false , parse_endpoint ) ;
2017-08-17 11:28:21 -04:00
}
EXPORT_SYMBOL_GPL ( v4l2_async_notifier_parse_fwnode_endpoints ) ;
2018-10-04 17:10:46 -04:00
int
v4l2_async_notifier_parse_fwnode_endpoints_by_port ( struct device * dev ,
2018-10-04 17:41:16 -04:00
struct v4l2_async_notifier * notifier ,
size_t asd_struct_size ,
unsigned int port ,
parse_endpoint_func parse_endpoint )
2017-08-17 11:28:21 -04:00
{
2018-10-04 17:41:16 -04:00
return __v4l2_async_notifier_parse_fwnode_ep ( dev , notifier ,
asd_struct_size ,
port , true ,
parse_endpoint ) ;
2017-08-17 11:28:21 -04:00
}
EXPORT_SYMBOL_GPL ( v4l2_async_notifier_parse_fwnode_endpoints_by_port ) ;
2017-06-20 07:47:24 -04:00
/*
* v4l2_fwnode_reference_parse - parse references for async sub - devices
* @ dev : the device node the properties of which are parsed for references
* @ notifier : the async notifier where the async subdevs will be added
* @ prop : the name of the property
*
* Return : 0 on success
* - ENOENT if no entries were found
* - ENOMEM if memory allocation failed
* - EINVAL if property parsing failed
*/
2018-10-04 17:10:46 -04:00
static int v4l2_fwnode_reference_parse ( struct device * dev ,
struct v4l2_async_notifier * notifier ,
const char * prop )
2017-06-20 07:47:24 -04:00
{
struct fwnode_reference_args args ;
unsigned int index ;
int ret ;
for ( index = 0 ;
2018-10-04 17:10:46 -04:00
! ( ret = fwnode_property_get_reference_args ( dev_fwnode ( dev ) ,
prop , NULL , 0 ,
index , & args ) ) ;
2017-06-20 07:47:24 -04:00
index + + )
fwnode_handle_put ( args . fwnode ) ;
if ( ! index )
return - ENOENT ;
/*
* Note that right now both - ENODATA and - ENOENT may signal
* out - of - bounds access . Return the error in cases other than that .
*/
if ( ret ! = - ENOENT & & ret ! = - ENODATA )
return ret ;
2018-10-04 17:10:46 -04:00
for ( index = 0 ;
! fwnode_property_get_reference_args ( dev_fwnode ( dev ) , prop , NULL ,
0 , index , & args ) ;
2017-06-20 07:47:24 -04:00
index + + ) {
struct v4l2_async_subdev * asd ;
2018-10-04 17:10:46 -04:00
asd = v4l2_async_notifier_add_fwnode_subdev ( notifier ,
args . fwnode ,
sizeof ( * asd ) ) ;
2018-09-29 15:54:08 -04:00
if ( IS_ERR ( asd ) ) {
ret = PTR_ERR ( asd ) ;
/* not an error if asd already exists */
if ( ret = = - EEXIST ) {
fwnode_handle_put ( args . fwnode ) ;
continue ;
}
2017-06-20 07:47:24 -04:00
goto error ;
}
}
return 0 ;
error :
fwnode_handle_put ( args . fwnode ) ;
return ret ;
}
2017-06-22 08:03:28 -04:00
/*
* v4l2_fwnode_reference_get_int_prop - parse a reference with integer
* arguments
* @ fwnode : fwnode to read @ prop from
* @ notifier : notifier for @ dev
* @ prop : the name of the property
* @ index : the index of the reference to get
* @ props : the array of integer property names
* @ nprops : the number of integer property names in @ nprops
*
* First find an fwnode referred to by the reference at @ index in @ prop .
*
* Then under that fwnode , @ nprops times , for each property in @ props ,
* iteratively follow child nodes starting from fwnode such that they have the
* property in @ props array at the index of the child node distance from the
* root node and the value of that property matching with the integer argument
* of the reference , at the same index .
*
2019-02-18 14:29:04 -05:00
* The child fwnode reached at the end of the iteration is then returned to the
2017-06-22 08:03:28 -04:00
* caller .
*
* The core reason for this is that you cannot refer to just any node in ACPI .
* So to refer to an endpoint ( easy in DT ) you need to refer to a device , then
* provide a list of ( property name , property value ) tuples where each tuple
* uniquely identifies a child node . The first tuple identifies a child directly
* underneath the device fwnode , the next tuple identifies a child node
* underneath the fwnode identified by the previous tuple , etc . until you
* reached the fwnode you need .
*
2019-03-19 04:07:32 -04:00
* THIS EXAMPLE EXISTS MERELY TO DOCUMENT THIS FUNCTION . DO NOT USE IT AS A
* REFERENCE IN HOW ACPI TABLES SHOULD BE WRITTEN ! ! See documentation under
* Documentation / acpi / dsd instead and especially graph . txt ,
* data - node - references . txt and leds . txt .
2017-06-22 08:03:28 -04:00
*
* Scope ( \ _SB . PCI0 . I2C2 )
* {
* Device ( CAM0 )
* {
* Name ( _DSD , Package ( ) {
* ToUUID ( " daffd814-6eba-4d8c-8a91-bc9bbf4aa301 " ) ,
* Package ( ) {
* Package ( ) {
* " compatible " ,
* Package ( ) { " nokia,smia " }
* } ,
* } ,
* ToUUID ( " dbb8e3e6-5886-4ba6-8795-1319f52a966b " ) ,
* Package ( ) {
* Package ( ) { " port0 " , " PRT0 " } ,
* }
* } )
* Name ( PRT0 , Package ( ) {
* ToUUID ( " daffd814-6eba-4d8c-8a91-bc9bbf4aa301 " ) ,
* Package ( ) {
* Package ( ) { " port " , 0 } ,
* } ,
* ToUUID ( " dbb8e3e6-5886-4ba6-8795-1319f52a966b " ) ,
* Package ( ) {
* Package ( ) { " endpoint0 " , " EP00 " } ,
* }
* } )
* Name ( EP00 , Package ( ) {
* ToUUID ( " daffd814-6eba-4d8c-8a91-bc9bbf4aa301 " ) ,
* Package ( ) {
* Package ( ) { " endpoint " , 0 } ,
* Package ( ) {
* " remote-endpoint " ,
* Package ( ) {
* \ _SB . PCI0 . ISP , 4 , 0
* }
* } ,
* }
* } )
* }
* }
*
* Scope ( \ _SB . PCI0 )
* {
* Device ( ISP )
* {
* Name ( _DSD , Package ( ) {
* ToUUID ( " dbb8e3e6-5886-4ba6-8795-1319f52a966b " ) ,
* Package ( ) {
* Package ( ) { " port4 " , " PRT4 " } ,
* }
* } )
*
* Name ( PRT4 , Package ( ) {
* ToUUID ( " daffd814-6eba-4d8c-8a91-bc9bbf4aa301 " ) ,
* Package ( ) {
* Package ( ) { " port " , 4 } ,
* } ,
* ToUUID ( " dbb8e3e6-5886-4ba6-8795-1319f52a966b " ) ,
* Package ( ) {
* Package ( ) { " endpoint0 " , " EP40 " } ,
* }
* } )
*
* Name ( EP40 , Package ( ) {
* ToUUID ( " daffd814-6eba-4d8c-8a91-bc9bbf4aa301 " ) ,
* Package ( ) {
* Package ( ) { " endpoint " , 0 } ,
* Package ( ) {
* " remote-endpoint " ,
* Package ( ) {
* \ _SB . PCI0 . I2C2 . CAM0 ,
* 0 , 0
* }
* } ,
* }
* } )
* }
* }
*
* From the EP40 node under ISP device , you could parse the graph remote
* endpoint using v4l2_fwnode_reference_get_int_prop with these arguments :
*
* @ fwnode : fwnode referring to EP40 under ISP .
* @ prop : " remote-endpoint "
* @ index : 0
* @ props : " port " , " endpoint "
* @ nprops : 2
*
* And you ' d get back fwnode referring to EP00 under CAM0 .
*
* The same works the other way around : if you use EP00 under CAM0 as the
* fwnode , you ' ll get fwnode referring to EP40 under ISP .
*
* The same example in DT syntax would look like this :
*
* cam : cam0 {
* compatible = " nokia,smia " ;
*
* port {
* port = < 0 > ;
* endpoint {
* endpoint = < 0 > ;
* remote - endpoint = < & isp 4 0 > ;
* } ;
* } ;
* } ;
*
* isp : isp {
* ports {
* port @ 4 {
* port = < 4 > ;
* endpoint {
* endpoint = < 0 > ;
* remote - endpoint = < & cam 0 0 > ;
* } ;
* } ;
* } ;
* } ;
*
* Return : 0 on success
* - ENOENT if no entries ( or the property itself ) were found
* - EINVAL if property parsing otherwise failed
* - ENOMEM if memory allocation failed
*/
2018-10-04 17:10:46 -04:00
static struct fwnode_handle *
v4l2_fwnode_reference_get_int_prop ( struct fwnode_handle * fwnode ,
const char * prop ,
unsigned int index ,
const char * const * props ,
unsigned int nprops )
2017-06-22 08:03:28 -04:00
{
struct fwnode_reference_args fwnode_args ;
2018-07-17 17:19:11 +03:00
u64 * args = fwnode_args . args ;
2017-06-22 08:03:28 -04:00
struct fwnode_handle * child ;
int ret ;
/*
* Obtain remote fwnode as well as the integer arguments .
*
* Note that right now both - ENODATA and - ENOENT may signal
* out - of - bounds access . Return - ENOENT in that case .
*/
ret = fwnode_property_get_reference_args ( fwnode , prop , NULL , nprops ,
index , & fwnode_args ) ;
if ( ret )
return ERR_PTR ( ret = = - ENODATA ? - ENOENT : ret ) ;
/*
* Find a node in the tree under the referred fwnode corresponding to
* the integer arguments .
*/
fwnode = fwnode_args . fwnode ;
while ( nprops - - ) {
u32 val ;
/* Loop over all child nodes under fwnode. */
fwnode_for_each_child_node ( fwnode , child ) {
if ( fwnode_property_read_u32 ( child , * props , & val ) )
continue ;
/* Found property, see if its value matches. */
if ( val = = * args )
break ;
}
fwnode_handle_put ( fwnode ) ;
/* No property found; return an error here. */
if ( ! child ) {
fwnode = ERR_PTR ( - ENOENT ) ;
break ;
}
props + + ;
args + + ;
fwnode = child ;
}
return fwnode ;
}
2018-10-04 17:55:06 -04:00
struct v4l2_fwnode_int_props {
const char * name ;
const char * const * props ;
unsigned int nprops ;
} ;
2017-06-22 08:03:28 -04:00
/*
* v4l2_fwnode_reference_parse_int_props - parse references for async
* sub - devices
* @ dev : struct device pointer
* @ notifier : notifier for @ dev
* @ prop : the name of the property
* @ props : the array of integer property names
* @ nprops : the number of integer properties
*
* Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
* property @ prop with integer arguments with child nodes matching in properties
* @ props . Then , set up V4L2 async sub - devices for those fwnodes in the notifier
* accordingly .
*
* While it is technically possible to use this function on DT , it is only
* meaningful on ACPI . On Device tree you can refer to any node in the tree but
* on ACPI the references are limited to devices .
*
* Return : 0 on success
* - ENOENT if no entries ( or the property itself ) were found
* - EINVAL if property parsing otherwisefailed
* - ENOMEM if memory allocation failed
*/
2018-10-04 17:10:46 -04:00
static int
v4l2_fwnode_reference_parse_int_props ( struct device * dev ,
struct v4l2_async_notifier * notifier ,
2018-10-04 17:55:06 -04:00
const struct v4l2_fwnode_int_props * p )
2017-06-22 08:03:28 -04:00
{
struct fwnode_handle * fwnode ;
unsigned int index ;
int ret ;
2018-10-04 17:55:06 -04:00
const char * prop = p - > name ;
const char * const * props = p - > props ;
unsigned int nprops = p - > nprops ;
2017-06-22 08:03:28 -04:00
2018-04-04 07:21:04 -04:00
index = 0 ;
do {
fwnode = v4l2_fwnode_reference_get_int_prop ( dev_fwnode ( dev ) ,
prop , index ,
props , nprops ) ;
if ( IS_ERR ( fwnode ) ) {
/*
* Note that right now both - ENODATA and - ENOENT may
* signal out - of - bounds access . Return the error in
* cases other than that .
*/
if ( PTR_ERR ( fwnode ) ! = - ENOENT & &
PTR_ERR ( fwnode ) ! = - ENODATA )
return PTR_ERR ( fwnode ) ;
break ;
}
2017-06-22 08:03:28 -04:00
fwnode_handle_put ( fwnode ) ;
2018-04-04 07:21:04 -04:00
index + + ;
} while ( 1 ) ;
2017-06-22 08:03:28 -04:00
2018-10-04 17:10:46 -04:00
for ( index = 0 ;
! IS_ERR ( ( fwnode = v4l2_fwnode_reference_get_int_prop ( dev_fwnode ( dev ) ,
prop , index ,
props ,
nprops ) ) ) ;
index + + ) {
2017-06-22 08:03:28 -04:00
struct v4l2_async_subdev * asd ;
2018-09-29 15:54:08 -04:00
asd = v4l2_async_notifier_add_fwnode_subdev ( notifier , fwnode ,
sizeof ( * asd ) ) ;
if ( IS_ERR ( asd ) ) {
ret = PTR_ERR ( asd ) ;
/* not an error if asd already exists */
if ( ret = = - EEXIST ) {
fwnode_handle_put ( fwnode ) ;
continue ;
}
2017-06-22 08:03:28 -04:00
goto error ;
}
}
return PTR_ERR ( fwnode ) = = - ENOENT ? 0 : PTR_ERR ( fwnode ) ;
error :
fwnode_handle_put ( fwnode ) ;
return ret ;
}
2018-10-04 17:10:46 -04:00
int v4l2_async_notifier_parse_fwnode_sensor_common ( struct device * dev ,
struct v4l2_async_notifier * notifier )
2017-09-06 08:35:42 -04:00
{
static const char * const led_props [ ] = { " led " } ;
2018-10-04 17:55:06 -04:00
static const struct v4l2_fwnode_int_props props [ ] = {
2017-09-06 08:35:42 -04:00
{ " flash-leds " , led_props , ARRAY_SIZE ( led_props ) } ,
{ " lens-focus " , NULL , 0 } ,
} ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( props ) ; i + + ) {
int ret ;
if ( props [ i ] . props & & is_acpi_node ( dev_fwnode ( dev ) ) )
2018-10-04 17:10:46 -04:00
ret = v4l2_fwnode_reference_parse_int_props ( dev ,
notifier ,
2018-10-04 17:55:06 -04:00
& props [ i ] ) ;
2017-09-06 08:35:42 -04:00
else
2018-10-04 17:10:46 -04:00
ret = v4l2_fwnode_reference_parse ( dev , notifier ,
props [ i ] . name ) ;
2017-09-06 08:35:42 -04:00
if ( ret & & ret ! = - ENOENT ) {
dev_warn ( dev , " parsing property \" %s \" failed (%d) \n " ,
props [ i ] . name , ret ) ;
return ret ;
}
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_async_notifier_parse_fwnode_sensor_common ) ;
2017-09-24 18:47:44 -04:00
int v4l2_async_register_subdev_sensor_common ( struct v4l2_subdev * sd )
{
struct v4l2_async_notifier * notifier ;
int ret ;
if ( WARN_ON ( ! sd - > dev ) )
return - ENODEV ;
notifier = kzalloc ( sizeof ( * notifier ) , GFP_KERNEL ) ;
if ( ! notifier )
return - ENOMEM ;
2018-09-29 15:54:08 -04:00
v4l2_async_notifier_init ( notifier ) ;
2017-09-24 18:47:44 -04:00
ret = v4l2_async_notifier_parse_fwnode_sensor_common ( sd - > dev ,
notifier ) ;
if ( ret < 0 )
goto out_cleanup ;
ret = v4l2_async_subdev_notifier_register ( sd , notifier ) ;
if ( ret < 0 )
goto out_cleanup ;
ret = v4l2_async_register_subdev ( sd ) ;
if ( ret < 0 )
goto out_unregister ;
sd - > subdev_notifier = notifier ;
return 0 ;
out_unregister :
v4l2_async_notifier_unregister ( notifier ) ;
out_cleanup :
v4l2_async_notifier_cleanup ( notifier ) ;
kfree ( notifier ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( v4l2_async_register_subdev_sensor_common ) ;
2018-10-04 17:10:46 -04:00
int v4l2_async_register_fwnode_subdev ( struct v4l2_subdev * sd ,
2018-10-04 17:41:16 -04:00
size_t asd_struct_size ,
unsigned int * ports ,
unsigned int num_ports ,
parse_endpoint_func parse_endpoint )
2018-09-29 15:54:09 -04:00
{
struct v4l2_async_notifier * notifier ;
struct device * dev = sd - > dev ;
struct fwnode_handle * fwnode ;
int ret ;
if ( WARN_ON ( ! dev ) )
return - ENODEV ;
fwnode = dev_fwnode ( dev ) ;
if ( ! fwnode_device_is_available ( fwnode ) )
return - ENODEV ;
notifier = kzalloc ( sizeof ( * notifier ) , GFP_KERNEL ) ;
if ( ! notifier )
return - ENOMEM ;
v4l2_async_notifier_init ( notifier ) ;
if ( ! ports ) {
2018-10-04 17:10:46 -04:00
ret = v4l2_async_notifier_parse_fwnode_endpoints ( dev , notifier ,
asd_struct_size ,
parse_endpoint ) ;
2018-09-29 15:54:09 -04:00
if ( ret < 0 )
goto out_cleanup ;
} else {
unsigned int i ;
for ( i = 0 ; i < num_ports ; i + + ) {
2018-10-04 17:10:46 -04:00
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port ( dev , notifier , asd_struct_size , ports [ i ] , parse_endpoint ) ;
2018-09-29 15:54:09 -04:00
if ( ret < 0 )
goto out_cleanup ;
}
}
ret = v4l2_async_subdev_notifier_register ( sd , notifier ) ;
if ( ret < 0 )
goto out_cleanup ;
ret = v4l2_async_register_subdev ( sd ) ;
if ( ret < 0 )
goto out_unregister ;
sd - > subdev_notifier = notifier ;
return 0 ;
out_unregister :
v4l2_async_notifier_unregister ( notifier ) ;
out_cleanup :
v4l2_async_notifier_cleanup ( notifier ) ;
kfree ( notifier ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( v4l2_async_register_fwnode_subdev ) ;
2016-08-12 08:05:51 -03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Sakari Ailus <sakari.ailus@linux.intel.com> " ) ;
MODULE_AUTHOR ( " Sylwester Nawrocki <s.nawrocki@samsung.com> " ) ;
MODULE_AUTHOR ( " Guennadi Liakhovetski <g.liakhovetski@gmx.de> " ) ;