]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/media/video/mt9m111.c
[media] mt9m111: convert to the control framework
[mirror_ubuntu-artful-kernel.git] / drivers / media / video / mt9m111.c
index ebebed929627c8a8ffcae19a6730992a066c0372..8cacbf016848df0b7fb5ee2640be4722dcf20e4b 100644 (file)
 #include <linux/gpio.h>
 #include <linux/delay.h>
 
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
 
 /*
  * MT9M111, MT9M112 and MT9M131:
 #define MT9M111_RESET_RESTART_FRAME    (1 << 1)
 #define MT9M111_RESET_RESET_MODE       (1 << 0)
 
+#define MT9M111_RM_FULL_POWER_RD       (0 << 10)
+#define MT9M111_RM_LOW_POWER_RD                (1 << 10)
+#define MT9M111_RM_COL_SKIP_4X         (1 << 5)
+#define MT9M111_RM_ROW_SKIP_4X         (1 << 4)
+#define MT9M111_RM_COL_SKIP_2X         (1 << 3)
+#define MT9M111_RM_ROW_SKIP_2X         (1 << 2)
 #define MT9M111_RMB_MIRROR_COLS                (1 << 1)
 #define MT9M111_RMB_MIRROR_ROWS                (1 << 0)
 #define MT9M111_CTXT_CTRL_RESTART      (1 << 15)
 
 #define MT9M111_OPMODE_AUTOEXPO_EN     (1 << 14)
 #define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
-
+#define MT9M111_OUTFMT_FLIP_BAYER_COL  (1 << 9)
+#define MT9M111_OUTFMT_FLIP_BAYER_ROW  (1 << 8)
 #define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
 #define MT9M111_OUTFMT_BYPASS_IFP      (1 << 10)
 #define MT9M111_OUTFMT_INV_PIX_CLOCK   (1 << 9)
 #define MT9M111_OUTFMT_TST_RAMP_FRAME  (3 << 4)
 #define MT9M111_OUTFMT_SHIFT_3_UP      (1 << 3)
 #define MT9M111_OUTFMT_AVG_CHROMA      (1 << 2)
-#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y  (1 << 1)
-#define MT9M111_OUTFMT_SWAP_RGB_EVEN   (1 << 1)
-#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr        (1 << 0)
+#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
+#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B        (1 << 0)
 
 /*
  * Camera control register addresses (0x200..0x2ff not implemented)
 #define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
 #define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
 #define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
+#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
+               (val), (mask))
 
 #define MT9M111_MIN_DARK_ROWS  8
 #define MT9M111_MIN_DARK_COLS  26
@@ -153,7 +163,11 @@ static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
        {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
        {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
        {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
        {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
+       {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
        {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
        {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
 };
@@ -165,22 +179,18 @@ enum mt9m111_context {
 
 struct mt9m111 {
        struct v4l2_subdev subdev;
+       struct v4l2_ctrl_handler hdl;
+       struct v4l2_ctrl *gain;
        int model;      /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
                         * from v4l2-chip-ident.h */
        enum mt9m111_context context;
        struct v4l2_rect rect;
+       struct mutex power_lock; /* lock to protect power_count */
+       int power_count;
        const struct mt9m111_datafmt *fmt;
-       unsigned int gain;
-       unsigned char autoexposure;
+       int lastpage;   /* PageMap cache value */
        unsigned char datawidth;
        unsigned int powered:1;
-       unsigned int hflip:1;
-       unsigned int vflip:1;
-       unsigned int swap_rgb_even_odd:1;
-       unsigned int swap_rgb_red_blue:1;
-       unsigned int swap_yuv_y_chromas:1;
-       unsigned int swap_yuv_cb_cr:1;
-       unsigned int autowhitebalance:1;
 };
 
 static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
