1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC |
4 | * |
5 | * Copyright (c) 2016-2019 Mentor Graphics Inc. |
6 | */ |
7 | #include <linux/fs.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include <media/v4l2-async.h> |
11 | #include <media/v4l2-event.h> |
12 | #include <media/imx.h> |
13 | #include "imx-media.h" |
14 | |
15 | static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) |
16 | { |
17 | return container_of(n, struct imx_media_dev, notifier); |
18 | } |
19 | |
20 | /* async subdev bound notifier */ |
21 | static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, |
22 | struct v4l2_subdev *sd, |
23 | struct v4l2_async_connection *asd) |
24 | { |
25 | struct imx_media_dev *imxmd = notifier2dev(n: notifier); |
26 | int ret; |
27 | |
28 | if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) { |
29 | /* register the IPU internal subdevs */ |
30 | ret = imx_media_register_ipu_internal_subdevs(imxmd, csi: sd); |
31 | if (ret) |
32 | return ret; |
33 | } |
34 | |
35 | dev_dbg(imxmd->md.dev, "subdev %s bound\n" , sd->name); |
36 | |
37 | return 0; |
38 | } |
39 | |
40 | /* async subdev complete notifier */ |
41 | static int imx6_media_probe_complete(struct v4l2_async_notifier *notifier) |
42 | { |
43 | struct imx_media_dev *imxmd = notifier2dev(n: notifier); |
44 | int ret; |
45 | |
46 | /* call the imx5/6/7 common probe completion handler */ |
47 | ret = imx_media_probe_complete(notifier); |
48 | if (ret) |
49 | return ret; |
50 | |
51 | mutex_lock(&imxmd->mutex); |
52 | |
53 | imxmd->m2m_vdev = imx_media_csc_scaler_device_init(dev: imxmd); |
54 | if (IS_ERR(ptr: imxmd->m2m_vdev)) { |
55 | ret = PTR_ERR(ptr: imxmd->m2m_vdev); |
56 | imxmd->m2m_vdev = NULL; |
57 | goto unlock; |
58 | } |
59 | |
60 | ret = imx_media_csc_scaler_device_register(vdev: imxmd->m2m_vdev); |
61 | unlock: |
62 | mutex_unlock(lock: &imxmd->mutex); |
63 | return ret; |
64 | } |
65 | |
66 | /* async subdev complete notifier */ |
67 | static const struct v4l2_async_notifier_operations imx_media_notifier_ops = { |
68 | .bound = imx_media_subdev_bound, |
69 | .complete = imx6_media_probe_complete, |
70 | }; |
71 | |
72 | static int imx_media_probe(struct platform_device *pdev) |
73 | { |
74 | struct device *dev = &pdev->dev; |
75 | struct device_node *node = dev->of_node; |
76 | struct imx_media_dev *imxmd; |
77 | int ret; |
78 | |
79 | imxmd = imx_media_dev_init(dev, NULL); |
80 | if (IS_ERR(ptr: imxmd)) |
81 | return PTR_ERR(ptr: imxmd); |
82 | |
83 | ret = imx_media_add_of_subdevs(dev: imxmd, np: node); |
84 | if (ret) { |
85 | v4l2_err(&imxmd->v4l2_dev, |
86 | "add_of_subdevs failed with %d\n" , ret); |
87 | goto cleanup; |
88 | } |
89 | |
90 | ret = imx_media_dev_notifier_register(imxmd, ops: &imx_media_notifier_ops); |
91 | if (ret) |
92 | goto cleanup; |
93 | |
94 | return 0; |
95 | |
96 | cleanup: |
97 | v4l2_async_nf_cleanup(notifier: &imxmd->notifier); |
98 | v4l2_device_unregister(v4l2_dev: &imxmd->v4l2_dev); |
99 | media_device_cleanup(mdev: &imxmd->md); |
100 | |
101 | return ret; |
102 | } |
103 | |
104 | static void imx_media_remove(struct platform_device *pdev) |
105 | { |
106 | struct imx_media_dev *imxmd = |
107 | (struct imx_media_dev *)platform_get_drvdata(pdev); |
108 | |
109 | v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n" ); |
110 | |
111 | if (imxmd->m2m_vdev) { |
112 | imx_media_csc_scaler_device_unregister(vdev: imxmd->m2m_vdev); |
113 | imxmd->m2m_vdev = NULL; |
114 | } |
115 | |
116 | v4l2_async_nf_unregister(notifier: &imxmd->notifier); |
117 | imx_media_unregister_ipu_internal_subdevs(imxmd); |
118 | v4l2_async_nf_cleanup(notifier: &imxmd->notifier); |
119 | media_device_unregister(mdev: &imxmd->md); |
120 | v4l2_device_unregister(v4l2_dev: &imxmd->v4l2_dev); |
121 | media_device_cleanup(mdev: &imxmd->md); |
122 | } |
123 | |
124 | static const struct of_device_id imx_media_dt_ids[] = { |
125 | { .compatible = "fsl,imx-capture-subsystem" }, |
126 | { /* sentinel */ } |
127 | }; |
128 | MODULE_DEVICE_TABLE(of, imx_media_dt_ids); |
129 | |
130 | static struct platform_driver imx_media_pdrv = { |
131 | .probe = imx_media_probe, |
132 | .remove_new = imx_media_remove, |
133 | .driver = { |
134 | .name = "imx-media" , |
135 | .of_match_table = imx_media_dt_ids, |
136 | }, |
137 | }; |
138 | |
139 | module_platform_driver(imx_media_pdrv); |
140 | |
141 | MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver" ); |
142 | MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>" ); |
143 | MODULE_LICENSE("GPL" ); |
144 | |