Skip to content

Commit 276604c

Browse files
Sakari Ailusrkhuangtao
authored andcommitted
FROMLIST: v4l: async: Allow binding notifiers to sub-devices
Registering a notifier has required the knowledge of struct v4l2_device for the reason that sub-devices generally are registered to the v4l2_device (as well as the media device, also available through v4l2_device). This information is not available for sub-device drivers at probe time. What this patch does is that it allows registering notifiers without having v4l2_device around. Instead the sub-device pointer is stored in the notifier. Once the sub-device of the driver that registered the notifier is registered, the notifier will gain the knowledge of the v4l2_device, and the binding of async sub-devices from the sub-device driver's notifier may proceed. The root notifier's complete callback is only called when all sub-device notifiers are completed. Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> (cherry picked from commit 6527ddc572c6fe60808ed0ee690158498cb50439) 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> BUG=b:64133998 TEST=media device topology shows subdevs registered successfully TEST=no camera regression Change-Id: I4e95e7f72f00a8f88786a26b73fc5ef22d4e4261 Reviewed-on: https://chromium-review.googlesource.com/693695 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 8857e46 commit 276604c

2 files changed

Lines changed: 199 additions & 25 deletions

File tree

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

Lines changed: 184 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,109 @@ static struct v4l2_async_subdev *v4l2_async_find_match(
128128
return NULL;
129129
}
130130