@@ -192,17 +202,17 @@ static int reg_page_map_set(struct i2c_client *client, const u16 reg)
 {
        int ret;
        u16 page;
-       static int lastpage = -1;       /* PageMap cache value */
+       struct mt9m111 *mt9m111 = to_mt9m111(client);
 
        page = (reg >> 8);
-       if (page == lastpage)
+       if (page == mt9m111->lastpage)
                return 0;
        if (page > 2)
                return -EINVAL;
 
        ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page));
        if (!ret)
-               lastpage = page;
+               mt9m111->lastpage = page;
        return ret;
 }
 
@@ -248,12 +258,26 @@ static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
        int ret;
 
        ret = mt9m111_reg_read(client, reg);
-       return mt9m111_reg_write(client, reg, ret & ~data);
+       if (ret >= 0)
+               ret = mt9m111_reg_write(client, reg, ret & ~data);
+       return ret;
 }
 
-static int mt9m111_set_context(struct i2c_client *client,
+static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
+                           const u16 data, const u16 mask)
+{
+       int ret;
+
+       ret = mt9m111_reg_read(client, reg);
+       if (ret >= 0)
+               ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
+       return ret;
+}
+
+static int mt9m111_set_context(struct mt9m111 *mt9m111,
                               enum mt9m111_context ctxt)
 {
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B
                | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B
                | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B
@@ -267,10 +291,10 @@ static int mt9m111_set_context(struct i2c_client *client,
                return reg_write(CONTEXT_CONTROL, valA);
 }
 
-static int mt9m111_setup_rect(struct i2c_client *client,
+static int mt9m111_setup_rect(struct mt9m111 *mt9m111,
                              struct v4l2_rect *rect)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int ret, is_raw_format;
        int width = rect->width;
        int height = rect->height;
@@ -312,81 +336,9 @@ static int mt9m111_setup_rect(struct i2c_client *client,
        return ret;
 }
 
-static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt)
-{
-       int ret;
-       u16 mask = MT9M111_OUTFMT_PROCESSED_BAYER | MT9M111_OUTFMT_RGB |
-               MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_SWAP_RGB_EVEN |
-               MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
-               MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr |
-               MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
-
-       ret = reg_read(OUTPUT_FORMAT_CTRL2_A);
-       if (ret >= 0)
-               ret = reg_write(OUTPUT_FORMAT_CTRL2_A, (ret & ~mask) | outfmt);
-       if (!ret)
-               ret = reg_read(OUTPUT_FORMAT_CTRL2_B);
-       if (ret >= 0)
-               ret = reg_write(OUTPUT_FORMAT_CTRL2_B, (ret & ~mask) | outfmt);
-
-       return ret;
-}
-
-static int mt9m111_setfmt_bayer8(struct i2c_client *client)
-{
-       return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER |
-                                   MT9M111_OUTFMT_RGB);
-}
-
-static int mt9m111_setfmt_bayer10(struct i2c_client *client)
-{
-       return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_BYPASS_IFP);
-}
-
-static int mt9m111_setfmt_rgb565(struct i2c_client *client)
-{
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       int val = 0;
-
-       if (mt9m111->swap_rgb_red_blue)
-               val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
-       if (mt9m111->swap_rgb_even_odd)
-               val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
-       val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
-
-       return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_setfmt_rgb555(struct i2c_client *client)
-{
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       int val = 0;
-
-       if (mt9m111->swap_rgb_red_blue)
-               val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
-       if (mt9m111->swap_rgb_even_odd)
-               val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
-       val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
-
-       return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_setfmt_yuv(struct i2c_client *client)
-{
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       int val = 0;
-
-       if (mt9m111->swap_yuv_cb_cr)
-               val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
-       if (mt9m111->swap_yuv_y_chromas)
-               val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
-
-       return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_enable(struct i2c_client *client)
+static int mt9m111_enable(struct mt9m111 *mt9m111)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int ret;
 
        ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE);
@@ -395,8 +347,9 @@ static int mt9m111_enable(struct i2c_client *client)
        return ret;
 }
 
-static int mt9m111_reset(struct i2c_client *client)
+static int mt9m111_reset(struct mt9m111 *mt9m111)
 {
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int ret;
 
        ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
@@ -409,26 +362,9 @@ static int mt9m111_reset(struct i2c_client *client)
        return ret;
 }
 
-static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
-{
-       struct soc_camera_link *icl = to_soc_camera_link(icd);
-       unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
-               SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-               SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
-       return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
-{
-       return 0;
-}
-
-static int mt9m111_make_rect(struct i2c_client *client,
+static int mt9m111_make_rect(struct mt9m111 *mt9m111,
                             struct v4l2_rect *rect)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-
        if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
            mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
                /* Bayer format - even size lengths */
@@ -444,14 +380,14 @@ static int mt9m111_make_rect(struct i2c_client *client,
        soc_camera_limit_side(&rect->top, &rect->height,
                     MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
 
-       return mt9m111_setup_rect(client, rect);
+       return mt9m111_setup_rect(mt9m111, rect);
 }
 
 static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
        struct v4l2_rect rect = a->c;
        struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
        int ret;
 
        dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
@@ -460,7 +396,7 @@ static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
        if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
-       ret = mt9m111_make_rect(client, &rect);
+       ret = mt9m111_make_rect(mt9m111, &rect);
        if (!ret)
                mt9m111->rect = rect;
        return ret;
@@ -468,8 +404,7 @@ static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 
 static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 
        a->c    = mt9m111->rect;
        a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -496,8 +431,7 @@ static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 static int mt9m111_g_fmt(struct v4l2_subdev *sd,
                         struct v4l2_mbus_framefmt *mf)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 
        mf->width       = mt9m111->rect.width;
        mf->height      = mt9m111->rect.height;
@@ -508,51 +442,73 @@ static int mt9m111_g_fmt(struct v4l2_subdev *sd,
        return 0;
 }
 
-static int mt9m111_set_pixfmt(struct i2c_client *client,
+static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
                              enum v4l2_mbus_pixelcode code)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+       u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+               MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
+               MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
+               MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
+               MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+               MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
        int ret;
 
        switch (code) {
        case V4L2_MBUS_FMT_SBGGR8_1X8:
-               ret = mt9m111_setfmt_bayer8(client);
+               data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+                       MT9M111_OUTFMT_RGB;
                break;
        case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
-               ret = mt9m111_setfmt_bayer10(client);
+               data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
                break;
        case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
-               ret = mt9m111_setfmt_rgb555(client);
+               data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
+                       MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+               break;
+       case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+               data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
                break;
        case V4L2_MBUS_FMT_RGB565_2X8_LE:
-               ret = mt9m111_setfmt_rgb565(client);
+               data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+                       MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+               break;
+       case V4L2_MBUS_FMT_RGB565_2X8_BE:
+               data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
+               break;
+       case V4L2_MBUS_FMT_BGR565_2X8_BE:
+               data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+                       MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+               break;
+       case V4L2_MBUS_FMT_BGR565_2X8_LE:
+               data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+                       MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+                       MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
                break;
        case V4L2_MBUS_FMT_UYVY8_2X8:
-               mt9m111->swap_yuv_y_chromas = 0;
-               mt9m111->swap_yuv_cb_cr = 0;
-               ret = mt9m111_setfmt_yuv(client);
+               data_outfmt2 = 0;
                break;
        case V4L2_MBUS_FMT_VYUY8_2X8:
-               mt9m111->swap_yuv_y_chromas = 0;
-               mt9m111->swap_yuv_cb_cr = 1;
-               ret = mt9m111_setfmt_yuv(client);
+               data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
                break;
        case V4L2_MBUS_FMT_YUYV8_2X8:
-               mt9m111->swap_yuv_y_chromas = 1;
-               mt9m111->swap_yuv_cb_cr = 0;
-               ret = mt9m111_setfmt_yuv(client);
+               data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
                break;
        case V4L2_MBUS_FMT_YVYU8_2X8:
-               mt9m111->swap_yuv_y_chromas = 1;
-               mt9m111->swap_yuv_cb_cr = 1;
-               ret = mt9m111_setfmt_yuv(client);
+               data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+                       MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
                break;
        default:
-               dev_err(&client->dev, "Pixel format not handled : %x\n",
-                       code);
-               ret = -EINVAL;
+               dev_err(&client->dev, "Pixel format not handled: %x\n", code);
+               return -EINVAL;
        }
 
+       ret = reg_mask(OUTPUT_FORMAT_CTRL2_A, data_outfmt2,
+                      mask_outfmt2);
+       if (!ret)
+               ret = reg_mask(OUTPUT_FORMAT_CTRL2_B, data_outfmt2,
+                              mask_outfmt2);
+
        return ret;
 }
 
@@ -561,7 +517,7 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd,
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        const struct mt9m111_datafmt *fmt;
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
        struct v4l2_rect rect = {
                .left   = mt9m111->rect.left,
                .top    = mt9m111->rect.top,
@@ -579,9 +535,9 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd,
                "%s code=%x left=%d, top=%d, width=%d, height=%d\n", __func__,
                mf->code, rect.left, rect.top, rect.width, rect.height);
 
-       ret = mt9m111_make_rect(client, &rect);
+       ret = mt9m111_make_rect(mt9m111, &rect);
        if (!ret)
-               ret = mt9m111_set_pixfmt(client, mf->code);
+               ret = mt9m111_set_pixfmt(mt9m111, mf->code);
        if (!ret) {
                mt9m111->rect   = rect;
                mt9m111->fmt    = fmt;
@@ -594,8 +550,7 @@ static int mt9m111_s_fmt(struct v4l2_subdev *sd,
 static int mt9m111_try_fmt(struct v4l2_subdev *sd,
                           struct v4l2_mbus_framefmt *mf)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
        const struct mt9m111_datafmt *fmt;
        bool bayer = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
                mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
@@ -635,7 +590,7 @@ static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
                                struct v4l2_dbg_chip_ident *id)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 
        if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
                return -EINVAL;
@@ -689,58 +644,9 @@ static int mt9m111_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static const struct v4l2_queryctrl mt9m111_controls[] = {
-       {
-               .id             = V4L2_CID_VFLIP,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-               .name           = "Flip Verticaly",
-               .minimum        = 0,
-               .maximum        = 1,
-               .step           = 1,
-               .default_value  = 0,
-       }, {
-               .id             = V4L2_CID_HFLIP,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-               .name           = "Flip Horizontaly",
-               .minimum        = 0,
-               .maximum        = 1,
-               .step           = 1,
-               .default_value  = 0,
-       }, {    /* gain = 1/32*val (=>gain=1 if val==32) */
-               .id             = V4L2_CID_GAIN,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-               .name           = "Gain",
-               .minimum        = 0,
-               .maximum        = 63 * 2 * 2,
-               .step           = 1,
-               .default_value  = 32,
-               .flags          = V4L2_CTRL_FLAG_SLIDER,
-       }, {
-               .id             = V4L2_CID_EXPOSURE_AUTO,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-               .name           = "Auto Exposure",
-               .minimum        = 0,
-               .maximum        = 1,
-               .step           = 1,
-               .default_value  = 1,
-       }
-};
-
-static int mt9m111_resume(struct soc_camera_device *icd);
-static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state);
-
-static struct soc_camera_ops mt9m111_ops = {
-       .suspend                = mt9m111_suspend,
-       .resume                 = mt9m111_resume,
-       .query_bus_param        = mt9m111_query_bus_param,
-       .set_bus_param          = mt9m111_set_bus_param,
-       .controls               = mt9m111_controls,
-       .num_controls           = ARRAY_SIZE(mt9m111_controls),
-};
-
-static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask)
+static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int ret;
 
        if (mt9m111->context == HIGHPOWER) {
@@ -758,8 +664,9 @@ static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask)
        return ret;
 }
 
-static int mt9m111_get_global_gain(struct i2c_client *client)
+static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
 {
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int data;
 
        data = reg_read(GLOBAL_GAIN);
@@ -769,15 +676,14 @@ static int mt9m111_get_global_gain(struct i2c_client *client)
        return data;
 }
 
-static int mt9m111_set_global_gain(struct i2c_client *client, int gain)
+static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        u16 val;
 
        if (gain > 63 * 2 * 2)
                return -EINVAL;
 
-       mt9m111->gain = gain;
        if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
                val = (1 << 10) | (1 << 9) | (gain / 4);
        else if ((gain >= 64) && (gain < 64 * 2))
@@ -788,173 +694,87 @@ static int mt9m111_set_global_gain(struct i2c_client *client, int gain)
        return reg_write(GLOBAL_GAIN, val);
 }
 
