Em 19-10-2010 19:24, Daniel Drake escreveu: > 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. Both patches 1 and 2 seem to be doing the right thing, passing the board-specific parameters via s_config call. ACK. PS.: I'll wait for Jon's comments before merging it on my tree. > > Signed-off-by: Daniel Drake <dsd@xxxxxxxxxx> > --- > drivers/media/video/ov7670.c | 133 ++++++++++++++++++++++++++++++------------ > drivers/media/video/ov7670.h | 20 ++++++ > 2 files changed, 115 insertions(+), 38 deletions(-) > create mode 100644 drivers/media/video/ov7670.h > > diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c > index 0b78f33..c881a64 100644 > --- a/drivers/media/video/ov7670.c > +++ b/drivers/media/video/ov7670.c > @@ -20,6 +20,7 @@ > #include <media/v4l2-chip-ident.h> > #include <media/v4l2-mediabus.h> > > +#include "ov7670.h" > > MODULE_AUTHOR("Jonathan Corbet <corbet@xxxxxxx>"); > MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); > @@ -43,11 +44,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); > #define QCIF_HEIGHT 144 > > /* > - * Our nominal (default) frame rate. > - */ > -#define OV7670_FRAME_RATE 30 > - > -/* > * The 7670 sits on i2c with ID 0x42 > */ > #define OV7670_I2C_ADDR 0x42 > @@ -198,7 +194,11 @@ struct ov7670_info { > struct ov7670_format_struct *fmt; /* Current format */ > unsigned char sat; /* Saturation value */ > int hue; /* Hue value */ > + int min_width; /* Filter out smaller sizes */ > + int min_height; /* Filter out smaller sizes */ > + int clock_speed; /* External clock speed (MHz) */ > u8 clkrc; /* Clock divider value */ > + bool use_smbus; /* Use smbus I/O instead of I2C */ > }; > > static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) > @@ -415,8 +415,7 @@ static struct regval_list ov7670_fmt_raw[] = { > * ov7670 is not really an SMBUS device, though, so the communication > * is not always entirely reliable. > */ > -#ifdef CONFIG_OLPC_XO_1 > -static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, > +static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, > unsigned char *value) > { > struct i2c_client *client = v4l2_get_subdevdata(sd); > @@ -431,7 +430,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, > } > > > -static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, > +static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, > unsigned char value) > { > struct i2c_client *client = v4l2_get_subdevdata(sd); > @@ -442,11 +441,10 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, > return ret; > } > > -#else /* ! CONFIG_OLPC_XO_1 */ > /* > * On most platforms, we'd rather do straight i2c I/O. > */ > -static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, > +static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, > unsigned char *value) > { > struct i2c_client *client = v4l2_get_subdevdata(sd); > @@ -479,7 +477,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, > } > > > -static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, > +static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, > unsigned char value) > { > struct i2c_client *client = v4l2_get_subdevdata(sd); > @@ -498,8 +496,26 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, > msleep(5); /* Wait for reset to run */ > return ret; > } > -#endif /* CONFIG_OLPC_XO_1 */ > > +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, > + unsigned char *value) > +{ > + struct ov7670_info *info = to_state(sd); > + if (info->use_smbus) > + return ov7670_read_smbus(sd, reg, value); > + else > + return ov7670_read_i2c(sd, reg, value); > +} > + > +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, > + unsigned char value) > +{ > + struct ov7670_info *info = to_state(sd); > + if (info->use_smbus) > + return ov7670_write_smbus(sd, reg, value); > + else > + return ov7670_write_i2c(sd, reg, value); > +} > > /* > * 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) > memset(cp, 0, sizeof(struct v4l2_captureparm)); > cp->capability = V4L2_CAP_TIMEPERFRAME; > cp->timeperframe.numerator = 1; > - cp->timeperframe.denominator = OV7670_FRAME_RATE; > + cp->timeperframe.denominator = info->clock_speed; > if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) > cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); > return 0; > @@ -875,14 +891,14 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) > if (tpf->numerator == 0 || tpf->denominator == 0) > div = 1; /* Reset to full rate */ > else > - div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; > + div = (tpf->numerator * info->clock_speed) / tpf->denominator; > if (div == 0) > div = 1; > else if (div > CLK_SCALE) > div = CLK_SCALE; > info->clkrc = (info->clkrc & 0x80) | div; > tpf->numerator = 1; > - tpf->denominator = OV7670_FRAME_RATE/div; > + tpf->denominator = info->clock_speed / div; > return ov7670_write(sd, REG_CLKRC, info->clkrc); > } > > @@ -912,14 +928,30 @@ static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, > static int ov7670_enum_framesizes(struct v4l2_subdev *sd, > struct v4l2_frmsizeenum *fsize) > { > + struct ov7670_info *info = to_state(sd); > + int i; > + int num_valid = -1; > __u32 index = fsize->index; > - if (index >= N_WIN_SIZES) > - return -EINVAL; > > - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; > - fsize->discrete.width = ov7670_win_sizes[index].width; > - fsize->discrete.height = ov7670_win_sizes[index].height; > - return 0; > + /* > + * If a minimum width/height was requested, filter out the capture > + * windows that fall outside that. > + */ > + for (i = 0; i < N_WIN_SIZES; i++) { > + struct ov7670_win_size *win = &ov7670_win_sizes[index]; > + if (info->min_width && win->width < info->min_width) > + continue; > + if (info->min_height && win->height < info->min_height) > + continue; > + if (index == ++num_valid) { > + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; > + fsize->discrete.width = win->width; > + fsize->discrete.height = win->height; > + return 0; > + } > + } > + > + return -EINVAL; > } > > /* > @@ -1417,6 +1449,47 @@ static int ov7670_g_chip_ident(struct v4l2_subdev *sd, > return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); > } > > +static int ov7670_s_config(struct v4l2_subdev *sd, int dumb, void *data) > +{ > + struct i2c_client *client = v4l2_get_subdevdata(sd); > + struct ov7670_config *config = data; > + struct ov7670_info *info = to_state(sd); > + int ret; > + > + info->clock_speed = 30; /* default: a guess */ > + > + /* > + * Must apply configuration before initializing device, because it > + * selects I/O method. > + */ > + if (config) { > + info->min_width = config->min_width; > + info->min_height = config->min_height; > + info->use_smbus = config->use_smbus; > + > + if (config->clock_speed) > + info->clock_speed = config->clock_speed; > + } > + > + /* Make sure it's an ov7670 */ > + ret = ov7670_detect(sd); > + if (ret) { > + v4l_dbg(1, debug, client, > + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", > + client->addr << 1, client->adapter->name); > + kfree(info); > + return ret; > + } > + v4l_info(client, "chip found @ 0x%02x (%s)\n", > + client->addr << 1, client->adapter->name); > + > + info->fmt = &ov7670_formats[0]; > + info->sat = 128; /* Review this */ > + info->clkrc = info->clock_speed / 30; > + > + return 0; > +} > + > #ifdef CONFIG_VIDEO_ADV_DEBUG > static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) > { > @@ -1455,6 +1528,7 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { > .s_ctrl = ov7670_s_ctrl, > .queryctrl = ov7670_queryctrl, > .reset = ov7670_reset, > + .s_config = ov7670_s_config, > .init = ov7670_init, > #ifdef CONFIG_VIDEO_ADV_DEBUG > .g_register = ov7670_g_register, > @@ -1484,7 +1558,6 @@ static int ov7670_probe(struct i2c_client *client, > { > struct v4l2_subdev *sd; > struct ov7670_info *info; > - int ret; > > info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); > if (info == NULL) > @@ -1492,22 +1565,6 @@ static int ov7670_probe(struct i2c_client *client, > sd = &info->sd; > v4l2_i2c_subdev_init(sd, client, &ov7670_ops); > > - /* Make sure it's an ov7670 */ > - ret = ov7670_detect(sd); > - if (ret) { > - v4l_dbg(1, debug, client, > - "chip found @ 0x%x (%s) is not an ov7670 chip.\n", > - client->addr << 1, client->adapter->name); > - kfree(info); > - return ret; > - } > - v4l_info(client, "chip found @ 0x%02x (%s)\n", > - client->addr << 1, client->adapter->name); > - > - info->fmt = &ov7670_formats[0]; > - info->sat = 128; /* Review this */ > - info->clkrc = 1; /* 30fps */ > - > return 0; > } > > diff --git a/drivers/media/video/ov7670.h b/drivers/media/video/ov7670.h > new file mode 100644 > index 0000000..b133bc1 > --- /dev/null > +++ b/drivers/media/video/ov7670.h > @@ -0,0 +1,20 @@ > +/* > + * A V4L2 driver for OmniVision OV7670 cameras. > + * > + * Copyright 2010 One Laptop Per Child > + * > + * This file may be distributed under the terms of the GNU General > + * Public License, version 2. > + */ > + > +#ifndef __OV7670_H > +#define __OV7670_H > + > +struct ov7670_config { > + int min_width; /* Filter out smaller sizes */ > + int min_height; /* Filter out smaller sizes */ > + int clock_speed; /* External clock speed (MHz) */ > + bool use_smbus; /* Use smbus I/O instead of I2C */ > +}; > + > +#endif -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html