Merge branch 'v4l_for_2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/mcheha...
[safe/jmp/linux-2.6] / drivers / media / video / mt9m001.c
index e81b324..79f096d 100644 (file)
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/log2.h>
-#include <linux/gpio.h>
 
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/soc_camera.h>
 
-/* mt9m001 i2c address 0x5d
- * The platform has to define i2c_board_info
- * and call i2c_register_board_info() */
+/*
+ * mt9m001 i2c address 0x5d
+ * The platform has to define ctruct i2c_board_info objects and link to them
+ * from struct soc_camera_link
+ */
 
 /* mt9m001 selected register addresses */
 #define MT9M001_CHIP_VERSION           0x00
 #define MT9M001_GLOBAL_GAIN            0x35
 #define MT9M001_CHIP_ENABLE            0xF1
 
-static const struct soc_camera_data_format mt9m001_colour_formats[] = {
-       /* Order important: first natively supported,
-        * second supported with a GPIO extender */
-       {
-               .name           = "Bayer (sRGB) 10 bit",
-               .depth          = 10,
-               .fourcc         = V4L2_PIX_FMT_SBGGR16,
-               .colorspace     = V4L2_COLORSPACE_SRGB,
-       }, {
-               .name           = "Bayer (sRGB) 8 bit",
-               .depth          = 8,
-               .fourcc         = V4L2_PIX_FMT_SBGGR8,
-               .colorspace     = V4L2_COLORSPACE_SRGB,
-       }
+#define MT9M001_MAX_WIDTH              1280
+#define MT9M001_MAX_HEIGHT             1024
+#define MT9M001_MIN_WIDTH              48
+#define MT9M001_MIN_HEIGHT             32
+#define MT9M001_COLUMN_SKIP            20
+#define MT9M001_ROW_SKIP               12
+
+/* MT9M001 has only one fixed colorspace per pixelcode */
+struct mt9m001_datafmt {
+       enum v4l2_mbus_pixelcode        code;
+       enum v4l2_colorspace            colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9m001_datafmt *mt9m001_find_datafmt(
+       enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
+       int n)
+{
+       int i;
+       for (i = 0; i < n; i++)
+               if (fmt[i].code == code)
+                       return fmt + i;
+
+       return NULL;
+}
+
+static const struct mt9m001_datafmt mt9m001_colour_fmts[] = {
+       /*
+        * Order important: first natively supported,
+        * second supported with a GPIO extender
+        */
+       {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
 };
 
-static const struct soc_camera_data_format mt9m001_monochrome_formats[] = {
+static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
        /* Order important - see above */
-       {
-               .name           = "Monochrome 10 bit",
-               .depth          = 10,
-               .fourcc         = V4L2_PIX_FMT_Y16,
-       }, {
-               .name           = "Monochrome 8 bit",
-               .depth          = 8,
-               .fourcc         = V4L2_PIX_FMT_GREY,
-       },
+       {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+       {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG},
 };
 
 struct mt9m001 {
-       struct i2c_client *client;
-       struct soc_camera_device icd;
+       struct v4l2_subdev subdev;
+       struct v4l2_rect rect;  /* Sensor window */
+       const struct mt9m001_datafmt *fmt;
+       const struct mt9m001_datafmt *fmts;
+       int num_fmts;
        int model;      /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
-       int switch_gpio;
+       unsigned int gain;
+       unsigned int exposure;
+       unsigned short y_skip_top;      /* Lines to skip at the top */
        unsigned char autoexposure;
-       unsigned char datawidth;
 };
 
-static int reg_read(struct soc_camera_device *icd, const u8 reg)
+static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
+{
+       return container_of(i2c_get_clientdata(client), struct mt9m001, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       struct i2c_client *client = mt9m001->client;
        s32 data = i2c_smbus_read_word_data(client, reg);
        return data < 0 ? data : swab16(data);
 }
 
-static int reg_write(struct soc_camera_device *icd, const u8 reg,
+static int reg_write(struct i2c_client *client, const u8 reg,
                     const u16 data)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       return i2c_smbus_write_word_data(mt9m001->client, reg, swab16(data));
+       return i2c_smbus_write_word_data(client, reg, swab16(data));
 }
 
-static int reg_set(struct soc_camera_device *icd, const u8 reg,
+static int reg_set(struct i2c_client *client, const u8 reg,
                   const u16 data)
 {
        int ret;
 
-       ret = reg_read(icd, reg);
+       ret = reg_read(client, reg);
        if (ret < 0)
                return ret;
-       return reg_write(icd, reg, ret | data);
+       return reg_write(client, reg, ret | data);
 }
 
-static int reg_clear(struct soc_camera_device *icd, const u8 reg,
+static int reg_clear(struct i2c_client *client, const u8 reg,
                     const u16 data)
 {
        int ret;
 
-       ret = reg_read(icd, reg);
+       ret = reg_read(client, reg);
        if (ret < 0)
                return ret;
-       return reg_write(icd, reg, ret & ~data);
+       return reg_write(client, reg, ret & ~data);
 }
 
-static int mt9m001_init(struct soc_camera_device *icd)
+static int mt9m001_init(struct i2c_client *client)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
        int ret;
 
-       dev_dbg(icd->vdev->parent, "%s\n", __func__);
-
-       if (icl->power) {
-               ret = icl->power(&mt9m001->client->dev, 1);
-               if (ret < 0) {
-                       dev_err(icd->vdev->parent,
-                               "Platform failed to power-on the camera.\n");
-                       return ret;
-               }
-       }
+       dev_dbg(&client->dev, "%s\n", __func__);
 
-       /* The camera could have been already on, we reset it additionally */
-       if (icl->reset)
-               ret = icl->reset(&mt9m001->client->dev);
-       else
-               ret = -ENODEV;
+       /*
+        * We don't know, whether platform provides reset, issue a soft reset
+        * too. This returns all registers to their default values.
+        */
+       ret = reg_write(client, MT9M001_RESET, 1);
+       if (!ret)
+               ret = reg_write(client, MT9M001_RESET, 0);
 
-       if (ret < 0) {
-               /* Either no platform reset, or platform reset failed */
-               ret = reg_write(icd, MT9M001_RESET, 1);
-               if (!ret)
-                       ret = reg_write(icd, MT9M001_RESET, 0);
-       }
        /* Disable chip, synchronous option update */
        if (!ret)
-               ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0);
+               ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
 
        return ret;
 }
 
-static int mt9m001_release(struct soc_camera_device *icd)
+static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
-
-       /* Disable the chip */
-       reg_write(icd, MT9M001_OUTPUT_CONTROL, 0);
-
-       if (icl->power)
-               icl->power(&mt9m001->client->dev, 0);
+       struct i2c_client *client = sd->priv;
 
-       return 0;
-}
-
-static int mt9m001_start_capture(struct soc_camera_device *icd)
-{
-       /* Switch to master "normal" mode */
-       if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 2) < 0)
+       /* Switch to master "normal" mode or stop sensor readout */
+       if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
                return -EIO;
        return 0;
 }
 
-static int mt9m001_stop_capture(struct soc_camera_device *icd)
-{
-       /* Stop sensor readout */
-       if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 0) < 0)
-               return -EIO;
-       return 0;
-}
-
-static int bus_switch_request(struct mt9m001 *mt9m001,
-                             struct soc_camera_link *icl)
-{
-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
-       int ret;
-       unsigned int gpio = icl->gpio;
-
-       if (gpio_is_valid(gpio)) {
-               /* We have a data bus switch. */
-               ret = gpio_request(gpio, "mt9m001");
-               if (ret < 0) {
-                       dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n",
-                               gpio);
-                       return ret;
-               }
-
-               ret = gpio_direction_output(gpio, 0);
-               if (ret < 0) {
-                       dev_err(&mt9m001->client->dev,
-                               "Cannot set GPIO %u to output\n", gpio);
-                       gpio_free(gpio);
-                       return ret;
-               }
-       }
-
-       mt9m001->switch_gpio = gpio;
-#else
-       mt9m001->switch_gpio = -EINVAL;
-#endif
-       return 0;
-}
-
-static void bus_switch_release(struct mt9m001 *mt9m001)
-{
-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
-       if (gpio_is_valid(mt9m001->switch_gpio))
-               gpio_free(mt9m001->switch_gpio);
-#endif
-}
-
-static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit)
-{
-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
-       if (!gpio_is_valid(mt9m001->switch_gpio))
-               return -ENODEV;
-
-       gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit);
-       return 0;
-#else
-       return -ENODEV;
-#endif
-}
-
-static int bus_switch_possible(struct mt9m001 *mt9m001)
-{
-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
-       return gpio_is_valid(mt9m001->switch_gpio);
-#else
-       return 0;
-#endif
-}
-
 static int mt9m001_set_bus_param(struct soc_camera_device *icd,
                                 unsigned long flags)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
