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 24static inline struct tegra_vip *host1x_client_to_vip(struct host1x_client *client) 25{ 26 return container_of(client, struct tegra_vip, client); 27} 28 29static 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 34static 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) */ 40static 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 51static 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 72err_prev_subdev_start_stream: 73err_start_streaming: 74 pm_runtime_put(vip->dev); 75 return err; 76} 77 78static 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 91static 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 103static const struct v4l2_subdev_video_ops tegra_vip_video_ops = { 104 .s_stream = tegra_vip_s_stream, 105}; 106 107static const struct v4l2_subdev_ops tegra_vip_ops = { 108 .video = &tegra_vip_video_ops, 109}; 110 111static 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 153err_node_put: 154 of_node_put(np); 155 return err; 156} 157 158static 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 185err_register_subdev: 186 media_entity_cleanup(&subdev->entity); 187 return err; 188} 189 190static 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 205err_init: 206 of_node_put(vip->chan.of_node); 207 return err; 208} 209 210static 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 222static const struct host1x_client_ops vip_client_ops = { 223 .init = tegra_vip_init, 224 .exit = tegra_vip_exit, 225}; 226 227static 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 257static 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) 267extern const struct tegra_vip_soc tegra20_vip_soc; 268#endif 269 270static 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}; 276MODULE_DEVICE_TABLE(of, tegra_vip_of_id_table); 277 278struct 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