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 / ov772x.c
index 0af2ca6..34034a7 100644 (file)
@@ -22,8 +22,9 @@
 #include <linux/delay.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
 #include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/ov772x.h>
 
 /*
@@ -51,6 +52,7 @@
 #define COM8        0x13 /* Common control 8 */
 #define COM9        0x14 /* Common control 9 */
 #define COM10       0x15 /* Common control 10 */
+#define REG16       0x16 /* Register 16 */
 #define HSTART      0x17 /* Horizontal sensor size */
 #define HSIZE       0x18 /* Horizontal frame (HREF column) end high 8-bit */
 #define VSTART      0x19 /* Vertical frame (row) start high 8-bit */
@@ -65,6 +67,7 @@
 #define AEW         0x24 /* AGC/AEC - Stable operating region (upper limit) */
 #define AEB         0x25 /* AGC/AEC - Stable operating region (lower limit) */
 #define VPT         0x26 /* AGC/AEC Fast mode operating region */
+#define REG28       0x28 /* Register 28 */
 #define HOUTSIZE    0x29 /* Horizontal data output size MSBs */
 #define EXHCH       0x2A /* Dummy pixel insert MSB */
 #define EXHCL       0x2B /* Dummy pixel insert LSB */
@@ -94,6 +97,7 @@
 #define TGT_R       0x43 /* BLC red  channel target value */
 #define TGT_GB      0x44 /* BLC Gb   channel target value */
 #define TGT_GR      0x45 /* BLC Gr   channel target value */
+/* for ov7720 */
 #define LCC0        0x46 /* Lens correction control 0 */
 #define LCC1        0x47 /* Lens correction option 1 - X coordinate */
 #define LCC2        0x48 /* Lens correction option 2 - Y coordinate */
 #define LCC4        0x4A /* Lens correction option 4 - radius of the circular */
 #define LCC5        0x4B /* Lens correction option 5 */
 #define LCC6        0x4C /* Lens correction option 6 */
+/* for ov7725 */
+#define LC_CTR      0x46 /* Lens correction control */
+#define LC_XC       0x47 /* X coordinate of lens correction center relative */
+#define LC_YC       0x48 /* Y coordinate of lens correction center relative */
+#define LC_COEF     0x49 /* Lens correction coefficient */
+#define LC_RADI     0x4A /* Lens correction radius */
+#define LC_COEFB    0x4B /* Lens B channel compensation coefficient */
+#define LC_COEFR    0x4C /* Lens R channel compensation coefficient */
+
 #define FIXGAIN     0x4D /* Analog fix gain amplifer */
 #define AREF0       0x4E /* Sensor reference control */
 #define AREF1       0x4F /* Sensor reference current control */
 #define AREF7       0x55 /* Analog reference control */
 #define UFIX        0x60 /* U channel fixed value output */
 #define VFIX        0x61 /* V channel fixed value output */
-#define AW_BB_BLK   0x62 /* AWB option for advanced AWB */
-#define AW_B_CTRL0  0x63 /* AWB control byte 0 */
+#define AWBB_BLK    0x62 /* AWB option for advanced AWB */
+#define AWB_CTRL0   0x63 /* AWB control byte 0 */
 #define DSP_CTRL1   0x64 /* DSP control byte 1 */
 #define DSP_CTRL2   0x65 /* DSP control byte 2 */
 #define DSP_CTRL3   0x66 /* DSP control byte 3 */
 #define DSP_CTRL4   0x67 /* DSP control byte 4 */