-       int ret;
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+       unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
 
-       /* Flags validity verified in test_bus_param */
+       /* Only one width bit may be set */
+       if (!is_power_of_2(width_flag))
+               return -EINVAL;
 
-       if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) ||
-           (mt9m001->datawidth != 9  && (width_flag == SOCAM_DATAWIDTH_9)) ||
-           (mt9m001->datawidth != 8  && (width_flag == SOCAM_DATAWIDTH_8))) {
-               /* Well, we actually only can do 10 or 8 bits... */
-               if (width_flag == SOCAM_DATAWIDTH_9)
-                       return -EINVAL;
-               ret = bus_switch_act(mt9m001,
-                                    width_flag == SOCAM_DATAWIDTH_8);
-               if (ret < 0)
-                       return ret;
+       if (icl->set_bus_param)
+               return icl->set_bus_param(icl, width_flag);
 
-               mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
-       }
+       /*
+        * Without board specific bus width settings we only support the
+        * sensors native bus width
+        */
+       if (width_flag == SOCAM_DATAWIDTH_10)
+               return 0;
 
-       return 0;
+       return -EINVAL;
 }
 
 static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
        /* MT9M001 has all capture_format parameters fixed */
-       unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING |
+       unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
                SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-               SOCAM_MASTER;
+               SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
 
