blob: 9d99f0a3861f88209589fd3125e9b746f41a31da [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 Ailus276565e2018-07-18 09:33:45 -040058 if (bus_type == V4L2_FWNODE_BUS_TYPE_CSI2_DPHY)
59 num_data_lanes = min_t(u32, bus->num_data_lanes,
60 V4L2_FWNODE_CSI2_MAX_DATA_LANES);
61
Sakari Ailusca50c192016-08-12 08:05:51 -030062 rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
63 if (rval > 0) {
Sakari Ailus276565e2018-07-18 09:33:45 -040064 num_data_lanes =
Sakari Ailusad3cdf32017-08-14 06:43:07 -040065 min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
Sakari Ailusca50c192016-08-12 08:05:51 -030066
67 fwnode_property_read_u32_array(fwnode, "data-lanes", array,
Sakari Ailus276565e2018-07-18 09:33:45 -040068 num_data_lanes);
Sakari Ailusca50c192016-08-12 08:05:51 -030069
Sakari Ailus276565e2018-07-18 09:33:45 -040070 for (i = 0; i < num_data_lanes; i++) {
Sakari Ailusca50c192016-08-12 08:05:51 -030071 if (lanes_used & BIT(array[i]))
72 pr_warn("duplicated lane %u in data-lanes\n",
73 array[i]);
74 lanes_used |= BIT(array[i]);
75
Sakari Ailusc8677aa2017-12-04 16:25:06 -050076 pr_debug("lane %u position %u\n", i, array[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -030077 }
Sakari Ailus276565e2018-07-18 09:33:45 -040078 }
Sakari Ailusca50c192016-08-12 08:05:51 -030079
Sakari Ailus276565e2018-07-18 09:33:45 -040080 rval = fwnode_property_read_u32_array(fwnode, "lane-polarities", NULL,
81 0);
82 if (rval > 0) {
83 if (rval != 1 + num_data_lanes /* clock+data */) {
84 pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
85 1 + num_data_lanes, rval);
86 return -EINVAL;
Sakari Ailusca50c192016-08-12 08:05:51 -030087 }
Sakari Ailusb24f0212017-08-14 06:15:21 -040088
Sakari Ailusaf11a742018-07-03 07:06:20 -040089 have_lane_polarities = true;
Sakari Ailusca50c192016-08-12 08:05:51 -030090 }
91
92 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
93 if (lanes_used & BIT(v))
94 pr_warn("duplicated lane %u in clock-lanes\n", v);
95 lanes_used |= BIT(v);
96
97 bus->clock_lane = v;
98 have_clk_lane = true;
Sakari Ailusc8677aa2017-12-04 16:25:06 -050099 pr_debug("clock lane position %u\n", v);
Sakari Ailusca50c192016-08-12 08:05:51 -0300100 }
101
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500102 if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300103 flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500104 pr_debug("non-continuous clock\n");
Sakari Ailusd4865322018-07-20 05:04:29 -0400105 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300106 flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500107 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300108
Sakari Ailus276565e2018-07-18 09:33:45 -0400109 if (bus_type == V4L2_FWNODE_BUS_TYPE_CSI2_DPHY || lanes_used ||
110 have_clk_lane || (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) {
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400111 bus->flags = flags;
112 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
Sakari Ailus276565e2018-07-18 09:33:45 -0400113 bus->num_data_lanes = num_data_lanes;
Sakari Ailusaf11a742018-07-03 07:06:20 -0400114 for (i = 0; i < num_data_lanes; i++)
115 bus->data_lanes[i] = array[i];
116
117 if (have_lane_polarities) {
118 fwnode_property_read_u32_array(fwnode,
119 "lane-polarities", array,
120 1 + num_data_lanes);
121
122 for (i = 0; i < 1 + num_data_lanes; i++) {
123 bus->lane_polarities[i] = array[i];
124 pr_debug("lane %u polarity %sinverted",
125 i, array[i] ? "" : "not ");
126 }
127 } else {
128 pr_debug("no lane polarities defined, assuming not inverted\n");
129 }
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400130 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300131
132 return 0;
133}
134
Sakari Ailus175b18b2018-01-02 08:14:30 -0500135#define PARALLEL_MBUS_FLAGS (V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
136 V4L2_MBUS_HSYNC_ACTIVE_LOW | \
137 V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
138 V4L2_MBUS_VSYNC_ACTIVE_LOW | \
139 V4L2_MBUS_FIELD_EVEN_HIGH | \
140 V4L2_MBUS_FIELD_EVEN_LOW)
141
Sakari Ailusca50c192016-08-12 08:05:51 -0300142static void v4l2_fwnode_endpoint_parse_parallel_bus(
Sakari Ailus175b18b2018-01-02 08:14:30 -0500143 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep,
144 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailusca50c192016-08-12 08:05:51 -0300145{
146 struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
147 unsigned int flags = 0;
148 u32 v;
149
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500150 if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300151 flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
152 V4L2_MBUS_HSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500153 pr_debug("hsync-active %s\n", v ? "high" : "low");
154 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300155
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500156 if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300157 flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
158 V4L2_MBUS_VSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500159 pr_debug("vsync-active %s\n", v ? "high" : "low");
160 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300161
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500162 if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300163 flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
164 V4L2_MBUS_FIELD_EVEN_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500165 pr_debug("field-even-active %s\n", v ? "high" : "low");
166 }
167
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500168 if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300169 flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
170 V4L2_MBUS_PCLK_SAMPLE_FALLING;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500171 pr_debug("pclk-sample %s\n", v ? "high" : "low");
172 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300173
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500174 if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300175 flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
176 V4L2_MBUS_DATA_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500177 pr_debug("data-active %s\n", v ? "high" : "low");
178 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300179
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500180 if (fwnode_property_present(fwnode, "slave-mode")) {
181 pr_debug("slave mode\n");
Sakari Ailusca50c192016-08-12 08:05:51 -0300182 flags |= V4L2_MBUS_SLAVE;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500183 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300184 flags |= V4L2_MBUS_MASTER;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500185 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300186
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500187 if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300188 bus->bus_width = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500189 pr_debug("bus-width %u\n", v);
190 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300191
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500192 if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300193 bus->data_shift = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500194 pr_debug("data-shift %u\n", v);
195 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300196
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500197 if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300198 flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
199 V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500200 pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
201 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300202
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500203 if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400204 flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
205 V4L2_MBUS_DATA_ENABLE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500206 pr_debug("data-enable-active %s\n", v ? "high" : "low");
207 }
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400208
Sakari Ailus175b18b2018-01-02 08:14:30 -0500209 switch (bus_type) {
210 default:
211 bus->flags = flags;
212 if (flags & PARALLEL_MBUS_FLAGS)
213 vep->bus_type = V4L2_MBUS_PARALLEL;
214 else
215 vep->bus_type = V4L2_MBUS_BT656;
216 break;
217 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400218 vep->bus_type = V4L2_MBUS_PARALLEL;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500219 bus->flags = flags;
220 break;
221 case V4L2_FWNODE_BUS_TYPE_BT656:
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400222 vep->bus_type = V4L2_MBUS_BT656;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500223 bus->flags = flags & ~PARALLEL_MBUS_FLAGS;
224 break;
225 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300226}
227
Mauro Carvalho Chehababc5b2c2017-07-20 16:27:27 -0400228static void
229v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
230 struct v4l2_fwnode_endpoint *vep,
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400231 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500232{
233 struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
234 u32 v;
235
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500236 if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500237 bus->clock_inv = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500238 pr_debug("clock-inv %u\n", v);
239 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500240
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500241 if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500242 bus->strobe = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500243 pr_debug("strobe %u\n", v);
244 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500245
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500246 if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500247 bus->data_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500248 pr_debug("data-lanes %u\n", v);
249 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500250
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500251 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500252 bus->clock_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500253 pr_debug("clock-lanes %u\n", v);
254 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500255
256 if (bus_type == V4L2_FWNODE_BUS_TYPE_CCP2)
257 vep->bus_type = V4L2_MBUS_CCP2;
258 else
259 vep->bus_type = V4L2_MBUS_CSI1;
260}
261
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500262static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
263 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300264{
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500265 u32 bus_type = 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300266 int rval;
267
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500268 pr_debug("===== begin V4L2 endpoint properties\n");
269
Sakari Ailusca50c192016-08-12 08:05:51 -0300270 fwnode_graph_parse_endpoint(fwnode, &vep->base);
271
272 /* Zero fields from bus_type to until the end */
273 memset(&vep->bus_type, 0, sizeof(*vep) -
274 offsetof(typeof(*vep), bus_type));
275
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500276 fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
277
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500278 switch (bus_type) {
279 case V4L2_FWNODE_BUS_TYPE_GUESS:
Sakari Ailus276565e2018-07-18 09:33:45 -0400280 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
281 bus_type);
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500282 if (rval)
283 return rval;
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400284
285 if (vep->bus_type == V4L2_MBUS_UNKNOWN)
Sakari Ailus175b18b2018-01-02 08:14:30 -0500286 v4l2_fwnode_endpoint_parse_parallel_bus(
287 fwnode, vep, V4L2_MBUS_UNKNOWN);
Sakari Ailusca50c192016-08-12 08:05:51 -0300288
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500289 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500290 case V4L2_FWNODE_BUS_TYPE_CCP2:
291 case V4L2_FWNODE_BUS_TYPE_CSI1:
292 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, bus_type);
293
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500294 break;
Sakari Ailus175b18b2018-01-02 08:14:30 -0500295 case V4L2_FWNODE_BUS_TYPE_CSI2_DPHY:
296 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
Sakari Ailus276565e2018-07-18 09:33:45 -0400297 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep,
298 bus_type);
Sakari Ailus175b18b2018-01-02 08:14:30 -0500299 if (rval)
300 return rval;
301
302 break;
303 case V4L2_FWNODE_BUS_TYPE_PARALLEL:
304 case V4L2_FWNODE_BUS_TYPE_BT656:
305 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep, bus_type);
306
307 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500308 default:
309 pr_warn("unsupported bus type %u\n", bus_type);
310 return -EINVAL;
311 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500312
313 return 0;
314}
315
316int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
317 struct v4l2_fwnode_endpoint *vep)
318{
319 int ret;
320
321 ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
322
323 pr_debug("===== end V4L2 endpoint properties\n");
324
325 return ret;
Sakari Ailusca50c192016-08-12 08:05:51 -0300326}
327EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
328
Sakari Ailusca50c192016-08-12 08:05:51 -0300329void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
330{
331 if (IS_ERR_OR_NULL(vep))
332 return;
333
334 kfree(vep->link_frequencies);
Sakari Ailusca50c192016-08-12 08:05:51 -0300335}
336EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
337
Sakari Ailus6970d372018-06-02 12:19:35 -0400338int v4l2_fwnode_endpoint_alloc_parse(
339 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300340{
Sakari Ailusca50c192016-08-12 08:05:51 -0300341 int rval;
342
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500343 rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
Sakari Ailusca50c192016-08-12 08:05:51 -0300344 if (rval < 0)
Sakari Ailus6970d372018-06-02 12:19:35 -0400345 return rval;
Sakari Ailusca50c192016-08-12 08:05:51 -0300346
347 rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
348 NULL, 0);
Sakari Ailus06f81522017-06-20 09:14:43 -0400349 if (rval > 0) {
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500350 unsigned int i;
351
Sakari Ailus06f81522017-06-20 09:14:43 -0400352 vep->link_frequencies =
353 kmalloc_array(rval, sizeof(*vep->link_frequencies),
354 GFP_KERNEL);
Sakari Ailus6970d372018-06-02 12:19:35 -0400355 if (!vep->link_frequencies)
356 return -ENOMEM;
Sakari Ailusca50c192016-08-12 08:05:51 -0300357
Sakari Ailus06f81522017-06-20 09:14:43 -0400358 vep->nr_of_link_frequencies = rval;
359
360 rval = fwnode_property_read_u64_array(
361 fwnode, "link-frequencies", vep->link_frequencies,
362 vep->nr_of_link_frequencies);
Sakari Ailus6970d372018-06-02 12:19:35 -0400363 if (rval < 0) {
364 v4l2_fwnode_endpoint_free(vep);
365 return rval;
366 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500367
368 for (i = 0; i < vep->nr_of_link_frequencies; i++)
369 pr_info("link-frequencies %u value %llu\n", i,
370 vep->link_frequencies[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -0300371 }
372
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500373 pr_debug("===== end V4L2 endpoint properties\n");
374
Sakari Ailus6970d372018-06-02 12:19:35 -0400375 return 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300376}
377EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
378
Sakari Ailusca50c192016-08-12 08:05:51 -0300379int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
380 struct v4l2_fwnode_link *link)
381{
382 const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
383 struct fwnode_handle *fwnode;
384
385 memset(link, 0, sizeof(*link));
386
387 fwnode = fwnode_get_parent(__fwnode);
388 fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
389 fwnode = fwnode_get_next_parent(fwnode);
390 if (is_of_node(fwnode) &&
391 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
392 fwnode = fwnode_get_next_parent(fwnode);
393 link->local_node = fwnode;
394
395 fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
396 if (!fwnode) {
397 fwnode_handle_put(fwnode);
398 return -ENOLINK;
399 }
400
401 fwnode = fwnode_get_parent(fwnode);
402 fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
403 fwnode = fwnode_get_next_parent(fwnode);
404 if (is_of_node(fwnode) &&
405 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
406 fwnode = fwnode_get_next_parent(fwnode);
407 link->remote_node = fwnode;
408
409 return 0;
410}
411EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
412
Sakari Ailusca50c192016-08-12 08:05:51 -0300413void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
414{
415 fwnode_handle_put(link->local_node);
416 fwnode_handle_put(link->remote_node);
417}
418EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
419
Sakari Ailus9ca46532017-08-17 11:28:21 -0400420static int v4l2_async_notifier_fwnode_parse_endpoint(
421 struct device *dev, struct v4l2_async_notifier *notifier,
422 struct fwnode_handle *endpoint, unsigned int asd_struct_size,
423 int (*parse_endpoint)(struct device *dev,
424 struct v4l2_fwnode_endpoint *vep,
425 struct v4l2_async_subdev *asd))
426{
Sakari Ailus6970d372018-06-02 12:19:35 -0400427 struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
Sakari Ailus9ca46532017-08-17 11:28:21 -0400428 struct v4l2_async_subdev *asd;
Sakari Ailus6970d372018-06-02 12:19:35 -0400429 int ret;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400430
431 asd = kzalloc(asd_struct_size, GFP_KERNEL);
432 if (!asd)
433 return -ENOMEM;
434
435 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400436 asd->match.fwnode =
Sakari Ailus9ca46532017-08-17 11:28:21 -0400437 fwnode_graph_get_remote_port_parent(endpoint);
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400438 if (!asd->match.fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400439 dev_warn(dev, "bad remote port parent\n");
Steve Longerbeam4382f372018-09-29 15:54:04 -0400440 ret = -ENOTCONN;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400441 goto out_err;
442 }
443
Sakari Ailus6970d372018-06-02 12:19:35 -0400444 ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
445 if (ret) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400446 dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
447 ret);
448 goto out_err;
449 }
450
Sakari Ailus6970d372018-06-02 12:19:35 -0400451 ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400452 if (ret == -ENOTCONN)
Sakari Ailus6970d372018-06-02 12:19:35 -0400453 dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
454 vep.base.id);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400455 else if (ret < 0)
456 dev_warn(dev,
457 "driver could not parse port@%u/endpoint@%u (%d)\n",
Sakari Ailus6970d372018-06-02 12:19:35 -0400458 vep.base.port, vep.base.id, ret);
459 v4l2_fwnode_endpoint_free(&vep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400460 if (ret < 0)
461 goto out_err;
462
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400463 ret = v4l2_async_notifier_add_subdev(notifier, asd);
464 if (ret < 0) {
465 /* not an error if asd already exists */
466 if (ret == -EEXIST)
467 ret = 0;
468 goto out_err;
469 }
Sakari Ailus9ca46532017-08-17 11:28:21 -0400470
471 return 0;
472
473out_err:
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400474 fwnode_handle_put(asd->match.fwnode);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400475 kfree(asd);
476
477 return ret == -ENOTCONN ? 0 : ret;
478}
479
480static int __v4l2_async_notifier_parse_fwnode_endpoints(
481 struct device *dev, struct v4l2_async_notifier *notifier,
482 size_t asd_struct_size, unsigned int port, bool has_port,
483 int (*parse_endpoint)(struct device *dev,
484 struct v4l2_fwnode_endpoint *vep,
485 struct v4l2_async_subdev *asd))
486{
487 struct fwnode_handle *fwnode;
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400488 int ret = 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400489
490 if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
491 return -EINVAL;
492
Sakari Ailus106ee382017-12-21 07:11:19 -0500493 fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400494 struct fwnode_handle *dev_fwnode;
495 bool is_available;
496
497 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
498 is_available = fwnode_device_is_available(dev_fwnode);
499 fwnode_handle_put(dev_fwnode);
500 if (!is_available)
501 continue;
502
503 if (has_port) {
504 struct fwnode_endpoint ep;
505
506 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400507 if (ret)
508 break;
509
510 if (ep.port != port)
511 continue;
512 }
513
514 ret = v4l2_async_notifier_fwnode_parse_endpoint(
515 dev, notifier, fwnode, asd_struct_size, parse_endpoint);
516 if (ret < 0)
517 break;
518 }
519
520 fwnode_handle_put(fwnode);
521
522 return ret;
523}
524
525int v4l2_async_notifier_parse_fwnode_endpoints(
526 struct device *dev, struct v4l2_async_notifier *notifier,
527 size_t asd_struct_size,
528 int (*parse_endpoint)(struct device *dev,
529 struct v4l2_fwnode_endpoint *vep,
530 struct v4l2_async_subdev *asd))
531{
532 return __v4l2_async_notifier_parse_fwnode_endpoints(
533 dev, notifier, asd_struct_size, 0, false, parse_endpoint);
534}
535EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
536
537int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
538 struct device *dev, struct v4l2_async_notifier *notifier,
539 size_t asd_struct_size, unsigned int port,
540 int (*parse_endpoint)(struct device *dev,
541 struct v4l2_fwnode_endpoint *vep,
542 struct v4l2_async_subdev *asd))
543{
544 return __v4l2_async_notifier_parse_fwnode_endpoints(
545 dev, notifier, asd_struct_size, port, true, parse_endpoint);
546}
547EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
548
Sakari Ailusd8428532017-06-20 07:47:24 -0400549/*
550 * v4l2_fwnode_reference_parse - parse references for async sub-devices
551 * @dev: the device node the properties of which are parsed for references
552 * @notifier: the async notifier where the async subdevs will be added
553 * @prop: the name of the property
554 *
555 * Return: 0 on success
556 * -ENOENT if no entries were found
557 * -ENOMEM if memory allocation failed
558 * -EINVAL if property parsing failed
559 */
560static int v4l2_fwnode_reference_parse(
561 struct device *dev, struct v4l2_async_notifier *notifier,
562 const char *prop)
563{
564 struct fwnode_reference_args args;
565 unsigned int index;
566 int ret;
567
568 for (index = 0;
569 !(ret = fwnode_property_get_reference_args(
570 dev_fwnode(dev), prop, NULL, 0, index, &args));
571 index++)
572 fwnode_handle_put(args.fwnode);
573
574 if (!index)
575 return -ENOENT;
576
577 /*
578 * Note that right now both -ENODATA and -ENOENT may signal
579 * out-of-bounds access. Return the error in cases other than that.
580 */
581 if (ret != -ENOENT && ret != -ENODATA)
582 return ret;
583
Sakari Ailusd8428532017-06-20 07:47:24 -0400584 for (index = 0; !fwnode_property_get_reference_args(
585 dev_fwnode(dev), prop, NULL, 0, index, &args);
586 index++) {
587 struct v4l2_async_subdev *asd;
588
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400589 asd = v4l2_async_notifier_add_fwnode_subdev(
590 notifier, args.fwnode, sizeof(*asd));
591 if (IS_ERR(asd)) {
592 ret = PTR_ERR(asd);
593 /* not an error if asd already exists */
594 if (ret == -EEXIST) {
595 fwnode_handle_put(args.fwnode);
596 continue;
597 }
598
Sakari Ailusd8428532017-06-20 07:47:24 -0400599 goto error;
600 }
Sakari Ailusd8428532017-06-20 07:47:24 -0400601 }
602
603 return 0;
604
605error:
606 fwnode_handle_put(args.fwnode);
607 return ret;
608}
609
Sakari Ailusa1699a42017-06-22 08:03:28 -0400610/*
611 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
612 * arguments
613 * @fwnode: fwnode to read @prop from
614 * @notifier: notifier for @dev
615 * @prop: the name of the property
616 * @index: the index of the reference to get
617 * @props: the array of integer property names
618 * @nprops: the number of integer property names in @nprops
619 *
620 * First find an fwnode referred to by the reference at @index in @prop.
621 *
622 * Then under that fwnode, @nprops times, for each property in @props,
623 * iteratively follow child nodes starting from fwnode such that they have the
624 * property in @props array at the index of the child node distance from the
625 * root node and the value of that property matching with the integer argument
626 * of the reference, at the same index.
627 *
628 * The child fwnode reched at the end of the iteration is then returned to the
629 * caller.
630 *
631 * The core reason for this is that you cannot refer to just any node in ACPI.
632 * So to refer to an endpoint (easy in DT) you need to refer to a device, then
633 * provide a list of (property name, property value) tuples where each tuple
634 * uniquely identifies a child node. The first tuple identifies a child directly
635 * underneath the device fwnode, the next tuple identifies a child node
636 * underneath the fwnode identified by the previous tuple, etc. until you
637 * reached the fwnode you need.
638 *
639 * An example with a graph, as defined in Documentation/acpi/dsd/graph.txt:
640 *
641 * Scope (\_SB.PCI0.I2C2)
642 * {
643 * Device (CAM0)
644 * {
645 * Name (_DSD, Package () {
646 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
647 * Package () {
648 * Package () {
649 * "compatible",
650 * Package () { "nokia,smia" }
651 * },
652 * },
653 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
654 * Package () {
655 * Package () { "port0", "PRT0" },
656 * }
657 * })
658 * Name (PRT0, Package() {
659 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
660 * Package () {
661 * Package () { "port", 0 },
662 * },
663 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
664 * Package () {
665 * Package () { "endpoint0", "EP00" },
666 * }
667 * })
668 * Name (EP00, Package() {
669 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
670 * Package () {
671 * Package () { "endpoint", 0 },
672 * Package () {
673 * "remote-endpoint",
674 * Package() {
675 * \_SB.PCI0.ISP, 4, 0
676 * }
677 * },
678 * }
679 * })
680 * }
681 * }
682 *
683 * Scope (\_SB.PCI0)
684 * {
685 * Device (ISP)
686 * {
687 * Name (_DSD, Package () {
688 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
689 * Package () {
690 * Package () { "port4", "PRT4" },
691 * }
692 * })
693 *
694 * Name (PRT4, Package() {
695 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
696 * Package () {
697 * Package () { "port", 4 },
698 * },
699 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
700 * Package () {
701 * Package () { "endpoint0", "EP40" },
702 * }
703 * })
704 *
705 * Name (EP40, Package() {
706 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
707 * Package () {
708 * Package () { "endpoint", 0 },
709 * Package () {
710 * "remote-endpoint",
711 * Package () {
712 * \_SB.PCI0.I2C2.CAM0,
713 * 0, 0
714 * }
715 * },
716 * }
717 * })
718 * }
719 * }
720 *
721 * From the EP40 node under ISP device, you could parse the graph remote
722 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
723 *
724 * @fwnode: fwnode referring to EP40 under ISP.
725 * @prop: "remote-endpoint"
726 * @index: 0
727 * @props: "port", "endpoint"
728 * @nprops: 2
729 *
730 * And you'd get back fwnode referring to EP00 under CAM0.
731 *
732 * The same works the other way around: if you use EP00 under CAM0 as the
733 * fwnode, you'll get fwnode referring to EP40 under ISP.
734 *
735 * The same example in DT syntax would look like this:
736 *
737 * cam: cam0 {
738 * compatible = "nokia,smia";
739 *
740 * port {
741 * port = <0>;
742 * endpoint {
743 * endpoint = <0>;
744 * remote-endpoint = <&isp 4 0>;
745 * };
746 * };
747 * };
748 *
749 * isp: isp {
750 * ports {
751 * port@4 {
752 * port = <4>;
753 * endpoint {
754 * endpoint = <0>;
755 * remote-endpoint = <&cam 0 0>;
756 * };
757 * };
758 * };
759 * };
760 *
761 * Return: 0 on success
762 * -ENOENT if no entries (or the property itself) were found
763 * -EINVAL if property parsing otherwise failed
764 * -ENOMEM if memory allocation failed
765 */
766static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop(
767 struct fwnode_handle *fwnode, const char *prop, unsigned int index,
768 const char * const *props, unsigned int nprops)
769{
770 struct fwnode_reference_args fwnode_args;
Sakari Ailus977d5ad2018-07-17 17:19:11 +0300771 u64 *args = fwnode_args.args;
Sakari Ailusa1699a42017-06-22 08:03:28 -0400772 struct fwnode_handle *child;
773 int ret;
774
775 /*
776 * Obtain remote fwnode as well as the integer arguments.
777 *
778 * Note that right now both -ENODATA and -ENOENT may signal
779 * out-of-bounds access. Return -ENOENT in that case.
780 */
781 ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
782 index, &fwnode_args);
783 if (ret)
784 return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
785
786 /*
787 * Find a node in the tree under the referred fwnode corresponding to
788 * the integer arguments.
789 */
790 fwnode = fwnode_args.fwnode;
791 while (nprops--) {
792 u32 val;
793
794 /* Loop over all child nodes under fwnode. */
795 fwnode_for_each_child_node(fwnode, child) {
796 if (fwnode_property_read_u32(child, *props, &val))
797 continue;
798
799 /* Found property, see if its value matches. */
800 if (val == *args)
801 break;
802 }
803
804 fwnode_handle_put(fwnode);
805
806 /* No property found; return an error here. */
807 if (!child) {
808 fwnode = ERR_PTR(-ENOENT);
809 break;
810 }
811
812 props++;
813 args++;
814 fwnode = child;
815 }
816
817 return fwnode;
818}
819
820/*
821 * v4l2_fwnode_reference_parse_int_props - parse references for async
822 * sub-devices
823 * @dev: struct device pointer
824 * @notifier: notifier for @dev
825 * @prop: the name of the property
826 * @props: the array of integer property names
827 * @nprops: the number of integer properties
828 *
829 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
830 * property @prop with integer arguments with child nodes matching in properties
831 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
832 * accordingly.
833 *
834 * While it is technically possible to use this function on DT, it is only
835 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
836 * on ACPI the references are limited to devices.
837 *
838 * Return: 0 on success
839 * -ENOENT if no entries (or the property itself) were found
840 * -EINVAL if property parsing otherwisefailed
841 * -ENOMEM if memory allocation failed
842 */
843static int v4l2_fwnode_reference_parse_int_props(
844 struct device *dev, struct v4l2_async_notifier *notifier,
845 const char *prop, const char * const *props, unsigned int nprops)
846{
847 struct fwnode_handle *fwnode;
848 unsigned int index;
849 int ret;
850
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400851 index = 0;
852 do {
853 fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
854 prop, index,
855 props, nprops);
856 if (IS_ERR(fwnode)) {
857 /*
858 * Note that right now both -ENODATA and -ENOENT may
859 * signal out-of-bounds access. Return the error in
860 * cases other than that.
861 */
862 if (PTR_ERR(fwnode) != -ENOENT &&
863 PTR_ERR(fwnode) != -ENODATA)
864 return PTR_ERR(fwnode);
865 break;
866 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400867 fwnode_handle_put(fwnode);
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400868 index++;
869 } while (1);
Sakari Ailusa1699a42017-06-22 08:03:28 -0400870
Sakari Ailusa1699a42017-06-22 08:03:28 -0400871 for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
872 dev_fwnode(dev), prop, index, props,
873 nprops))); index++) {
874 struct v4l2_async_subdev *asd;
875
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400876 asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode,
877 sizeof(*asd));
878 if (IS_ERR(asd)) {
879 ret = PTR_ERR(asd);
880 /* not an error if asd already exists */
881 if (ret == -EEXIST) {
882 fwnode_handle_put(fwnode);
883 continue;
884 }
885
Sakari Ailusa1699a42017-06-22 08:03:28 -0400886 goto error;
887 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400888 }
889
890 return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
891
892error:
893 fwnode_handle_put(fwnode);
894 return ret;
895}
896
Sakari Ailus7a9ec802017-09-06 08:35:42 -0400897int v4l2_async_notifier_parse_fwnode_sensor_common(
898 struct device *dev, struct v4l2_async_notifier *notifier)
899{
900 static const char * const led_props[] = { "led" };
901 static const struct {
902 const char *name;
903 const char * const *props;
904 unsigned int nprops;
905 } props[] = {
906 { "flash-leds", led_props, ARRAY_SIZE(led_props) },
907 { "lens-focus", NULL, 0 },
908 };
909 unsigned int i;
910
911 for (i = 0; i < ARRAY_SIZE(props); i++) {
912 int ret;
913
914 if (props[i].props && is_acpi_node(dev_fwnode(dev)))
915 ret = v4l2_fwnode_reference_parse_int_props(
916 dev, notifier, props[i].name,
917 props[i].props, props[i].nprops);
918 else
919 ret = v4l2_fwnode_reference_parse(
920 dev, notifier, props[i].name);
921 if (ret && ret != -ENOENT) {
922 dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
923 props[i].name, ret);
924 return ret;
925 }
926 }
927
928 return 0;
929}
930EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common);
931
Sakari Ailusaef69d52017-09-24 18:47:44 -0400932int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd)
933{
934 struct v4l2_async_notifier *notifier;
935 int ret;
936
937 if (WARN_ON(!sd->dev))
938 return -ENODEV;
939
940 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
941 if (!notifier)
942 return -ENOMEM;
943
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400944 v4l2_async_notifier_init(notifier);
945
Sakari Ailusaef69d52017-09-24 18:47:44 -0400946 ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev,
947 notifier);
948 if (ret < 0)
949 goto out_cleanup;
950
951 ret = v4l2_async_subdev_notifier_register(sd, notifier);
952 if (ret < 0)
953 goto out_cleanup;
954
955 ret = v4l2_async_register_subdev(sd);
956 if (ret < 0)
957 goto out_unregister;
958
959 sd->subdev_notifier = notifier;
960
961 return 0;
962
963out_unregister:
964 v4l2_async_notifier_unregister(notifier);
965
966out_cleanup:
967 v4l2_async_notifier_cleanup(notifier);
968 kfree(notifier);
969
970 return ret;
971}
972EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common);
973
Steve Longerbeam1634f0e2018-09-29 15:54:09 -0400974int v4l2_async_register_fwnode_subdev(
975 struct v4l2_subdev *sd, size_t asd_struct_size,
976 unsigned int *ports, unsigned int num_ports,
977 int (*parse_endpoint)(struct device *dev,
978 struct v4l2_fwnode_endpoint *vep,
979 struct v4l2_async_subdev *asd))
980{
981 struct v4l2_async_notifier *notifier;
982 struct device *dev = sd->dev;
983 struct fwnode_handle *fwnode;
984 int ret;
985
986 if (WARN_ON(!dev))
987 return -ENODEV;
988
989 fwnode = dev_fwnode(dev);
990 if (!fwnode_device_is_available(fwnode))
991 return -ENODEV;
992
993 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
994 if (!notifier)
995 return -ENOMEM;
996
997 v4l2_async_notifier_init(notifier);
998
999 if (!ports) {
1000 ret = v4l2_async_notifier_parse_fwnode_endpoints(
1001 dev, notifier, asd_struct_size, parse_endpoint);
1002 if (ret < 0)
1003 goto out_cleanup;
1004 } else {
1005 unsigned int i;
1006
1007 for (i = 0; i < num_ports; i++) {
1008 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
1009 dev, notifier, asd_struct_size,
1010 ports[i], parse_endpoint);
1011 if (ret < 0)
1012 goto out_cleanup;
1013 }
1014 }
1015
1016 ret = v4l2_async_subdev_notifier_register(sd, notifier);
1017 if (ret < 0)
1018 goto out_cleanup;
1019
1020 ret = v4l2_async_register_subdev(sd);
1021 if (ret < 0)
1022 goto out_unregister;
1023
1024 sd->subdev_notifier = notifier;
1025
1026 return 0;
1027
1028out_unregister:
1029 v4l2_async_notifier_unregister(notifier);
1030out_cleanup:
1031 v4l2_async_notifier_cleanup(notifier);
1032 kfree(notifier);
1033
1034 return ret;
1035}
1036EXPORT_SYMBOL_GPL(v4l2_async_register_fwnode_subdev);
1037
Sakari Ailusca50c192016-08-12 08:05:51 -03001038MODULE_LICENSE("GPL");
1039MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1040MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1041MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");