linux/drivers/staging/media/tegra-video/video.c
<<
>>
Prefs
   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_210_SOC)
 127        { .compatible = "nvidia,tegra210-csi", },
 128        { .compatible = "nvidia,tegra210-vi", },
 129#endif
 130        { }
 131};
 132
 133static struct host1x_driver host1x_video_driver = {
 134        .driver = {
 135                .name = "tegra-video",
 136        },
 137        .probe = host1x_video_probe,
 138        .remove = host1x_video_remove,
 139        .subdevs = host1x_video_subdevs,
 140};
 141
 142static struct platform_driver * const drivers[] = {
 143        &tegra_csi_driver,
 144        &tegra_vi_driver,
 145};
 146
 147static int __init host1x_video_init(void)
 148{
 149        int err;
 150
 151        err = host1x_driver_register(&host1x_video_driver);
 152        if (err < 0)
 153                return err;
 154
 155        err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
 156        if (err < 0)
 157                goto unregister_host1x;
 158
 159        return 0;
 160
 161unregister_host1x:
 162        host1x_driver_unregister(&host1x_video_driver);
 163        return err;
 164}
 165module_init(host1x_video_init);
 166
 167static void __exit host1x_video_exit(void)
 168{
 169        platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
 170        host1x_driver_unregister(&host1x_video_driver);
 171}
 172module_exit(host1x_video_exit);
 173
 174MODULE_AUTHOR("Sowjanya Komatineni <skomatineni@nvidia.com>");
 175MODULE_DESCRIPTION("NVIDIA Tegra Host1x Video driver");
 176MODULE_LICENSE("GPL v2");
 177