linux/drivers/media/platform/atmel/atmel-sama5d2-isc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Microchip Image Sensor Controller (ISC) driver
   4 *
   5 * Copyright (C) 2016-2019 Microchip Technology, Inc.
   6 *
   7 * Author: Songjun Wu
   8 * Author: Eugen Hristev <eugen.hristev@microchip.com>
   9 *
  10 *
  11 * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA
  12 *
  13 * ISC video pipeline integrates the following submodules:
  14 * PFE: Parallel Front End to sample the camera sensor input stream
  15 *  WB: Programmable white balance in the Bayer domain
  16 * CFA: Color filter array interpolation module
  17 *  CC: Programmable color correction
  18 * GAM: Gamma correction
  19 * CSC: Programmable color space conversion
  20 * CBC: Contrast and Brightness control
  21 * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
  22 * RLP: This module performs rounding, range limiting
  23 *      and packing of the incoming data
  24 */
  25
  26#include <linux/clk.h>
  27#include <linux/clkdev.h>
  28#include <linux/clk-provider.h>
  29#include <linux/delay.h>
  30#include <linux/interrupt.h>
  31#include <linux/math64.h>
  32#include <linux/module.h>
  33#include <linux/of.h>
  34#include <linux/of_graph.h>
  35#include <linux/platform_device.h>
  36#include <linux/pm_runtime.h>
  37#include <linux/regmap.h>
  38#include <linux/videodev2.h>
  39
  40#include <media/v4l2-ctrls.h>
  41#include <media/v4l2-device.h>
  42#include <media/v4l2-event.h>
  43#include <media/v4l2-image-sizes.h>
  44#include <media/v4l2-ioctl.h>
  45#include <media/v4l2-fwnode.h>
  46#include <media/v4l2-subdev.h>
  47#include <media/videobuf2-dma-contig.h>
  48
  49#include "atmel-isc-regs.h"
  50#include "atmel-isc.h"
  51
  52#define ISC_MAX_SUPPORT_WIDTH   2592
  53#define ISC_MAX_SUPPORT_HEIGHT  1944
  54
  55#define ISC_CLK_MAX_DIV         255
  56
  57static int isc_parse_dt(struct device *dev, struct isc_device *isc)
  58{
  59        struct device_node *np = dev->of_node;
  60        struct device_node *epn = NULL, *rem;
  61        struct isc_subdev_entity *subdev_entity;
  62        unsigned int flags;
  63        int ret;
  64
  65        INIT_LIST_HEAD(&isc->subdev_entities);
  66
  67        while (1) {
  68                struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
  69
  70                epn = of_graph_get_next_endpoint(np, epn);
  71                if (!epn)
  72                        return 0;
  73
  74                rem = of_graph_get_remote_port_parent(epn);
  75                if (!rem) {
  76                        dev_notice(dev, "Remote device at %pOF not found\n",
  77                                   epn);
  78                        continue;
  79                }
  80
  81                ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
  82                                                 &v4l2_epn);
  83                if (ret) {
  84                        of_node_put(rem);
  85                        ret = -EINVAL;
  86                        dev_err(dev, "Could not parse the endpoint\n");
  87                        break;
  88                }
  89
  90                subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
  91                                             GFP_KERNEL);
  92                if (!subdev_entity) {
  93                        of_node_put(rem);
  94                        ret = -ENOMEM;
  95                        break;
  96                }
  97
  98                /* asd will be freed by the subsystem once it's added to the
  99                 * notifier list
 100                 */
 101                subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd),
 102                                             GFP_KERNEL);
 103                if (!subdev_entity->asd) {
 104                        of_node_put(rem);
 105                        ret = -ENOMEM;
 106                        break;
 107                }
 108
 109                flags = v4l2_epn.bus.parallel.flags;
 110
 111                if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
 112                        subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
 113
 114                if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
 115                        subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
 116
 117                if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
 118                        subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
 119
 120                if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
 121                        subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
 122                                        ISC_PFE_CFG0_CCIR656;
 123
 124                subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
 125                subdev_entity->asd->match.fwnode = of_fwnode_handle(rem);
 126                list_add_tail(&subdev_entity->list, &isc->subdev_entities);
 127        }
 128
 129        of_node_put(epn);
 130        return ret;
 131}
 132
 133static int atmel_isc_probe(struct platform_device *pdev)
 134{
 135        struct device *dev = &pdev->dev;
 136        struct isc_device *isc;
 137        struct resource *res;
 138        void __iomem *io_base;
 139        struct isc_subdev_entity *subdev_entity;
 140        int irq;
 141        int ret;
 142
 143        isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
 144        if (!isc)
 145                return -ENOMEM;
 146
 147        platform_set_drvdata(pdev, isc);
 148        isc->dev = dev;
 149
 150        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 151        io_base = devm_ioremap_resource(dev, res);
 152        if (IS_ERR(io_base))
 153                return PTR_ERR(io_base);
 154
 155        isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config);
 156        if (IS_ERR(isc->regmap)) {
 157                ret = PTR_ERR(isc->regmap);
 158                dev_err(dev, "failed to init register map: %d\n", ret);
 159                return ret;
 160        }
 161
 162        irq = platform_get_irq(pdev, 0);
 163        if (irq < 0)
 164                return irq;
 165
 166        ret = devm_request_irq(dev, irq, isc_interrupt, 0,
 167                               ATMEL_ISC_NAME, isc);
 168        if (ret < 0) {
 169                dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
 170                        irq, ret);
 171                return ret;
 172        }
 173
 174        ret = isc_pipeline_init(isc);
 175        if (ret)
 176                return ret;
 177
 178        isc->hclock = devm_clk_get(dev, "hclock");
 179        if (IS_ERR(isc->hclock)) {
 180                ret = PTR_ERR(isc->hclock);
 181                dev_err(dev, "failed to get hclock: %d\n", ret);
 182                return ret;
 183        }
 184
 185        ret = clk_prepare_enable(isc->hclock);
 186        if (ret) {
 187                dev_err(dev, "failed to enable hclock: %d\n", ret);
 188                return ret;
 189        }
 190
 191        ret = isc_clk_init(isc);
 192        if (ret) {
 193                dev_err(dev, "failed to init isc clock: %d\n", ret);
 194                goto unprepare_hclk;
 195        }
 196
 197        isc->ispck = isc->isc_clks[ISC_ISPCK].clk;
 198
 199        ret = clk_prepare_enable(isc->ispck);
 200        if (ret) {
 201                dev_err(dev, "failed to enable ispck: %d\n", ret);
 202                goto unprepare_hclk;
 203        }
 204
 205        /* ispck should be greater or equal to hclock */
 206        ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock));
 207        if (ret) {
 208                dev_err(dev, "failed to set ispck rate: %d\n", ret);
 209                goto unprepare_clk;
 210        }
 211
 212        ret = v4l2_device_register(dev, &isc->v4l2_dev);
 213        if (ret) {
 214                dev_err(dev, "unable to register v4l2 device.\n");
 215                goto unprepare_clk;
 216        }
 217
 218        ret = isc_parse_dt(dev, isc);
 219        if (ret) {
 220                dev_err(dev, "fail to parse device tree\n");
 221                goto unregister_v4l2_device;
 222        }
 223
 224        if (list_empty(&isc->subdev_entities)) {
 225                dev_err(dev, "no subdev found\n");
 226                ret = -ENODEV;
 227                goto unregister_v4l2_device;
 228        }
 229
 230        list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
 231                v4l2_async_notifier_init(&subdev_entity->notifier);
 232
 233                ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier,
 234                                                     subdev_entity->asd);
 235                if (ret) {
 236                        fwnode_handle_put(subdev_entity->asd->match.fwnode);
 237                        kfree(subdev_entity->asd);
 238                        goto cleanup_subdev;
 239                }
 240
 241                subdev_entity->notifier.ops = &isc_async_ops;
 242
 243                ret = v4l2_async_notifier_register(&isc->v4l2_dev,
 244                                                   &subdev_entity->notifier);
 245                if (ret) {
 246                        dev_err(dev, "fail to register async notifier\n");
 247                        goto cleanup_subdev;
 248                }
 249
 250                if (video_is_registered(&isc->video_dev))
 251                        break;
 252        }
 253
 254        pm_runtime_set_active(dev);
 255        pm_runtime_enable(dev);
 256        pm_request_idle(dev);
 257
 258        return 0;
 259
 260cleanup_subdev:
 261        isc_subdev_cleanup(isc);
 262
 263unregister_v4l2_device:
 264        v4l2_device_unregister(&isc->v4l2_dev);
 265
 266unprepare_clk:
 267        clk_disable_unprepare(isc->ispck);
 268unprepare_hclk:
 269        clk_disable_unprepare(isc->hclock);
 270
 271        isc_clk_cleanup(isc);
 272
 273        return ret;
 274}
 275
 276static int atmel_isc_remove(struct platform_device *pdev)
 277{
 278        struct isc_device *isc = platform_get_drvdata(pdev);
 279
 280        pm_runtime_disable(&pdev->dev);
 281
 282        isc_subdev_cleanup(isc);
 283
 284        v4l2_device_unregister(&isc->v4l2_dev);
 285
 286        clk_disable_unprepare(isc->ispck);
 287        clk_disable_unprepare(isc->hclock);
 288
 289        isc_clk_cleanup(isc);
 290
 291        return 0;
 292}
 293
 294static int __maybe_unused isc_runtime_suspend(struct device *dev)
 295{
 296        struct isc_device *isc = dev_get_drvdata(dev);
 297
 298        clk_disable_unprepare(isc->ispck);
 299        clk_disable_unprepare(isc->hclock);
 300
 301        return 0;
 302}
 303
 304static int __maybe_unused isc_runtime_resume(struct device *dev)
 305{
 306        struct isc_device *isc = dev_get_drvdata(dev);
 307        int ret;
 308
 309        ret = clk_prepare_enable(isc->hclock);
 310        if (ret)
 311                return ret;
 312
 313        ret = clk_prepare_enable(isc->ispck);
 314        if (ret)
 315                clk_disable_unprepare(isc->hclock);
 316
 317        return ret;
 318}
 319
 320static const struct dev_pm_ops atmel_isc_dev_pm_ops = {
 321        SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
 322};
 323
 324#if IS_ENABLED(CONFIG_OF)
 325static const struct of_device_id atmel_isc_of_match[] = {
 326        { .compatible = "atmel,sama5d2-isc" },
 327        { }
 328};
 329MODULE_DEVICE_TABLE(of, atmel_isc_of_match);
 330#endif
 331
 332static struct platform_driver atmel_isc_driver = {
 333        .probe  = atmel_isc_probe,
 334        .remove = atmel_isc_remove,
 335        .driver = {
 336                .name           = ATMEL_ISC_NAME,
 337                .pm             = &atmel_isc_dev_pm_ops,
 338                .of_match_table = of_match_ptr(atmel_isc_of_match),
 339        },
 340};
 341
 342module_platform_driver(atmel_isc_driver);
 343
 344MODULE_AUTHOR("Songjun Wu");
 345MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC");
 346MODULE_LICENSE("GPL v2");
 347MODULE_SUPPORTED_DEVICE("video");
 348