linux/drivers/media/platform/soc_camera/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/err.h>
  13#include <linux/i2c.h>
  14#include <linux/io.h>
  15#include <linux/platform_device.h>
  16#include <linux/pm_runtime.h>
  17#include <linux/slab.h>
  18#include <linux/videodev2.h>
  19#include <linux/module.h>
  20
  21#include <media/sh_mobile_ceu.h>
  22#include <media/sh_mobile_csi2.h>
  23#include <media/soc_camera.h>
  24#include <media/soc_mediabus.h>
  25#include <media/v4l2-common.h>
  26#include <media/v4l2-dev.h>
  27#include <media/v4l2-device.h>
  28#include <media/v4l2-mediabus.h>
  29#include <media/v4l2-subdev.h>
  30
  31#define SH_CSI2_TREF    0x00
  32#define SH_CSI2_SRST    0x04
  33#define SH_CSI2_PHYCNT  0x08
  34#define SH_CSI2_CHKSUM  0x0C
  35#define SH_CSI2_VCDT    0x10
  36
  37struct sh_csi2 {
  38        struct v4l2_subdev              subdev;
  39        unsigned int                    irq;
  40        unsigned long                   mipi_flags;
  41        void __iomem                    *base;
  42        struct platform_device          *pdev;
  43        struct sh_csi2_client_config    *client;
  44};
  45
  46static void sh_csi2_hwinit(struct sh_csi2 *priv);
  47
  48static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
  49                           struct v4l2_mbus_framefmt *mf)
  50{
  51        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
  52        struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
  53
  54        if (mf->width > 8188)
  55                mf->width = 8188;
  56        else if (mf->width & 1)
  57                mf->width &= ~1;
  58
  59        switch (pdata->type) {
  60        case SH_CSI2C:
  61                switch (mf->code) {
  62                case MEDIA_BUS_FMT_UYVY8_2X8:           /* YUV422 */
  63                case MEDIA_BUS_FMT_YUYV8_1_5X8:         /* YUV420 */
  64                case MEDIA_BUS_FMT_Y8_1X8:              /* RAW8 */
  65                case MEDIA_BUS_FMT_SBGGR8_1X8:
  66                case MEDIA_BUS_FMT_SGRBG8_1X8:
  67                        break;
  68                default:
  69                        /* All MIPI CSI-2 devices must support one of primary formats */
  70                        mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
  71                }
  72                break;
  73        case SH_CSI2I:
  74                switch (mf->code) {
  75                case MEDIA_BUS_FMT_Y8_1X8:              /* RAW8 */
  76                case MEDIA_BUS_FMT_SBGGR8_1X8:
  77                case MEDIA_BUS_FMT_SGRBG8_1X8:
  78                case MEDIA_BUS_FMT_SBGGR10_1X10:        /* RAW10 */
  79                case MEDIA_BUS_FMT_SBGGR12_1X12:        /* RAW12 */
  80                        break;
  81                default:
  82                        /* All MIPI CSI-2 devices must support one of primary formats */
  83                        mf->code = MEDIA_BUS_FMT_SBGGR8_1X8;
  84                }
  85                break;
  86        }
  87
  88        return 0;
  89}
  90
  91/*
  92 * We have done our best in try_fmt to try and tell the sensor, which formats
  93 * we support. If now the configuration is unsuitable for us we can only
  94 * error out.
  95 */
  96static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
  97                         struct v4l2_mbus_framefmt *mf)
  98{
  99        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
 100        u32 tmp = (priv->client->channel & 3) << 8;
 101
 102        dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
 103        if (mf->width > 8188 || mf->width & 1)
 104                return -EINVAL;
 105
 106        switch (mf->code) {
 107        case MEDIA_BUS_FMT_UYVY8_2X8:
 108                tmp |= 0x1e;    /* YUV422 8 bit */
 109                break;
 110        case MEDIA_BUS_FMT_YUYV8_1_5X8:
 111                tmp |= 0x18;    /* YUV420 8 bit */
 112                break;
 113        case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
 114                tmp |= 0x21;    /* RGB555 */
 115                break;
 116        case MEDIA_BUS_FMT_RGB565_2X8_BE:
 117                tmp |= 0x22;    /* RGB565 */
 118                break;
 119        case MEDIA_BUS_FMT_Y8_1X8:
 120        case MEDIA_BUS_FMT_SBGGR8_1X8:
 121        case MEDIA_BUS_FMT_SGRBG8_1X8:
 122                tmp |= 0x2a;    /* RAW8 */
 123                break;
 124        default:
 125                return -EINVAL;
 126        }
 127
 128        iowrite32(tmp, priv->base + SH_CSI2_VCDT);
 129
 130        return 0;
 131}
 132
 133static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
 134                                 struct v4l2_mbus_config *cfg)
 135{
 136        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
 137
 138        if (!priv->mipi_flags) {
 139                struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
 140                struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
 141                struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
 142                unsigned long common_flags, csi2_flags;
 143                struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
 144                int ret;
 145
 146                /* Check if we can support this camera */
 147                csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
 148                        V4L2_MBUS_CSI2_1_LANE;
 149
 150                switch (pdata->type) {
 151                case SH_CSI2C:
 152                        if (priv->client->lanes != 1)
 153                                csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
 154                        break;
 155                case SH_CSI2I:
 156                        switch (priv->client->lanes) {
 157                        default:
 158                                csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
 159                        case 3:
 160                                csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
 161                        case 2:
 162                                csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
 163                        }
 164                }
 165
 166                ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg);
 167                if (ret == -ENOIOCTLCMD)
 168                        common_flags = csi2_flags;
 169                else if (!ret)
 170                        common_flags = soc_mbus_config_compatible(&client_cfg,
 171                                                                  csi2_flags);
 172                else
 173                        common_flags = 0;
 174
 175                if (!common_flags)
 176                        return -EINVAL;
 177
 178                /* All good: camera MIPI configuration supported */
 179                priv->mipi_flags = common_flags;
 180        }
 181
 182        if (cfg) {
 183                cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
 184                        V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
 185                        V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
 186                cfg->type = V4L2_MBUS_PARALLEL;
 187        }
 188
 189        return 0;
 190}
 191
 192static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
 193                                 const struct v4l2_mbus_config *cfg)
 194{
 195        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
 196        struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
 197        struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
 198        struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
 199        int ret = sh_csi2_g_mbus_config(sd, NULL);
 200
 201        if (ret < 0)
 202                return ret;
 203
 204        pm_runtime_get_sync(&priv->pdev->dev);
 205
 206        sh_csi2_hwinit(priv);
 207
 208        client_cfg.flags = priv->mipi_flags;
 209
 210        return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
 211}
 212
 213static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
 214        .s_mbus_fmt     = sh_csi2_s_fmt,
 215        .try_mbus_fmt   = sh_csi2_try_fmt,
 216        .g_mbus_config  = sh_csi2_g_mbus_config,
 217        .s_mbus_config  = sh_csi2_s_mbus_config,
 218};
 219
 220static void sh_csi2_hwinit(struct sh_csi2 *priv)
 221{
 222        struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
 223        __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */
 224
 225        /* Reflect registers immediately */
 226        iowrite32(0x00000001, priv->base + SH_CSI2_TREF);
 227        /* reset CSI2 harware */
 228        iowrite32(0x00000001, priv->base + SH_CSI2_SRST);
 229        udelay(5);
 230        iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
 231
 232        switch (pdata->type) {
 233        case SH_CSI2C:
 234                if (priv->client->lanes == 1)
 235                        tmp |= 1;
 236                else
 237                        /* Default - both lanes */
 238                        tmp |= 3;
 239                break;
 240        case SH_CSI2I:
 241                if (!priv->client->lanes || priv->client->lanes > 4)
 242                        /* Default - all 4 lanes */
 243                        tmp |= 0xf;
 244                else
 245                        tmp |= (1 << priv->client->lanes) - 1;
 246        }
 247
 248        if (priv->client->phy == SH_CSI2_PHY_MAIN)
 249                tmp |= 0x8000;
 250
 251        iowrite32(tmp, priv->base + SH_CSI2_PHYCNT);
 252
 253        tmp = 0;
 254        if (pdata->flags & SH_CSI2_ECC)
 255                tmp |= 2;
 256        if (pdata->flags & SH_CSI2_CRC)
 257                tmp |= 1;
 258        iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
 259}
 260
 261static int sh_csi2_client_connect(struct sh_csi2 *priv)
 262{
 263        struct device *dev = v4l2_get_subdevdata(&priv->subdev);
 264        struct sh_csi2_pdata *pdata = dev->platform_data;
 265        struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
 266        int i;
 267
 268        if (priv->client)
 269                return -EBUSY;
 270
 271        for (i = 0; i < pdata->num_clients; i++)
 272                if ((pdata->clients[i].pdev &&
 273                     &pdata->clients[i].pdev->dev == icd->pdev) ||
 274                    (icd->control &&
 275                     strcmp(pdata->clients[i].name, dev_name(icd->control))))
 276                        break;
 277
 278        dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
 279
 280        if (i == pdata->num_clients)
 281                return -ENODEV;
 282
 283        priv->client = pdata->clients + i;
 284
 285        return 0;
 286}
 287
 288static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
 289{
 290        if (!priv->client)
 291                return;
 292
 293        priv->client = NULL;
 294
 295        pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
 296}
 297
 298static int sh_csi2_s_power(struct v4l2_subdev *sd, int on)
 299{
 300        struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
 301
 302        if (on)
 303                return sh_csi2_client_connect(priv);
 304
 305        sh_csi2_client_disconnect(priv);
 306        return 0;
 307}
 308
 309static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = {
 310        .s_power        = sh_csi2_s_power,
 311};
 312
 313static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
 314        .core   = &sh_csi2_subdev_core_ops,
 315        .video  = &sh_csi2_subdev_video_ops,
 316};
 317
 318static int sh_csi2_probe(struct platform_device *pdev)
 319{
 320        struct resource *res;
 321        unsigned int irq;
 322        int ret;
 323        struct sh_csi2 *priv;
 324        /* Platform data specify the PHY, lanes, ECC, CRC */
 325        struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
 326
 327        if (!pdata)
 328                return -EINVAL;
 329
 330        priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
 331        if (!priv)
 332                return -ENOMEM;
 333
 334        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 335        /* Interrupt unused so far */
 336        irq = platform_get_irq(pdev, 0);
 337
 338        if (!res || (int)irq <= 0) {
 339                dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
 340                return -ENODEV;
 341        }
 342
 343        /* TODO: Add support for CSI2I. Careful: different register layout! */
 344        if (pdata->type != SH_CSI2C) {
 345                dev_err(&pdev->dev, "Only CSI2C supported ATM.\n");
 346                return -EINVAL;
 347        }
 348
 349        priv->irq = irq;
 350
 351        priv->base = devm_ioremap_resource(&pdev->dev, res);
 352        if (IS_ERR(priv->base))
 353                return PTR_ERR(priv->base);
 354
 355        priv->pdev = pdev;
 356        priv->subdev.owner = THIS_MODULE;
 357        priv->subdev.dev = &pdev->dev;
 358        platform_set_drvdata(pdev, &priv->subdev);
 359
 360        v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
 361        v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
 362
 363        snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
 364                 dev_name(&pdev->dev));
 365
 366        ret = v4l2_async_register_subdev(&priv->subdev);
 367        if (ret < 0)
 368                return ret;
 369
 370        pm_runtime_enable(&pdev->dev);
 371
 372        dev_dbg(&pdev->dev, "CSI2 probed.\n");
 373
 374        return 0;
 375}
 376
 377static int sh_csi2_remove(struct platform_device *pdev)
 378{
 379        struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
 380        struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev);
 381
 382        v4l2_async_unregister_subdev(&priv->subdev);
 383        pm_runtime_disable(&pdev->dev);
 384
 385        return 0;
 386}
 387
 388static struct platform_driver __refdata sh_csi2_pdrv = {
 389        .remove = sh_csi2_remove,
 390        .probe  = sh_csi2_probe,
 391        .driver = {
 392                .name   = "sh-mobile-csi2",
 393        },
 394};
 395
 396module_platform_driver(sh_csi2_pdrv);
 397
 398MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver");
 399MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
 400MODULE_LICENSE("GPL v2");
 401MODULE_ALIAS("platform:sh-mobile-csi2");
 402