-static int mt9m111_set_autoexposure(struct i2c_client *client, int on)
+static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       int ret;
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 
        if (on)
-               ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-       else
-               ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-
-       if (!ret)
-               mt9m111->autoexposure = on;
-
-       return ret;
+               return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+       return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
 }
 
-static int mt9m111_set_autowhitebalance(struct i2c_client *client, int on)
+static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       int ret;
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 
        if (on)
-               ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-       else
-               ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-
-       if (!ret)
-               mt9m111->autowhitebalance = on;
-
-       return ret;
+               return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+       return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
 }
 
-static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       int data;
+       struct mt9m111 *mt9m111 = container_of(ctrl->handler,
+                                              struct mt9m111, hdl);
 
        switch (ctrl->id) {
        case V4L2_CID_VFLIP:
-               if (mt9m111->context == HIGHPOWER)
-                       data = reg_read(READ_MODE_B);
-               else
-                       data = reg_read(READ_MODE_A);
-
-               if (data < 0)
-                       return -EIO;
-               ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS);
-               break;
-       case V4L2_CID_HFLIP:
-               if (mt9m111->context == HIGHPOWER)
-                       data = reg_read(READ_MODE_B);
-               else
-                       data = reg_read(READ_MODE_A);
-
-               if (data < 0)
-                       return -EIO;
-               ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
-               break;
-       case V4L2_CID_GAIN:
-               data = mt9m111_get_global_gain(client);
-               if (data < 0)
-                       return data;
-               ctrl->value = data;
-               break;
-       case V4L2_CID_EXPOSURE_AUTO:
-               ctrl->value = mt9m111->autoexposure;
-               break;
-       case V4L2_CID_AUTO_WHITE_BALANCE:
-               ctrl->value = mt9m111->autowhitebalance;
-               break;
-       }
-       return 0;
-}
-
-static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-       const struct v4l2_queryctrl *qctrl;
-       int ret;
-
-       qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
-       if (!qctrl)
-               return -EINVAL;
-
-       switch (ctrl->id) {
-       case V4L2_CID_VFLIP:
-               mt9m111->vflip = ctrl->value;
-               ret = mt9m111_set_flip(client, ctrl->value,
+               return mt9m111_set_flip(mt9m111, ctrl->val,
                                        MT9M111_RMB_MIRROR_ROWS);
-               break;
        case V4L2_CID_HFLIP:
-               mt9m111->hflip = ctrl->value;
-               ret = mt9m111_set_flip(client, ctrl->value,
+               return mt9m111_set_flip(mt9m111, ctrl->val,
                                        MT9M111_RMB_MIRROR_COLS);
-               break;
        case V4L2_CID_GAIN:
-               ret = mt9m111_set_global_gain(client, ctrl->value);
-               break;
+               return mt9m111_set_global_gain(mt9m111, ctrl->val);
        case V4L2_CID_EXPOSURE_AUTO:
-               ret =  mt9m111_set_autoexposure(client, ctrl->value);
-               break;
+               return mt9m111_set_autoexposure(mt9m111, ctrl->val);
        case V4L2_CID_AUTO_WHITE_BALANCE:
-               ret =  mt9m111_set_autowhitebalance(client, ctrl->value);
-               break;
-       default:
-               ret = -EINVAL;
+               return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
        }
 
-       return ret;
+       return -EINVAL;
 }
 
-static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state)
+static int mt9m111_suspend(struct mt9m111 *mt9m111)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-
-       mt9m111->gain = mt9m111_get_global_gain(client);
+       v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
 
        return 0;
 }
 
