blob: 5f3bb3d1191c3c0e6d69afac137f446d820b67d4 [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 Ailusb4357d22018-07-20 09:57:48 -040050 bool have_clk_lane = false, have_data_lanes = false,
51 have_lane_polarities = false;
Sakari Ailusca50c192016-08-12 08:05:51 -030052 unsigned int flags = 0, lanes_used = 0;
Sakari Ailus276565e2018-07-18 09:33:45 -040053 u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES];
Sakari Ailusb4357d22018-07-20 09:57:48 -040054 u32 clock_lane = 0;
Sakari Ailus276565e2018-07-18 09:33:45 -040055 unsigned int num_data_lanes = 0;
Sakari Ailusb4357d22018-07-20 09:57:48 -040056 bool use_default_lane_mapping = false;
Sakari Ailusca50c192016-08-12 08:05:51 -030057 unsigned int i;
58 u32 v;
59 int rval;
60
Sakari Ailusc2475ae2018-07-20 06:14:14 -040061 if (bus_type == V4L2_FWNODE_BUS_TYPE_CSI2_DPHY) {
Sakari Ailusb4357d22018-07-20 09:57:48 -040062 use_default_lane_mapping = true;
63
Sakari Ailus276565e2018-07-18 09:33:45 -040064 num_data_lanes = min_t(u32, bus->num_data_lanes,
65 V4L2_FWNODE_CSI2_MAX_DATA_LANES);
66
Sakari Ailusb4357d22018-07-20 09:57:48 -040067 clock_lane = bus->clock_lane;
68 if (clock_lane)
69 use_default_lane_mapping = false;
70
71 for (i = 0; i < num_data_lanes; i++) {
Sakari Ailusc2475ae2018-07-20 06:14:14 -040072 array[i] = bus->data_lanes[i];
Sakari Ailusb4357d22018-07-20 09:57:48 -040073 if (array[i])
74 use_default_lane_mapping = false;
75 }
76
77 if (use_default_lane_mapping)
78 pr_debug("using default lane mapping\n");
Sakari Ailusc2475ae2018-07-20 06:14:14 -040079 }
80
Sakari Ailusca50c192016-08-12 08:05:51 -030081 rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
82 if (rval > 0) {
Sakari Ailus276565e2018-07-18 09:33:45 -040083 num_data_lanes =
Sakari Ailusad3cdf32017-08-14 06:43:07 -040084 min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
Sakari Ailusca50c192016-08-12 08:05:51 -030085
86 fwnode_property_read_u32_array(fwnode, "data-lanes", array,
Sakari Ailus276565e2018-07-18 09:33:45 -040087 num_data_lanes);
Sakari Ailusb4357d22018-07-20 09:57:48 -040088
89 have_data_lanes = true;
Sakari Ailusc2475ae2018-07-20 06:14:14 -040090 }
Sakari Ailusca50c192016-08-12 08:05:51 -030091
Sakari Ailusc2475ae2018-07-20 06:14:14 -040092 for (i = 0; i < num_data_lanes; i++) {
Sakari Ailusb4357d22018-07-20 09:57:48 -040093 if (lanes_used & BIT(array[i])) {
94 if (have_data_lanes || !use_default_lane_mapping)
95 pr_warn("duplicated lane %u in data-lanes, using defaults\n",
96 array[i]);
97 use_default_lane_mapping = true;
98 }
Sakari Ailusc2475ae2018-07-20 06:14:14 -040099 lanes_used |= BIT(array[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -0300100
Sakari Ailusb4357d22018-07-20 09:57:48 -0400101 if (have_data_lanes)
102 pr_debug("lane %u position %u\n", i, array[i]);
Sakari Ailus276565e2018-07-18 09:33:45 -0400103 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300104
Sakari Ailus276565e2018-07-18 09:33:45 -0400105 rval = fwnode_property_read_u32_array(fwnode, "lane-polarities", NULL,
106 0);
107 if (rval > 0) {
108 if (rval != 1 + num_data_lanes /* clock+data */) {
109 pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
110 1 + num_data_lanes, rval);
111 return -EINVAL;
Sakari Ailusca50c192016-08-12 08:05:51 -0300112 }
Sakari Ailusb24f0212017-08-14 06:15:21 -0400113
Sakari Ailusaf11a742018-07-03 07:06:20 -0400114 have_lane_polarities = true;
Sakari Ailusca50c192016-08-12 08:05:51 -0300115 }
116
117 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
Sakari Ailusb4357d22018-07-20 09:57:48 -0400118 clock_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500119 pr_debug("clock lane position %u\n", v);
Sakari Ailusb4357d22018-07-20 09:57:48 -0400120 have_clk_lane = true;
121 }
122
123 if (lanes_used & BIT(clock_lane)) {
124 if (have_clk_lane || !use_default_lane_mapping)
125 pr_warn("duplicated lane %u in clock-lanes, using defaults\n",
126 v);
127 use_default_lane_mapping = true;
Sakari Ailusca50c192016-08-12 08:05:51 -0300128 }
129
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500130 if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300131 flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500132 pr_debug("non-continuous clock\n");
Sakari Ailusd4865322018-07-20 05:04:29 -0400133 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300134 flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500135 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300136
Sakari Ailus276565e2018-07-18 09:33:45 -0400137 if (bus_type == V4L2_FWNODE_BUS_TYPE_CSI2_DPHY || lanes_used ||
138 have_clk_lane || (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) {
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400139 bus->flags = flags;
140 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
Sakari Ailus276565e2018-07-18 09:33:45 -0400141 bus->num_data_lanes = num_data_lanes;
Sakari Ailusb4357d22018-07-20 09:57:48 -0400142
143 if (use_default_lane_mapping) {
144 bus->clock_lane = 0;
145 for (i = 0; i < num_data_lanes; i++)
146 bus->data_lanes[i] = 1 + i;
147 } else {
148 bus->clock_lane = clock_lane;
149 for (i = 0; i < num_data_lanes; i++)
150 bus->data_lanes[i] = array[i];
151 }
Sakari Ailusaf11a742018-07-03 07:06:20 -0400152
153 if (have_lane_polarities) {
154 fwnode_property_read_u32_array(fwnode,
155 "lane-polarities", array,
156 1 + num_data_lanes);
157
158 for (i = 0; i < 1 + num_data_lanes; i++) {
159 bus->lane_polarities[i] = array[i];
160 pr_debug("lane %u polarity %sinverted",
161 i, array[i] ? "" : "not ");
162 }
163 } else {
164 pr_debug("no lane polarities defined, assuming not inverted\n");
165 }
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400166 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300167
168 return 0;
169}
170
Sakari Ailus175b18b2018-01-02 08:14:30 -0500171#define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
172 V4L2_MBUS_HSYNC_ACTIVE_LOW | \
173 V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
174 V4L2_MBUS_VSYNC_ACTIVE_LOW | \
175 V4L2_MBUS_FIELD_EVEN_HIGH | \
176 V4L2_MBUS_FIELD_EVEN_LOW)
177
Sakari Ailusca50c192016-08-12 08:05:51 -0300178static void v4l2_fwnode_endpoint_parse_parallel_bus(
Sakari Ailus175b18b2018-01-02 08:14:30 -0500179 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep,
180 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailusca50c192016-08-12 08:05:51 -0300181{
182 struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
183 unsigned int flags = 0;
184 u32 v;
185
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500186 if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300187 flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
188 V4L2_MBUS_HSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500189 pr_debug("hsync-active %s\n", v ? "high" : "low");
190 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300191
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500192 if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300193 flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
194 V4L2_MBUS_VSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500195 pr_debug("vsync-active %s\n", v ? "high" : "low");
196 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300197
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500198 if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300199 flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
200 V4L2_MBUS_FIELD_EVEN_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500201 pr_debug("field-even-active %s\n", v ? "high" : "low");
202 }
203
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500204 if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300205 flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
206 V4L2_MBUS_PCLK_SAMPLE_FALLING;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500207 pr_debug("pclk-sample %s\n", v ? "high" : "low");
208 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300209
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500210 if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300211 flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
212 V4L2_MBUS_DATA_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500213 pr_debug("data-active %s\n", v ? "high" : "low");
214 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300215
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500216 if (fwnode_property_present(fwnode, "slave-mode")) {
217 pr_debug("slave mode\n");
Sakari Ailusca50c192016-08-12 08:05:51 -0300218 flags |= V4L2_MBUS_SLAVE;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500219 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300220 flags |= V4L2_MBUS_MASTER;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500221 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300222
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500223 if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300224 bus->bus_width = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500225 pr_debug("bus-width %u\n", v);
226 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300227
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500228 if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300229 bus->data_shift = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500230 pr_debug("data-shift %u\n", v);
231 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300232
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500233 if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300234 flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
235 V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500236 pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
237 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300238
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500239 if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400240 flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
241 V4L2_MBUS_DATA_ENABLE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500242 pr_debug("data-enable-active %s\n", v ? "high" : "low");
243 }
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400244
Sakari Ailus175b18b2018-01-02 08:14:30 -0500245 switch (bus_type) {
246 default:
247 bus->flags = flags;
248 if (flags & PARALLEL_MBUS_FLAGS)
249 vep->bus_type = V4L2_MBUS_PARALLEL;
250 else
251 vep->bus_type = V4L2_MBUS_BT656;
252 break;
253 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400254 vep->bus_type = V4L2_MBUS_PARALLEL;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500255 bus->flags = flags;
256 break;
257 case V4L2_FWNODE_BUS_TYPE_BT656:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400258 vep->bus_type = V4L2_MBUS_BT656;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500259 bus->flags = flags & ~PARALLEL_MBUS_FLAGS;
260 break;
261 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300262}
263
Mauro Carvalho Chehababc5b2c2017-07-20 16:27:27 -0400264static void
265v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
266 struct v4l2_fwnode_endpoint *vep,
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400267 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500268{
269 struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
270 u32 v;
271
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500272 if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500273 bus->clock_inv = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500274 pr_debug("clock-inv %u\n", v);
275 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500276
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500277 if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500278 bus->strobe = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500279 pr_debug("strobe %u\n", v);
280 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500281
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500282 if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500283 bus->data_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500284 pr_debug("data-lanes %u\n", v);
285 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500286
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500287 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500288 bus->clock_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500289 pr_debug("clock-lanes %u\n", v);
290 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500291
292 if (bus_type == V4L2_FWNODE_BUS_TYPE_CCP2)
293 vep->bus_type = V4L2_MBUS_CCP2;
294 else
295 vep->bus_type = V4L2_MBUS_CSI1;
296}
297
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500298static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
299 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300300{
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500301 u32 bus_type = 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300302 int rval;
303
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500304 pr_debug("===== begin V4L2 endpoint properties\n");
305
Sakari Ailus32593dd2018-07-18 09:20:34 -0400306 /*
307 * Zero the fwnode graph endpoint memory in case we don't end up parsing
308 * the endpoint.
309 */
310 memset(&vep->base, 0, sizeof(vep->base));
Sakari Ailusca50c192016-08-12 08:05:51 -0300311
312 /* Zero fields from bus_type to until the end */
313 memset(&vep->bus_type, 0, sizeof(*vep) -
314 offsetof(typeof(*vep), bus_type));
315
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500316 fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
317
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500318 switch (bus_type) {
319 case V4L2_FWNODE_BUS_TYPE_GUESS:
Sakari Ailus276565e2018-07-18 09:33:45 -0400320 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
321 bus_type);
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500322 if (rval)
323 return rval;
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400324
325 if (vep->bus_type == V4L2_MBUS_UNKNOWN)
Sakari Ailus175b18b2018-01-02 08:14:30 -0500326 v4l2_fwnode_endpoint_parse_parallel_bus(
327 fwnode, vep, V4L2_MBUS_UNKNOWN);
Sakari Ailusca50c192016-08-12 08:05:51 -0300328
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500329 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500330 case V4L2_FWNODE_BUS_TYPE_CCP2:
331 case V4L2_FWNODE_BUS_TYPE_CSI1:
332 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, bus_type);
333
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500334 break;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500335 case V4L2_FWNODE_BUS_TYPE_CSI2_DPHY:
336 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
Sakari Ailus276565e2018-07-18 09:33:45 -0400337 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
338 bus_type);
Sakari Ailus175b18b2018-01-02 08:14:30 -0500339 if (rval)
340 return rval;
341
342 break;
343 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
344 case V4L2_FWNODE_BUS_TYPE_BT656:
345 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep, bus_type);
346
347 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500348 default:
349 pr_warn("unsupported bus type %u\n", bus_type);
350 return -EINVAL;
351 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500352
Sakari Ailus32593dd2018-07-18 09:20:34 -0400353 fwnode_graph_parse_endpoint(fwnode, &vep->base);
354
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500355 return 0;
356}
357
358int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
359 struct v4l2_fwnode_endpoint *vep)
360{
361 int ret;
362
363 ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
364
365 pr_debug("===== end V4L2 endpoint properties\n");
366
367 return ret;
Sakari Ailusca50c192016-08-12 08:05:51 -0300368}
369EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
370
Sakari Ailusca50c192016-08-12 08:05:51 -0300371void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
372{
373 if (IS_ERR_OR_NULL(vep))
374 return;
375
376 kfree(vep->link_frequencies);
Sakari Ailusca50c192016-08-12 08:05:51 -0300377}
378EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
379
Sakari Ailus6970d372018-06-02 12:19:35 -0400380int v4l2_fwnode_endpoint_alloc_parse(
381 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300382{
Sakari Ailusca50c192016-08-12 08:05:51 -0300383 int rval;
384
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500385 rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
Sakari Ailusca50c192016-08-12 08:05:51 -0300386 if (rval < 0)
Sakari Ailus6970d372018-06-02 12:19:35 -0400387 return rval;
Sakari Ailusca50c192016-08-12 08:05:51 -0300388
389 rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
390 NULL, 0);
Sakari Ailus06f81522017-06-20 09:14:43 -0400391 if (rval > 0) {
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500392 unsigned int i;
393
Sakari Ailus06f81522017-06-20 09:14:43 -0400394 vep->link_frequencies =
395 kmalloc_array(rval, sizeof(*vep->link_frequencies),
396 GFP_KERNEL);
Sakari Ailus6970d372018-06-02 12:19:35 -0400397 if (!vep->link_frequencies)
398 return -ENOMEM;
Sakari Ailusca50c192016-08-12 08:05:51 -0300399
Sakari Ailus06f81522017-06-20 09:14:43 -0400400 vep->nr_of_link_frequencies = rval;
401
402 rval = fwnode_property_read_u64_array(
403 fwnode, "link-frequencies", vep->link_frequencies,
404 vep->nr_of_link_frequencies);
Sakari Ailus6970d372018-06-02 12:19:35 -0400405 if (rval < 0) {
406 v4l2_fwnode_endpoint_free(vep);
407 return rval;
408 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500409
410 for (i = 0; i < vep->nr_of_link_frequencies; i++)
411 pr_info("link-frequencies %u value %llu\n", i,
412 vep->link_frequencies[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -0300413 }
414
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500415 pr_debug("===== end V4L2 endpoint properties\n");
416
Sakari Ailus6970d372018-06-02 12:19:35 -0400417 return 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300418}
419EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
420
Sakari Ailusca50c192016-08-12 08:05:51 -0300421int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
422 struct v4l2_fwnode_link *link)
423{
424 const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
425 struct fwnode_handle *fwnode;
426
427 memset(link, 0, sizeof(*link));
428
429 fwnode = fwnode_get_parent(__fwnode);
430 fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
431 fwnode = fwnode_get_next_parent(fwnode);
432 if (is_of_node(fwnode) &&
433 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
434 fwnode = fwnode_get_next_parent(fwnode);
435 link->local_node = fwnode;
436
437 fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
438 if (!fwnode) {
439 fwnode_handle_put(fwnode);
440 return -ENOLINK;
441 }
442
443 fwnode = fwnode_get_parent(fwnode);
444 fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
445 fwnode = fwnode_get_next_parent(fwnode);
446 if (is_of_node(fwnode) &&
447 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
448 fwnode = fwnode_get_next_parent(fwnode);
449 link->remote_node = fwnode;
450
451 return 0;
452}
453EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
454
Sakari Ailusca50c192016-08-12 08:05:51 -0300455void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
456{
457 fwnode_handle_put(link->local_node);
458 fwnode_handle_put(link->remote_node);
459}
460EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
461
Sakari Ailus9ca46532017-08-17 11:28:21 -0400462static int v4l2_async_notifier_fwnode_parse_endpoint(
463 struct device *dev, struct v4l2_async_notifier *notifier,
464 struct fwnode_handle *endpoint, unsigned int asd_struct_size,
465 int (*parse_endpoint)(struct device *dev,
466 struct v4l2_fwnode_endpoint *vep,
467 struct v4l2_async_subdev *asd))
468{
Sakari Ailus6970d372018-06-02 12:19:35 -0400469 struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
Sakari Ailus9ca46532017-08-17 11:28:21 -0400470 struct v4l2_async_subdev *asd;
Sakari Ailus6970d372018-06-02 12:19:35 -0400471 int ret;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400472
473 asd = kzalloc(asd_struct_size, GFP_KERNEL);
474 if (!asd)
475 return -ENOMEM;
476
477 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400478 asd->match.fwnode =
Sakari Ailus9ca46532017-08-17 11:28:21 -0400479 fwnode_graph_get_remote_port_parent(endpoint);
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400480 if (!asd->match.fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400481 dev_warn(dev, "bad remote port parent\n");
Steve Longerbeam4382f372018-09-29 15:54:04 -0400482 ret = -ENOTCONN;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400483 goto out_err;
484 }
485
Sakari Ailus6970d372018-06-02 12:19:35 -0400486 ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
487 if (ret) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400488 dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
489 ret);
490 goto out_err;
491 }
492
Sakari Ailus6970d372018-06-02 12:19:35 -0400493 ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400494 if (ret == -ENOTCONN)
Sakari Ailus6970d372018-06-02 12:19:35 -0400495 dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
496 vep.base.id);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400497 else if (ret < 0)
498 dev_warn(dev,
499 "driver could not parse port@%u/endpoint@%u (%d)\n",
Sakari Ailus6970d372018-06-02 12:19:35 -0400500 vep.base.port, vep.base.id, ret);
501 v4l2_fwnode_endpoint_free(&vep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400502 if (ret < 0)
503 goto out_err;
504
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400505 ret = v4l2_async_notifier_add_subdev(notifier, asd);
506 if (ret < 0) {
507 /* not an error if asd already exists */
508 if (ret == -EEXIST)
509 ret = 0;
510 goto out_err;
511 }
Sakari Ailus9ca46532017-08-17 11:28:21 -0400512
513 return 0;
514
515out_err:
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400516 fwnode_handle_put(asd->match.fwnode);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400517 kfree(asd);
518
519 return ret == -ENOTCONN ? 0 : ret;
520}
521
522static int __v4l2_async_notifier_parse_fwnode_endpoints(
523 struct device *dev, struct v4l2_async_notifier *notifier,
524 size_t asd_struct_size, unsigned int port, bool has_port,
525 int (*parse_endpoint)(struct device *dev,
526 struct v4l2_fwnode_endpoint *vep,
527 struct v4l2_async_subdev *asd))
528{
529 struct fwnode_handle *fwnode;
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400530 int ret = 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400531
532 if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
533 return -EINVAL;
534
Sakari Ailus106ee382017-12-21 07:11:19 -0500535 fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400536 struct fwnode_handle *dev_fwnode;
537 bool is_available;
538
539 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
540 is_available = fwnode_device_is_available(dev_fwnode);
541 fwnode_handle_put(dev_fwnode);
542 if (!is_available)
543 continue;
544
545 if (has_port) {
546 struct fwnode_endpoint ep;
547
548 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400549 if (ret)
550 break;
551
552 if (ep.port != port)
553 continue;
554 }
555
556 ret = v4l2_async_notifier_fwnode_parse_endpoint(
557 dev, notifier, fwnode, asd_struct_size, parse_endpoint);
558 if (ret < 0)
559 break;
560 }
561
562 fwnode_handle_put(fwnode);
563
564 return ret;
565}
566
567int v4l2_async_notifier_parse_fwnode_endpoints(
568 struct device *dev, struct v4l2_async_notifier *notifier,
569 size_t asd_struct_size,
570 int (*parse_endpoint)(struct device *dev,
571 struct v4l2_fwnode_endpoint *vep,
572 struct v4l2_async_subdev *asd))
573{
574 return __v4l2_async_notifier_parse_fwnode_endpoints(
575 dev, notifier, asd_struct_size, 0, false, parse_endpoint);
576}
577EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
578
579int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
580 struct device *dev, struct v4l2_async_notifier *notifier,
581 size_t asd_struct_size, unsigned int port,
582 int (*parse_endpoint)(struct device *dev,
583 struct v4l2_fwnode_endpoint *vep,
584 struct v4l2_async_subdev *asd))
585{
586 return __v4l2_async_notifier_parse_fwnode_endpoints(
587 dev, notifier, asd_struct_size, port, true, parse_endpoint);
588}
589EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
590
Sakari Ailusd8428532017-06-20 07:47:24 -0400591/*
592 * v4l2_fwnode_reference_parse - parse references for async sub-devices
593 * @dev: the device node the properties of which are parsed for references
594 * @notifier: the async notifier where the async subdevs will be added
595 * @prop: the name of the property
596 *
597 * Return: 0 on success
598 * -ENOENT if no entries were found
599 * -ENOMEM if memory allocation failed
600 * -EINVAL if property parsing failed
601 */
602static int v4l2_fwnode_reference_parse(
603 struct device *dev, struct v4l2_async_notifier *notifier,
604 const char *prop)
605{
606 struct fwnode_reference_args args;
607 unsigned int index;
608 int ret;
609
610 for (index = 0;
611 !(ret = fwnode_property_get_reference_args(
612 dev_fwnode(dev), prop, NULL, 0, index, &args));
613 index++)
614 fwnode_handle_put(args.fwnode);
615
616 if (!index)
617 return -ENOENT;
618
619 /*
620 * Note that right now both -ENODATA and -ENOENT may signal
621 * out-of-bounds access. Return the error in cases other than that.
622 */
623 if (ret != -ENOENT && ret != -ENODATA)
624 return ret;
625
Sakari Ailusd8428532017-06-20 07:47:24 -0400626 for (index = 0; !fwnode_property_get_reference_args(
627 dev_fwnode(dev), prop, NULL, 0, index, &args);
628 index++) {
629 struct v4l2_async_subdev *asd;
630
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400631 asd = v4l2_async_notifier_add_fwnode_subdev(
632 notifier, args.fwnode, sizeof(*asd));
633 if (IS_ERR(asd)) {
634 ret = PTR_ERR(asd);
635 /* not an error if asd already exists */
636 if (ret == -EEXIST) {
637 fwnode_handle_put(args.fwnode);
638 continue;
639 }
640
Sakari Ailusd8428532017-06-20 07:47:24 -0400641 goto error;
642 }
Sakari Ailusd8428532017-06-20 07:47:24 -0400643 }
644
645 return 0;
646
647error:
648 fwnode_handle_put(args.fwnode);
649 return ret;
650}
651
Sakari Ailusa1699a42017-06-22 08:03:28 -0400652/*
653 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
654 * arguments
655 * @fwnode: fwnode to read @prop from
656 * @notifier: notifier for @dev
657 * @prop: the name of the property
658 * @index: the index of the reference to get
659 * @props: the array of integer property names
660 * @nprops: the number of integer property names in @nprops
661 *
662 * First find an fwnode referred to by the reference at @index in @prop.
663 *
664 * Then under that fwnode, @nprops times, for each property in @props,
665 * iteratively follow child nodes starting from fwnode such that they have the
666 * property in @props array at the index of the child node distance from the
667 * root node and the value of that property matching with the integer argument
668 * of the reference, at the same index.
669 *
670 * The child fwnode reched at the end of the iteration is then returned to the
671 * caller.
672 *
673 * The core reason for this is that you cannot refer to just any node in ACPI.
674 * So to refer to an endpoint (easy in DT) you need to refer to a device, then
675 * provide a list of (property name, property value) tuples where each tuple
676 * uniquely identifies a child node. The first tuple identifies a child directly
677 * underneath the device fwnode, the next tuple identifies a child node
678 * underneath the fwnode identified by the previous tuple, etc. until you
679 * reached the fwnode you need.
680 *
681 * An example with a graph, as defined in Documentation/acpi/dsd/graph.txt:
682 *
683 * Scope (\_SB.PCI0.I2C2)
684 * {
685 * Device (CAM0)
686 * {
687 * Name (_DSD, Package () {
688 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
689 * Package () {
690 * Package () {
691 * "compatible",
692 * Package () { "nokia,smia" }
693 * },
694 * },
695 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
696 * Package () {
697 * Package () { "port0", "PRT0" },
698 * }
699 * })
700 * Name (PRT0, Package() {
701 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
702 * Package () {
703 * Package () { "port", 0 },
704 * },
705 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
706 * Package () {
707 * Package () { "endpoint0", "EP00" },
708 * }
709 * })
710 * Name (EP00, Package() {
711 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
712 * Package () {
713 * Package () { "endpoint", 0 },
714 * Package () {
715 * "remote-endpoint",
716 * Package() {
717 * \_SB.PCI0.ISP, 4, 0
718 * }
719 * },
720 * }
721 * })
722 * }
723 * }
724 *
725 * Scope (\_SB.PCI0)
726 * {
727 * Device (ISP)
728 * {
729 * Name (_DSD, Package () {
730 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
731 * Package () {
732 * Package () { "port4", "PRT4" },
733 * }
734 * })
735 *
736 * Name (PRT4, Package() {
737 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
738 * Package () {
739 * Package () { "port", 4 },
740 * },
741 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
742 * Package () {
743 * Package () { "endpoint0", "EP40" },
744 * }
745 * })
746 *
747 * Name (EP40, Package() {
748 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
749 * Package () {
750 * Package () { "endpoint", 0 },
751 * Package () {
752 * "remote-endpoint",
753 * Package () {
754 * \_SB.PCI0.I2C2.CAM0,
755 * 0, 0
756 * }
757 * },
758 * }
759 * })
760 * }
761 * }
762 *
763 * From the EP40 node under ISP device, you could parse the graph remote
764 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
765 *
766 * @fwnode: fwnode referring to EP40 under ISP.
767 * @prop: "remote-endpoint"
768 * @index: 0
769 * @props: "port", "endpoint"
770 * @nprops: 2
771 *
772 * And you'd get back fwnode referring to EP00 under CAM0.
773 *
774 * The same works the other way around: if you use EP00 under CAM0 as the
775 * fwnode, you'll get fwnode referring to EP40 under ISP.
776 *
777 * The same example in DT syntax would look like this:
778 *
779 * cam: cam0 {
780 * compatible = "nokia,smia";
781 *
782 * port {
783 * port = <0>;
784 * endpoint {
785 * endpoint = <0>;
786 * remote-endpoint = <&isp 4 0>;
787 * };
788 * };
789 * };
790 *
791 * isp: isp {
792 * ports {
793 * port@4 {
794 * port = <4>;
795 * endpoint {
796 * endpoint = <0>;
797 * remote-endpoint = <&cam 0 0>;
798 * };
799 * };
800 * };
801 * };
802 *
803 * Return: 0 on success
804 * -ENOENT if no entries (or the property itself) were found
805 * -EINVAL if property parsing otherwise failed
806 * -ENOMEM if memory allocation failed
807 */
808static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop(
809 struct fwnode_handle *fwnode, const char *prop, unsigned int index,
810 const char * const *props, unsigned int nprops)
811{
812 struct fwnode_reference_args fwnode_args;
Sakari Ailus977d5ad2018-07-17 17:19:11 +0300813 u64 *args = fwnode_args.args;
Sakari Ailusa1699a42017-06-22 08:03:28 -0400814 struct fwnode_handle *child;
815 int ret;
816
817 /*
818 * Obtain remote fwnode as well as the integer arguments.
819 *
820 * Note that right now both -ENODATA and -ENOENT may signal
821 * out-of-bounds access. Return -ENOENT in that case.
822 */
823 ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
824 index, &fwnode_args);
825 if (ret)
826 return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
827
828 /*
829 * Find a node in the tree under the referred fwnode corresponding to
830 * the integer arguments.
831 */
832 fwnode = fwnode_args.fwnode;
833 while (nprops--) {
834 u32 val;
835
836 /* Loop over all child nodes under fwnode. */
837 fwnode_for_each_child_node(fwnode, child) {
838 if (fwnode_property_read_u32(child, *props, &val))
839 continue;
840
841 /* Found property, see if its value matches. */
842 if (val == *args)
843 break;
844 }
845
846 fwnode_handle_put(fwnode);
847
848 /* No property found; return an error here. */
849 if (!child) {
850 fwnode = ERR_PTR(-ENOENT);
851 break;
852 }
853
854 props++;
855 args++;
856 fwnode = child;
857 }
858
859 return fwnode;
860}
861
862/*
863 * v4l2_fwnode_reference_parse_int_props - parse references for async
864 * sub-devices
865 * @dev: struct device pointer
866 * @notifier: notifier for @dev
867 * @prop: the name of the property
868 * @props: the array of integer property names
869 * @nprops: the number of integer properties
870 *
871 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
872 * property @prop with integer arguments with child nodes matching in properties
873 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
874 * accordingly.
875 *
876 * While it is technically possible to use this function on DT, it is only
877 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
878 * on ACPI the references are limited to devices.
879 *
880 * Return: 0 on success
881 * -ENOENT if no entries (or the property itself) were found
882 * -EINVAL if property parsing otherwisefailed
883 * -ENOMEM if memory allocation failed
884 */
885static int v4l2_fwnode_reference_parse_int_props(
886 struct device *dev, struct v4l2_async_notifier *notifier,
887 const char *prop, const char * const *props, unsigned int nprops)
888{
889 struct fwnode_handle *fwnode;
890 unsigned int index;
891 int ret;
892
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400893 index = 0;
894 do {
895 fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
896 prop, index,
897 props, nprops);
898 if (IS_ERR(fwnode)) {
899 /*
900 * Note that right now both -ENODATA and -ENOENT may
901 * signal out-of-bounds access. Return the error in
902 * cases other than that.
903 */
904 if (PTR_ERR(fwnode) != -ENOENT &&
905 PTR_ERR(fwnode) != -ENODATA)
906 return PTR_ERR(fwnode);
907 break;
908 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400909 fwnode_handle_put(fwnode);
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400910 index++;
911 } while (1);
Sakari Ailusa1699a42017-06-22 08:03:28 -0400912
Sakari Ailusa1699a42017-06-22 08:03:28 -0400913 for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
914 dev_fwnode(dev), prop, index, props,
915 nprops))); index++) {
916 struct v4l2_async_subdev *asd;
917
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400918 asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode,
919 sizeof(*asd));
920 if (IS_ERR(asd)) {
921 ret = PTR_ERR(asd);
922 /* not an error if asd already exists */
923 if (ret == -EEXIST) {
924 fwnode_handle_put(fwnode);
925 continue;
926 }
927
Sakari Ailusa1699a42017-06-22 08:03:28 -0400928 goto error;
929 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400930 }
931
932 return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
933
934error:
935 fwnode_handle_put(fwnode);
936 return ret;
937}
938
Sakari Ailus7a9ec802017-09-06 08:35:42 -0400939int v4l2_async_notifier_parse_fwnode_sensor_common(
940 struct device *dev, struct v4l2_async_notifier *notifier)
941{
942 static const char * const led_props[] = { "led" };
943 static const struct {
944 const char *name;
945 const char * const *props;
946 unsigned int nprops;
947 } props[] = {
948 { "flash-leds", led_props, ARRAY_SIZE(led_props) },
949 { "lens-focus", NULL, 0 },
950 };
951 unsigned int i;
952
953 for (i = 0; i < ARRAY_SIZE(props); i++) {
954 int ret;
955
956 if (props[i].props && is_acpi_node(dev_fwnode(dev)))
957 ret = v4l2_fwnode_reference_parse_int_props(
958 dev, notifier, props[i].name,
959 props[i].props, props[i].nprops);
960 else
961 ret = v4l2_fwnode_reference_parse(
962 dev, notifier, props[i].name);
963 if (ret && ret != -ENOENT) {
964 dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
965 props[i].name, ret);
966 return ret;
967 }
968 }
969
970 return 0;
971}
972EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common);
973
Sakari Ailusaef69d52017-09-24 18:47:44 -0400974int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd)
975{
976 struct v4l2_async_notifier *notifier;
977 int ret;
978
979 if (WARN_ON(!sd->dev))
980 return -ENODEV;
981
982 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
983 if (!notifier)
984 return -ENOMEM;
985
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400986 v4l2_async_notifier_init(notifier);
987
Sakari Ailusaef69d52017-09-24 18:47:44 -0400988 ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev,
989 notifier);
990 if (ret < 0)
991 goto out_cleanup;
992
993 ret = v4l2_async_subdev_notifier_register(sd, notifier);
994 if (ret < 0)
995 goto out_cleanup;
996
997 ret = v4l2_async_register_subdev(sd);
998 if (ret < 0)
999 goto out_unregister;
1000
1001 sd->subdev_notifier = notifier;
1002
1003 return 0;
1004
1005out_unregister:
1006 v4l2_async_notifier_unregister(notifier);
1007
1008out_cleanup:
1009 v4l2_async_notifier_cleanup(notifier);
1010 kfree(notifier);
1011
1012 return ret;
1013}
1014EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common);
1015
Steve Longerbeam1634f0e2018-09-29 15:54:09 -04001016int v4l2_async_register_fwnode_subdev(
1017 struct v4l2_subdev *sd, size_t asd_struct_size,
1018 unsigned int *ports, unsigned int num_ports,
1019 int (*parse_endpoint)(struct device *dev,
1020 struct v4l2_fwnode_endpoint *vep,
1021 struct v4l2_async_subdev *asd))
1022{
1023 struct v4l2_async_notifier *notifier;
1024 struct device *dev = sd->dev;
1025 struct fwnode_handle *fwnode;
1026 int ret;
1027
1028 if (WARN_ON(!dev))
1029 return -ENODEV;
1030
1031 fwnode = dev_fwnode(dev);
1032 if (!fwnode_device_is_available(fwnode))
1033 return -ENODEV;
1034
1035 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
1036 if (!notifier)
1037 return -ENOMEM;
1038
1039 v4l2_async_notifier_init(notifier);
1040
1041 if (!ports) {
1042 ret = v4l2_async_notifier_parse_fwnode_endpoints(
1043 dev, notifier, asd_struct_size, parse_endpoint);
1044 if (ret < 0)
1045 goto out_cleanup;
1046 } else {
1047 unsigned int i;
1048
1049 for (i = 0; i < num_ports; i++) {
1050 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
1051 dev, notifier, asd_struct_size,
1052 ports[i], parse_endpoint);
1053 if (ret < 0)
1054 goto out_cleanup;
1055 }
1056 }
1057
1058 ret = v4l2_async_subdev_notifier_register(sd, notifier);
1059 if (ret < 0)
1060 goto out_cleanup;
1061
1062 ret = v4l2_async_register_subdev(sd);
1063 if (ret < 0)
1064 goto out_unregister;
1065
1066 sd->subdev_notifier = notifier;
1067
1068 return 0;
1069
1070out_unregister:
1071 v4l2_async_notifier_unregister(notifier);
1072out_cleanup:
1073 v4l2_async_notifier_cleanup(notifier);
1074 kfree(notifier);
1075
1076 return ret;
1077}
1078EXPORT_SYMBOL_GPL(v4l2_async_register_fwnode_subdev);
1079
Sakari Ailusca50c192016-08-12 08:05:51 -03001080MODULE_LICENSE("GPL");
1081MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1082MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1083MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");