brintos

brintos / linux-shallow public Read only

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