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