V4L/DVB: ov7670: Wire up V4L2_CID_GAIN and V4L2_CID_AUTOGAIN
authorJonathan Corbet <corbet@lwn.net>
Thu, 4 Mar 2010 21:50:34 +0000 (18:50 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 18 May 2010 03:51:21 +0000 (00:51 -0300)
Allow applications to play with the gain settings.

Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/ov7670.c

index 95507770f120703fc75dcdf65fca450e0e8fd70b..e7555d1f90e69629b06fac050546ca3807a54626 100644 (file)
@@ -1131,6 +1131,67 @@ static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
        return ret;
 }
 
+/*
+ * GAIN is split between REG_GAIN and REG_VREF[7:6].  If one believes
+ * the data sheet, the VREF parts should be the most significant, but
+ * experience shows otherwise.  There seems to be little value in
+ * messing with the VREF bits, so we leave them alone.
+ */
+static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
+{
+       int ret;
+       unsigned char gain;
+
+       ret = ov7670_read(sd, REG_GAIN, &gain);
+       *value = gain;
+       return ret;
+}
+
+static int ov7670_s_gain(struct v4l2_subdev *sd, int value)
+{
+       int ret;
+       unsigned char com8;
+
+       ret = ov7670_write(sd, REG_GAIN, value & 0xff);
+       /* Have to turn off AGC as well */
+       if (ret == 0) {
+               ret = ov7670_read(sd, REG_COM8, &com8);
+               ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC);
+       }
+       return ret;
+}
+
+/*
+ * Tweak autogain.
+ */
+static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value)
+{
+       int ret;
+       unsigned char com8;
+
+       ret = ov7670_read(sd, REG_COM8, &com8);
+       *value = (com8 & COM8_AGC) != 0;
+       return ret;
+}
+
+static int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
+{
+       int ret;
+       unsigned char com8;
+
+       ret = ov7670_read(sd, REG_COM8, &com8);
+       if (ret == 0) {
+               if (value)
+                       com8 |= COM8_AGC;
+               else
+                       com8 &= ~COM8_AGC;
+               ret = ov7670_write(sd, REG_COM8, com8);
+       }
+       return ret;
+}
+
+
+
 static int ov7670_queryctrl(struct v4l2_subdev *sd,
                struct v4l2_queryctrl *qc)
 {
@@ -1147,6 +1208,10 @@ static int ov7670_queryctrl(struct v4l2_subdev *sd,
                return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128);
        case V4L2_CID_HUE:
                return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0);
+       case V4L2_CID_GAIN:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+       case V4L2_CID_AUTOGAIN:
+               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
        }
        return -EINVAL;
 }
@@ -1166,6 +1231,10 @@ static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
                return ov7670_g_vflip(sd, &ctrl->value);
        case V4L2_CID_HFLIP:
                return ov7670_g_hflip(sd, &ctrl->value);
+       case V4L2_CID_GAIN:
+               return ov7670_g_gain(sd, &ctrl->value);
+       case V4L2_CID_AUTOGAIN:
+               return ov7670_g_autogain(sd, &ctrl->value);
        }
        return -EINVAL;
 }
@@ -1185,6 +1254,10 @@ static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
                return ov7670_s_vflip(sd, ctrl->value);
        case V4L2_CID_HFLIP:
                return ov7670_s_hflip(sd, ctrl->value);
+       case V4L2_CID_GAIN:
+               return ov7670_s_gain(sd, ctrl->value);
+       case V4L2_CID_AUTOGAIN:
+               return ov7670_s_autogain(sd, ctrl->value);
        }
        return -EINVAL;
 }