Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[pandora-kernel.git] / drivers / media / video / sh_mobile_csi2.c
index 426c8ad..8a652b5 100644 (file)
@@ -20,6 +20,7 @@
 #include <media/sh_mobile_ceu.h>
 #include <media/sh_mobile_csi2.h>
 #include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
@@ -36,11 +37,10 @@ struct sh_csi2 {
        struct v4l2_subdev              subdev;
        struct list_head                list;
        unsigned int                    irq;
+       unsigned long                   mipi_flags;
        void __iomem                    *base;
        struct platform_device          *pdev;
        struct sh_csi2_client_config    *client;
-       unsigned long (*query_bus_param)(struct soc_camera_device *);
-       int (*set_bus_param)(struct soc_camera_device *, unsigned long);
 };
 
 static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
@@ -128,9 +128,34 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
        return 0;
 }
 
+static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
+                                struct v4l2_mbus_config *cfg)
+{
+       cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+               V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+       cfg->type = V4L2_MBUS_PARALLEL;
+
+       return 0;
+}
+
+static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
+                                const struct v4l2_mbus_config *cfg)
+{
+       struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+       struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+       struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+       struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
+                                             .flags = priv->mipi_flags};
+
+       return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
+}
+
 static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
        .s_mbus_fmt     = sh_csi2_s_fmt,
        .try_mbus_fmt   = sh_csi2_try_fmt,
+       .g_mbus_config  = sh_csi2_g_mbus_config,
+       .s_mbus_config  = sh_csi2_s_mbus_config,
 };
 
 static void sh_csi2_hwinit(struct sh_csi2 *priv)
@@ -145,11 +170,21 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
        udelay(5);
        iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
 
-       if (priv->client->lanes & 3)
-               tmp |= priv->client->lanes & 3;
-       else
-               /* Default - both lanes */
-               tmp |= 3;
+       switch (pdata->type) {
+       case SH_CSI2C:
+               if (priv->client->lanes == 1)
+                       tmp |= 1;
+               else
+                       /* Default - both lanes */
+                       tmp |= 3;
+               break;
+       case SH_CSI2I:
+               if (!priv->client->lanes || priv->client->lanes > 4)
+                       /* Default - all 4 lanes */
+                       tmp |= 0xf;
+               else
+                       tmp |= (1 << priv->client->lanes) - 1;
+       }
 
        if (priv->client->phy == SH_CSI2_PHY_MAIN)
                tmp |= 0x8000;
@@ -164,38 +199,18 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
        iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
 }
 
-static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
-                                unsigned long flags)
-{
-       return 0;
-}
-
-static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd)
-{
-       struct soc_camera_link *icl = to_soc_camera_link(icd);
-       const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
-               SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-               SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH;
-
-       return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 static int sh_csi2_client_connect(struct sh_csi2 *priv)
 {
        struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
-       struct v4l2_subdev *sd, *csi2_sd = &priv->subdev;
-       struct soc_camera_device *icd = NULL;
+       struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
+       struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
        struct device *dev = v4l2_get_subdevdata(&priv->subdev);
-       int i;
+       struct v4l2_mbus_config cfg;
+       unsigned long common_flags, csi2_flags;
+       int i, ret;
 
-       v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev)
-               if (sd->grp_id) {
-                       icd = (struct soc_camera_device *)sd->grp_id;
-                       break;
-               }
-
-       if (!icd)
-               return -EINVAL;
+       if (priv->client)
+               return -EBUSY;
 
        for (i = 0; i < pdata->num_clients; i++)
                if (&pdata->clients[i].pdev->dev == icd->pdev)
@@ -206,14 +221,41 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
        if (i == pdata->num_clients)
                return -ENODEV;
 
-       priv->client = pdata->clients + i;
+       /* Check if we can support this camera */
+       csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
 
-       priv->set_bus_param             = icd->ops->set_bus_param;
-       priv->query_bus_param           = icd->ops->query_bus_param;
-       icd->ops->set_bus_param         = sh_csi2_set_bus_param;
-       icd->ops->query_bus_param       = sh_csi2_query_bus_param;
+       switch (pdata->type) {
+       case SH_CSI2C:
+               if (pdata->clients[i].lanes != 1)
+                       csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+               break;
+       case SH_CSI2I:
+               switch (pdata->clients[i].lanes) {
+               default:
+                       csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+               case 3:
+                       csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+               case 2:
+                       csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+               }
+       }
+
+       cfg.type = V4L2_MBUS_CSI2;
+       ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
+       if (ret == -ENOIOCTLCMD)
+               common_flags = csi2_flags;
+       else if (!ret)
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         csi2_flags);
+       else
+               common_flags = 0;
 
-       csi2_sd->grp_id = (long)icd;
+       if (!common_flags)
+               return -EINVAL;
+
+       /* All good: camera MIPI configuration supported */
+       priv->mipi_flags = common_flags;
+       priv->client = pdata->clients + i;
 
        pm_runtime_get_sync(dev);
 
@@ -224,16 +266,10 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
 
 static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
 {
-       struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+       if (!priv->client)
+               return;
 
        priv->client = NULL;
-       priv->subdev.grp_id = 0;
-
-       /* Driver is about to be unbound */
-       icd->ops->set_bus_param         = priv->set_bus_param;
-       icd->ops->query_bus_param       = priv->query_bus_param;
-       priv->set_bus_param             = NULL;
-       priv->query_bus_param           = NULL;
 
        pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
 }