-#define AW_B_BIAS   0x68 /* AWB BLC level clip */
-#define AW_BCTRL1   0x69 /* AWB control  1 */
-#define AW_BCTRL2   0x6A /* AWB control  2 */
-#define AW_BCTRL3   0x6B /* AWB control  3 */
-#define AW_BCTRL4   0x6C /* AWB control  4 */
-#define AW_BCTRL5   0x6D /* AWB control  5 */
-#define AW_BCTRL6   0x6E /* AWB control  6 */
-#define AW_BCTRL7   0x6F /* AWB control  7 */
-#define AW_BCTRL8   0x70 /* AWB control  8 */
-#define AW_BCTRL9   0x71 /* AWB control  9 */
-#define AW_BCTRL10  0x72 /* AWB control 10 */
-#define AW_BCTRL11  0x73 /* AWB control 11 */
-#define AW_BCTRL12  0x74 /* AWB control 12 */
-#define AW_BCTRL13  0x75 /* AWB control 13 */
-#define AW_BCTRL14  0x76 /* AWB control 14 */
-#define AW_BCTRL15  0x77 /* AWB control 15 */
-#define AW_BCTRL16  0x78 /* AWB control 16 */
-#define AW_BCTRL17  0x79 /* AWB control 17 */
-#define AW_BCTRL18  0x7A /* AWB control 18 */
-#define AW_BCTRL19  0x7B /* AWB control 19 */
-#define AW_BCTRL20  0x7C /* AWB control 20 */
-#define AW_BCTRL21  0x7D /* AWB control 21 */
+#define AWB_BIAS    0x68 /* AWB BLC level clip */
+#define AWB_CTRL1   0x69 /* AWB control  1 */
+#define AWB_CTRL2   0x6A /* AWB control  2 */
+#define AWB_CTRL3   0x6B /* AWB control  3 */
+#define AWB_CTRL4   0x6C /* AWB control  4 */
+#define AWB_CTRL5   0x6D /* AWB control  5 */
+#define AWB_CTRL6   0x6E /* AWB control  6 */
+#define AWB_CTRL7   0x6F /* AWB control  7 */
+#define AWB_CTRL8   0x70 /* AWB control  8 */
+#define AWB_CTRL9   0x71 /* AWB control  9 */
+#define AWB_CTRL10  0x72 /* AWB control 10 */
+#define AWB_CTRL11  0x73 /* AWB control 11 */
+#define AWB_CTRL12  0x74 /* AWB control 12 */
+#define AWB_CTRL13  0x75 /* AWB control 13 */
+#define AWB_CTRL14  0x76 /* AWB control 14 */
+#define AWB_CTRL15  0x77 /* AWB control 15 */
+#define AWB_CTRL16  0x78 /* AWB control 16 */
+#define AWB_CTRL17  0x79 /* AWB control 17 */
+#define AWB_CTRL18  0x7A /* AWB control 18 */
+#define AWB_CTRL19  0x7B /* AWB control 19 */
+#define AWB_CTRL20  0x7C /* AWB control 20 */
+#define AWB_CTRL21  0x7D /* AWB control 21 */
 #define GAM1        0x7E /* Gamma Curve  1st segment input end point */
 #define GAM2        0x7F /* Gamma Curve  2nd segment input end point */
 #define GAM3        0x80 /* Gamma Curve  3rd segment input end point */
 #define GAM15       0x8C /* Gamma Curve 15th segment input end point */
 #define SLOP        0x8D /* Gamma curve highest segment slope */
 #define DNSTH       0x8E /* De-noise threshold */
-#define EDGE0       0x8F /* Edge enhancement control 0 */
-#define EDGE1       0x90 /* Edge enhancement control 1 */
+#define EDGE_STRNGT 0x8F /* Edge strength  control when manual mode */
+#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */
 #define DNSOFF      0x91 /* Auto De-noise threshold control */
-#define EDGE2       0x92 /* Edge enhancement strength low  point control */
-#define EDGE3       0x93 /* Edge enhancement strength high point control */
+#define EDGE_UPPER  0x92 /* Edge strength upper limit when Auto mode */
+#define EDGE_LOWER  0x93 /* Edge strength lower limit when Auto mode */
 #define MTX1        0x94 /* Matrix coefficient 1 */
 #define MTX2        0x95 /* Matrix coefficient 2 */
 #define MTX3        0x96 /* Matrix coefficient 3 */
 #define SDE         0xA6 /* Special digital effect control */
 #define USAT        0xA7 /* U component saturation control */
 #define VSAT        0xA8 /* V component saturation control */
+/* for ov7720 */
 #define HUE0        0xA9 /* Hue control 0 */
 #define HUE1        0xAA /* Hue control 1 */
+/* for ov7725 */
+#define HUECOS      0xA9 /* Cosine value */
+#define HUESIN      0xAA /* Sine value */
+
 #define SIGN        0xAB /* Sign bit for Hue and contrast */
 #define DSPAUTO     0xAC /* DSP auto function ON/OFF control */
 
 #define OCAP_4x         0x03   /* 4x */
 
 /* COM3 */
-#define SWAP_MASK       0x38
+#define SWAP_MASK       (SWAP_RGB | SWAP_YUV | SWAP_ML)
+#define IMG_MASK        (VFLIP_IMG | HFLIP_IMG)
 
-#define VFIMG_ON_OFF    0x80   /* Vertical flip image ON/OFF selection */
-#define HMIMG_ON_OFF    0x40   /* Horizontal mirror image ON/OFF selection */
+#define VFLIP_IMG       0x80   /* Vertical flip image ON/OFF selection */
+#define HFLIP_IMG       0x40   /* Horizontal mirror image ON/OFF selection */
 #define SWAP_RGB        0x20   /* Swap B/R  output sequence in RGB mode */
 #define SWAP_YUV        0x10   /* Swap Y/UV output sequence in YUV mode */
 #define SWAP_ML         0x08   /* Swap output MSB/LSB */
 
 /* COM5 */
 #define AFR_ON_OFF      0x80   /* Auto frame rate control ON/OFF selection */
-#define AFR_SPPED       0x40   /* Auto frame rate control speed slection */
+#define AFR_SPPED       0x40   /* Auto frame rate control speed selection */
                                /* Auto frame rate max rate control */
 #define AFR_NO_RATE     0x00   /*     No  reduction of frame rate */
 #define AFR_1p2         0x10   /*     Max reduction to 1/2 frame rate */
 #define SLCT_QVGA       0x40   /*   1 : QVGA */
 #define ITU656_ON_OFF   0x20   /* ITU656 protocol ON/OFF selection */
                                /* RGB output format control */
+#define FMT_MASK        0x0c   /*      Mask of color format */
 #define FMT_GBR422      0x00   /*      00 : GBR 4:2:2 */
 #define FMT_RGB565      0x04   /*      01 : RGB 565 */
 #define FMT_RGB555      0x08   /*      10 : RGB 555 */
 #define FMT_RGB444      0x0c   /* 11 : RGB 444 */
                                /* Output format control */
