1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2020 NVIDIA CORPORATION. All rights reserved. 4 */ 5 6#include <linux/host1x.h> 7#include <linux/module.h> 8#include <linux/platform_device.h> 9 10#include <media/v4l2-event.h> 11 12#include "video.h" 13 14static void tegra_v4l2_dev_release(struct v4l2_device *v4l2_dev) 15{ 16 struct tegra_video_device *vid; 17 18 vid = container_of(v4l2_dev, struct tegra_video_device, v4l2_dev); 19 20 /* cleanup channels here as all video device nodes are released */ 21 tegra_channels_cleanup(vid->vi); 22 23 v4l2_device_unregister(v4l2_dev); 24 media_device_unregister(&vid->media_dev); 25 media_device_cleanup(&vid->media_dev); 26 kfree(vid); 27} 28 29static void tegra_v4l2_dev_notify(struct v4l2_subdev *sd, 30 unsigned int notification, void *arg) 31{ 32 struct tegra_vi_channel *chan; 33 const struct v4l2_event *ev = arg; 34 35 if (notification != V4L2_DEVICE_NOTIFY_EVENT) 36 return; 37 38 chan = v4l2_get_subdev_hostdata(sd); 39 v4l2_event_queue(&chan->video, arg); 40 if (ev->type == V4L2_EVENT_SOURCE_CHANGE && vb2_is_streaming(&chan->queue)) 41 vb2_queue_error(&chan->queue); 42} 43 44static int host1x_video_probe(struct host1x_device *dev) 45{ 46 struct tegra_video_device *vid; 47 int ret; 48 49 vid = kzalloc(sizeof(*vid), GFP_KERNEL); 50 if (!vid) 51 return -ENOMEM; 52 53 dev_set_drvdata(&dev->dev, vid); 54 55 vid->media_dev.dev = &dev->dev; 56 strscpy(vid->media_dev.model, "NVIDIA Tegra Video Input Device", 57 sizeof(vid->media_dev.model)); 58 59 media_device_init(&vid->media_dev); 60 ret = media_device_register(&vid->media_dev); 61 if (ret < 0) { 62 dev_err(&dev->dev, 63 "failed to register media device: %d\n", ret); 64 goto cleanup; 65 } 66 67 vid->v4l2_dev.mdev = &vid->media_dev; 68 vid->v4l2_dev.release = tegra_v4l2_dev_release; 69 vid->v4l2_dev.notify = tegra_v4l2_dev_notify; 70 ret = v4l2_device_register(&dev->dev, &vid->v4l2_dev); 71 if (ret < 0) { 72 dev_err(&dev->dev, 73 "V4L2 device registration failed: %d\n", ret); 74 goto unregister_media; 75 } 76 77 ret = host1x_device_init(dev); 78 if (ret < 0) 79 goto unregister_v4l2; 80 81 if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) { 82 /* 83 * Both vi and csi channels are available now. 84 * Register v4l2 nodes and create media links for TPG. 85 */ 86 ret = tegra_v4l2_nodes_setup_tpg(vid); 87 if (ret < 0) { 88 dev_err(&dev->dev, 89 "failed to setup tpg graph: %d\n", ret); 90 goto device_exit; 91 } 92 } 93 94 return 0; 95 96device_exit: 97 host1x_device_exit(dev); 98 /* vi exit ops does not clean channels, so clean them here */ 99 tegra_channels_cleanup(vid->vi); 100unregister_v4l2: 101 v4l2_device_unregister(&vid->v4l2_dev); 102unregister_media: 103 media_device_unregister(&vid->media_dev); 104cleanup: 105 media_device_cleanup(&vid->media_dev); 106 kfree(vid); 107 return ret; 108} 109 110static int host1x_video_remove(struct host1x_device *dev) 111{ 112 struct tegra_video_device *vid = dev_get_drvdata(&dev->dev); 113 114 if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) 115 tegra_v4l2_nodes_cleanup_tpg(vid); 116 117 host1x_device_exit(dev); 118 119 /* This calls v4l2_dev release callback on last reference */ 120 v4l2_device_put(&vid->v4l2_dev); 121 122 return 0; 123} 124 125static const struct of_device_id host1x_video_subdevs[] = { 126#if defined(CONFIG_ARCH_TEGRA_2x_SOC) 127 { .compatible = "nvidia,tegra20-vip", }, 128 { .compatible = "nvidia,tegra20-vi", }, 129#endif 130#if defined(CONFIG_ARCH_TEGRA_210_SOC) 131 { .compatible = "nvidia,tegra210-csi", }, 132 { .compatible = "nvidia,tegra210-vi", }, 133#endif 134 { } 135}; 136 137static struct host1x_driver host1x_video_driver = { 138 .driver = { 139 .name = "tegra-video", 140 }, 141 .probe = host1x_video_probe, 142 .remove = host1x_video_remove, 143 .subdevs = host1x_video_subdevs, 144}; 145 146static struct platform_driver * const drivers[] = { 147 &tegra_csi_driver, 148 &tegra_vip_driver, 149 &tegra_vi_driver, 150}; 151 152static int __init host1x_video_init(void) 153{ 154 int err; 155 156 err = host1x_driver_register(&host1x_video_driver); 157 if (err < 0) 158 return err; 159 160 err = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 161 if (err < 0) 162 goto unregister_host1x; 163 164 return 0; 165 166unregister_host1x: 167 host1x_driver_unregister(&host1x_video_driver); 168 return err; 169} 170module_init(host1x_video_init); 171 172static void __exit host1x_video_exit(void) 173{ 174 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 175 host1x_driver_unregister(&host1x_video_driver); 176} 177module_exit(host1x_video_exit); 178 179MODULE_AUTHOR("Sowjanya Komatineni <skomatineni@nvidia.com>"); 180MODULE_DESCRIPTION("NVIDIA Tegra Host1x Video driver"); 181MODULE_LICENSE("GPL v2"); 182