Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * V4L2 fwnode binding parsing library
0004  *
0005  * The origins of the V4L2 fwnode library are in V4L2 OF library that
0006  * formerly was located in v4l2-of.c.
0007  *
0008  * Copyright (c) 2016 Intel Corporation.
0009  * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
0010  *
0011  * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
0012  * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
0013  *
0014  * Copyright (C) 2012 Renesas Electronics Corp.
0015  * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
0016  */
0017 #include <linux/acpi.h>
0018 #include <linux/kernel.h>
0019 #include <linux/mm.h>
0020 #include <linux/module.h>
0021 #include <linux/of.h>
0022 #include <linux/property.h>
0023 #include <linux/slab.h>
0024 #include <linux/string.h>
0025 #include <linux/types.h>
0026 
0027 #include <media/v4l2-async.h>
0028 #include <media/v4l2-fwnode.h>
0029 #include <media/v4l2-subdev.h>
0030 
0031 static const struct v4l2_fwnode_bus_conv {
0032     enum v4l2_fwnode_bus_type fwnode_bus_type;
0033     enum v4l2_mbus_type mbus_type;
0034     const char *name;
0035 } buses[] = {
0036     {
0037         V4L2_FWNODE_BUS_TYPE_GUESS,
0038         V4L2_MBUS_UNKNOWN,
0039         "not specified",
0040     }, {
0041         V4L2_FWNODE_BUS_TYPE_CSI2_CPHY,
0042         V4L2_MBUS_CSI2_CPHY,
0043         "MIPI CSI-2 C-PHY",
0044     }, {
0045         V4L2_FWNODE_BUS_TYPE_CSI1,
0046         V4L2_MBUS_CSI1,
0047         "MIPI CSI-1",
0048     }, {
0049         V4L2_FWNODE_BUS_TYPE_CCP2,
0050         V4L2_MBUS_CCP2,
0051         "compact camera port 2",
0052     }, {
0053         V4L2_FWNODE_BUS_TYPE_CSI2_DPHY,
0054         V4L2_MBUS_CSI2_DPHY,
0055         "MIPI CSI-2 D-PHY",
0056     }, {
0057         V4L2_FWNODE_BUS_TYPE_PARALLEL,
0058         V4L2_MBUS_PARALLEL,
0059         "parallel",
0060     }, {
0061         V4L2_FWNODE_BUS_TYPE_BT656,
0062         V4L2_MBUS_BT656,
0063         "Bt.656",
0064     }, {
0065         V4L2_FWNODE_BUS_TYPE_DPI,
0066         V4L2_MBUS_DPI,
0067         "DPI",
0068     }
0069 };
0070 
0071 static const struct v4l2_fwnode_bus_conv *
0072 get_v4l2_fwnode_bus_conv_by_fwnode_bus(enum v4l2_fwnode_bus_type type)
0073 {
0074     unsigned int i;
0075 
0076     for (i = 0; i < ARRAY_SIZE(buses); i++)
0077         if (buses[i].fwnode_bus_type == type)
0078             return &buses[i];
0079 
0080     return NULL;
0081 }
0082 
0083 static enum v4l2_mbus_type
0084 v4l2_fwnode_bus_type_to_mbus(enum v4l2_fwnode_bus_type type)
0085 {
0086     const struct v4l2_fwnode_bus_conv *conv =
0087         get_v4l2_fwnode_bus_conv_by_fwnode_bus(type);
0088 
0089     return conv ? conv->mbus_type : V4L2_MBUS_INVALID;
0090 }
0091 
0092 static const char *
0093 v4l2_fwnode_bus_type_to_string(enum v4l2_fwnode_bus_type type)
0094 {
0095     const struct v4l2_fwnode_bus_conv *conv =
0096         get_v4l2_fwnode_bus_conv_by_fwnode_bus(type);
0097 
0098     return conv ? conv->name : "not found";
0099 }
0100 
0101 static const struct v4l2_fwnode_bus_conv *
0102 get_v4l2_fwnode_bus_conv_by_mbus(enum v4l2_mbus_type type)
0103 {
0104     unsigned int i;
0105 
0106     for (i = 0; i < ARRAY_SIZE(buses); i++)
0107         if (buses[i].mbus_type == type)
0108             return &buses[i];
0109 
0110     return NULL;
0111 }
0112 
0113 static const char *
0114 v4l2_fwnode_mbus_type_to_string(enum v4l2_mbus_type type)
0115 {
0116     const struct v4l2_fwnode_bus_conv *conv =
0117         get_v4l2_fwnode_bus_conv_by_mbus(type);
0118 
0119     return conv ? conv->name : "not found";
0120 }
0121 
0122 static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
0123                            struct v4l2_fwnode_endpoint *vep,
0124                            enum v4l2_mbus_type bus_type)
0125 {
0126     struct v4l2_mbus_config_mipi_csi2 *bus = &vep->bus.mipi_csi2;
0127     bool have_clk_lane = false, have_data_lanes = false,
0128         have_lane_polarities = false;
0129     unsigned int flags = 0, lanes_used = 0;
0130     u32 array[1 + V4L2_MBUS_CSI2_MAX_DATA_LANES];
0131     u32 clock_lane = 0;
0132     unsigned int num_data_lanes = 0;
0133     bool use_default_lane_mapping = false;
0134     unsigned int i;
0135     u32 v;
0136     int rval;
0137 
0138     if (bus_type == V4L2_MBUS_CSI2_DPHY ||
0139         bus_type == V4L2_MBUS_CSI2_CPHY) {
0140         use_default_lane_mapping = true;
0141 
0142         num_data_lanes = min_t(u32, bus->num_data_lanes,
0143                        V4L2_MBUS_CSI2_MAX_DATA_LANES);
0144 
0145         clock_lane = bus->clock_lane;
0146         if (clock_lane)
0147             use_default_lane_mapping = false;
0148 
0149         for (i = 0; i < num_data_lanes; i++) {
0150             array[i] = bus->data_lanes[i];
0151             if (array[i])
0152                 use_default_lane_mapping = false;
0153         }
0154 
0155         if (use_default_lane_mapping)
0156             pr_debug("no lane mapping given, using defaults\n");
0157     }
0158 
0159     rval = fwnode_property_count_u32(fwnode, "data-lanes");
0160     if (rval > 0) {
0161         num_data_lanes =
0162             min_t(int, V4L2_MBUS_CSI2_MAX_DATA_LANES, rval);
0163 
0164         fwnode_property_read_u32_array(fwnode, "data-lanes", array,
0165                            num_data_lanes);
0166 
0167         have_data_lanes = true;
0168         if (use_default_lane_mapping) {
0169             pr_debug("data-lanes property exists; disabling default mapping\n");
0170             use_default_lane_mapping = false;
0171         }
0172     }
0173 
0174     for (i = 0; i < num_data_lanes; i++) {
0175         if (lanes_used & BIT(array[i])) {
0176             if (have_data_lanes || !use_default_lane_mapping)
0177                 pr_warn("duplicated lane %u in data-lanes, using defaults\n",
0178                     array[i]);
0179             use_default_lane_mapping = true;
0180         }
0181         lanes_used |= BIT(array[i]);
0182 
0183         if (have_data_lanes)
0184             pr_debug("lane %u position %u\n", i, array[i]);
0185     }
0186 
0187     rval = fwnode_property_count_u32(fwnode, "lane-polarities");
0188     if (rval > 0) {
0189         if (rval != 1 + num_data_lanes /* clock+data */) {
0190             pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
0191                 1 + num_data_lanes, rval);
0192             return -EINVAL;
0193         }
0194 
0195         have_lane_polarities = true;
0196     }
0197 
0198     if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
0199         clock_lane = v;
0200         pr_debug("clock lane position %u\n", v);
0201         have_clk_lane = true;
0202     }
0203 
0204     if (have_clk_lane && lanes_used & BIT(clock_lane) &&
0205         !use_default_lane_mapping) {
0206         pr_warn("duplicated lane %u in clock-lanes, using defaults\n",
0207             v);
0208         use_default_lane_mapping = true;
0209     }
0210 
0211     if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
0212         flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
0213         pr_debug("non-continuous clock\n");
0214     }
0215 
0216     if (bus_type == V4L2_MBUS_CSI2_DPHY ||
0217         bus_type == V4L2_MBUS_CSI2_CPHY ||
0218         lanes_used || have_clk_lane || flags) {
0219         /* Only D-PHY has a clock lane. */
0220         unsigned int dfl_data_lane_index =
0221             bus_type == V4L2_MBUS_CSI2_DPHY;
0222 
0223         bus->flags = flags;
0224         if (bus_type == V4L2_MBUS_UNKNOWN)
0225             vep->bus_type = V4L2_MBUS_CSI2_DPHY;
0226         bus->num_data_lanes = num_data_lanes;
0227 
0228         if (use_default_lane_mapping) {
0229             bus->clock_lane = 0;
0230             for (i = 0; i < num_data_lanes; i++)
0231                 bus->data_lanes[i] = dfl_data_lane_index + i;
0232         } else {
0233             bus->clock_lane = clock_lane;
0234             for (i = 0; i < num_data_lanes; i++)
0235                 bus->data_lanes[i] = array[i];
0236         }
0237 
0238         if (have_lane_polarities) {
0239             fwnode_property_read_u32_array(fwnode,
0240                                "lane-polarities", array,
0241                                1 + num_data_lanes);
0242 
0243             for (i = 0; i < 1 + num_data_lanes; i++) {
0244                 bus->lane_polarities[i] = array[i];
0245                 pr_debug("lane %u polarity %sinverted",
0246                      i, array[i] ? "" : "not ");
0247             }
0248         } else {
0249             pr_debug("no lane polarities defined, assuming not inverted\n");
0250         }
0251     }
0252 
0253     return 0;
0254 }
0255 
0256 #define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH |  \
0257                  V4L2_MBUS_HSYNC_ACTIVE_LOW |   \
0258                  V4L2_MBUS_VSYNC_ACTIVE_HIGH |  \
0259                  V4L2_MBUS_VSYNC_ACTIVE_LOW |   \
0260                  V4L2_MBUS_FIELD_EVEN_HIGH |    \
0261                  V4L2_MBUS_FIELD_EVEN_LOW)
0262 
0263 static void
0264 v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode,
0265                     struct v4l2_fwnode_endpoint *vep,
0266                     enum v4l2_mbus_type bus_type)
0267 {
0268     struct v4l2_mbus_config_parallel *bus = &vep->bus.parallel;
0269     unsigned int flags = 0;
0270     u32 v;
0271 
0272     if (bus_type == V4L2_MBUS_PARALLEL || bus_type == V4L2_MBUS_BT656)
0273         flags = bus->flags;
0274 
0275     if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
0276         flags &= ~(V4L2_MBUS_HSYNC_ACTIVE_HIGH |
0277                V4L2_MBUS_HSYNC_ACTIVE_LOW);
0278         flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
0279             V4L2_MBUS_HSYNC_ACTIVE_LOW;
0280         pr_debug("hsync-active %s\n", v ? "high" : "low");
0281     }
0282 
0283     if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
0284         flags &= ~(V4L2_MBUS_VSYNC_ACTIVE_HIGH |
0285                V4L2_MBUS_VSYNC_ACTIVE_LOW);
0286         flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
0287             V4L2_MBUS_VSYNC_ACTIVE_LOW;
0288         pr_debug("vsync-active %s\n", v ? "high" : "low");
0289     }
0290 
0291     if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
0292         flags &= ~(V4L2_MBUS_FIELD_EVEN_HIGH |
0293                V4L2_MBUS_FIELD_EVEN_LOW);
0294         flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
0295             V4L2_MBUS_FIELD_EVEN_LOW;
0296         pr_debug("field-even-active %s\n", v ? "high" : "low");
0297     }
0298 
0299     if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
0300         flags &= ~(V4L2_MBUS_PCLK_SAMPLE_RISING |
0301                V4L2_MBUS_PCLK_SAMPLE_FALLING);
0302         flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
0303             V4L2_MBUS_PCLK_SAMPLE_FALLING;
0304         pr_debug("pclk-sample %s\n", v ? "high" : "low");
0305     }
0306 
0307     if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
0308         flags &= ~(V4L2_MBUS_DATA_ACTIVE_HIGH |
0309                V4L2_MBUS_DATA_ACTIVE_LOW);
0310         flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
0311             V4L2_MBUS_DATA_ACTIVE_LOW;
0312         pr_debug("data-active %s\n", v ? "high" : "low");
0313     }
0314 
0315     if (fwnode_property_present(fwnode, "slave-mode")) {
0316         pr_debug("slave mode\n");
0317         flags &= ~V4L2_MBUS_MASTER;
0318         flags |= V4L2_MBUS_SLAVE;
0319     } else {
0320         flags &= ~V4L2_MBUS_SLAVE;
0321         flags |= V4L2_MBUS_MASTER;
0322     }
0323 
0324     if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
0325         bus->bus_width = v;
0326         pr_debug("bus-width %u\n", v);
0327     }
0328 
0329     if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
0330         bus->data_shift = v;
0331         pr_debug("data-shift %u\n", v);
0332     }
0333 
0334     if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
0335         flags &= ~(V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH |
0336                V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW);
0337         flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
0338             V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
0339         pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
0340     }
0341 
0342     if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
0343         flags &= ~(V4L2_MBUS_DATA_ENABLE_HIGH |
0344                V4L2_MBUS_DATA_ENABLE_LOW);
0345         flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
0346             V4L2_MBUS_DATA_ENABLE_LOW;
0347         pr_debug("data-enable-active %s\n", v ? "high" : "low");
0348     }
0349 
0350     switch (bus_type) {
0351     default:
0352         bus->flags = flags;
0353         if (flags & PARALLEL_MBUS_FLAGS)
0354             vep->bus_type = V4L2_MBUS_PARALLEL;
0355         else
0356             vep->bus_type = V4L2_MBUS_BT656;
0357         break;
0358     case V4L2_MBUS_PARALLEL:
0359         vep->bus_type = V4L2_MBUS_PARALLEL;
0360         bus->flags = flags;
0361         break;
0362     case V4L2_MBUS_BT656:
0363         vep->bus_type = V4L2_MBUS_BT656;
0364         bus->flags = flags & ~PARALLEL_MBUS_FLAGS;
0365         break;
0366     }
0367 }
0368 
0369 static void
0370 v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
0371                     struct v4l2_fwnode_endpoint *vep,
0372                     enum v4l2_mbus_type bus_type)
0373 {
0374     struct v4l2_mbus_config_mipi_csi1 *bus = &vep->bus.mipi_csi1;
0375     u32 v;
0376 
0377     if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
0378         bus->clock_inv = v;
0379         pr_debug("clock-inv %u\n", v);
0380     }
0381 
0382     if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
0383         bus->strobe = v;
0384         pr_debug("strobe %u\n", v);
0385     }
0386 
0387     if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
0388         bus->data_lane = v;
0389         pr_debug("data-lanes %u\n", v);
0390     }
0391 
0392     if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
0393         bus->clock_lane = v;
0394         pr_debug("clock-lanes %u\n", v);
0395     }
0396 
0397     if (bus_type == V4L2_MBUS_CCP2)
0398         vep->bus_type = V4L2_MBUS_CCP2;
0399     else
0400         vep->bus_type = V4L2_MBUS_CSI1;
0401 }
0402 
0403 static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
0404                     struct v4l2_fwnode_endpoint *vep)
0405 {
0406     u32 bus_type = V4L2_FWNODE_BUS_TYPE_GUESS;
0407     enum v4l2_mbus_type mbus_type;
0408     int rval;
0409 
0410     pr_debug("===== begin parsing endpoint %pfw\n", fwnode);
0411 
0412     fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
0413     pr_debug("fwnode video bus type %s (%u), mbus type %s (%u)\n",
0414          v4l2_fwnode_bus_type_to_string(bus_type), bus_type,
0415          v4l2_fwnode_mbus_type_to_string(vep->bus_type),
0416          vep->bus_type);
0417     mbus_type = v4l2_fwnode_bus_type_to_mbus(bus_type);
0418     if (mbus_type == V4L2_MBUS_INVALID) {
0419         pr_debug("unsupported bus type %u\n", bus_type);
0420         return -EINVAL;
0421     }
0422 
0423     if (vep->bus_type != V4L2_MBUS_UNKNOWN) {
0424         if (mbus_type != V4L2_MBUS_UNKNOWN &&
0425             vep->bus_type != mbus_type) {
0426             pr_debug("expecting bus type %s\n",
0427                  v4l2_fwnode_mbus_type_to_string(vep->bus_type));
0428             return -ENXIO;
0429         }
0430     } else {
0431         vep->bus_type = mbus_type;
0432     }
0433 
0434     switch (vep->bus_type) {
0435     case V4L2_MBUS_UNKNOWN:
0436         rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
0437                                V4L2_MBUS_UNKNOWN);
0438         if (rval)
0439             return rval;
0440 
0441         if (vep->bus_type == V4L2_MBUS_UNKNOWN)
0442             v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep,
0443                                 V4L2_MBUS_UNKNOWN);
0444 
0445         pr_debug("assuming media bus type %s (%u)\n",
0446              v4l2_fwnode_mbus_type_to_string(vep->bus_type),
0447              vep->bus_type);
0448 
0449         break;
0450     case V4L2_MBUS_CCP2:
0451     case V4L2_MBUS_CSI1:
0452         v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, vep->bus_type);
0453 
0454         break;
0455     case V4L2_MBUS_CSI2_DPHY:
0456     case V4L2_MBUS_CSI2_CPHY:
0457         rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
0458                                vep->bus_type);
0459         if (rval)
0460             return rval;
0461 
0462         break;
0463     case V4L2_MBUS_PARALLEL:
0464     case V4L2_MBUS_BT656:
0465         v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep,
0466                             vep->bus_type);
0467 
0468         break;
0469     default:
0470         pr_warn("unsupported bus type %u\n", mbus_type);
0471         return -EINVAL;
0472     }
0473 
0474     fwnode_graph_parse_endpoint(fwnode, &vep->base);
0475 
0476     return 0;
0477 }
0478 
0479 int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
0480                    struct v4l2_fwnode_endpoint *vep)
0481 {
0482     int ret;
0483 
0484     ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
0485 
0486     pr_debug("===== end parsing endpoint %pfw\n", fwnode);
0487 
0488     return ret;
0489 }
0490 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
0491 
0492 void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
0493 {
0494     if (IS_ERR_OR_NULL(vep))
0495         return;
0496 
0497     kfree(vep->link_frequencies);
0498     vep->link_frequencies = NULL;
0499 }
0500 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
0501 
0502 int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode,
0503                      struct v4l2_fwnode_endpoint *vep)
0504 {
0505     int rval;
0506 
0507     rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
0508     if (rval < 0)
0509         return rval;
0510 
0511     rval = fwnode_property_count_u64(fwnode, "link-frequencies");
0512     if (rval > 0) {
0513         unsigned int i;
0514 
0515         vep->link_frequencies =
0516             kmalloc_array(rval, sizeof(*vep->link_frequencies),
0517                       GFP_KERNEL);
0518         if (!vep->link_frequencies)
0519             return -ENOMEM;
0520 
0521         vep->nr_of_link_frequencies = rval;
0522 
0523         rval = fwnode_property_read_u64_array(fwnode,
0524                               "link-frequencies",
0525                               vep->link_frequencies,
0526                               vep->nr_of_link_frequencies);
0527         if (rval < 0) {
0528             v4l2_fwnode_endpoint_free(vep);
0529             return rval;
0530         }
0531 
0532         for (i = 0; i < vep->nr_of_link_frequencies; i++)
0533             pr_debug("link-frequencies %u value %llu\n", i,
0534                  vep->link_frequencies[i]);
0535     }
0536 
0537     pr_debug("===== end parsing endpoint %pfw\n", fwnode);
0538 
0539     return 0;
0540 }
0541 EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
0542 
0543 int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,
0544                struct v4l2_fwnode_link *link)
0545 {
0546     struct fwnode_endpoint fwep;
0547 
0548     memset(link, 0, sizeof(*link));
0549 
0550     fwnode_graph_parse_endpoint(fwnode, &fwep);
0551     link->local_id = fwep.id;
0552     link->local_port = fwep.port;
0553     link->local_node = fwnode_graph_get_port_parent(fwnode);
0554 
0555     fwnode = fwnode_graph_get_remote_endpoint(fwnode);
0556     if (!fwnode) {
0557         fwnode_handle_put(fwnode);
0558         return -ENOLINK;
0559     }
0560 
0561     fwnode_graph_parse_endpoint(fwnode, &fwep);
0562     link->remote_id = fwep.id;
0563     link->remote_port = fwep.port;
0564     link->remote_node = fwnode_graph_get_port_parent(fwnode);
0565 
0566     return 0;
0567 }
0568 EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
0569 
0570 void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
0571 {
0572     fwnode_handle_put(link->local_node);
0573     fwnode_handle_put(link->remote_node);
0574 }
0575 EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
0576 
0577 static const struct v4l2_fwnode_connector_conv {
0578     enum v4l2_connector_type type;
0579     const char *compatible;
0580 } connectors[] = {
0581     {
0582         .type = V4L2_CONN_COMPOSITE,
0583         .compatible = "composite-video-connector",
0584     }, {
0585         .type = V4L2_CONN_SVIDEO,
0586         .compatible = "svideo-connector",
0587     },
0588 };
0589 
0590 static enum v4l2_connector_type
0591 v4l2_fwnode_string_to_connector_type(const char *con_str)
0592 {
0593     unsigned int i;
0594 
0595     for (i = 0; i < ARRAY_SIZE(connectors); i++)
0596         if (!strcmp(con_str, connectors[i].compatible))
0597             return connectors[i].type;
0598 
0599     return V4L2_CONN_UNKNOWN;
0600 }
0601 
0602 static void
0603 v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
0604                    struct v4l2_fwnode_connector *vc)
0605 {
0606     u32 stds;
0607     int ret;
0608 
0609     ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
0610 
0611     /* The property is optional. */
0612     vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
0613 }
0614 
0615 void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
0616 {
0617     struct v4l2_connector_link *link, *tmp;
0618 
0619     if (IS_ERR_OR_NULL(connector) || connector->type == V4L2_CONN_UNKNOWN)
0620         return;
0621 
0622     list_for_each_entry_safe(link, tmp, &connector->links, head) {
0623         v4l2_fwnode_put_link(&link->fwnode_link);
0624         list_del(&link->head);
0625         kfree(link);
0626     }
0627 
0628     kfree(connector->label);
0629     connector->label = NULL;
0630     connector->type = V4L2_CONN_UNKNOWN;
0631 }
0632 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
0633 
0634 static enum v4l2_connector_type
0635 v4l2_fwnode_get_connector_type(struct fwnode_handle *fwnode)
0636 {
0637     const char *type_name;
0638     int err;
0639 
0640     if (!fwnode)
0641         return V4L2_CONN_UNKNOWN;
0642 
0643     /* The connector-type is stored within the compatible string. */
0644     err = fwnode_property_read_string(fwnode, "compatible", &type_name);
0645     if (err)
0646         return V4L2_CONN_UNKNOWN;
0647 
0648     return v4l2_fwnode_string_to_connector_type(type_name);
0649 }
0650 
0651 int v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode,
0652                 struct v4l2_fwnode_connector *connector)
0653 {
0654     struct fwnode_handle *connector_node;
0655     enum v4l2_connector_type connector_type;
0656     const char *label;
0657     int err;
0658 
0659     if (!fwnode)
0660         return -EINVAL;
0661 
0662     memset(connector, 0, sizeof(*connector));
0663 
0664     INIT_LIST_HEAD(&connector->links);
0665 
0666     connector_node = fwnode_graph_get_port_parent(fwnode);
0667     connector_type = v4l2_fwnode_get_connector_type(connector_node);
0668     if (connector_type == V4L2_CONN_UNKNOWN) {
0669         fwnode_handle_put(connector_node);
0670         connector_node = fwnode_graph_get_remote_port_parent(fwnode);
0671         connector_type = v4l2_fwnode_get_connector_type(connector_node);
0672     }
0673 
0674     if (connector_type == V4L2_CONN_UNKNOWN) {
0675         pr_err("Unknown connector type\n");
0676         err = -ENOTCONN;
0677         goto out;
0678     }
0679 
0680     connector->type = connector_type;
0681     connector->name = fwnode_get_name(connector_node);
0682     err = fwnode_property_read_string(connector_node, "label", &label);
0683     connector->label = err ? NULL : kstrdup_const(label, GFP_KERNEL);
0684 
0685     /* Parse the connector specific properties. */
0686     switch (connector->type) {
0687     case V4L2_CONN_COMPOSITE:
0688     case V4L2_CONN_SVIDEO:
0689         v4l2_fwnode_connector_parse_analog(connector_node, connector);
0690         break;
0691     /* Avoid compiler warnings */
0692     case V4L2_CONN_UNKNOWN:
0693         break;
0694     }
0695 
0696 out:
0697     fwnode_handle_put(connector_node);
0698 
0699     return err;
0700 }
0701 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse);
0702 
0703 int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode,
0704                    struct v4l2_fwnode_connector *connector)
0705 {
0706     struct fwnode_handle *connector_ep;
0707     struct v4l2_connector_link *link;
0708     int err;
0709 
0710     if (!fwnode || !connector || connector->type == V4L2_CONN_UNKNOWN)
0711         return -EINVAL;
0712 
0713     connector_ep = fwnode_graph_get_remote_endpoint(fwnode);
0714     if (!connector_ep)
0715         return -ENOTCONN;
0716 
0717     link = kzalloc(sizeof(*link), GFP_KERNEL);
0718     if (!link) {
0719         err = -ENOMEM;
0720         goto err;
0721     }
0722 
0723     err = v4l2_fwnode_parse_link(connector_ep, &link->fwnode_link);
0724     if (err)
0725         goto err;
0726 
0727     fwnode_handle_put(connector_ep);
0728 
0729     list_add(&link->head, &connector->links);
0730     connector->nr_of_links++;
0731 
0732     return 0;
0733 
0734 err:
0735     kfree(link);
0736     fwnode_handle_put(connector_ep);
0737 
0738     return err;
0739 }
0740 EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link);
0741 
0742 int v4l2_fwnode_device_parse(struct device *dev,
0743                  struct v4l2_fwnode_device_properties *props)
0744 {
0745     struct fwnode_handle *fwnode = dev_fwnode(dev);
0746     u32 val;
0747     int ret;
0748 
0749     memset(props, 0, sizeof(*props));
0750 
0751     props->orientation = V4L2_FWNODE_PROPERTY_UNSET;
0752     ret = fwnode_property_read_u32(fwnode, "orientation", &val);
0753     if (!ret) {
0754         switch (val) {
0755         case V4L2_FWNODE_ORIENTATION_FRONT:
0756         case V4L2_FWNODE_ORIENTATION_BACK:
0757         case V4L2_FWNODE_ORIENTATION_EXTERNAL:
0758             break;
0759         default:
0760             dev_warn(dev, "Unsupported device orientation: %u\n", val);
0761             return -EINVAL;
0762         }
0763 
0764         props->orientation = val;
0765         dev_dbg(dev, "device orientation: %u\n", val);
0766     }
0767 
0768     props->rotation = V4L2_FWNODE_PROPERTY_UNSET;
0769     ret = fwnode_property_read_u32(fwnode, "rotation", &val);
0770     if (!ret) {
0771         if (val >= 360) {
0772             dev_warn(dev, "Unsupported device rotation: %u\n", val);
0773             return -EINVAL;
0774         }
0775 
0776         props->rotation = val;
0777         dev_dbg(dev, "device rotation: %u\n", val);
0778     }
0779 
0780     return 0;
0781 }
0782 EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse);
0783 
0784 static int
0785 v4l2_async_nf_fwnode_parse_endpoint(struct device *dev,
0786                     struct v4l2_async_notifier *notifier,
0787                     struct fwnode_handle *endpoint,
0788                     unsigned int asd_struct_size,
0789                     parse_endpoint_func parse_endpoint)
0790 {
0791     struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
0792     struct v4l2_async_subdev *asd;
0793     int ret;
0794 
0795     asd = kzalloc(asd_struct_size, GFP_KERNEL);
0796     if (!asd)
0797         return -ENOMEM;
0798 
0799     asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
0800     asd->match.fwnode =
0801         fwnode_graph_get_remote_port_parent(endpoint);
0802     if (!asd->match.fwnode) {
0803         dev_dbg(dev, "no remote endpoint found\n");
0804         ret = -ENOTCONN;
0805         goto out_err;
0806     }
0807 
0808     ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
0809     if (ret) {
0810         dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
0811              ret);
0812         goto out_err;
0813     }
0814 
0815     ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
0816     if (ret == -ENOTCONN)
0817         dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
0818             vep.base.id);
0819     else if (ret < 0)
0820         dev_warn(dev,
0821              "driver could not parse port@%u/endpoint@%u (%d)\n",
0822              vep.base.port, vep.base.id, ret);
0823     v4l2_fwnode_endpoint_free(&vep);
0824     if (ret < 0)
0825         goto out_err;
0826 
0827     ret = __v4l2_async_nf_add_subdev(notifier, asd);
0828     if (ret < 0) {
0829         /* not an error if asd already exists */
0830         if (ret == -EEXIST)
0831             ret = 0;
0832         goto out_err;
0833     }
0834 
0835     return 0;
0836 
0837 out_err:
0838     fwnode_handle_put(asd->match.fwnode);
0839     kfree(asd);
0840 
0841     return ret == -ENOTCONN ? 0 : ret;
0842 }
0843 
0844 int
0845 v4l2_async_nf_parse_fwnode_endpoints(struct device *dev,
0846                      struct v4l2_async_notifier *notifier,
0847                      size_t asd_struct_size,
0848                      parse_endpoint_func parse_endpoint)
0849 {
0850     struct fwnode_handle *fwnode;
0851     int ret = 0;
0852 
0853     if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
0854         return -EINVAL;
0855 
0856     fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
0857         struct fwnode_handle *dev_fwnode;
0858         bool is_available;
0859 
0860         dev_fwnode = fwnode_graph_get_port_parent(fwnode);
0861         is_available = fwnode_device_is_available(dev_fwnode);
0862         fwnode_handle_put(dev_fwnode);
0863         if (!is_available)
0864             continue;
0865 
0866 
0867         ret = v4l2_async_nf_fwnode_parse_endpoint(dev, notifier,
0868                               fwnode,
0869                               asd_struct_size,
0870                               parse_endpoint);
0871         if (ret < 0)
0872             break;
0873     }
0874 
0875     fwnode_handle_put(fwnode);
0876 
0877     return ret;
0878 }
0879 EXPORT_SYMBOL_GPL(v4l2_async_nf_parse_fwnode_endpoints);
0880 
0881 /*
0882  * v4l2_fwnode_reference_parse - parse references for async sub-devices
0883  * @dev: the device node the properties of which are parsed for references
0884  * @notifier: the async notifier where the async subdevs will be added
0885  * @prop: the name of the property
0886  *
0887  * Return: 0 on success
0888  *     -ENOENT if no entries were found
0889  *     -ENOMEM if memory allocation failed
0890  *     -EINVAL if property parsing failed
0891  */
0892 static int v4l2_fwnode_reference_parse(struct device *dev,
0893                        struct v4l2_async_notifier *notifier,
0894                        const char *prop)
0895 {
0896     struct fwnode_reference_args args;
0897     unsigned int index;
0898     int ret;
0899 
0900     for (index = 0;
0901          !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), prop,
0902                             NULL, 0, index, &args));
0903          index++) {
0904         struct v4l2_async_subdev *asd;
0905 
0906         asd = v4l2_async_nf_add_fwnode(notifier, args.fwnode,
0907                            struct v4l2_async_subdev);
0908         fwnode_handle_put(args.fwnode);
0909         if (IS_ERR(asd)) {
0910             /* not an error if asd already exists */
0911             if (PTR_ERR(asd) == -EEXIST)
0912                 continue;
0913 
0914             return PTR_ERR(asd);
0915         }
0916     }
0917 
0918     /* -ENOENT here means successful parsing */
0919     if (ret != -ENOENT)
0920         return ret;
0921 
0922     /* Return -ENOENT if no references were found */
0923     return index ? 0 : -ENOENT;
0924 }
0925 
0926 /*
0927  * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
0928  *                  arguments
0929  * @fwnode: fwnode to read @prop from
0930  * @notifier: notifier for @dev
0931  * @prop: the name of the property
0932  * @index: the index of the reference to get
0933  * @props: the array of integer property names
0934  * @nprops: the number of integer property names in @nprops
0935  *
0936  * First find an fwnode referred to by the reference at @index in @prop.
0937  *
0938  * Then under that fwnode, @nprops times, for each property in @props,
0939  * iteratively follow child nodes starting from fwnode such that they have the
0940  * property in @props array at the index of the child node distance from the
0941  * root node and the value of that property matching with the integer argument
0942  * of the reference, at the same index.
0943  *
0944  * The child fwnode reached at the end of the iteration is then returned to the
0945  * caller.
0946  *
0947  * The core reason for this is that you cannot refer to just any node in ACPI.
0948  * So to refer to an endpoint (easy in DT) you need to refer to a device, then
0949  * provide a list of (property name, property value) tuples where each tuple
0950  * uniquely identifies a child node. The first tuple identifies a child directly
0951  * underneath the device fwnode, the next tuple identifies a child node
0952  * underneath the fwnode identified by the previous tuple, etc. until you
0953  * reached the fwnode you need.
0954  *
0955  * THIS EXAMPLE EXISTS MERELY TO DOCUMENT THIS FUNCTION. DO NOT USE IT AS A
0956  * REFERENCE IN HOW ACPI TABLES SHOULD BE WRITTEN!! See documentation under
0957  * Documentation/firmware-guide/acpi/dsd/ instead and especially graph.txt,
0958  * data-node-references.txt and leds.txt .
0959  *
0960  *  Scope (\_SB.PCI0.I2C2)
0961  *  {
0962  *      Device (CAM0)
0963  *      {
0964  *          Name (_DSD, Package () {
0965  *              ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
0966  *              Package () {
0967  *                  Package () {
0968  *                      "compatible",
0969  *                      Package () { "nokia,smia" }
0970  *                  },
0971  *              },
0972  *              ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
0973  *              Package () {
0974  *                  Package () { "port0", "PRT0" },
0975  *              }
0976  *          })
0977  *          Name (PRT0, Package() {
0978  *              ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
0979  *              Package () {
0980  *                  Package () { "port", 0 },
0981  *              },
0982  *              ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
0983  *              Package () {
0984  *                  Package () { "endpoint0", "EP00" },
0985  *              }
0986  *          })
0987  *          Name (EP00, Package() {
0988  *              ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
0989  *              Package () {
0990  *                  Package () { "endpoint", 0 },
0991  *                  Package () {
0992  *                      "remote-endpoint",
0993  *                      Package() {
0994  *                          \_SB.PCI0.ISP, 4, 0
0995  *                      }
0996  *                  },
0997  *              }
0998  *          })
0999  *      }
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>");