-       if (bus_switch_possible(mt9m001))
-               flags |= SOCAM_DATAWIDTH_8;
+       if (icl->query_bus_param)
+               flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
+       else
+               flags |= SOCAM_DATAWIDTH_10;
 
        return soc_camera_apply_sensor_flags(icl, flags);
 }
 
-static int mt9m001_set_fmt(struct soc_camera_device *icd,
-                          __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+       struct v4l2_rect rect = a->c;
+       struct soc_camera_device *icd = client->dev.platform_data;
        int ret;
        const u16 hblank = 9, vblank = 25;
+       unsigned int total_h;
+
+       if (mt9m001->fmts == mt9m001_colour_fmts)
+               /*
+                * Bayer format - even number of rows for simplicity,
+                * but let the user play with the top row.
+                */
+               rect.height = ALIGN(rect.height, 2);
+
+       /* Datasheet requirement: see register description */
+       rect.width = ALIGN(rect.width, 2);
+       rect.left = ALIGN(rect.left, 2);
+
+       soc_camera_limit_side(&rect.left, &rect.width,
+                    MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
+
+       soc_camera_limit_side(&rect.top, &rect.height,
+                    MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
+
+       total_h = rect.height + mt9m001->y_skip_top + vblank;
 
        /* Blanking and start values - default... */
-       ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank);
+       ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
        if (!ret)
-               ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank);
+               ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
 
-       /* The caller provides a supported format, as verified per
-        * call to icd->try_fmt() */
+       /*
+        * The caller provides a supported format, as verified per
+        * call to icd->try_fmt()
+        */
        if (!ret)
-               ret = reg_write(icd, MT9M001_COLUMN_START, rect->left);
+               ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
        if (!ret)
-               ret = reg_write(icd, MT9M001_ROW_START, rect->top);
+               ret = reg_write(client, MT9M001_ROW_START, rect.top);
        if (!ret)
-               ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1);
+               ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
        if (!ret)