+#define OFMT_MASK       0x03    /*      Mask of output format */
 #define OFMT_YUV        0x00   /*      00 : YUV */
 #define OFMT_P_BRAW     0x01   /*      01 : Processed Bayer RAW */
 #define OFMT_RGB        0x02   /*      10 : RGB */
 #define GAIN_2x         0x00   /*    000 :   2x */
 #define GAIN_4x         0x10   /*    001 :   4x */
 #define GAIN_8x         0x20   /*    010 :   8x */
-#define GAIN_16x        0x30   /* 011 :  16x */
+#define GAIN_16x        0x30   /*    011 :  16x */
 #define GAIN_32x        0x40   /*    100 :  32x */
 #define GAIN_64x        0x50   /* 101 :  64x */
 #define GAIN_128x       0x60   /* 110 : 128x */
 #define VOSZ_VGA        0xF0
 #define VOSZ_QVGA       0x78
 
+/* DSPAUTO (DSP Auto Function ON/OFF Control) */
+#define AWB_ACTRL       0x80 /* AWB auto threshold control */
+#define DENOISE_ACTRL   0x40 /* De-noise auto threshold control */
+#define EDGE_ACTRL      0x20 /* Edge enhancement auto strength control */
+#define UV_ACTRL        0x10 /* UV adjust auto slope control */
+#define SCAL0_ACTRL     0x08 /* Auto scaling factor control */
+#define SCAL1_2_ACTRL   0x04 /* Auto scaling factor control */
+
 /*
- * bit configure (32 bit)
- * this is used in struct ov772x_color_format :: option
+ * ID
  */
-#define OP_UV       0x00000001
-#define OP_SWAP_RGB 0x00000002
+#define OV7720  0x7720
+#define OV7725  0x7721
+#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
 
 /*
  * struct
@@ -354,10 +383,11 @@ struct regval_list {
 };
 
 struct ov772x_color_format {
-       char                     *name;
-       __u32                     fourcc;
-       const struct regval_list *regs;
-       unsigned int              option;
+       enum v4l2_mbus_pixelcode code;
+       enum v4l2_colorspace colorspace;
+       u8 dsp3;
+       u8 com3;
+       u8 com7;
 };
 
 struct ov772x_win_size {
@@ -369,55 +399,19 @@ struct ov772x_win_size {
 };
 
 struct ov772x_priv {
+       struct v4l2_subdev                subdev;
        struct ov772x_camera_info        *info;
-       struct i2c_client                *client;
-       struct soc_camera_device          icd;
-       const struct ov772x_color_format *fmt;
+       const struct ov772x_color_format *cfmt;
        const struct ov772x_win_size     *win;
+       int                               model;
+       unsigned short                    flag_vflip:1;
+       unsigned short                    flag_hflip:1;
+       /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
+       unsigned short                    band_filter;
 };
 
 #define ENDMARKER { 0xff, 0xff }
 
-static const struct regval_list ov772x_default_regs[] =
-{
-       { COM3,  0x00 },
-       { COM4,  PLL_4x | 0x01 },
-       { 0x16,  0x00 }, /* Mystery */
-       { COM11, 0x10 }, /* Mystery */
-       { 0x28,  0x00 }, /* Mystery */
-       { HREF,  0x00 },
-       { COM13, 0xe2 }, /* Mystery */
-       { AREF0, 0xef },
-       { AREF2, 0x60 },
-       { AREF6, 0x7a },
-       ENDMARKER,
-};
-
-/*
- * register setting for color format
- */
-static const struct regval_list ov772x_RGB555_regs[] = {
-       { COM7, FMT_RGB555 | OFMT_RGB },
-       ENDMARKER,
-};
-
-static const struct regval_list ov772x_RGB565_regs[] = {
-       { COM7, FMT_RGB565 | OFMT_RGB },
-       ENDMARKER,
-};
-
-static const struct regval_list ov772x_YYUV_regs[] = {
-       { COM3, SWAP_YUV },
-       { COM7, OFMT_YUV },
-       ENDMARKER,
-};
-
-static const struct regval_list ov772x_UVYY_regs[] = {
-       { COM7, OFMT_YUV },
-       ENDMARKER,
-};
-
-
 /*
  * register setting for window size
  */
@@ -442,83 +436,57 @@ static const struct regval_list ov772x_vga_regs[] = {
 };
 
 /*
- * supported format list
- */
-
-#define SETFOURCC(type) .name = (#type), .fourcc = (V4L2_PIX_FMT_ ## type)
-static const struct soc_camera_data_format ov772x_fmt_lists[] = {
-       {
-               SETFOURCC(YUYV),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-       },
-       {
-               SETFOURCC(YVYU),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-       },
-       {
-               SETFOURCC(UYVY),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-       },
-       {
-               SETFOURCC(RGB555),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-       {
-               SETFOURCC(RGB555X),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-       {
-               SETFOURCC(RGB565),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-       {
-               SETFOURCC(RGB565X),
-               .depth      = 16,
-               .colorspace = V4L2_COLORSPACE_SRGB,
-       },
-};
-
-/*
- * color format list
+ * supported color format list
  */
-#define T_YUYV 0
 static const struct ov772x_color_format ov772x_cfmts[] = {
-       [T_YUYV] = {
-               SETFOURCC(YUYV),
-               .regs   = ov772x_YYUV_regs,
+       {
+               .code           = V4L2_MBUS_FMT_YUYV8_2X8_LE,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+               .dsp3           = 0x0,
+               .com3           = SWAP_YUV,
+               .com7           = OFMT_YUV,
        },
        {
-               SETFOURCC(YVYU),
-               .regs   = ov772x_YYUV_regs,
-               .option = OP_UV,
+               .code           = V4L2_MBUS_FMT_YVYU8_2X8_LE,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+               .dsp3           = UV_ON,
+               .com3           = SWAP_YUV,
+               .com7           = OFMT_YUV,
        },
        {
-               SETFOURCC(UYVY),
-               .regs   = ov772x_UVYY_regs,
+               .code           = V4L2_MBUS_FMT_YUYV8_2X8_BE,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+               .dsp3           = 0x0,
+               .com3           = 0x0,
+               .com7           = OFMT_YUV,
        },
        {
-               SETFOURCC(RGB555),
-               .regs   = ov772x_RGB555_regs,
-               .option = OP_SWAP_RGB,
+               .code           = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = SWAP_RGB,
+               .com7           = FMT_RGB555 | OFMT_RGB,
        },
        {
-               SETFOURCC(RGB555X),
-               .regs   = ov772x_RGB555_regs,
+               .code           = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = 0x0,
+               .com7           = FMT_RGB555 | OFMT_RGB,
        },
        {
-               SETFOURCC(RGB565),
-               .regs   = ov772x_RGB565_regs,
-               .option = OP_SWAP_RGB,
+               .code           = V4L2_MBUS_FMT_RGB565_2X8_LE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = SWAP_RGB,
+               .com7           = FMT_RGB565 | OFMT_RGB,
        },
        {
-               SETFOURCC(RGB565X),
-               .regs   = ov772x_RGB565_regs,
+               .code           = V4L2_MBUS_FMT_RGB565_2X8_BE,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+               .dsp3           = 0x0,
+               .com3           = 0x0,
+               .com7           = FMT_RGB565 | OFMT_RGB,
        },
 };
 
@@ -549,11 +517,46 @@ static const struct ov772x_win_size ov772x_win_qvga = {
        .regs     = ov772x_qvga_regs,
 };
 
+static const struct v4l2_queryctrl ov772x_controls[] = {
+       {
+               .id             = V4L2_CID_VFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Flip Vertically",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+       },
+       {
+               .id             = V4L2_CID_HFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Flip Horizontally",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+       },
+       {
+               .id             = V4L2_CID_BAND_STOP_FILTER,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "Band-stop filter",
+               .minimum        = 0,
+               .maximum        = 256,
+               .step           = 1,
+               .default_value  = 0,
+       },
+};
 
 /*
  * general function
  */
 
+static struct ov772x_priv *to_ov772x(const struct i2c_client *client)
+{
+       return container_of(i2c_get_clientdata(client), struct ov772x_priv,
+                           subdev);
+}
+
 static int ov772x_write_array(struct i2c_client        *client,
                              const struct regval_list *vals)
 {
@@ -574,8 +577,11 @@ static int ov772x_mask_set(struct i2c_client *client,
                                          u8  set)
 {
        s32 val = i2c_smbus_read_byte_data(client, command);
+       if (val < 0)
+               return val;
+
        val &= ~mask;
-       val |=  set;
+       val |= set & mask;
 
        return i2c_smbus_write_byte_data(client, command, val);
 }
@@ -591,208 +597,443 @@ static int ov772x_reset(struct i2c_client *client)
  * soc_camera_ops function
  */
 
-static int ov772x_init(struct soc_camera_device *icd)
+static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
 {
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+
+       if (!enable) {
+               ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+               return 0;
+       }
+
+       if (!priv->win || !priv->cfmt) {
+               dev_err(&client->dev, "norm or win select error\n");
+               return -EPERM;
+       }
+
+       ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
+
+       dev_dbg(&client->dev, "format %d, win %s\n",
+               priv->cfmt->code, priv->win->name);
+
        return 0;
 }
 
-static int ov772x_release(struct soc_camera_device *icd)
+static int ov772x_set_bus_param(struct soc_camera_device *icd,
+                               unsigned long             flags)
 {
        return 0;
 }
 
-static int ov772x_start_capture(struct soc_camera_device *icd)
+static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
+{
+       struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+       unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+               SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+               SOCAM_DATA_ACTIVE_HIGH;
+
+       if (priv->info->flags & OV772X_FLAG_8BIT)
+               flags |= SOCAM_DATAWIDTH_8;
+       else
+               flags |= SOCAM_DATAWIDTH_10;
+
+       return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               ctrl->value = priv->flag_vflip;
+               break;
+       case V4L2_CID_HFLIP:
+               ctrl->value = priv->flag_hflip;
+               break;
+       case V4L2_CID_BAND_STOP_FILTER:
+               ctrl->value = priv->band_filter;
+               break;
+       }
+       return 0;
+}
+
+static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+       int ret = 0;
+       u8 val;
+
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               val = ctrl->value ? VFLIP_IMG : 0x00;
+               priv->flag_vflip = ctrl->value;
+               if (priv->info->flags & OV772X_FLAG_VFLIP)
+                       val ^= VFLIP_IMG;
+               ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val);
+               break;
+       case V4L2_CID_HFLIP:
+               val = ctrl->value ? HFLIP_IMG : 0x00;
+               priv->flag_hflip = ctrl->value;
+               if (priv->info->flags & OV772X_FLAG_HFLIP)
+                       val ^= HFLIP_IMG;
+               ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
+               break;
+       case V4L2_CID_BAND_STOP_FILTER:
+               if ((unsigned)ctrl->value > 256)
+                       ctrl->value = 256;
+               if (ctrl->value == priv->band_filter)
+                       break;
+               if (!ctrl->value) {
+                       /* Switch the filter off, it is on now */
+                       ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
+                       if (!ret)
+                               ret = ov772x_mask_set(client, COM8,
+                                                     BNDF_ON_OFF, 0);
+               } else {
+                       /* Switch the filter on, set AEC low limit */
+                       val = 256 - ctrl->value;
+                       ret = ov772x_mask_set(client, COM8,
+                                             BNDF_ON_OFF, BNDF_ON_OFF);
+                       if (!ret)
+                               ret = ov772x_mask_set(client, BDBASE,
+                                                     0xff, val);
+               }
+               if (!ret)
+                       priv->band_filter = ctrl->value;
+               break;
+       }
+
+       return ret;
+}
+
+static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
+                              struct v4l2_dbg_chip_ident *id)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+
+       id->ident    = priv->model;
+       id->revision = 0;
+
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov772x_g_register(struct v4l2_subdev *sd,
+                            struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = sd->priv;
+       int ret;
+
+       reg->size = 1;
+       if (reg->reg > 0xff)
+               return -EINVAL;
+
+       ret = i2c_smbus_read_byte_data(client, reg->reg);
+       if (ret < 0)
+               return ret;
+
+       reg->val = (__u64)ret;
+
+       return 0;
+}
+
+static int ov772x_s_register(struct v4l2_subdev *sd,
+                            struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = sd->priv;
+
+       if (reg->reg > 0xff ||
+           reg->val > 0xff)
+               return -EINVAL;
+
+       return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
+{
+       __u32 diff;
+       const struct ov772x_win_size *win;
+
+       /* default is QVGA */
+       diff = abs(width - ov772x_win_qvga.width) +
+               abs(height - ov772x_win_qvga.height);
+       win = &ov772x_win_qvga;
+
+       /* VGA */
+       if (diff >
+           abs(width  - ov772x_win_vga.width) +
+           abs(height - ov772x_win_vga.height))
+               win = &ov772x_win_vga;
+
+       return win;
+}
+
+static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
+                            enum v4l2_mbus_pixelcode code)
 {
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-       int                 ret;
+       struct ov772x_priv *priv = to_ov772x(client);
+       int ret = -EINVAL;
+       u8  val;
+       int i;
 
+       /*
+        * select format
+        */
+       priv->cfmt = NULL;
+       for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+               if (code == ov772x_cfmts[i].code) {
+                       priv->cfmt = ov772x_cfmts + i;
+                       break;
+               }
+       }
+       if (!priv->cfmt)
+               goto ov772x_set_fmt_error;
 
-       if (!priv->win)
-               priv->win = &ov772x_win_vga;
-       if (!priv->fmt)
-               priv->fmt = &ov772x_cfmts[T_YUYV];
+       /*
+        * select win
+        */
+       priv->win = ov772x_select_win(*width, *height);
 
        /*
         * reset hardware
         */
-       ov772x_reset(priv->client);
-       ret = ov772x_write_array(priv->client, ov772x_default_regs);
-       if (ret < 0)
-               goto start_end;
+       ov772x_reset(client);
 
        /*
-        * set color format
+        * Edge Ctrl
         */
-       ret = ov772x_write_array(priv->client, priv->fmt->regs);
-       if (ret < 0)
-               goto start_end;
+       if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
+
+               /*
+                * Manual Edge Control Mode
+                *
+                * Edge auto strength bit is set by default.
+                * Remove it when manual mode.
+                */
+
+               ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+
+               ret = ov772x_mask_set(client,
+                                     EDGE_TRSHLD, EDGE_THRESHOLD_MASK,
+                                     priv->info->edgectrl.threshold);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+
+               ret = ov772x_mask_set(client,
+                                     EDGE_STRNGT, EDGE_STRENGTH_MASK,
+                                     priv->info->edgectrl.strength);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+
+       } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
+               /*
+                * Auto Edge Control Mode
+                *
+                * set upper and lower limit
+                */
+               ret = ov772x_mask_set(client,
+                                     EDGE_UPPER, EDGE_UPPER_MASK,
+                                     priv->info->edgectrl.upper);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+
+               ret = ov772x_mask_set(client,
+                                     EDGE_LOWER, EDGE_LOWER_MASK,
+                                     priv->info->edgectrl.lower);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+       }
 
        /*
         * set size format
         */
