linux/drivers/media/video/sh_mobile_csi2.c
<<
>>
Prefs
   1/*
   2 * Driver for the SH-Mobile MIPI CSI-2 unit
   3 *
   4 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/delay.h>
  12#include <linux/i2c.h>
  13#include <linux/io.h>
  14#include <linux/platform_device.h>
  15#include <linux/pm_runtime.h>
  16#include <linux/slab.h>
  17#include <linux/videodev2.h>
  18
  19#include <media/sh_mobile_csi2.h>
  20#include <media/soc_camera.h>
  21#include <media/v4l2-common.h>
  22#include <media/v4l2-dev.h>
  23#include <media/v4l2-device.h>
  24#include <media/v4l2-mediabus.h>
  25#include <media/v4l2-subdev.h>
  26
  27#define SH_CSI2_TREF    0x00
  28#define SH_CSI2_SRST    0x04
  29#define SH_CSI2_PHYCNT  0x08
  30#define SH_CSI2_CHKSUM  0x0C
  31#define SH_CSI2_VCDT    0x10
  32
  33struct sh_csi2 {
  34        struct v4l2_subdev              subdev;
  35        struct list_head                list;
  36        struct notifier_block           notifier;
  37        unsigned int                    irq;
  38        void __iomem                    *base;
  39        struct platform_device          *pdev;
  40        struct sh_csi2_client_config    *client;
  41        unsigned long (*query_bus_param)(struct soc_camera_device *);
  42        int (*set_bus_param)(struct soc_camera_device *, unsigned long);
  43};
  44
  45static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
  46                           struct v4l2_mbus_framefmt *mf)
  47{
  48        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
  49        struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
  50
  51        if (mf->width > 8188)
  52                mf->width = 8188;
  53        else if (mf->width & 1)
  54                mf->width &= ~1;
  55
  56        switch (pdata->type) {
  57        case SH_CSI2C:
  58                switch (mf->code) {
  59                case V4L2_MBUS_FMT_UYVY8_2X8:           /* YUV422 */
  60                case V4L2_MBUS_FMT_YUYV8_1_5X8:         /* YUV420 */
  61                case V4L2_MBUS_FMT_Y8_1X8:              /* RAW8 */
  62                case V4L2_MBUS_FMT_SBGGR8_1X8:
  63                case V4L2_MBUS_FMT_SGRBG8_1X8:
  64                        break;
  65                default:
  66                        /* All MIPI CSI-2 devices must support one of primary formats */
  67                        mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
  68                }
  69                break;
  70        case SH_CSI2I:
  71                switch (mf->code) {
  72                case V4L2_MBUS_FMT_Y8_1X8:              /* RAW8 */
  73                case V4L2_MBUS_FMT_SBGGR8_1X8:
  74                case V4L2_MBUS_FMT_SGRBG8_1X8:
  75                case V4L2_MBUS_FMT_SBGGR10_1X10:        /* RAW10 */
  76                case V4L2_MBUS_FMT_SBGGR12_1X12:        /* RAW12 */
  77                        break;
  78                default:
  79                        /* All MIPI CSI-2 devices must support one of primary formats */
  80                        mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
  81                }
  82                break;
  83        }
  84
  85        return 0;
  86}
  87
  88/*
  89 * We have done our best in try_fmt to try and tell the sensor, which formats
  90 * we support. If now the configuration is unsuitable for us we can only
  91 * error out.
  92 */
  93static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
  94                         struct v4l2_mbus_framefmt *mf)
  95{
  96        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
  97        u32 tmp = (priv->client->channel & 3) << 8;
  98
  99        dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
 100        if (mf->width > 8188 || mf->width & 1)
 101                return -EINVAL;
 102
 103        switch (mf->code) {
 104        case V4L2_MBUS_FMT_UYVY8_2X8:
 105                tmp |= 0x1e;    /* YUV422 8 bit */
 106                break;
 107        case V4L2_MBUS_FMT_YUYV8_1_5X8:
 108                tmp |= 0x18;    /* YUV420 8 bit */
 109                break;
 110        case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
 111                tmp |= 0x21;    /* RGB555 */
 112                break;
 113        case V4L2_MBUS_FMT_RGB565_2X8_BE:
 114                tmp |= 0x22;    /* RGB565 */
 115                break;
 116        case V4L2_MBUS_FMT_Y8_1X8:
 117        case V4L2_MBUS_FMT_SBGGR8_1X8:
 118        case V4L2_MBUS_FMT_SGRBG8_1X8:
 119                tmp |= 0x2a;    /* RAW8 */
 120                break;
 121        default:
 122                return -EINVAL;
 123        }
 124
 125        iowrite32(tmp, priv->base + SH_CSI2_VCDT);
 126
 127        return 0;
 128}
 129
 130static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
 131        .s_mbus_fmt     = sh_csi2_s_fmt,
 132        .try_mbus_fmt   = sh_csi2_try_fmt,
 133};
 134
 135static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops;
 136
 137static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
 138        .core   = &sh_csi2_subdev_core_ops,
 139        .video  = &sh_csi2_subdev_video_ops,
 140};
 141
 142static void sh_csi2_hwinit(struct sh_csi2 *priv)
 143{
 144        struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
 145        __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */
 146
 147        /* Reflect registers immediately */
 148        iowrite32(0x00000001, priv->base + SH_CSI2_TREF);
 149        /* reset CSI2 harware */
 150        iowrite32(0x00000001, priv->base + SH_CSI2_SRST);
 151        udelay(5);
 152        iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
 153
 154        if (priv->client->lanes & 3)
 155                tmp |= priv->client->lanes & 3;
 156        else
 157                /* Default - both lanes */
 158                tmp |= 3;
 159
 160        if (priv->client->phy == SH_CSI2_PHY_MAIN)
 161                tmp |= 0x8000;
 162
 163        iowrite32(tmp, priv->base + SH_CSI2_PHYCNT);
 164
 165        tmp = 0;
 166        if (pdata->flags & SH_CSI2_ECC)
 167                tmp |= 2;
 168        if (pdata->flags & SH_CSI2_CRC)
 169                tmp |= 1;
 170        iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
 171}
 172
 173static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
 174                                 unsigned long flags)
 175{
 176        return 0;
 177}
 178
 179static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd)
 180{
 181        struct soc_camera_link *icl = to_soc_camera_link(icd);
 182        const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
 183                SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
 184                SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH;
 185
 186        return soc_camera_apply_sensor_flags(icl, flags);
 187}
 188
 189static int sh_csi2_notify(struct notifier_block *nb,
 190                          unsigned long action, void *data)
 191{
 192        struct device *dev = data;
 193        struct soc_camera_device *icd = to_soc_camera_dev(dev);
 194        struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent);
 195        struct sh_csi2 *priv =
 196                container_of(nb, struct sh_csi2, notifier);
 197        struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
 198        int ret, i;
 199
 200        for (i = 0; i < pdata->num_clients; i++)
 201                if (&pdata->clients[i].pdev->dev == icd->pdev)
 202                        break;
 203
 204        dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i);
 205
 206        if (i == pdata->num_clients)
 207                return NOTIFY_DONE;
 208
 209        switch (action) {
 210        case BUS_NOTIFY_BOUND_DRIVER:
 211                snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s",
 212                         dev_name(v4l2_dev->dev), ".mipi-csi");
 213                priv->subdev.grp_id = (long)icd;
 214                ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev);
 215                dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
 216                if (ret < 0)
 217                        return NOTIFY_DONE;
 218
 219                priv->client = pdata->clients + i;
 220
 221                priv->set_bus_param             = icd->ops->set_bus_param;
 222                priv->query_bus_param           = icd->ops->query_bus_param;
 223                icd->ops->set_bus_param         = sh_csi2_set_bus_param;
 224                icd->ops->query_bus_param       = sh_csi2_query_bus_param;
 225
 226                pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev));
 227
 228                sh_csi2_hwinit(priv);
 229                break;
 230        case BUS_NOTIFY_UNBIND_DRIVER:
 231                priv->client = NULL;
 232
 233                /* Driver is about to be unbound */
 234                icd->ops->set_bus_param         = priv->set_bus_param;
 235                icd->ops->query_bus_param       = priv->query_bus_param;
 236                priv->set_bus_param             = NULL;
 237                priv->query_bus_param           = NULL;
 238
 239                v4l2_device_unregister_subdev(&priv->subdev);
 240
 241                pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
 242                break;
 243        }
 244
 245        return NOTIFY_OK;
 246}
 247
 248static __devinit int sh_csi2_probe(struct platform_device *pdev)
 249{
 250        struct resource *res;
 251        unsigned int irq;
 252        int ret;
 253        struct sh_csi2 *priv;
 254        /* Platform data specify the PHY, lanes, ECC, CRC */
 255        struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
 256
 257        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 258        /* Interrupt unused so far */
 259        irq = platform_get_irq(pdev, 0);
 260
 261        if (!res || (int)irq <= 0 || !pdata) {
 262                dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
 263                return -ENODEV;
 264        }
 265
 266        /* TODO: Add support for CSI2I. Careful: different register layout! */
 267        if (pdata->type != SH_CSI2C) {
 268                dev_err(&pdev->dev, "Only CSI2C supported ATM.\n");
 269                return -EINVAL;
 270        }
 271
 272        priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL);
 273        if (!priv)
 274                return -ENOMEM;
 275
 276        priv->irq = irq;
 277        priv->notifier.notifier_call = sh_csi2_notify;
 278
 279        /* We MUST attach after the MIPI sensor */
 280        ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier);
 281        if (ret < 0) {
 282                dev_err(&pdev->dev, "CSI2 cannot register notifier\n");
 283                goto ernotify;
 284        }
 285
 286        if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
 287                dev_err(&pdev->dev, "CSI2 register region already claimed\n");
 288                ret = -EBUSY;
 289                goto ereqreg;
 290        }
 291
 292        priv->base = ioremap(res->start, resource_size(res));
 293        if (!priv->base) {
 294                ret = -ENXIO;
 295                dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n");
 296                goto eremap;
 297        }
 298
 299        priv->pdev = pdev;
 300
 301        v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
 302        v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
 303
 304        platform_set_drvdata(pdev, priv);
 305
 306        pm_runtime_enable(&pdev->dev);
 307
 308        dev_dbg(&pdev->dev, "CSI2 probed.\n");
 309
 310        return 0;
 311
 312eremap:
 313        release_mem_region(res->start, resource_size(res));
 314ereqreg:
 315        bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
 316ernotify:
 317        kfree(priv);
 318
 319        return ret;
 320}
 321
 322static __devexit int sh_csi2_remove(struct platform_device *pdev)
 323{
 324        struct sh_csi2 *priv = platform_get_drvdata(pdev);
 325        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 326
 327        bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
 328        pm_runtime_disable(&pdev->dev);
 329        iounmap(priv->base);
 330        release_mem_region(res->start, resource_size(res));
 331        platform_set_drvdata(pdev, NULL);
 332        kfree(priv);
 333
 334        return 0;
 335}
 336
 337static struct platform_driver __refdata sh_csi2_pdrv = {
 338        .remove  = __devexit_p(sh_csi2_remove),
 339        .driver  = {
 340                .name   = "sh-mobile-csi2",
 341                .owner  = THIS_MODULE,
 342        },
 343};
 344
 345static int __init sh_csi2_init(void)
 346{
 347        return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe);
 348}
 349
 350static void __exit sh_csi2_exit(void)
 351{
 352        platform_driver_unregister(&sh_csi2_pdrv);
 353}
 354
 355module_init(sh_csi2_init);
 356module_exit(sh_csi2_exit);
 357
 358MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver");
 359MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
 360MODULE_LICENSE("GPL v2");
 361MODULE_ALIAS("platform:sh-mobile-csi2");
 362