Re: [PATCHv2 10/16] ov2640: enable clock and fix power/reset

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 01/31/2017 11:20 AM, Sakari Ailus wrote:
> Hi Hans,
> 
> Thank you for the patchset!
> 
> On Mon, Jan 30, 2017 at 03:06:22PM +0100, Hans Verkuil wrote:
>> From: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>>
>> Convert v4l2_clk to normal clk, enable the clock and fix the power/reset
>> handling.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> ---
>>  drivers/media/i2c/ov2640.c | 80 +++++++++++++++++-----------------------------
>>  1 file changed, 29 insertions(+), 51 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
>> index 83f88ef..565742b 100644
>> --- a/drivers/media/i2c/ov2640.c
>> +++ b/drivers/media/i2c/ov2640.c
>> @@ -16,15 +16,14 @@
>>  #include <linux/init.h>
>>  #include <linux/module.h>
>>  #include <linux/i2c.h>
>> +#include <linux/clk.h>
>>  #include <linux/slab.h>
>>  #include <linux/delay.h>
>>  #include <linux/gpio.h>
>>  #include <linux/gpio/consumer.h>
>> -#include <linux/of_gpio.h>
>>  #include <linux/v4l2-mediabus.h>
>>  #include <linux/videodev2.h>
>>  
>> -#include <media/v4l2-clk.h>
>>  #include <media/v4l2-device.h>
>>  #include <media/v4l2-subdev.h>
>>  #include <media/v4l2-ctrls.h>
>> @@ -284,7 +283,7 @@ struct ov2640_priv {
>>  	struct v4l2_subdev		subdev;
>>  	struct v4l2_ctrl_handler	hdl;
>>  	u32	cfmt_code;
>> -	struct v4l2_clk			*clk;
>> +	struct clk			*clk;
> 
> Nice!
> 
>>  	const struct ov2640_win_size	*win;
>>  
>>  	struct gpio_desc *resetb_gpio;
>> @@ -656,8 +655,9 @@ static int ov2640_mask_set(struct i2c_client *client,
>>  	return i2c_smbus_write_byte_data(client, reg, val);
>>  }
>>  
>> -static int ov2640_reset(struct i2c_client *client)
>> +static int ov2640_reset(struct v4l2_subdev *sd, u32 val)
>>  {
>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>>  	int ret;
>>  	const struct regval_list reset_seq[] = {
>>  		{BANK_SEL, BANK_SEL_SENS},
>> @@ -735,21 +735,6 @@ static int ov2640_s_register(struct v4l2_subdev *sd,
>>  }
>>  #endif
>>  
>> -static int ov2640_s_power(struct v4l2_subdev *sd, int on)
>> -{
>> -	struct i2c_client *client = v4l2_get_subdevdata(sd);
>> -	struct ov2640_priv *priv = to_ov2640(client);
>> -
>> -	gpiod_direction_output(priv->pwdn_gpio, !on);
>> -	if (on && priv->resetb_gpio) {
>> -		/* Active the resetb pin to perform a reset pulse */
>> -		gpiod_direction_output(priv->resetb_gpio, 1);
>> -		usleep_range(3000, 5000);
>> -		gpiod_direction_output(priv->resetb_gpio, 0);
> 
> Do you still perform the reset sequence somewhere? This could be crucial for
> reliability.

All I can go with is what soc_camera did, and there the reset is performed only
once, when the sensor driver is bound to soc_camera. This is the equivalent of
that. The reset is now performed in ov2640_init_gpio although it doesn't do a
full reset there. See more about this below.

> 
>> -	}
>> -	return 0;
>> -}
>> -
>>  /* Select the nearest higher resolution for capture */
>>  static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height)
>>  {
>> @@ -769,9 +754,10 @@ static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height)
>>  	return &ov2640_supported_win_sizes[default_size];
>>  }
>>  
>> -static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
>> +static int ov2640_set_params(struct v4l2_subdev *sd, u32 *width, u32 *height,
>>  			     u32 code)
>>  {
>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>>  	struct ov2640_priv       *priv = to_ov2640(client);
>>  	const struct regval_list *selected_cfmt_regs;
>>  	int ret;
>> @@ -802,7 +788,7 @@ static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
>>  	}
>>  
>>  	/* reset hardware */
>> -	ov2640_reset(client);
>> +	ov2640_reset(sd, 0);
>>  
>>  	/* initialize the sensor with default data */
>>  	dev_dbg(&client->dev, "%s: Init default", __func__);
>> @@ -840,7 +826,7 @@ static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
>>  
>>  err:
>>  	dev_err(&client->dev, "%s: Error %d", __func__, ret);
>> -	ov2640_reset(client);
>> +	ov2640_reset(sd, 0);
>>  	priv->win = NULL;
>>  
>>  	return ret;
>> @@ -877,7 +863,6 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
>>  		struct v4l2_subdev_format *format)
>>  {
>>  	struct v4l2_mbus_framefmt *mf = &format->format;
>> -	struct i2c_client *client = v4l2_get_subdevdata(sd);
>>  
>>  	if (format->pad)
>>  		return -EINVAL;
>> @@ -902,7 +887,7 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
>>  	}
>>  
>>  	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
>> -		return ov2640_set_params(client, &mf->width,
>> +		return ov2640_set_params(sd, &mf->width,
>>  					 &mf->height, mf->code);
>>  	cfg->try_fmt = *mf;
>>  	return 0;
>> @@ -947,10 +932,6 @@ static int ov2640_video_probe(struct i2c_client *client)
>>  	const char *devname;
>>  	int ret;
>>  
>> -	ret = ov2640_s_power(&priv->subdev, 1);
>> -	if (ret < 0)
>> -		return ret;
>> -
>>  	/*
>>  	 * check and show product ID and manufacturer ID
>>  	 */
>> @@ -978,7 +959,6 @@ static int ov2640_video_probe(struct i2c_client *client)
>>  	ret = v4l2_ctrl_handler_setup(&priv->hdl);
>>  
>>  done:
>> -	ov2640_s_power(&priv->subdev, 0);
>>  	return ret;
>>  }
>>  
>> @@ -991,7 +971,7 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
>>  	.g_register	= ov2640_g_register,
>>  	.s_register	= ov2640_s_register,
>>  #endif
>> -	.s_power	= ov2640_s_power,
>> +	.reset		= ov2640_reset,
> 
> Why?
> 
> We have s_power() callback. Shouldn't you run the reset sequence when the
> device is powered on?
> 
> Few if any bridge / ISP drivers will use the reset op. It should rather be
> removed.