-       ret = ov772x_write_array(priv->client, priv->win->regs);
+       ret = ov772x_write_array(client, priv->win->regs);
        if (ret < 0)
-               goto start_end;
+               goto ov772x_set_fmt_error;
+
+       /*
+        * set DSP_CTRL3
+        */
+       val = priv->cfmt->dsp3;
+       if (val) {
+               ret = ov772x_mask_set(client,
+                                     DSP_CTRL3, UV_MASK, val);
+               if (ret < 0)
+                       goto ov772x_set_fmt_error;
+       }
 
        /*
-        * set COM7 bit ( QVGA or VGA )
+        * set COM3
         */
-       ret = ov772x_mask_set(priv->client,
-                             COM7, SLCT_MASK, priv->win->com7_bit);
+       val = priv->cfmt->com3;
+       if (priv->info->flags & OV772X_FLAG_VFLIP)
+               val |= VFLIP_IMG;
+       if (priv->info->flags & OV772X_FLAG_HFLIP)
+               val |= HFLIP_IMG;
+       if (priv->flag_vflip)
+               val ^= VFLIP_IMG;
+       if (priv->flag_hflip)
+               val ^= HFLIP_IMG;
+
+       ret = ov772x_mask_set(client,
+                             COM3, SWAP_MASK | IMG_MASK, val);
        if (ret < 0)