-static int mt9m111_restore_state(struct i2c_client *client)
+static void mt9m111_restore_state(struct mt9m111 *mt9m111)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
-
-       mt9m111_set_context(client, mt9m111->context);
-       mt9m111_set_pixfmt(client, mt9m111->fmt->code);
-       mt9m111_setup_rect(client, &mt9m111->rect);
-       mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
-       mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
-       mt9m111_set_global_gain(client, mt9m111->gain);
-       mt9m111_set_autoexposure(client, mt9m111->autoexposure);
-       mt9m111_set_autowhitebalance(client, mt9m111->autowhitebalance);
-       return 0;
+       mt9m111_set_context(mt9m111, mt9m111->context);
+       mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
+       mt9m111_setup_rect(mt9m111, &mt9m111->rect);
+       v4l2_ctrl_handler_setup(&mt9m111->hdl);
 }
 
-static int mt9m111_resume(struct soc_camera_device *icd)
+static int mt9m111_resume(struct mt9m111 *mt9m111)
 {
-       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
        int ret = 0;
 
        if (mt9m111->powered) {
-               ret = mt9m111_enable(client);
+               ret = mt9m111_enable(mt9m111);
                if (!ret)
-                       ret = mt9m111_reset(client);
+                       ret = mt9m111_reset(mt9m111);
                if (!ret)
-                       ret = mt9m111_restore_state(client);
+                       mt9m111_restore_state(mt9m111);
        }
        return ret;
 }
 
