]> git.proxmox.com Git - mirror_ubuntu-kernels.git/blob - drivers/staging/media/imx/imx-media-dev.c
Merge branches 'for-5.1/upstream-fixes', 'for-5.2/core', 'for-5.2/ish', 'for-5.2...
[mirror_ubuntu-kernels.git] / drivers / staging / media / imx / imx-media-dev.c
1 /*
2 * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
3 *
4 * Copyright (c) 2016 Mentor Graphics Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11 #include <linux/delay.h>
12 #include <linux/fs.h>
13 #include <linux/module.h>
14 #include <linux/of_graph.h>
15 #include <linux/of_platform.h>
16 #include <linux/pinctrl/consumer.h>
17 #include <linux/platform_device.h>
18 #include <linux/sched.h>
19 #include <linux/slab.h>
20 #include <linux/spinlock.h>
21 #include <linux/timer.h>
22 #include <media/v4l2-ctrls.h>
23 #include <media/v4l2-event.h>
24 #include <media/v4l2-ioctl.h>
25 #include <media/v4l2-mc.h>
26 #include <video/imx-ipu-v3.h>
27 #include <media/imx.h>
28 #include "imx-media.h"
29
30 static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n)
31 {
32 return container_of(n, struct imx_media_dev, notifier);
33 }
34
35 /*
36 * Adds a subdev to the root notifier's async subdev list. If fwnode is
37 * non-NULL, adds the async as a V4L2_ASYNC_MATCH_FWNODE match type,
38 * otherwise as a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name
39 * of the given platform_device. This is called during driver load when
40 * forming the async subdev list.
41 */
42 int imx_media_add_async_subdev(struct imx_media_dev *imxmd,
43 struct fwnode_handle *fwnode,
44 struct platform_device *pdev)
45 {
46 struct device_node *np = to_of_node(fwnode);
47 struct imx_media_async_subdev *imxasd;
48 struct v4l2_async_subdev *asd;
49 const char *devname = NULL;
50 int ret;
51
52 if (fwnode) {
53 asd = v4l2_async_notifier_add_fwnode_subdev(
54 &imxmd->notifier, fwnode, sizeof(*imxasd));
55 } else {
56 devname = dev_name(&pdev->dev);
57 asd = v4l2_async_notifier_add_devname_subdev(
58 &imxmd->notifier, devname, sizeof(*imxasd));
59 }
60
61 if (IS_ERR(asd)) {
62 ret = PTR_ERR(asd);
63 if (ret == -EEXIST) {
64 if (np)
65 dev_dbg(imxmd->md.dev, "%s: already added %pOFn\n",
66 __func__, np);
67 else
68 dev_dbg(imxmd->md.dev, "%s: already added %s\n",
69 __func__, devname);
70 }
71 return ret;
72 }
73
74 imxasd = to_imx_media_asd(asd);
75
76 if (devname)
77 imxasd->pdev = pdev;
78
79 if (np)
80 dev_dbg(imxmd->md.dev, "%s: added %pOFn, match type FWNODE\n",
81 __func__, np);
82 else
83 dev_dbg(imxmd->md.dev, "%s: added %s, match type DEVNAME\n",
84 __func__, devname);
85
86 return 0;
87 }
88
89 /*
90 * get IPU from this CSI and add it to the list of IPUs
91 * the media driver will control.
92 */
93 static int imx_media_get_ipu(struct imx_media_dev *imxmd,
94 struct v4l2_subdev *csi_sd)
95 {
96 struct ipu_soc *ipu;
97 int ipu_id;
98
99 ipu = dev_get_drvdata(csi_sd->dev->parent);
100 if (!ipu) {
101 v4l2_err(&imxmd->v4l2_dev,
102 "CSI %s has no parent IPU!\n", csi_sd->name);
103 return -ENODEV;
104 }
105
106 ipu_id = ipu_get_num(ipu);
107 if (ipu_id > 1) {
108 v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id);
109 return -ENODEV;
110 }
111
112 if (!imxmd->ipu[ipu_id])
113 imxmd->ipu[ipu_id] = ipu;
114
115 return 0;
116 }
117
118 /* async subdev bound notifier */
119 int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
120 struct v4l2_subdev *sd,
121 struct v4l2_async_subdev *asd)
122 {
123 struct imx_media_dev *imxmd = notifier2dev(notifier);
124 int ret = 0;
125
126 mutex_lock(&imxmd->mutex);
127
128 if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) {
129 ret = imx_media_get_ipu(imxmd, sd);
130 if (ret)
131 goto out;
132 }
133
134 v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name);
135 out:
136 mutex_unlock(&imxmd->mutex);
137 return ret;
138 }
139
140 /*
141 * Create the media links for all subdevs that registered.
142 * Called after all async subdevs have bound.
143 */
144 static int imx_media_create_links(struct v4l2_async_notifier *notifier)
145 {
146 struct imx_media_dev *imxmd = notifier2dev(notifier);
147 struct v4l2_subdev *sd;
148 int ret;
149
150 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
151 switch (sd->grp_id) {
152 case IMX_MEDIA_GRP_ID_IPU_VDIC:
153 case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
154 case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
155 case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
156 case IMX_MEDIA_GRP_ID_IPU_CSI0:
157 case IMX_MEDIA_GRP_ID_IPU_CSI1:
158 ret = imx_media_create_ipu_internal_links(imxmd, sd);
159 if (ret)
160 return ret;
161 /*
162 * the CSIs straddle between the external and the IPU
163 * internal entities, so create the external links
164 * to the CSI sink pads.
165 */
166 if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI)
167 imx_media_create_csi_of_links(imxmd, sd);
168 break;
169 case IMX_MEDIA_GRP_ID_CSI:
170 imx_media_create_csi_of_links(imxmd, sd);
171
172 break;
173 default:
174 /*
175 * if this subdev has fwnode links, create media
176 * links for them.
177 */
178 imx_media_create_of_links(imxmd, sd);
179 break;
180 }
181 }
182
183 return 0;
184 }
185
186 /*
187 * adds given video device to given imx-media source pad vdev list.
188 * Continues upstream from the pad entity's sink pads.
189 */
190 static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd,
191 struct imx_media_video_dev *vdev,
192 struct media_pad *srcpad)
193 {
194 struct media_entity *entity = srcpad->entity;
195 struct imx_media_pad_vdev *pad_vdev;
196 struct list_head *pad_vdev_list;
197 struct media_link *link;
198 struct v4l2_subdev *sd;
199 int i, ret;
200
201 /* skip this entity if not a v4l2_subdev */
202 if (!is_media_entity_v4l2_subdev(entity))
203 return 0;
204
205 sd = media_entity_to_v4l2_subdev(entity);
206
207 pad_vdev_list = to_pad_vdev_list(sd, srcpad->index);
208 if (!pad_vdev_list) {
209 v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n",
210 entity->name, srcpad->index);
211 /*
212 * shouldn't happen, but no reason to fail driver load,
213 * just skip this entity.
214 */
215 return 0;
216 }
217
218 /* just return if we've been here before */
219 list_for_each_entry(pad_vdev, pad_vdev_list, list) {
220 if (pad_vdev->vdev == vdev)
221 return 0;
222 }
223
224 dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n",
225 vdev->vfd->entity.name, entity->name, srcpad->index);
226
227 pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL);
228 if (!pad_vdev)
229 return -ENOMEM;
230
231 /* attach this vdev to this pad */
232 pad_vdev->vdev = vdev;
233 list_add_tail(&pad_vdev->list, pad_vdev_list);
234
235 /* move upstream from this entity's sink pads */
236 for (i = 0; i < entity->num_pads; i++) {
237 struct media_pad *pad = &entity->pads[i];
238
239 if (!(pad->flags & MEDIA_PAD_FL_SINK))
240 continue;
241
242 list_for_each_entry(link, &entity->links, list) {
243 if (link->sink != pad)
244 continue;
245 ret = imx_media_add_vdev_to_pad(imxmd, vdev,
246 link->source);
247 if (ret)
248 return ret;
249 }
250 }
251
252 return 0;
253 }
254
255 /*
256 * For every subdevice, allocate an array of list_head's, one list_head
257 * for each pad, to hold the list of video devices reachable from that
258 * pad.
259 */
260 static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd)
261 {
262 struct list_head *vdev_lists;
263 struct media_entity *entity;
264 struct v4l2_subdev *sd;
265 int i;
266
267 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
268 entity = &sd->entity;
269 vdev_lists = devm_kcalloc(
270 imxmd->md.dev,
271 entity->num_pads, sizeof(*vdev_lists),
272 GFP_KERNEL);
273 if (!vdev_lists)
274 return -ENOMEM;
275
276 /* attach to the subdev's host private pointer */
277 sd->host_priv = vdev_lists;
278
279 for (i = 0; i < entity->num_pads; i++)
280 INIT_LIST_HEAD(to_pad_vdev_list(sd, i));
281 }
282
283 return 0;
284 }
285
286 /* form the vdev lists in all imx-media source pads */
287 static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd)
288 {
289 struct imx_media_video_dev *vdev;
290 struct media_link *link;
291 int ret;
292
293 ret = imx_media_alloc_pad_vdev_lists(imxmd);
294 if (ret)
295 return ret;
296
297 list_for_each_entry(vdev, &imxmd->vdev_list, list) {
298 link = list_first_entry(&vdev->vfd->entity.links,
299 struct media_link, list);
300 ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source);
301 if (ret)
302 return ret;
303 }
304
305 return 0;
306 }
307
308 /* async subdev complete notifier */
309 int imx_media_probe_complete(struct v4l2_async_notifier *notifier)
310 {
311 struct imx_media_dev *imxmd = notifier2dev(notifier);
312 int ret;
313
314 mutex_lock(&imxmd->mutex);
315
316 ret = imx_media_create_links(notifier);
317 if (ret)
318 goto unlock;
319
320 ret = imx_media_create_pad_vdev_lists(imxmd);
321 if (ret)
322 goto unlock;
323
324 ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev);
325 unlock:
326 mutex_unlock(&imxmd->mutex);
327 if (ret)
328 return ret;
329
330 return media_device_register(&imxmd->md);
331 }
332
333 /*
334 * adds controls to a video device from an entity subdevice.
335 * Continues upstream from the entity's sink pads.
336 */
337 static int imx_media_inherit_controls(struct imx_media_dev *imxmd,
338 struct video_device *vfd,
339 struct media_entity *entity)
340 {
341 int i, ret = 0;
342
343 if (is_media_entity_v4l2_subdev(entity)) {
344 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
345
346 dev_dbg(imxmd->md.dev,
347 "adding controls to %s from %s\n",
348 vfd->entity.name, sd->entity.name);
349
350 ret = v4l2_ctrl_add_handler(vfd->ctrl_handler,
351 sd->ctrl_handler,
352 NULL, true);
353 if (ret)
354 return ret;
355 }
356
357 /* move upstream */
358 for (i = 0; i < entity->num_pads; i++) {
359 struct media_pad *pad, *spad = &entity->pads[i];
360
361 if (!(spad->flags & MEDIA_PAD_FL_SINK))
362 continue;
363
364 pad = media_entity_remote_pad(spad);
365 if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
366 continue;
367
368 ret = imx_media_inherit_controls(imxmd, vfd, pad->entity);
369 if (ret)
370 break;
371 }
372
373 return ret;
374 }
375
376 int imx_media_link_notify(struct media_link *link, u32 flags,
377 unsigned int notification)
378 {
379 struct media_entity *source = link->source->entity;
380 struct imx_media_pad_vdev *pad_vdev;
381 struct list_head *pad_vdev_list;
382 struct imx_media_dev *imxmd;
383 struct video_device *vfd;
384 struct v4l2_subdev *sd;
385 int pad_idx, ret;
386
387 ret = v4l2_pipeline_link_notify(link, flags, notification);
388 if (ret)
389 return ret;
390
391 /* don't bother if source is not a subdev */
392 if (!is_media_entity_v4l2_subdev(source))
393 return 0;
394
395 sd = media_entity_to_v4l2_subdev(source);
396 pad_idx = link->source->index;
397
398 imxmd = dev_get_drvdata(sd->v4l2_dev->dev);
399
400 pad_vdev_list = to_pad_vdev_list(sd, pad_idx);
401 if (!pad_vdev_list) {
402 /* shouldn't happen, but no reason to fail link setup */
403 return 0;
404 }
405
406 /*
407 * Before disabling a link, reset controls for all video
408 * devices reachable from this link.
409 *
410 * After enabling a link, refresh controls for all video
411 * devices reachable from this link.
412 */
413 if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
414 !(flags & MEDIA_LNK_FL_ENABLED)) {
415 list_for_each_entry(pad_vdev, pad_vdev_list, list) {
416 vfd = pad_vdev->vdev->vfd;
417 dev_dbg(imxmd->md.dev,
418 "reset controls for %s\n",
419 vfd->entity.name);
420 v4l2_ctrl_handler_free(vfd->ctrl_handler);
421 v4l2_ctrl_handler_init(vfd->ctrl_handler, 0);
422 }
423 } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
424 (link->flags & MEDIA_LNK_FL_ENABLED)) {
425 list_for_each_entry(pad_vdev, pad_vdev_list, list) {
426 vfd = pad_vdev->vdev->vfd;
427 dev_dbg(imxmd->md.dev,
428 "refresh controls for %s\n",
429 vfd->entity.name);
430 ret = imx_media_inherit_controls(imxmd, vfd,
431 &vfd->entity);
432 if (ret)
433 break;
434 }
435 }
436
437 return ret;
438 }
439
440 void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification,
441 void *arg)
442 {
443 struct media_entity *entity = &sd->entity;
444 int i;
445
446 if (notification != V4L2_DEVICE_NOTIFY_EVENT)
447 return;
448
449 for (i = 0; i < entity->num_pads; i++) {
450 struct media_pad *pad = &entity->pads[i];
451 struct imx_media_pad_vdev *pad_vdev;
452 struct list_head *pad_vdev_list;
453
454 pad_vdev_list = to_pad_vdev_list(sd, pad->index);
455 if (!pad_vdev_list)
456 continue;
457 list_for_each_entry(pad_vdev, pad_vdev_list, list)
458 v4l2_event_queue(pad_vdev->vdev->vfd, arg);
459 }
460 }
461
462 static int imx_media_probe(struct platform_device *pdev)
463 {
464 struct device *dev = &pdev->dev;
465 struct device_node *node = dev->of_node;
466 struct imx_media_dev *imxmd;
467 int ret;
468
469 imxmd = imx_media_dev_init(dev);
470 if (IS_ERR(imxmd))
471 return PTR_ERR(imxmd);
472
473 ret = imx_media_add_of_subdevs(imxmd, node);
474 if (ret) {
475 v4l2_err(&imxmd->v4l2_dev,
476 "add_of_subdevs failed with %d\n", ret);
477 goto cleanup;
478 }
479
480 ret = imx_media_add_internal_subdevs(imxmd);
481 if (ret) {
482 v4l2_err(&imxmd->v4l2_dev,
483 "add_internal_subdevs failed with %d\n", ret);
484 goto cleanup;
485 }
486
487 ret = imx_media_dev_notifier_register(imxmd);
488 if (ret)
489 goto del_int;
490
491 return 0;
492
493 del_int:
494 imx_media_remove_internal_subdevs(imxmd);
495 cleanup:
496 v4l2_async_notifier_cleanup(&imxmd->notifier);
497 v4l2_device_unregister(&imxmd->v4l2_dev);
498 media_device_cleanup(&imxmd->md);
499
500 return ret;
501 }
502
503 static int imx_media_remove(struct platform_device *pdev)
504 {
505 struct imx_media_dev *imxmd =
506 (struct imx_media_dev *)platform_get_drvdata(pdev);
507
508 v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n");
509
510 v4l2_async_notifier_unregister(&imxmd->notifier);
511 imx_media_remove_internal_subdevs(imxmd);
512 v4l2_async_notifier_cleanup(&imxmd->notifier);
513 media_device_unregister(&imxmd->md);
514 v4l2_device_unregister(&imxmd->v4l2_dev);
515 media_device_cleanup(&imxmd->md);
516
517 return 0;
518 }
519
520 static const struct of_device_id imx_media_dt_ids[] = {
521 { .compatible = "fsl,imx-capture-subsystem" },
522 { /* sentinel */ }
523 };
524 MODULE_DEVICE_TABLE(of, imx_media_dt_ids);
525
526 static struct platform_driver imx_media_pdrv = {
527 .probe = imx_media_probe,
528 .remove = imx_media_remove,
529 .driver = {
530 .name = "imx-media",
531 .of_match_table = imx_media_dt_ids,
532 },
533 };
534
535 module_platform_driver(imx_media_pdrv);
536
537 MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
538 MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
539 MODULE_LICENSE("GPL");