In certain machines, camera devices are supplied directly by a number of regulators. This patch add the ability to drive these regulators directly by the soc_camera driver. Signed-off-by: Alberto Panizzo <maramaopercheseimorto@xxxxxxxxx> --- v2 changes: It is used the more standard regulator_bulk API, thanks to Mark Brown for pointing this. v3 changes: - Fixed errors addressed. - Changed the behaviour of soc_camera_power_set: when powering off, the platform .power function have the priority because the manipulation of the camera status through gpios, make sense only if the camera is powered. drivers/media/video/soc_camera.c | 86 ++++++++++++++++++++++++++++--------- include/media/soc_camera.h | 5 ++ 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 43848a7..136e54f 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -24,6 +24,7 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/vmalloc.h> @@ -43,6 +44,51 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ +static int soc_camera_power_set(struct soc_camera_device *icd, + struct soc_camera_link *icl, + int power_on) +{ + int ret; + + if (power_on) { + ret = regulator_bulk_enable(icl->num_regulators, + icl->regulators); + if (ret < 0) { + dev_err(icd->pdev, "Cannot enable regulators\n"); + return ret; + } + + if (icl->power) + ret = icl->power(icd->pdev, power_on); + if (ret < 0) { + dev_err(icd->pdev, + "Platform failed to power-on the camera.\n"); + + regulator_bulk_disable(icl->num_regulators, + icl->regulators); + return ret; + } + } else { + ret = 0; + if (icl->power) + ret = icl->power(icd->pdev, 0); + if (ret < 0) { + dev_err(icd->pdev, + "Platform failed to power-off the camera.\n"); + return ret; + } + + ret = regulator_bulk_disable(icl->num_regulators, + icl->regulators); + if (ret < 0) { + dev_err(icd->pdev, "Cannot disable regulators\n"); + return ret; + } + } + + return 0; +} + const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) { @@ -375,11 +421,9 @@ static int soc_camera_open(struct file *file) }, }; - if (icl->power) { - ret = icl->power(icd->pdev, 1); - if (ret < 0) - goto epower; - } + ret = soc_camera_power_set(icd, icl, 1); + if (ret < 0) + goto epower; /* The camera could have been already on, try to reset */ if (icl->reset) @@ -425,8 +469,7 @@ esfmt: eresume: ici->ops->remove(icd); eiciadd: - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); epower: icd->use_count--; mutex_unlock(&icd->video_lock); @@ -450,8 +493,7 @@ static int soc_camera_close(struct file *file) ici->ops->remove(icd); - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); } if (icd->streamer == file) @@ -941,14 +983,14 @@ static int soc_camera_probe(struct device *dev) dev_info(dev, "Probing %s\n", dev_name(dev)); - if (icl->power) { - ret = icl->power(icd->pdev, 1); - if (ret < 0) { - dev_err(dev, - "Platform failed to power-on the camera.\n"); - goto epower; - } - } + ret = regulator_bulk_get(icd->pdev, icl->num_regulators, + icl->regulators); + if (ret < 0) + goto ereg; + + ret = soc_camera_power_set(icd, icl, 1); + if (ret < 0) + goto epower; /* The camera could have been already on, try to reset */ if (icl->reset) @@ -1021,8 +1063,7 @@ static int soc_camera_probe(struct device *dev) ici->ops->remove(icd); - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); mutex_unlock(&icd->video_lock); @@ -1044,9 +1085,10 @@ eadddev: evdc: ici->ops->remove(icd); eadd: - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); epower: + regulator_bulk_free(icl->num_regulators, icl->regulators); +ereg: return ret; } @@ -1081,6 +1123,8 @@ static int soc_camera_remove(struct device *dev) } soc_camera_free_user_formats(icd); + regulator_bulk_free(icl->num_regulators, icl->regulators); + return 0; } diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 86e3631..9386db8 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -97,6 +97,7 @@ struct soc_camera_host_ops { #define SOCAM_SENSOR_INVERT_DATA (1 << 4) struct i2c_board_info; +struct regulator_bulk_data; struct soc_camera_link { /* Camera bus id, used to match a camera and a bus */ @@ -108,6 +109,10 @@ struct soc_camera_link { const char *module_name; void *priv; + /* Optional regulators that have to be managed on power on/off events */ + struct regulator_bulk_data *regulators; + int num_regulators; + /* * For non-I2C devices platform platform has to provide methods to * add a device to the system and to remove -- 1.6.3.3 -- 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