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