diff options
Diffstat (limited to 'drivers/media/i2c/ov7670.c')
-rw-r--r-- | drivers/media/i2c/ov7670.c | 75 |
1 files changed, 66 insertions, 9 deletions
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 56cfb5ca9c95..7270c68ed18a 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -10,12 +10,15 @@ * This file may be distributed under the terms of the GNU General * Public License, version 2. */ +#include <linux/clk.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/delay.h> #include <linux/videodev2.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-mediabus.h> @@ -227,6 +230,9 @@ struct ov7670_info { struct v4l2_ctrl *hue; }; struct ov7670_format_struct *fmt; /* Current format */ + struct clk *clk; + struct gpio_desc *resetb_gpio; + struct gpio_desc *pwdn_gpio; int min_width; /* Filter out smaller sizes */ int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ @@ -589,8 +595,6 @@ static int ov7670_init(struct v4l2_subdev *sd, u32 val) return ov7670_write_array(sd, ov7670_default_regs); } - - static int ov7670_detect(struct v4l2_subdev *sd) { unsigned char v; @@ -1046,7 +1050,6 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; info->devtype->get_framerate(sd, &cp->timeperframe); @@ -1061,9 +1064,8 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; + cp->capability = V4L2_CAP_TIMEPERFRAME; return info->devtype->set_framerate(sd, tpf); } @@ -1549,6 +1551,27 @@ static const struct ov7670_devtype ov7670_devdata[] = { }, }; +static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info) +{ + info->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(info->pwdn_gpio)) { + dev_info(&client->dev, "can't get %s GPIO\n", "powerdown"); + return PTR_ERR(info->pwdn_gpio); + } + + info->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(info->resetb_gpio)) { + dev_info(&client->dev, "can't get %s GPIO\n", "reset"); + return PTR_ERR(info->resetb_gpio); + } + + usleep_range(3000, 5000); + + return 0; +} + static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1589,13 +1612,28 @@ static int ov7670_probe(struct i2c_client *client, info->pclk_hb_disable = true; } + info->clk = devm_clk_get(&client->dev, "xclk"); + if (IS_ERR(info->clk)) + return -EPROBE_DEFER; + clk_prepare_enable(info->clk); + + ret = ov7670_init_gpio(client, info); + if (ret) + goto clk_disable; + + info->clock_speed = clk_get_rate(info->clk) / 1000000; + if (info->clock_speed < 10 || info->clock_speed > 48) { + ret = -EINVAL; + goto clk_disable; + } + /* 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); - return ret; + goto clk_disable; } v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); @@ -1636,10 +1674,9 @@ static int ov7670_probe(struct i2c_client *client, V4L2_EXPOSURE_AUTO); sd->ctrl_handler = &info->hdl; if (info->hdl.error) { - int err = info->hdl.error; + ret = info->hdl.error; - v4l2_ctrl_handler_free(&info->hdl); - return err; + goto hdl_free; } /* * We have checked empirically that hw allows to read back the gain @@ -1651,7 +1688,17 @@ static int ov7670_probe(struct i2c_client *client, v4l2_ctrl_cluster(2, &info->saturation); v4l2_ctrl_handler_setup(&info->hdl); + ret = v4l2_async_register_subdev(&info->sd); + if (ret < 0) + goto hdl_free; + return 0; + +hdl_free: + v4l2_ctrl_handler_free(&info->hdl); +clk_disable: + clk_disable_unprepare(info->clk); + return ret; } @@ -1662,6 +1709,7 @@ static int ov7670_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&info->hdl); + clk_disable_unprepare(info->clk); return 0; } @@ -1672,9 +1720,18 @@ static const struct i2c_device_id ov7670_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov7670_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov7670_of_match[] = { + { .compatible = "ovti,ov7670", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov7670_of_match); +#endif + static struct i2c_driver ov7670_driver = { .driver = { .name = "ov7670", + .of_match_table = of_match_ptr(ov7670_of_match), }, .probe = ov7670_probe, .remove = ov7670_remove, |