diff options
author | Daniel Drake <dsd@laptop.org> | 2010-10-19 17:24:05 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-23 11:09:23 -0400 |
commit | 75e2bdad8901a0b599e01a96229be922eef1e488 (patch) | |
tree | 6839dfdef15550a3eac76932a15d9a3da943e334 /drivers/media | |
parent | f8c612744359179c6c90faccb6b0ca185cb65d15 (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.c | 133 | ||||
-rw-r--r-- | drivers/media/video/ov7670.h | 20 |
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 | ||
24 | MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); | 25 | MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); |
25 | MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); | 26 | MODULE_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 | ||
204 | static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) | 204 | static 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 | 418 | static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, |
419 | static 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 | ||
434 | static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | 433 | static 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 | */ |
449 | static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, | 447 | static 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 | ||
482 | static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | 480 | static 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 | ||
500 | static 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 | |||
510 | static 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, | |||
912 | static int ov7670_enum_framesizes(struct v4l2_subdev *sd, | 928 | static 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 | ||
1452 | static 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 |
1421 | static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) | 1494 | static 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 | |||
13 | struct 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 | ||