-static int mt9m111_init(struct i2c_client *client)
+static int mt9m111_init(struct mt9m111 *mt9m111)
 {
-       struct mt9m111 *mt9m111 = to_mt9m111(client);
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
        int ret;
 
        mt9m111->context = HIGHPOWER;
-       ret = mt9m111_enable(client);
-       if (!ret)
-               ret = mt9m111_reset(client);
+       ret = mt9m111_enable(mt9m111);
        if (!ret)
-               ret = mt9m111_set_context(client, mt9m111->context);
+               ret = mt9m111_reset(mt9m111);
        if (!ret)
-               ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
+               ret = mt9m111_set_context(mt9m111, mt9m111->context);
        if (ret)
                dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
        return ret;
@@ -971,19 +791,11 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
        s32 data;
        int ret;
 
-       /*
-        * 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;
+       /* We must have a parent by now. And it cannot be a wrong one. */
+       BUG_ON(!icd->parent ||
+              to_soc_camera_host(icd->parent)->nr != icd->iface);
 
-       mt9m111->autoexposure = 1;
-       mt9m111->autowhitebalance = 1;
-
-       mt9m111->swap_rgb_even_odd = 1;
-       mt9m111->swap_rgb_red_blue = 1;
+       mt9m111->lastpage = -1;
 
        data = reg_read(CHIP_VERSION);
 
@@ -998,23 +810,59 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
                dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
                break;
        default:
-               ret = -ENODEV;
                dev_err(&client->dev,
                        "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
                        data);
-               goto ei2c;
+               return -ENODEV;
+       }
+
+       ret = mt9m111_init(mt9m111);
+       if (ret)
+               return ret;
+       return v4l2_ctrl_handler_setup(&mt9m111->hdl);
+}
+
+static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
+{
+       struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = 0;
+
+       mutex_lock(&mt9m111->power_lock);
+
+       /*
+        * If the power count is modified from 0 to != 0 or from != 0 to 0,
+        * update the power state.
+        */
+       if (mt9m111->power_count == !on) {
+               if (on) {
+                       ret = mt9m111_resume(mt9m111);
+                       if (ret) {
+                               dev_err(&client->dev,
+                                       "Failed to resume the sensor: %d\n", ret);
+                               goto out;
+                       }
+               } else {
+                       mt9m111_suspend(mt9m111);
+               }
        }
 
-       ret = mt9m111_init(client);
+       /* Update the power count. */
+       mt9m111->power_count += on ? 1 : -1;
+       WARN_ON(mt9m111->power_count < 0);
 
-ei2c:
+out:
+       mutex_unlock(&mt9m111->power_lock);
        return ret;
 }
 
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+       .s_ctrl = mt9m111_s_ctrl,
+};
+
 static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
