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 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 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 */ 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 */ 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 */ 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