-               goto start_end;
+               goto ov772x_set_fmt_error;
 
        /*
-        * set UV setting
+        * set COM7
         */
-       if (priv->fmt->option & OP_UV) {
-               ret = ov772x_mask_set(priv->client,
-                                     DSP_CTRL3, UV_MASK, UV_ON);
-               if (ret < 0)
-                       goto start_end;
-       }
+       val = priv->win->com7_bit | priv->cfmt->com7;
+       ret = ov772x_mask_set(client,
+                             COM7, SLCT_MASK | FMT_MASK | OFMT_MASK,
+                             val);
+       if (ret < 0)
+               goto ov772x_set_fmt_error;
 
        /*
-        * set SWAP setting
+        * set COM8
         */
-       if (priv->fmt->option & OP_SWAP_RGB) {
-               ret = ov772x_mask_set(priv->client,
-                                     COM3, SWAP_MASK, SWAP_RGB);
+       if (priv->band_filter) {
+               ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+               if (!ret)
+                       ret = ov772x_mask_set(client, BDBASE,
+                                             0xff, 256 - priv->band_filter);
                if (ret < 0)
-                       goto start_end;
+                       goto ov772x_set_fmt_error;
        }
 
-       dev_info(&icd->dev,
-                "format %s, win %s\n", priv->fmt->name, priv->win->name);
+       *width = priv->win->width;
+       *height = priv->win->height;
+
+       return ret;
+
+ov772x_set_fmt_error:
 
-start_end:
-       priv->fmt = NULL;
+       ov772x_reset(client);
        priv->win = NULL;
+       priv->cfmt = NULL;
 
        return ret;
 }
 
-static int ov772x_stop_capture(struct soc_camera_device *icd)
+static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-       ov772x_reset(priv->client);
-       return 0;
-}
+       a->c.left       = 0;
+       a->c.top        = 0;
+       a->c.width      = VGA_WIDTH;
+       a->c.height     = VGA_HEIGHT;
+       a->type         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
-static int ov772x_set_bus_param(struct soc_camera_device *icd,
-                               unsigned long             flags)
-{
        return 0;
 }
 
-static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
-{
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-
-       return  SOCAM_PCLK_SAMPLE_RISING |
-               SOCAM_HSYNC_ACTIVE_HIGH  |
-               SOCAM_VSYNC_ACTIVE_HIGH  |
-               SOCAM_MASTER             |
-               priv->info->buswidth;
-}
-
-static int ov772x_get_chip_id(struct soc_camera_device *icd,
-                             struct v4l2_chip_ident   *id)
+static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
-       id->ident    = V4L2_IDENT_OV772X;
-       id->revision = 0;
+       a->bounds.left                  = 0;
+       a->bounds.top                   = 0;
+       a->bounds.width                 = VGA_WIDTH;
+       a->bounds.height                = VGA_HEIGHT;
+       a->defrect                      = a->bounds;
+       a->type                         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       a->pixelaspect.numerator        = 1;
+       a->pixelaspect.denominator      = 1;
 
        return 0;
 }
 
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov772x_get_register(struct soc_camera_device *icd,
-                              struct v4l2_register *reg)
+static int ov772x_g_fmt(struct v4l2_subdev *sd,
+                       struct v4l2_mbus_framefmt *mf)
 {
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-       int                 ret;
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
 
-       if (reg->reg > 0xff)
-               return -EINVAL;
-
-       ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
-       if (ret < 0)
-               return ret;
+       if (!priv->win || !priv->cfmt) {
+               u32 width = VGA_WIDTH, height = VGA_HEIGHT;
+               int ret = ov772x_set_params(client, &width, &height,
+                                           V4L2_MBUS_FMT_YUYV8_2X8_LE);
+               if (ret < 0)
+                       return ret;
+       }
 
-       reg->val = (__u64)ret;
+       mf->width       = priv->win->width;
+       mf->height      = priv->win->height;
+       mf->code        = priv->cfmt->code;
+       mf->colorspace  = priv->cfmt->colorspace;
+       mf->field       = V4L2_FIELD_NONE;
 
        return 0;
 }
 