-               ret = reg_write(icd, MT9M001_WINDOW_HEIGHT,
-                               rect->height + icd->y_skip_top - 1);
+               ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
+                               rect.height + mt9m001->y_skip_top - 1);
        if (!ret && mt9m001->autoexposure) {
-               ret = reg_write(icd, MT9M001_SHUTTER_WIDTH,
-                               rect->height + icd->y_skip_top + vblank);
+               ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
                if (!ret) {
                        const struct v4l2_queryctrl *qctrl =
                                soc_camera_find_qctrl(icd->ops,
                                                      V4L2_CID_EXPOSURE);
-                       icd->exposure = (524 + (rect->height + icd->y_skip_top +
-                                               vblank - 1) *
-                                        (qctrl->maximum - qctrl->minimum)) /
+                       mt9m001->exposure = (524 + (total_h - 1) *
+                                (qctrl->maximum - qctrl->minimum)) /
                                1048 + qctrl->minimum;
                }
        }
 
+       if (!ret)
+               mt9m001->rect = rect;
+
        return ret;
 }
 
-static int mt9m001_try_fmt(struct soc_camera_device *icd,
-                          struct v4l2_format *f)
+static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-       struct v4l2_pix_format *pix = &f->fmt.pix;
-
-       if (pix->height < 32 + icd->y_skip_top)
-               pix->height = 32 + icd->y_skip_top;
-       if (pix->height > 1024 + icd->y_skip_top)
-               pix->height = 1024 + icd->y_skip_top;
-       if (pix->width < 48)
-               pix->width = 48;
-       if (pix->width > 1280)
-               pix->width = 1280;
-       pix->width &= ~0x01; /* has to be even, unsure why was ~3 */
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+       a->c    = mt9m001->rect;
+       a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
        return 0;
 }
 
-static int mt9m001_get_chip_id(struct soc_camera_device *icd,
-                              struct v4l2_chip_ident *id)
+static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       a->bounds.left                  = MT9M001_COLUMN_SKIP;
+       a->bounds.top                   = MT9M001_ROW_SKIP;
+       a->bounds.width                 = MT9M001_MAX_WIDTH;
+       a->bounds.height                = MT9M001_MAX_HEIGHT;
+       a->defrect                      = a->bounds;
+       a->type                         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       a->pixelaspect.numerator        = 1;
+       a->pixelaspect.denominator      = 1;
 
