Commit f1a64f56 authored by Dongchun Zhu's avatar Dongchun Zhu Committed by Mauro Carvalho Chehab

media: i2c: ov5695: Fix power on and off sequences

From the measured hardware signal, OV5695 reset pin goes high for a
short period of time during boot-up. From the sensor specification, the
reset pin is active low and the DT binding defines the pin as active
low, which means that the values set by the driver are inverted and thus
the value requested in probe ends up high.

Fix it by changing probe to request the reset GPIO initialized to high,
which makes the initial state of the physical signal low.

In addition, DOVDD rising must occur before DVDD rising from spec., but
regulator_bulk_enable() API enables all the regulators asynchronously.
Use an explicit loops of regulator_enable() instead.

For power off sequence, it is required that DVDD falls first. Given the
bulk API does not give any guarantee about the order of regulators,
change the driver to use regulator_disable() instead.

The sensor also requires a delay between reset high and first I2C
transaction, which was assumed to be 8192 XVCLK cycles, but 1ms is
recommended by the vendor. Fix this as well.
Signed-off-by: default avatarDongchun Zhu <dongchun.zhu@mediatek.com>
Signed-off-by: default avatarTomasz Figa <tfiga@chromium.org>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 25130b8a
...@@ -971,16 +971,9 @@ static int ov5695_s_stream(struct v4l2_subdev *sd, int on) ...@@ -971,16 +971,9 @@ static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
return ret; return ret;
} }
/* Calculate the delay in us by clock rate and clock cycles */
static inline u32 ov5695_cal_delay(u32 cycles)
{
return DIV_ROUND_UP(cycles, OV5695_XVCLK_FREQ / 1000 / 1000);
}
static int __ov5695_power_on(struct ov5695 *ov5695) static int __ov5695_power_on(struct ov5695 *ov5695)
{ {
int ret; int i, ret;
u32 delay_us;
struct device *dev = &ov5695->client->dev; struct device *dev = &ov5695->client->dev;
ret = clk_prepare_enable(ov5695->xvclk); ret = clk_prepare_enable(ov5695->xvclk);
...@@ -991,21 +984,28 @@ static int __ov5695_power_on(struct ov5695 *ov5695) ...@@ -991,21 +984,28 @@ static int __ov5695_power_on(struct ov5695 *ov5695)
gpiod_set_value_cansleep(ov5695->reset_gpio, 1); gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies); /*
if (ret < 0) { * The hardware requires the regulators to be powered on in order,
dev_err(dev, "Failed to enable regulators\n"); * so enable them one by one.
goto disable_clk; */
for (i = 0; i < OV5695_NUM_SUPPLIES; i++) {
ret = regulator_enable(ov5695->supplies[i].consumer);
if (ret) {
dev_err(dev, "Failed to enable %s: %d\n",
ov5695->supplies[i].supply, ret);
goto disable_reg_clk;
}
} }
gpiod_set_value_cansleep(ov5695->reset_gpio, 0); gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
/* 8192 cycles prior to first SCCB transaction */ usleep_range(1000, 1200);
delay_us = ov5695_cal_delay(8192);
usleep_range(delay_us, delay_us * 2);
return 0; return 0;
disable_clk: disable_reg_clk:
for (--i; i >= 0; i--)
regulator_disable(ov5695->supplies[i].consumer);
clk_disable_unprepare(ov5695->xvclk); clk_disable_unprepare(ov5695->xvclk);
return ret; return ret;
...@@ -1013,9 +1013,22 @@ static int __ov5695_power_on(struct ov5695 *ov5695) ...@@ -1013,9 +1013,22 @@ static int __ov5695_power_on(struct ov5695 *ov5695)
static void __ov5695_power_off(struct ov5695 *ov5695) static void __ov5695_power_off(struct ov5695 *ov5695)
{ {
struct device *dev = &ov5695->client->dev;
int i, ret;
clk_disable_unprepare(ov5695->xvclk); clk_disable_unprepare(ov5695->xvclk);
gpiod_set_value_cansleep(ov5695->reset_gpio, 1); gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies);
/*
* The hardware requires the regulators to be powered off in order,
* so disable them one by one.
*/
for (i = OV5695_NUM_SUPPLIES - 1; i >= 0; i--) {
ret = regulator_disable(ov5695->supplies[i].consumer);
if (ret)
dev_err(dev, "Failed to disable %s: %d\n",
ov5695->supplies[i].supply, ret);
}
} }
static int __maybe_unused ov5695_runtime_resume(struct device *dev) static int __maybe_unused ov5695_runtime_resume(struct device *dev)
...@@ -1285,7 +1298,7 @@ static int ov5695_probe(struct i2c_client *client, ...@@ -1285,7 +1298,7 @@ static int ov5695_probe(struct i2c_client *client,
if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ)
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ov5695->reset_gpio)) { if (IS_ERR(ov5695->reset_gpio)) {
dev_err(dev, "Failed to get reset-gpios\n"); dev_err(dev, "Failed to get reset-gpios\n");
return -EINVAL; return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment