1 // SPDX-License-Identifier: GPL-2.0-only
3 * V4L2 fwnode binding parsing library
5 * The origins of the V4L2 fwnode library are in V4L2 OF library that
6 * formerly was located in v4l2-of.c.
8 * Copyright (c) 2016 Intel Corporation.
9 * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
11 * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
12 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
14 * Copyright (C) 2012 Renesas Electronics Corp.
15 * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
17 #include <linux/acpi.h>
18 #include <linux/kernel.h>
20 #include <linux/module.h>
22 #include <linux/property.h>
23 #include <linux/slab.h>
24 #include <linux/string.h>
25 #include <linux/types.h>
27 #include <media/v4l2-async.h>
28 #include <media/v4l2-fwnode.h>
29 #include <media/v4l2-subdev.h>
31 #include "v4l2-subdev-priv.h"
33 static const struct v4l2_fwnode_bus_conv
{
34 enum v4l2_fwnode_bus_type fwnode_bus_type
;
35 enum v4l2_mbus_type mbus_type
;
39 V4L2_FWNODE_BUS_TYPE_GUESS
,
43 V4L2_FWNODE_BUS_TYPE_CSI2_CPHY
,
47 V4L2_FWNODE_BUS_TYPE_CSI1
,
51 V4L2_FWNODE_BUS_TYPE_CCP2
,
53 "compact camera port 2",
55 V4L2_FWNODE_BUS_TYPE_CSI2_DPHY
,
59 V4L2_FWNODE_BUS_TYPE_PARALLEL
,
63 V4L2_FWNODE_BUS_TYPE_BT656
,
67 V4L2_FWNODE_BUS_TYPE_DPI
,
73 static const struct v4l2_fwnode_bus_conv
*
74 get_v4l2_fwnode_bus_conv_by_fwnode_bus(enum v4l2_fwnode_bus_type type
)
78 for (i
= 0; i
< ARRAY_SIZE(buses
); i
++)
79 if (buses
[i
].fwnode_bus_type
== type
)
85 static enum v4l2_mbus_type
86 v4l2_fwnode_bus_type_to_mbus(enum v4l2_fwnode_bus_type type
)
88 const struct v4l2_fwnode_bus_conv
*conv
=
89 get_v4l2_fwnode_bus_conv_by_fwnode_bus(type
);
91 return conv
? conv
->mbus_type
: V4L2_MBUS_INVALID
;
95 v4l2_fwnode_bus_type_to_string(enum v4l2_fwnode_bus_type type
)
97 const struct v4l2_fwnode_bus_conv
*conv
=
98 get_v4l2_fwnode_bus_conv_by_fwnode_bus(type
);
100 return conv
? conv
->name
: "not found";
103 static const struct v4l2_fwnode_bus_conv
*
104 get_v4l2_fwnode_bus_conv_by_mbus(enum v4l2_mbus_type type
)
108 for (i
= 0; i
< ARRAY_SIZE(buses
); i
++)
109 if (buses
[i
].mbus_type
== type
)
116 v4l2_fwnode_mbus_type_to_string(enum v4l2_mbus_type type
)
118 const struct v4l2_fwnode_bus_conv
*conv
=
119 get_v4l2_fwnode_bus_conv_by_mbus(type
);
121 return conv
? conv
->name
: "not found";
124 static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle
*fwnode
,
125 struct v4l2_fwnode_endpoint
*vep
,
126 enum v4l2_mbus_type bus_type
)
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
];
134 unsigned int num_data_lanes
= 0;
135 bool use_default_lane_mapping
= false;
140 if (bus_type
== V4L2_MBUS_CSI2_DPHY
||
141 bus_type
== V4L2_MBUS_CSI2_CPHY
) {
142 use_default_lane_mapping
= true;
144 num_data_lanes
= min_t(u32
, bus
->num_data_lanes
,
145 V4L2_MBUS_CSI2_MAX_DATA_LANES
);
147 clock_lane
= bus
->clock_lane
;
149 use_default_lane_mapping
= false;
151 for (i
= 0; i
< num_data_lanes
; i
++) {
152 array
[i
] = bus
->data_lanes
[i
];
154 use_default_lane_mapping
= false;
157 if (use_default_lane_mapping
)
158 pr_debug("no lane mapping given, using defaults\n");
161 rval
= fwnode_property_count_u32(fwnode
, "data-lanes");
164 min_t(int, V4L2_MBUS_CSI2_MAX_DATA_LANES
, rval
);
166 fwnode_property_read_u32_array(fwnode
, "data-lanes", array
,
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;
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",
181 use_default_lane_mapping
= true;
183 lanes_used
|= BIT(array
[i
]);
186 pr_debug("lane %u position %u\n", i
, array
[i
]);
189 rval
= fwnode_property_count_u32(fwnode
, "lane-polarities");
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
);
197 have_lane_polarities
= true;
200 if (!fwnode_property_read_u32(fwnode
, "clock-lanes", &v
)) {
202 pr_debug("clock lane position %u\n", v
);
203 have_clk_lane
= true;
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",
210 use_default_lane_mapping
= true;
213 if (fwnode_property_present(fwnode
, "clock-noncontinuous")) {
214 flags
|= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK
;
215 pr_debug("non-continuous clock\n");
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
;
226 if (bus_type
== V4L2_MBUS_UNKNOWN
)
227 vep
->bus_type
= V4L2_MBUS_CSI2_DPHY
;
228 bus
->num_data_lanes
= num_data_lanes
;
230 if (use_default_lane_mapping
) {
232 for (i
= 0; i
< num_data_lanes
; i
++)
233 bus
->data_lanes
[i
] = dfl_data_lane_index
+ i
;
235 bus
->clock_lane
= clock_lane
;
236 for (i
= 0; i
< num_data_lanes
; i
++)
237 bus
->data_lanes
[i
] = array
[i
];
240 if (have_lane_polarities
) {
241 fwnode_property_read_u32_array(fwnode
,
242 "lane-polarities", array
,
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 ");
251 pr_debug("no lane polarities defined, assuming not inverted\n");
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)
266 v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle
*fwnode
,
267 struct v4l2_fwnode_endpoint
*vep
,
268 enum v4l2_mbus_type bus_type
)
270 struct v4l2_mbus_config_parallel
*bus
= &vep
->bus
.parallel
;
271 unsigned int flags
= 0;
274 if (bus_type
== V4L2_MBUS_PARALLEL
|| bus_type
== V4L2_MBUS_BT656
)
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");
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");
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");
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
);
307 flags
|= V4L2_MBUS_PCLK_SAMPLE_FALLING
;
308 pr_debug("pclk-sample low\n");
311 flags
|= V4L2_MBUS_PCLK_SAMPLE_RISING
;
312 pr_debug("pclk-sample high\n");
315 flags
|= V4L2_MBUS_PCLK_SAMPLE_DUALEDGE
;
316 pr_debug("pclk-sample dual edge\n");
319 pr_warn("invalid argument for pclk-sample");
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");
332 if (fwnode_property_present(fwnode
, "slave-mode")) {
333 pr_debug("slave mode\n");
334 flags
&= ~V4L2_MBUS_MASTER
;
335 flags
|= V4L2_MBUS_SLAVE
;
337 flags
&= ~V4L2_MBUS_SLAVE
;
338 flags
|= V4L2_MBUS_MASTER
;
341 if (!fwnode_property_read_u32(fwnode
, "bus-width", &v
)) {
343 pr_debug("bus-width %u\n", v
);
346 if (!fwnode_property_read_u32(fwnode
, "data-shift", &v
)) {
348 pr_debug("data-shift %u\n", v
);
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");
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");
370 if (flags
& PARALLEL_MBUS_FLAGS
)
371 vep
->bus_type
= V4L2_MBUS_PARALLEL
;
373 vep
->bus_type
= V4L2_MBUS_BT656
;
375 case V4L2_MBUS_PARALLEL
:
376 vep
->bus_type
= V4L2_MBUS_PARALLEL
;
379 case V4L2_MBUS_BT656
:
380 vep
->bus_type
= V4L2_MBUS_BT656
;
381 bus
->flags
= flags
& ~PARALLEL_MBUS_FLAGS
;
387 v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle
*fwnode
,
388 struct v4l2_fwnode_endpoint
*vep
,
389 enum v4l2_mbus_type bus_type
)
391 struct v4l2_mbus_config_mipi_csi1
*bus
= &vep
->bus
.mipi_csi1
;
394 if (!fwnode_property_read_u32(fwnode
, "clock-inv", &v
)) {
396 pr_debug("clock-inv %u\n", v
);
399 if (!fwnode_property_read_u32(fwnode
, "strobe", &v
)) {
401 pr_debug("strobe %u\n", v
);
404 if (!fwnode_property_read_u32(fwnode
, "data-lanes", &v
)) {
406 pr_debug("data-lanes %u\n", v
);
409 if (!fwnode_property_read_u32(fwnode
, "clock-lanes", &v
)) {
411 pr_debug("clock-lanes %u\n", v
);
414 if (bus_type
== V4L2_MBUS_CCP2
)
415 vep
->bus_type
= V4L2_MBUS_CCP2
;
417 vep
->bus_type
= V4L2_MBUS_CSI1
;
420 static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle
*fwnode
,
421 struct v4l2_fwnode_endpoint
*vep
)
423 u32 bus_type
= V4L2_FWNODE_BUS_TYPE_GUESS
;
424 enum v4l2_mbus_type mbus_type
;
427 pr_debug("===== begin parsing endpoint %pfw\n", fwnode
);
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
),
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
);
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
));
448 vep
->bus_type
= mbus_type
;
451 switch (vep
->bus_type
) {
452 case V4L2_MBUS_UNKNOWN
:
453 rval
= v4l2_fwnode_endpoint_parse_csi2_bus(fwnode
, vep
,
458 if (vep
->bus_type
== V4L2_MBUS_UNKNOWN
)
459 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode
, vep
,
462 pr_debug("assuming media bus type %s (%u)\n",
463 v4l2_fwnode_mbus_type_to_string(vep
->bus_type
),
469 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode
, vep
, vep
->bus_type
);
472 case V4L2_MBUS_CSI2_DPHY
:
473 case V4L2_MBUS_CSI2_CPHY
:
474 rval
= v4l2_fwnode_endpoint_parse_csi2_bus(fwnode
, vep
,
480 case V4L2_MBUS_PARALLEL
:
481 case V4L2_MBUS_BT656
:
482 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode
, vep
,
487 pr_warn("unsupported bus type %u\n", mbus_type
);
491 fwnode_graph_parse_endpoint(fwnode
, &vep
->base
);
496 int v4l2_fwnode_endpoint_parse(struct fwnode_handle
*fwnode
,
497 struct v4l2_fwnode_endpoint
*vep
)
501 ret
= __v4l2_fwnode_endpoint_parse(fwnode
, vep
);
503 pr_debug("===== end parsing endpoint %pfw\n", fwnode
);
507 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse
);
509 void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint
*vep
)
511 if (IS_ERR_OR_NULL(vep
))
514 kfree(vep
->link_frequencies
);
515 vep
->link_frequencies
= NULL
;
517 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free
);
519 int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle
*fwnode
,
520 struct v4l2_fwnode_endpoint
*vep
)
524 rval
= __v4l2_fwnode_endpoint_parse(fwnode
, vep
);
528 rval
= fwnode_property_count_u64(fwnode
, "link-frequencies");
532 vep
->link_frequencies
=
533 kmalloc_array(rval
, sizeof(*vep
->link_frequencies
),
535 if (!vep
->link_frequencies
)
538 vep
->nr_of_link_frequencies
= rval
;
540 rval
= fwnode_property_read_u64_array(fwnode
,
542 vep
->link_frequencies
,
543 vep
->nr_of_link_frequencies
);
545 v4l2_fwnode_endpoint_free(vep
);
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
]);
554 pr_debug("===== end parsing endpoint %pfw\n", fwnode
);
558 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse
);
560 int v4l2_fwnode_parse_link(struct fwnode_handle
*fwnode
,
561 struct v4l2_fwnode_link
*link
)
563 struct fwnode_endpoint fwep
;
565 memset(link
, 0, sizeof(*link
));
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
)
574 fwnode
= fwnode_graph_get_remote_endpoint(fwnode
);
576 goto err_put_local_node
;
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
;
587 err_put_remote_endpoint
:
588 fwnode_handle_put(fwnode
);
591 fwnode_handle_put(link
->local_node
);
595 EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link
);
597 void v4l2_fwnode_put_link(struct v4l2_fwnode_link
*link
)
599 fwnode_handle_put(link
->local_node
);
600 fwnode_handle_put(link
->remote_node
);
602 EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link
);
604 static const struct v4l2_fwnode_connector_conv
{
605 enum v4l2_connector_type type
;
606 const char *compatible
;
609 .type
= V4L2_CONN_COMPOSITE
,
610 .compatible
= "composite-video-connector",
612 .type
= V4L2_CONN_SVIDEO
,
613 .compatible
= "svideo-connector",
617 static enum v4l2_connector_type
618 v4l2_fwnode_string_to_connector_type(const char *con_str
)
622 for (i
= 0; i
< ARRAY_SIZE(connectors
); i
++)
623 if (!strcmp(con_str
, connectors
[i
].compatible
))
624 return connectors
[i
].type
;
626 return V4L2_CONN_UNKNOWN
;
630 v4l2_fwnode_connector_parse_analog(struct fwnode_handle
*fwnode
,
631 struct v4l2_fwnode_connector
*vc
)
636 ret
= fwnode_property_read_u32(fwnode
, "sdtv-standards", &stds
);
638 /* The property is optional. */
639 vc
->connector
.analog
.sdtv_stds
= ret
? V4L2_STD_ALL
: stds
;
642 void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector
*connector
)
644 struct v4l2_connector_link
*link
, *tmp
;
646 if (IS_ERR_OR_NULL(connector
) || connector
->type
== V4L2_CONN_UNKNOWN
)
649 list_for_each_entry_safe(link
, tmp
, &connector
->links
, head
) {
650 v4l2_fwnode_put_link(&link
->fwnode_link
);
651 list_del(&link
->head
);
655 kfree(connector
->label
);
656 connector
->label
= NULL
;
657 connector
->type
= V4L2_CONN_UNKNOWN
;
659 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free
);
661 static enum v4l2_connector_type
662 v4l2_fwnode_get_connector_type(struct fwnode_handle
*fwnode
)
664 const char *type_name
;
668 return V4L2_CONN_UNKNOWN
;
670 /* The connector-type is stored within the compatible string. */
671 err
= fwnode_property_read_string(fwnode
, "compatible", &type_name
);
673 return V4L2_CONN_UNKNOWN
;
675 return v4l2_fwnode_string_to_connector_type(type_name
);
678 int v4l2_fwnode_connector_parse(struct fwnode_handle
*fwnode
,
679 struct v4l2_fwnode_connector
*connector
)
681 struct fwnode_handle
*connector_node
;
682 enum v4l2_connector_type connector_type
;
689 memset(connector
, 0, sizeof(*connector
));
691 INIT_LIST_HEAD(&connector
->links
);
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
);
701 if (connector_type
== V4L2_CONN_UNKNOWN
) {
702 pr_err("Unknown connector type\n");
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
);
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
);
718 /* Avoid compiler warnings */
719 case V4L2_CONN_UNKNOWN
:
724 fwnode_handle_put(connector_node
);
728 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse
);
730 int v4l2_fwnode_connector_add_link(struct fwnode_handle
*fwnode
,
731 struct v4l2_fwnode_connector
*connector
)
733 struct fwnode_handle
*connector_ep
;
734 struct v4l2_connector_link
*link
;
737 if (!fwnode
|| !connector
|| connector
->type
== V4L2_CONN_UNKNOWN
)
740 connector_ep
= fwnode_graph_get_remote_endpoint(fwnode
);
744 link
= kzalloc(sizeof(*link
), GFP_KERNEL
);
750 err
= v4l2_fwnode_parse_link(connector_ep
, &link
->fwnode_link
);
754 fwnode_handle_put(connector_ep
);
756 list_add(&link
->head
, &connector
->links
);
757 connector
->nr_of_links
++;
763 fwnode_handle_put(connector_ep
);
767 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link
);
769 int v4l2_fwnode_device_parse(struct device
*dev
,
770 struct v4l2_fwnode_device_properties
*props
)
772 struct fwnode_handle
*fwnode
= dev_fwnode(dev
);
776 memset(props
, 0, sizeof(*props
));
778 props
->orientation
= V4L2_FWNODE_PROPERTY_UNSET
;
779 ret
= fwnode_property_read_u32(fwnode
, "orientation", &val
);
782 case V4L2_FWNODE_ORIENTATION_FRONT
:
783 case V4L2_FWNODE_ORIENTATION_BACK
:
784 case V4L2_FWNODE_ORIENTATION_EXTERNAL
:
787 dev_warn(dev
, "Unsupported device orientation: %u\n", val
);
791 props
->orientation
= val
;
792 dev_dbg(dev
, "device orientation: %u\n", val
);
795 props
->rotation
= V4L2_FWNODE_PROPERTY_UNSET
;
796 ret
= fwnode_property_read_u32(fwnode
, "rotation", &val
);
799 dev_warn(dev
, "Unsupported device rotation: %u\n", val
);
803 props
->rotation
= val
;
804 dev_dbg(dev
, "device rotation: %u\n", val
);
809 EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse
);
812 * v4l2_fwnode_reference_parse - parse references for async sub-devices
813 * @dev: the device node the properties of which are parsed for references
814 * @notifier: the async notifier where the async subdevs will be added
815 * @prop: the name of the property
817 * Return: 0 on success
818 * -ENOENT if no entries were found
819 * -ENOMEM if memory allocation failed
820 * -EINVAL if property parsing failed
822 static int v4l2_fwnode_reference_parse(struct device
*dev
,
823 struct v4l2_async_notifier
*notifier
,
826 struct fwnode_reference_args args
;
831 !(ret
= fwnode_property_get_reference_args(dev_fwnode(dev
), prop
,
832 NULL
, 0, index
, &args
));
834 struct v4l2_async_connection
*asd
;
836 asd
= v4l2_async_nf_add_fwnode(notifier
, args
.fwnode
,
837 struct v4l2_async_connection
);
838 fwnode_handle_put(args
.fwnode
);
840 /* not an error if asd already exists */
841 if (PTR_ERR(asd
) == -EEXIST
)
848 /* -ENOENT here means successful parsing */
852 /* Return -ENOENT if no references were found */
853 return index
? 0 : -ENOENT
;
857 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
859 * @fwnode: fwnode to read @prop from
860 * @notifier: notifier for @dev
861 * @prop: the name of the property
862 * @index: the index of the reference to get
863 * @props: the array of integer property names
864 * @nprops: the number of integer property names in @nprops
866 * First find an fwnode referred to by the reference at @index in @prop.
868 * Then under that fwnode, @nprops times, for each property in @props,
869 * iteratively follow child nodes starting from fwnode such that they have the
870 * property in @props array at the index of the child node distance from the
871 * root node and the value of that property matching with the integer argument
872 * of the reference, at the same index.
874 * The child fwnode reached at the end of the iteration is then returned to the
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, then
879 * provide a list of (property name, property value) tuples where each tuple
880 * uniquely identifies a child node. The first tuple identifies a child directly
881 * underneath the device fwnode, the next tuple identifies a child node
882 * underneath the fwnode identified by the previous tuple, etc. until you
883 * reached the fwnode you need.
885 * THIS EXAMPLE EXISTS MERELY TO DOCUMENT THIS FUNCTION. DO NOT USE IT AS A
886 * REFERENCE IN HOW ACPI TABLES SHOULD BE WRITTEN!! See documentation under
887 * Documentation/firmware-guide/acpi/dsd/ instead and especially graph.txt,
888 * data-node-references.txt and leds.txt .
890 * Scope (\_SB.PCI0.I2C2)
894 * Name (_DSD, Package () {
895 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
899 * Package () { "nokia,smia" }
902 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
904 * Package () { "port0", "PRT0" },
907 * Name (PRT0, Package() {
908 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
910 * Package () { "port", 0 },
912 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
914 * Package () { "endpoint0", "EP00" },
917 * Name (EP00, Package() {
918 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
920 * Package () { "endpoint", 0 },
924 * \_SB.PCI0.ISP, 4, 0
936 * Name (_DSD, Package () {
937 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
939 * Package () { "port4", "PRT4" },
943 * Name (PRT4, Package() {
944 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
946 * Package () { "port", 4 },
948 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
950 * Package () { "endpoint0", "EP40" },
954 * Name (EP40, Package() {
955 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
957 * Package () { "endpoint", 0 },
961 * \_SB.PCI0.I2C2.CAM0,
970 * From the EP40 node under ISP device, you could parse the graph remote
971 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
973 * @fwnode: fwnode referring to EP40 under ISP.
974 * @prop: "remote-endpoint"
976 * @props: "port", "endpoint"
979 * And you'd get back fwnode referring to EP00 under CAM0.
981 * The same works the other way around: if you use EP00 under CAM0 as the
982 * fwnode, you'll get fwnode referring to EP40 under ISP.
984 * The same example in DT syntax would look like this:
987 * compatible = "nokia,smia";
993 * remote-endpoint = <&isp 4 0>;
1004 * remote-endpoint = <&cam 0 0>;
1010 * Return: 0 on success
1011 * -ENOENT if no entries (or the property itself) were found
1012 * -EINVAL if property parsing otherwise failed
1013 * -ENOMEM if memory allocation failed
1015 static struct fwnode_handle
*
1016 v4l2_fwnode_reference_get_int_prop(struct fwnode_handle
*fwnode
,
1019 const char * const *props
,
1020 unsigned int nprops
)
1022 struct fwnode_reference_args fwnode_args
;
1023 u64
*args
= fwnode_args
.args
;
1024 struct fwnode_handle
*child
;
1028 * Obtain remote fwnode as well as the integer arguments.
1030 * Note that right now both -ENODATA and -ENOENT may signal
1031 * out-of-bounds access. Return -ENOENT in that case.
1033 ret
= fwnode_property_get_reference_args(fwnode
, prop
, NULL
, nprops
,
1034 index
, &fwnode_args
);
1036 return ERR_PTR(ret
== -ENODATA
? -ENOENT
: ret
);
1039 * Find a node in the tree under the referred fwnode corresponding to
1040 * the integer arguments.
1042 fwnode
= fwnode_args
.fwnode
;
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
))
1051 /* Found property, see if its value matches. */
1056 fwnode_handle_put(fwnode
);
1058 /* No property found; return an error here. */
1060 fwnode
= ERR_PTR(-ENOENT
);
1072 struct v4l2_fwnode_int_props
{
1074 const char * const *props
;
1075 unsigned int nprops
;
1079 * v4l2_fwnode_reference_parse_int_props - parse references for async
1081 * @dev: struct device pointer
1082 * @notifier: notifier for @dev
1083 * @prop: the name of the property
1084 * @props: the array of integer property names
1085 * @nprops: the number of integer properties
1087 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
1088 * property @prop with integer arguments with child nodes matching in properties
1089 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
1092 * While it is technically possible to use this function on DT, it is only
1093 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
1094 * on ACPI the references are limited to devices.
1096 * Return: 0 on success
1097 * -ENOENT if no entries (or the property itself) were found
1098 * -EINVAL if property parsing otherwisefailed
1099 * -ENOMEM if memory allocation failed
1102 v4l2_fwnode_reference_parse_int_props(struct device
*dev
,
1103 struct v4l2_async_notifier
*notifier
,
1104 const struct v4l2_fwnode_int_props
*p
)
1106 struct fwnode_handle
*fwnode
;
1109 const char *prop
= p
->name
;
1110 const char * const *props
= p
->props
;
1111 unsigned int nprops
= p
->nprops
;
1115 fwnode
= v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev
),
1118 if (IS_ERR(fwnode
)) {
1120 * Note that right now both -ENODATA and -ENOENT may
1121 * signal out-of-bounds access. Return the error in
1122 * cases other than that.
1124 if (PTR_ERR(fwnode
) != -ENOENT
&&
1125 PTR_ERR(fwnode
) != -ENODATA
)
1126 return PTR_ERR(fwnode
);
1129 fwnode_handle_put(fwnode
);
1134 !IS_ERR((fwnode
= v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev
),
1139 struct v4l2_async_connection
*asd
;
1141 asd
= v4l2_async_nf_add_fwnode(notifier
, fwnode
,
1142 struct v4l2_async_connection
);
1143 fwnode_handle_put(fwnode
);
1146 /* not an error if asd already exists */
1150 return PTR_ERR(asd
);
1154 return !fwnode
|| PTR_ERR(fwnode
) == -ENOENT
? 0 : PTR_ERR(fwnode
);
1158 * v4l2_async_nf_parse_fwnode_sensor - parse common references on
1159 * sensors for async sub-devices
1160 * @dev: the device node the properties of which are parsed for references
1161 * @notifier: the async notifier where the async subdevs will be added
1163 * Parse common sensor properties for remote devices related to the
1164 * sensor and set up async sub-devices for them.
1166 * Any notifier populated using this function must be released with a call to
1167 * v4l2_async_nf_release() after it has been unregistered and the async
1168 * sub-devices are no longer in use, even in the case the function returned an
1171 * Return: 0 on success
1172 * -ENOMEM if memory allocation failed
1173 * -EINVAL if property parsing failed
1176 v4l2_async_nf_parse_fwnode_sensor(struct device
*dev
,
1177 struct v4l2_async_notifier
*notifier
)
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", },
1184 { "mipi-img-lens-focus", },
1188 for (i
= 0; i
< ARRAY_SIZE(props
); i
++) {
1191 if (props
[i
].props
&& is_acpi_node(dev_fwnode(dev
)))
1192 ret
= v4l2_fwnode_reference_parse_int_props(dev
,
1196 ret
= v4l2_fwnode_reference_parse(dev
, notifier
,
1198 if (ret
&& ret
!= -ENOENT
) {
1199 dev_warn(dev
, "parsing property \"%s\" failed (%d)\n",
1200 props
[i
].name
, ret
);
1208 int v4l2_async_register_subdev_sensor(struct v4l2_subdev
*sd
)
1210 struct v4l2_async_notifier
*notifier
;
1213 if (WARN_ON(!sd
->dev
))
1216 notifier
= kzalloc(sizeof(*notifier
), GFP_KERNEL
);
1220 v4l2_async_subdev_nf_init(notifier
, sd
);
1222 ret
= v4l2_subdev_get_privacy_led(sd
);
1226 ret
= v4l2_async_nf_parse_fwnode_sensor(sd
->dev
, notifier
);
1230 ret
= v4l2_async_nf_register(notifier
);
1234 ret
= v4l2_async_register_subdev(sd
);
1236 goto out_unregister
;
1238 sd
->subdev_notifier
= notifier
;
1243 v4l2_async_nf_unregister(notifier
);
1246 v4l2_subdev_put_privacy_led(sd
);
1247 v4l2_async_nf_cleanup(notifier
);
1252 EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor
);
1254 MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");
1255 MODULE_LICENSE("GPL");
1256 MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1257 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1258 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");