-       if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+       return 0;
+}
+
+static int mt9m001_g_fmt(struct v4l2_subdev *sd,
+                        struct v4l2_mbus_framefmt *mf)
+{
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+       mf->width       = mt9m001->rect.width;
+       mf->height      = mt9m001->rect.height;
+       mf->code        = mt9m001->fmt->code;
+       mf->colorspace  = mt9m001->fmt->colorspace;
+       mf->field       = V4L2_FIELD_NONE;
+
+       return 0;
+}
+
+static int mt9m001_s_fmt(struct v4l2_subdev *sd,
+                        struct v4l2_mbus_framefmt *mf)
+{
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+       struct v4l2_crop a = {
+               .c = {
+                       .left   = mt9m001->rect.left,
+                       .top    = mt9m001->rect.top,
+                       .width  = mf->width,
+                       .height = mf->height,
+               },
+       };
+       int ret;
+
+       /* No support for scaling so far, just crop. TODO: use skipping */
+       ret = mt9m001_s_crop(sd, &a);
+       if (!ret) {
+               mf->width       = mt9m001->rect.width;
+               mf->height      = mt9m001->rect.height;
+               mt9m001->fmt    = mt9m001_find_datafmt(mf->code,
+                                       mt9m001->fmts, mt9m001->num_fmts);
+               mf->colorspace  = mt9m001->fmt->colorspace;
+       }
+
+       return ret;
+}
+
+static int mt9m001_try_fmt(struct v4l2_subdev *sd,
+                          struct v4l2_mbus_framefmt *mf)
+{
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+       const struct mt9m001_datafmt *fmt;
+
+       v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH,
+               MT9M001_MAX_WIDTH, 1,
+               &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top,
+               MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0);
+
+       if (mt9m001->fmts == mt9m001_colour_fmts)
+               mf->height = ALIGN(mf->height - 1, 2);
+
+       fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts,
+                                  mt9m001->num_fmts);
+       if (!fmt) {
+               fmt = mt9m001->fmt;
+               mf->code = fmt->code;
+       }
+
+       mf->colorspace  = fmt->colorspace;
+
+       return 0;
+}
+
+static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
+                               struct v4l2_dbg_chip_ident *id)
+{
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+       if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
                return -EINVAL;
 
-       if (id->match_chip != mt9m001->client->addr)
+       if (id->match.addr != client->addr)
                return -ENODEV;
 
        id->ident       = mt9m001->model;
@@ -360,18 +383,19 @@ static int mt9m001_get_chip_id(struct soc_camera_device *icd,
 }
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9m001_get_register(struct soc_camera_device *icd,
-                               struct v4l2_register *reg)
+static int mt9m001_g_register(struct v4l2_subdev *sd,
+                             struct v4l2_dbg_register *reg)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       struct i2c_client *client = sd->priv;
 
-       if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+       if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
                return -EINVAL;
 
-       if (reg->match_chip != mt9m001->client->addr)
+       if (reg->match.addr != client->addr)
                return -ENODEV;
 
-       reg->val = reg_read(icd, reg->reg);
+       reg->size = 2;
+       reg->val = reg_read(client, reg->reg);
 
        if (reg->val > 0xffff)
                return -EIO;
@@ -379,18 +403,18 @@ static int mt9m001_get_register(struct soc_camera_device *icd,
        return 0;
 }
 
-static int mt9m001_set_register(struct soc_camera_device *icd,
-                               struct v4l2_register *reg)
+static int mt9m001_s_register(struct v4l2_subdev *sd,
+                             struct v4l2_dbg_register *reg)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       struct i2c_client *client = sd->priv;
 
-       if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+       if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
                return -EINVAL;
 
-       if (reg->match_chip != mt9m001->client->addr)
+       if (reg->match.addr != client->addr)
                return -ENODEV;
 
-       if (reg_write(icd, reg->reg, reg->val) < 0)
+       if (reg_write(client, reg->reg, reg->val) < 0)
                return -EIO;
 
        return 0;
@@ -435,42 +459,22 @@ static const struct v4l2_queryctrl mt9m001_controls[] = {
        }
 };
 
-static int mt9m001_video_probe(struct soc_camera_device *);
-static void mt9m001_video_remove(struct soc_camera_device *);
-static int mt9m001_get_control(struct soc_camera_device *, struct v4l2_control *);
-static int mt9m001_set_control(struct soc_camera_device *, struct v4l2_control *);
-
 static struct soc_camera_ops mt9m001_ops = {
-       .owner                  = THIS_MODULE,
-       .probe                  = mt9m001_video_probe,
-       .remove                 = mt9m001_video_remove,
-       .init                   = mt9m001_init,
-       .release                = mt9m001_release,
-       .start_capture          = mt9m001_start_capture,
-       .stop_capture           = mt9m001_stop_capture,
-       .set_fmt                = mt9m001_set_fmt,
-       .try_fmt                = mt9m001_try_fmt,
        .set_bus_param          = mt9m001_set_bus_param,
        .query_bus_param        = mt9m001_query_bus_param,
        .controls               = mt9m001_controls,
        .num_controls           = ARRAY_SIZE(mt9m001_controls),
-       .get_control            = mt9m001_get_control,
-       .set_control            = mt9m001_set_control,
-       .get_chip_id            = mt9m001_get_chip_id,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       .get_register           = mt9m001_get_register,
-       .set_register           = mt9m001_set_register,
-#endif
 };
 
-static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
        int data;
 
        switch (ctrl->id) {
        case V4L2_CID_VFLIP:
-               data = reg_read(icd, MT9M001_READ_OPTIONS2);
+               data = reg_read(client, MT9M001_READ_OPTIONS2);
                if (data < 0)
                        return -EIO;
                ctrl->value = !!(data & 0x8000);
@@ -478,13 +482,21 @@ static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_contro
        case V4L2_CID_EXPOSURE_AUTO:
                ctrl->value = mt9m001->autoexposure;
                break;
+       case V4L2_CID_GAIN:
+               ctrl->value = mt9m001->gain;
+               break;
+       case V4L2_CID_EXPOSURE:
+               ctrl->value = mt9m001->exposure;
+               break;
        }
        return 0;
 }
 
