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