Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Daniel Drake and committed by
Mauro Carvalho Chehab
75e2bdad f8c61274

+115 -38
+95 -38
drivers/media/video/ov7670.c
··· 20 20 #include <media/v4l2-chip-ident.h> 21 21 #include <media/v4l2-mediabus.h> 22 22 23 + #include "ov7670.h" 23 24 24 25 MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); 25 26 MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); ··· 42 41 #define CIF_HEIGHT 288 43 42 #define QCIF_WIDTH 176 44 43 #define QCIF_HEIGHT 144 45 - 46 - /* 47 - * Our nominal (default) frame rate. 48 - */ 49 - #define OV7670_FRAME_RATE 30 50 44 51 45 /* 52 46 * The 7670 sits on i2c with ID 0x42 ··· 194 198 struct ov7670_format_struct *fmt; /* Current format */ 195 199 unsigned char sat; /* Saturation value */ 196 200 int hue; /* Hue value */ 201 + int min_width; /* Filter out smaller sizes */ 202 + int min_height; /* Filter out smaller sizes */ 203 + int clock_speed; /* External clock speed (MHz) */ 197 204 u8 clkrc; /* Clock divider value */ 205 + bool use_smbus; /* Use smbus I/O instead of I2C */ 198 206 }; 199 207 200 208 static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) ··· 415 415 * ov7670 is not really an SMBUS device, though, so the communication 416 416 * is not always entirely reliable. 417 417 */ 418 - #ifdef CONFIG_OLPC_XO_1 419 - static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 418 + static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, 420 419 unsigned char *value) 421 420 { 422 421 struct i2c_client *client = v4l2_get_subdevdata(sd); ··· 430 431 } 431 432 432 433 433 - static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 434 + static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, 434 435 unsigned char value) 435 436 { 436 437 struct i2c_client *client = v4l2_get_subdevdata(sd); ··· 441 442 return ret; 442 443 } 443 444 444 - #else /* ! CONFIG_OLPC_XO_1 */ 445 445 /* 446 446 * On most platforms, we'd rather do straight i2c I/O. 447 447 */ 448 - static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 448 + static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, 449 449 unsigned char *value) 450 450 { 451 451 struct i2c_client *client = v4l2_get_subdevdata(sd); ··· 477 479 } 478 480 479 481 480 - static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 482 + static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, 481 483 unsigned char value) 482 484 { 483 485 struct i2c_client *client = v4l2_get_subdevdata(sd); ··· 496 498 msleep(5); /* Wait for reset to run */ 497 499 return ret; 498 500 } 499 - #endif /* CONFIG_OLPC_XO_1 */ 500 501 502 + static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 503 + unsigned char *value) 504 + { 505 + struct ov7670_info *info = to_state(sd); 506 + if (info->use_smbus) 507 + return ov7670_read_smbus(sd, reg, value); 508 + else 509 + return ov7670_read_i2c(sd, reg, value); 510 + } 511 + 512 + static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 513 + unsigned char value) 514 + { 515 + struct ov7670_info *info = to_state(sd); 516 + if (info->use_smbus) 517 + return ov7670_write_smbus(sd, reg, value); 518 + else 519 + return ov7670_write_i2c(sd, reg, value); 520 + } 501 521 502 522 /* 503 523 * Write a list of register settings; ff/ff stops the process. ··· 870 854 memset(cp, 0, sizeof(struct v4l2_captureparm)); 871 855 cp->capability = V4L2_CAP_TIMEPERFRAME; 872 856 cp->timeperframe.numerator = 1; 873 - cp->timeperframe.denominator = OV7670_FRAME_RATE; 857 + cp->timeperframe.denominator = info->clock_speed; 874 858 if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) 875 859 cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); 876 860 return 0; ··· 891 875 if (tpf->numerator == 0 || tpf->denominator == 0) 892 876 div = 1; /* Reset to full rate */ 893 877 else 894 - div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; 878 + div = (tpf->numerator * info->clock_speed) / tpf->denominator; 895 879 if (div == 0) 896 880 div = 1; 897 881 else if (div > CLK_SCALE) 898 882 div = CLK_SCALE; 899 883 info->clkrc = (info->clkrc & 0x80) | div; 900 884 tpf->numerator = 1; 901 - tpf->denominator = OV7670_FRAME_RATE/div; 885 + tpf->denominator = info->clock_speed / div; 902 886 return ov7670_write(sd, REG_CLKRC, info->clkrc); 903 887 } 904 888 ··· 928 912 static int ov7670_enum_framesizes(struct v4l2_subdev *sd, 929 913 struct v4l2_frmsizeenum *fsize) 930 914 { 915 + struct ov7670_info *info = to_state(sd); 916 + int i; 917 + int num_valid = -1; 931 918 __u32 index = fsize->index; 932 - if (index >= N_WIN_SIZES) 933 - return -EINVAL; 934 919 935 - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 936 - fsize->discrete.width = ov7670_win_sizes[index].width; 937 - fsize->discrete.height = ov7670_win_sizes[index].height; 938 - return 0; 920 + /* 921 + * If a minimum width/height was requested, filter out the capture 922 + * windows that fall outside that. 923 + */ 924 + for (i = 0; i < N_WIN_SIZES; i++) { 925 + struct ov7670_win_size *win = &ov7670_win_sizes[index]; 926 + if (info->min_width && win->width < info->min_width) 927 + continue; 928 + if (info->min_height && win->height < info->min_height) 929 + continue; 930 + if (index == ++num_valid) { 931 + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 932 + fsize->discrete.width = win->width; 933 + fsize->discrete.height = win->height; 934 + return 0; 935 + } 936 + } 937 + 938 + return -EINVAL; 939 939 } 940 940 941 941 /* ··· 1449 1417 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); 1450 1418 } 1451 1419 1420 + static int ov7670_s_config(struct v4l2_subdev *sd, int dumb, void *data) 1421 + { 1422 + struct i2c_client *client = v4l2_get_subdevdata(sd); 1423 + struct ov7670_config *config = data; 1424 + struct ov7670_info *info = to_state(sd); 1425 + int ret; 1426 + 1427 + info->clock_speed = 30; /* default: a guess */ 1428 + 1429 + /* 1430 + * Must apply configuration before initializing device, because it 1431 + * selects I/O method. 1432 + */ 1433 + if (config) { 1434 + info->min_width = config->min_width; 1435 + info->min_height = config->min_height; 1436 + info->use_smbus = config->use_smbus; 1437 + 1438 + if (config->clock_speed) 1439 + info->clock_speed = config->clock_speed; 1440 + } 1441 + 1442 + /* Make sure it's an ov7670 */ 1443 + ret = ov7670_detect(sd); 1444 + if (ret) { 1445 + v4l_dbg(1, debug, client, 1446 + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", 1447 + client->addr << 1, client->adapter->name); 1448 + kfree(info); 1449 + return ret; 1450 + } 1451 + v4l_info(client, "chip found @ 0x%02x (%s)\n", 1452 + client->addr << 1, client->adapter->name); 1453 + 1454 + info->fmt = &ov7670_formats[0]; 1455 + info->sat = 128; /* Review this */ 1456 + info->clkrc = info->clock_speed / 30; 1457 + 1458 + return 0; 1459 + } 1460 + 1452 1461 #ifdef CONFIG_VIDEO_ADV_DEBUG 1453 1462 static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 1454 1463 { ··· 1528 1455 .s_ctrl = ov7670_s_ctrl, 1529 1456 .queryctrl = ov7670_queryctrl, 1530 1457 .reset = ov7670_reset, 1458 + .s_config = ov7670_s_config, 1531 1459 .init = ov7670_init, 1532 1460 #ifdef CONFIG_VIDEO_ADV_DEBUG 1533 1461 .g_register = ov7670_g_register, ··· 1558 1484 { 1559 1485 struct v4l2_subdev *sd; 1560 1486 struct ov7670_info *info; 1561 - int ret; 1562 1487 1563 1488 info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); 1564 1489 if (info == NULL) 1565 1490 return -ENOMEM; 1566 1491 sd = &info->sd; 1567 1492 v4l2_i2c_subdev_init(sd, client, &ov7670_ops); 1568 - 1569 - /* Make sure it's an ov7670 */ 1570 - ret = ov7670_detect(sd); 1571 - if (ret) { 1572 - v4l_dbg(1, debug, client, 1573 - "chip found @ 0x%x (%s) is not an ov7670 chip.\n", 1574 - client->addr << 1, client->adapter->name); 1575 - kfree(info); 1576 - return ret; 1577 - } 1578 - v4l_info(client, "chip found @ 0x%02x (%s)\n", 1579 - client->addr << 1, client->adapter->name); 1580 - 1581 - info->fmt = &ov7670_formats[0]; 1582 - info->sat = 128; /* Review this */ 1583 - info->clkrc = 1; /* 30fps */ 1584 1493 1585 1494 return 0; 1586 1495 }
+20
drivers/media/video/ov7670.h
··· 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