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