blob: aa3d28c4a50b8521a20d0af5f46b96d232539fc1 [file] [log] [blame]
Sakari Ailusca50c192016-08-12 08:05:51 -03001/*
2 * V4L2 fwnode binding parsing library
3 *
4 * The origins of the V4L2 fwnode library are in V4L2 OF library that
5 * formerly was located in v4l2-of.c.
6 *
7 * Copyright (c) 2016 Intel Corporation.
8 * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
9 *
10 * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
11 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
12 *
13 * Copyright (C) 2012 Renesas Electronics Corp.
14 * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of version 2 of the GNU General Public License as
18 * published by the Free Software Foundation.
19 */
20#include <linux/acpi.h>
21#include <linux/kernel.h>
Sakari Ailus9ca46532017-08-17 11:28:21 -040022#include <linux/mm.h>
Sakari Ailusca50c192016-08-12 08:05:51 -030023#include <linux/module.h>
24#include <linux/of.h>
25#include <linux/property.h>
26#include <linux/slab.h>
27#include <linux/string.h>
28#include <linux/types.h>
29
Sakari Ailus9ca46532017-08-17 11:28:21 -040030#include <media/v4l2-async.h>
Sakari Ailusca50c192016-08-12 08:05:51 -030031#include <media/v4l2-fwnode.h>
Sakari Ailusaef69d52017-09-24 18:47:44 -040032#include <media/v4l2-subdev.h>
Sakari Ailusca50c192016-08-12 08:05:51 -030033
Sakari Ailuse07a41f2015-02-25 14:51:01 -050034enum v4l2_fwnode_bus_type {
35 V4L2_FWNODE_BUS_TYPE_GUESS = 0,
36 V4L2_FWNODE_BUS_TYPE_CSI2_CPHY,
37 V4L2_FWNODE_BUS_TYPE_CSI1,
38 V4L2_FWNODE_BUS_TYPE_CCP2,
Sakari Ailusbf638562018-07-03 17:40:37 -040039 V4L2_FWNODE_BUS_TYPE_CSI2_DPHY,
40 V4L2_FWNODE_BUS_TYPE_PARALLEL,
41 V4L2_FWNODE_BUS_TYPE_BT656,
Sakari Ailuse07a41f2015-02-25 14:51:01 -050042 NR_OF_V4L2_FWNODE_BUS_TYPE,
43};
44
Sakari Ailusf3112732017-02-20 05:42:09 -050045static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
46 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -030047{
48 struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2;
49 bool have_clk_lane = false;
50 unsigned int flags = 0, lanes_used = 0;
51 unsigned int i;
52 u32 v;
53 int rval;
54
55 rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
56 if (rval > 0) {
Sakari Ailusad3cdf32017-08-14 06:43:07 -040057 u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES];
Sakari Ailusca50c192016-08-12 08:05:51 -030058
Sakari Ailusad3cdf32017-08-14 06:43:07 -040059 bus->num_data_lanes =
60 min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
Sakari Ailusca50c192016-08-12 08:05:51 -030061
62 fwnode_property_read_u32_array(fwnode, "data-lanes", array,
63 bus->num_data_lanes);
64
65 for (i = 0; i < bus->num_data_lanes; i++) {
66 if (lanes_used & BIT(array[i]))
67 pr_warn("duplicated lane %u in data-lanes\n",
68 array[i]);
69 lanes_used |= BIT(array[i]);
70
71 bus->data_lanes[i] = array[i];
Sakari Ailusc8677aa2017-12-04 16:25:06 -050072 pr_debug("lane %u position %u\n", i, array[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -030073 }
Sakari Ailusca50c192016-08-12 08:05:51 -030074
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040075 rval = fwnode_property_read_u32_array(fwnode,
Sakari Ailusb24f0212017-08-14 06:15:21 -040076 "lane-polarities", NULL,
77 0);
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040078 if (rval > 0) {
Sakari Ailusb24f0212017-08-14 06:15:21 -040079 if (rval != 1 + bus->num_data_lanes /* clock+data */) {
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040080 pr_warn("invalid number of lane-polarities entries (need %u, got %u)\n",
81 1 + bus->num_data_lanes, rval);
82 return -EINVAL;
83 }
Sakari Ailusca50c192016-08-12 08:05:51 -030084
Sakari Ailusb24f0212017-08-14 06:15:21 -040085 fwnode_property_read_u32_array(fwnode,
86 "lane-polarities", array,
87 1 + bus->num_data_lanes);
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040088
Sakari Ailusc8677aa2017-12-04 16:25:06 -050089 for (i = 0; i < 1 + bus->num_data_lanes; i++) {
Mauro Carvalho Chehab4ee23622017-06-26 14:07:54 -040090 bus->lane_polarities[i] = array[i];
Sakari Ailusc8677aa2017-12-04 16:25:06 -050091 pr_debug("lane %u polarity %sinverted",
92 i, array[i] ? "" : "not ");
93 }
94 } else {
95 pr_debug("no lane polarities defined, assuming not inverted\n");
Sakari Ailusca50c192016-08-12 08:05:51 -030096 }
Sakari Ailusb24f0212017-08-14 06:15:21 -040097
Sakari Ailusca50c192016-08-12 08:05:51 -030098 }
99
100 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
101 if (lanes_used & BIT(v))
102 pr_warn("duplicated lane %u in clock-lanes\n", v);
103 lanes_used |= BIT(v);
104
105 bus->clock_lane = v;
106 have_clk_lane = true;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500107 pr_debug("clock lane position %u\n", v);
Sakari Ailusca50c192016-08-12 08:05:51 -0300108 }
109
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500110 if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300111 flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500112 pr_debug("non-continuous clock\n");
Sakari Ailusd4865322018-07-20 05:04:29 -0400113 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300114 flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500115 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300116
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400117 if (lanes_used || have_clk_lane ||
118 (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) {
119 bus->flags = flags;
120 vep->bus_type = V4L2_MBUS_CSI2_DPHY;
121 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300122
123 return 0;
124}
125
126static void v4l2_fwnode_endpoint_parse_parallel_bus(
127 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
128{
129 struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
130 unsigned int flags = 0;
131 u32 v;
132
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500133 if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300134 flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
135 V4L2_MBUS_HSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500136 pr_debug("hsync-active %s\n", v ? "high" : "low");
137 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300138
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500139 if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300140 flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
141 V4L2_MBUS_VSYNC_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500142 pr_debug("vsync-active %s\n", v ? "high" : "low");
143 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300144
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500145 if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300146 flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
147 V4L2_MBUS_FIELD_EVEN_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500148 pr_debug("field-even-active %s\n", v ? "high" : "low");
149 }
150
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500151 if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300152 flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
153 V4L2_MBUS_PCLK_SAMPLE_FALLING;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500154 pr_debug("pclk-sample %s\n", v ? "high" : "low");
155 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300156
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500157 if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300158 flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
159 V4L2_MBUS_DATA_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500160 pr_debug("data-active %s\n", v ? "high" : "low");
161 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300162
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500163 if (fwnode_property_present(fwnode, "slave-mode")) {
164 pr_debug("slave mode\n");
Sakari Ailusca50c192016-08-12 08:05:51 -0300165 flags |= V4L2_MBUS_SLAVE;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500166 } else {
Sakari Ailusca50c192016-08-12 08:05:51 -0300167 flags |= V4L2_MBUS_MASTER;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500168 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300169
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500170 if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300171 bus->bus_width = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500172 pr_debug("bus-width %u\n", v);
173 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300174
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500175 if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300176 bus->data_shift = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500177 pr_debug("data-shift %u\n", v);
178 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300179
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500180 if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) {
Sakari Ailusca50c192016-08-12 08:05:51 -0300181 flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
182 V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500183 pr_debug("sync-on-green-active %s\n", v ? "high" : "low");
184 }
Sakari Ailusca50c192016-08-12 08:05:51 -0300185
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500186 if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) {
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400187 flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
188 V4L2_MBUS_DATA_ENABLE_LOW;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500189 pr_debug("data-enable-active %s\n", v ? "high" : "low");
190 }
Jacopo Mondi9b04fcc2018-07-09 10:19:19 -0400191
Sakari Ailusca50c192016-08-12 08:05:51 -0300192 bus->flags = flags;
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400193 if (flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
194 V4L2_MBUS_HSYNC_ACTIVE_LOW |
195 V4L2_MBUS_VSYNC_ACTIVE_HIGH |
196 V4L2_MBUS_VSYNC_ACTIVE_LOW |
197 V4L2_MBUS_FIELD_EVEN_HIGH |
198 V4L2_MBUS_FIELD_EVEN_LOW))
199 vep->bus_type = V4L2_MBUS_PARALLEL;
200 else
201 vep->bus_type = V4L2_MBUS_BT656;
Sakari Ailusca50c192016-08-12 08:05:51 -0300202}
203
Mauro Carvalho Chehababc5b2c2017-07-20 16:27:27 -0400204static void
205v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
206 struct v4l2_fwnode_endpoint *vep,
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400207 enum v4l2_fwnode_bus_type bus_type)
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500208{
209 struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
210 u32 v;
211
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500212 if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500213 bus->clock_inv = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500214 pr_debug("clock-inv %u\n", v);
215 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500216
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500217 if (!fwnode_property_read_u32(fwnode, "strobe", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500218 bus->strobe = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500219 pr_debug("strobe %u\n", v);
220 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500221
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500222 if (!fwnode_property_read_u32(fwnode, "data-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500223 bus->data_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500224 pr_debug("data-lanes %u\n", v);
225 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500226
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500227 if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500228 bus->clock_lane = v;
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500229 pr_debug("clock-lanes %u\n", v);
230 }
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500231
232 if (bus_type == V4L2_FWNODE_BUS_TYPE_CCP2)
233 vep->bus_type = V4L2_MBUS_CCP2;
234 else
235 vep->bus_type = V4L2_MBUS_CSI1;
236}
237
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500238static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
239 struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300240{
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500241 u32 bus_type = 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300242 int rval;
243
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500244 pr_debug("===== begin V4L2 endpoint properties\n");
245
Sakari Ailusca50c192016-08-12 08:05:51 -0300246 fwnode_graph_parse_endpoint(fwnode, &vep->base);
247
248 /* Zero fields from bus_type to until the end */
249 memset(&vep->bus_type, 0, sizeof(*vep) -
250 offsetof(typeof(*vep), bus_type));
251
Sakari Ailuse07a41f2015-02-25 14:51:01 -0500252 fwnode_property_read_u32(fwnode, "bus-type", &bus_type);
253
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500254 switch (bus_type) {
255 case V4L2_FWNODE_BUS_TYPE_GUESS:
256 rval = v4l2_fwnode_endpoint_parse_csi2_bus(fwnode, vep);
257 if (rval)
258 return rval;
Sakari Ailus2835b5b2018-07-31 06:43:14 -0400259
260 if (vep->bus_type == V4L2_MBUS_UNKNOWN)
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500261 v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep);
Sakari Ailusca50c192016-08-12 08:05:51 -0300262
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500263 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500264 case V4L2_FWNODE_BUS_TYPE_CCP2:
265 case V4L2_FWNODE_BUS_TYPE_CSI1:
266 v4l2_fwnode_endpoint_parse_csi1_bus(fwnode, vep, bus_type);
267
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500268 break;
Sakari Ailus97bbdf02015-02-25 14:39:11 -0500269 default:
270 pr_warn("unsupported bus type %u\n", bus_type);
271 return -EINVAL;
272 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500273
274 return 0;
275}
276
277int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
278 struct v4l2_fwnode_endpoint *vep)
279{
280 int ret;
281
282 ret = __v4l2_fwnode_endpoint_parse(fwnode, vep);
283
284 pr_debug("===== end V4L2 endpoint properties\n");
285
286 return ret;
Sakari Ailusca50c192016-08-12 08:05:51 -0300287}
288EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
289
Sakari Ailusca50c192016-08-12 08:05:51 -0300290void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
291{
292 if (IS_ERR_OR_NULL(vep))
293 return;
294
295 kfree(vep->link_frequencies);
Sakari Ailusca50c192016-08-12 08:05:51 -0300296}
297EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
298
Sakari Ailus6970d372018-06-02 12:19:35 -0400299int v4l2_fwnode_endpoint_alloc_parse(
300 struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
Sakari Ailusca50c192016-08-12 08:05:51 -0300301{
Sakari Ailusca50c192016-08-12 08:05:51 -0300302 int rval;
303
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500304 rval = __v4l2_fwnode_endpoint_parse(fwnode, vep);
Sakari Ailusca50c192016-08-12 08:05:51 -0300305 if (rval < 0)
Sakari Ailus6970d372018-06-02 12:19:35 -0400306 return rval;
Sakari Ailusca50c192016-08-12 08:05:51 -0300307
308 rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
309 NULL, 0);
Sakari Ailus06f81522017-06-20 09:14:43 -0400310 if (rval > 0) {
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500311 unsigned int i;
312
Sakari Ailus06f81522017-06-20 09:14:43 -0400313 vep->link_frequencies =
314 kmalloc_array(rval, sizeof(*vep->link_frequencies),
315 GFP_KERNEL);
Sakari Ailus6970d372018-06-02 12:19:35 -0400316 if (!vep->link_frequencies)
317 return -ENOMEM;
Sakari Ailusca50c192016-08-12 08:05:51 -0300318
Sakari Ailus06f81522017-06-20 09:14:43 -0400319 vep->nr_of_link_frequencies = rval;
320
321 rval = fwnode_property_read_u64_array(
322 fwnode, "link-frequencies", vep->link_frequencies,
323 vep->nr_of_link_frequencies);
Sakari Ailus6970d372018-06-02 12:19:35 -0400324 if (rval < 0) {
325 v4l2_fwnode_endpoint_free(vep);
326 return rval;
327 }
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500328
329 for (i = 0; i < vep->nr_of_link_frequencies; i++)
330 pr_info("link-frequencies %u value %llu\n", i,
331 vep->link_frequencies[i]);
Sakari Ailusca50c192016-08-12 08:05:51 -0300332 }
333
Sakari Ailusc8677aa2017-12-04 16:25:06 -0500334 pr_debug("===== end V4L2 endpoint properties\n");
335
Sakari Ailus6970d372018-06-02 12:19:35 -0400336 return 0;
Sakari Ailusca50c192016-08-12 08:05:51 -0300337}
338EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
339
Sakari Ailusca50c192016-08-12 08:05:51 -0300340int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
341 struct v4l2_fwnode_link *link)
342{
343 const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
344 struct fwnode_handle *fwnode;
345
346 memset(link, 0, sizeof(*link));
347
348 fwnode = fwnode_get_parent(__fwnode);
349 fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
350 fwnode = fwnode_get_next_parent(fwnode);
351 if (is_of_node(fwnode) &&
352 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
353 fwnode = fwnode_get_next_parent(fwnode);
354 link->local_node = fwnode;
355
356 fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
357 if (!fwnode) {
358 fwnode_handle_put(fwnode);
359 return -ENOLINK;
360 }
361
362 fwnode = fwnode_get_parent(fwnode);
363 fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
364 fwnode = fwnode_get_next_parent(fwnode);
365 if (is_of_node(fwnode) &&
366 of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
367 fwnode = fwnode_get_next_parent(fwnode);
368 link->remote_node = fwnode;
369
370 return 0;
371}
372EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
373
Sakari Ailusca50c192016-08-12 08:05:51 -0300374void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
375{
376 fwnode_handle_put(link->local_node);
377 fwnode_handle_put(link->remote_node);
378}
379EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
380
Sakari Ailus9ca46532017-08-17 11:28:21 -0400381static int v4l2_async_notifier_fwnode_parse_endpoint(
382 struct device *dev, struct v4l2_async_notifier *notifier,
383 struct fwnode_handle *endpoint, unsigned int asd_struct_size,
384 int (*parse_endpoint)(struct device *dev,
385 struct v4l2_fwnode_endpoint *vep,
386 struct v4l2_async_subdev *asd))
387{
Sakari Ailus6970d372018-06-02 12:19:35 -0400388 struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
Sakari Ailus9ca46532017-08-17 11:28:21 -0400389 struct v4l2_async_subdev *asd;
Sakari Ailus6970d372018-06-02 12:19:35 -0400390 int ret;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400391
392 asd = kzalloc(asd_struct_size, GFP_KERNEL);
393 if (!asd)
394 return -ENOMEM;
395
396 asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400397 asd->match.fwnode =
Sakari Ailus9ca46532017-08-17 11:28:21 -0400398 fwnode_graph_get_remote_port_parent(endpoint);
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400399 if (!asd->match.fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400400 dev_warn(dev, "bad remote port parent\n");
Steve Longerbeam4382f372018-09-29 15:54:04 -0400401 ret = -ENOTCONN;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400402 goto out_err;
403 }
404
Sakari Ailus6970d372018-06-02 12:19:35 -0400405 ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
406 if (ret) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400407 dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
408 ret);
409 goto out_err;
410 }
411
Sakari Ailus6970d372018-06-02 12:19:35 -0400412 ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400413 if (ret == -ENOTCONN)
Sakari Ailus6970d372018-06-02 12:19:35 -0400414 dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
415 vep.base.id);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400416 else if (ret < 0)
417 dev_warn(dev,
418 "driver could not parse port@%u/endpoint@%u (%d)\n",
Sakari Ailus6970d372018-06-02 12:19:35 -0400419 vep.base.port, vep.base.id, ret);
420 v4l2_fwnode_endpoint_free(&vep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400421 if (ret < 0)
422 goto out_err;
423
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400424 ret = v4l2_async_notifier_add_subdev(notifier, asd);
425 if (ret < 0) {
426 /* not an error if asd already exists */
427 if (ret == -EEXIST)
428 ret = 0;
429 goto out_err;
430 }
Sakari Ailus9ca46532017-08-17 11:28:21 -0400431
432 return 0;
433
434out_err:
Mauro Carvalho Chehab4e48afe2017-09-27 10:12:00 -0400435 fwnode_handle_put(asd->match.fwnode);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400436 kfree(asd);
437
438 return ret == -ENOTCONN ? 0 : ret;
439}
440
441static int __v4l2_async_notifier_parse_fwnode_endpoints(
442 struct device *dev, struct v4l2_async_notifier *notifier,
443 size_t asd_struct_size, unsigned int port, bool has_port,
444 int (*parse_endpoint)(struct device *dev,
445 struct v4l2_fwnode_endpoint *vep,
446 struct v4l2_async_subdev *asd))
447{
448 struct fwnode_handle *fwnode;
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400449 int ret = 0;
Sakari Ailus9ca46532017-08-17 11:28:21 -0400450
451 if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
452 return -EINVAL;
453
Sakari Ailus106ee382017-12-21 07:11:19 -0500454 fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
Sakari Ailus9ca46532017-08-17 11:28:21 -0400455 struct fwnode_handle *dev_fwnode;
456 bool is_available;
457
458 dev_fwnode = fwnode_graph_get_port_parent(fwnode);
459 is_available = fwnode_device_is_available(dev_fwnode);
460 fwnode_handle_put(dev_fwnode);
461 if (!is_available)
462 continue;
463
464 if (has_port) {
465 struct fwnode_endpoint ep;
466
467 ret = fwnode_graph_parse_endpoint(fwnode, &ep);
Sakari Ailus9ca46532017-08-17 11:28:21 -0400468 if (ret)
469 break;
470
471 if (ep.port != port)
472 continue;
473 }
474
475 ret = v4l2_async_notifier_fwnode_parse_endpoint(
476 dev, notifier, fwnode, asd_struct_size, parse_endpoint);
477 if (ret < 0)
478 break;
479 }
480
481 fwnode_handle_put(fwnode);
482
483 return ret;
484}
485
486int v4l2_async_notifier_parse_fwnode_endpoints(
487 struct device *dev, struct v4l2_async_notifier *notifier,
488 size_t asd_struct_size,
489 int (*parse_endpoint)(struct device *dev,
490 struct v4l2_fwnode_endpoint *vep,
491 struct v4l2_async_subdev *asd))
492{
493 return __v4l2_async_notifier_parse_fwnode_endpoints(
494 dev, notifier, asd_struct_size, 0, false, parse_endpoint);
495}
496EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
497
498int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
499 struct device *dev, struct v4l2_async_notifier *notifier,
500 size_t asd_struct_size, unsigned int port,
501 int (*parse_endpoint)(struct device *dev,
502 struct v4l2_fwnode_endpoint *vep,
503 struct v4l2_async_subdev *asd))
504{
505 return __v4l2_async_notifier_parse_fwnode_endpoints(
506 dev, notifier, asd_struct_size, port, true, parse_endpoint);
507}
508EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
509
Sakari Ailusd8428532017-06-20 07:47:24 -0400510/*
511 * v4l2_fwnode_reference_parse - parse references for async sub-devices
512 * @dev: the device node the properties of which are parsed for references
513 * @notifier: the async notifier where the async subdevs will be added
514 * @prop: the name of the property
515 *
516 * Return: 0 on success
517 * -ENOENT if no entries were found
518 * -ENOMEM if memory allocation failed
519 * -EINVAL if property parsing failed
520 */
521static int v4l2_fwnode_reference_parse(
522 struct device *dev, struct v4l2_async_notifier *notifier,
523 const char *prop)
524{
525 struct fwnode_reference_args args;
526 unsigned int index;
527 int ret;
528
529 for (index = 0;
530 !(ret = fwnode_property_get_reference_args(
531 dev_fwnode(dev), prop, NULL, 0, index, &args));
532 index++)
533 fwnode_handle_put(args.fwnode);
534
535 if (!index)
536 return -ENOENT;
537
538 /*
539 * Note that right now both -ENODATA and -ENOENT may signal
540 * out-of-bounds access. Return the error in cases other than that.
541 */
542 if (ret != -ENOENT && ret != -ENODATA)
543 return ret;
544
Sakari Ailusd8428532017-06-20 07:47:24 -0400545 for (index = 0; !fwnode_property_get_reference_args(
546 dev_fwnode(dev), prop, NULL, 0, index, &args);
547 index++) {
548 struct v4l2_async_subdev *asd;
549
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400550 asd = v4l2_async_notifier_add_fwnode_subdev(
551 notifier, args.fwnode, sizeof(*asd));
552 if (IS_ERR(asd)) {
553 ret = PTR_ERR(asd);
554 /* not an error if asd already exists */
555 if (ret == -EEXIST) {
556 fwnode_handle_put(args.fwnode);
557 continue;
558 }
559
Sakari Ailusd8428532017-06-20 07:47:24 -0400560 goto error;
561 }
Sakari Ailusd8428532017-06-20 07:47:24 -0400562 }
563
564 return 0;
565
566error:
567 fwnode_handle_put(args.fwnode);
568 return ret;
569}
570
Sakari Ailusa1699a42017-06-22 08:03:28 -0400571/*
572 * v4l2_fwnode_reference_get_int_prop - parse a reference with integer
573 * arguments
574 * @fwnode: fwnode to read @prop from
575 * @notifier: notifier for @dev
576 * @prop: the name of the property
577 * @index: the index of the reference to get
578 * @props: the array of integer property names
579 * @nprops: the number of integer property names in @nprops
580 *
581 * First find an fwnode referred to by the reference at @index in @prop.
582 *
583 * Then under that fwnode, @nprops times, for each property in @props,
584 * iteratively follow child nodes starting from fwnode such that they have the
585 * property in @props array at the index of the child node distance from the
586 * root node and the value of that property matching with the integer argument
587 * of the reference, at the same index.
588 *
589 * The child fwnode reched at the end of the iteration is then returned to the
590 * caller.
591 *
592 * The core reason for this is that you cannot refer to just any node in ACPI.
593 * So to refer to an endpoint (easy in DT) you need to refer to a device, then
594 * provide a list of (property name, property value) tuples where each tuple
595 * uniquely identifies a child node. The first tuple identifies a child directly
596 * underneath the device fwnode, the next tuple identifies a child node
597 * underneath the fwnode identified by the previous tuple, etc. until you
598 * reached the fwnode you need.
599 *
600 * An example with a graph, as defined in Documentation/acpi/dsd/graph.txt:
601 *
602 * Scope (\_SB.PCI0.I2C2)
603 * {
604 * Device (CAM0)
605 * {
606 * Name (_DSD, Package () {
607 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
608 * Package () {
609 * Package () {
610 * "compatible",
611 * Package () { "nokia,smia" }
612 * },
613 * },
614 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
615 * Package () {
616 * Package () { "port0", "PRT0" },
617 * }
618 * })
619 * Name (PRT0, Package() {
620 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
621 * Package () {
622 * Package () { "port", 0 },
623 * },
624 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
625 * Package () {
626 * Package () { "endpoint0", "EP00" },
627 * }
628 * })
629 * Name (EP00, Package() {
630 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
631 * Package () {
632 * Package () { "endpoint", 0 },
633 * Package () {
634 * "remote-endpoint",
635 * Package() {
636 * \_SB.PCI0.ISP, 4, 0
637 * }
638 * },
639 * }
640 * })
641 * }
642 * }
643 *
644 * Scope (\_SB.PCI0)
645 * {
646 * Device (ISP)
647 * {
648 * Name (_DSD, Package () {
649 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
650 * Package () {
651 * Package () { "port4", "PRT4" },
652 * }
653 * })
654 *
655 * Name (PRT4, Package() {
656 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
657 * Package () {
658 * Package () { "port", 4 },
659 * },
660 * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
661 * Package () {
662 * Package () { "endpoint0", "EP40" },
663 * }
664 * })
665 *
666 * Name (EP40, Package() {
667 * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
668 * Package () {
669 * Package () { "endpoint", 0 },
670 * Package () {
671 * "remote-endpoint",
672 * Package () {
673 * \_SB.PCI0.I2C2.CAM0,
674 * 0, 0
675 * }
676 * },
677 * }
678 * })
679 * }
680 * }
681 *
682 * From the EP40 node under ISP device, you could parse the graph remote
683 * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments:
684 *
685 * @fwnode: fwnode referring to EP40 under ISP.
686 * @prop: "remote-endpoint"
687 * @index: 0
688 * @props: "port", "endpoint"
689 * @nprops: 2
690 *
691 * And you'd get back fwnode referring to EP00 under CAM0.
692 *
693 * The same works the other way around: if you use EP00 under CAM0 as the
694 * fwnode, you'll get fwnode referring to EP40 under ISP.
695 *
696 * The same example in DT syntax would look like this:
697 *
698 * cam: cam0 {
699 * compatible = "nokia,smia";
700 *
701 * port {
702 * port = <0>;
703 * endpoint {
704 * endpoint = <0>;
705 * remote-endpoint = <&isp 4 0>;
706 * };
707 * };
708 * };
709 *
710 * isp: isp {
711 * ports {
712 * port@4 {
713 * port = <4>;
714 * endpoint {
715 * endpoint = <0>;
716 * remote-endpoint = <&cam 0 0>;
717 * };
718 * };
719 * };
720 * };
721 *
722 * Return: 0 on success
723 * -ENOENT if no entries (or the property itself) were found
724 * -EINVAL if property parsing otherwise failed
725 * -ENOMEM if memory allocation failed
726 */
727static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop(
728 struct fwnode_handle *fwnode, const char *prop, unsigned int index,
729 const char * const *props, unsigned int nprops)
730{
731 struct fwnode_reference_args fwnode_args;
Sakari Ailus977d5ad2018-07-17 17:19:11 +0300732 u64 *args = fwnode_args.args;
Sakari Ailusa1699a42017-06-22 08:03:28 -0400733 struct fwnode_handle *child;
734 int ret;
735
736 /*
737 * Obtain remote fwnode as well as the integer arguments.
738 *
739 * Note that right now both -ENODATA and -ENOENT may signal
740 * out-of-bounds access. Return -ENOENT in that case.
741 */
742 ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops,
743 index, &fwnode_args);
744 if (ret)
745 return ERR_PTR(ret == -ENODATA ? -ENOENT : ret);
746
747 /*
748 * Find a node in the tree under the referred fwnode corresponding to
749 * the integer arguments.
750 */
751 fwnode = fwnode_args.fwnode;
752 while (nprops--) {
753 u32 val;
754
755 /* Loop over all child nodes under fwnode. */
756 fwnode_for_each_child_node(fwnode, child) {
757 if (fwnode_property_read_u32(child, *props, &val))
758 continue;
759
760 /* Found property, see if its value matches. */
761 if (val == *args)
762 break;
763 }
764
765 fwnode_handle_put(fwnode);
766
767 /* No property found; return an error here. */
768 if (!child) {
769 fwnode = ERR_PTR(-ENOENT);
770 break;
771 }
772
773 props++;
774 args++;
775 fwnode = child;
776 }
777
778 return fwnode;
779}
780
781/*
782 * v4l2_fwnode_reference_parse_int_props - parse references for async
783 * sub-devices
784 * @dev: struct device pointer
785 * @notifier: notifier for @dev
786 * @prop: the name of the property
787 * @props: the array of integer property names
788 * @nprops: the number of integer properties
789 *
790 * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in
791 * property @prop with integer arguments with child nodes matching in properties
792 * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier
793 * accordingly.
794 *
795 * While it is technically possible to use this function on DT, it is only
796 * meaningful on ACPI. On Device tree you can refer to any node in the tree but
797 * on ACPI the references are limited to devices.
798 *
799 * Return: 0 on success
800 * -ENOENT if no entries (or the property itself) were found
801 * -EINVAL if property parsing otherwisefailed
802 * -ENOMEM if memory allocation failed
803 */
804static int v4l2_fwnode_reference_parse_int_props(
805 struct device *dev, struct v4l2_async_notifier *notifier,
806 const char *prop, const char * const *props, unsigned int nprops)
807{
808 struct fwnode_handle *fwnode;
809 unsigned int index;
810 int ret;
811
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400812 index = 0;
813 do {
814 fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
815 prop, index,
816 props, nprops);
817 if (IS_ERR(fwnode)) {
818 /*
819 * Note that right now both -ENODATA and -ENOENT may
820 * signal out-of-bounds access. Return the error in
821 * cases other than that.
822 */
823 if (PTR_ERR(fwnode) != -ENOENT &&
824 PTR_ERR(fwnode) != -ENODATA)
825 return PTR_ERR(fwnode);
826 break;
827 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400828 fwnode_handle_put(fwnode);
Mauro Carvalho Chehab9879c9d2018-04-04 07:21:04 -0400829 index++;
830 } while (1);
Sakari Ailusa1699a42017-06-22 08:03:28 -0400831
Sakari Ailusa1699a42017-06-22 08:03:28 -0400832 for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
833 dev_fwnode(dev), prop, index, props,
834 nprops))); index++) {
835 struct v4l2_async_subdev *asd;
836
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400837 asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode,
838 sizeof(*asd));
839 if (IS_ERR(asd)) {
840 ret = PTR_ERR(asd);
841 /* not an error if asd already exists */
842 if (ret == -EEXIST) {
843 fwnode_handle_put(fwnode);
844 continue;
845 }
846
Sakari Ailusa1699a42017-06-22 08:03:28 -0400847 goto error;
848 }
Sakari Ailusa1699a42017-06-22 08:03:28 -0400849 }
850
851 return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode);
852
853error:
854 fwnode_handle_put(fwnode);
855 return ret;
856}
857
Sakari Ailus7a9ec802017-09-06 08:35:42 -0400858int v4l2_async_notifier_parse_fwnode_sensor_common(
859 struct device *dev, struct v4l2_async_notifier *notifier)
860{
861 static const char * const led_props[] = { "led" };
862 static const struct {
863 const char *name;
864 const char * const *props;
865 unsigned int nprops;
866 } props[] = {
867 { "flash-leds", led_props, ARRAY_SIZE(led_props) },
868 { "lens-focus", NULL, 0 },
869 };
870 unsigned int i;
871
872 for (i = 0; i < ARRAY_SIZE(props); i++) {
873 int ret;
874
875 if (props[i].props && is_acpi_node(dev_fwnode(dev)))
876 ret = v4l2_fwnode_reference_parse_int_props(
877 dev, notifier, props[i].name,
878 props[i].props, props[i].nprops);
879 else
880 ret = v4l2_fwnode_reference_parse(
881 dev, notifier, props[i].name);
882 if (ret && ret != -ENOENT) {
883 dev_warn(dev, "parsing property \"%s\" failed (%d)\n",
884 props[i].name, ret);
885 return ret;
886 }
887 }
888
889 return 0;
890}
891EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common);
892
Sakari Ailusaef69d52017-09-24 18:47:44 -0400893int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd)
894{
895 struct v4l2_async_notifier *notifier;
896 int ret;
897
898 if (WARN_ON(!sd->dev))
899 return -ENODEV;
900
901 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
902 if (!notifier)
903 return -ENOMEM;
904
Steve Longerbeameae2aed2018-09-29 15:54:08 -0400905 v4l2_async_notifier_init(notifier);
906
Sakari Ailusaef69d52017-09-24 18:47:44 -0400907 ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev,
908 notifier);
909 if (ret < 0)
910 goto out_cleanup;
911
912 ret = v4l2_async_subdev_notifier_register(sd, notifier);
913 if (ret < 0)
914 goto out_cleanup;
915
916 ret = v4l2_async_register_subdev(sd);
917 if (ret < 0)
918 goto out_unregister;
919
920 sd->subdev_notifier = notifier;
921
922 return 0;
923
924out_unregister:
925 v4l2_async_notifier_unregister(notifier);
926
927out_cleanup:
928 v4l2_async_notifier_cleanup(notifier);
929 kfree(notifier);
930
931 return ret;
932}
933EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common);
934
Steve Longerbeam1634f0e2018-09-29 15:54:09 -0400935int v4l2_async_register_fwnode_subdev(
936 struct v4l2_subdev *sd, size_t asd_struct_size,
937 unsigned int *ports, unsigned int num_ports,
938 int (*parse_endpoint)(struct device *dev,
939 struct v4l2_fwnode_endpoint *vep,
940 struct v4l2_async_subdev *asd))
941{
942 struct v4l2_async_notifier *notifier;
943 struct device *dev = sd->dev;
944 struct fwnode_handle *fwnode;
945 int ret;
946
947 if (WARN_ON(!dev))
948 return -ENODEV;
949
950 fwnode = dev_fwnode(dev);
951 if (!fwnode_device_is_available(fwnode))
952 return -ENODEV;
953
954 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
955 if (!notifier)
956 return -ENOMEM;
957
958 v4l2_async_notifier_init(notifier);
959
960 if (!ports) {
961 ret = v4l2_async_notifier_parse_fwnode_endpoints(
962 dev, notifier, asd_struct_size, parse_endpoint);
963 if (ret < 0)
964 goto out_cleanup;
965 } else {
966 unsigned int i;
967
968 for (i = 0; i < num_ports; i++) {
969 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
970 dev, notifier, asd_struct_size,
971 ports[i], parse_endpoint);
972 if (ret < 0)
973 goto out_cleanup;
974 }
975 }
976
977 ret = v4l2_async_subdev_notifier_register(sd, notifier);
978 if (ret < 0)
979 goto out_cleanup;
980
981 ret = v4l2_async_register_subdev(sd);
982 if (ret < 0)
983 goto out_unregister;
984
985 sd->subdev_notifier = notifier;
986
987 return 0;
988
989out_unregister:
990 v4l2_async_notifier_unregister(notifier);
991out_cleanup:
992 v4l2_async_notifier_cleanup(notifier);
993 kfree(notifier);
994
995 return ret;
996}
997EXPORT_SYMBOL_GPL(v4l2_async_register_fwnode_subdev);
998
Sakari Ailusca50c192016-08-12 08:05:51 -0300999MODULE_LICENSE("GPL");
1000MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1001MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1002MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");