-       .g_ctrl         = mt9m111_g_ctrl,
-       .s_ctrl         = mt9m111_s_ctrl,
        .g_chip_ident   = mt9m111_g_chip_ident,
+       .s_power        = mt9m111_s_power,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register     = mt9m111_g_register,
        .s_register     = mt9m111_s_register,
@@ -1031,6 +879,22 @@ static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
        return 0;
 }
 
+static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
+                               struct v4l2_mbus_config *cfg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct soc_camera_device *icd = client->dev.platform_data;
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+       cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+               V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_DATA_ACTIVE_HIGH;
+       cfg->type = V4L2_MBUS_PARALLEL;
+       cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+       return 0;
+}
+
 static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
        .s_mbus_fmt     = mt9m111_s_fmt,
        .g_mbus_fmt     = mt9m111_g_fmt,
@@ -1039,6 +903,7 @@ static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
        .g_crop         = mt9m111_g_crop,
        .cropcap        = mt9m111_cropcap,
        .enum_mbus_fmt  = mt9m111_enum_fmt,
+       .g_mbus_config  = mt9m111_g_mbus_config,
 };
 
 static struct v4l2_subdev_ops mt9m111_subdev_ops = {
@@ -1077,10 +942,27 @@ static int mt9m111_probe(struct i2c_client *client,
                return -ENOMEM;
 
        v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
+       v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
+       v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+                       V4L2_CID_VFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+                       V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+       mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
+       v4l2_ctrl_new_std_menu(&mt9m111->hdl,
+                       &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+                       V4L2_EXPOSURE_AUTO);
+       mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
+       if (mt9m111->hdl.error) {
+               int err = mt9m111->hdl.error;
 
-       /* Second stage probe - when a capture adapter is there */
-       icd->ops                = &mt9m111_ops;
+               kfree(mt9m111);
+               return err;
+       }
 
+       /* Second stage probe - when a capture adapter is there */
        mt9m111->rect.left      = MT9M111_MIN_DARK_COLS;
        mt9m111->rect.top       = MT9M111_MIN_DARK_ROWS;
        mt9m111->rect.width     = MT9M111_MAX_WIDTH;
@@ -1089,7 +971,7 @@ static int mt9m111_probe(struct i2c_client *client,
 
        ret = mt9m111_video_probe(icd, client);
        if (ret) {
-               icd->ops = NULL;
+               v4l2_ctrl_handler_free(&mt9m111->hdl);
                kfree(mt9m111);
        }
 
@@ -1099,9 +981,9 @@ static int mt9m111_probe(struct i2c_client *client,
 static int mt9m111_remove(struct i2c_client *client)
 {
        struct mt9m111 *mt9m111 = to_mt9m111(client);
-       struct soc_camera_device *icd = client->dev.platform_data;
 
-       icd->ops = NULL;
+       v4l2_device_unregister_subdev(&mt9m111->subdev);
+       v4l2_ctrl_handler_free(&mt9m111->hdl);
        kfree(mt9m111);
 
        return 0;