Good point. I'm not sure why I did this.

I will restore the s_power callback. I seem to remember that I had problems
with s_power, but I will have to retest this.

> 
>>  };
>>  
>>  static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = {
>> @@ -1006,9 +986,17 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = {
>>  	.pad	= &ov2640_subdev_pad_ops,
>>  };
>>  
>> -static int ov2640_probe_dt(struct i2c_client *client,
>> -		struct ov2640_priv *priv)
>> +static int ov2640_init_gpio(struct i2c_client *client,
>> +			    struct ov2640_priv *priv)
>>  {
>> +	/* Request the power down GPIO deasserted */
>> +	priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
>> +			GPIOD_OUT_LOW);
>> +	if (!priv->pwdn_gpio)
>> +		dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
> 
> I'm pretty sure not finding a GPIO using devm_gpiod_get_optional() already
> produces at least one line of output elsewhere.

No, it doesn't. It's optional, after all.

> 
>> +	else if (IS_ERR(priv->pwdn_gpio))
>> +		return PTR_ERR(priv->pwdn_gpio);
>> +
>>  	/* Request the reset GPIO deasserted */
>>  	priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb",
>>  			GPIOD_OUT_LOW);
>> @@ -1017,14 +1005,6 @@ static int ov2640_probe_dt(struct i2c_client *client,
>>  	else if (IS_ERR(priv->resetb_gpio))
>>  		return PTR_ERR(priv->resetb_gpio);
>>  
>> -	/* Request the power down GPIO asserted */
>> -	priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
>> -			GPIOD_OUT_HIGH);
>> -	if (!priv->pwdn_gpio)
>> -		dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
> 
> Same here.
> 
>> -	else if (IS_ERR(priv->pwdn_gpio))
>> -		return PTR_ERR(priv->pwdn_gpio);
>> -
>>  	return 0;
>>  }
>>  
>> @@ -1051,9 +1031,10 @@ static int ov2640_probe(struct i2c_client *client,
>>  		return -ENOMEM;
>>  	}
>>  
>> -	priv->clk = v4l2_clk_get(&client->dev, "xvclk");
>> +	priv->clk = clk_get(&client->dev, "xvclk");
>>  	if (IS_ERR(priv->clk))
>>  		return -EPROBE_DEFER;
>> +	clk_prepare_enable(priv->clk);
> 
> I wonder V4L2 clock framework did this. This should be done in the s_power()
> callback, not here.

Yeah, I'll need to revisit this.

> 
>>  
>>  	if (!client->dev.of_node) {
>>  		dev_err(&client->dev, "Missing platform_data for driver\n");
>> @@ -1061,9 +1042,9 @@ static int ov2640_probe(struct i2c_client *client,
>>  		goto err_clk;
>>  	}
>>  
>> -	ret = ov2640_probe_dt(client, priv);
>> +	ret = ov2640_init_gpio(client, priv);
>>  	if (ret)
>> -		goto err_clk;
>> +		return ret;
>>  
>>  	v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
>>  	v4l2_ctrl_handler_init(&priv->hdl, 2);
>> @@ -1074,25 +1055,23 @@ static int ov2640_probe(struct i2c_client *client,
>>  	priv->subdev.ctrl_handler = &priv->hdl;
>>  	if (priv->hdl.error) {
>>  		ret = priv->hdl.error;
>> -		goto err_clk;
>> +		goto err_hdl;
>>  	}
>>  
>>  	ret = ov2640_video_probe(client);
>>  	if (ret < 0)
>> -		goto err_videoprobe;
>> +		goto err_hdl;
>>  
>>  	ret = v4l2_async_register_subdev(&priv->subdev);
>>  	if (ret < 0)
>> -		goto err_videoprobe;
>> +		goto err_hdl;
>>  
>>  	dev_info(&adapter->dev, "OV2640 Probed\n");
>>  
>>  	return 0;
>>  
>> -err_videoprobe:
>> +err_hdl:
>>  	v4l2_ctrl_handler_free(&priv->hdl);
>> -err_clk:
>> -	v4l2_clk_put(priv->clk);
>>  	return ret;
>>  }
>>  
>> @@ -1101,9 +1080,8 @@ static int ov2640_remove(struct i2c_client *client)
>>  	struct ov2640_priv       *priv = to_ov2640(client);
>>  
>>  	v4l2_async_unregister_subdev(&priv->subdev);
>> -	v4l2_clk_put(priv->clk);
>> -	v4l2_device_unregister_subdev(&priv->subdev);
>>  	v4l2_ctrl_handler_free(&priv->hdl);
> 
> Shouldn't you free the control handler afterwards? I'm not sure if this
> makes a lot of difference though.

The order makes no difference here.

Regards,

	Hans

> 
>> +	v4l2_device_unregister_subdev(&priv->subdev);
>>  	return 0;
>>  }
>>  
> 


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



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux