Skip to content

Commit 01e017d

Browse files
Sakari Ailusrkhuangtao
authored andcommitted
FROMLIST: v4l: fwnode: Support generic parsing of graph endpoints in a device
Add two functions for parsing devices graph endpoints: v4l2_async_notifier_parse_fwnode_endpoints and v4l2_async_notifier_parse_fwnode_endpoints_by_port. The former iterates over all endpoints whereas the latter only iterates over the endpoints in a given port. The former is mostly useful for existing drivers that currently implement the iteration over all the endpoints themselves whereas the latter is especially intended for devices with both sinks and sources: async sub-devices for external devices connected to the device's sources will have already been set up, or they are part of the master device. Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> (cherry picked from commit 339b5569a4c60144ae8b4aae497080ceea9190fa) https://git.linuxtv.org/sailus/media_tree.git/log/?h=010f7f4393fd http://www.spinics.net/lists/linux-media/msg122688.html Signed-off-by: Marc Herbert <marc.herbert@intel.com> Conflicts: include/media/v4l2-fwnode.h (purely contextual conflict) BUG=b:64133998 TEST=media device topology shows subdevs registered successfully TEST=no camera regression Change-Id: I97772ddab949a86f4a0eb2e3610d72cba22709ef Reviewed-on: https://chromium-review.googlesource.com/693688 Commit-Ready: Tomasz Figa <tfiga@chromium.org> Tested-by: Hyungwoo Yang <hyungwoo.yang@intel.com> Reviewed-by: Tomasz Figa <tfiga@chromium.org> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
1 parent ae3bcb9 commit 01e017d

4 files changed

Lines changed: 392 additions & 2 deletions

File tree

drivers/media/v4l2-core/v4l2-async.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <media/v4l2-async.h>
2323
#include <media/v4l2-device.h>
24+
#include <media/v4l2-fwnode.h>
2425
#include <media/v4l2-subdev.h>
2526

2627
static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
@@ -225,6 +226,35 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
225226
}
226227
EXPORT_SYMBOL(v4l2_async_notifier_unregister);
227228

