cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

v4l2-fwnode.c (34065B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * V4L2 fwnode binding parsing library
      4 *
      5 * The origins of the V4L2 fwnode library are in V4L2 OF library that
      6 * formerly was located in v4l2-of.c.
      7 *
      8 * Copyright (c) 2016 Intel Corporation.
      9 * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
     10 *
     11 * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
     12 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
     13 *
     14 * Copyright (C) 2012 Renesas Electronics Corp.
     15 * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
     16 */
     17#include <linux/acpi.h>
     18#include <linux/kernel.h>
     19#include <linux/mm.h>
     20#include <linux/module.h>
     21#include <linux/of.h>
     22#include <linux/property.h>
     23#include <linux/slab.h>
     24#include <linux/string.h>
     25#include <linux/types.h>
     26
     27#include <media/v4l2-async.h>
     28#include <media/v4l2-fwnode.h>
     29#include <media/v4l2-subdev.h>
     30
     31static const struct v4l2_fwnode_bus_conv {
     32	enum v4l2_fwnode_bus_type fwnode_bus_type;
     33	enum v4l2_mbus_type mbus_type;
     34	const char *name;
     35} buses[] = {
     36	{
     37		V4L2_FWNODE_BUS_TYPE_GUESS,
     38		V4L2_MBUS_UNKNOWN,
     39		"not specified",
     40	}, {
     41		V4L2_FWNODE_BUS_TYPE_CSI2_CPHY,
     42		V4L2_MBUS_CSI2_CPHY,
     43		"MIPI CSI-2 C-PHY",
     44	}, {
     45		V4L2_FWNODE_BUS_TYPE_CSI1,
     46		V4L2_MBUS_CSI1,
     47		"MIPI CSI-1",
     48	}, {
     49		V4L2_FWNODE_BUS_TYPE_CCP2,
     50		V4L2_MBUS_CCP2,
     51		"compact camera port 2",
     52	}, {
     53		V4L2_FWNODE_BUS_TYPE_CSI2_DPHY,
     54		V4L2_MBUS_CSI2_DPHY,
     55		"MIPI CSI-2 D-PHY",
     56	}, {
     57		V4L2_FWNODE_BUS_TYPE_PARALLEL,
     58		V4L2_MBUS_PARALLEL,
     59		"parallel",
     60	}, {
     61		V4L2_FWNODE_BUS_TYPE_BT656,
     62		V4L2_MBUS_BT656,
     63		"Bt.656",
     64	}, {
     65		V4L2_FWNODE_BUS_TYPE_DPI,
     66		V4L2_MBUS_DPI,
     67		"DPI",
     68	}
     69};
     70
     71static const struct v4l2_fwnode_bus_conv *
     72get_v4l2_fwnode_bus_conv_by_fwnode_bus(enum v4l2_fwnode_bus_type type)
     73{
     74	unsigned int i;
     75
     76	for (i = 0; i < ARRAY_SIZE(buses); i++)
     77		if (buses[i].fwnode_bus_type == type)
     78			return &buses[i];
     79
     80	return NULL;
     81}
     82
     83static enum v4l2_mbus_type
     84v4l2_fwnode_bus_type_to_mbus(enum v4l2_fwnode_bus_type type)
     85{
     86	const struct v4l2_fwnode_bus_conv *conv =
     87		get_v4l2_fwnode_bus_conv_by_fwnode_bus(type);
     88
     89	return conv ? conv->mbus_type : V4L2_MBUS_INVALID;
     90}
     91
     92static const char *
     93v4l2_fwnode_bus_type_to_string(enum v4l2_fwnode_bus_type type)
     94{
     95	const struct v4l2_fwnode_bus_conv *conv =
     96		get_v4l2_fwnode_bus_conv_by_fwnode_bus(type);
     97
     98	return conv ? conv->name : "not found";
     99}
    100
    101static const struct v4l2_fwnode_bus_conv *
    102get_v4l2_fwnode_bus_conv_by_mbus(enum v4l2_mbus_type type)
    103{
    104	unsigned int i;
    105
    106	for (i = 0; i < ARRAY_SIZE(buses); i++)
    107		if (buses[i].mbus_type == type)
    108			return &buses[i];
    109
    110	return NULL;
    111}
    112
    113static const char *
    114v4l2_fwnode_mbus_type_to_string(enum v4l2_mbus_type type)
    115{
    116	const struct v4l2_fwnode_bus_conv *conv =
    117		get_v4l2_fwnode_bus_conv_by_mbus(type);
    118
    119	return conv ? conv->name : "not found";
    120}
    121
    122static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
    123					       struct v4l2_fwnode_endpoint *vep,
    124					       enum v4l2_mbus_type bus_type)
    125{
    126	struct v4l2_mbus_config_mipi_csi2 *bus = &vep->bus.mipi_csi2;
    127	bool have_clk_lane = false, have_data_lanes = false,
    128		have_lane_polarities = false;
    129	unsigned int flags = 0, lanes_used = 0;
    130	u32 array[1 + V4L2_MBUS_CSI2_MAX_DATA_LANES];
    131	u32 clock_lane = 0;
    132	unsigned int num_data_lanes = 0;
    133	bool use_default_lane_mapping = false;
    134	unsigned int i;
    135	u32 v;
    136	int rval;
    137
    138	if (bus_type == V4L2_MBUS_CSI2_DPHY ||
    139	    bus_type == V4L2_MBUS_CSI2_CPHY) {
    140		use_default_lane_mapping = true;
    141
    142		num_data_lanes = min_t(u32, bus->num_data_lanes,
    143				       V4L2_MBUS_CSI2_MAX_DATA_LANES);
    144
    145		clock_lane = bus->clock_lane;
    146		if (clock_lane)
    147			use_default_lane_mapping = false;
    148
    149		for (i = 0; i < num_data_lanes; i++) {
    150			array[i] = bus->data_lanes[i];
    151			if (array[i])
    152				use_default_lane_mapping = false;
    153		}
    154
    155		if (use_default_lane_mapping)
    156			pr_debug("no lane mapping given, using defaults\n");
    157	}
    158
    159	rval = fwnode_property_count_u32(fwnode, "data-lanes");
    160	if (rval > 0) {
    161		num_data_lanes =
    162			min_t(int, V4L2_MBUS_CSI2_MAX_DATA_LANES, rval);
    163
    164		fwnode_property_read_u32_array(fwnode, "data-lanes", array,
    165					       num_data_lanes);
    166
    167		have_data_lanes = true;
    168		if (use_default_lane_mapping) {
    169			pr_debug("data-lanes property exists; disabling default mapping\n");
    170			use_default_lane_mapping = false;
    171		}
    172	}
    173
    174	for (i = 0; i < num_data_lanes; i++) {
    175		if (lanes_used & BIT(array[i])) {
    176			if (have_data_lanes || !use_default_lane_mapping)
    177				pr_warn("duplicated lane %u in data-lanes, using defaults\n",
    178					array[i]);
    179			use_default_lane_mapping = true;
    180		}
    181		lanes_used |= BIT(array[i]);
    182
    183		if (have_data_lanes)
    184			pr_debug("lane %u position %u\n", i, array[i]);
    185	}
    186
    187	rval = fwnode_property_count_u32(fwnode, "lane-polarities");
    188	if (rval > 0) {
    189		if (rval != 1 + num_data_lanes /* clock+data */) {
    190			pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
    191				1 + num_data_lanes, rval);
    192			return -EINVAL;
    193		}
    194
    195		have_lane_polarities = true;
    196	}
    197
    198	if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
    199		clock_lane = v;
    200		pr_debug("clock lane position %u\n", v);
    201		have_clk_lane = true;
    202	}
    203
    204	if (have_clk_lane && lanes_used & BIT(clock_lane) &&
    205	    !use_default_lane_mapping) {
    206		pr_warn("duplicated lane %u in clock-lanes, using defaults\n",
    207			v);
    208		use_default_lane_mapping = true;
    209	}
    210
    211	if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
    212		flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
    213		pr_debug("non-continuous clock\n");
    214	}
    215
    216	if (bus_type == V4L2_MBUS_CSI2_DPHY ||
    217	    bus_type == V4L2_MBUS_CSI2_CPHY ||
    218	    lanes_used || have_clk_lane || flags) {
    219		/* Only D-PHY has a clock lane. */
    220		unsigned int dfl_data_lane_index =
    221			bus_type == V4L2_MBUS_CSI2_DPHY;
    222
    223		bus->flags = flags;
    224		if (bus_type == V4L2_MBUS_UNKNOWN)
    225			vep->bus_type = V4L2_MBUS_CSI2_DPHY;
    226		bus->num_data_lanes = num_data_lanes;
    227
    228		if (use_default_lane_mapping) {
    229			bus->clock_lane = 0;
    230			for (i = 0; i < num_data_lanes; i++)
    231				bus->data_lanes[i] = dfl_data_lane_index + i;
    232		} else {
    233			bus->clock_lane = clock_lane;
    234			for (i = 0; i < num_data_lanes; i++)
    235				bus->data_lanes[i] = array[i];
    236		}
    237
    238		if (have_lane_polarities) {
    239			fwnode_property_read_u32_array(fwnode,
    240						       "lane-polarities", array,
    241						       1 + num_data_lanes);
    242
    243			for (i = 0; i < 1 + num_data_lanes; i++) {
    244				bus->lane_polarities[i] = array[i];
    245				pr_debug("lane %u polarity %sinverted",
    246					 i, array[i] ? "" : "not ");
    247			}
    248		} else {
    249			pr_debug("no lane polarities defined, assuming not inverted\n");
    250		}
    251	}
    252
    253	return 0;
    254}
    255
    256#define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH |	\
    257			     V4L2_MBUS_HSYNC_ACTIVE_LOW |	\
    258			     V4L2_MBUS_VSYNC_ACTIVE_HIGH |	\
    259			     V4L2_MBUS_VSYNC_ACTIVE_LOW |	\
    260			     V4L2_MBUS_FIELD_EVEN_HIGH |	\
    261			     V4L2_MBUS_FIELD_EVEN_LOW)
    262
    263static void
    264v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode,
    265					struct v4l2_fwnode_endpoint *vep,
    266					enum v4l2_mbus_type bus_type)
    267{
    268	struct v4l2_mbus_config_parallel *bus = &vep->bus.parallel;
    269	unsigned int flags = 0;
    270	u32 v;
    271
    272	if (bus_type == V4L2_MBUS_PARALLEL || bus_type == V4L2_MBUS_BT656)
    273		flags = bus->flags;
    274
    275	if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
    276		flags &= ~(V4L2_MBUS_HSYNC_ACTIVE_HIGH |
    277			   V4L2_MBUS_HSYNC_ACTIVE_LOW);
    278		flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
    279			V4L2_MBUS_HSYNC_ACTIVE_LOW;
    280		pr_debug("hsync-active %s\n", v ? "high" : "low");
    281	}
    282
    283	if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
    284		flags &= ~(V4L2_MBUS_VSYNC_ACTIVE_HIGH |
    285			   V4L2_MBUS_VSYNC_ACTIVE_LOW);
    286		flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
    287			V4L2_MBUS_VSYNC_ACTIVE_LOW;
    288		pr_debug("vsync-active %s\n", v ? "high" : "low");
    289	}
    290
    291	if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
    292		flags &= ~(V4L2_MBUS_FIELD_EVEN_HIGH |
    293			   V4L2_MBUS_FIELD_EVEN_LOW);
    294		flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
    295			V4L2_MBUS_FIELD_EVEN_LOW;
    296		pr_debug("field-even-active %s\n", v ? "high" : "low");
    297	}
    298
    299	if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
    300		flags &= ~(V4L2_MBUS_PCLK_SAMPLE_RISING |
    301			   V4L2_MBUS_PCLK_SAMPLE_FALLING);
    302		flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
    303			V4L2_MBUS_PCLK_SAMPLE_FALLING;
    304		pr_debug("pclk-sample %s\n", v ? "high" : "low");
    305	}
    306
    307	if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
    308		flags &= ~(V4L2_MBUS_DATA_ACTIVE_HIGH |
    309			   V4L2_MBUS_DATA_ACTIVE_LOW);
    310		flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
    311			V4L2_MBUS_DATA_ACTIVE_LOW;
    312		pr_debug("data-active %s\n", v ? "high" : "low");
    313	}
    314
    315	if (fwnode_property_present(fwnode, "slave-mode")) {
    316		pr_debug("slave mode\n");
    317		flags &= ~V4L2_MBUS_MASTER;
    318		flags |= V4L2_MBUS_SLAVE;
    319	} else {
    320		flags &= ~V4L2_MBUS_SLAVE;
    321		flags |= V4L2_MBUS_MASTER;
    322	}
    323
    324	if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
    325		bus->bus_width = v;
    326		pr_debug("bus-width %u\n", v);
    327	}
    328
    329	if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
    330		bus->data_shift = v;
    331		pr_debug("data-shift %u\n", v);
    332	}
    333
    334	if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
    335		flags &= ~(V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH |
    336			   V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW);
    337		flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
    338			V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
    339		pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
    340	}
    341
    342	if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
    343		flags &= ~(V4L2_MBUS_DATA_ENABLE_HIGH |
    344			   V4L2_MBUS_DATA_ENABLE_LOW);
    345		flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
    346			V4L2_MBUS_DATA_ENABLE_LOW;
    347		pr_debug("data-enable-active %s\n", v ? "high" : "low");
    348	}
    349
    350	switch (bus_type) {
    351	default:
    352		bus->flags = flags;
    353		if (flags & PARALLEL_MBUS_FLAGS)
    354			vep->bus_type = V4L2_MBUS_PARALLEL;
    355		else
    356			vep->bus_type = V4L2_MBUS_BT656;
    357		break;
    358	case V4L2_MBUS_PARALLEL:
    359		vep->bus_type = V4L2_MBUS_PARALLEL;
    360		bus->flags = flags;
    361		break;
    362	case V4L2_MBUS_BT656:
    363		vep->bus_type = V4L2_MBUS_BT656;
    364		bus->flags = flags & ~PARALLEL_MBUS_FLAGS;
    365		break;
    366	}
    367}
    368
    369static void
    370v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
    371				    struct v4l2_fwnode_endpoint *vep,
    372				    enum v4l2_mbus_type bus_type)
    373{
    374	struct v4l2_mbus_config_mipi_csi1 *bus = &vep->bus.mipi_csi1;
    375	u32 v;
    376
    377	if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
    378		bus->clock_inv = v;
    379		pr_debug("clock-inv %u\n", v);
    380	}
    381
    382	if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
    383		bus->strobe = v;
    384		pr_debug("strobe %u\n", v);
    385	}
    386
    387	if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
    388		bus->data_lane = v;
    389		pr_debug("data-lanes %u\n", v);
    390	}
    391
    392	if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
    393		bus->clock_lane = v;
    394		pr_debug("clock-lanes %u\n", v);
    395	}
    396
    397	if (bus_type == V4L2_MBUS_CCP2)
    398		vep->bus_type = V4L2_MBUS_CCP2;
    399	else
    400		vep->bus_type = V4L2_MBUS_CSI1;
    401}
    402
    403static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
    404					struct v4l2_fwnode_endpoint *vep)
    405{
    406	u32 bus_type = V4L2_FWNODE_BUS_TYPE_GUESS;
    407	enum v4l2_mbus_type mbus_type;
    408	int rval;
    409
    410	pr_debug("===== begin parsing endpoint %pfw\n", fwnode);
    411
    412	fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
    413	pr_debug("fwnode video bus type %s (%u), mbus type %s (%u)\n",
    414		 v4l2_fwnode_bus_type_to_string(bus_type), bus_type,
    415		 v4l2_fwnode_mbus_type_to_string(vep->bus_type),
    416		 vep->bus_type);
    417	mbus_type = v4l2_fwnode_bus_type_to_mbus(bus_type);
    418	if (mbus_type == V4L2_MBUS_INVALID) {
    419		pr_debug("unsupported bus type %u\n", bus_type);
    420		return -EINVAL;
    421	}
    422
    423	if (vep->bus_type != V4L2_MBUS_UNKNOWN) {
    424		if (mbus_type != V4L2_MBUS_UNKNOWN &&
    425		    vep->bus_type != mbus_type) {
    426			pr_debug("expecting bus type %s\n",
    427				 v4l2_fwnode_mbus_type_to_string(vep->bus_type));
    428			return -ENXIO;
    429		}
    430	} else {
    431		vep->bus_type = mbus_type;
    432	}
    433
    434	switch (vep->bus_type) {
    435	case V4L2_MBUS_UNKNOWN:
    436		rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
    437							   V4L2_MBUS_UNKNOWN);
    438		if (rval)
    439			return rval;
    440
    441		if (vep->bus_type == V4L2_MBUS_UNKNOWN)
    442			v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep,
    443								V4L2_MBUS_UNKNOWN);
    444
    445		pr_debug("assuming media bus type %s (%u)\n",
    446			 v4l2_fwnode_mbus_type_to_string(vep->bus_type),
    447			 vep->bus_type);
    448
    449		break;
    450	case V4L2_MBUS_CCP2:
    451	case V4L2_MBUS_CSI1:
    452		v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, vep->bus_type);
    453
    454		break;
    455	case V4L2_MBUS_CSI2_DPHY:
    456	case V4L2_MBUS_CSI2_CPHY:
    457		rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
    458							   vep->bus_type);
    459		if (rval)
    460			return rval;
    461
    462		break;
    463	case V4L2_MBUS_PARALLEL:
    464	case V4L2_MBUS_BT656:
    465		v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep,
    466							vep->bus_type);
    467
    468		break;
    469	default:
    470		pr_warn("unsupported bus type %u\n", mbus_type);
    471		return -EINVAL;
    472	}
    473
    474	fwnode_graph_parse_endpoint(fwnode, &vep->base);
    475
    476	return 0;
    477}
    478
    479int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
    480			       struct v4l2_fwnode_endpoint *vep)
    481{
    482	int ret;
    483
    484	ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
    485
    486	pr_debug("===== end parsing endpoint %pfw\n", fwnode);
    487
    488	return ret;
    489}
    490EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
    491
    492void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
    493{
    494	if (IS_ERR_OR_NULL(vep))
    495		return;
    496
    497	kfree(vep->link_frequencies);
    498	vep->link_frequencies = NULL;
    499}
    500EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
    501
    502int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode,
    503				     struct v4l2_fwnode_endpoint *vep)
    504{
    505	int rval;
    506
    507	rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
    508	if (rval < 0)
    509		return rval;
    510
    511	rval = fwnode_property_count_u64(fwnode, "link-frequencies");
    512	if (rval > 0) {
    513		unsigned int i;
    514
    515		vep->link_frequencies =
    516			kmalloc_array(rval, sizeof(*vep->link_frequencies),
    517				      GFP_KERNEL);
    518		if (!vep->link_frequencies)
    519			return -ENOMEM;
    520
    521		vep->nr_of_link_frequencies = rval;
    522
    523		rval = fwnode_property_read_u64_array(fwnode,
    524						      "link-frequencies",
    525						      vep->link_frequencies,
    526						      vep->nr_of_link_frequencies);
    527		if (rval < 0) {
    528			v4l2_fwnode_endpoint_free(vep);
    529			return rval;
    530		}
    531
    532		for (i = 0; i < vep->nr_of_link_frequencies; i++)
    533			pr_debug("link-frequencies %u value %llu\n", i,
    534				 vep->link_frequencies[i]);
    535	}
    536
    537	pr_debug("===== end parsing endpoint %pfw\n", fwnode);
    538
    539	return 0;
    540}
    541EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
    542
    543int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,
    544			   struct v4l2_fwnode_link *link)
    545{
    546	struct fwnode_endpoint fwep;
    547
    548	memset(link, 0, sizeof(*link));
    549
    550	fwnode_graph_parse_endpoint(fwnode, &fwep);
    551	link->local_id = fwep.id;
    552	link->local_port = fwep.port;
    553	link->local_node = fwnode_graph_get_port_parent(fwnode);
    554
    555	fwnode = fwnode_graph_get_remote_endpoint(fwnode);
    556	if (!fwnode) {
    557		fwnode_handle_put(fwnode);
    558		return -ENOLINK;
    559	}
    560
    561	fwnode_graph_parse_endpoint(fwnode, &fwep);
    562	link->remote_id = fwep.id;
    563	link->remote_port = fwep.port;
    564	link->remote_node = fwnode_graph_get_port_parent(fwnode);
    565
    566	return 0;
    567}
    568EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
    569
    570void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
    571{
    572	fwnode_handle_put(link->local_node);
    573	fwnode_handle_put(link->remote_node);
    574}
    575EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
    576
    577static const struct v4l2_fwnode_connector_conv {
    578	enum v4l2_connector_type type;
    579	const char *compatible;
    580} connectors[] = {
    581	{
    582		.type = V4L2_CONN_COMPOSITE,
    583		.compatible = "composite-video-connector",
    584	}, {
    585		.type = V4L2_CONN_SVIDEO,
    586		.compatible = "svideo-connector",
    587	},
    588};
    589
    590static enum v4l2_connector_type
    591v4l2_fwnode_string_to_connector_type(const char *con_str)
    592{
    593	unsigned int i;
    594
    595	for (i = 0; i < ARRAY_SIZE(connectors); i++)
    596		if (!strcmp(con_str, connectors[i].compatible))
    597			return connectors[i].type;
    598
    599	return V4L2_CONN_UNKNOWN;
    600}
    601
    602static void
    603v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
    604				   struct v4l2_fwnode_connector *vc)
    605{
    606	u32 stds;
    607	int ret;
    608
    609	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
    610
    611	/* The property is optional. */
    612	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
    613}
    614
    615void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
    616{
    617	struct v4l2_connector_link *link, *tmp;
    618
    619	if (IS_ERR_OR_NULL(connector) || connector->type == V4L2_CONN_UNKNOWN)
    620		return;
    621
    622	list_for_each_entry_safe(link, tmp, &connector->links, head) {
    623		v4l2_fwnode_put_link(&link->fwnode_link);
    624		list_del(&link->head);
    625		kfree(link);
    626	}
    627
    628	kfree(connector->label);
    629	connector->label = NULL;
    630	connector->type = V4L2_CONN_UNKNOWN;
    631}
    632EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
    633
    634static enum v4l2_connector_type
    635v4l2_fwnode_get_connector_type(struct fwnode_handle *fwnode)
    636{
    637	const char *type_name;
    638	int err;
    639
    640	if (!fwnode)
    641		return V4L2_CONN_UNKNOWN;
    642
    643	/* The connector-type is stored within the compatible string. */
    644	err = fwnode_property_read_string(fwnode, "compatible", &type_name);
    645	if (err)
    646		return V4L2_CONN_UNKNOWN;
    647
    648	return v4l2_fwnode_string_to_connector_type(type_name);
    649}
    650
    651int v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode,
    652				struct v4l2_fwnode_connector *connector)
    653{
    654	struct fwnode_handle *connector_node;
    655	enum v4l2_connector_type connector_type;
    656	const char *label;
    657	int err;
    658
    659	if (!fwnode)
    660		return -EINVAL;
    661
    662	memset(connector, 0, sizeof(*connector));
    663
    664	INIT_LIST_HEAD(&connector->links);
    665
    666	connector_node = fwnode_graph_get_port_parent(fwnode);
    667	connector_type = v4l2_fwnode_get_connector_type(connector_node);
    668	if (connector_type == V4L2_CONN_UNKNOWN) {
    669		fwnode_handle_put(connector_node);
    670		connector_node = fwnode_graph_get_remote_port_parent(fwnode);
    671		connector_type = v4l2_fwnode_get_connector_type(connector_node);
    672	}
    673
    674	if (connector_type == V4L2_CONN_UNKNOWN) {
    675		pr_err("Unknown connector type\n");
    676		err = -ENOTCONN;
    677		goto out;
    678	}
    679
    680	connector->type = connector_type;
    681	connector->name = fwnode_get_name(connector_node);
    682	err = fwnode_property_read_string(connector_node, "label", &label);
    683	connector->label = err ? NULL : kstrdup_const(label, GFP_KERNEL);
    684
    685	/* Parse the connector specific properties. */
    686	switch (connector->type) {
    687	case V4L2_CONN_COMPOSITE:
    688	case V4L2_CONN_SVIDEO:
    689		v4l2_fwnode_connector_parse_analog(connector_node, connector);
    690		break;
    691	/* Avoid compiler warnings */
    692	case V4L2_CONN_UNKNOWN:
    693		break;
    694	}
    695
    696out:
    697	fwnode_handle_put(connector_node);
    698
    699	return err;
    700}
    701EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse);
    702
    703int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode,
    704				   struct v4l2_fwnode_connector *connector)
    705{
    706	struct fwnode_handle *connector_ep;
    707	struct v4l2_connector_link *link;
    708	int err;
    709
    710	if (!fwnode || !connector || connector->type == V4L2_CONN_UNKNOWN)
    711		return -EINVAL;
    712
    713	connector_ep = fwnode_graph_get_remote_endpoint(fwnode);
    714	if (!connector_ep)
    715		return -ENOTCONN;
    716
    717	link = kzalloc(sizeof(*link), GFP_KERNEL);
    718	if (!link) {
    719		err = -ENOMEM;
    720		goto err;
    721	}
    722
    723	err = v4l2_fwnode_parse_link(connector_ep, &link->fwnode_link);
    724	if (err)
    725		goto err;
    726
    727	fwnode_handle_put(connector_ep);
    728
    729	list_add(&link->head, &connector->links);
    730	connector->nr_of_links++;
    731
    732	return 0;
    733
    734err:
    735	kfree(link);
    736	fwnode_handle_put(connector_ep);
    737
    738	return err;
    739}
    740EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link);
    741
    742int v4l2_fwnode_device_parse(struct device *dev,
    743			     struct v4l2_fwnode_device_properties *props)
    744{
    745	struct fwnode_handle *fwnode = dev_fwnode(dev);
    746	u32 val;
    747	int ret;
    748
    749	memset(props, 0, sizeof(*props));
    750
    751	props->orientation = V4L2_FWNODE_PROPERTY_UNSET;
    752	ret = fwnode_property_read_u32(fwnode, "orientation", &val);
    753	if (!ret) {
    754		switch (val) {
    755		case V4L2_FWNODE_ORIENTATION_FRONT:
    756		case V4L2_FWNODE_ORIENTATION_BACK:
    757		case V4L2_FWNODE_ORIENTATION_EXTERNAL:
    758			break;
    759		default:
    760			dev_warn(dev, "Unsupported device orientation: %u\n", val);
    761			return -EINVAL;
    762		}
    763
    764		props->orientation = val;
    765		dev_dbg(dev, "device orientation: %u\n", val);
    766	}
    767
    768	props->rotation = V4L2_FWNODE_PROPERTY_UNSET;
    769	ret = fwnode_property_read_u32(fwnode, "rotation", &val);
    770	if (!ret) {
    771		if (val >= 360) {
    772			dev_warn(dev, "Unsupported device rotation: %u\n", val);
    773			return -EINVAL;
    774		}
    775
    776		props->rotation = val;
    777		dev_dbg(dev, "device rotation: %u\n", val);
    778	}
    779
    780	return 0;
    781}
    782EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse);
    783
    784static int
    785v4l2_async_nf_fwnode_parse_endpoint(struct device *dev,
    786				    struct v4l2_async_notifier *notifier,
    787				    struct fwnode_handle *endpoint,
    788				    unsigned int asd_struct_size,
    789				    parse_endpoint_func parse_endpoint)
    790{
    791	struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
    792	struct v4l2_async_subdev *asd;
    793	int ret;
    794
    795	asd = kzalloc(asd_struct_size, GFP_KERNEL);
    796	if (!asd)
    797		return -ENOMEM;
    798
    799	asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
    800	asd->match.fwnode =
    801		fwnode_graph_get_remote_port_parent(endpoint);
    802	if (!asd->match.fwnode) {
    803		dev_dbg(dev, "no remote endpoint found\n");
    804		ret = -ENOTCONN;
    805		goto out_err;
    806	}
    807
    808	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
    809	if (ret) {
    810		dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
    811			 ret);
    812		goto out_err;
    813	}
    814
    815	ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
    816	if (ret == -ENOTCONN)
    817		dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
    818			vep.base.id);
    819	else if (ret < 0)
    820		dev_warn(dev,
    821			 "driver could not parse port@%u/endpoint@%u (%d)\n",
    822			 vep.base.port, vep.base.id, ret);
    823	v4l2_fwnode_endpoint_free(&vep);
    824	if (ret < 0)
    825		goto out_err;
    826
    827	ret = __v4l2_async_nf_add_subdev(notifier, asd);
    828	if (ret < 0) {
    829		/* not an error if asd already exists */
    830		if (ret == -EEXIST)
    831			ret = 0;
    832		goto out_err;
    833	}
    834
    835	return 0;
    836
    837out_err:
    838	fwnode_handle_put(asd->match.fwnode);
    839	kfree(asd);
    840
    841	return ret == -ENOTCONN ? 0 : ret;
    842}
    843
    844int
    845v4l2_async_nf_parse_fwnode_endpoints(struct device *dev,
    846				     struct v4l2_async_notifier *notifier,
    847				     size_t asd_struct_size,
    848				     parse_endpoint_func parse_endpoint)
    849{
    850	struct fwnode_handle *fwnode;
    851	int ret = 0;
    852
    853	if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
    854		return -EINVAL;
    855
    856	fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
    857		struct fwnode_handle *dev_fwnode;
    858		bool is_available;
    859
    860		dev_fwnode = fwnode_graph_get_port_parent(fwnode);
    861		is_available = fwnode_device_is_available(dev_fwnode);
    862		fwnode_handle_put(dev_fwnode);
    863		if (!is_available)
    864			continue;
    865
    866
    867		ret = v4l2_async_nf_fwnode_parse_endpoint(dev, notifier,
    868							  fwnode,
    869							  asd_struct_size,
    870							  parse_endpoint);
    871		if (ret < 0)
    872			break;
    873	}
    874
    875	fwnode_handle_put(fwnode);
    876
    877	return ret;
    878}
    879EXPORT_SYMBOL_GPL(v4l2_async_nf_parse_fwnode_endpoints);
    880
    881/*
    882 * v4l2_fwnode_reference_parse - parse references for async sub-devices
    883 * @dev: the device node the properties of which are parsed for references
    884 * @notifier: the async notifier where the async subdevs will be added
    885 * @prop: the name of the property
    886 *
    887 * Return: 0 on success
    888 *	   -ENOENT if no entries were found
    889 *	   -ENOMEM if memory allocation failed
    890 *	   -EINVAL if property parsing failed
    891 */
    892static int v4l2_fwnode_reference_parse(struct device *dev,
    893				       struct v4l2_async_notifier *notifier,
    894				       const char *prop)
    895{
    896	struct fwnode_reference_args args;
    897	unsigned int index;
    898	int ret;
    899
    900	for (index = 0;
    901	     !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), prop,
    902							NULL, 0, index, &args));
    903	     index++) {
    904		struct v4l2_async_subdev *asd;
    905
    906		asd = v4l2_async_nf_add_fwnode(notifier, args.fwnode,
    907					       struct v4l2_async_subdev);
    908		fwnode_handle_put(args.fwnode);
    909		if (IS_ERR(asd)) {
    910			/* not an error if asd already exists */
    911			if (PTR_ERR(asd) == -EEXIST)
    912				continue;
    913
    914			return PTR_ERR(asd);
    915		}
    916	}
    917
    918	/* -ENOENT here means successful parsing */
    919	if (ret != -ENOENT)
    920		return ret;
    921
    922	/* Return -ENOENT if no references were found */
    923	return index ? 0 : -ENOENT;
    924}
    925
    926/*
    927 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
    928 *					arguments
    929 * @fwnode: fwnode to read @prop from
    930 * @notifier: notifier for @dev
    931 * @prop: the name of the property
    932 * @index: the index of the reference to get
    933 * @props: the array of integer property names
    934 * @nprops: the number of integer property names in @nprops
    935 *
    936 * First find an fwnode referred to by the reference at @index in @prop.
    937 *
    938 * Then under that fwnode, @nprops times, for each property in @props,
    939 * iteratively follow child nodes starting from fwnode such that they have the
    940 * property in @props array at the index of the child node distance from the
    941 * root node and the value of that property matching with the integer argument
    942 * of the reference, at the same index.
    943 *
    944 * The child fwnode reached at the end of the iteration is then returned to the
    945 * caller.
    946 *
    947 * The core reason for this is that you cannot refer to just any node in ACPI.
    948 * So to refer to an endpoint (easy in DT) you need to refer to a device, then
    949 * provide a list of (property name, property value) tuples where each tuple
    950 * uniquely identifies a child node. The first tuple identifies a child directly
    951 * underneath the device fwnode, the next tuple identifies a child node
    952 * underneath the fwnode identified by the previous tuple, etc. until you
    953 * reached the fwnode you need.
    954 *
    955 * THIS EXAMPLE EXISTS MERELY TO DOCUMENT THIS FUNCTION. DO NOT USE IT AS A
    956 * REFERENCE IN HOW ACPI TABLES SHOULD BE WRITTEN!! See documentation under
    957 * Documentation/firmware-guide/acpi/dsd/ instead and especially graph.txt,
    958 * data-node-references.txt and leds.txt .
    959 *
    960 *	Scope (\_SB.PCI0.I2C2)
    961 *	{
    962 *		Device (CAM0)
    963 *		{
    964 *			Name (_DSD, Package () {
    965 *				ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
    966 *				Package () {
    967 *					Package () {
    968 *						"compatible",
    969 *						Package () { "nokia,smia" }
    970 *					},
    971 *				},
    972 *				ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
    973 *				Package () {
    974 *					Package () { "port0", "PRT0" },
    975 *				}
    976 *			})
    977 *			Name (PRT0, Package() {
    978 *				ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
    979 *				Package () {
    980 *					Package () { "port", 0 },
    981 *				},
    982 *				ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
    983 *				Package () {
    984 *					Package () { "endpoint0", "EP00" },
    985 *				}
    986 *			})
    987 *			Name (EP00, Package() {
    988 *				ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
    989 *				Package () {
    990 *					Package () { "endpoint", 0 },
    991 *					Package () {
    992 *						"remote-endpoint",
    993 *						Package() {
    994 *							\_SB.PCI0.ISP, 4, 0
    995 *						}
    996 *					},
    997 *				}
    998 *			})
    999 *		}
   1000 *	}
   1001 *
   1002 *	Scope (\_SB.PCI0)
   1003 *	{
   1004 *		Device (ISP)
   1005 *		{
   1006 *			Name (_DSD, Package () {
   1007 *				ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
   1008 *				Package () {
   1009 *					Package () { "port4", "PRT4" },
   1010 *				}
   1011 *			})
   1012 *
   1013 *			Name (PRT4, Package() {
   1014 *				ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
   1015 *				Package () {
   1016 *					Package () { "port", 4 },
   1017 *				},
   1018 *				ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
   1019 *				Package () {
   1020 *					Package () { "endpoint0", "EP40" },
   1021 *				}
   1022 *			})
   1023 *
   1024 *			Name (EP40, Package() {
   1025 *				ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
   1026 *				Package () {
   1027 *					Package () { "endpoint", 0 },
   1028 *					Package () {
   1029 *						"remote-endpoint",
   1030 *						Package () {
   1031 *							\_SB.PCI0.I2C2.CAM0,
   1032 *							0, 0
   1033 *						}
   1034 *					},
   1035 *				}
   1036 *			})
   1037 *		}
   1038 *	}
   1039 *
   1040 * From the EP40 node under ISP device, you could parse the graph remote
   1041 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
   1042 *
   1043 *  @fwnode: fwnode referring to EP40 under ISP.
   1044 *  @prop: "remote-endpoint"
   1045 *  @index: 0
   1046 *  @props: "port", "endpoint"
   1047 *  @nprops: 2
   1048 *
   1049 * And you'd get back fwnode referring to EP00 under CAM0.
   1050 *
   1051 * The same works the other way around: if you use EP00 under CAM0 as the
   1052 * fwnode, you'll get fwnode referring to EP40 under ISP.
   1053 *
   1054 * The same example in DT syntax would look like this:
   1055 *
   1056 * cam: cam0 {
   1057 *	compatible = "nokia,smia";
   1058 *
   1059 *	port {
   1060 *		port = <0>;
   1061 *		endpoint {
   1062 *			endpoint = <0>;
   1063 *			remote-endpoint = <&isp 4 0>;
   1064 *		};
   1065 *	};
   1066 * };
   1067 *
   1068 * isp: isp {
   1069 *	ports {
   1070 *		port@4 {
   1071 *			port = <4>;
   1072 *			endpoint {
   1073 *				endpoint = <0>;
   1074 *				remote-endpoint = <&cam 0 0>;
   1075 *			};
   1076 *		};
   1077 *	};
   1078 * };
   1079 *
   1080 * Return: 0 on success
   1081 *	   -ENOENT if no entries (or the property itself) were found
   1082 *	   -EINVAL if property parsing otherwise failed
   1083 *	   -ENOMEM if memory allocation failed
   1084 */
   1085static struct fwnode_handle *
   1086v4l2_fwnode_reference_get_int_prop(struct fwnode_handle *fwnode,
   1087				   const char *prop,
   1088				   unsigned int index,
   1089				   const char * const *props,
   1090				   unsigned int nprops)
   1091{
   1092	struct fwnode_reference_args fwnode_args;
   1093	u64 *args = fwnode_args.args;
   1094	struct fwnode_handle *child;
   1095	int ret;
   1096
   1097	/*
   1098	 * Obtain remote fwnode as well as the integer arguments.
   1099	 *
   1100	 * Note that right now both -ENODATA and -ENOENT may signal
   1101	 * out-of-bounds access. Return -ENOENT in that case.
   1102	 */
   1103	ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
   1104						 index, &fwnode_args);
   1105	if (ret)
   1106		return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
   1107
   1108	/*
   1109	 * Find a node in the tree under the referred fwnode corresponding to
   1110	 * the integer arguments.
   1111	 */
   1112	fwnode = fwnode_args.fwnode;
   1113	while (nprops--) {
   1114		u32 val;
   1115
   1116		/* Loop over all child nodes under fwnode. */
   1117		fwnode_for_each_child_node(fwnode, child) {
   1118			if (fwnode_property_read_u32(child, *props, &val))
   1119				continue;
   1120
   1121			/* Found property, see if its value matches. */
   1122			if (val == *args)
   1123				break;
   1124		}
   1125
   1126		fwnode_handle_put(fwnode);
   1127
   1128		/* No property found; return an error here. */
   1129		if (!child) {
   1130			fwnode = ERR_PTR(-ENOENT);
   1131			break;
   1132		}
   1133
   1134		props++;
   1135		args++;
   1136		fwnode = child;
   1137	}
   1138
   1139	return fwnode;
   1140}
   1141
   1142struct v4l2_fwnode_int_props {
   1143	const char *name;
   1144	const char * const *props;
   1145	unsigned int nprops;
   1146};
   1147
   1148/*
   1149 * v4l2_fwnode_reference_parse_int_props - parse references for async
   1150 *					   sub-devices
   1151 * @dev: struct device pointer
   1152 * @notifier: notifier for @dev
   1153 * @prop: the name of the property
   1154 * @props: the array of integer property names
   1155 * @nprops: the number of integer properties
   1156 *
   1157 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
   1158 * property @prop with integer arguments with child nodes matching in properties
   1159 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
   1160 * accordingly.
   1161 *
   1162 * While it is technically possible to use this function on DT, it is only
   1163 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
   1164 * on ACPI the references are limited to devices.
   1165 *
   1166 * Return: 0 on success
   1167 *	   -ENOENT if no entries (or the property itself) were found
   1168 *	   -EINVAL if property parsing otherwisefailed
   1169 *	   -ENOMEM if memory allocation failed
   1170 */
   1171static int
   1172v4l2_fwnode_reference_parse_int_props(struct device *dev,
   1173				      struct v4l2_async_notifier *notifier,
   1174				      const struct v4l2_fwnode_int_props *p)
   1175{
   1176	struct fwnode_handle *fwnode;
   1177	unsigned int index;
   1178	int ret;
   1179	const char *prop = p->name;
   1180	const char * const *props = p->props;
   1181	unsigned int nprops = p->nprops;
   1182
   1183	index = 0;
   1184	do {
   1185		fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
   1186							    prop, index,
   1187							    props, nprops);
   1188		if (IS_ERR(fwnode)) {
   1189			/*
   1190			 * Note that right now both -ENODATA and -ENOENT may
   1191			 * signal out-of-bounds access. Return the error in
   1192			 * cases other than that.
   1193			 */
   1194			if (PTR_ERR(fwnode) != -ENOENT &&
   1195			    PTR_ERR(fwnode) != -ENODATA)
   1196				return PTR_ERR(fwnode);
   1197			break;
   1198		}
   1199		fwnode_handle_put(fwnode);
   1200		index++;
   1201	} while (1);
   1202
   1203	for (index = 0;
   1204	     !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
   1205								  prop, index,
   1206								  props,
   1207								  nprops)));
   1208	     index++) {
   1209		struct v4l2_async_subdev *asd;
   1210
   1211		asd = v4l2_async_nf_add_fwnode(notifier, fwnode,
   1212					       struct v4l2_async_subdev);
   1213		fwnode_handle_put(fwnode);
   1214		if (IS_ERR(asd)) {
   1215			ret = PTR_ERR(asd);
   1216			/* not an error if asd already exists */
   1217			if (ret == -EEXIST)
   1218				continue;
   1219
   1220			return PTR_ERR(asd);
   1221		}
   1222	}
   1223
   1224	return !fwnode || PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
   1225}
   1226
   1227/**
   1228 * v4l2_async_nf_parse_fwnode_sensor - parse common references on
   1229 *					     sensors for async sub-devices
   1230 * @dev: the device node the properties of which are parsed for references
   1231 * @notifier: the async notifier where the async subdevs will be added
   1232 *
   1233 * Parse common sensor properties for remote devices related to the
   1234 * sensor and set up async sub-devices for them.
   1235 *
   1236 * Any notifier populated using this function must be released with a call to
   1237 * v4l2_async_nf_release() after it has been unregistered and the async
   1238 * sub-devices are no longer in use, even in the case the function returned an
   1239 * error.
   1240 *
   1241 * Return: 0 on success
   1242 *	   -ENOMEM if memory allocation failed
   1243 *	   -EINVAL if property parsing failed
   1244 */
   1245static int
   1246v4l2_async_nf_parse_fwnode_sensor(struct device *dev,
   1247				  struct v4l2_async_notifier *notifier)
   1248{
   1249	static const char * const led_props[] = { "led" };
   1250	static const struct v4l2_fwnode_int_props props[] = {
   1251		{ "flash-leds", led_props, ARRAY_SIZE(led_props) },
   1252		{ "lens-focus", NULL, 0 },
   1253	};
   1254	unsigned int i;
   1255
   1256	for (i = 0; i < ARRAY_SIZE(props); i++) {
   1257		int ret;
   1258
   1259		if (props[i].props && is_acpi_node(dev_fwnode(dev)))
   1260			ret = v4l2_fwnode_reference_parse_int_props(dev,
   1261								    notifier,
   1262								    &props[i]);
   1263		else
   1264			ret = v4l2_fwnode_reference_parse(dev, notifier,
   1265							  props[i].name);
   1266		if (ret && ret != -ENOENT) {
   1267			dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
   1268				 props[i].name, ret);
   1269			return ret;
   1270		}
   1271	}
   1272
   1273	return 0;
   1274}
   1275
   1276int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
   1277{
   1278	struct v4l2_async_notifier *notifier;
   1279	int ret;
   1280
   1281	if (WARN_ON(!sd->dev))
   1282		return -ENODEV;
   1283
   1284	notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
   1285	if (!notifier)
   1286		return -ENOMEM;
   1287
   1288	v4l2_async_nf_init(notifier);
   1289
   1290	ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
   1291	if (ret < 0)
   1292		goto out_cleanup;
   1293
   1294	ret = v4l2_async_subdev_nf_register(sd, notifier);
   1295	if (ret < 0)
   1296		goto out_cleanup;
   1297
   1298	ret = v4l2_async_register_subdev(sd);
   1299	if (ret < 0)
   1300		goto out_unregister;
   1301
   1302	sd->subdev_notifier = notifier;
   1303
   1304	return 0;
   1305
   1306out_unregister:
   1307	v4l2_async_nf_unregister(notifier);
   1308
   1309out_cleanup:
   1310	v4l2_async_nf_cleanup(notifier);
   1311	kfree(notifier);
   1312
   1313	return ret;
   1314}
   1315EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
   1316
   1317MODULE_LICENSE("GPL");
   1318MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
   1319MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
   1320MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");