blob: 2d0d2facf20fed7e0879cd5b4127877387304fc0 [file] [log] [blame]
Sakari Ailusca50c192016-08-12 08:05:51 -03001/*
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>
Sakari Ailus9ca46532017-08-17 11:28:21 -040022#include <linux/mm.h>
Sakari Ailusca50c192016-08-12 08:05:51 -030023#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
Sakari Ailus9ca46532017-08-17 11:28:21 -040030#include <media/v4l2-async.h>
Sakari Ailusca50c192016-08-12 08:05:51 -030031#include <media/v4l2-fwnode.h>
Sakari Ailusaef69d52017-09-24 18:47:44 -040032#include <media/v4l2-subdev.h>
Sakari Ailusca50c192016-08-12 08:05:51 -030033
Sakari Ailuse07a41f2015-02-25 14:51:01 -050034enum 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,
Sakari Ailusbf638562018-07-03 17:40:37 -040039 V4L2_FWNODE_BUS_TYPE_CSI2_DPHY,
40 V4L2_FWNODE_BUS_TYPE_PARALLEL,
41 V4L2_FWNODE_BUS_TYPE_BT656,
Sakari Ailuse07a41f2015-02-25 14:51:01 -050042 NR_OF_V4L2_FWNODE_BUS_TYPE,
43};
44
Sakari Ailusf3112732017-02-20 05:42:09 -050045static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
46 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -030047{
48 struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2;
49 bool have_clk_lane = false;
50 unsigned int flags = 0, lanes_used = 0;
51 unsigned int i;
52 u32 v;
53 int rval;
54
55 rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
56 if (rval > 0) {
Sakari Ailusad3cdf32017-08-14 06:43:07 -040057 u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES];
Sakari Ailusca50c192016-08-12 08:05:51 -030058
Sakari Ailusad3cdf32017-08-14 06:43:07 -040059 bus->num_data_lanes =
60 min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
Sakari Ailusca50c192016-08-12 08:05:51 -030061
62 fwnode_property_read_u32_array(fwnode, "data-lanes", array,
63 bus->num_data_lanes);
64
65 for (i = 0; i < bus->num_data_lanes; i++) {
66 if (lanes_used & BIT(array[i]))
67 pr_warn("duplicated lane %u in data-lanes\n",
68 array[i]);
69 lanes_used |= BIT(array[i]);
70
71 bus->data_lanes[i] = array[i];
Sakari Ailusc8677aa2017-12-04 16:25:06 -050072 pr_debug("lane %u position %u\n", i, array[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -030073 }
Sakari Ailusca50c192016-08-12 08:05:51 -030074
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040075 rval = fwnode_property_read_u32_array(fwnode,
Sakari Ailusb24f0212017-08-14 06:15:21 -040076 "lane-polarities", NULL,
77 0);
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040078 if (rval > 0) {
Sakari Ailusb24f0212017-08-14 06:15:21 -040079 if (rval != 1 + bus->num_data_lanes /* clock+data */) {
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040080 pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
81 1 + bus->num_data_lanes, rval);
82 return -EINVAL;
83 }
Sakari Ailusca50c192016-08-12 08:05:51 -030084
Sakari Ailusb24f0212017-08-14 06:15:21 -040085 fwnode_property_read_u32_array(fwnode,
86 "lane-polarities", array,
87 1 + bus->num_data_lanes);
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040088
Sakari Ailusc8677aa2017-12-04 16:25:06 -050089 for (i = 0; i < 1 + bus->num_data_lanes; i++) {
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040090 bus->lane_polarities[i] = array[i];
Sakari Ailusc8677aa2017-12-04 16:25:06 -050091 pr_debug("lane %u polarity %sinverted",
92 i, array[i] ? "" : "not ");
93 }
94 } else {
95 pr_debug("no lane polarities defined, assuming not inverted\n");
Sakari Ailusca50c192016-08-12 08:05:51 -030096 }
Sakari Ailusb24f0212017-08-14 06:15:21 -040097
Sakari Ailusca50c192016-08-12 08:05:51 -030098 }
99
100 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
101 if (lanes_used & BIT(v))
102 pr_warn("duplicated lane %u in clock-lanes\n", v);
103 lanes_used |= BIT(v);
104
105 bus->clock_lane = v;
106 have_clk_lane = true;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500107 pr_debug("clock lane position %u\n", v);
Sakari Ailusca50c192016-08-12 08:05:51 -0300108 }
109
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500110 if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300111 flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500112 pr_debug("non-continuous clock\n");
Sakari Ailusd4865322018-07-20 05:04:29 -0400113 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300114 flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500115 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300116
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400117 if (lanes_used || have_clk_lane ||
118 (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) {
119 bus->flags = flags;
120 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
121 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300122
123 return 0;
124}
125
Sakari Ailus175b18b2018-01-02 08:14:30 -0500126#define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
127 V4L2_MBUS_HSYNC_ACTIVE_LOW | \
128 V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
129 V4L2_MBUS_VSYNC_ACTIVE_LOW | \
130 V4L2_MBUS_FIELD_EVEN_HIGH | \
131 V4L2_MBUS_FIELD_EVEN_LOW)
132
Sakari Ailusca50c192016-08-12 08:05:51 -0300133static void v4l2_fwnode_endpoint_parse_parallel_bus(
Sakari Ailus175b18b2018-01-02 08:14:30 -0500134 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep,
135 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailusca50c192016-08-12 08:05:51 -0300136{
137 struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
138 unsigned int flags = 0;
139 u32 v;
140
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500141 if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300142 flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
143 V4L2_MBUS_HSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500144 pr_debug("hsync-active %s\n", v ? "high" : "low");
145 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300146
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500147 if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300148 flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
149 V4L2_MBUS_VSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500150 pr_debug("vsync-active %s\n", v ? "high" : "low");
151 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300152
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500153 if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300154 flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
155 V4L2_MBUS_FIELD_EVEN_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500156 pr_debug("field-even-active %s\n", v ? "high" : "low");
157 }
158
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500159 if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300160 flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
161 V4L2_MBUS_PCLK_SAMPLE_FALLING;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500162 pr_debug("pclk-sample %s\n", v ? "high" : "low");
163 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300164
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500165 if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300166 flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
167 V4L2_MBUS_DATA_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500168 pr_debug("data-active %s\n", v ? "high" : "low");
169 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300170
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500171 if (fwnode_property_present(fwnode, "slave-mode")) {
172 pr_debug("slave mode\n");
Sakari Ailusca50c192016-08-12 08:05:51 -0300173 flags |= V4L2_MBUS_SLAVE;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500174 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300175 flags |= V4L2_MBUS_MASTER;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500176 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300177
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500178 if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300179 bus->bus_width = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500180 pr_debug("bus-width %u\n", v);
181 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300182
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500183 if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300184 bus->data_shift = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500185 pr_debug("data-shift %u\n", v);
186 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300187
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500188 if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300189 flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
190 V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500191 pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
192 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300193
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500194 if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400195 flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
196 V4L2_MBUS_DATA_ENABLE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500197 pr_debug("data-enable-active %s\n", v ? "high" : "low");
198 }
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400199
Sakari Ailus175b18b2018-01-02 08:14:30 -0500200 switch (bus_type) {
201 default:
202 bus->flags = flags;
203 if (flags & PARALLEL_MBUS_FLAGS)
204 vep->bus_type = V4L2_MBUS_PARALLEL;
205 else
206 vep->bus_type = V4L2_MBUS_BT656;
207 break;
208 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400209 vep->bus_type = V4L2_MBUS_PARALLEL;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500210 bus->flags = flags;
211 break;
212 case V4L2_FWNODE_BUS_TYPE_BT656:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400213 vep->bus_type = V4L2_MBUS_BT656;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500214 bus->flags = flags & ~PARALLEL_MBUS_FLAGS;
215 break;
216 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300217}
218
Mauro Carvalho Chehababc5b2c2017-07-20 16:27:27 -0400219static void
220v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
221 struct v4l2_fwnode_endpoint *vep,
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400222 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500223{
224 struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
225 u32 v;
226
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500227 if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500228 bus->clock_inv = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500229 pr_debug("clock-inv %u\n", v);
230 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500231
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500232 if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500233 bus->strobe = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500234 pr_debug("strobe %u\n", v);
235 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500236
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500237 if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500238 bus->data_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500239 pr_debug("data-lanes %u\n", v);
240 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500241
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500242 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500243 bus->clock_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500244 pr_debug("clock-lanes %u\n", v);
245 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500246
247 if (bus_type == V4L2_FWNODE_BUS_TYPE_CCP2)
248 vep->bus_type = V4L2_MBUS_CCP2;
249 else
250 vep->bus_type = V4L2_MBUS_CSI1;
251}
252
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500253static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
254 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300255{
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500256 u32 bus_type = 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300257 int rval;
258
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500259 pr_debug("===== begin V4L2 endpoint properties\n");
260
Sakari Ailusca50c192016-08-12 08:05:51 -0300261 fwnode_graph_parse_endpoint(fwnode, &vep->base);
262
263 /* Zero fields from bus_type to until the end */
264 memset(&vep->bus_type, 0, sizeof(*vep) -
265 offsetof(typeof(*vep), bus_type));
266
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500267 fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
268
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500269 switch (bus_type) {
270 case V4L2_FWNODE_BUS_TYPE_GUESS:
271 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep);
272 if (rval)
273 return rval;
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400274
275 if (vep->bus_type == V4L2_MBUS_UNKNOWN)
Sakari Ailus175b18b2018-01-02 08:14:30 -0500276 v4l2_fwnode_endpoint_parse_parallel_bus(
277 fwnode, vep, V4L2_MBUS_UNKNOWN);
Sakari Ailusca50c192016-08-12 08:05:51 -0300278
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500279 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500280 case V4L2_FWNODE_BUS_TYPE_CCP2:
281 case V4L2_FWNODE_BUS_TYPE_CSI1:
282 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, bus_type);
283
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500284 break;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500285 case V4L2_FWNODE_BUS_TYPE_CSI2_DPHY:
286 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
287 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep);
288 if (rval)
289 return rval;
290
291 break;
292 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
293 case V4L2_FWNODE_BUS_TYPE_BT656:
294 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep, bus_type);
295
296 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500297 default:
298 pr_warn("unsupported bus type %u\n", bus_type);
299 return -EINVAL;
300 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500301
302 return 0;
303}
304
305int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
306 struct v4l2_fwnode_endpoint *vep)
307{
308 int ret;
309
310 ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
311
312 pr_debug("===== end V4L2 endpoint properties\n");
313
314 return ret;
Sakari Ailusca50c192016-08-12 08:05:51 -0300315}
316EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
317
Sakari Ailusca50c192016-08-12 08:05:51 -0300318void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
319{
320 if (IS_ERR_OR_NULL(vep))
321 return;
322
323 kfree(vep->link_frequencies);
Sakari Ailusca50c192016-08-12 08:05:51 -0300324}
325EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
326
Sakari Ailus6970d372018-06-02 12:19:35 -0400327int v4l2_fwnode_endpoint_alloc_parse(
328 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300329{
Sakari Ailusca50c192016-08-12 08:05:51 -0300330 int rval;
331
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500332 rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
Sakari Ailusca50c192016-08-12 08:05:51 -0300333 if (rval < 0)
Sakari Ailus6970d372018-06-02 12:19:35 -0400334 return rval;
Sakari Ailusca50c192016-08-12 08:05:51 -0300335
336 rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
337 NULL, 0);
Sakari Ailus06f81522017-06-20 09:14:43 -0400338 if (rval > 0) {
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500339 unsigned int i;
340
Sakari Ailus06f81522017-06-20 09:14:43 -0400341 vep->link_frequencies =
342 kmalloc_array(rval, sizeof(*vep->link_frequencies),
343 GFP_KERNEL);
Sakari Ailus6970d372018-06-02 12:19:35 -0400344 if (!vep->link_frequencies)
345 return -ENOMEM;
Sakari Ailusca50c192016-08-12 08:05:51 -0300346
Sakari Ailus06f81522017-06-20 09:14:43 -0400347 vep->nr_of_link_frequencies = rval;
348
349 rval = fwnode_property_read_u64_array(
350 fwnode, "link-frequencies", vep->link_frequencies,
351 vep->nr_of_link_frequencies);
Sakari Ailus6970d372018-06-02 12:19:35 -0400352 if (rval < 0) {
353 v4l2_fwnode_endpoint_free(vep);
354 return rval;
355 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500356
357 for (i = 0; i < vep->nr_of_link_frequencies; i++)
358 pr_info("link-frequencies %u value %llu\n", i,
359 vep->link_frequencies[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -0300360 }
361
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500362 pr_debug("===== end V4L2 endpoint properties\n");
363
Sakari Ailus6970d372018-06-02 12:19:35 -0400364 return 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300365}
366EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
367
Sakari Ailusca50c192016-08-12 08:05:51 -0300368int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
369 struct v4l2_fwnode_link *link)
370{
371 const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
372 struct fwnode_handle *fwnode;
373
374 memset(link, 0, sizeof(*link));
375
376 fwnode = fwnode_get_parent(__fwnode);
377 fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
378 fwnode = fwnode_get_next_parent(fwnode);
379 if (is_of_node(fwnode) &&
380 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
381 fwnode = fwnode_get_next_parent(fwnode);
382 link->local_node = fwnode;
383
384 fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
385 if (!fwnode) {
386 fwnode_handle_put(fwnode);
387 return -ENOLINK;
388 }
389
390 fwnode = fwnode_get_parent(fwnode);
391 fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
392 fwnode = fwnode_get_next_parent(fwnode);
393 if (is_of_node(fwnode) &&
394 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
395 fwnode = fwnode_get_next_parent(fwnode);
396 link->remote_node = fwnode;
397
398 return 0;
399}
400EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
401
Sakari Ailusca50c192016-08-12 08:05:51 -0300402void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
403{
404 fwnode_handle_put(link->local_node);
405 fwnode_handle_put(link->remote_node);
406}
407EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
408
Sakari Ailus9ca46532017-08-17 11:28:21 -0400409static int v4l2_async_notifier_fwnode_parse_endpoint(
410 struct device *dev, struct v4l2_async_notifier *notifier,
411 struct fwnode_handle *endpoint, unsigned int asd_struct_size,
412 int (*parse_endpoint)(struct device *dev,
413 struct v4l2_fwnode_endpoint *vep,
414 struct v4l2_async_subdev *asd))
415{
Sakari Ailus6970d372018-06-02 12:19:35 -0400416 struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
Sakari Ailus9ca46532017-08-17 11:28:21 -0400417 struct v4l2_async_subdev *asd;
Sakari Ailus6970d372018-06-02 12:19:35 -0400418 int ret;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400419
420 asd = kzalloc(asd_struct_size, GFP_KERNEL);
421 if (!asd)
422 return -ENOMEM;
423
424 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400425 asd->match.fwnode =
Sakari Ailus9ca46532017-08-17 11:28:21 -0400426 fwnode_graph_get_remote_port_parent(endpoint);
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400427 if (!asd->match.fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400428 dev_warn(dev, "bad remote port parent\n");
Steve Longerbeam4382f372018-09-29 15:54:04 -0400429 ret = -ENOTCONN;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400430 goto out_err;
431 }
432
Sakari Ailus6970d372018-06-02 12:19:35 -0400433 ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
434 if (ret) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400435 dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
436 ret);
437 goto out_err;
438 }
439
Sakari Ailus6970d372018-06-02 12:19:35 -0400440 ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400441 if (ret == -ENOTCONN)
Sakari Ailus6970d372018-06-02 12:19:35 -0400442 dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
443 vep.base.id);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400444 else if (ret < 0)
445 dev_warn(dev,
446 "driver could not parse port@%u/endpoint@%u (%d)\n",
Sakari Ailus6970d372018-06-02 12:19:35 -0400447 vep.base.port, vep.base.id, ret);
448 v4l2_fwnode_endpoint_free(&vep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400449 if (ret < 0)
450 goto out_err;
451
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400452 ret = v4l2_async_notifier_add_subdev(notifier, asd);
453 if (ret < 0) {
454 /* not an error if asd already exists */
455 if (ret == -EEXIST)
456 ret = 0;
457 goto out_err;
458 }
Sakari Ailus9ca46532017-08-17 11:28:21 -0400459
460 return 0;
461
462out_err:
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400463 fwnode_handle_put(asd->match.fwnode);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400464 kfree(asd);
465
466 return ret == -ENOTCONN ? 0 : ret;
467}
468
469static int __v4l2_async_notifier_parse_fwnode_endpoints(
470 struct device *dev, struct v4l2_async_notifier *notifier,
471 size_t asd_struct_size, unsigned int port, bool has_port,
472 int (*parse_endpoint)(struct device *dev,
473 struct v4l2_fwnode_endpoint *vep,
474 struct v4l2_async_subdev *asd))
475{
476 struct fwnode_handle *fwnode;
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400477 int ret = 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400478
479 if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
480 return -EINVAL;
481
Sakari Ailus106ee382017-12-21 07:11:19 -0500482 fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400483 struct fwnode_handle *dev_fwnode;
484 bool is_available;
485
486 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
487 is_available = fwnode_device_is_available(dev_fwnode);
488 fwnode_handle_put(dev_fwnode);
489 if (!is_available)
490 continue;
491
492 if (has_port) {
493 struct fwnode_endpoint ep;
494
495 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400496 if (ret)
497 break;
498
499 if (ep.port != port)
500 continue;
501 }
502
503 ret = v4l2_async_notifier_fwnode_parse_endpoint(
504 dev, notifier, fwnode, asd_struct_size, parse_endpoint);
505 if (ret < 0)
506 break;
507 }
508
509 fwnode_handle_put(fwnode);
510
511 return ret;
512}
513
514int v4l2_async_notifier_parse_fwnode_endpoints(
515 struct device *dev, struct v4l2_async_notifier *notifier,
516 size_t asd_struct_size,
517 int (*parse_endpoint)(struct device *dev,
518 struct v4l2_fwnode_endpoint *vep,
519 struct v4l2_async_subdev *asd))
520{
521 return __v4l2_async_notifier_parse_fwnode_endpoints(
522 dev, notifier, asd_struct_size, 0, false, parse_endpoint);
523}
524EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
525
526int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
527 struct device *dev, struct v4l2_async_notifier *notifier,
528 size_t asd_struct_size, unsigned int port,
529 int (*parse_endpoint)(struct device *dev,
530 struct v4l2_fwnode_endpoint *vep,
531 struct v4l2_async_subdev *asd))
532{
533 return __v4l2_async_notifier_parse_fwnode_endpoints(
534 dev, notifier, asd_struct_size, port, true, parse_endpoint);
535}
536EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
537
Sakari Ailusd8428532017-06-20 07:47:24 -0400538/*
539 * v4l2_fwnode_reference_parse - parse references for async sub-devices
540 * @dev: the device node the properties of which are parsed for references
541 * @notifier: the async notifier where the async subdevs will be added
542 * @prop: the name of the property
543 *
544 * Return: 0 on success
545 * -ENOENT if no entries were found
546 * -ENOMEM if memory allocation failed
547 * -EINVAL if property parsing failed
548 */
549static int v4l2_fwnode_reference_parse(
550 struct device *dev, struct v4l2_async_notifier *notifier,
551 const char *prop)
552{
553 struct fwnode_reference_args args;
554 unsigned int index;
555 int ret;
556
557 for (index = 0;
558 !(ret = fwnode_property_get_reference_args(
559 dev_fwnode(dev), prop, NULL, 0, index, &args));
560 index++)
561 fwnode_handle_put(args.fwnode);
562
563 if (!index)
564 return -ENOENT;
565
566 /*
567 * Note that right now both -ENODATA and -ENOENT may signal
568 * out-of-bounds access. Return the error in cases other than that.
569 */
570 if (ret != -ENOENT && ret != -ENODATA)
571 return ret;
572
Sakari Ailusd8428532017-06-20 07:47:24 -0400573 for (index = 0; !fwnode_property_get_reference_args(
574 dev_fwnode(dev), prop, NULL, 0, index, &args);
575 index++) {
576 struct v4l2_async_subdev *asd;
577
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400578 asd = v4l2_async_notifier_add_fwnode_subdev(
579 notifier, args.fwnode, sizeof(*asd));
580 if (IS_ERR(asd)) {
581 ret = PTR_ERR(asd);
582 /* not an error if asd already exists */
583 if (ret == -EEXIST) {
584 fwnode_handle_put(args.fwnode);
585 continue;
586 }
587
Sakari Ailusd8428532017-06-20 07:47:24 -0400588 goto error;
589 }
Sakari Ailusd8428532017-06-20 07:47:24 -0400590 }
591
592 return 0;
593
594error:
595 fwnode_handle_put(args.fwnode);
596 return ret;
597}
598
Sakari Ailusa1699a42017-06-22 08:03:28 -0400599/*
600 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
601 * arguments
602 * @fwnode: fwnode to read @prop from
603 * @notifier: notifier for @dev
604 * @prop: the name of the property
605 * @index: the index of the reference to get
606 * @props: the array of integer property names
607 * @nprops: the number of integer property names in @nprops
608 *
609 * First find an fwnode referred to by the reference at @index in @prop.
610 *
611 * Then under that fwnode, @nprops times, for each property in @props,
612 * iteratively follow child nodes starting from fwnode such that they have the
613 * property in @props array at the index of the child node distance from the
614 * root node and the value of that property matching with the integer argument
615 * of the reference, at the same index.
616 *
617 * The child fwnode reched at the end of the iteration is then returned to the
618 * caller.
619 *
620 * The core reason for this is that you cannot refer to just any node in ACPI.
621 * So to refer to an endpoint (easy in DT) you need to refer to a device, then
622 * provide a list of (property name, property value) tuples where each tuple
623 * uniquely identifies a child node. The first tuple identifies a child directly
624 * underneath the device fwnode, the next tuple identifies a child node
625 * underneath the fwnode identified by the previous tuple, etc. until you
626 * reached the fwnode you need.
627 *
628 * An example with a graph, as defined in Documentation/acpi/dsd/graph.txt:
629 *
630 * Scope (\_SB.PCI0.I2C2)
631 * {
632 * Device (CAM0)
633 * {
634 * Name (_DSD, Package () {
635 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
636 * Package () {
637 * Package () {
638 * "compatible",
639 * Package () { "nokia,smia" }
640 * },
641 * },
642 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
643 * Package () {
644 * Package () { "port0", "PRT0" },
645 * }
646 * })
647 * Name (PRT0, Package() {
648 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
649 * Package () {
650 * Package () { "port", 0 },
651 * },
652 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
653 * Package () {
654 * Package () { "endpoint0", "EP00" },
655 * }
656 * })
657 * Name (EP00, Package() {
658 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
659 * Package () {
660 * Package () { "endpoint", 0 },
661 * Package () {
662 * "remote-endpoint",
663 * Package() {
664 * \_SB.PCI0.ISP, 4, 0
665 * }
666 * },
667 * }
668 * })
669 * }
670 * }
671 *
672 * Scope (\_SB.PCI0)
673 * {
674 * Device (ISP)
675 * {
676 * Name (_DSD, Package () {
677 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
678 * Package () {
679 * Package () { "port4", "PRT4" },
680 * }
681 * })
682 *
683 * Name (PRT4, Package() {
684 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
685 * Package () {
686 * Package () { "port", 4 },
687 * },
688 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
689 * Package () {
690 * Package () { "endpoint0", "EP40" },
691 * }
692 * })
693 *
694 * Name (EP40, Package() {
695 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
696 * Package () {
697 * Package () { "endpoint", 0 },
698 * Package () {
699 * "remote-endpoint",
700 * Package () {
701 * \_SB.PCI0.I2C2.CAM0,
702 * 0, 0
703 * }
704 * },
705 * }
706 * })
707 * }
708 * }
709 *
710 * From the EP40 node under ISP device, you could parse the graph remote
711 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
712 *
713 * @fwnode: fwnode referring to EP40 under ISP.
714 * @prop: "remote-endpoint"
715 * @index: 0
716 * @props: "port", "endpoint"
717 * @nprops: 2
718 *
719 * And you'd get back fwnode referring to EP00 under CAM0.
720 *
721 * The same works the other way around: if you use EP00 under CAM0 as the
722 * fwnode, you'll get fwnode referring to EP40 under ISP.
723 *
724 * The same example in DT syntax would look like this:
725 *
726 * cam: cam0 {
727 * compatible = "nokia,smia";
728 *
729 * port {
730 * port = <0>;
731 * endpoint {
732 * endpoint = <0>;
733 * remote-endpoint = <&isp 4 0>;
734 * };
735 * };
736 * };
737 *
738 * isp: isp {
739 * ports {
740 * port@4 {
741 * port = <4>;
742 * endpoint {
743 * endpoint = <0>;
744 * remote-endpoint = <&cam 0 0>;
745 * };
746 * };
747 * };
748 * };
749 *
750 * Return: 0 on success
751 * -ENOENT if no entries (or the property itself) were found
752 * -EINVAL if property parsing otherwise failed
753 * -ENOMEM if memory allocation failed
754 */
755static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop(
756 struct fwnode_handle *fwnode, const char *prop, unsigned int index,
757 const char * const *props, unsigned int nprops)
758{
759 struct fwnode_reference_args fwnode_args;
Sakari Ailus977d5ad2018-07-17 17:19:11 +0300760 u64 *args = fwnode_args.args;
Sakari Ailusa1699a42017-06-22 08:03:28 -0400761 struct fwnode_handle *child;
762 int ret;
763
764 /*
765 * Obtain remote fwnode as well as the integer arguments.
766 *
767 * Note that right now both -ENODATA and -ENOENT may signal
768 * out-of-bounds access. Return -ENOENT in that case.
769 */
770 ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
771 index, &fwnode_args);
772 if (ret)
773 return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
774
775 /*
776 * Find a node in the tree under the referred fwnode corresponding to
777 * the integer arguments.
778 */
779 fwnode = fwnode_args.fwnode;
780 while (nprops--) {
781 u32 val;
782
783 /* Loop over all child nodes under fwnode. */
784 fwnode_for_each_child_node(fwnode, child) {
785 if (fwnode_property_read_u32(child, *props, &val))
786 continue;
787
788 /* Found property, see if its value matches. */
789 if (val == *args)
790 break;
791 }
792
793 fwnode_handle_put(fwnode);
794
795 /* No property found; return an error here. */
796 if (!child) {
797 fwnode = ERR_PTR(-ENOENT);
798 break;
799 }
800
801 props++;
802 args++;
803 fwnode = child;
804 }
805
806 return fwnode;
807}
808
809/*
810 * v4l2_fwnode_reference_parse_int_props - parse references for async
811 * sub-devices
812 * @dev: struct device pointer
813 * @notifier: notifier for @dev
814 * @prop: the name of the property
815 * @props: the array of integer property names
816 * @nprops: the number of integer properties
817 *
818 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
819 * property @prop with integer arguments with child nodes matching in properties
820 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
821 * accordingly.
822 *
823 * While it is technically possible to use this function on DT, it is only
824 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
825 * on ACPI the references are limited to devices.
826 *
827 * Return: 0 on success
828 * -ENOENT if no entries (or the property itself) were found
829 * -EINVAL if property parsing otherwisefailed
830 * -ENOMEM if memory allocation failed
831 */
832static int v4l2_fwnode_reference_parse_int_props(
833 struct device *dev, struct v4l2_async_notifier *notifier,
834 const char *prop, const char * const *props, unsigned int nprops)
835{
836 struct fwnode_handle *fwnode;
837 unsigned int index;
838 int ret;
839
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400840 index = 0;
841 do {
842 fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
843 prop, index,
844 props, nprops);
845 if (IS_ERR(fwnode)) {
846 /*
847 * Note that right now both -ENODATA and -ENOENT may
848 * signal out-of-bounds access. Return the error in
849 * cases other than that.
850 */
851 if (PTR_ERR(fwnode) != -ENOENT &&
852 PTR_ERR(fwnode) != -ENODATA)
853 return PTR_ERR(fwnode);
854 break;
855 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400856 fwnode_handle_put(fwnode);
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400857 index++;
858 } while (1);
Sakari Ailusa1699a42017-06-22 08:03:28 -0400859
Sakari Ailusa1699a42017-06-22 08:03:28 -0400860 for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
861 dev_fwnode(dev), prop, index, props,
862 nprops))); index++) {
863 struct v4l2_async_subdev *asd;
864
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400865 asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode,
866 sizeof(*asd));
867 if (IS_ERR(asd)) {
868 ret = PTR_ERR(asd);
869 /* not an error if asd already exists */
870 if (ret == -EEXIST) {
871 fwnode_handle_put(fwnode);
872 continue;
873 }
874
Sakari Ailusa1699a42017-06-22 08:03:28 -0400875 goto error;
876 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400877 }
878
879 return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
880
881error:
882 fwnode_handle_put(fwnode);
883 return ret;
884}
885
Sakari Ailus7a9ec802017-09-06 08:35:42 -0400886int v4l2_async_notifier_parse_fwnode_sensor_common(
887 struct device *dev, struct v4l2_async_notifier *notifier)
888{
889 static const char * const led_props[] = { "led" };
890 static const struct {
891 const char *name;
892 const char * const *props;
893 unsigned int nprops;
894 } props[] = {
895 { "flash-leds", led_props, ARRAY_SIZE(led_props) },
896 { "lens-focus", NULL, 0 },
897 };
898 unsigned int i;
899
900 for (i = 0; i < ARRAY_SIZE(props); i++) {
901 int ret;
902
903 if (props[i].props && is_acpi_node(dev_fwnode(dev)))
904 ret = v4l2_fwnode_reference_parse_int_props(
905 dev, notifier, props[i].name,
906 props[i].props, props[i].nprops);
907 else
908 ret = v4l2_fwnode_reference_parse(
909 dev, notifier, props[i].name);
910 if (ret && ret != -ENOENT) {
911 dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
912 props[i].name, ret);
913 return ret;
914 }
915 }
916
917 return 0;
918}
919EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common);
920
Sakari Ailusaef69d52017-09-24 18:47:44 -0400921int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd)
922{
923 struct v4l2_async_notifier *notifier;
924 int ret;
925
926 if (WARN_ON(!sd->dev))
927 return -ENODEV;
928
929 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
930 if (!notifier)
931 return -ENOMEM;
932
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400933 v4l2_async_notifier_init(notifier);
934
Sakari Ailusaef69d52017-09-24 18:47:44 -0400935 ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev,
936 notifier);
937 if (ret < 0)
938 goto out_cleanup;
939
940 ret = v4l2_async_subdev_notifier_register(sd, notifier);
941 if (ret < 0)
942 goto out_cleanup;
943
944 ret = v4l2_async_register_subdev(sd);
945 if (ret < 0)
946 goto out_unregister;
947
948 sd->subdev_notifier = notifier;
949
950 return 0;
951
952out_unregister:
953 v4l2_async_notifier_unregister(notifier);
954
955out_cleanup:
956 v4l2_async_notifier_cleanup(notifier);
957 kfree(notifier);
958
959 return ret;
960}
961EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common);
962
Steve Longerbeam1634f0e2018-09-29 15:54:09 -0400963int v4l2_async_register_fwnode_subdev(
964 struct v4l2_subdev *sd, size_t asd_struct_size,
965 unsigned int *ports, unsigned int num_ports,
966 int (*parse_endpoint)(struct device *dev,
967 struct v4l2_fwnode_endpoint *vep,
968 struct v4l2_async_subdev *asd))
969{
970 struct v4l2_async_notifier *notifier;
971 struct device *dev = sd->dev;
972 struct fwnode_handle *fwnode;
973 int ret;
974
975 if (WARN_ON(!dev))
976 return -ENODEV;
977
978 fwnode = dev_fwnode(dev);
979 if (!fwnode_device_is_available(fwnode))
980 return -ENODEV;
981
982 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
983 if (!notifier)
984 return -ENOMEM;
985
986 v4l2_async_notifier_init(notifier);
987
988 if (!ports) {
989 ret = v4l2_async_notifier_parse_fwnode_endpoints(
990 dev, notifier, asd_struct_size, parse_endpoint);
991 if (ret < 0)
992 goto out_cleanup;
993 } else {
994 unsigned int i;
995
996 for (i = 0; i < num_ports; i++) {
997 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
998 dev, notifier, asd_struct_size,
999 ports[i], parse_endpoint);
1000 if (ret < 0)
1001 goto out_cleanup;
1002 }
1003 }
1004
1005 ret = v4l2_async_subdev_notifier_register(sd, notifier);
1006 if (ret < 0)
1007 goto out_cleanup;
1008
1009 ret = v4l2_async_register_subdev(sd);
1010 if (ret < 0)
1011 goto out_unregister;
1012
1013 sd->subdev_notifier = notifier;
1014
1015 return 0;
1016
1017out_unregister:
1018 v4l2_async_notifier_unregister(notifier);
1019out_cleanup:
1020 v4l2_async_notifier_cleanup(notifier);
1021 kfree(notifier);
1022
1023 return ret;
1024}
1025EXPORT_SYMBOL_GPL(v4l2_async_register_fwnode_subdev);
1026
Sakari Ailusca50c192016-08-12 08:05:51 -03001027MODULE_LICENSE("GPL");
1028MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1029MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1030MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");