-static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+       struct soc_camera_device *icd = client->dev.platform_data;
        const struct v4l2_queryctrl *qctrl;
        int data;
 
@@ -496,9 +508,9 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
        switch (ctrl->id) {
        case V4L2_CID_VFLIP:
                if (ctrl->value)
-                       data = reg_set(icd, MT9M001_READ_OPTIONS2, 0x8000);
+                       data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
                else
-                       data = reg_clear(icd, MT9M001_READ_OPTIONS2, 0x8000);
+                       data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
                if (data < 0)
                        return -EIO;
                break;
@@ -511,8 +523,8 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
                        unsigned long range = qctrl->default_value - qctrl->minimum;
                        data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
 
-                       dev_dbg(&icd->dev, "Setting gain %d\n", data);
-                       data = reg_write(icd, MT9M001_GLOBAL_GAIN, data);
+                       dev_dbg(&client->dev, "Setting gain %d\n", data);
+                       data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
                        if (data < 0)
                                return -EIO;
                } else {
@@ -529,15 +541,15 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
                        else
                                data = ((gain - 64) * 7 + 28) / 56 + 96;
 
-                       dev_dbg(&icd->dev, "Setting gain from %d to %d\n",
-                                reg_read(icd, MT9M001_GLOBAL_GAIN), data);
-                       data = reg_write(icd, MT9M001_GLOBAL_GAIN, data);
+                       dev_dbg(&client->dev, "Setting gain from %d to %d\n",
+                                reg_read(client, MT9M001_GLOBAL_GAIN), data);
+                       data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
                        if (data < 0)
                                return -EIO;
                }
 
                /* Success */
-               icd->gain = ctrl->value;
+               mt9m001->gain = ctrl->value;
                break;
        case V4L2_CID_EXPOSURE:
                /* mt9m001 has maximum == default */
@@ -548,23 +560,27 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
                        unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
                                                 range / 2) / range + 1;
 
-                       dev_dbg(&icd->dev, "Setting shutter width from %d to %lu\n",
-                                reg_read(icd, MT9M001_SHUTTER_WIDTH), shutter);
-                       if (reg_write(icd, MT9M001_SHUTTER_WIDTH, shutter) < 0)
+                       dev_dbg(&client->dev,
+                               "Setting shutter width from %d to %lu\n",
+                               reg_read(client, MT9M001_SHUTTER_WIDTH),
+                               shutter);
+                       if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
                                return -EIO;
-                       icd->exposure = ctrl->value;
+                       mt9m001->exposure = ctrl->value;
                        mt9m001->autoexposure = 0;
                }
                break;
        case V4L2_CID_EXPOSURE_AUTO:
                if (ctrl->value) {
                        const u16 vblank = 25;
-                       if (reg_write(icd, MT9M001_SHUTTER_WIDTH, icd->height +
-                                     icd->y_skip_top + vblank) < 0)
+                       unsigned int total_h = mt9m001->rect.height +
+                               mt9m001->y_skip_top + vblank;
+                       if (reg_write(client, MT9M001_SHUTTER_WIDTH,
+                                     total_h) < 0)
                                return -EIO;
                        qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
-                       icd->exposure = (524 + (icd->height + icd->y_skip_top + vblank - 1) *
-                                        (qctrl->maximum - qctrl->minimum)) /
+                       mt9m001->exposure = (524 + (total_h - 1) *
+                                (qctrl->maximum - qctrl->minimum)) /
                                1048 + qctrl->minimum;
                        mt9m001->autoexposure = 1;
                } else
@@ -574,87 +590,166 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
        return 0;
 }
 
-/* Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one */
-static int mt9m001_video_probe(struct soc_camera_device *icd)
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9m001_video_probe(struct soc_camera_device *icd,
+                              struct i2c_client *client)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
-       struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
        s32 data;
+       unsigned long flags;
        int ret;
 
