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