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