2019-05-28 10:10:09 -07:00
// SPDX-License-Identifier: GPL-2.0-only
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 >
*/
# 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
2023-01-27 21:37:25 +01:00
# include "v4l2-subdev-priv.h"
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 " ,
2022-04-22 10:47:15 +02:00
} , {
V4L2_FWNODE_BUS_TYPE_DPI ,
V4L2_MBUS_DPI ,
" DPI " ,
2018-07-17 12:17:02 -04:00
}
} ;
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 ) ;
2020-09-17 18:08:47 +02:00
return conv ? conv - > mbus_type : V4L2_MBUS_INVALID ;
2018-07-17 12:17:02 -04:00
}
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
{
2022-01-03 17:24:10 +01:00
struct v4l2_mbus_config_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 ;
2022-01-03 17:24:10 +01:00
u32 array [ 1 + V4L2_MBUS_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 ,
2022-01-03 17:24:10 +01:00
V4L2_MBUS_CSI2_MAX_DATA_LANES ) ;
2018-07-18 09:33:45 -04:00
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
}
2019-07-23 16:21:28 -04:00
rval = fwnode_property_count_u32 ( fwnode , " data-lanes " ) ;
2016-08-12 08:05:51 -03:00
if ( rval > 0 ) {
2018-07-18 09:33:45 -04:00
num_data_lanes =
2022-01-03 17:24:10 +01:00
min_t ( int , V4L2_MBUS_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
2019-07-23 16:21:28 -04:00
rval = fwnode_property_count_u32 ( fwnode , " lane-polarities " ) ;
2018-07-18 09:33:45 -04:00
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 ;
}
2019-05-03 06:39:41 -04:00
if ( have_clk_lane & & lanes_used & BIT ( clock_lane ) & &
! use_default_lane_mapping ) {
pr_warn ( " duplicated lane %u in clock-lanes, using defaults \n " ,
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 " ) ;
}
2016-08-12 08:05:51 -03:00
2018-07-23 09:09:28 -04:00
if ( bus_type = = V4L2_MBUS_CSI2_DPHY | |
2022-01-05 21:15:58 +01:00
bus_type = = V4L2_MBUS_CSI2_CPHY | |
lanes_used | | have_clk_lane | | flags ) {
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
{
2022-01-03 17:24:10 +01:00
struct v4l2_mbus_config_parallel * bus = & vep - > bus . parallel ;
2016-08-12 08:05:51 -03:00
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 |
2022-01-14 11:57:55 +01:00
V4L2_MBUS_PCLK_SAMPLE_FALLING |
V4L2_MBUS_PCLK_SAMPLE_DUALEDGE ) ;
switch ( v ) {
case 0 :
flags | = V4L2_MBUS_PCLK_SAMPLE_FALLING ;
pr_debug ( " pclk-sample low \n " ) ;
break ;
case 1 :
flags | = V4L2_MBUS_PCLK_SAMPLE_RISING ;
pr_debug ( " pclk-sample high \n " ) ;
break ;
case 2 :
flags | = V4L2_MBUS_PCLK_SAMPLE_DUALEDGE ;
pr_debug ( " pclk-sample dual edge \n " ) ;
break ;
default :
pr_warn ( " invalid argument for pclk-sample " ) ;
break ;
}
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 , " 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
{
2022-01-03 17:24:10 +01:00
struct v4l2_mbus_config_mipi_csi1 * bus = & vep - > bus . mipi_csi1 ;
2015-02-25 14:39:11 -05:00
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 ;
2019-10-21 09:29:26 +02:00
pr_debug ( " ===== begin parsing endpoint %pfw \n " , fwnode ) ;
2017-12-04 16:25:06 -05: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 ) ;
2020-09-17 18:08:47 +02:00
if ( mbus_type = = V4L2_MBUS_INVALID ) {
pr_debug ( " unsupported bus type %u \n " , bus_type ) ;
return - EINVAL ;
}
2018-07-17 12:17:02 -04:00
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 ) ;
2019-10-21 09:29:26 +02:00
pr_debug ( " ===== end parsing endpoint %pfw \n " , fwnode ) ;
2017-12-04 16:25:06 -05:00
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 ) ;
2019-09-23 06:09:46 -03:00
vep - > link_frequencies = NULL ;
2016-08-12 08:05:51 -03:00
}
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
2019-07-23 16:21:28 -04:00
rval = fwnode_property_count_u64 ( fwnode , " link-frequencies " ) ;
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 + + )
2020-09-02 14:04:12 +02:00
pr_debug ( " 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
2019-10-21 09:29:26 +02:00
pr_debug ( " ===== end parsing endpoint %pfw \n " , fwnode ) ;
2017-12-04 16:25:06 -05:00
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 ) ;
2020-03-12 11:31:40 +01:00
int v4l2_fwnode_parse_link ( struct fwnode_handle * fwnode ,
2016-08-12 08:05:51 -03:00
struct v4l2_fwnode_link * link )
{
2020-03-12 11:31:40 +01:00
struct fwnode_endpoint fwep ;
2016-08-12 08:05:51 -03:00
memset ( link , 0 , sizeof ( * link ) ) ;
2020-03-12 11:31:40 +01:00
fwnode_graph_parse_endpoint ( fwnode , & fwep ) ;
2020-03-12 11:31:41 +01:00
link - > local_id = fwep . id ;
2020-03-12 11:31:40 +01:00
link - > local_port = fwep . port ;
link - > local_node = fwnode_graph_get_port_parent ( fwnode ) ;
2023-06-14 20:31:05 +02:00
if ( ! link - > local_node )
return - ENOLINK ;
2016-08-12 08:05:51 -03:00
2020-03-12 11:31:40 +01:00
fwnode = fwnode_graph_get_remote_endpoint ( fwnode ) ;
2023-06-14 20:31:05 +02:00
if ( ! fwnode )
goto err_put_local_node ;
2016-08-12 08:05:51 -03:00
2020-03-12 11:31:40 +01:00
fwnode_graph_parse_endpoint ( fwnode , & fwep ) ;
2020-03-12 11:31:41 +01:00
link - > remote_id = fwep . id ;
2020-03-12 11:31:40 +01:00
link - > remote_port = fwep . port ;
link - > remote_node = fwnode_graph_get_port_parent ( fwnode ) ;
2023-06-14 20:31:05 +02:00
if ( ! link - > remote_node )
goto err_put_remote_endpoint ;
2016-08-12 08:05:51 -03:00
return 0 ;
2023-06-14 20:31:05 +02:00
err_put_remote_endpoint :
fwnode_handle_put ( fwnode ) ;
err_put_local_node :
fwnode_handle_put ( link - > local_node ) ;
return - ENOLINK ;
2016-08-12 08:05:51 -03:00
}
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 ) ;
2020-03-12 11:31:43 +01:00
static const struct v4l2_fwnode_connector_conv {
enum v4l2_connector_type type ;
const char * compatible ;
} connectors [ ] = {
{
. type = V4L2_CONN_COMPOSITE ,
. compatible = " composite-video-connector " ,
} , {
. type = V4L2_CONN_SVIDEO ,
. compatible = " svideo-connector " ,
} ,
} ;
static enum v4l2_connector_type
v4l2_fwnode_string_to_connector_type ( const char * con_str )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( connectors ) ; i + + )
if ( ! strcmp ( con_str , connectors [ i ] . compatible ) )
return connectors [ i ] . type ;
return V4L2_CONN_UNKNOWN ;
}
static void
v4l2_fwnode_connector_parse_analog ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_connector * vc )
{
u32 stds ;
int ret ;
ret = fwnode_property_read_u32 ( fwnode , " sdtv-standards " , & stds ) ;
/* The property is optional. */
vc - > connector . analog . sdtv_stds = ret ? V4L2_STD_ALL : stds ;
}
void v4l2_fwnode_connector_free ( struct v4l2_fwnode_connector * connector )
{
struct v4l2_connector_link * link , * tmp ;
if ( IS_ERR_OR_NULL ( connector ) | | connector - > type = = V4L2_CONN_UNKNOWN )
return ;
list_for_each_entry_safe ( link , tmp , & connector - > links , head ) {
v4l2_fwnode_put_link ( & link - > fwnode_link ) ;
list_del ( & link - > head ) ;
kfree ( link ) ;
}
kfree ( connector - > label ) ;
connector - > label = NULL ;
connector - > type = V4L2_CONN_UNKNOWN ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_connector_free ) ;
static enum v4l2_connector_type
v4l2_fwnode_get_connector_type ( struct fwnode_handle * fwnode )
{
const char * type_name ;
int err ;
if ( ! fwnode )
return V4L2_CONN_UNKNOWN ;
/* The connector-type is stored within the compatible string. */
err = fwnode_property_read_string ( fwnode , " compatible " , & type_name ) ;
if ( err )
return V4L2_CONN_UNKNOWN ;
return v4l2_fwnode_string_to_connector_type ( type_name ) ;
}
int v4l2_fwnode_connector_parse ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_connector * connector )
{
struct fwnode_handle * connector_node ;
enum v4l2_connector_type connector_type ;
const char * label ;
int err ;
if ( ! fwnode )
return - EINVAL ;
memset ( connector , 0 , sizeof ( * connector ) ) ;
INIT_LIST_HEAD ( & connector - > links ) ;
connector_node = fwnode_graph_get_port_parent ( fwnode ) ;
connector_type = v4l2_fwnode_get_connector_type ( connector_node ) ;
if ( connector_type = = V4L2_CONN_UNKNOWN ) {
fwnode_handle_put ( connector_node ) ;
connector_node = fwnode_graph_get_remote_port_parent ( fwnode ) ;
connector_type = v4l2_fwnode_get_connector_type ( connector_node ) ;
}
if ( connector_type = = V4L2_CONN_UNKNOWN ) {
pr_err ( " Unknown connector type \n " ) ;
err = - ENOTCONN ;
goto out ;
}
connector - > type = connector_type ;
connector - > name = fwnode_get_name ( connector_node ) ;
err = fwnode_property_read_string ( connector_node , " label " , & label ) ;
connector - > label = err ? NULL : kstrdup_const ( label , GFP_KERNEL ) ;
/* Parse the connector specific properties. */
switch ( connector - > type ) {
case V4L2_CONN_COMPOSITE :
case V4L2_CONN_SVIDEO :
v4l2_fwnode_connector_parse_analog ( connector_node , connector ) ;
break ;
/* Avoid compiler warnings */
case V4L2_CONN_UNKNOWN :
break ;
}
out :
fwnode_handle_put ( connector_node ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_connector_parse ) ;
int v4l2_fwnode_connector_add_link ( struct fwnode_handle * fwnode ,
struct v4l2_fwnode_connector * connector )
{
struct fwnode_handle * connector_ep ;
struct v4l2_connector_link * link ;
int err ;
if ( ! fwnode | | ! connector | | connector - > type = = V4L2_CONN_UNKNOWN )
return - EINVAL ;
connector_ep = fwnode_graph_get_remote_endpoint ( fwnode ) ;
if ( ! connector_ep )
return - ENOTCONN ;
link = kzalloc ( sizeof ( * link ) , GFP_KERNEL ) ;
if ( ! link ) {
err = - ENOMEM ;
goto err ;
}
err = v4l2_fwnode_parse_link ( connector_ep , & link - > fwnode_link ) ;
if ( err )
goto err ;
fwnode_handle_put ( connector_ep ) ;
list_add ( & link - > head , & connector - > links ) ;
connector - > nr_of_links + + ;
return 0 ;
err :
kfree ( link ) ;
fwnode_handle_put ( connector_ep ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_connector_add_link ) ;
2020-05-09 11:04:49 +02:00
int v4l2_fwnode_device_parse ( struct device * dev ,
struct v4l2_fwnode_device_properties * props )
{
struct fwnode_handle * fwnode = dev_fwnode ( dev ) ;
u32 val ;
int ret ;
memset ( props , 0 , sizeof ( * props ) ) ;
props - > orientation = V4L2_FWNODE_PROPERTY_UNSET ;
ret = fwnode_property_read_u32 ( fwnode , " orientation " , & val ) ;
if ( ! ret ) {
switch ( val ) {
case V4L2_FWNODE_ORIENTATION_FRONT :
case V4L2_FWNODE_ORIENTATION_BACK :
case V4L2_FWNODE_ORIENTATION_EXTERNAL :
break ;
default :
dev_warn ( dev , " Unsupported device orientation: %u \n " , val ) ;
return - EINVAL ;
}
props - > orientation = val ;
dev_dbg ( dev , " device orientation: %u \n " , val ) ;
}
props - > rotation = V4L2_FWNODE_PROPERTY_UNSET ;
ret = fwnode_property_read_u32 ( fwnode , " rotation " , & val ) ;
if ( ! ret ) {
if ( val > = 360 ) {
dev_warn ( dev , " Unsupported device rotation: %u \n " , val ) ;
return - EINVAL ;
}
props - > rotation = val ;
dev_dbg ( dev , " device rotation: %u \n " , val ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_fwnode_device_parse ) ;
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 ;
2022-01-29 00:26:07 +02:00
! ( ret = fwnode_property_get_reference_args ( dev_fwnode ( dev ) , prop ,
NULL , 0 , index , & args ) ) ;
2017-06-20 07:47:24 -04:00
index + + ) {
2023-02-16 14:54:53 +01:00
struct v4l2_async_connection * asd ;
2017-06-20 07:47:24 -04:00
2021-03-05 18:13:12 +01:00
asd = v4l2_async_nf_add_fwnode ( notifier , args . fwnode ,
2023-02-16 14:54:53 +01:00
struct v4l2_async_connection ) ;
2019-04-04 19:43:29 -04:00
fwnode_handle_put ( args . fwnode ) ;
2018-09-29 15:54:08 -04:00
if ( IS_ERR ( asd ) ) {
/* not an error if asd already exists */
2019-04-04 19:43:29 -04:00
if ( PTR_ERR ( asd ) = = - EEXIST )
2018-09-29 15:54:08 -04:00
continue ;
2017-06-20 07:47:24 -04:00
2019-04-04 19:43:29 -04:00
return PTR_ERR ( asd ) ;
2017-06-20 07:47:24 -04:00
}
}
2022-01-29 00:26:07 +02:00
/* -ENOENT here means successful parsing */
if ( ret ! = - ENOENT )
return ret ;
/* Return -ENOENT if no references were found */
return index ? 0 : - ENOENT ;
2017-06-20 07:47:24 -04:00
}
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
2020-04-14 18:48:36 +02:00
* Documentation / firmware - guide / acpi / dsd / instead and especially graph . txt ,
2019-03-19 04:07:32 -04:00
* 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 + + ) {
2023-02-16 14:54:53 +01:00
struct v4l2_async_connection * asd ;
2017-06-22 08:03:28 -04:00
2021-03-05 18:13:12 +01:00
asd = v4l2_async_nf_add_fwnode ( notifier , fwnode ,
2023-02-16 14:54:53 +01:00
struct v4l2_async_connection ) ;
2019-04-04 19:43:29 -04:00
fwnode_handle_put ( fwnode ) ;
2018-09-29 15:54:08 -04:00
if ( IS_ERR ( asd ) ) {
ret = PTR_ERR ( asd ) ;
/* not an error if asd already exists */
2019-04-04 19:43:29 -04:00
if ( ret = = - EEXIST )
2018-09-29 15:54:08 -04:00
continue ;
2017-06-22 08:03:28 -04:00
2019-04-04 19:43:29 -04:00
return PTR_ERR ( asd ) ;
2017-06-22 08:03:28 -04:00
}
}
2019-06-06 10:18:42 -04:00
return ! fwnode | | PTR_ERR ( fwnode ) = = - ENOENT ? 0 : PTR_ERR ( fwnode ) ;
2017-06-22 08:03:28 -04:00
}
2021-03-05 16:59:22 +01:00
/**
2021-03-05 18:13:12 +01:00
* v4l2_async_nf_parse_fwnode_sensor - parse common references on
2021-03-05 16:59:22 +01:00
* sensors 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
*
* Parse common sensor properties for remote devices related to the
* sensor and set up async sub - devices for them .
*
* Any notifier populated using this function must be released with a call to
2021-03-05 18:13:12 +01:00
* v4l2_async_nf_release ( ) after it has been unregistered and the async
2021-03-05 16:59:22 +01:00
* sub - devices are no longer in use , even in the case the function returned an
* error .
*
* Return : 0 on success
* - ENOMEM if memory allocation failed
* - EINVAL if property parsing failed
*/
static int
2021-03-05 18:13:12 +01:00
v4l2_async_nf_parse_fwnode_sensor ( 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 ;
}
2021-03-05 17:42:34 +01:00
int v4l2_async_register_subdev_sensor ( struct v4l2_subdev * sd )
2017-09-24 18:47:44 -04:00
{
struct v4l2_async_notifier * notifier ;
int ret ;
if ( WARN_ON ( ! sd - > dev ) )
return - ENODEV ;
notifier = kzalloc ( sizeof ( * notifier ) , GFP_KERNEL ) ;
if ( ! notifier )
return - ENOMEM ;
2023-02-23 16:24:48 +01:00
v4l2_async_subdev_nf_init ( notifier , sd ) ;
2018-09-29 15:54:08 -04:00
2023-01-27 21:37:25 +01:00
ret = v4l2_subdev_get_privacy_led ( sd ) ;
if ( ret < 0 )
goto out_cleanup ;
2021-03-05 18:13:12 +01:00
ret = v4l2_async_nf_parse_fwnode_sensor ( sd - > dev , notifier ) ;
2017-09-24 18:47:44 -04:00
if ( ret < 0 )
goto out_cleanup ;
2023-02-23 16:24:48 +01:00
ret = v4l2_async_nf_register ( notifier ) ;
2017-09-24 18:47:44 -04:00
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 :
2021-03-05 18:13:12 +01:00
v4l2_async_nf_unregister ( notifier ) ;
2017-09-24 18:47:44 -04:00
out_cleanup :
2023-01-27 21:37:25 +01:00
v4l2_subdev_put_privacy_led ( sd ) ;
2021-03-05 18:13:12 +01:00
v4l2_async_nf_cleanup ( notifier ) ;
2017-09-24 18:47:44 -04:00
kfree ( notifier ) ;
return ret ;
}
2021-03-05 17:42:34 +01:00
EXPORT_SYMBOL_GPL ( v4l2_async_register_subdev_sensor ) ;
2017-09-24 18:47:44 -04:00
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> " ) ;