-static int ov772x_set_register(struct soc_camera_device *icd,
-                              struct v4l2_register *reg)
+static int ov772x_s_fmt(struct v4l2_subdev *sd,
+                       struct v4l2_mbus_framefmt *mf)
 {
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+       int ret = ov772x_set_params(client, &mf->width, &mf->height,
+                                   mf->code);
 
-       if (reg->reg > 0xff ||
-           reg->val > 0xff)
-               return -EINVAL;
+       if (!ret)
+               mf->colorspace = priv->cfmt->colorspace;
 
-       return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+       return ret;
 }
-#endif
 
-static int ov772x_set_fmt_cap(struct soc_camera_device *icd,
-                             __u32                     pixfmt,
-                             struct v4l2_rect         *rect)
+static int ov772x_try_fmt(struct v4l2_subdev *sd,
+                         struct v4l2_mbus_framefmt *mf)
 {
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-       int ret = -EINVAL;
+       struct i2c_client *client = sd->priv;
+       struct ov772x_priv *priv = to_ov772x(client);
+       const struct ov772x_win_size *win;
        int i;
 
        /*
-        * select format
+        * select suitable win
         */
-       priv->fmt = NULL;
-       for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
-               if (pixfmt == ov772x_cfmts[i].fourcc) {
-                       priv->fmt = ov772x_cfmts + i;
-                       ret = 0;
-                       break;
-               }
-       }
+       win = ov772x_select_win(mf->width, mf->height);
 
-       return ret;
-}
-
-static int ov772x_try_fmt_cap(struct soc_camera_device *icd,
-                             struct v4l2_format       *f)
-{
-       struct v4l2_pix_format *pix  = &f->fmt.pix;
-       struct ov772x_priv     *priv;
+       mf->width       = win->width;
+       mf->height      = win->height;
+       mf->field       = V4L2_FIELD_NONE;
 
-       priv = container_of(icd, struct ov772x_priv, icd);
-
-       /* QVGA */
-       if (pix->width  <= ov772x_win_qvga.width ||
-           pix->height <= ov772x_win_qvga.height) {
-               priv->win   = &ov772x_win_qvga;
-               pix->width  =  ov772x_win_qvga.width;
-               pix->height =  ov772x_win_qvga.height;
-       }
+       for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++)
+               if (mf->code == ov772x_cfmts[i].code)
+                       break;
 
-       /* VGA */
-       else if (pix->width  <= ov772x_win_vga.width ||
-                pix->height <= ov772x_win_vga.height) {
-               priv->win   = &ov772x_win_vga;
-               pix->width  =  ov772x_win_vga.width;
-               pix->height =  ov772x_win_vga.height;
+       if (i == ARRAY_SIZE(ov772x_cfmts)) {
+               /* Unsupported format requested. Propose either */
+               if (priv->cfmt) {
+                       /* the current one or */
+                       mf->colorspace = priv->cfmt->colorspace;
+                       mf->code = priv->cfmt->code;
+               } else {
+                       /* the default one */
+                       mf->colorspace = ov772x_cfmts[0].colorspace;
+                       mf->code = ov772x_cfmts[0].code;
+               }
+       } else {
+               /* Also return the colorspace */
+               mf->colorspace  = ov772x_cfmts[i].colorspace;
        }
 
-       pix->field = V4L2_FIELD_NONE;
-
        return 0;
 }
 
-static int ov772x_video_probe(struct soc_camera_device *icd)
+static int ov772x_video_probe(struct soc_camera_device *icd,
+                             struct i2c_client *client)
 {
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+       struct ov772x_priv *priv = to_ov772x(client);
        u8                  pid, ver;
+       const char         *devname;
 
        /*
         * We must have a parent by now. And it cannot be a wrong one.
@@ -803,88 +1044,99 @@ static int ov772x_video_probe(struct soc_camera_device *icd)
                return -ENODEV;
 
        /*
-        * ov772x only use 8 or 10 bit bus width
-        */
-       if (SOCAM_DATAWIDTH_10 != priv->info->buswidth &&
-           SOCAM_DATAWIDTH_8  != priv->info->buswidth) {
-               dev_err(&icd->dev, "bus width error\n");
-               return -ENODEV;
-       }
-
-       icd->formats     = ov772x_fmt_lists;
-       icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists);
-
-       if (priv->info->link.power)
-               priv->info->link.power(&priv->client->dev, 1);
-
-       /*
         * check and show product ID and manufacturer ID
         */
