xref: /linux/drivers/media/v4l2-core/v4l2-fwnode.c (revision 2da68a77)
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 
31 static 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 
71 static const struct v4l2_fwnode_bus_conv *
72 get_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 
83 static enum v4l2_mbus_type
84 v4l2_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 
92 static const char *
93 v4l2_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 
101 static const struct v4l2_fwnode_bus_conv *
102 get_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 
113 static const char *
114 v4l2_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 
122 static 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 
263 static void
264 v4l2_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 
369 static void
370 v4l2_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 
403 static 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 
479 int 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 }
490 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
491 
492 void 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 }
500 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
501 
502 int 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 }
541 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
542 
543 int 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 }
568 EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
569 
570 void 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 }
575 EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
576 
577 static 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 
590 static enum v4l2_connector_type
591 v4l2_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 
602 static void
603 v4l2_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 
615 void 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 }
632 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
633 
634 static enum v4l2_connector_type
635 v4l2_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 
651 int 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 
696 out:
697 	fwnode_handle_put(connector_node);
698 
699 	return err;
700 }
701 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse);
702 
703 int 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 
734 err:
735 	kfree(link);
736 	fwnode_handle_put(connector_ep);
737 
738 	return err;
739 }
740 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link);
741 
742 int 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 }
782 EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse);
783 
784 static int
785 v4l2_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 
837 out_err:
838 	fwnode_handle_put(asd->match.fwnode);
839 	kfree(asd);
840 
841 	return ret == -ENOTCONN ? 0 : ret;
842 }
843 
844 int
845 v4l2_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 }
879 EXPORT_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  */
892 static 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  */
1085 static struct fwnode_handle *
1086 v4l2_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 
1142 struct 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  */
1171 static int
1172 v4l2_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  */
1245 static int
1246 v4l2_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 
1276 int 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 
1306 out_unregister:
1307 	v4l2_async_nf_unregister(notifier);
1308 
1309 out_cleanup:
1310 	v4l2_async_nf_cleanup(notifier);
1311 	kfree(notifier);
1312 
1313 	return ret;
1314 }
1315 EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
1316 
1317 MODULE_LICENSE("GPL");
1318 MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1319 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1320 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
1321