Commit 9dc4e48f authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mauro Carvalho Chehab

V4L/DVB (7276): soc-camera: deactivate cameras when not used

Only attach cameras to the host interface for probing, then detach until
open. This allows platforms with several cameras on an interface,
physically supporting only one camera, to handle multiple cameras and
activate them selectively after initial probing. The first attach during
probe is needed to activate the host interface to be able to physically
communicate with cameras.
Signed-off-by: default avatarGuennadi Liakhovetski <g.liakhovetski@pengutronix.de>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 6f289675
...@@ -179,11 +179,9 @@ static int soc_camera_dqbuf(struct file *file, void *priv, ...@@ -179,11 +179,9 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
static int soc_camera_open(struct inode *inode, struct file *file) static int soc_camera_open(struct inode *inode, struct file *file)
{ {
struct video_device *vdev = video_devdata(file); struct video_device *vdev;
struct soc_camera_device *icd = container_of(vdev->dev, struct soc_camera_device *icd;
struct soc_camera_device, dev); struct soc_camera_host *ici;
struct soc_camera_host *ici =
to_soc_camera_host(icd->dev.parent);
struct soc_camera_file *icf; struct soc_camera_file *icf;
int ret; int ret;
...@@ -191,7 +189,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) ...@@ -191,7 +189,12 @@ static int soc_camera_open(struct inode *inode, struct file *file)
if (!icf) if (!icf)
return -ENOMEM; return -ENOMEM;
icf->icd = icd; /* Protect against icd->remove() until we module_get() both drivers. */
mutex_lock(&video_lock);
vdev = video_devdata(file);
icd = container_of(vdev->dev, struct soc_camera_device, dev);
ici = to_soc_camera_host(icd->dev.parent);
if (!try_module_get(icd->ops->owner)) { if (!try_module_get(icd->ops->owner)) {
dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); dev_err(&icd->dev, "Couldn't lock sensor driver.\n");
...@@ -205,6 +208,22 @@ static int soc_camera_open(struct inode *inode, struct file *file) ...@@ -205,6 +208,22 @@ static int soc_camera_open(struct inode *inode, struct file *file)
goto emgi; goto emgi;
} }
icd->use_count++;
icf->icd = icd;
/* Now we really have to activate the camera */
if (icd->use_count == 1) {
ret = ici->add(icd);
if (ret < 0) {
dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
icd->use_count--;
goto eiciadd;
}
}
mutex_unlock(&video_lock);
file->private_data = icf; file->private_data = icf;
dev_dbg(&icd->dev, "camera device open\n"); dev_dbg(&icd->dev, "camera device open\n");
...@@ -216,9 +235,13 @@ static int soc_camera_open(struct inode *inode, struct file *file) ...@@ -216,9 +235,13 @@ static int soc_camera_open(struct inode *inode, struct file *file)
return 0; return 0;
/* All errors are entered with the video_lock held */
eiciadd:
module_put(ici->owner);
emgi: emgi:
module_put(icd->ops->owner); module_put(icd->ops->owner);
emgd: emgd:
mutex_unlock(&video_lock);
vfree(icf); vfree(icf);
return ret; return ret;
} }
...@@ -230,8 +253,14 @@ static int soc_camera_close(struct inode *inode, struct file *file) ...@@ -230,8 +253,14 @@ static int soc_camera_close(struct inode *inode, struct file *file)
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct video_device *vdev = icd->vdev; struct video_device *vdev = icd->vdev;
mutex_lock(&video_lock);
icd->use_count--;
if (!icd->use_count)
ici->remove(icd);
module_put(icd->ops->owner); module_put(icd->ops->owner);
module_put(ici->owner); module_put(ici->owner);
mutex_unlock(&video_lock);
vfree(file->private_data); vfree(file->private_data);
dev_dbg(vdev->dev, "camera device close\n"); dev_dbg(vdev->dev, "camera device close\n");
...@@ -673,14 +702,14 @@ static int soc_camera_probe(struct device *dev) ...@@ -673,14 +702,14 @@ static int soc_camera_probe(struct device *dev)
if (!icd->probe) if (!icd->probe)
return -ENODEV; return -ENODEV;
/* We only call ->add() here to activate and probe the camera.
* We shall ->remove() and deactivate it immediately afterwards. */
ret = ici->add(icd); ret = ici->add(icd);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = icd->probe(icd); ret = icd->probe(icd);
if (ret < 0) if (ret >= 0) {
ici->remove(icd);
else {
const struct v4l2_queryctrl *qctrl; const struct v4l2_queryctrl *qctrl;
qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN);
...@@ -689,6 +718,7 @@ static int soc_camera_probe(struct device *dev) ...@@ -689,6 +718,7 @@ static int soc_camera_probe(struct device *dev)
icd->exposure = qctrl ? qctrl->default_value : icd->exposure = qctrl ? qctrl->default_value :
(unsigned short)~0; (unsigned short)~0;
} }
ici->remove(icd);
return ret; return ret;
} }
...@@ -698,14 +728,10 @@ static int soc_camera_probe(struct device *dev) ...@@ -698,14 +728,10 @@ static int soc_camera_probe(struct device *dev)
static int soc_camera_remove(struct device *dev) static int soc_camera_remove(struct device *dev)
{ {
struct soc_camera_device *icd = to_soc_camera_dev(dev); struct soc_camera_device *icd = to_soc_camera_dev(dev);
struct soc_camera_host *ici =
to_soc_camera_host(icd->dev.parent);
if (icd->remove) if (icd->remove)
icd->remove(icd); icd->remove(icd);
ici->remove(icd);
return 0; return 0;
} }
......
...@@ -41,6 +41,8 @@ struct soc_camera_device { ...@@ -41,6 +41,8 @@ struct soc_camera_device {
int (*probe)(struct soc_camera_device *icd); int (*probe)(struct soc_camera_device *icd);
void (*remove)(struct soc_camera_device *icd); void (*remove)(struct soc_camera_device *icd);
struct module *owner; struct module *owner;
/* soc_camera.c private count. Only accessed with video_lock held */
int use_count;
}; };
struct soc_camera_file { struct soc_camera_file {
......
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