root/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sun4i_csi_notify_bound
  2. sun4i_csi_notify_complete
  3. sun4i_csi_notifier_init
  4. sun4i_csi_probe
  5. sun4i_csi_remove
  6. sun4i_csi_runtime_resume
  7. sun4i_csi_runtime_suspend

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright (C) 2016 NextThing Co
   4  * Copyright (C) 2016-2019 Bootlin
   5  *
   6  * Author: Maxime Ripard <maxime.ripard@bootlin.com>
   7  */
   8 
   9 #include <linux/clk.h>
  10 #include <linux/interrupt.h>
  11 #include <linux/module.h>
  12 #include <linux/mutex.h>
  13 #include <linux/of.h>
  14 #include <linux/of_device.h>
  15 #include <linux/of_graph.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/pm_runtime.h>
  18 #include <linux/reset.h>
  19 #include <linux/videodev2.h>
  20 
  21 #include <media/v4l2-dev.h>
  22 #include <media/v4l2-device.h>
  23 #include <media/v4l2-fwnode.h>
  24 #include <media/v4l2-ioctl.h>
  25 #include <media/v4l2-mediabus.h>
  26 
  27 #include <media/videobuf2-core.h>
  28 #include <media/videobuf2-dma-contig.h>
  29 
  30 #include "sun4i_csi.h"
  31 
  32 static const struct media_entity_operations sun4i_csi_video_entity_ops = {
  33         .link_validate = v4l2_subdev_link_validate,
  34 };
  35 
  36 static int sun4i_csi_notify_bound(struct v4l2_async_notifier *notifier,
  37                                   struct v4l2_subdev *subdev,
  38                                   struct v4l2_async_subdev *asd)
  39 {
  40         struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi,
  41                                              notifier);
  42 
  43         csi->src_subdev = subdev;
  44         csi->src_pad = media_entity_get_fwnode_pad(&subdev->entity,
  45                                                    subdev->fwnode,
  46                                                    MEDIA_PAD_FL_SOURCE);
  47         if (csi->src_pad < 0) {
  48                 dev_err(csi->dev, "Couldn't find output pad for subdev %s\n",
  49                         subdev->name);
  50                 return csi->src_pad;
  51         }
  52 
  53         dev_dbg(csi->dev, "Bound %s pad: %d\n", subdev->name, csi->src_pad);
  54         return 0;
  55 }
  56 
  57 static int sun4i_csi_notify_complete(struct v4l2_async_notifier *notifier)
  58 {
  59         struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi,
  60                                              notifier);
  61         struct v4l2_subdev *subdev = &csi->subdev;
  62         struct video_device *vdev = &csi->vdev;
  63         int ret;
  64 
  65         ret = v4l2_device_register_subdev(&csi->v4l, subdev);
  66         if (ret < 0)
  67                 return ret;
  68 
  69         ret = sun4i_csi_v4l2_register(csi);
  70         if (ret < 0)
  71                 return ret;
  72 
  73         ret = media_device_register(&csi->mdev);
  74         if (ret)
  75                 return ret;
  76 
  77         /* Create link from subdev to main device */
  78         ret = media_create_pad_link(&subdev->entity, CSI_SUBDEV_SOURCE,
  79                                     &vdev->entity, 0,
  80                                     MEDIA_LNK_FL_ENABLED |
  81                                     MEDIA_LNK_FL_IMMUTABLE);
  82         if (ret)
  83                 goto err_clean_media;
  84 
  85         ret = media_create_pad_link(&csi->src_subdev->entity, csi->src_pad,
  86                                     &subdev->entity, CSI_SUBDEV_SINK,
  87                                     MEDIA_LNK_FL_ENABLED |
  88                                     MEDIA_LNK_FL_IMMUTABLE);
  89         if (ret)
  90                 goto err_clean_media;
  91 
  92         ret = v4l2_device_register_subdev_nodes(&csi->v4l);
  93         if (ret < 0)
  94                 goto err_clean_media;
  95 
  96         return 0;
  97 
  98 err_clean_media:
  99         media_device_unregister(&csi->mdev);
 100 
 101         return ret;
 102 }
 103 
 104 static const struct v4l2_async_notifier_operations sun4i_csi_notify_ops = {
 105         .bound          = sun4i_csi_notify_bound,
 106         .complete       = sun4i_csi_notify_complete,
 107 };
 108 
 109 static int sun4i_csi_notifier_init(struct sun4i_csi *csi)
 110 {
 111         struct v4l2_fwnode_endpoint vep = {
 112                 .bus_type = V4L2_MBUS_PARALLEL,
 113         };
 114         struct fwnode_handle *ep;
 115         int ret;
 116 
 117         v4l2_async_notifier_init(&csi->notifier);
 118 
 119         ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0,
 120                                              FWNODE_GRAPH_ENDPOINT_NEXT);
 121         if (!ep)
 122                 return -EINVAL;
 123 
 124         ret = v4l2_fwnode_endpoint_parse(ep, &vep);
 125         if (ret)
 126                 goto out;
 127 
 128         csi->bus = vep.bus.parallel;
 129 
 130         ret = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier,
 131                                                            ep, &csi->asd);
 132         if (ret)
 133                 goto out;
 134 
 135         csi->notifier.ops = &sun4i_csi_notify_ops;
 136 
 137 out:
 138         fwnode_handle_put(ep);
 139         return ret;
 140 }
 141 
 142 static int sun4i_csi_probe(struct platform_device *pdev)
 143 {
 144         struct v4l2_subdev *subdev;
 145         struct video_device *vdev;
 146         struct sun4i_csi *csi;
 147         struct resource *res;
 148         int ret;
 149         int irq;
 150 
 151         csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
 152         if (!csi)
 153                 return -ENOMEM;
 154         platform_set_drvdata(pdev, csi);
 155         csi->dev = &pdev->dev;
 156         subdev = &csi->subdev;
 157         vdev = &csi->vdev;
 158 
 159         /*
 160          * On Allwinner SoCs, some high memory bandwidth devices do DMA
 161          * directly over the memory bus (called MBUS), instead of the
 162          * system bus. The memory bus has a different addressing scheme
 163          * without the DRAM starting offset.
 164          *
 165          * In some cases this can be described by an interconnect in
 166          * the device tree. In other cases where the hardware is not
 167          * fully understood and the interconnect is left out of the
 168          * device tree, fall back to a default offset.
 169          */
 170         if (of_find_property(csi->dev->of_node, "interconnects", NULL)) {
 171                 ret = of_dma_configure(csi->dev, csi->dev->of_node, true);
 172                 if (ret)
 173                         return ret;
 174         } else {
 175 #ifdef PHYS_PFN_OFFSET
 176                 csi->dev->dma_pfn_offset = PHYS_PFN_OFFSET;
 177 #endif
 178         }
 179 
 180         csi->mdev.dev = csi->dev;
 181         strscpy(csi->mdev.model, "Allwinner Video Capture Device",
 182                 sizeof(csi->mdev.model));
 183         csi->mdev.hw_revision = 0;
 184         media_device_init(&csi->mdev);
 185         csi->v4l.mdev = &csi->mdev;
 186 
 187         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 188         csi->regs = devm_ioremap_resource(&pdev->dev, res);
 189         if (IS_ERR(csi->regs))
 190                 return PTR_ERR(csi->regs);
 191 
 192         irq = platform_get_irq(pdev, 0);
 193         if (irq < 0)
 194                 return irq;
 195 
 196         csi->bus_clk = devm_clk_get(&pdev->dev, "bus");
 197         if (IS_ERR(csi->bus_clk)) {
 198                 dev_err(&pdev->dev, "Couldn't get our bus clock\n");
 199                 return PTR_ERR(csi->bus_clk);
 200         }
 201 
 202         csi->isp_clk = devm_clk_get(&pdev->dev, "isp");
 203         if (IS_ERR(csi->isp_clk)) {
 204                 dev_err(&pdev->dev, "Couldn't get our ISP clock\n");
 205                 return PTR_ERR(csi->isp_clk);
 206         }
 207 
 208         csi->ram_clk = devm_clk_get(&pdev->dev, "ram");
 209         if (IS_ERR(csi->ram_clk)) {
 210                 dev_err(&pdev->dev, "Couldn't get our ram clock\n");
 211                 return PTR_ERR(csi->ram_clk);
 212         }
 213 
 214         csi->rst = devm_reset_control_get(&pdev->dev, NULL);
 215         if (IS_ERR(csi->rst)) {
 216                 dev_err(&pdev->dev, "Couldn't get our reset line\n");
 217                 return PTR_ERR(csi->rst);
 218         }
 219 
 220         /* Initialize subdev */
 221         v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops);
 222         subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
 223         subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
 224         subdev->owner = THIS_MODULE;
 225         snprintf(subdev->name, sizeof(subdev->name), "sun4i-csi-0");
 226         v4l2_set_subdevdata(subdev, csi);
 227 
 228         csi->subdev_pads[CSI_SUBDEV_SINK].flags =
 229                 MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
 230         csi->subdev_pads[CSI_SUBDEV_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 231         ret = media_entity_pads_init(&subdev->entity, CSI_SUBDEV_PADS,
 232                                      csi->subdev_pads);
 233         if (ret < 0)
 234                 return ret;
 235 
 236         csi->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
 237         vdev->entity.ops = &sun4i_csi_video_entity_ops;
 238         ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad);
 239         if (ret < 0)
 240                 return ret;
 241 
 242         ret = sun4i_csi_dma_register(csi, irq);
 243         if (ret)
 244                 goto err_clean_pad;
 245 
 246         ret = sun4i_csi_notifier_init(csi);
 247         if (ret)
 248                 goto err_unregister_media;
 249 
 250         ret = v4l2_async_notifier_register(&csi->v4l, &csi->notifier);
 251         if (ret) {
 252                 dev_err(csi->dev, "Couldn't register our notifier.\n");
 253                 goto err_unregister_media;
 254         }
 255 
 256         pm_runtime_enable(&pdev->dev);
 257 
 258         return 0;
 259 
 260 err_unregister_media:
 261         media_device_unregister(&csi->mdev);
 262         sun4i_csi_dma_unregister(csi);
 263 
 264 err_clean_pad:
 265         media_device_cleanup(&csi->mdev);
 266 
 267         return ret;
 268 }
 269 
 270 static int sun4i_csi_remove(struct platform_device *pdev)
 271 {
 272         struct sun4i_csi *csi = platform_get_drvdata(pdev);
 273 
 274         v4l2_async_notifier_unregister(&csi->notifier);
 275         v4l2_async_notifier_cleanup(&csi->notifier);
 276         media_device_unregister(&csi->mdev);
 277         sun4i_csi_dma_unregister(csi);
 278         media_device_cleanup(&csi->mdev);
 279 
 280         return 0;
 281 }
 282 
 283 static const struct of_device_id sun4i_csi_of_match[] = {
 284         { .compatible = "allwinner,sun7i-a20-csi0" },
 285         { /* Sentinel */ }
 286 };
 287 MODULE_DEVICE_TABLE(of, sun4i_csi_of_match);
 288 
 289 static int __maybe_unused sun4i_csi_runtime_resume(struct device *dev)
 290 {
 291         struct sun4i_csi *csi = dev_get_drvdata(dev);
 292 
 293         reset_control_deassert(csi->rst);
 294         clk_prepare_enable(csi->bus_clk);
 295         clk_prepare_enable(csi->ram_clk);
 296         clk_set_rate(csi->isp_clk, 80000000);
 297         clk_prepare_enable(csi->isp_clk);
 298 
 299         writel(1, csi->regs + CSI_EN_REG);
 300 
 301         return 0;
 302 }
 303 
 304 static int __maybe_unused sun4i_csi_runtime_suspend(struct device *dev)
 305 {
 306         struct sun4i_csi *csi = dev_get_drvdata(dev);
 307 
 308         clk_disable_unprepare(csi->isp_clk);
 309         clk_disable_unprepare(csi->ram_clk);
 310         clk_disable_unprepare(csi->bus_clk);
 311 
 312         reset_control_assert(csi->rst);
 313 
 314         return 0;
 315 }
 316 
 317 static const struct dev_pm_ops sun4i_csi_pm_ops = {
 318         SET_RUNTIME_PM_OPS(sun4i_csi_runtime_suspend,
 319                            sun4i_csi_runtime_resume,
 320                            NULL)
 321 };
 322 
 323 static struct platform_driver sun4i_csi_driver = {
 324         .probe  = sun4i_csi_probe,
 325         .remove = sun4i_csi_remove,
 326         .driver = {
 327                 .name           = "sun4i-csi",
 328                 .of_match_table = sun4i_csi_of_match,
 329                 .pm             = &sun4i_csi_pm_ops,
 330         },
 331 };
 332 module_platform_driver(sun4i_csi_driver);
 333 
 334 MODULE_DESCRIPTION("Allwinner A10 Camera Sensor Interface driver");
 335 MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
 336 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */