aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorDaniel Drake <dsd@laptop.org>2010-10-19 17:24:05 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-23 11:09:23 -0400
commit75e2bdad8901a0b599e01a96229be922eef1e488 (patch)
tree6839dfdef15550a3eac76932a15d9a3da943e334 /drivers/media
parentf8c612744359179c6c90faccb6b0ca185cb65d15 (diff)
[media] ov7670: allow configuration of image size, clock speed, and I/O method
These parameters need to be configurable based on the host system. They can now be communicated through the s_config call. The old CONFIG_OLPC_XO_1 selector was not correct; this kind of arrangement wouldn't allow for a universal kernel that would work on both laptops. Certain parts of the probe routine had to be moved later (into s_config), because we can't do any I/O until we know which I/O method has been selected through this mechanism. Signed-off-by: Daniel Drake <dsd@laptop.org> Acked-by: Jonathan Corbet <corbet@lwn.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/ov7670.c133
-rw-r--r--drivers/media/video/ov7670.h20
2 files changed, 115 insertions, 38 deletions
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 0b78f33a69b4..c881a64b41fd 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -20,6 +20,7 @@
20#include <media/v4l2-chip-ident.h> 20#include <media/v4l2-chip-ident.h>
21#include <media/v4l2-mediabus.h> 21#include <media/v4l2-mediabus.h>
22 22
23#include "ov7670.h"
23 24
24MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); 25MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
25MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); 26MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
@@ -43,11 +44,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
43#define QCIF_HEIGHT 144 44#define QCIF_HEIGHT 144
44 45
45/* 46/*
46 * Our nominal (default) frame rate.
47 */
48#define OV7670_FRAME_RATE 30
49
50/*
51 * The 7670 sits on i2c with ID 0x42 47 * The 7670 sits on i2c with ID 0x42
52 */ 48 */
53#define OV7670_I2C_ADDR 0x42 49#define OV7670_I2C_ADDR 0x42
@@ -198,7 +194,11 @@ struct ov7670_info {
198 struct ov7670_format_struct *fmt; /* Current format */ 194 struct ov7670_format_struct *fmt; /* Current format */
199 unsigned char sat; /* Saturation value */ 195 unsigned char sat; /* Saturation value */
200 int hue; /* Hue value */ 196 int hue; /* Hue value */
197 int min_width; /* Filter out smaller sizes */
198 int min_height; /* Filter out smaller sizes */
199 int clock_speed; /* External clock speed (MHz) */
201 u8 clkrc; /* Clock divider value */ 200 u8 clkrc; /* Clock divider value */
201 bool use_smbus; /* Use smbus I/O instead of I2C */
202}; 202};
203 203
204static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) 204static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
@@ -415,8 +415,7 @@ static struct regval_list ov7670_fmt_raw[] = {
415 * ov7670 is not really an SMBUS device, though, so the communication 415 * ov7670 is not really an SMBUS device, though, so the communication
416 * is not always entirely reliable. 416 * is not always entirely reliable.
417 */ 417 */
418#ifdef CONFIG_OLPC_XO_1 418static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
419static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
420 unsigned char *value) 419 unsigned char *value)
421{ 420{
422 struct i2c_client *client = v4l2_get_subdevdata(sd); 421 struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -431,7 +430,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
431} 430}
432 431
433 432
434static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 433static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
435 unsigned char value) 434 unsigned char value)
436{ 435{
437 struct i2c_client *client = v4l2_get_subdevdata(sd); 436 struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -442,11 +441,10 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
442 return ret; 441 return ret;
443} 442}
444 443
445#else /* ! CONFIG_OLPC_XO_1 */
446/* 444/*
447 * On most platforms, we'd rather do straight i2c I/O. 445 * On most platforms, we'd rather do straight i2c I/O.
448 */ 446 */
449static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 447static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
450 unsigned char *value) 448 unsigned char *value)
451{ 449{
452 struct i2c_client *client = v4l2_get_subdevdata(sd); 450 struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -479,7 +477,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
479} 477}
480 478
481 479
482static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 480static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
483 unsigned char value) 481 unsigned char value)
484{ 482{
485 struct i2c_client *client = v4l2_get_subdevdata(sd); 483 struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -498,8 +496,26 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
498 msleep(5); /* Wait for reset to run */ 496 msleep(5); /* Wait for reset to run */
499 return ret; 497 return ret;
500} 498}
501#endif /* CONFIG_OLPC_XO_1 */
502 499
500static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
501 unsigned char *value)
502{
503 struct ov7670_info *info = to_state(sd);
504 if (info->use_smbus)
505 return ov7670_read_smbus(sd, reg, value);
506 else
507 return ov7670_read_i2c(sd, reg, value);
508}
509
510static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
511 unsigned char value)
512{
513 struct ov7670_info *info = to_state(sd);
514 if (info->use_smbus)
515 return ov7670_write_smbus(sd, reg, value);
516 else
517 return ov7670_write_i2c(sd, reg, value);
518}
503 519
504/* 520/*
505 * Write a list of register settings; ff/ff stops the process. 521 * Write a list of register settings; ff/ff stops the process.
@@ -854,7 +870,7 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
854 memset(cp, 0, sizeof(struct v4l2_captureparm)); 870 memset(cp, 0, sizeof(struct v4l2_captureparm));
855 cp->capability = V4L2_CAP_TIMEPERFRAME; 871 cp->capability = V4L2_CAP_TIMEPERFRAME;
856 cp->timeperframe.numerator = 1; 872 cp->timeperframe.numerator = 1;
857 cp->timeperframe.denominator = OV7670_FRAME_RATE; 873 cp->timeperframe.denominator = info->clock_speed;
858 if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) 874 if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
859 cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); 875 cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE);
860 return 0; 876 return 0;
@@ -875,14 +891,14 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
875 if (tpf->numerator == 0 || tpf->denominator == 0) 891 if (tpf->numerator == 0 || tpf->denominator == 0)
876 div = 1; /* Reset to full rate */ 892 div = 1; /* Reset to full rate */
877 else 893 else
878 div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; 894 div = (tpf->numerator * info->clock_speed) / tpf->denominator;
879 if (div == 0) 895 if (div == 0)
880 div = 1; 896 div = 1;
881 else if (div > CLK_SCALE) 897 else if (div > CLK_SCALE)
882 div = CLK_SCALE; 898 div = CLK_SCALE;
883 info->clkrc = (info->clkrc & 0x80) | div; 899 info->clkrc = (info->clkrc & 0x80) | div;
884 tpf->numerator = 1; 900 tpf->numerator = 1;
885 tpf->denominator = OV7670_FRAME_RATE/div; 901 tpf->denominator = info->clock_speed / div;
886 return ov7670_write(sd, REG_CLKRC, info->clkrc); 902 return ov7670_write(sd, REG_CLKRC, info->clkrc);
887} 903}
888 904
@@ -912,14 +928,30 @@ static int ov7670_enum_frameintervals(struct v4l2_subdev *sd,
912static int ov7670_enum_framesizes(struct v4l2_subdev *sd, 928static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
913 struct v4l2_frmsizeenum *fsize) 929 struct v4l2_frmsizeenum *fsize)
914{ 930{
931 struct ov7670_info *info = to_state(sd);
932 int i;
933 int num_valid = -1;
915 __u32 index = fsize->index; 934 __u32 index = fsize->index;
916 if (index >= N_WIN_SIZES)
917 return -EINVAL;
918 935
919 fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 936 /*
920 fsize->discrete.width = ov7670_win_sizes[index].width; 937 * If a minimum width/height was requested, filter out the capture
921 fsize->discrete.height = ov7670_win_sizes[index].height; 938 * windows that fall outside that.
922 return 0; 939 */
940 for (i = 0; i < N_WIN_SIZES; i++) {
941 struct ov7670_win_size *win = &ov7670_win_sizes[index];
942 if (info->min_width && win->width < info->min_width)
943 continue;
944 if (info->min_height && win->height < info->min_height)
945 continue;
946 if (index == ++num_valid) {
947 fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
948 fsize->discrete.width = win->width;
949 fsize->discrete.height = win->height;
950 return 0;
951 }
952 }
953
954 return -EINVAL;
923} 955}
924 956
925/* 957/*
@@ -1417,6 +1449,47 @@ static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
1417 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); 1449 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
1418} 1450}
1419 1451
1452static int ov7670_s_config(struct v4l2_subdev *sd, int dumb, void *data)
1453{
1454 struct i2c_client *client = v4l2_get_subdevdata(sd);
1455 struct ov7670_config *config = data;
1456 struct ov7670_info *info = to_state(sd);
1457 int ret;
1458
1459 info->clock_speed = 30; /* default: a guess */
1460
1461 /*
1462 * Must apply configuration before initializing device, because it
1463 * selects I/O method.
1464 */
1465 if (config) {
1466 info->min_width = config->min_width;
1467 info->min_height = config->min_height;
1468 info->use_smbus = config->use_smbus;
1469
1470 if (config->clock_speed)
1471 info->clock_speed = config->clock_speed;
1472 }
1473
1474 /* Make sure it's an ov7670 */
1475 ret = ov7670_detect(sd);
1476 if (ret) {
1477 v4l_dbg(1, debug, client,
1478 "chip found @ 0x%x (%s) is not an ov7670 chip.\n",
1479 client->addr << 1, client->adapter->name);
1480 kfree(info);
1481 return ret;
1482 }
1483 v4l_info(client, "chip found @ 0x%02x (%s)\n",
1484 client->addr << 1, client->adapter->name);
1485
1486 info->fmt = &ov7670_formats[0];
1487 info->sat = 128; /* Review this */
1488 info->clkrc = info->clock_speed / 30;
1489
1490 return 0;
1491}
1492
1420#ifdef CONFIG_VIDEO_ADV_DEBUG 1493#ifdef CONFIG_VIDEO_ADV_DEBUG
1421static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 1494static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1422{ 1495{
@@ -1455,6 +1528,7 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = {
1455 .s_ctrl = ov7670_s_ctrl, 1528 .s_ctrl = ov7670_s_ctrl,
1456 .queryctrl = ov7670_queryctrl, 1529 .queryctrl = ov7670_queryctrl,
1457 .reset = ov7670_reset, 1530 .reset = ov7670_reset,
1531 .s_config = ov7670_s_config,
1458 .init = ov7670_init, 1532 .init = ov7670_init,
1459#ifdef CONFIG_VIDEO_ADV_DEBUG 1533#ifdef CONFIG_VIDEO_ADV_DEBUG
1460 .g_register = ov7670_g_register, 1534 .g_register = ov7670_g_register,
@@ -1484,7 +1558,6 @@ static int ov7670_probe(struct i2c_client *client,
1484{ 1558{
1485 struct v4l2_subdev *sd; 1559 struct v4l2_subdev *sd;
1486 struct ov7670_info *info; 1560 struct ov7670_info *info;
1487 int ret;
1488 1561
1489 info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); 1562 info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
1490 if (info == NULL) 1563 if (info == NULL)
@@ -1492,22 +1565,6 @@ static int ov7670_probe(struct i2c_client *client,
1492 sd = &info->sd; 1565 sd = &info->sd;
1493 v4l2_i2c_subdev_init(sd, client, &ov7670_ops); 1566 v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
1494 1567
1495 /* Make sure it's an ov7670 */
1496 ret = ov7670_detect(sd);
1497 if (ret) {
1498 v4l_dbg(1, debug, client,
1499 "chip found @ 0x%x (%s) is not an ov7670 chip.\n",
1500 client->addr << 1, client->adapter->name);
1501 kfree(info);
1502 return ret;
1503 }
1504 v4l_info(client, "chip found @ 0x%02x (%s)\n",
1505 client->addr << 1, client->adapter->name);
1506
1507 info->fmt = &ov7670_formats[0];
1508 info->sat = 128; /* Review this */
1509 info->clkrc = 1; /* 30fps */
1510
1511 return 0; 1568 return 0;
1512} 1569}
1513 1570
diff --git a/drivers/media/video/ov7670.h b/drivers/media/video/ov7670.h
new file mode 100644
index 000000000000..b133bc123031
--- /dev/null
+++ b/drivers/media/video/ov7670.h
@@ -0,0 +1,20 @@
1/*
2 * A V4L2 driver for OmniVision OV7670 cameras.
3 *
4 * Copyright 2010 One Laptop Per Child
5 *
6 * This file may be distributed under the terms of the GNU General
7 * Public License, version 2.
8 */
9
10#ifndef __OV7670_H
11#define __OV7670_H
12
13struct ov7670_config {
14 int min_width; /* Filter out smaller sizes */
15 int min_height; /* Filter out smaller sizes */
16 int clock_speed; /* External clock speed (MHz) */
17 bool use_smbus; /* Use smbus I/O instead of I2C */
18};
19
20#endif