-       /* We must have a parent by now. And it cannot be a wrong one.
-        * So this entire test is completely redundant. */
+       /*
+        * We must have a parent by now. And it cannot be a wrong one.
+        * So this entire test is completely redundant.
+        */
        if (!icd->dev.parent ||
            to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
                return -ENODEV;
 
        /* Enable the chip */
-       data = reg_write(&mt9m001->icd, MT9M001_CHIP_ENABLE, 1);
-       dev_dbg(&icd->dev, "write: %d\n", data);
+       data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
+       dev_dbg(&client->dev, "write: %d\n", data);
 
        /* Read out the chip version register */
-       data = reg_read(icd, MT9M001_CHIP_VERSION);
+       data = reg_read(client, MT9M001_CHIP_VERSION);
 
        /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
        switch (data) {
        case 0x8411:
        case 0x8421:
                mt9m001->model = V4L2_IDENT_MT9M001C12ST;
-               icd->formats = mt9m001_colour_formats;
-               if (gpio_is_valid(icl->gpio))
-                       icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats);
-               else
-                       icd->num_formats = 1;
+               mt9m001->fmts = mt9m001_colour_fmts;
                break;
        case 0x8431:
                mt9m001->model = V4L2_IDENT_MT9M001C12STM;
-               icd->formats = mt9m001_monochrome_formats;
-               if (gpio_is_valid(icl->gpio))
-                       icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats);
-               else
-                       icd->num_formats = 1;
+               mt9m001->fmts = mt9m001_monochrome_fmts;
                break;
        default:
-               ret = -ENODEV;
-               dev_err(&icd->dev,
+               dev_err(&client->dev,
                        "No MT9M001 chip detected, register read %x\n", data);
-               goto ei2c;
+               return -ENODEV;
        }
 
-       dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
+       mt9m001->num_fmts = 0;
+
+       /*
+        * This is a 10bit sensor, so by default we only allow 10bit.
+        * The platform may support different bus widths due to
+        * different routing of the data lines.
+        */
+       if (icl->query_bus_param)
+               flags = icl->query_bus_param(icl);
+       else
+               flags = SOCAM_DATAWIDTH_10;
+
+       if (flags & SOCAM_DATAWIDTH_10)
+               mt9m001->num_fmts++;
+       else
+               mt9m001->fmts++;
+
+       if (flags & SOCAM_DATAWIDTH_8)
+               mt9m001->num_fmts++;
+
+       mt9m001->fmt = &mt9m001->fmts[0];
+
+       dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
                 data == 0x8431 ? "C12STM" : "C12ST");
 
-       /* Now that we know the model, we can start video */
-       ret = soc_camera_video_start(icd);
-       if (ret)
-               goto eisis;
+       ret = mt9m001_init(client);
+       if (ret < 0)
+               dev_err(&client->dev, "Failed to initialise the camera\n");
 
-       return 0;
+       /* mt9m001_init() has reset the chip, returning registers to defaults */
+       mt9m001->gain = 64;
+       mt9m001->exposure = 255;
 
-eisis:
-ei2c:
        return ret;
 }
 
 static void mt9m001_video_remove(struct soc_camera_device *icd)
 {
-       struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+       dev_dbg(&icd->dev, "Video removed: %p, %p\n",
+               icd->dev.parent, icd->vdev);
+       if (icl->free_bus)
+               icl->free_bus(icl);
+}
+
+static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+       *lines = mt9m001->y_skip_top;
+
+       return 0;
+}
+
+static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
+       .g_ctrl         = mt9m001_g_ctrl,
+       .s_ctrl         = mt9m001_s_ctrl,
+       .g_chip_ident   = mt9m001_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .g_register     = mt9m001_g_register,
+       .s_register     = mt9m001_s_register,
+#endif
+};
+
+static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+                           enum v4l2_mbus_pixelcode *code)
+{
+       struct i2c_client *client = sd->priv;
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
 
-       dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr,
-               mt9m001->icd.dev.parent, mt9m001->icd.vdev);
-       soc_camera_video_stop(&mt9m001->icd);
+       if (index >= mt9m001->num_fmts)
+               return -EINVAL;
+
+       *code = mt9m001->fmts[index].code;
+       return 0;
 }
 
