1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/acpi.h>
21#include <linux/kernel.h>
22#include <linux/mm.h>
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
30#include <media/v4l2-async.h>
31#include <media/v4l2-fwnode.h>
32#include <media/v4l2-subdev.h>
33
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,
39 NR_OF_V4L2_FWNODE_BUS_TYPE,
40};
41
42static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
43 struct v4l2_fwnode_endpoint *vep)
44{
45 struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2;
46 bool have_clk_lane = false;
47 unsigned int flags = 0, lanes_used = 0;
48 unsigned int i;
49 u32 v;
50 int rval;
51
52 rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
53 if (rval > 0) {
54 u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES];
55
56 bus->num_data_lanes =
57 min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
58
59 fwnode_property_read_u32_array(fwnode, "data-lanes", array,
60 bus->num_data_lanes);
61
62 for (i = 0; i < bus->num_data_lanes; i++) {
63 if (lanes_used & BIT(array[i]))
64 pr_warn("duplicated lane %u in data-lanes\n",
65 array[i]);
66 lanes_used |= BIT(array[i]);
67
68 bus->data_lanes[i] = array[i];
69 }
70
71 rval = fwnode_property_read_u32_array(fwnode,
72 "lane-polarities", NULL,
73 0);
74 if (rval > 0) {
75 if (rval != 1 + bus->num_data_lanes ) {
76 pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
77 1 + bus->num_data_lanes, rval);
78 return -EINVAL;
79 }
80
81 fwnode_property_read_u32_array(fwnode,
82 "lane-polarities", array,
83 1 + bus->num_data_lanes);
84
85 for (i = 0; i < 1 + bus->num_data_lanes; i++)
86 bus->lane_polarities[i] = array[i];
87 }
88
89 }
90
91 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
92 if (lanes_used & BIT(v))
93 pr_warn("duplicated lane %u in clock-lanes\n", v);
94 lanes_used |= BIT(v);
95
96 bus->clock_lane = v;
97 have_clk_lane = true;
98 }
99
100 if (fwnode_property_present(fwnode, "clock-noncontinuous"))
101 flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
102 else if (have_clk_lane || bus->num_data_lanes > 0)
103 flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
104
105 bus->flags = flags;
106 vep->bus_type = V4L2_MBUS_CSI2;
107
108 return 0;
109}
110
111static void v4l2_fwnode_endpoint_parse_parallel_bus(
112 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
113{
114 struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
115 unsigned int flags = 0;
116 u32 v;
117
118 if (!fwnode_property_read_u32(fwnode, "hsync-active", &v))
119 flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
120 V4L2_MBUS_HSYNC_ACTIVE_LOW;
121
122 if (!fwnode_property_read_u32(fwnode, "vsync-active", &v))
123 flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
124 V4L2_MBUS_VSYNC_ACTIVE_LOW;
125
126 if (!fwnode_property_read_u32(fwnode, "field-even-active", &v))
127 flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
128 V4L2_MBUS_FIELD_EVEN_LOW;
129 if (flags)
130 vep->bus_type = V4L2_MBUS_PARALLEL;
131 else
132 vep->bus_type = V4L2_MBUS_BT656;
133
134 if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v))
135 flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
136 V4L2_MBUS_PCLK_SAMPLE_FALLING;
137
138 if (!fwnode_property_read_u32(fwnode, "data-active", &v))
139 flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
140 V4L2_MBUS_DATA_ACTIVE_LOW;
141
142 if (fwnode_property_present(fwnode, "slave-mode"))
143 flags |= V4L2_MBUS_SLAVE;
144 else
145 flags |= V4L2_MBUS_MASTER;
146
147 if (!fwnode_property_read_u32(fwnode, "bus-width", &v))
148 bus->bus_width = v;
149
150 if (!fwnode_property_read_u32(fwnode, "data-shift", &v))
151 bus->data_shift = v;
152
153 if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v))
154 flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
155 V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
156
157 if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v))
158 flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
159 V4L2_MBUS_DATA_ENABLE_LOW;
160
161 bus->flags = flags;
162
163}
164
165static void
166v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
167 struct v4l2_fwnode_endpoint *vep,
168 u32 bus_type)
169{
170 struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
171 u32 v;
172
173 if (!fwnode_property_read_u32(fwnode, "clock-inv", &v))
174 bus->clock_inv = v;
175
176 if (!fwnode_property_read_u32(fwnode, "strobe", &v))
177 bus->strobe = v;
178
179 if (!fwnode_property_read_u32(fwnode, "data-lanes", &v))
180 bus->data_lane = v;
181
182 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v))
183 bus->clock_lane = v;
184
185 if (bus_type == V4L2_FWNODE_BUS_TYPE_CCP2)
186 vep->bus_type = V4L2_MBUS_CCP2;
187 else
188 vep->bus_type = V4L2_MBUS_CSI1;
189}
190
191int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
192 struct v4l2_fwnode_endpoint *vep)
193{
194 u32 bus_type = 0;
195 int rval;
196
197 fwnode_graph_parse_endpoint(fwnode, &vep->base);
198
199
200 memset(&vep->bus_type, 0, sizeof(*vep) -
201 offsetof(typeof(*vep), bus_type));
202
203 fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
204
205 switch (bus_type) {
206 case V4L2_FWNODE_BUS_TYPE_GUESS:
207 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep);
208 if (rval)
209 return rval;
210
211
212
213
214 if (vep->bus.mipi_csi2.flags == 0)
215 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep);
216
217 return 0;
218 case V4L2_FWNODE_BUS_TYPE_CCP2:
219 case V4L2_FWNODE_BUS_TYPE_CSI1:
220 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, bus_type);
221
222 return 0;
223 default:
224 pr_warn("unsupported bus type %u\n", bus_type);
225 return -EINVAL;
226 }
227}
228EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
229
230void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
231{
232 if (IS_ERR_OR_NULL(vep))
233 return;
234
235 kfree(vep->link_frequencies);
236 kfree(vep);
237}
238EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
239
240struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse(
241 struct fwnode_handle *fwnode)
242{
243 struct v4l2_fwnode_endpoint *vep;
244 int rval;
245
246 vep = kzalloc(sizeof(*vep), GFP_KERNEL);
247 if (!vep)
248 return ERR_PTR(-ENOMEM);
249
250 rval = v4l2_fwnode_endpoint_parse(fwnode, vep);
251 if (rval < 0)
252 goto out_err;
253
254 rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
255 NULL, 0);
256 if (rval > 0) {
257 vep->link_frequencies =
258 kmalloc_array(rval, sizeof(*vep->link_frequencies),
259 GFP_KERNEL);
260 if (!vep->link_frequencies) {
261 rval = -ENOMEM;
262 goto out_err;
263 }
264
265 vep->nr_of_link_frequencies = rval;
266
267 rval = fwnode_property_read_u64_array(
268 fwnode, "link-frequencies", vep->link_frequencies,
269 vep->nr_of_link_frequencies);
270 if (rval < 0)
271 goto out_err;
272 }
273
274 return vep;
275
276out_err:
277 v4l2_fwnode_endpoint_free(vep);
278 return ERR_PTR(rval);
279}
280EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
281
282int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
283 struct v4l2_fwnode_link *link)
284{
285 const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
286 struct fwnode_handle *fwnode;
287
288 memset(link, 0, sizeof(*link));
289
290 fwnode = fwnode_get_parent(__fwnode);
291 fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
292 fwnode = fwnode_get_next_parent(fwnode);
293 if (is_of_node(fwnode) &&
294 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
295 fwnode = fwnode_get_next_parent(fwnode);
296 link->local_node = fwnode;
297
298 fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
299 if (!fwnode) {
300 fwnode_handle_put(fwnode);
301 return -ENOLINK;
302 }
303
304 fwnode = fwnode_get_parent(fwnode);
305 fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
306 fwnode = fwnode_get_next_parent(fwnode);
307 if (is_of_node(fwnode) &&
308 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
309 fwnode = fwnode_get_next_parent(fwnode);
310 link->remote_node = fwnode;
311
312 return 0;
313}
314EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
315
316void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
317{
318 fwnode_handle_put(link->local_node);
319 fwnode_handle_put(link->remote_node);
320}
321EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
322
323static int v4l2_async_notifier_realloc(struct v4l2_async_notifier *notifier,
324 unsigned int max_subdevs)
325{
326 struct v4l2_async_subdev **subdevs;
327
328 if (max_subdevs <= notifier->max_subdevs)
329 return 0;
330
331 subdevs = kvmalloc_array(
332 max_subdevs, sizeof(*notifier->subdevs),
333 GFP_KERNEL | __GFP_ZERO);
334 if (!subdevs)
335 return -ENOMEM;
336
337 if (notifier->subdevs) {
338 memcpy(subdevs, notifier->subdevs,
339 sizeof(*subdevs) * notifier->num_subdevs);
340
341 kvfree(notifier->subdevs);
342 }
343
344 notifier->subdevs = subdevs;
345 notifier->max_subdevs = max_subdevs;
346
347 return 0;
348}
349
350static int v4l2_async_notifier_fwnode_parse_endpoint(
351 struct device *dev, struct v4l2_async_notifier *notifier,
352 struct fwnode_handle *endpoint, unsigned int asd_struct_size,
353 int (*parse_endpoint)(struct device *dev,
354 struct v4l2_fwnode_endpoint *vep,
355 struct v4l2_async_subdev *asd))
356{
357 struct v4l2_async_subdev *asd;
358 struct v4l2_fwnode_endpoint *vep;
359 int ret = 0;
360
361 asd = kzalloc(asd_struct_size, GFP_KERNEL);
362 if (!asd)
363 return -ENOMEM;
364
365 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
366 asd->match.fwnode =
367 fwnode_graph_get_remote_port_parent(endpoint);
368 if (!asd->match.fwnode) {
369 dev_warn(dev, "bad remote port parent\n");
370 ret = -EINVAL;
371 goto out_err;
372 }
373
374 vep = v4l2_fwnode_endpoint_alloc_parse(endpoint);
375 if (IS_ERR(vep)) {
376 ret = PTR_ERR(vep);
377 dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
378 ret);
379 goto out_err;
380 }
381
382 ret = parse_endpoint ? parse_endpoint(dev, vep, asd) : 0;
383 if (ret == -ENOTCONN)
384 dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep->base.port,
385 vep->base.id);
386 else if (ret < 0)
387 dev_warn(dev,
388 "driver could not parse port@%u/endpoint@%u (%d)\n",
389 vep->base.port, vep->base.id, ret);
390 v4l2_fwnode_endpoint_free(vep);
391 if (ret < 0)
392 goto out_err;
393
394 notifier->subdevs[notifier->num_subdevs] = asd;
395 notifier->num_subdevs++;
396
397 return 0;
398
399out_err:
400 fwnode_handle_put(asd->match.fwnode);
401 kfree(asd);
402
403 return ret == -ENOTCONN ? 0 : ret;
404}
405
406static int __v4l2_async_notifier_parse_fwnode_endpoints(
407 struct device *dev, struct v4l2_async_notifier *notifier,
408 size_t asd_struct_size, unsigned int port, bool has_port,
409 int (*parse_endpoint)(struct device *dev,
410 struct v4l2_fwnode_endpoint *vep,
411 struct v4l2_async_subdev *asd))
412{
413 struct fwnode_handle *fwnode;
414 unsigned int max_subdevs = notifier->max_subdevs;
415 int ret;
416
417 if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
418 return -EINVAL;
419
420 for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
421 dev_fwnode(dev), fwnode)); ) {
422 struct fwnode_handle *dev_fwnode;
423 bool is_available;
424
425 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
426 is_available = fwnode_device_is_available(dev_fwnode);
427 fwnode_handle_put(dev_fwnode);
428 if (!is_available)
429 continue;
430
431 if (has_port) {
432 struct fwnode_endpoint ep;
433
434 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
435 if (ret) {
436 fwnode_handle_put(fwnode);
437 return ret;
438 }
439
440 if (ep.port != port)
441 continue;
442 }
443 max_subdevs++;
444 }
445
446
447 if (max_subdevs == notifier->max_subdevs)
448 return 0;
449
450 ret = v4l2_async_notifier_realloc(notifier, max_subdevs);
451 if (ret)
452 return ret;
453
454 for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
455 dev_fwnode(dev), fwnode)); ) {
456 struct fwnode_handle *dev_fwnode;
457 bool is_available;
458
459 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
460 is_available = fwnode_device_is_available(dev_fwnode);
461 fwnode_handle_put(dev_fwnode);
462 if (!is_available)
463 continue;
464
465 if (has_port) {
466 struct fwnode_endpoint ep;
467
468 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
469 if (ret)
470 break;
471
472 if (ep.port != port)
473 continue;
474 }
475
476 if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) {
477 ret = -EINVAL;
478 break;
479 }
480
481 ret = v4l2_async_notifier_fwnode_parse_endpoint(
482 dev, notifier, fwnode, asd_struct_size, parse_endpoint);
483 if (ret < 0)
484 break;
485 }
486
487 fwnode_handle_put(fwnode);
488
489 return ret;
490}
491
492int v4l2_async_notifier_parse_fwnode_endpoints(
493 struct device *dev, struct v4l2_async_notifier *notifier,
494 size_t asd_struct_size,
495 int (*parse_endpoint)(struct device *dev,
496 struct v4l2_fwnode_endpoint *vep,
497 struct v4l2_async_subdev *asd))
498{
499 return __v4l2_async_notifier_parse_fwnode_endpoints(
500 dev, notifier, asd_struct_size, 0, false, parse_endpoint);
501}
502EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
503
504int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
505 struct device *dev, struct v4l2_async_notifier *notifier,
506 size_t asd_struct_size, unsigned int port,
507 int (*parse_endpoint)(struct device *dev,
508 struct v4l2_fwnode_endpoint *vep,
509 struct v4l2_async_subdev *asd))
510{
511 return __v4l2_async_notifier_parse_fwnode_endpoints(
512 dev, notifier, asd_struct_size, port, true, parse_endpoint);
513}
514EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
515
516
517
518
519
520
521
522
523
524
525
526
527static int v4l2_fwnode_reference_parse(
528 struct device *dev, struct v4l2_async_notifier *notifier,
529 const char *prop)
530{
531 struct fwnode_reference_args args;
532 unsigned int index;
533 int ret;
534
535 for (index = 0;
536 !(ret = fwnode_property_get_reference_args(
537 dev_fwnode(dev), prop, NULL, 0, index, &args));
538 index++)
539 fwnode_handle_put(args.fwnode);
540
541 if (!index)
542 return -ENOENT;
543
544
545
546
547
548 if (ret != -ENOENT && ret != -ENODATA)
549 return ret;
550
551 ret = v4l2_async_notifier_realloc(notifier,
552 notifier->num_subdevs + index);
553 if (ret)
554 return ret;
555
556 for (index = 0; !fwnode_property_get_reference_args(
557 dev_fwnode(dev), prop, NULL, 0, index, &args);
558 index++) {
559 struct v4l2_async_subdev *asd;
560
561 if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) {
562 ret = -EINVAL;
563 goto error;
564 }
565
566 asd = kzalloc(sizeof(*asd), GFP_KERNEL);
567 if (!asd) {
568 ret = -ENOMEM;
569 goto error;
570 }
571
572 notifier->subdevs[notifier->num_subdevs] = asd;
573 asd->match.fwnode = args.fwnode;
574 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
575 notifier->num_subdevs++;
576 }
577
578 return 0;
579
580error:
581 fwnode_handle_put(args.fwnode);
582 return ret;
583}
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop(
742 struct fwnode_handle *fwnode, const char *prop, unsigned int index,
743 const char * const *props, unsigned int nprops)
744{
745 struct fwnode_reference_args fwnode_args;
746 u64 *args = fwnode_args.args;
747 struct fwnode_handle *child;
748 int ret;
749
750
751
752
753
754
755
756 ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
757 index, &fwnode_args);
758 if (ret)
759 return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
760
761
762
763
764
765 fwnode = fwnode_args.fwnode;
766 while (nprops--) {
767 u32 val;
768
769
770 fwnode_for_each_child_node(fwnode, child) {
771 if (fwnode_property_read_u32(child, *props, &val))
772 continue;
773
774
775 if (val == *args)
776 break;
777 }
778
779 fwnode_handle_put(fwnode);
780
781
782 if (!child) {
783 fwnode = ERR_PTR(-ENOENT);
784 break;
785 }
786
787 props++;
788 args++;
789 fwnode = child;
790 }
791
792 return fwnode;
793}
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818static int v4l2_fwnode_reference_parse_int_props(
819 struct device *dev, struct v4l2_async_notifier *notifier,
820 const char *prop, const char * const *props, unsigned int nprops)
821{
822 struct fwnode_handle *fwnode;
823 unsigned int index;
824 int ret;
825
826 index = 0;
827 do {
828 fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
829 prop, index,
830 props, nprops);
831 if (IS_ERR(fwnode)) {
832
833
834
835
836
837 if (PTR_ERR(fwnode) != -ENOENT &&
838 PTR_ERR(fwnode) != -ENODATA)
839 return PTR_ERR(fwnode);
840 break;
841 }
842 fwnode_handle_put(fwnode);
843 index++;
844 } while (1);
845
846 ret = v4l2_async_notifier_realloc(notifier,
847 notifier->num_subdevs + index);
848 if (ret)
849 return -ENOMEM;
850
851 for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
852 dev_fwnode(dev), prop, index, props,
853 nprops))); index++) {
854 struct v4l2_async_subdev *asd;
855
856 if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) {
857 ret = -EINVAL;
858 goto error;
859 }
860
861 asd = kzalloc(sizeof(struct v4l2_async_subdev), GFP_KERNEL);
862 if (!asd) {
863 ret = -ENOMEM;
864 goto error;
865 }
866
867 notifier->subdevs[notifier->num_subdevs] = asd;
868 asd->match.fwnode = fwnode;
869 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
870 notifier->num_subdevs++;
871 }
872
873 return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
874
875error:
876 fwnode_handle_put(fwnode);
877 return ret;
878}
879
880int v4l2_async_notifier_parse_fwnode_sensor_common(
881 struct device *dev, struct v4l2_async_notifier *notifier)
882{
883 static const char * const led_props[] = { "led" };
884 static const struct {
885 const char *name;
886 const char * const *props;
887 unsigned int nprops;
888 } props[] = {
889 { "flash-leds", led_props, ARRAY_SIZE(led_props) },
890 { "lens-focus", NULL, 0 },
891 };
892 unsigned int i;
893
894 for (i = 0; i < ARRAY_SIZE(props); i++) {
895 int ret;
896
897 if (props[i].props && is_acpi_node(dev_fwnode(dev)))
898 ret = v4l2_fwnode_reference_parse_int_props(
899 dev, notifier, props[i].name,
900 props[i].props, props[i].nprops);
901 else
902 ret = v4l2_fwnode_reference_parse(
903 dev, notifier, props[i].name);
904 if (ret && ret != -ENOENT) {
905 dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
906 props[i].name, ret);
907 return ret;
908 }
909 }
910
911 return 0;
912}
913EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common);
914
915int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd)
916{
917 struct v4l2_async_notifier *notifier;
918 int ret;
919
920 if (WARN_ON(!sd->dev))
921 return -ENODEV;
922
923 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
924 if (!notifier)
925 return -ENOMEM;
926
927 ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev,
928 notifier);
929 if (ret < 0)
930 goto out_cleanup;
931
932 ret = v4l2_async_subdev_notifier_register(sd, notifier);
933 if (ret < 0)
934 goto out_cleanup;
935
936 ret = v4l2_async_register_subdev(sd);
937 if (ret < 0)
938 goto out_unregister;
939
940 sd->subdev_notifier = notifier;
941
942 return 0;
943
944out_unregister:
945 v4l2_async_notifier_unregister(notifier);
946
947out_cleanup:
948 v4l2_async_notifier_cleanup(notifier);
949 kfree(notifier);
950
951 return ret;
952}
953EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common);
954
955MODULE_LICENSE("GPL");
956MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
957MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
958MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
959