229+
void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier)
230+
{
231+
unsigned int i;
232+
233+
if (!notifier->max_subdevs)
234+
return;
235+
236+
for (i = 0; i < notifier->num_subdevs; i++) {
237+
struct v4l2_async_subdev *asd = notifier->subdevs[i];
238+
239+
switch (asd->match_type) {
240+
case V4L2_ASYNC_MATCH_FWNODE:
241+
fwnode_handle_put(asd->match.fwnode.fwnode);
242+
break;
243+
default:
244+
WARN_ON_ONCE(true);
245+
}
246+
247+
kfree(asd);
248+
}
249+
250+
notifier->max_subdevs = 0;
251+
notifier->num_subdevs = 0;
252+
253+
kvfree(notifier->subdevs);
254+
notifier->subdevs = NULL;
255+
}
256+
EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup);
257+
228258
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
229259
{
230260
struct v4l2_async_notifier *notifier;

drivers/media/v4l2-core/v4l2-fwnode.c

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
*/
2020
#include <linux/acpi.h>
2121
#include <linux/kernel.h>
22+
#include <linux/mm.h>
2223
#include <linux/module.h>
2324
#include <linux/of.h>
2425
#include <linux/property.h>
2526
#include <linux/slab.h>
2627
#include <linux/string.h>
2728
#include <linux/types.h>
2829

30+
#include <media/v4l2-async.h>
2931
#include <media/v4l2-fwnode.h>
3032

3133
static int v4l2_fwnode_endpoint_parse_csi_bus(struct fwnode_handle *fwnode,
@@ -264,6 +266,200 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
264266
}
265267
EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
266268

269+
static int v4l2_async_notifier_realloc(struct v4l2_async_notifier *notifier,
270+
unsigned int max_subdevs)
271+
{
272+
struct v4l2_async_subdev **subdevs;
273+
274+
if (max_subdevs <= notifier->max_subdevs)
275+
return 0;
276+
277+
subdevs = kcalloc(
278+
max_subdevs, sizeof(*notifier->subdevs),
279+
GFP_KERNEL | __GFP_ZERO);
280+
if (!subdevs)
281+
return -ENOMEM;
282+
283+
if (notifier->subdevs) {
284+
memcpy(subdevs, notifier->subdevs,
285+
sizeof(*subdevs) * notifier->num_subdevs);
286+
287+
kfree(notifier->subdevs);
288+
}
289+
290+
notifier->subdevs = subdevs;
291+
notifier->max_subdevs = max_subdevs;
292+
293+
return 0;
294+
}
295+
296+
static int v4l2_async_notifier_fwnode_parse_endpoint(
297+
struct device *dev, struct v4l2_async_notifier *notifier,
298+
struct fwnode_handle *endpoint, unsigned int asd_struct_size,
299+
int (*parse_endpoint)(struct device *dev,
300+
struct v4l2_fwnode_endpoint *vep,
301+
struct v4l2_async_subdev *asd))
302+
{
303+
struct v4l2_async_subdev *asd;
304+
struct v4l2_fwnode_endpoint *vep;
305+
int ret = 0;
306+
307+
asd = kzalloc(asd_struct_size, GFP_KERNEL);
308+
if (!asd)
309+
return -ENOMEM;
310+
311+
asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
312+
asd->match.fwnode.fwnode =
313+
fwnode_graph_get_remote_port_parent(endpoint);
314+
if (!asd->match.fwnode.fwnode) {
315+
dev_warn(dev, "bad remote port parent\n");
316+
ret = -EINVAL;
317+
goto out_err;
318+
}
319+
320+
vep = v4l2_fwnode_endpoint_alloc_parse(endpoint);
321+
if (IS_ERR(vep)) {
322+
ret = PTR_ERR(vep);
323+
dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
324+
ret);
325+
goto out_err;
326+
}
327+
328+
ret = parse_endpoint ? parse_endpoint(dev, vep, asd) : 0;
329+
if (ret == -ENOTCONN)
330+
dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep->base.port,
331+
vep->base.id);
332+
else if (ret < 0)
333+
dev_warn(dev,
334+
"driver could not parse port@%u/endpoint@%u (%d)\n",
335+
vep->base.port, vep->base.id, ret);
336+
v4l2_fwnode_endpoint_free(vep);
337+
if (ret < 0)
338+
goto out_err;
339+
340+
notifier->subdevs[notifier->num_subdevs] = asd;
341+
notifier->num_subdevs++;
342+
343+
return 0;
344+
345+
out_err:
346+
fwnode_handle_put(asd->match.fwnode.fwnode);
347+
kfree(asd);
348+
349+
return ret == -ENOTCONN ? 0 : ret;
350+
}
351+
352+
static int __v4l2_async_notifier_parse_fwnode_endpoints(
353+
struct device *dev, struct v4l2_async_notifier *notifier,
354+
size_t asd_struct_size, unsigned int port, bool has_port,
355+
int (*parse_endpoint)(struct device *dev,
356+
struct v4l2_fwnode_endpoint *vep,
357+
struct v4l2_async_subdev *asd))
358+
{
359+
struct fwnode_handle *fwnode = NULL;
360+
unsigned int max_subdevs = notifier->max_subdevs;
361+
int ret;
362+
363+
if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
364+
return -EINVAL;
365+
366+
for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
367+
dev_fwnode(dev), fwnode)); ) {
368+
struct fwnode_handle *dev_fwnode;
369+
bool is_available;
370+
371+
dev_fwnode = fwnode_graph_get_port_parent(fwnode);
372+
is_available = fwnode_device_is_available(dev_fwnode);
373+
fwnode_handle_put(dev_fwnode);
374+
if (!is_available)
375+
continue;
376+
377+
if (has_port) {
378+
struct fwnode_endpoint ep;
379+
380+
ret = fwnode_graph_parse_endpoint(fwnode, &ep);
381+
if (ret) {
382+
fwnode_handle_put(fwnode);
383+
return ret;
384+
}
385+
386+
if (ep.port != port)
387+
continue;
388+
}
389+
max_subdevs++;
390+
}
391+
392+
/* No subdevs to add? Return here. */
393+
if (max_subdevs == notifier->max_subdevs)
394+
return 0;
395+
396+
ret = v4l2_async_notifier_realloc(notifier, max_subdevs);
397+
if (ret)
398+
return ret;
399+
400+
for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
401+
dev_fwnode(dev), fwnode)); ) {
402+
struct fwnode_handle *dev_fwnode;
403+
bool is_available;
404+
405+
dev_fwnode = fwnode_graph_get_port_parent(fwnode);
406+
is_available = fwnode_device_is_available(dev_fwnode);
407+
fwnode_handle_put(dev_fwnode);
408+
409+
if (!fwnode_device_is_available(dev_fwnode))
410+
continue;
411+
412+
if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) {
413+
ret = -EINVAL;
414+
break;
415+
}
416+
417+
if (has_port) {
418+
struct fwnode_endpoint ep;
419+
420+
ret = fwnode_graph_parse_endpoint(fwnode, &ep);
421+
if (ret)
422+
break;
423+
424+
if (ep.port != port)
425+
continue;
426+
}
427+
428+
ret = v4l2_async_notifier_fwnode_parse_endpoint(
429+
dev, notifier, fwnode, asd_struct_size, parse_endpoint);
430+
if (ret < 0)
431+
break;
432+
}
433+
434+
fwnode_handle_put(fwnode);
435+
436+
return ret;
437+
}
438+
439+
int v4l2_async_notifier_parse_fwnode_endpoints(
440+
struct device *dev, struct v4l2_async_notifier *notifier,
441+
size_t asd_struct_size,
442+
int (*parse_endpoint)(struct device *dev,
443+
struct v4l2_fwnode_endpoint *vep,
444+
struct v4l2_async_subdev *asd))
445+
{
446+
return __v4l2_async_notifier_parse_fwnode_endpoints(
447+
dev, notifier, asd_struct_size, 0, false, parse_endpoint);
448+
}
449+
EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints);
450+
451+
int v4l2_async_notifier_parse_fwnode_endpoints_by_port(
452+
struct device *dev, struct v4l2_async_notifier *notifier,
453+
size_t asd_struct_size, unsigned int port,
454+
int (*parse_endpoint)(struct device *dev,
455+
struct v4l2_fwnode_endpoint *vep,
456+
struct v4l2_async_subdev *asd))
457+
{
458+
return __v4l2_async_notifier_parse_fwnode_endpoints(
459+
dev, notifier, asd_struct_size, port, true, parse_endpoint);
460+
}
461+
EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port);
462+
267463
MODULE_LICENSE("GPL");
268464
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
269465
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");

