{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- int ret = 0;
-
- if (icd->ops->set_std)
- ret = icd->ops->set_std(icd, a);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return ret;
+ return v4l2_subdev_call(sd, core, s_std, *a);
}
static int soc_camera_reqbufs(struct file *file, void *priv,
icd->user_formats = NULL;
}
+#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 24) & 0xff
+
/* Called with .vb_lock held */
static int soc_camera_set_fmt(struct soc_camera_file *icf,
struct v4l2_format *f)
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
+ dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
/* We always call try_fmt() before set_fmt() or set_crop() */
ret = ici->ops->try_fmt(icd, f);
if (ret < 0)
return ret;
} else if (!icd->current_fmt ||
icd->current_fmt->fourcc != pix->pixelformat) {
- dev_err(ici->v4l2_dev.dev,
+ dev_err(&icd->dev,
"Host driver hasn't set up current format correctly!\n");
return -EINVAL;
}
- icd->rect_current.width = pix->width;
- icd->rect_current.height = pix->height;
- icf->vb_vidq.field =
- icd->field = pix->field;
+ icd->user_width = pix->width;
+ icd->user_height = pix->height;
+ icf->vb_vidq.field =
+ icd->field = pix->field;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
f->type);
dev_dbg(&icd->dev, "set width: %d height: %d\n",
- icd->rect_current.width, icd->rect_current.height);
+ icd->user_width, icd->user_height);
/* set physical bus parameters */
return ici->ops->set_bus_param(icd, pix->pixelformat);
static int soc_camera_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
- struct soc_camera_device *icd = container_of(vdev->parent, struct soc_camera_device, dev);
+ struct soc_camera_device *icd = container_of(vdev->parent,
+ struct soc_camera_device,
+ dev);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct soc_camera_host *ici;
struct soc_camera_file *icf;
goto emgi;
}
- /* Protect against icd->ops->remove() until we module_get() both drivers. */
+ /*
+ * Protect against icd->ops->remove() until we module_get() both
+ * drivers.
+ */
mutex_lock(&icd->video_lock);
icf->icd = icd;
struct v4l2_format f = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.fmt.pix = {
- .width = icd->rect_current.width,
- .height = icd->rect_current.height,
+ .width = icd->user_width,
+ .height = icd->user_height,
.field = icd->field,
.pixelformat = icd->current_fmt->fourcc,
.colorspace = icd->current_fmt->colorspace,
goto eiciadd;
}
- if (icd->ops->init) {
- ret = icd->ops->init(icd);
- if (ret < 0)
- goto einit;
- }
-
/* Try to configure with default parameters */
ret = soc_camera_set_fmt(icf, &f);
if (ret < 0)
* and use_count == 1
*/
esfmt:
- if (icd->ops->release)
- icd->ops->release(icd);
-einit:
ici->ops->remove(icd);
eiciadd:
if (icl->power)
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct video_device *vdev = icd->vdev;
mutex_lock(&icd->video_lock);
icd->use_count--;
if (!icd->use_count) {
struct soc_camera_link *icl = to_soc_camera_link(icd);
- if (icd->ops->release)
- icd->ops->release(icd);
ici->ops->remove(icd);
if (icl->power)
icl->power(icd->pdev, 0);
vfree(icf);
- dev_dbg(vdev->parent, "camera device close\n");
+ dev_dbg(&icd->dev, "camera device close\n");
return 0;
}
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct video_device *vdev = icd->vdev;
int err = -EINVAL;
- dev_err(vdev->parent, "camera device read not implemented\n");
+ dev_err(&icd->dev, "camera device read not implemented\n");
return err;
}
mutex_lock(&icf->vb_vidq.vb_lock);
- if (videobuf_queue_is_busy(&icf->vb_vidq)) {
- dev_err(&icd->dev, "S_FMT denied: queue busy\n");
+ if (icf->vb_vidq.bufs[0]) {
+ dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
ret = -EBUSY;
goto unlock;
}
WARN_ON(priv != file->private_data);
- pix->width = icd->rect_current.width;
- pix->height = icd->rect_current.height;
+ pix->width = icd->user_width;
+ pix->height = icd->user_height;
pix->field = icf->vb_vidq.field;
pix->pixelformat = icd->current_fmt->fourcc;
pix->bytesperline = pix->width *
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int ret;
WARN_ON(priv != file->private_data);
mutex_lock(&icd->video_lock);
- v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, video, s_stream, 1);
+ v4l2_subdev_call(sd, video, s_stream, 1);
/* This calls buf_queue from host driver's videobuf_queue_ops */
ret = videobuf_streamon(&icf->vb_vidq);
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
WARN_ON(priv != file->private_data);
* remaining buffers. When the last buffer is freed, stop capture */
videobuf_streamoff(&icf->vb_vidq);
- v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, video, s_stream, 0);
+ v4l2_subdev_call(sd, video, s_stream, 0);
mutex_unlock(&icd->video_lock);
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int ret;
WARN_ON(priv != file->private_data);
- switch (ctrl->id) {
- case V4L2_CID_GAIN:
- if (icd->gain == (unsigned short)~0)
- return -EINVAL;
- ctrl->value = icd->gain;
- return 0;
- case V4L2_CID_EXPOSURE:
- if (icd->exposure == (unsigned short)~0)
- return -EINVAL;
- ctrl->value = icd->exposure;
- return 0;
- }
-
if (ici->ops->get_ctrl) {
ret = ici->ops->get_ctrl(icd, ctrl);
if (ret != -ENOIOCTLCMD)
return ret;
}
- return v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, core, g_ctrl, ctrl);
+ return v4l2_subdev_call(sd, core, g_ctrl, ctrl);
}
static int soc_camera_s_ctrl(struct file *file, void *priv,
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int ret;
WARN_ON(priv != file->private_data);
return ret;
}
- return v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, core, s_ctrl, ctrl);
+ return v4l2_subdev_call(sd, core, s_ctrl, ctrl);
}
static int soc_camera_cropcap(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->bounds = icd->rect_max;
- a->defrect.left = icd->rect_max.left;
- a->defrect.top = icd->rect_max.top;
- a->defrect.width = DEFAULT_WIDTH;
- a->defrect.height = DEFAULT_HEIGHT;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
+ return ici->ops->cropcap(icd, a);
}
static int soc_camera_g_crop(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int ret;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->c = icd->rect_current;
+ mutex_lock(&icf->vb_vidq.vb_lock);
+ ret = ici->ops->get_crop(icd, a);
+ mutex_unlock(&icf->vb_vidq.vb_lock);
- return 0;
+ return ret;
}
+/*
+ * According to the V4L2 API, drivers shall not update the struct v4l2_crop
+ * argument with the actual geometry, instead, the user shall use G_CROP to
+ * retrieve it. However, we expect camera host and client drivers to update
+ * the argument, which we then use internally, but do not return to the user.
+ */
static int soc_camera_s_crop(struct file *file, void *fh,
struct v4l2_crop *a)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_crop current_crop;
int ret;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n",
+ rect->width, rect->height, rect->left, rect->top);
+
/* Cropping is allowed during a running capture, guard consistency */
mutex_lock(&icf->vb_vidq.vb_lock);
- if (a->c.width > icd->rect_max.width)
- a->c.width = icd->rect_max.width;
-
- if (a->c.width < icd->width_min)
- a->c.width = icd->width_min;
-
- if (a->c.height > icd->rect_max.height)
- a->c.height = icd->rect_max.height;
-
- if (a->c.height < icd->height_min)
- a->c.height = icd->height_min;
+ /* If get_crop fails, we'll let host and / or client drivers decide */
+ ret = ici->ops->get_crop(icd, ¤t_crop);
- if (a->c.width + a->c.left > icd->rect_max.width + icd->rect_max.left)
- a->c.left = icd->rect_max.width + icd->rect_max.left -
- a->c.width;
-
- if (a->c.height + a->c.top > icd->rect_max.height + icd->rect_max.top)
- a->c.top = icd->rect_max.height + icd->rect_max.top -
- a->c.height;
-
- ret = ici->ops->set_crop(icd, &a->c);
- if (!ret)
- icd->rect_current = a->c;
+ /* Prohibit window size change with initialised buffers */
+ if (icf->vb_vidq.bufs[0] && !ret &&
+ (a->c.width != current_crop.c.width ||
+ a->c.height != current_crop.c.height)) {
+ dev_err(&icd->dev,
+ "S_CROP denied: queue initialised and sizes differ\n");
+ ret = -EBUSY;
+ } else {
+ ret = ici->ops->set_crop(icd, a);
+ }
mutex_unlock(&icf->vb_vidq.vb_lock);
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, core, g_chip_ident, id);
+ return v4l2_subdev_call(sd, core, g_chip_ident, id);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, core, g_register, reg);
+ return v4l2_subdev_call(sd, core, g_register, reg);
}
static int soc_camera_s_register(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, core, s_register, reg);
+ return v4l2_subdev_call(sd, core, s_register, reg);
}
#endif
goto ei2cnd;
}
- subdev->grp_id = (__u32)icd;
client = subdev->priv;
/* Use to_i2c_client(dev) to recover the i2c client */
struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct device *control = NULL;
+ struct v4l2_subdev *sd;
+ struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
int ret;
dev_info(dev, "Probing %s\n", dev_name(dev));
if (ret < 0)
goto eadddev;
- /* FIXME: this is racy, have to use driver-binding notification */
+ /*
+ * FIXME: this is racy, have to use driver-binding notification,
+ * when it is available
+ */
control = to_soc_camera_control(icd);
- if (!control || !control->driver ||
+ if (!control || !control->driver || !dev_get_drvdata(control) ||
!try_module_get(control->driver->owner)) {
icl->del_device(icl);
goto enodrv;
if (ret < 0)
goto eiufmt;
- icd->rect_current = icd->rect_max;
icd->field = V4L2_FIELD_ANY;
/* ..._video_start() will create a device node, so we have to protect */
if (ret < 0)
goto evidstart;
+ /* Try to improve our guess of a reasonable window format */
+ sd = soc_camera_to_subdev(icd);
+ if (!v4l2_subdev_call(sd, video, g_fmt, &f)) {
+ icd->user_width = f.fmt.pix.width;
+ icd->user_height = f.fmt.pix.height;
+ }
+
/* Do we have to sysfs_remove_link() before device_unregister()? */
- if (to_soc_camera_control(icd) &&
- sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
+ if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
"control"))
dev_warn(&icd->dev, "Failed creating the control symlink\n");
{
}
+static int default_cropcap(struct soc_camera_device *icd,
+ struct v4l2_cropcap *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, cropcap, a);
+}
+
+static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, g_crop, a);
+}
+
+static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, s_crop, a);
+}
+
+static void soc_camera_device_init(struct device *dev, void *pdata)
+{
+ dev->platform_data = pdata;
+ dev->bus = &soc_camera_bus_type;
+ dev->release = dummy_release;
+}
+
int soc_camera_host_register(struct soc_camera_host *ici)
{
struct soc_camera_host *ix;
if (!ici || !ici->ops ||
!ici->ops->try_fmt ||
!ici->ops->set_fmt ||
- !ici->ops->set_crop ||
!ici->ops->set_bus_param ||
!ici->ops->querycap ||
!ici->ops->init_videobuf ||
!ici->v4l2_dev.dev)
return -EINVAL;
+ if (!ici->ops->set_crop)
+ ici->ops->set_crop = default_s_crop;
+ if (!ici->ops->get_crop)
+ ici->ops->get_crop = default_g_crop;
+ if (!ici->ops->cropcap)
+ ici->ops->cropcap = default_cropcap;
+
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
if (ix->nr == ici->nr) {
list_for_each_entry(icd, &devices, list) {
if (icd->iface == ici->nr) {
+ void *pdata = icd->dev.platform_data;
/* The bus->remove will be called */
device_unregister(&icd->dev);
- /* Not before device_unregister(), .remove
- * needs parent to call ici->ops->remove() */
- icd->dev.parent = NULL;
-
- /* If the host module is loaded again, device_register()
- * would complain "already initialised" */
- memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj));
+ /*
+ * Not before device_unregister(), .remove
+ * needs parent to call ici->ops->remove().
+ * If the host module is loaded again, device_register()
+ * would complain "already initialised," since 2.6.32
+ * this is also needed to prevent use-after-free of the
+ * device private data.
+ */
+ memset(&icd->dev, 0, sizeof(icd->dev));
+ soc_camera_device_init(&icd->dev, pdata);
}
}
* man, stay reasonable... */
return -ENOMEM;
- icd->devnum = num;
- icd->dev.bus = &soc_camera_bus_type;
-
- icd->dev.release = dummy_release;
+ icd->devnum = num;
icd->use_count = 0;
icd->host_priv = NULL;
mutex_init(&icd->video_lock);
*/
static int soc_camera_video_start(struct soc_camera_device *icd)
{
- const struct v4l2_queryctrl *qctrl;
int ret;
if (!icd->dev.parent)
return ret;
}
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN);
- icd->gain = qctrl ? qctrl->default_value : (unsigned short)~0;
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
- icd->exposure = qctrl ? qctrl->default_value : (unsigned short)~0;
-
return 0;
}
icd->iface = icl->bus_id;
icd->pdev = &pdev->dev;
platform_set_drvdata(pdev, icd);
- icd->dev.platform_data = icl;
ret = soc_camera_device_register(icd);
if (ret < 0)
goto escdevreg;
+ soc_camera_device_init(&icd->dev, icl);
+
+ icd->user_width = DEFAULT_WIDTH;
+ icd->user_height = DEFAULT_HEIGHT;
+
return 0;
escdevreg: