blob: 040323c2e67aa893015ac8f675c42441e5848ba6 [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,
Sakari Ailus276565e2018-07-18 09:33:45 -040046 struct v4l2_fwnode_endpoint *vep,
47 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailusca50c192016-08-12 08:05:51 -030048{
49 struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2;
Sakari Ailusaf11a742018-07-03 07:06:20 -040050 bool have_clk_lane = false, have_lane_polarities = false;
Sakari Ailusca50c192016-08-12 08:05:51 -030051 unsigned int flags = 0, lanes_used = 0;
Sakari Ailus276565e2018-07-18 09:33:45 -040052 u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES];
53 unsigned int num_data_lanes = 0;
Sakari Ailusca50c192016-08-12 08:05:51 -030054 unsigned int i;
55 u32 v;
56 int rval;
57
Sakari Ailusc2475ae2018-07-20 06:14:14 -040058 if (bus_type == V4L2_FWNODE_BUS_TYPE_CSI2_DPHY) {
Sakari Ailus276565e2018-07-18 09:33:45 -040059 num_data_lanes = min_t(u32, bus->num_data_lanes,
60 V4L2_FWNODE_CSI2_MAX_DATA_LANES);
61
Sakari Ailusc2475ae2018-07-20 06:14:14 -040062 for (i = 0; i < num_data_lanes; i++)
63 array[i] = bus->data_lanes[i];
64 }
65
Sakari Ailusca50c192016-08-12 08:05:51 -030066 rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
67 if (rval > 0) {
Sakari Ailus276565e2018-07-18 09:33:45 -040068 num_data_lanes =
Sakari Ailusad3cdf32017-08-14 06:43:07 -040069 min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
Sakari Ailusca50c192016-08-12 08:05:51 -030070
71 fwnode_property_read_u32_array(fwnode, "data-lanes", array,
Sakari Ailus276565e2018-07-18 09:33:45 -040072 num_data_lanes);
Sakari Ailusc2475ae2018-07-20 06:14:14 -040073 }
Sakari Ailusca50c192016-08-12 08:05:51 -030074
Sakari Ailusc2475ae2018-07-20 06:14:14 -040075 for (i = 0; i < num_data_lanes; i++) {
76 if (lanes_used & BIT(array[i]))
77 pr_warn("duplicated lane %u in data-lanes\n",
78 array[i]);
79 lanes_used |= BIT(array[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -030080
Sakari Ailusc2475ae2018-07-20 06:14:14 -040081 pr_debug("lane %u position %u\n", i, array[i]);
Sakari Ailus276565e2018-07-18 09:33:45 -040082 }
Sakari Ailusca50c192016-08-12 08:05:51 -030083
Sakari Ailus276565e2018-07-18 09:33:45 -040084 rval = fwnode_property_read_u32_array(fwnode, "lane-polarities", NULL,
85 0);
86 if (rval > 0) {
87 if (rval != 1 + num_data_lanes /* clock+data */) {
88 pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
89 1 + num_data_lanes, rval);
90 return -EINVAL;
Sakari Ailusca50c192016-08-12 08:05:51 -030091 }
Sakari Ailusb24f0212017-08-14 06:15:21 -040092
Sakari Ailusaf11a742018-07-03 07:06:20 -040093 have_lane_polarities = true;
Sakari Ailusca50c192016-08-12 08:05:51 -030094 }
95
96 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
97 if (lanes_used & BIT(v))
98 pr_warn("duplicated lane %u in clock-lanes\n", v);
99 lanes_used |= BIT(v);
100
101 bus->clock_lane = v;
102 have_clk_lane = true;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500103 pr_debug("clock lane position %u\n", v);
Sakari Ailusca50c192016-08-12 08:05:51 -0300104 }
105
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500106 if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300107 flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500108 pr_debug("non-continuous clock\n");
Sakari Ailusd4865322018-07-20 05:04:29 -0400109 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300110 flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500111 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300112
Sakari Ailus276565e2018-07-18 09:33:45 -0400113 if (bus_type == V4L2_FWNODE_BUS_TYPE_CSI2_DPHY || lanes_used ||
114 have_clk_lane || (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) {
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400115 bus->flags = flags;
116 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
Sakari Ailus276565e2018-07-18 09:33:45 -0400117 bus->num_data_lanes = num_data_lanes;
Sakari Ailusaf11a742018-07-03 07:06:20 -0400118 for (i = 0; i < num_data_lanes; i++)
119 bus->data_lanes[i] = array[i];
120
121 if (have_lane_polarities) {
122 fwnode_property_read_u32_array(fwnode,
123 "lane-polarities", array,
124 1 + num_data_lanes);
125
126 for (i = 0; i < 1 + num_data_lanes; i++) {
127 bus->lane_polarities[i] = array[i];
128 pr_debug("lane %u polarity %sinverted",
129 i, array[i] ? "" : "not ");
130 }
131 } else {
132 pr_debug("no lane polarities defined, assuming not inverted\n");
133 }
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400134 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300135
136 return 0;
137}
138
Sakari Ailus175b18b2018-01-02 08:14:30 -0500139#define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
140 V4L2_MBUS_HSYNC_ACTIVE_LOW | \
141 V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
142 V4L2_MBUS_VSYNC_ACTIVE_LOW | \
143 V4L2_MBUS_FIELD_EVEN_HIGH | \
144 V4L2_MBUS_FIELD_EVEN_LOW)
145
Sakari Ailusca50c192016-08-12 08:05:51 -0300146static void v4l2_fwnode_endpoint_parse_parallel_bus(
Sakari Ailus175b18b2018-01-02 08:14:30 -0500147 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep,
148 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailusca50c192016-08-12 08:05:51 -0300149{
150 struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
151 unsigned int flags = 0;
152 u32 v;
153
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500154 if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300155 flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
156 V4L2_MBUS_HSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500157 pr_debug("hsync-active %s\n", v ? "high" : "low");
158 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300159
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500160 if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300161 flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
162 V4L2_MBUS_VSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500163 pr_debug("vsync-active %s\n", v ? "high" : "low");
164 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300165
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500166 if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300167 flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
168 V4L2_MBUS_FIELD_EVEN_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500169 pr_debug("field-even-active %s\n", v ? "high" : "low");
170 }
171
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500172 if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300173 flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
174 V4L2_MBUS_PCLK_SAMPLE_FALLING;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500175 pr_debug("pclk-sample %s\n", v ? "high" : "low");
176 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300177
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500178 if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300179 flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
180 V4L2_MBUS_DATA_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500181 pr_debug("data-active %s\n", v ? "high" : "low");
182 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300183
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500184 if (fwnode_property_present(fwnode, "slave-mode")) {
185 pr_debug("slave mode\n");
Sakari Ailusca50c192016-08-12 08:05:51 -0300186 flags |= V4L2_MBUS_SLAVE;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500187 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300188 flags |= V4L2_MBUS_MASTER;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500189 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300190
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500191 if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300192 bus->bus_width = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500193 pr_debug("bus-width %u\n", v);
194 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300195
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500196 if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300197 bus->data_shift = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500198 pr_debug("data-shift %u\n", v);
199 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300200
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500201 if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300202 flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
203 V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500204 pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
205 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300206
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500207 if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400208 flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
209 V4L2_MBUS_DATA_ENABLE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500210 pr_debug("data-enable-active %s\n", v ? "high" : "low");
211 }
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400212
Sakari Ailus175b18b2018-01-02 08:14:30 -0500213 switch (bus_type) {
214 default:
215 bus->flags = flags;
216 if (flags & PARALLEL_MBUS_FLAGS)
217 vep->bus_type = V4L2_MBUS_PARALLEL;
218 else
219 vep->bus_type = V4L2_MBUS_BT656;
220 break;
221 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400222 vep->bus_type = V4L2_MBUS_PARALLEL;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500223 bus->flags = flags;
224 break;
225 case V4L2_FWNODE_BUS_TYPE_BT656:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400226 vep->bus_type = V4L2_MBUS_BT656;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500227 bus->flags = flags & ~PARALLEL_MBUS_FLAGS;
228 break;
229 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300230}
231
Mauro Carvalho Chehababc5b2c2017-07-20 16:27:27 -0400232static void
233v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
234 struct v4l2_fwnode_endpoint *vep,
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400235 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500236{
237 struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
238 u32 v;
239
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500240 if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500241 bus->clock_inv = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500242 pr_debug("clock-inv %u\n", v);
243 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500244
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500245 if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500246 bus->strobe = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500247 pr_debug("strobe %u\n", v);
248 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500249
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500250 if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500251 bus->data_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500252 pr_debug("data-lanes %u\n", v);
253 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500254
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500255 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500256 bus->clock_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500257 pr_debug("clock-lanes %u\n", v);
258 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500259
260 if (bus_type == V4L2_FWNODE_BUS_TYPE_CCP2)
261 vep->bus_type = V4L2_MBUS_CCP2;
262 else
263 vep->bus_type = V4L2_MBUS_CSI1;
264}
265
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500266static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
267 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300268{
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500269 u32 bus_type = 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300270 int rval;
271
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500272 pr_debug("===== begin V4L2 endpoint properties\n");
273
Sakari Ailusca50c192016-08-12 08:05:51 -0300274 fwnode_graph_parse_endpoint(fwnode, &vep->base);
275
276 /* Zero fields from bus_type to until the end */
277 memset(&vep->bus_type, 0, sizeof(*vep) -
278 offsetof(typeof(*vep), bus_type));
279
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500280 fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
281
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500282 switch (bus_type) {
283 case V4L2_FWNODE_BUS_TYPE_GUESS:
Sakari Ailus276565e2018-07-18 09:33:45 -0400284 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
285 bus_type);
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500286 if (rval)
287 return rval;
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400288
289 if (vep->bus_type == V4L2_MBUS_UNKNOWN)
Sakari Ailus175b18b2018-01-02 08:14:30 -0500290 v4l2_fwnode_endpoint_parse_parallel_bus(
291 fwnode, vep, V4L2_MBUS_UNKNOWN);
Sakari Ailusca50c192016-08-12 08:05:51 -0300292
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500293 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500294 case V4L2_FWNODE_BUS_TYPE_CCP2:
295 case V4L2_FWNODE_BUS_TYPE_CSI1:
296 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, bus_type);
297
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500298 break;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500299 case V4L2_FWNODE_BUS_TYPE_CSI2_DPHY:
300 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
Sakari Ailus276565e2018-07-18 09:33:45 -0400301 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
302 bus_type);
Sakari Ailus175b18b2018-01-02 08:14:30 -0500303 if (rval)
304 return rval;
305
306 break;
307 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
308 case V4L2_FWNODE_BUS_TYPE_BT656:
309 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep, bus_type);
310
311 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500312 default:
313 pr_warn("unsupported bus type %u\n", bus_type);
314 return -EINVAL;
315 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500316
317 return 0;
318}
319
320int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
321 struct v4l2_fwnode_endpoint *vep)
322{
323 int ret;
324
325 ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
326
327 pr_debug("===== end V4L2 endpoint properties\n");
328
329 return ret;
Sakari Ailusca50c192016-08-12 08:05:51 -0300330}
331EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
332
Sakari Ailusca50c192016-08-12 08:05:51 -0300333void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
334{
335 if (IS_ERR_OR_NULL(vep))
336 return;
337
338 kfree(vep->link_frequencies);
Sakari Ailusca50c192016-08-12 08:05:51 -0300339}
340EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
341
Sakari Ailus6970d372018-06-02 12:19:35 -0400342int v4l2_fwnode_endpoint_alloc_parse(
343 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300344{
Sakari Ailusca50c192016-08-12 08:05:51 -0300345 int rval;
346
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500347 rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
Sakari Ailusca50c192016-08-12 08:05:51 -0300348 if (rval < 0)
Sakari Ailus6970d372018-06-02 12:19:35 -0400349 return rval;
Sakari Ailusca50c192016-08-12 08:05:51 -0300350
351 rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
352 NULL, 0);
Sakari Ailus06f81522017-06-20 09:14:43 -0400353 if (rval > 0) {
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500354 unsigned int i;
355
Sakari Ailus06f81522017-06-20 09:14:43 -0400356 vep->link_frequencies =
357 kmalloc_array(rval, sizeof(*vep->link_frequencies),
358 GFP_KERNEL);
Sakari Ailus6970d372018-06-02 12:19:35 -0400359 if (!vep->link_frequencies)
360 return -ENOMEM;
Sakari Ailusca50c192016-08-12 08:05:51 -0300361
Sakari Ailus06f81522017-06-20 09:14:43 -0400362 vep->nr_of_link_frequencies = rval;
363
364 rval = fwnode_property_read_u64_array(
365 fwnode, "link-frequencies", vep->link_frequencies,
366 vep->nr_of_link_frequencies);
Sakari Ailus6970d372018-06-02 12:19:35 -0400367 if (rval < 0) {
368 v4l2_fwnode_endpoint_free(vep);
369 return rval;
370 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500371
372 for (i = 0; i < vep->nr_of_link_frequencies; i++)
373 pr_info("link-frequencies %u value %llu\n", i,
374 vep->link_frequencies[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -0300375 }
376
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500377 pr_debug("===== end V4L2 endpoint properties\n");
378
Sakari Ailus6970d372018-06-02 12:19:35 -0400379 return 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300380}
381EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
382
Sakari Ailusca50c192016-08-12 08:05:51 -0300383int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
384 struct v4l2_fwnode_link *link)
385{
386 const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
387 struct fwnode_handle *fwnode;
388
389 memset(link, 0, sizeof(*link));
390
391 fwnode = fwnode_get_parent(__fwnode);
392 fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
393 fwnode = fwnode_get_next_parent(fwnode);
394 if (is_of_node(fwnode) &&
395 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
396 fwnode = fwnode_get_next_parent(fwnode);
397 link->local_node = fwnode;
398
399 fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
400 if (!fwnode) {
401 fwnode_handle_put(fwnode);
402 return -ENOLINK;
403 }
404
405 fwnode = fwnode_get_parent(fwnode);
406 fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
407 fwnode = fwnode_get_next_parent(fwnode);
408 if (is_of_node(fwnode) &&
409 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
410 fwnode = fwnode_get_next_parent(fwnode);
411 link->remote_node = fwnode;
412
413 return 0;
414}
415EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
416
Sakari Ailusca50c192016-08-12 08:05:51 -0300417void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
418{
419 fwnode_handle_put(link->local_node);
420 fwnode_handle_put(link->remote_node);
421}
422EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
423
Sakari Ailus9ca46532017-08-17 11:28:21 -0400424static int v4l2_async_notifier_fwnode_parse_endpoint(
425 struct device *dev, struct v4l2_async_notifier *notifier,
426 struct fwnode_handle *endpoint, unsigned int asd_struct_size,
427 int (*parse_endpoint)(struct device *dev,
428 struct v4l2_fwnode_endpoint *vep,
429 struct v4l2_async_subdev *asd))
430{
Sakari Ailus6970d372018-06-02 12:19:35 -0400431 struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
Sakari Ailus9ca46532017-08-17 11:28:21 -0400432 struct v4l2_async_subdev *asd;
Sakari Ailus6970d372018-06-02 12:19:35 -0400433 int ret;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400434
435 asd = kzalloc(asd_struct_size, GFP_KERNEL);
436 if (!asd)
437 return -ENOMEM;
438
439 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400440 asd->match.fwnode =
Sakari Ailus9ca46532017-08-17 11:28:21 -0400441 fwnode_graph_get_remote_port_parent(endpoint);
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400442 if (!asd->match.fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400443 dev_warn(dev, "bad remote port parent\n");
Steve Longerbeam4382f372018-09-29 15:54:04 -0400444 ret = -ENOTCONN;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400445 goto out_err;
446 }
447
Sakari Ailus6970d372018-06-02 12:19:35 -0400448 ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
449 if (ret) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400450 dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
451 ret);
452 goto out_err;
453 }
454
Sakari Ailus6970d372018-06-02 12:19:35 -0400455 ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400456 if (ret == -ENOTCONN)
Sakari Ailus6970d372018-06-02 12:19:35 -0400457 dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
458 vep.base.id);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400459 else if (ret < 0)
460 dev_warn(dev,
461 "driver could not parse port@%u/endpoint@%u (%d)\n",
Sakari Ailus6970d372018-06-02 12:19:35 -0400462 vep.base.port, vep.base.id, ret);
463 v4l2_fwnode_endpoint_free(&vep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400464 if (ret < 0)
465 goto out_err;
466
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400467 ret = v4l2_async_notifier_add_subdev(notifier, asd);
468 if (ret < 0) {
469 /* not an error if asd already exists */
470 if (ret == -EEXIST)
471 ret = 0;
472 goto out_err;
473 }
Sakari Ailus9ca46532017-08-17 11:28:21 -0400474
475 return 0;
476
477out_err:
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400478 fwnode_handle_put(asd->match.fwnode);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400479 kfree(asd);
480
481 return ret == -ENOTCONN ? 0 : ret;
482}
483
484static int __v4l2_async_notifier_parse_fwnode_endpoints(
485 struct device *dev, struct v4l2_async_notifier *notifier,
486 size_t asd_struct_size, unsigned int port, bool has_port,
487 int (*parse_endpoint)(struct device *dev,
488 struct v4l2_fwnode_endpoint *vep,
489 struct v4l2_async_subdev *asd))
490{
491 struct fwnode_handle *fwnode;
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400492 int ret = 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400493
494 if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
495 return -EINVAL;
496
Sakari Ailus106ee382017-12-21 07:11:19 -0500497 fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400498 struct fwnode_handle *dev_fwnode;
499 bool is_available;
500
501 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
502 is_available = fwnode_device_is_available(dev_fwnode);
503 fwnode_handle_put(dev_fwnode);
504 if (!is_available)
505 continue;
506
507 if (has_port) {
508 struct fwnode_endpoint ep;
509
510 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400511 if (ret)
512 break;
513
514 if (ep.port != port)
515 continue;
516 }
517
518 ret = v4l2_async_notifier_fwnode_parse_endpoint(
519 dev, notifier, fwnode, asd_struct_size, parse_endpoint);
520 if (ret < 0)
521 break;
522 }
523
524 fwnode_handle_put(fwnode);
525
526 return ret;
527}
528
529int v4l2_async_notifier_parse_fwnode_endpoints(
530 struct device *dev, struct v4l2_async_notifier *notifier,
531 size_t asd_struct_size,
532 int (*parse_endpoint)(struct device *dev,
533 struct v4l2_fwnode_endpoint *vep,
534 struct v4l2_async_subdev *asd))
535{
536 return __v4l2_async_notifier_parse_fwnode_endpoints(
537 dev, notifier, asd_struct_size, 0, false, parse_endpoint);
538}
539EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
540
541int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
542 struct device *dev, struct v4l2_async_notifier *notifier,
543 size_t asd_struct_size, unsigned int port,
544 int (*parse_endpoint)(struct device *dev,
545 struct v4l2_fwnode_endpoint *vep,
546 struct v4l2_async_subdev *asd))
547{
548 return __v4l2_async_notifier_parse_fwnode_endpoints(
549 dev, notifier, asd_struct_size, port, true, parse_endpoint);
550}
551EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
552
Sakari Ailusd8428532017-06-20 07:47:24 -0400553/*
554 * v4l2_fwnode_reference_parse - parse references for async sub-devices
555 * @dev: the device node the properties of which are parsed for references
556 * @notifier: the async notifier where the async subdevs will be added
557 * @prop: the name of the property
558 *
559 * Return: 0 on success
560 * -ENOENT if no entries were found
561 * -ENOMEM if memory allocation failed
562 * -EINVAL if property parsing failed
563 */
564static int v4l2_fwnode_reference_parse(
565 struct device *dev, struct v4l2_async_notifier *notifier,
566 const char *prop)
567{
568 struct fwnode_reference_args args;
569 unsigned int index;
570 int ret;
571
572 for (index = 0;
573 !(ret = fwnode_property_get_reference_args(
574 dev_fwnode(dev), prop, NULL, 0, index, &args));
575 index++)
576 fwnode_handle_put(args.fwnode);
577
578 if (!index)
579 return -ENOENT;
580
581 /*
582 * Note that right now both -ENODATA and -ENOENT may signal
583 * out-of-bounds access. Return the error in cases other than that.
584 */
585 if (ret != -ENOENT && ret != -ENODATA)
586 return ret;
587
Sakari Ailusd8428532017-06-20 07:47:24 -0400588 for (index = 0; !fwnode_property_get_reference_args(
589 dev_fwnode(dev), prop, NULL, 0, index, &args);
590 index++) {
591 struct v4l2_async_subdev *asd;
592
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400593 asd = v4l2_async_notifier_add_fwnode_subdev(
594 notifier, args.fwnode, sizeof(*asd));
595 if (IS_ERR(asd)) {
596 ret = PTR_ERR(asd);
597 /* not an error if asd already exists */
598 if (ret == -EEXIST) {
599 fwnode_handle_put(args.fwnode);
600 continue;
601 }
602
Sakari Ailusd8428532017-06-20 07:47:24 -0400603 goto error;
604 }
Sakari Ailusd8428532017-06-20 07:47:24 -0400605 }
606
607 return 0;
608
609error:
610 fwnode_handle_put(args.fwnode);
611 return ret;
612}
613
Sakari Ailusa1699a42017-06-22 08:03:28 -0400614/*
615 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
616 * arguments
617 * @fwnode: fwnode to read @prop from
618 * @notifier: notifier for @dev
619 * @prop: the name of the property
620 * @index: the index of the reference to get
621 * @props: the array of integer property names
622 * @nprops: the number of integer property names in @nprops
623 *
624 * First find an fwnode referred to by the reference at @index in @prop.
625 *
626 * Then under that fwnode, @nprops times, for each property in @props,
627 * iteratively follow child nodes starting from fwnode such that they have the
628 * property in @props array at the index of the child node distance from the
629 * root node and the value of that property matching with the integer argument
630 * of the reference, at the same index.
631 *
632 * The child fwnode reched at the end of the iteration is then returned to the
633 * caller.
634 *
635 * The core reason for this is that you cannot refer to just any node in ACPI.
636 * So to refer to an endpoint (easy in DT) you need to refer to a device, then
637 * provide a list of (property name, property value) tuples where each tuple
638 * uniquely identifies a child node. The first tuple identifies a child directly
639 * underneath the device fwnode, the next tuple identifies a child node
640 * underneath the fwnode identified by the previous tuple, etc. until you
641 * reached the fwnode you need.
642 *
643 * An example with a graph, as defined in Documentation/acpi/dsd/graph.txt:
644 *
645 * Scope (\_SB.PCI0.I2C2)
646 * {
647 * Device (CAM0)
648 * {
649 * Name (_DSD, Package () {
650 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
651 * Package () {
652 * Package () {
653 * "compatible",
654 * Package () { "nokia,smia" }
655 * },
656 * },
657 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
658 * Package () {
659 * Package () { "port0", "PRT0" },
660 * }
661 * })
662 * Name (PRT0, Package() {
663 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
664 * Package () {
665 * Package () { "port", 0 },
666 * },
667 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
668 * Package () {
669 * Package () { "endpoint0", "EP00" },
670 * }
671 * })
672 * Name (EP00, Package() {
673 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
674 * Package () {
675 * Package () { "endpoint", 0 },
676 * Package () {
677 * "remote-endpoint",
678 * Package() {
679 * \_SB.PCI0.ISP, 4, 0
680 * }
681 * },
682 * }
683 * })
684 * }
685 * }
686 *
687 * Scope (\_SB.PCI0)
688 * {
689 * Device (ISP)
690 * {
691 * Name (_DSD, Package () {
692 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
693 * Package () {
694 * Package () { "port4", "PRT4" },
695 * }
696 * })
697 *
698 * Name (PRT4, Package() {
699 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
700 * Package () {
701 * Package () { "port", 4 },
702 * },
703 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
704 * Package () {
705 * Package () { "endpoint0", "EP40" },
706 * }
707 * })
708 *
709 * Name (EP40, Package() {
710 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
711 * Package () {
712 * Package () { "endpoint", 0 },
713 * Package () {
714 * "remote-endpoint",
715 * Package () {
716 * \_SB.PCI0.I2C2.CAM0,
717 * 0, 0
718 * }
719 * },
720 * }
721 * })
722 * }
723 * }
724 *
725 * From the EP40 node under ISP device, you could parse the graph remote
726 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
727 *
728 * @fwnode: fwnode referring to EP40 under ISP.
729 * @prop: "remote-endpoint"
730 * @index: 0
731 * @props: "port", "endpoint"
732 * @nprops: 2
733 *
734 * And you'd get back fwnode referring to EP00 under CAM0.
735 *
736 * The same works the other way around: if you use EP00 under CAM0 as the
737 * fwnode, you'll get fwnode referring to EP40 under ISP.
738 *
739 * The same example in DT syntax would look like this:
740 *
741 * cam: cam0 {
742 * compatible = "nokia,smia";
743 *
744 * port {
745 * port = <0>;
746 * endpoint {
747 * endpoint = <0>;
748 * remote-endpoint = <&isp 4 0>;
749 * };
750 * };
751 * };
752 *
753 * isp: isp {
754 * ports {
755 * port@4 {
756 * port = <4>;
757 * endpoint {
758 * endpoint = <0>;
759 * remote-endpoint = <&cam 0 0>;
760 * };
761 * };
762 * };
763 * };
764 *
765 * Return: 0 on success
766 * -ENOENT if no entries (or the property itself) were found
767 * -EINVAL if property parsing otherwise failed
768 * -ENOMEM if memory allocation failed
769 */
770static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop(
771 struct fwnode_handle *fwnode, const char *prop, unsigned int index,
772 const char * const *props, unsigned int nprops)
773{
774 struct fwnode_reference_args fwnode_args;
Sakari Ailus977d5ad2018-07-17 17:19:11 +0300775 u64 *args = fwnode_args.args;
Sakari Ailusa1699a42017-06-22 08:03:28 -0400776 struct fwnode_handle *child;
777 int ret;
778
779 /*
780 * Obtain remote fwnode as well as the integer arguments.
781 *
782 * Note that right now both -ENODATA and -ENOENT may signal
783 * out-of-bounds access. Return -ENOENT in that case.
784 */
785 ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
786 index, &fwnode_args);
787 if (ret)
788 return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
789
790 /*
791 * Find a node in the tree under the referred fwnode corresponding to
792 * the integer arguments.
793 */
794 fwnode = fwnode_args.fwnode;
795 while (nprops--) {
796 u32 val;
797
798 /* Loop over all child nodes under fwnode. */
799 fwnode_for_each_child_node(fwnode, child) {
800 if (fwnode_property_read_u32(child, *props, &val))
801 continue;
802
803 /* Found property, see if its value matches. */
804 if (val == *args)
805 break;
806 }
807
808 fwnode_handle_put(fwnode);
809
810 /* No property found; return an error here. */
811 if (!child) {
812 fwnode = ERR_PTR(-ENOENT);
813 break;
814 }
815
816 props++;
817 args++;
818 fwnode = child;
819 }
820
821 return fwnode;
822}
823
824/*
825 * v4l2_fwnode_reference_parse_int_props - parse references for async
826 * sub-devices
827 * @dev: struct device pointer
828 * @notifier: notifier for @dev
829 * @prop: the name of the property
830 * @props: the array of integer property names
831 * @nprops: the number of integer properties
832 *
833 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
834 * property @prop with integer arguments with child nodes matching in properties
835 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
836 * accordingly.
837 *
838 * While it is technically possible to use this function on DT, it is only
839 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
840 * on ACPI the references are limited to devices.
841 *
842 * Return: 0 on success
843 * -ENOENT if no entries (or the property itself) were found
844 * -EINVAL if property parsing otherwisefailed
845 * -ENOMEM if memory allocation failed
846 */
847static int v4l2_fwnode_reference_parse_int_props(
848 struct device *dev, struct v4l2_async_notifier *notifier,
849 const char *prop, const char * const *props, unsigned int nprops)
850{
851 struct fwnode_handle *fwnode;
852 unsigned int index;
853 int ret;
854
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400855 index = 0;
856 do {
857 fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
858 prop, index,
859 props, nprops);
860 if (IS_ERR(fwnode)) {
861 /*
862 * Note that right now both -ENODATA and -ENOENT may
863 * signal out-of-bounds access. Return the error in
864 * cases other than that.
865 */
866 if (PTR_ERR(fwnode) != -ENOENT &&
867 PTR_ERR(fwnode) != -ENODATA)
868 return PTR_ERR(fwnode);
869 break;
870 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400871 fwnode_handle_put(fwnode);
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400872 index++;
873 } while (1);
Sakari Ailusa1699a42017-06-22 08:03:28 -0400874
Sakari Ailusa1699a42017-06-22 08:03:28 -0400875 for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
876 dev_fwnode(dev), prop, index, props,
877 nprops))); index++) {
878 struct v4l2_async_subdev *asd;
879
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400880 asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode,
881 sizeof(*asd));
882 if (IS_ERR(asd)) {
883 ret = PTR_ERR(asd);
884 /* not an error if asd already exists */
885 if (ret == -EEXIST) {
886 fwnode_handle_put(fwnode);
887 continue;
888 }
889
Sakari Ailusa1699a42017-06-22 08:03:28 -0400890 goto error;
891 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400892 }
893
894 return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
895
896error:
897 fwnode_handle_put(fwnode);
898 return ret;
899}
900
Sakari Ailus7a9ec802017-09-06 08:35:42 -0400901int v4l2_async_notifier_parse_fwnode_sensor_common(
902 struct device *dev, struct v4l2_async_notifier *notifier)
903{
904 static const char * const led_props[] = { "led" };
905 static const struct {
906 const char *name;
907 const char * const *props;
908 unsigned int nprops;
909 } props[] = {
910 { "flash-leds", led_props, ARRAY_SIZE(led_props) },
911 { "lens-focus", NULL, 0 },
912 };
913 unsigned int i;
914
915 for (i = 0; i < ARRAY_SIZE(props); i++) {
916 int ret;
917
918 if (props[i].props && is_acpi_node(dev_fwnode(dev)))
919 ret = v4l2_fwnode_reference_parse_int_props(
920 dev, notifier, props[i].name,
921 props[i].props, props[i].nprops);
922 else
923 ret = v4l2_fwnode_reference_parse(
924 dev, notifier, props[i].name);
925 if (ret && ret != -ENOENT) {
926 dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
927 props[i].name, ret);
928 return ret;
929 }
930 }
931
932 return 0;
933}
934EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common);
935
Sakari Ailusaef69d52017-09-24 18:47:44 -0400936int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd)
937{
938 struct v4l2_async_notifier *notifier;
939 int ret;
940
941 if (WARN_ON(!sd->dev))
942 return -ENODEV;
943
944 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
945 if (!notifier)
946 return -ENOMEM;
947
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400948 v4l2_async_notifier_init(notifier);
949
Sakari Ailusaef69d52017-09-24 18:47:44 -0400950 ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev,
951 notifier);
952 if (ret < 0)
953 goto out_cleanup;
954
955 ret = v4l2_async_subdev_notifier_register(sd, notifier);
956 if (ret < 0)
957 goto out_cleanup;
958
959 ret = v4l2_async_register_subdev(sd);
960 if (ret < 0)
961 goto out_unregister;
962
963 sd->subdev_notifier = notifier;
964
965 return 0;
966
967out_unregister:
968 v4l2_async_notifier_unregister(notifier);
969
970out_cleanup:
971 v4l2_async_notifier_cleanup(notifier);
972 kfree(notifier);
973
974 return ret;
975}
976EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common);
977
Steve Longerbeam1634f0e2018-09-29 15:54:09 -0400978int v4l2_async_register_fwnode_subdev(
979 struct v4l2_subdev *sd, size_t asd_struct_size,
980 unsigned int *ports, unsigned int num_ports,
981 int (*parse_endpoint)(struct device *dev,
982 struct v4l2_fwnode_endpoint *vep,
983 struct v4l2_async_subdev *asd))
984{
985 struct v4l2_async_notifier *notifier;
986 struct device *dev = sd->dev;
987 struct fwnode_handle *fwnode;
988 int ret;
989
990 if (WARN_ON(!dev))
991 return -ENODEV;
992
993 fwnode = dev_fwnode(dev);
994 if (!fwnode_device_is_available(fwnode))
995 return -ENODEV;
996
997 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
998 if (!notifier)
999 return -ENOMEM;
1000
1001 v4l2_async_notifier_init(notifier);
1002
1003 if (!ports) {
1004 ret = v4l2_async_notifier_parse_fwnode_endpoints(
1005 dev, notifier, asd_struct_size, parse_endpoint);
1006 if (ret < 0)
1007 goto out_cleanup;
1008 } else {
1009 unsigned int i;
1010
1011 for (i = 0; i < num_ports; i++) {
1012 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
1013 dev, notifier, asd_struct_size,
1014 ports[i], parse_endpoint);
1015 if (ret < 0)
1016 goto out_cleanup;
1017 }
1018 }
1019
1020 ret = v4l2_async_subdev_notifier_register(sd, notifier);
1021 if (ret < 0)
1022 goto out_cleanup;
1023
1024 ret = v4l2_async_register_subdev(sd);
1025 if (ret < 0)
1026 goto out_unregister;
1027
1028 sd->subdev_notifier = notifier;
1029
1030 return 0;
1031
1032out_unregister:
1033 v4l2_async_notifier_unregister(notifier);
1034out_cleanup:
1035 v4l2_async_notifier_cleanup(notifier);
1036 kfree(notifier);
1037
1038 return ret;
1039}
1040EXPORT_SYMBOL_GPL(v4l2_async_register_fwnode_subdev);
1041
Sakari Ailusca50c192016-08-12 08:05:51 -03001042MODULE_LICENSE("GPL");
1043MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1044MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1045MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");