The ov7670 has the test pattern generator features.  This makes use of
it through V4L2_CID_TEST_PATTERN control.

Cc: Jonathan Corbet <cor...@lwn.net>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
 drivers/media/i2c/ov7670.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index b61d88e..c6c32f6 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -163,6 +163,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
 #define   DBLV_X6        0x10    /* clock x6 */
 #define   DBLV_X8        0x11    /* clock x8 */
 
+#define REG_SCALING_XSC        0x70    /* Test pattern and horizontal scale 
factor */
+#define   TEST_PATTTERN_0 0x80
+#define REG_SCALING_YSC        0x71    /* Test pattern and vertical scale 
factor */
+#define   TEST_PATTTERN_1 0x80
+
 #define REG_REG76      0x76    /* OV's name */
 #define   R76_BLKPCOR    0x80    /* Black pixel correction enable */
 #define   R76_WHTPCOR    0x40    /* White pixel correction enable */
@@ -292,7 +297,8 @@ static struct regval_list ov7670_default_regs[] = {
 
        { REG_COM3, 0 },        { REG_COM14, 0 },
        /* Mystery scaling numbers */
-       { 0x70, 0x3a },         { 0x71, 0x35 },
+       { REG_SCALING_XSC, 0x3a },
+       { REG_SCALING_YSC, 0x35 },
        { 0x72, 0x11 },         { 0x73, 0xf0 },
        { 0xa2, 0x02 },         { REG_COM10, 0x0 },
 
@@ -568,6 +574,19 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned 
char reg,
                return ov7670_write_i2c(sd, reg, value);
 }
 
+static int ov7670_update_bits(struct v4l2_subdev *sd, unsigned char reg,
+               unsigned char mask, unsigned char value)
+{
+       unsigned char orig;
+       int ret;
+
+       ret = ov7670_read(sd, reg, &orig);
+       if (ret)
+               return ret;
+
+       return ov7670_write(sd, reg, (orig & ~mask) | (value & mask));
+}
+
 /*
  * Write a list of register settings; ff/ff stops the process.
  */
@@ -1470,6 +1489,25 @@ static int ov7670_s_autoexp(struct v4l2_subdev *sd,
        return ret;
 }
 
+static const char * const ov7670_test_pattern_menu[] = {
+       "No test output",
+       "Shifting \"1\"",
+       "8-bar color bar",
+       "Fade to gray color bar",
+};
+
+static int ov7670_s_test_pattern(struct v4l2_subdev *sd, int value)
+{
+       int ret;
+
+       ret = ov7670_update_bits(sd, REG_SCALING_XSC, TEST_PATTTERN_0,
+                               value & BIT(0) ? TEST_PATTTERN_0 : 0);
+       if (ret)
+               return ret;
+
+       return ov7670_update_bits(sd, REG_SCALING_YSC, TEST_PATTTERN_1,
+                               value & BIT(1) ? TEST_PATTTERN_1 : 0);
+}
 
 static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
@@ -1516,6 +1554,8 @@ static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl)
                        return ov7670_s_exp(sd, info->exposure->val);
                }
                return ov7670_s_autoexp(sd, ctrl->val);
+       case V4L2_CID_TEST_PATTERN:
+               return ov7670_s_test_pattern(sd, ctrl->val);
        }
        return -EINVAL;
 }
@@ -1770,6 +1810,10 @@ static int ov7670_probe(struct i2c_client *client,
        info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, 
&ov7670_ctrl_ops,
                        V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
                        V4L2_EXPOSURE_AUTO);
+       v4l2_ctrl_new_std_menu_items(&info->hdl, &ov7670_ctrl_ops,
+                       V4L2_CID_TEST_PATTERN,
+                       ARRAY_SIZE(ov7670_test_pattern_menu) - 1, 0, 0,
+                       ov7670_test_pattern_menu);
        sd->ctrl_handler = &info->hdl;
        if (info->hdl.error) {
                ret = info->hdl.error;
-- 
2.7.4

Reply via email to