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