include/media/v4l2-async.h

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ struct device;
1818
struct device_node;
1919
struct v4l2_device;
2020
struct v4l2_subdev;
21-
struct v4l2_async_notifier;
2221

2322
/* A random max subdevice number, used to allocate an array on stack */
2423
#define V4L2_MAX_SUBDEVS 128U
@@ -50,6 +49,10 @@ enum v4l2_async_match_type {
5049
* @match: union of per-bus type matching data sets
5150
* @list: used to link struct v4l2_async_subdev objects, waiting to be
5251
* probed, to a notifier->waiting list
52+
*
53+
* When this struct is used as a member in a driver specific struct,
54+
* the driver specific struct shall contain the &struct
55+
* v4l2_async_subdev as its first member.
5356
*/
5457
struct v4l2_async_subdev {
5558
enum v4l2_async_match_type match_type;
@@ -78,7 +81,8 @@ struct v4l2_async_subdev {
7881
/**
7982
* struct v4l2_async_notifier - v4l2_device notifier data
8083
*
81-
* @num_subdevs: number of subdevices
84+
* @num_subdevs: number of subdevices used in the subdevs array
85+
* @max_subdevs: number of subdevices allocated in the subdevs array
8286
* @subdevs: array of pointers to subdevice descriptors
8387
* @v4l2_dev: pointer to struct v4l2_device
8488
* @waiting: list of struct v4l2_async_subdev, waiting for their drivers
@@ -90,6 +94,7 @@ struct v4l2_async_subdev {
9094
*/
9195
struct v4l2_async_notifier {
9296
unsigned int num_subdevs;
97+
unsigned int max_subdevs;
9398
struct v4l2_async_subdev **subdevs;
9499
struct v4l2_device *v4l2_dev;
95100
struct list_head waiting;
@@ -104,9 +109,50 @@ struct v4l2_async_notifier {
104109
struct v4l2_async_subdev *asd);
105110
};
106111

112+
/**
113+
* v4l2_async_notifier_register - registers a subdevice asynchronous notifier
114+
*
115+
* @v4l2_dev: pointer to &struct v4l2_device
116+
* @notifier: pointer to &struct v4l2_async_notifier
117+
*/
107118
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
108119
struct v4l2_async_notifier *notifier);
120+
121+
/**
122+
* v4l2_async_notifier_unregister - unregisters a subdevice asynchronous notifier
123+
*
124+
* @notifier: pointer to &struct v4l2_async_notifier
125+
*/
109126
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
127+
128+
/**
129+
* v4l2_async_notifier_cleanup - clean up notifier resources
130+
* @notifier: the notifier the resources of which are to be cleaned up
131+
*
132+
* Release memory resources related to a notifier, including the async
133+
* sub-devices allocated for the purposes of the notifier but not the notifier
134+
* itself. The user is responsible for calling this function to clean up the
135+
* notifier after calling @v4l2_async_notifier_parse_fwnode_endpoints.
136+
*
137+
* There is no harm from calling v4l2_async_notifier_cleanup in other
138+
* cases as long as its memory has been zeroed after it has been
139+
* allocated.
140+
*/
141+
void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier);
142+
143+
/**
144+
* v4l2_async_register_subdev - registers a sub-device to the asynchronous
145+
* subdevice framework
146+
*
147+
* @sd: pointer to &struct v4l2_subdev
148+
*/
110149
int v4l2_async_register_subdev(struct v4l2_subdev *sd);
150+
151+
/**
152+
* v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
153+
* subdevice framework
154+
*
155+
* @sd: pointer to &struct v4l2_subdev
156+
*/
111157
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
112158
#endif

0 commit comments

Comments
 (0)