aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ov772x.c
diff options
context:
space:
mode:
authorAkinobu Mita <akinobu.mita@gmail.com>2018-05-06 10:19:24 -0400
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>2018-06-28 09:06:39 -0400
commit34af7d920b766ffa582231ff3ce526d0c6179cd0 (patch)
tree1ae600c67fc9b78cf481d16b76411f2836cf1760 /drivers/media/i2c/ov772x.c
parentc2cae8951772aff8355171882fcc8e4eb9f53840 (diff)
media: ov772x: handle nested s_power() calls
Depending on the v4l2 driver, calling s_power() could be nested. So the actual transitions between power saving mode and normal operation mode should only happen at the first power on and the last power off. This adds an s_power() nesting counter and updates the power state if the counter is modified from 0 to != 0 or from != 0 to 0. Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Cc: Hans Verkuil <hans.verkuil@cisco.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/i2c/ov772x.c')
-rw-r--r--drivers/media/i2c/ov772x.c34
1 files changed, 30 insertions, 4 deletions
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index b5ae88e69b94..4a30a4338445 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,6 +424,9 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
unsigned short band_filter;
unsigned int fps;
+ /* lock to protect power_count */
+ struct mutex lock;
+ int power_count;
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad pad;
#endif
@@ -871,9 +874,26 @@ static int ov772x_power_off(struct ov772x_priv *priv)
static int ov772x_s_power(struct v4l2_subdev *sd, int on)
{
struct ov772x_priv *priv = to_ov772x(sd);
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (priv->power_count == !on)
+ ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv);
+
+ if (!ret) {
+ /* Update the power count. */
+ priv->power_count += on ? 1 : -1;
+ WARN(priv->power_count < 0, "Unbalanced power count\n");
+ WARN(priv->power_count > 1, "Duplicated s_power call\n");
+ }
+
+ mutex_unlock(&priv->lock);
- return on ? ov772x_power_on(priv) :
- ov772x_power_off(priv);
+ return ret;
}
static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
@@ -1303,6 +1323,7 @@ static int ov772x_probe(struct i2c_client *client,
return -ENOMEM;
priv->info = client->dev.platform_data;
+ mutex_init(&priv->lock);
v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
v4l2_ctrl_handler_init(&priv->hdl, 3);
@@ -1313,8 +1334,10 @@ static int ov772x_probe(struct i2c_client *client,
v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error)
- return priv->hdl.error;
+ if (priv->hdl.error) {
+ ret = priv->hdl.error;
+ goto error_mutex_destroy;
+ }
priv->clk = clk_get(&client->dev, NULL);
if (IS_ERR(priv->clk)) {
@@ -1362,6 +1385,8 @@ error_clk_put:
clk_put(priv->clk);
error_ctrl_free:
v4l2_ctrl_handler_free(&priv->hdl);
+error_mutex_destroy:
+ mutex_destroy(&priv->lock);
return ret;
}
@@ -1376,6 +1401,7 @@ static int ov772x_remove(struct i2c_client *client)
gpiod_put(priv->pwdn_gpio);
v4l2_async_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
+ mutex_destroy(&priv->lock);
return 0;
}