[media] V4L2: soc-camera: work around unbalanced calls to .s_power()
Some non soc-camera drivers, e.g. em28xx, use subdevice drivers, originally written for soc-camera, which use soc_camera_power_on() and soc_camera_power_off() helpers to implement their .s_power() methods. Those helpers in turn can enable and disable a clock, if it is supplied to them as a parameter. This works well when camera host drivers balance their calls to subdevices' .s_power() methods. However, some such drivers fail to do that, which leads to unbalanced calls to v4l2_clk_enable() / v4l2_clk_disable(), which then in turn produce kernel warnings. Such behaviour is wrong and should be fixed, however, sometimes it is difficult, because some of those drivers are rather old and use lots of subdevices, which all should be tested after such a fix. To support such drivers this patch adds a work-around, allowing host drivers or platforms to set a flag, in which case soc-camera helpers will only enable the clock, if it is disabled, and disable it only once on the first call to .s_power(0). Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
This commit is contained in:
parent
fc5d0f8a88
commit
40f075330a
2 changed files with 30 additions and 6 deletions
|
@ -71,11 +71,21 @@ static int video_dev_create(struct soc_camera_device *icd);
|
|||
int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
|
||||
struct v4l2_clk *clk)
|
||||
{
|
||||
int ret = clk ? v4l2_clk_enable(clk) : 0;
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot enable clock: %d\n", ret);
|
||||
return ret;
|
||||
int ret;
|
||||
bool clock_toggle;
|
||||
|
||||
if (clk && (!ssdd->unbalanced_power ||
|
||||
!test_and_set_bit(0, &ssdd->clock_state))) {
|
||||
ret = v4l2_clk_enable(clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot enable clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
clock_toggle = true;
|
||||
} else {
|
||||
clock_toggle = false;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators,
|
||||
ssdd->sd_pdata.regulators);
|
||||
if (ret < 0) {
|
||||
|
@ -98,7 +108,7 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
|
|||
regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
|
||||
ssdd->sd_pdata.regulators);
|
||||
eregenable:
|
||||
if (clk)
|
||||
if (clock_toggle)
|
||||
v4l2_clk_disable(clk);
|
||||
|
||||
return ret;
|
||||
|
@ -127,7 +137,7 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd
|
|||
ret = ret ? : err;
|
||||
}
|
||||
|
||||
if (clk)
|
||||
if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state)))
|
||||
v4l2_clk_disable(clk);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -146,6 +146,15 @@ struct soc_camera_subdev_desc {
|
|||
/* sensor driver private platform data */
|
||||
void *drv_priv;
|
||||
|
||||
/*
|
||||
* Set unbalanced_power to true to deal with legacy drivers, failing to
|
||||
* balance their calls to subdevice's .s_power() method. clock_state is
|
||||
* then used internally by helper functions, it shouldn't be touched by
|
||||
* drivers or the platform code.
|
||||
*/
|
||||
bool unbalanced_power;
|
||||
unsigned long clock_state;
|
||||
|
||||
/* Optional callbacks to power on or off and reset the sensor */
|
||||
int (*power)(struct device *, int);
|
||||
int (*reset)(struct device *);
|
||||
|
@ -201,6 +210,11 @@ struct soc_camera_link {
|
|||
|
||||
void *priv;
|
||||
|
||||
/* Set by platforms to handle misbehaving drivers */
|
||||
bool unbalanced_power;
|
||||
/* Used by soc-camera helper functions */
|
||||
unsigned long clock_state;
|
||||
|
||||
/* Optional callbacks to power on or off and reset the sensor */
|
||||
int (*power)(struct device *, int);
|
||||
int (*reset)(struct device *);
|
||||
|
|
Loading…
Reference in a new issue