-       pid = i2c_smbus_read_byte_data(priv->client, PID);
-       ver = i2c_smbus_read_byte_data(priv->client, VER);
-       if (pid != 0x77 ||
-           ver != 0x21) {
-               if (priv->info->link.power)
-                       priv->info->link.power(&priv->client->dev, 0);
+       pid = i2c_smbus_read_byte_data(client, PID);
+       ver = i2c_smbus_read_byte_data(client, VER);
+
+       switch (VERSION(pid, ver)) {
+       case OV7720:
+               devname     = "ov7720";
+               priv->model = V4L2_IDENT_OV7720;
+               break;
+       case OV7725:
+               devname     = "ov7725";
+               priv->model = V4L2_IDENT_OV7725;
+               break;
+       default:
+               dev_err(&client->dev,
+                       "Product ID error %x:%x\n", pid, ver);
                return -ENODEV;
        }
 
-       dev_info(&icd->dev,
-                "ov772x Product ID %0x:%0x Manufacturer ID %x:%x\n",
+       dev_info(&client->dev,
+                "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+                devname,
                 pid,
                 ver,
-                i2c_smbus_read_byte_data(priv->client, MIDH),
-                i2c_smbus_read_byte_data(priv->client, MIDL));
-
-
-       return soc_camera_video_start(icd);
-}
-
-static void ov772x_video_remove(struct soc_camera_device *icd)
-{
-       struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-
-       soc_camera_video_stop(icd);
-
-       if (priv->info->link.power)
-               priv->info->link.power(&priv->client->dev, 0);
+                i2c_smbus_read_byte_data(client, MIDH),
+                i2c_smbus_read_byte_data(client, MIDL));
 
+       return 0;
 }
 
 static struct soc_camera_ops ov772x_ops = {
-       .owner                  = THIS_MODULE,
-       .probe                  = ov772x_video_probe,
-       .remove                 = ov772x_video_remove,
-       .init                   = ov772x_init,
-       .release                = ov772x_release,
-       .start_capture          = ov772x_start_capture,
-       .stop_capture           = ov772x_stop_capture,
-       .set_fmt_cap            = ov772x_set_fmt_cap,
-       .try_fmt_cap            = ov772x_try_fmt_cap,
        .set_bus_param          = ov772x_set_bus_param,
        .query_bus_param        = ov772x_query_bus_param,
-       .get_chip_id            = ov772x_get_chip_id,
+       .controls               = ov772x_controls,
+       .num_controls           = ARRAY_SIZE(ov772x_controls),
+};
+
+static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+       .g_ctrl         = ov772x_g_ctrl,
+       .s_ctrl         = ov772x_s_ctrl,
+       .g_chip_ident   = ov772x_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-       .get_register           = ov772x_get_register,
-       .set_register           = ov772x_set_register,
+       .g_register     = ov772x_g_register,
+       .s_register     = ov772x_s_register,
 #endif
 };
 
+static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+                          enum v4l2_mbus_pixelcode *code)
+{
+       if (index >= ARRAY_SIZE(ov772x_cfmts))
+               return -EINVAL;
+
+       *code = ov772x_cfmts[index].code;
+       return 0;
+}
+
+static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
+       .s_stream       = ov772x_s_stream,
+       .g_mbus_fmt     = ov772x_g_fmt,
+       .s_mbus_fmt     = ov772x_s_fmt,
+       .try_mbus_fmt   = ov772x_try_fmt,
+       .cropcap        = ov772x_cropcap,
+       .g_crop         = ov772x_g_crop,
+       .enum_mbus_fmt  = ov772x_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov772x_subdev_ops = {
+       .core   = &ov772x_subdev_core_ops,
+       .video  = &ov772x_subdev_video_ops,
+};
+
 /*
  * i2c_driver function
  */
 
 static int ov772x_probe(struct i2c_client *client,
-                        const struct i2c_device_id *did)
+                       const struct i2c_device_id *did)
 {
        struct ov772x_priv        *priv;
-       struct ov772x_camera_info *info;
-       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;
        int                        ret;
 
-       info = client->dev.platform_data;
-       if (!info)
+       if (!icd) {
+               dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
+               return -EINVAL;
+       }
+
+       icl = to_soc_camera_link(icd);
+       if (!icl || !icl->priv)
                return -EINVAL;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -898,36 +1150,33 @@ static int ov772x_probe(struct i2c_client *client,
        if (!priv)
                return -ENOMEM;
 
-       priv->info   = info;
-       priv->client = client;
-       i2c_set_clientdata(client, priv);
+       priv->info = icl->priv;
 
-       icd             = &priv->icd;
-       icd->ops        = &ov772x_ops;
-       icd->control    = &client->dev;
-       icd->width_max  = MAX_WIDTH;
-       icd->height_max = MAX_HEIGHT;
-       icd->iface      = priv->info->link.bus_id;
+       v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
 
-       ret = soc_camera_device_register(icd);
+       icd->ops                = &ov772x_ops;
 
-       if (ret)
+       ret = ov772x_video_probe(icd, client);
+       if (ret) {
+               icd->ops = NULL;
                kfree(priv);
+       }
 
        return ret;
 }
 
 static int ov772x_remove(struct i2c_client *client)
 {
-       struct ov772x_priv *priv = i2c_get_clientdata(client);
+       struct ov772x_priv *priv = to_ov772x(client);
+       struct soc_camera_device *icd = client->dev.platform_data;
 
-       soc_camera_device_unregister(&priv->icd);
+       icd->ops = NULL;
        kfree(priv);
        return 0;
 }
 
 static const struct i2c_device_id ov772x_id[] = {
-       {"ov772x", 0},
+       { "ov772x", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ov772x_id);
@@ -947,7 +1196,6 @@ static struct i2c_driver ov772x_i2c_driver = {
 
 static int __init ov772x_module_init(void)
 {
-       printk(KERN_INFO "ov772x driver\n");
        return i2c_add_driver(&ov772x_i2c_driver);
 }