1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Media driver for Freescale i.MX5/6 SOC
4 *
5 * Open Firmware parsing.
6 *
7 * Copyright (c) 2016 Mentor Graphics Inc.
8 */
9 #include <linux/of_platform.h>
10 #include <media/v4l2-ctrls.h>
11 #include <media/v4l2-device.h>
12 #include <media/v4l2-fwnode.h>
13 #include <media/v4l2-subdev.h>
14 #include <media/videobuf2-dma-contig.h>
15 #include <linux/of_graph.h>
16 #include <video/imx-ipu-v3.h>
17 #include "imx-media.h"
18
imx_media_of_add_csi(struct imx_media_dev * imxmd,struct device_node * csi_np)19 int imx_media_of_add_csi(struct imx_media_dev *imxmd,
20 struct device_node *csi_np)
21 {
22 struct v4l2_async_subdev *asd;
23 int ret = 0;
24
25 if (!of_device_is_available(csi_np)) {
26 dev_dbg(imxmd->md.dev, "%s: %pOFn not enabled\n", __func__,
27 csi_np);
28 return -ENODEV;
29 }
30
31 /* add CSI fwnode to async notifier */
32 asd = v4l2_async_notifier_add_fwnode_subdev(&imxmd->notifier,
33 of_fwnode_handle(csi_np),
34 sizeof(*asd));
35 if (IS_ERR(asd)) {
36 ret = PTR_ERR(asd);
37 if (ret == -EEXIST)
38 dev_dbg(imxmd->md.dev, "%s: already added %pOFn\n",
39 __func__, csi_np);
40 }
41
42 return ret;
43 }
44 EXPORT_SYMBOL_GPL(imx_media_of_add_csi);
45
imx_media_add_of_subdevs(struct imx_media_dev * imxmd,struct device_node * np)46 int imx_media_add_of_subdevs(struct imx_media_dev *imxmd,
47 struct device_node *np)
48 {
49 struct device_node *csi_np;
50 int i, ret;
51
52 for (i = 0; ; i++) {
53 csi_np = of_parse_phandle(np, "ports", i);
54 if (!csi_np)
55 break;
56
57 ret = imx_media_of_add_csi(imxmd, csi_np);
58 if (ret) {
59 /* unavailable or already added is not an error */
60 if (ret == -ENODEV || ret == -EEXIST) {
61 of_node_put(csi_np);
62 continue;
63 }
64
65 /* other error, can't continue */
66 goto err_out;
67 }
68 }
69
70 return 0;
71
72 err_out:
73 of_node_put(csi_np);
74 return ret;
75 }
76 EXPORT_SYMBOL_GPL(imx_media_add_of_subdevs);
77
78 /*
79 * Create a single media link to/from sd using a fwnode link.
80 *
81 * NOTE: this function assumes an OF port node is equivalent to
82 * a media pad (port id equal to media pad index), and that an
83 * OF endpoint node is equivalent to a media link.
84 */
create_of_link(struct imx_media_dev * imxmd,struct v4l2_subdev * sd,struct v4l2_fwnode_link * link)85 static int create_of_link(struct imx_media_dev *imxmd,
86 struct v4l2_subdev *sd,
87 struct v4l2_fwnode_link *link)
88 {
89 struct v4l2_subdev *remote, *src, *sink;
90 int src_pad, sink_pad;
91
92 if (link->local_port >= sd->entity.num_pads)
93 return -EINVAL;
94
95 remote = imx_media_find_subdev_by_fwnode(imxmd, link->remote_node);
96 if (!remote)
97 return 0;
98
99 if (sd->entity.pads[link->local_port].flags & MEDIA_PAD_FL_SINK) {
100 src = remote;
101 src_pad = link->remote_port;
102 sink = sd;
103 sink_pad = link->local_port;
104 } else {
105 src = sd;
106 src_pad = link->local_port;
107 sink = remote;
108 sink_pad = link->remote_port;
109 }
110
111 /* make sure link doesn't already exist before creating */
112 if (media_entity_find_link(&src->entity.pads[src_pad],
113 &sink->entity.pads[sink_pad]))
114 return 0;
115
116 v4l2_info(sd->v4l2_dev, "%s:%d -> %s:%d\n",
117 src->name, src_pad, sink->name, sink_pad);
118
119 return media_create_pad_link(&src->entity, src_pad,
120 &sink->entity, sink_pad, 0);
121 }
122
123 /*
124 * Create media links to/from sd using its device-tree endpoints.
125 */
imx_media_create_of_links(struct imx_media_dev * imxmd,struct v4l2_subdev * sd)126 int imx_media_create_of_links(struct imx_media_dev *imxmd,
127 struct v4l2_subdev *sd)
128 {
129 struct v4l2_fwnode_link link;
130 struct device_node *ep;
131 int ret;
132
133 for_each_endpoint_of_node(sd->dev->of_node, ep) {
134 ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link);
135 if (ret)
136 continue;
137
138 ret = create_of_link(imxmd, sd, &link);
139 v4l2_fwnode_put_link(&link);
140 if (ret)
141 return ret;
142 }
143
144 return 0;
145 }
146 EXPORT_SYMBOL_GPL(imx_media_create_of_links);
147
148 /*
149 * Create media links to the given CSI subdevice's sink pads,
150 * using its device-tree endpoints.
151 */
imx_media_create_csi_of_links(struct imx_media_dev * imxmd,struct v4l2_subdev * csi)152 int imx_media_create_csi_of_links(struct imx_media_dev *imxmd,
153 struct v4l2_subdev *csi)
154 {
155 struct device_node *csi_np = csi->dev->of_node;
156 struct device_node *ep;
157
158 for_each_child_of_node(csi_np, ep) {
159 struct fwnode_handle *fwnode, *csi_ep;
160 struct v4l2_fwnode_link link;
161 int ret;
162
163 memset(&link, 0, sizeof(link));
164
165 link.local_node = of_fwnode_handle(csi_np);
166 link.local_port = CSI_SINK_PAD;
167
168 csi_ep = of_fwnode_handle(ep);
169
170 fwnode = fwnode_graph_get_remote_endpoint(csi_ep);
171 if (!fwnode)
172 continue;
173
174 fwnode = fwnode_get_parent(fwnode);
175 fwnode_property_read_u32(fwnode, "reg", &link.remote_port);
176 fwnode = fwnode_get_next_parent(fwnode);
177 if (is_of_node(fwnode) &&
178 of_node_name_eq(to_of_node(fwnode), "ports"))
179 fwnode = fwnode_get_next_parent(fwnode);
180 link.remote_node = fwnode;
181
182 ret = create_of_link(imxmd, csi, &link);
183 fwnode_handle_put(link.remote_node);
184 if (ret)
185 return ret;
186 }
187
188 return 0;
189 }
190 EXPORT_SYMBOL_GPL(imx_media_create_csi_of_links);
191