2 * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
4 * Copyright (c) 2016 Mentor Graphics Inc.
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.
11 #include <linux/delay.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"
30 static inline struct imx_media_dev
*notifier2dev(struct v4l2_async_notifier
*n
)
32 return container_of(n
, struct imx_media_dev
, notifier
);
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.
42 int imx_media_add_async_subdev(struct imx_media_dev
*imxmd
,
43 struct fwnode_handle
*fwnode
,
44 struct platform_device
*pdev
)
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
;
53 asd
= v4l2_async_notifier_add_fwnode_subdev(
54 &imxmd
->notifier
, fwnode
, sizeof(*imxasd
));
56 devname
= dev_name(&pdev
->dev
);
57 asd
= v4l2_async_notifier_add_devname_subdev(
58 &imxmd
->notifier
, devname
, sizeof(*imxasd
));
65 dev_dbg(imxmd
->md
.dev
, "%s: already added %pOFn\n",
68 dev_dbg(imxmd
->md
.dev
, "%s: already added %s\n",
74 imxasd
= to_imx_media_asd(asd
);
80 dev_dbg(imxmd
->md
.dev
, "%s: added %pOFn, match type FWNODE\n",
83 dev_dbg(imxmd
->md
.dev
, "%s: added %s, match type DEVNAME\n",
90 * get IPU from this CSI and add it to the list of IPUs
91 * the media driver will control.
93 static int imx_media_get_ipu(struct imx_media_dev
*imxmd
,
94 struct v4l2_subdev
*csi_sd
)
99 ipu
= dev_get_drvdata(csi_sd
->dev
->parent
);
101 v4l2_err(&imxmd
->v4l2_dev
,
102 "CSI %s has no parent IPU!\n", csi_sd
->name
);
106 ipu_id
= ipu_get_num(ipu
);
108 v4l2_err(&imxmd
->v4l2_dev
, "invalid IPU id %d!\n", ipu_id
);
112 if (!imxmd
->ipu
[ipu_id
])
113 imxmd
->ipu
[ipu_id
] = ipu
;
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
)
123 struct imx_media_dev
*imxmd
= notifier2dev(notifier
);
126 mutex_lock(&imxmd
->mutex
);
128 if (sd
->grp_id
& IMX_MEDIA_GRP_ID_IPU_CSI
) {
129 ret
= imx_media_get_ipu(imxmd
, sd
);
134 v4l2_info(&imxmd
->v4l2_dev
, "subdev %s bound\n", sd
->name
);
136 mutex_unlock(&imxmd
->mutex
);
141 * Create the media links for all subdevs that registered.
142 * Called after all async subdevs have bound.
144 static int imx_media_create_links(struct v4l2_async_notifier
*notifier
)
146 struct imx_media_dev
*imxmd
= notifier2dev(notifier
);
147 struct v4l2_subdev
*sd
;
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
);
162 * the CSIs straddle between the external and the IPU
163 * internal entities, so create the external links
164 * to the CSI sink pads.
166 if (sd
->grp_id
& IMX_MEDIA_GRP_ID_IPU_CSI
)
167 imx_media_create_csi_of_links(imxmd
, sd
);
169 case IMX_MEDIA_GRP_ID_CSI
:
170 imx_media_create_csi_of_links(imxmd
, sd
);
175 * if this subdev has fwnode links, create media
178 imx_media_create_of_links(imxmd
, sd
);
187 * adds given video device to given imx-media source pad vdev list.
188 * Continues upstream from the pad entity's sink pads.
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
)
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
;
201 /* skip this entity if not a v4l2_subdev */
202 if (!is_media_entity_v4l2_subdev(entity
))
205 sd
= media_entity_to_v4l2_subdev(entity
);
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
);
212 * shouldn't happen, but no reason to fail driver load,
213 * just skip this entity.
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
)
224 dev_dbg(imxmd
->md
.dev
, "adding %s to pad %s:%u\n",
225 vdev
->vfd
->entity
.name
, entity
->name
, srcpad
->index
);
227 pad_vdev
= devm_kzalloc(imxmd
->md
.dev
, sizeof(*pad_vdev
), GFP_KERNEL
);
231 /* attach this vdev to this pad */
232 pad_vdev
->vdev
= vdev
;
233 list_add_tail(&pad_vdev
->list
, pad_vdev_list
);
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
];
239 if (!(pad
->flags
& MEDIA_PAD_FL_SINK
))
242 list_for_each_entry(link
, &entity
->links
, list
) {
243 if (link
->sink
!= pad
)
245 ret
= imx_media_add_vdev_to_pad(imxmd
, vdev
,
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
260 static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev
*imxmd
)
262 struct list_head
*vdev_lists
;
263 struct media_entity
*entity
;
264 struct v4l2_subdev
*sd
;
267 list_for_each_entry(sd
, &imxmd
->v4l2_dev
.subdevs
, list
) {
268 entity
= &sd
->entity
;
269 vdev_lists
= devm_kcalloc(
271 entity
->num_pads
, sizeof(*vdev_lists
),
276 /* attach to the subdev's host private pointer */
277 sd
->host_priv
= vdev_lists
;
279 for (i
= 0; i
< entity
->num_pads
; i
++)
280 INIT_LIST_HEAD(to_pad_vdev_list(sd
, i
));
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
)
289 struct imx_media_video_dev
*vdev
;
290 struct media_link
*link
;
293 ret
= imx_media_alloc_pad_vdev_lists(imxmd
);
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
);
308 /* async subdev complete notifier */
309 int imx_media_probe_complete(struct v4l2_async_notifier
*notifier
)
311 struct imx_media_dev
*imxmd
= notifier2dev(notifier
);
314 mutex_lock(&imxmd
->mutex
);
316 ret
= imx_media_create_links(notifier
);
320 ret
= imx_media_create_pad_vdev_lists(imxmd
);
324 ret
= v4l2_device_register_subdev_nodes(&imxmd
->v4l2_dev
);
326 mutex_unlock(&imxmd
->mutex
);
330 return media_device_register(&imxmd
->md
);
334 * adds controls to a video device from an entity subdevice.
335 * Continues upstream from the entity's sink pads.
337 static int imx_media_inherit_controls(struct imx_media_dev
*imxmd
,
338 struct video_device
*vfd
,
339 struct media_entity
*entity
)
343 if (is_media_entity_v4l2_subdev(entity
)) {
344 struct v4l2_subdev
*sd
= media_entity_to_v4l2_subdev(entity
);
346 dev_dbg(imxmd
->md
.dev
,
347 "adding controls to %s from %s\n",
348 vfd
->entity
.name
, sd
->entity
.name
);
350 ret
= v4l2_ctrl_add_handler(vfd
->ctrl_handler
,
358 for (i
= 0; i
< entity
->num_pads
; i
++) {
359 struct media_pad
*pad
, *spad
= &entity
->pads
[i
];
361 if (!(spad
->flags
& MEDIA_PAD_FL_SINK
))
364 pad
= media_entity_remote_pad(spad
);
365 if (!pad
|| !is_media_entity_v4l2_subdev(pad
->entity
))
368 ret
= imx_media_inherit_controls(imxmd
, vfd
, pad
->entity
);
376 int imx_media_link_notify(struct media_link
*link
, u32 flags
,
377 unsigned int notification
)
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
;
387 ret
= v4l2_pipeline_link_notify(link
, flags
, notification
);
391 /* don't bother if source is not a subdev */
392 if (!is_media_entity_v4l2_subdev(source
))
395 sd
= media_entity_to_v4l2_subdev(source
);
396 pad_idx
= link
->source
->index
;
398 imxmd
= dev_get_drvdata(sd
->v4l2_dev
->dev
);
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 */
407 * Before disabling a link, reset controls for all video
408 * devices reachable from this link.
410 * After enabling a link, refresh controls for all video
411 * devices reachable from this link.
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",
420 v4l2_ctrl_handler_free(vfd
->ctrl_handler
);
421 v4l2_ctrl_handler_init(vfd
->ctrl_handler
, 0);
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",
430 ret
= imx_media_inherit_controls(imxmd
, vfd
,
440 void imx_media_notify(struct v4l2_subdev
*sd
, unsigned int notification
,
443 struct media_entity
*entity
= &sd
->entity
;
446 if (notification
!= V4L2_DEVICE_NOTIFY_EVENT
)
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
;
454 pad_vdev_list
= to_pad_vdev_list(sd
, pad
->index
);
457 list_for_each_entry(pad_vdev
, pad_vdev_list
, list
)
458 v4l2_event_queue(pad_vdev
->vdev
->vfd
, arg
);
462 static int imx_media_probe(struct platform_device
*pdev
)
464 struct device
*dev
= &pdev
->dev
;
465 struct device_node
*node
= dev
->of_node
;
466 struct imx_media_dev
*imxmd
;
469 imxmd
= imx_media_dev_init(dev
);
471 return PTR_ERR(imxmd
);
473 ret
= imx_media_add_of_subdevs(imxmd
, node
);
475 v4l2_err(&imxmd
->v4l2_dev
,
476 "add_of_subdevs failed with %d\n", ret
);
480 ret
= imx_media_add_internal_subdevs(imxmd
);
482 v4l2_err(&imxmd
->v4l2_dev
,
483 "add_internal_subdevs failed with %d\n", ret
);
487 ret
= imx_media_dev_notifier_register(imxmd
);
494 imx_media_remove_internal_subdevs(imxmd
);
496 v4l2_async_notifier_cleanup(&imxmd
->notifier
);
497 v4l2_device_unregister(&imxmd
->v4l2_dev
);
498 media_device_cleanup(&imxmd
->md
);
503 static int imx_media_remove(struct platform_device
*pdev
)
505 struct imx_media_dev
*imxmd
=
506 (struct imx_media_dev
*)platform_get_drvdata(pdev
);
508 v4l2_info(&imxmd
->v4l2_dev
, "Removing imx-media\n");
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
);
520 static const struct of_device_id imx_media_dt_ids
[] = {
521 { .compatible
= "fsl,imx-capture-subsystem" },
524 MODULE_DEVICE_TABLE(of
, imx_media_dt_ids
);
526 static struct platform_driver imx_media_pdrv
= {
527 .probe
= imx_media_probe
,
528 .remove
= imx_media_remove
,
531 .of_match_table
= imx_media_dt_ids
,
535 module_platform_driver(imx_media_pdrv
);
537 MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
538 MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
539 MODULE_LICENSE("GPL");