+static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
+       .s_stream       = mt9m001_s_stream,
+       .s_mbus_fmt     = mt9m001_s_fmt,
+       .g_mbus_fmt     = mt9m001_g_fmt,
+       .try_mbus_fmt   = mt9m001_try_fmt,
+       .s_crop         = mt9m001_s_crop,
+       .g_crop         = mt9m001_g_crop,
+       .cropcap        = mt9m001_cropcap,
+       .enum_mbus_fmt  = mt9m001_enum_fmt,
+};
+
+static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
+       .g_skip_top_lines       = mt9m001_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9m001_subdev_ops = {
+       .core   = &mt9m001_subdev_core_ops,
+       .video  = &mt9m001_subdev_video_ops,
+       .sensor = &mt9m001_subdev_sensor_ops,
+};
+
 static int mt9m001_probe(struct i2c_client *client,
                         const struct i2c_device_id *did)
 {
        struct mt9m001 *mt9m001;
-       struct soc_camera_device *icd;
+       struct soc_camera_device *icd = client->dev.platform_data;
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct soc_camera_link *icl = client->dev.platform_data;
+       struct soc_camera_link *icl;
        int ret;
 
+       if (!icd) {
+               dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
+               return -EINVAL;
+       }
+
+       icl = to_soc_camera_link(icd);
        if (!icl) {
                dev_err(&client->dev, "MT9M001 driver needs platform data\n");
                return -EINVAL;
@@ -670,54 +765,40 @@ static int mt9m001_probe(struct i2c_client *client,
        if (!mt9m001)
                return -ENOMEM;
 
-       mt9m001->client = client;
-       i2c_set_clientdata(client, mt9m001);
+       v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
 
        /* Second stage probe - when a capture adapter is there */
-       icd = &mt9m001->icd;
-       icd->ops        = &mt9m001_ops;
-       icd->control    = &client->dev;
-       icd->x_min      = 20;
-       icd->y_min      = 12;
-       icd->x_current  = 20;
-       icd->y_current  = 12;
-       icd->width_min  = 48;
-       icd->width_max  = 1280;
-       icd->height_min = 32;
-       icd->height_max = 1024;
-       icd->y_skip_top = 1;
-       icd->iface      = icl->bus_id;
-       /* Default datawidth - this is the only width this camera (normally)
-        * supports. It is only with extra logic that it can support
-        * other widths. Therefore it seems to be a sensible default. */
-       mt9m001->datawidth = 10;
-       /* Simulated autoexposure. If enabled, we calculate shutter width
-        * ourselves in the driver based on vertical blanking and frame width */
+       icd->ops                = &mt9m001_ops;
+
+       mt9m001->y_skip_top     = 0;
+       mt9m001->rect.left      = MT9M001_COLUMN_SKIP;
+       mt9m001->rect.top       = MT9M001_ROW_SKIP;
+       mt9m001->rect.width     = MT9M001_MAX_WIDTH;
+       mt9m001->rect.height    = MT9M001_MAX_HEIGHT;
+
+       /*
+        * Simulated autoexposure. If enabled, we calculate shutter width
+        * ourselves in the driver based on vertical blanking and frame width
+        */
        mt9m001->autoexposure = 1;
 
-       ret = bus_switch_request(mt9m001, icl);
-       if (ret)
-               goto eswinit;
-
-       ret = soc_camera_device_register(icd);
-       if (ret)
-               goto eisdr;
-
-       return 0;
+       ret = mt9m001_video_probe(icd, client);
+       if (ret) {
+               icd->ops = NULL;
+               kfree(mt9m001);
+       }
 
-eisdr:
-       bus_switch_release(mt9m001);
-eswinit:
-       kfree(mt9m001);
        return ret;
 }
 
 static int mt9m001_remove(struct i2c_client *client)
 {
-       struct mt9m001 *mt9m001 = i2c_get_clientdata(client);
+       struct mt9m001 *mt9m001 = to_mt9m001(client);
+       struct soc_camera_device *icd = client->dev.platform_data;
 
-       soc_camera_device_unregister(&mt9m001->icd);
-       bus_switch_release(mt9m001);
+       icd->ops = NULL;
+       mt9m001_video_remove(icd);
+       client->driver = NULL;
        kfree(mt9m001);
 
        return 0;