131+
/* Find the sub-device notifier registered by a sub-device driver. */
132+
static struct v4l2_async_notifier *v4l2_async_find_subdev_notifier(
133+
struct v4l2_subdev *sd)
134+
{
135+
struct v4l2_async_notifier *n;
136+
137+
list_for_each_entry(n, &notifier_list, list)
138+
if (n->sd == sd)
139+
return n;
140+
141+
return NULL;
142+
}
143+
144+
/* Get v4l2_device related to the notifier if one can be found. */
145+
static struct v4l2_device *v4l2_async_notifier_find_v4l2_dev(
146+
struct v4l2_async_notifier *notifier)
147+
{
148+
while (notifier->parent)
149+
notifier = notifier->parent;
150+
151+
return notifier->v4l2_dev;
152+
}
153+
154+
/*
155+
* Return true if all child sub-device notifiers are complete, false otherwise.
156+
*/
157+
static bool v4l2_async_notifier_can_complete(
158+
struct v4l2_async_notifier *notifier)
159+
{
160+
struct v4l2_subdev *sd;
161+
162+
if (!list_empty(&notifier->waiting))
163+
return false;
164+
165+
list_for_each_entry(sd, &notifier->done, async_list) {
166+
struct v4l2_async_notifier *subdev_notifier =
167+
v4l2_async_find_subdev_notifier(sd);
168+
169+
if (subdev_notifier &&
170+
!v4l2_async_notifier_can_complete(subdev_notifier))
171+
return false;
172+
}
173+
174+
return true;
175+
}
176+
177+
/* Complete all notifiers. Call on the root notifier. */
178+
static int v4l2_async_notifier_complete(
179+
struct v4l2_async_notifier *notifier)
180+
{
181+
struct v4l2_subdev *sd;
182+
183+
list_for_each_entry(sd, &notifier->done, async_list) {
184+
struct v4l2_async_notifier *subdev_notifier =
185+
v4l2_async_find_subdev_notifier(sd);
186+
int ret;
187+
188+
if (!subdev_notifier)
189+
continue;
190+
191+
ret = v4l2_async_notifier_complete(subdev_notifier);
192+
if (ret)
193+
return ret;
194+
}
195+
196+
return v4l2_async_notifier_call_complete(notifier);
197+
}
198+
199+
/*
200+
* Complete notifiers if possible. This is done when all async sub-devices have
201+
* been bound; v4l2_device is also available then.
202+
*/
203+
static int v4l2_async_notifier_try_complete(
204+
struct v4l2_async_notifier *notifier)
205+
{
206+
/* Quick check whether there are still more sub-devices here. */
207+
if (!list_empty(&notifier->waiting))
208+
return 0;
209+
210+
/* Check the entire notifier tree; find the root notifier first. */
211+
while (notifier->parent)
212+
notifier = notifier->parent;
213+
214+
/* This is root if it has v4l2_dev. */
215+
if (!notifier->v4l2_dev)
216+
return 0;
217+
218+
/* Is everything ready? */
219+
if (!v4l2_async_notifier_can_complete(notifier))
220+
return 0;
221+
222+
return v4l2_async_notifier_complete(notifier);
223+
}
224+
225+
static int v4l2_async_notifier_try_all_subdevs(
226+
struct v4l2_async_notifier *notifier);
227+
131228
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
132229
struct v4l2_device *v4l2_dev,
133230
struct v4l2_subdev *sd,
134231
struct v4l2_async_subdev *asd)
135232
{
233+
struct v4l2_async_notifier *subdev_notifier;
136234
int ret;
137235

138236
ret = v4l2_device_register_subdev(v4l2_dev, sd);
@@ -153,8 +251,17 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
153251
/* Move from the global subdevice list to notifier's done */
154252
list_move(&sd->async_list, &notifier->done);
155253

156-
if (list_empty(&notifier->waiting))
157-
return v4l2_async_notifier_call_complete(notifier);
254+
/*
255+
* See if the sub-device has a notifier. If it does, proceed
256+
* with checking for its async sub-devices.
257+
*/
258+
subdev_notifier = v4l2_async_find_subdev_notifier(sd);
259+
if (subdev_notifier && !subdev_notifier->parent) {
260+
subdev_notifier->parent = notifier;
261+
ret = v4l2_async_notifier_try_all_subdevs(subdev_notifier);
262+
if (ret)
263+
return ret;
264+
}
158265

159266
return 0;
160267
}
@@ -163,10 +270,15 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
163270
static int v4l2_async_notifier_try_all_subdevs(
164271
struct v4l2_async_notifier *notifier)
165272
{
166-
struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
167-
struct v4l2_subdev *sd, *tmp;
273+
struct v4l2_device *v4l2_dev =
274+
v4l2_async_notifier_find_v4l2_dev(notifier);
275+
struct v4l2_subdev *sd;
168276

169-
list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
277+
if (!v4l2_dev)
278+
return 0;
279+
280+
again:
281+
list_for_each_entry(sd, &subdev_list, async_list) {
170282
struct v4l2_async_subdev *asd;
171283
int ret;
172284

@@ -175,10 +287,16 @@ static int v4l2_async_notifier_try_all_subdevs(
175287
continue;
176288

177289
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
178-
if (ret < 0) {
179-
mutex_unlock(&list_lock);
290+
if (ret < 0)
180291
return ret;
181-
}
292+
293+
/*
294+
* v4l2_async_match_notify() may lead to registering a
295+
* new notifier and thus changing the async subdevs
296+
* list. In order to proceed safely from here, restart
297+
* parsing the list from the beginning.
298+
*/
299+
goto again;
182300
}
183301

184302
return 0;
@@ -210,6 +328,7 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
210328

211329
ret = v4l2_async_notifier_call_complete(notifier);
212330
notifier->v4l2_dev = NULL;
331+
notifier->sd = NULL;
213332

214333
return ret;
215334
}
@@ -240,6 +359,8 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
240359
return ret;
241360
}
242361

362+
ret = v4l2_async_notifier_try_complete(notifier);
363+
243364
/* Keep also completed notifiers on the list */
244365
list_add(&notifier->list, &notifier_list);
245366

@@ -251,7 +372,7 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
251372
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
252373
struct v4l2_async_notifier *notifier)
253374
{
254-
if (WARN_ON(!v4l2_dev))
375+
if (WARN_ON(!v4l2_dev || notifier->sd))
255376
return -EINVAL;
256377

257378
notifier->v4l2_dev = v4l2_dev;
@@ -260,28 +381,56 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
260381
}
261382
EXPORT_SYMBOL(v4l2_async_notifier_register);
262383

263-
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
384+
int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
385+
struct v4l2_async_notifier *notifier)
264386
{
265-
struct v4l2_subdev *sd, *tmp;
387+
if (WARN_ON(!sd || notifier->v4l2_dev))
388+
return -EINVAL;
266389

267-
if (!notifier->v4l2_dev)
268-
return;
390+
notifier->sd = sd;
269391

270-
mutex_lock(&list_lock);
392+
return __v4l2_async_notifier_register(notifier);
393+
}
394+
EXPORT_SYMBOL(v4l2_async_subdev_notifier_register);
271395

