1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Parallel video capture module (VIP) for the Tegra VI. 4 * 5 * This file implements the VIP-specific infrastructure. 6 * 7 * Copyright (C) 2023 SKIDATA GmbH 8 * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> 9 */ 10 11 #include <linux/device.h> 12 #include <linux/host1x.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/of_graph.h> 16 #include <linux/platform_device.h> 17 #include <linux/pm_runtime.h> 18 19 #include <media/v4l2-fwnode.h> 20 21 #include "vip.h" 22 #include "video.h" 23 24 static inline struct tegra_vip *host1x_client_to_vip(struct host1x_client *client) 25 { 26 return container_of(client, struct tegra_vip, client); 27 } 28 29 static inline struct tegra_vip_channel *subdev_to_vip_channel(struct v4l2_subdev *subdev) 30 { 31 return container_of(subdev, struct tegra_vip_channel, subdev); 32 } 33 34 static inline struct tegra_vip *vip_channel_to_vip(struct tegra_vip_channel *chan) 35 { 36 return container_of(chan, struct tegra_vip, chan); 37 } 38 39 /* Find the previous subdev in the pipeline (i.e. the one connected to our sink pad) */ 40 static struct v4l2_subdev *tegra_vip_channel_get_prev_subdev(struct tegra_vip_channel *chan) 41 { 42 struct media_pad *remote_pad; 43 44 remote_pad = media_pad_remote_pad_first(&chan->pads[TEGRA_VIP_PAD_SINK]); 45 if (!remote_pad) 46 return NULL; 47 48 return media_entity_to_v4l2_subdev(remote_pad->entity); 49 } 50 51 static int tegra_vip_enable_stream(struct v4l2_subdev *subdev) 52 { 53 struct tegra_vip_channel *vip_chan = subdev_to_vip_channel(subdev); 54 struct tegra_vip *vip = vip_channel_to_vip(vip_chan); 55 struct v4l2_subdev *prev_subdev = tegra_vip_channel_get_prev_subdev(vip_chan); 56 int err; 57 58 err = pm_runtime_resume_and_get(vip->dev); 59 if (err) 60 return dev_err_probe(vip->dev, err, "failed to get runtime PM\n"); 61 62 err = vip->soc->ops->vip_start_streaming(vip_chan); 63 if (err < 0) 64 goto err_start_streaming; 65 66 err = v4l2_subdev_call(prev_subdev, video, s_stream, true); 67 if (err < 0 && err != -ENOIOCTLCMD) 68 goto err_prev_subdev_start_stream; 69 70 return 0; 71 72 err_prev_subdev_start_stream: 73 err_start_streaming: 74 pm_runtime_put(vip->dev); 75 return err; 76 } 77 78 static int tegra_vip_disable_stream(struct v4l2_subdev *subdev) 79 { 80 struct tegra_vip_channel *vip_chan = subdev_to_vip_channel(subdev); 81 struct tegra_vip *vip = vip_channel_to_vip(vip_chan); 82 struct v4l2_subdev *prev_subdev = tegra_vip_channel_get_prev_subdev(vip_chan); 83 84 v4l2_subdev_call(prev_subdev, video, s_stream, false); 85 86 pm_runtime_put(vip->dev); 87 88 return 0; 89 } 90 91 static int tegra_vip_s_stream(struct v4l2_subdev *subdev, int enable) 92 { 93 int err; 94 95 if (enable) 96 err = tegra_vip_enable_stream(subdev); 97 else 98 err = tegra_vip_disable_stream(subdev); 99 100 return err; 101 } 102 103 static const struct v4l2_subdev_video_ops tegra_vip_video_ops = { 104 .s_stream = tegra_vip_s_stream, 105 }; 106 107 static const struct v4l2_subdev_ops tegra_vip_ops = { 108 .video = &tegra_vip_video_ops, 109 }; 110 111 static int tegra_vip_channel_of_parse(struct tegra_vip *vip) 112 { 113 struct device *dev = vip->dev; 114 struct device_node *np = dev->of_node; 115 struct v4l2_fwnode_endpoint v4l2_ep = { 116 .bus_type = V4L2_MBUS_PARALLEL 117 }; 118 struct fwnode_handle *fwh; 119 struct device_node *ep; 120 unsigned int num_pads; 121 int err; 122 123 dev_dbg(dev, "Parsing %pOF", np); 124 125 ep = of_graph_get_endpoint_by_regs(np, 0, 0); 126 if (!ep) { 127 err = -EINVAL; 128 dev_err_probe(dev, err, "%pOF: error getting endpoint node\n", np); 129 goto err_node_put; 130 } 131 132 fwh = of_fwnode_handle(ep); 133 err = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep); 134 of_node_put(ep); 135 if (err) { 136 dev_err_probe(dev, err, "%pOF: failed to parse v4l2 endpoint\n", np); 137 goto err_node_put; 138 } 139 140 num_pads = of_graph_get_endpoint_count(np); 141 if (num_pads != TEGRA_VIP_PADS_NUM) { 142 err = -EINVAL; 143 dev_err_probe(dev, err, "%pOF: need 2 pads, got %d\n", np, num_pads); 144 goto err_node_put; 145 } 146 147 vip->chan.of_node = of_node_get(np); 148 vip->chan.pads[TEGRA_VIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 149 vip->chan.pads[TEGRA_VIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 150 151 return 0; 152 153 err_node_put: 154 of_node_put(np); 155 return err; 156 } 157 158 static int tegra_vip_channel_init(struct tegra_vip *vip) 159 { 160 struct v4l2_subdev *subdev; 161 int err; 162 163 subdev = &vip->chan.subdev; 164 v4l2_subdev_init(subdev, &tegra_vip_ops); 165 subdev->dev = vip->dev; 166 snprintf(subdev->name, sizeof(subdev->name), "%s", 167 kbasename(vip->chan.of_node->full_name)); 168 169 v4l2_set_subdevdata(subdev, &vip->chan); 170 subdev->fwnode = of_fwnode_handle(vip->chan.of_node); 171 subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 172 173 err = media_entity_pads_init(&subdev->entity, TEGRA_VIP_PADS_NUM, vip->chan.pads); 174 if (err) 175 return dev_err_probe(vip->dev, err, "failed to initialize media entity\n"); 176 177 err = v4l2_async_register_subdev(subdev); 178 if (err) { 179 dev_err_probe(vip->dev, err, "failed to register subdev\n"); 180 goto err_register_subdev; 181 } 182 183 return 0; 184 185 err_register_subdev: 186 media_entity_cleanup(&subdev->entity); 187 return err; 188 } 189 190 static int tegra_vip_init(struct host1x_client *client) 191 { 192 struct tegra_vip *vip = host1x_client_to_vip(client); 193 int err; 194 195 err = tegra_vip_channel_of_parse(vip); 196 if (err) 197 return err; 198 199 err = tegra_vip_channel_init(vip); 200 if (err) 201 goto err_init; 202 203 return 0; 204 205 err_init: 206 of_node_put(vip->chan.of_node); 207 return err; 208 } 209 210 static int tegra_vip_exit(struct host1x_client *client) 211 { 212 struct tegra_vip *vip = host1x_client_to_vip(client); 213 struct v4l2_subdev *subdev = &vip->chan.subdev; 214 215 v4l2_async_unregister_subdev(subdev); 216 media_entity_cleanup(&subdev->entity); 217 of_node_put(vip->chan.of_node); 218 219 return 0; 220 } 221 222 static const struct host1x_client_ops vip_client_ops = { 223 .init = tegra_vip_init, 224 .exit = tegra_vip_exit, 225 }; 226 227 static int tegra_vip_probe(struct platform_device *pdev) 228 { 229 struct tegra_vip *vip; 230 int err; 231 232 dev_dbg(&pdev->dev, "Probing VIP \"%s\" from %pOF\n", pdev->name, pdev->dev.of_node); 233 234 vip = devm_kzalloc(&pdev->dev, sizeof(*vip), GFP_KERNEL); 235 if (!vip) 236 return -ENOMEM; 237 238 vip->soc = of_device_get_match_data(&pdev->dev); 239 240 vip->dev = &pdev->dev; 241 platform_set_drvdata(pdev, vip); 242 243 /* initialize host1x interface */ 244 INIT_LIST_HEAD(&vip->client.list); 245 vip->client.ops = &vip_client_ops; 246 vip->client.dev = &pdev->dev; 247 248 err = host1x_client_register(&vip->client); 249 if (err) 250 return dev_err_probe(&pdev->dev, err, "failed to register host1x client\n"); 251 252 pm_runtime_enable(&pdev->dev); 253 254 return 0; 255 } 256 257 static void tegra_vip_remove(struct platform_device *pdev) 258 { 259 struct tegra_vip *vip = platform_get_drvdata(pdev); 260 261 host1x_client_unregister(&vip->client); 262 263 pm_runtime_disable(&pdev->dev); 264 } 265 266 #if defined(CONFIG_ARCH_TEGRA_2x_SOC) 267 extern const struct tegra_vip_soc tegra20_vip_soc; 268 #endif 269 270 static const struct of_device_id tegra_vip_of_id_table[] = { 271 #if defined(CONFIG_ARCH_TEGRA_2x_SOC) 272 { .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc }, 273 #endif 274 { } 275 }; 276 MODULE_DEVICE_TABLE(of, tegra_vip_of_id_table); 277 278 struct platform_driver tegra_vip_driver = { 279 .driver = { 280 .name = "tegra-vip", 281 .of_match_table = tegra_vip_of_id_table, 282 }, 283 .probe = tegra_vip_probe, 284 .remove_new = tegra_vip_remove, 285 }; 286