aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/ov7670.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov7670.c')
-rw-r--r--drivers/media/i2c/ov7670.c75
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,