272-
list_del(&notifier->list);
396+
/* Unbind all sub-devices in the notifier tree. */
397+
static void v4l2_async_notifier_unbind_all_subdevs(
398+
struct v4l2_async_notifier *notifier)
399+
{
400+
struct v4l2_subdev *sd, *tmp;
273401

274402
list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
403+
struct v4l2_async_notifier *subdev_notifier =
404+
v4l2_async_find_subdev_notifier(sd);
405+
406+
if (subdev_notifier)
407+
v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
408+
275409
v4l2_async_cleanup(sd);
276410

277411
v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
278412

279413
list_move(&sd->async_list, &subdev_list);
280414
}
281415

282-
mutex_unlock(&list_lock);
416+
notifier->parent = NULL;
417+
}
283418

419+
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
420+
{
421+
if (!notifier->v4l2_dev && !notifier->sd)
422+
return;
423+
424+
mutex_lock(&list_lock);
425+
426+
v4l2_async_notifier_unbind_all_subdevs(notifier);
427+
428+
notifier->sd = NULL;
284429
notifier->v4l2_dev = NULL;
430+
431+
list_del(&notifier->list);
432+
433+
mutex_unlock(&list_lock);
285434
}
286435
EXPORT_SYMBOL(v4l2_async_notifier_unregister);
287436

@@ -331,14 +480,25 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
331480
INIT_LIST_HEAD(&sd->async_list);
332481

333482
list_for_each_entry(notifier, &notifier_list, list) {
334-
struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier,
335-
sd);
336-
if (asd) {
337-
int ret = v4l2_async_match_notify(
338-
notifier, notifier->v4l2_dev, sd, asd);
339-
mutex_unlock(&list_lock);
340-
return ret;
341-
}
483+
struct v4l2_device *v4l2_dev =
484+
v4l2_async_notifier_find_v4l2_dev(notifier);
485+
struct v4l2_async_subdev *asd;
486+
int ret;
487+
488+
if (!v4l2_dev)
489+
continue;
490+
491+
asd = v4l2_async_find_match(notifier, sd);
492+
if (!asd)
493+
continue;
494+
495+
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
496+
497+
if (!ret)
498+
ret = v4l2_async_notifier_try_complete(notifier);
499+
500+
mutex_unlock(&list_lock);
501+
return ret;
342502
}
343503

344504
/* None matched, wait for hot-plugging */

include/media/v4l2-async.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ struct v4l2_async_notifier_operations {
102102
* @num_subdevs: number of subdevices used in the subdevs array
103103
* @max_subdevs: number of subdevices allocated in the subdevs array
104104
* @subdevs: array of pointers to subdevice descriptors
105-
* @v4l2_dev: pointer to struct v4l2_device
105+
* @v4l2_dev: v4l2_device of the root notifier, NULL otherwise
106+
* @sd: sub-device that registered the notifier, NULL otherwise
107+
* @parent: parent notifier
106108
* @waiting: list of struct v4l2_async_subdev, waiting for their drivers
107109
* @done: list of struct v4l2_subdev, already probed
108110
* @list: member in a global list of notifiers
@@ -113,6 +115,8 @@ struct v4l2_async_notifier {
113115
unsigned int max_subdevs;
114116
struct v4l2_async_subdev **subdevs;
115117
struct v4l2_device *v4l2_dev;
118+
struct v4l2_subdev *sd;
119+
struct v4l2_async_notifier *parent;
116120
struct list_head waiting;
117121
struct list_head done;
118122
struct list_head list;
@@ -127,6 +131,16 @@ struct v4l2_async_notifier {
127131
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
128132
struct v4l2_async_notifier *notifier);
129133

134+
/**
135+
* v4l2_async_subdev_notifier_register - registers a subdevice asynchronous
136+
* notifier for a sub-device
137+
*
138+
* @sd: pointer to &struct v4l2_subdev
139+
* @notifier: pointer to &struct v4l2_async_notifier
140+
*/
141+
int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
142+
struct v4l2_async_notifier *notifier);
143+
130144
/**
131145
* v4l2_async_notifier_unregister - unregisters a subdevice asynchronous notifier
132146
*

0 commit comments

Comments
 (0)