@@ -174,6 +174,27 @@ static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf)
}
/**
+ * ceu_subdev - Wraps v4l2 sub-device and provides async notification
+ */
+struct ceu_subdev {
+ struct v4l2_subdev *v4l2_subdev;
+
+ /* async subdev notification */
+ struct v4l2_async_notifier notifier;
+ struct v4l2_async_subdev *asd;
+
+ unsigned int mbus_flags;
+ struct ceu_mbus_fmt mbus_fmt;
+
+ struct list_head list;
+};
+
+static inline struct ceu_subdev *to_ceu_subdev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct ceu_subdev, notifier);
+}
+
+/**
* ceu_device - CEU device instance
*/
struct ceu_device {
@@ -181,12 +202,12 @@ struct ceu_device {
struct video_device vdev;
struct v4l2_device v4l2_dev;
- struct v4l2_subdev *sensor;
- struct ceu_mbus_fmt sensor_fmt;
-
- struct v4l2_async_notifier notifier;
- struct v4l2_async_subdev asd;
- struct v4l2_async_subdev *asds[1];
+ /* subdevs list, number and current subdev */
+ struct list_head subdev_list;
+ unsigned int num_subdevs;
+ struct ceu_subdev *subdevs;
+ struct ceu_subdev *subdev;
+ unsigned int subdev_index;
/* vb2 queue, capture buffer list and active buffer pointer */
struct vb2_queue vb2_vq;
@@ -205,7 +226,6 @@ struct ceu_device {
enum v4l2_field field;
struct v4l2_pix_format v4l2_pix;
- unsigned long mbus_flags;
struct ceu_info *pdata;
};
@@ -518,11 +538,12 @@ static void ceu_videobuf_queue(struct vb2_buffer *vb)
static int ceu_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct ceu_device *ceudev = vb2_get_drv_priv(vq);
+ struct v4l2_subdev *v4l2_sd = ceudev->subdev->v4l2_subdev;
struct ceu_buffer *buf;
unsigned long irqflags;
int ret = 0;
- ret = v4l2_subdev_call(ceudev->sensor, video, s_stream, 1);
+ ret = v4l2_subdev_call(v4l2_sd, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD) {
v4l2_err(&ceudev->v4l2_dev, "stream on failed in subdev\n");
goto error_return_bufs;
@@ -553,7 +574,7 @@ static int ceu_start_streaming(struct vb2_queue *vq, unsigned int count)
return 0;
error_stop_sensor:
- v4l2_subdev_call(ceudev->sensor, video, s_stream, 0);
+ v4l2_subdev_call(v4l2_sd, video, s_stream, 0);
error_return_bufs:
spin_lock_irqsave(&ceudev->lock, irqflags);
@@ -569,10 +590,11 @@ static int ceu_start_streaming(struct vb2_queue *vq, unsigned int count)
static void ceu_stop_streaming(struct vb2_queue *vq)
{
struct ceu_device *ceudev = vb2_get_drv_priv(vq);
+ struct v4l2_subdev *v4l2_sd = ceudev->subdev->v4l2_subdev;
struct ceu_buffer *buf;
unsigned long irqflags;
- v4l2_subdev_call(ceudev->sensor, video, s_stream, 0);
+ v4l2_subdev_call(v4l2_sd, video, s_stream, 0);
spin_lock_irqsave(&ceudev->lock, irqflags);
if (ceudev->active) {
@@ -638,14 +660,14 @@ static unsigned int ceu_mbus_config_compatible(
*/
static int ceu_test_mbus_param(struct ceu_device *ceudev)
{
- struct v4l2_subdev *sd = ceudev->sensor;
+ struct v4l2_subdev *v4l2_sd = ceudev->subdev->v4l2_subdev;
unsigned long common_flags = CEU_BUS_FLAGS;
struct v4l2_mbus_config cfg = {
.type = V4L2_MBUS_PARALLEL,
};
int ret;
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ ret = v4l2_subdev_call(v4l2_sd, video, g_mbus_config, &cfg);
if (ret < 0 && ret == -ENOIOCTLCMD)
return ret;
else if (ret == -ENOIOCTLCMD)
@@ -721,10 +743,12 @@ static void ceu_set_sizes(struct ceu_device *ceudev)
*/
static int ceu_set_bus_params(struct ceu_device *ceudev)
{
- struct ceu_mbus_fmt *sensor_fmt = &ceudev->sensor_fmt;
+ struct ceu_subdev *ceu_sd = ceudev->subdev;
+ struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_subdev;
+ unsigned int mbus_flags = ceu_sd->mbus_flags;
struct v4l2_pix_format *pix = &ceudev->v4l2_pix;
unsigned long common_flags = CEU_BUS_FLAGS;
- struct v4l2_subdev *sd = ceudev->sensor;
u32 camcr, caifr, cdocr;
int ret;
@@ -748,7 +772,7 @@ static int ceu_set_bus_params(struct ceu_device *ceudev)
*/
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (ceudev->mbus_flags & CEU_FLAG_HSYNC_LOW)
+ if (mbus_flags & CEU_FLAG_HSYNC_LOW)
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
@@ -756,14 +780,14 @@ static int ceu_set_bus_params(struct ceu_device *ceudev)
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (ceudev->mbus_flags & CEU_FLAG_VSYNC_LOW)
+ if (mbus_flags & CEU_FLAG_VSYNC_LOW)
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ ret = v4l2_subdev_call(v4l2_sd, video, s_mbus_config, &cfg);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
@@ -794,20 +818,20 @@ static int ceu_set_bus_params(struct ceu_device *ceudev)
case V4L2_PIX_FMT_NV16:
cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
case V4L2_PIX_FMT_NV12:
- if (sensor_fmt->swapped)
- camcr |= sensor_fmt->fmt_order_swap;
+ if (mbus_fmt->swapped)
+ camcr |= mbus_fmt->fmt_order_swap;
else
- camcr |= sensor_fmt->fmt_order;
+ camcr |= mbus_fmt->fmt_order;
break;
/* swapped output formats */
case V4L2_PIX_FMT_NV61:
cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
case V4L2_PIX_FMT_NV21:
- if (sensor_fmt->swapped)
- camcr |= sensor_fmt->fmt_order;
+ if (mbus_fmt->swapped)
+ camcr |= mbus_fmt->fmt_order;
else
- camcr |= sensor_fmt->fmt_order_swap;
+ camcr |= mbus_fmt->fmt_order_swap;
break;
}
camcr |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
@@ -869,9 +893,10 @@ static int ceu_set_bus_params(struct ceu_device *ceudev)
*/
static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
{
- struct ceu_mbus_fmt *sensor_fmt = &ceudev->sensor_fmt;
+ struct ceu_subdev *ceu_sd = ceudev->subdev;
+ struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_subdev;
struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix;
- struct v4l2_subdev *sensor = ceudev->sensor;
struct v4l2_subdev_pad_config pad_cfg;
const struct ceu_fmt *ceu_fmt;
int ret;
@@ -912,8 +937,8 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
* Set format on sensor sub device: bus format is selected at
* format initialization time
*/
- v4l2_fill_mbus_format(&sd_format.format, pix, sensor_fmt->mbus_code);
- ret = v4l2_subdev_call(sensor, pad, set_fmt, &pad_cfg, &sd_format);
+ v4l2_fill_mbus_format(&sd_format.format, pix, mbus_fmt->mbus_code);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format);
if (ret)
return ret;
@@ -937,7 +962,9 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
*/
static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
{
- struct ceu_mbus_fmt *sensor_fmt = &ceudev->sensor_fmt;
+ struct ceu_subdev *ceu_sd = ceudev->subdev;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_subdev;
+ struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt;
int ret;
struct v4l2_subdev_format format = {
@@ -949,9 +976,8 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
return ret;
v4l2_fill_mbus_format(&format.format, &v4l2_fmt->fmt.pix,
- sensor_fmt->mbus_code);
- ret = v4l2_subdev_call(ceudev->sensor, pad,
- set_fmt, NULL, &format);
+ mbus_fmt->mbus_code);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format);
if (ret)
return ret;
@@ -1003,20 +1029,21 @@ static int ceu_set_default_fmt(struct ceu_device *ceudev)
*/
static int ceu_init_formats(struct ceu_device *ceudev)
{
- struct ceu_mbus_fmt *sensor_fmt = &ceudev->sensor_fmt;
- struct v4l2_subdev *sensor = ceudev->sensor;
+ struct ceu_subdev *ceu_sd = ceudev->subdev;
+ struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_subdev;
bool yuyv_bus_fmt = false;
- struct v4l2_subdev_mbus_code_enum mbus_fmt = {
+ struct v4l2_subdev_mbus_code_enum sd_mbus_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.index = 0,
};
/* Find out if sensor can produce any permutation of 8-bits yuyv */
while (!yuyv_bus_fmt &&
- !v4l2_subdev_call(sensor, pad, enum_mbus_code,
- NULL, &mbus_fmt)) {
- switch (mbus_fmt.code) {
+ !v4l2_subdev_call(v4l2_sd, pad, enum_mbus_code,
+ NULL, &sd_mbus_fmt)) {
+ switch (sd_mbus_fmt.code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_YVYU8_2X8:
case MEDIA_BUS_FMT_UYVY8_2X8:
@@ -1037,37 +1064,37 @@ static int ceu_init_formats(struct ceu_device *ceudev)
if (!yuyv_bus_fmt)
return -ENXIO;
- /*
- * Save the first encountered YUYV format as "sensor_fmt" and use it
+ /*
+ * Save the first encountered YUYV format as "mbus_fmt" and use it
* to output all planar YUV422 and YUV420 (NV*) formats to memory.
*/
- sensor_fmt->mbus_code = mbus_fmt.code;
- sensor_fmt->bps = 8;
+ mbus_fmt->mbus_code = sd_mbus_fmt.code;
+ mbus_fmt->bps = 8;
/* Annotate the selected bus format components ordering */
- switch (mbus_fmt.code) {
+ switch (sd_mbus_fmt.code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
- sensor_fmt->fmt_order = CEU_CAMCR_DTARY_8_YUYV;
- sensor_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YVYU;
- sensor_fmt->swapped = false;
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YUYV;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YVYU;
+ mbus_fmt->swapped = false;
break;
case MEDIA_BUS_FMT_YVYU8_2X8:
- sensor_fmt->fmt_order = CEU_CAMCR_DTARY_8_YVYU;
- sensor_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YUYV;
- sensor_fmt->swapped = true;
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YVYU;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YUYV;
+ mbus_fmt->swapped = true;
break;
case MEDIA_BUS_FMT_UYVY8_2X8:
- sensor_fmt->fmt_order = CEU_CAMCR_DTARY_8_UYVY;
- sensor_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_VYUY;
- sensor_fmt->swapped = false;
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_UYVY;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_VYUY;
+ mbus_fmt->swapped = false;
break;
case MEDIA_BUS_FMT_VYUY8_2X8:
- sensor_fmt->fmt_order = CEU_CAMCR_DTARY_8_VYUY;
- sensor_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_UYVY;
- sensor_fmt->swapped = true;
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_VYUY;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_UYVY;
+ mbus_fmt->swapped = true;
break;
}
@@ -1088,9 +1115,9 @@ static int ceu_init_formats(struct ceu_device *ceudev)
static int ceu_runtime_suspend(struct device *dev)
{
struct ceu_device *ceudev = dev_get_drvdata(dev);
- struct v4l2_subdev *sensor = ceudev->sensor;
+ struct v4l2_subdev *v4l2_sd = ceudev->subdev->v4l2_subdev;
- v4l2_subdev_call(sensor, core, s_power, 0);
+ v4l2_subdev_call(v4l2_sd, core, s_power, 0);
ceu_write(ceudev, CEU_CEIER, 0);
ceu_soft_reset(ceudev);
@@ -1104,9 +1131,9 @@ static int ceu_runtime_suspend(struct device *dev)
static int ceu_runtime_resume(struct device *dev)
{
struct ceu_device *ceudev = dev_get_drvdata(dev);
- struct v4l2_subdev *sensor = ceudev->sensor;
+ struct v4l2_subdev *v4l2_sd = ceudev->subdev->v4l2_subdev;
- v4l2_subdev_call(sensor, core, s_power, 1);
+ v4l2_subdev_call(v4l2_sd, core, s_power, 1);
ceu_soft_reset(ceudev);
@@ -1220,52 +1247,82 @@ static int ceu_enum_input(struct file *file, void *priv,
static int ceu_g_input(struct file *file, void *priv, unsigned int *i)
{
- *i = 0;
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ *i = ceudev->subdev_index;
return 0;
}
static int ceu_s_input(struct file *file, void *priv, unsigned int i)
{
- if (i > 0)
+ struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceu_sd_old;
+ int ret;
+
+ if (i >= ceudev->num_subdevs)
return -EINVAL;
+ ceu_sd_old = ceudev->subdev;
+ ceudev->subdev = &ceudev->subdevs[i];
+
+ /*
+ * This is enough to make sure we can generate all available formats,
+ * as long as we only support planar YUV422/YUV420 as memory output
+ * formats.
+ */
+ ret = ceu_init_formats(ceudev);
+ if (ret) {
+ ceudev->subdev = ceu_sd_old;
+ return -EINVAL;
+ }
+
+ /* now that we're sure we can use the sensor, power off the old one */
+ v4l2_subdev_call(ceu_sd_old->v4l2_subdev, core, s_power, 0);
+ v4l2_subdev_call(ceudev->subdev->v4l2_subdev, core, s_power, 1);
+
+ ceudev->subdev_index = i;
+
return 0;
}
static int ceu_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct ceu_device *ceudev = video_drvdata(file);
+ struct v4l2_subdev *v4l2_subdev = ceudev->subdev->v4l2_subdev;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return v4l2_subdev_call(ceudev->sensor, video, g_parm, a);
+ return v4l2_subdev_call(v4l2_subdev, video, g_parm, a);
}
static int ceu_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct ceu_device *ceudev = video_drvdata(file);
+ struct v4l2_subdev *v4l2_subdev = ceudev->subdev->v4l2_subdev;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return v4l2_subdev_call(ceudev->sensor, video, s_parm, a);
+ return v4l2_subdev_call(v4l2_subdev, video, s_parm, a);
}
static int ceu_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceu_sd = ceudev->subdev;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_subdev;
int ret;
struct v4l2_subdev_frame_size_enum fse = {
- .code = ceudev->sensor_fmt.mbus_code,
+ .code = ceu_sd->mbus_fmt.mbus_code,
.index = fsize->index,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- ret = v4l2_subdev_call(ceudev->sensor, pad, enum_frame_size,
+ ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_size,
NULL, &fse);
if (ret)
return ret;
@@ -1281,17 +1338,19 @@ static int ceu_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceu_sd = ceudev->subdev;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_subdev;
int ret;
struct v4l2_subdev_frame_interval_enum fie = {
- .code = ceudev->sensor_fmt.mbus_code,
+ .code = ceu_sd->mbus_fmt.mbus_code,
.index = fival->index,
.width = fival->width,
.height = fival->height,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- ret = v4l2_subdev_call(ceudev->sensor, pad, enum_frame_interval, NULL,
+ ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_interval, NULL,
&fie);
if (ret)
return ret;
@@ -1340,14 +1399,41 @@ void ceu_vdev_release(struct video_device *vdev)
kfree(ceudev);
}
+/**
+ * ceu_sensor_bound() - Set v4l2_subdev and select primary sensor
+ */
static int ceu_sensor_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
+ struct v4l2_subdev *v4l2_sd,
struct v4l2_async_subdev *asd)
{
struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev);
+ struct ceu_subdev *ceu_sd = to_ceu_subdev(notifier);
+
+ if (video_is_registered(&ceudev->vdev)) {
+ v4l2_err(&ceudev->v4l2_dev, "only supports one sub-device.\n");
+ return -EBUSY;
+ }
+
+ if (ceu_sd->mbus_flags | CEU_FLAG_PRIMARY_SENS &&
+ ceudev->subdev == NULL) {
+ ceudev->subdev_index = ceudev->num_subdevs;
+ ceudev->subdev = ceu_sd;
+ }
+
+ ceudev->num_subdevs++;
+ ceu_sd->v4l2_subdev = v4l2_sd;
+
+ return 0;
+}
+
+static int ceu_sensor_complete(struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
+ struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev);
struct video_device *vdev = &ceudev->vdev;
struct vb2_queue *q = &ceudev->vb2_vq;
+ struct v4l2_subdev *v4l2_sd;
int ret;
/* Initialize vb2 queue */
@@ -1365,8 +1451,15 @@ static int ceu_sensor_bound(struct v4l2_async_notifier *notifier,
if (ret)
return ret;
- /* Initialize formats and set default format on sensor */
- ceudev->sensor = subdev;
+ /*
+ * Make sure at least one sensor is primary and use it to initialize
+ * ceu formats
+ */
+ if (ceudev->subdev == NULL)
+ ceudev->subdev = list_first_entry(&ceudev->subdev_list,
+ struct ceu_subdev, list);
+ v4l2_sd = ceudev->subdev->v4l2_subdev;
+
ret = ceu_init_formats(ceudev);
if (ret)
return ret;
@@ -1380,7 +1473,7 @@ static int ceu_sensor_bound(struct v4l2_async_notifier *notifier,
vdev->v4l2_dev = v4l2_dev;
vdev->lock = &ceudev->mlock;
vdev->queue = &ceudev->vb2_vq;
- vdev->ctrl_handler = subdev->ctrl_handler;
+ vdev->ctrl_handler = v4l2_sd->ctrl_handler;
vdev->fops = &ceu_fops;
vdev->ioctl_ops = &ceu_ioctl_ops;
vdev->release = ceu_vdev_release;
@@ -1394,12 +1487,56 @@ static int ceu_sensor_bound(struct v4l2_async_notifier *notifier,
return ret;
}
+ dev_info(ceudev->dev, "Video device registered\n");
+
+ return 0;
+}
+
+/**
+ * Parse platform data to collect bus flags and async sensors description
+ */
+static int ceu_parse_platform_data(struct ceu_device *ceudev, void *pdata)
+{
+ struct ceu_async_subdev *async_sd;
+ struct ceu_info *info = pdata;
+ struct ceu_subdev *ceu_sd;
+ unsigned int i;
+
+ ceudev->pdata = pdata;
+ ceudev->subdev = NULL;
+ ceudev->subdev_index = 0;
+ ceudev->num_subdevs = 0;
+ ceudev->num_subdevs = info->num_subdevs;
+ ceudev->subdevs = devm_kcalloc(ceudev->dev, info->num_subdevs,
+ sizeof(*ceudev->subdevs),
+ GFP_KERNEL);
+ if (!ceudev->subdevs)
+ return -ENOMEM;
+
+ for (i = 0; i < info->num_subdevs; i++) {
+ ceu_sd = &ceudev->subdevs[i];
+ async_sd = &info->subdevs[i];
+
+ ceu_sd->asd = devm_kmalloc(ceudev->dev, sizeof(*ceu_sd->asd),
+ GFP_KERNEL);
+ if (!ceu_sd)
+ return -ENOMEM;
+
+ ceu_sd->mbus_flags = async_sd->flags;
+ ceu_sd->asd->match_type = V4L2_ASYNC_MATCH_I2C;
+ ceu_sd->asd->match.i2c.adapter_id = async_sd->i2c_adapter_id;
+ ceu_sd->asd->match.i2c.address = async_sd->i2c_address;
+
+ list_add_tail(&ceu_sd->list, &ceudev->subdev_list);
+ }
+
return 0;
}
static int ceu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct ceu_subdev *ceu_sd;
struct ceu_device *ceudev;
struct resource *res;
void __iomem *base;
@@ -1415,6 +1552,7 @@ static int ceu_probe(struct platform_device *pdev)
ceudev->dev = dev;
INIT_LIST_HEAD(&ceudev->capture);
+ INIT_LIST_HEAD(&ceudev->subdev_list);
spin_lock_init(&ceudev->lock);
mutex_init(&ceudev->mlock);
@@ -1440,13 +1578,12 @@ static int ceu_probe(struct platform_device *pdev)
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
/* TODO: implement OF parsing */
} else if (dev->platform_data) {
- ceudev->pdata = dev->platform_data;
- ceudev->mbus_flags = ceudev->pdata->flags;
- ceudev->asd.match_type = V4L2_ASYNC_MATCH_I2C;
- ceudev->asd.match.i2c.adapter_id =
- ceudev->pdata->sensor_i2c_adapter_id;
- ceudev->asd.match.i2c.address =
- ceudev->pdata->sensor_i2c_address;
+ ret = ceu_parse_platform_data(ceudev, dev->platform_data);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "CEU unable to parse platform data\n");
+ return ret;
+ }
} else {
dev_err(&pdev->dev, "CEU platform data not set.\n");
return -EINVAL;
@@ -1459,20 +1596,27 @@ static int ceu_probe(struct platform_device *pdev)
if (ret)
goto error_pm_disable;
- ceudev->asds[0] = &ceudev->asd;
- ceudev->notifier.v4l2_dev = &ceudev->v4l2_dev;
- ceudev->notifier.subdevs = ceudev->asds;
- ceudev->notifier.num_subdevs = 1;
- ceudev->notifier.bound = ceu_sensor_bound;
- ret = v4l2_async_notifier_register(&ceudev->v4l2_dev,
- &ceudev->notifier);
- if (ret)
- goto error_v4l2_unregister;
+ list_for_each_entry(ceu_sd, &ceudev->subdev_list, list) {
+ ceu_sd->notifier.v4l2_dev = &ceudev->v4l2_dev;
+ ceu_sd->notifier.subdevs = &ceu_sd->asd;
+ ceu_sd->notifier.num_subdevs = 1;
+ ceu_sd->notifier.bound = ceu_sensor_bound;
+ ceu_sd->notifier.complete = ceu_sensor_complete;
+ ret = v4l2_async_notifier_register(&ceudev->v4l2_dev,
+ &ceu_sd->notifier);
+ if (ret)
+ goto error_v4l2_unregister_notifiers;
+
+ }
return 0;
-error_v4l2_unregister:
+error_v4l2_unregister_notifiers:
+ list_for_each_entry(ceu_sd, &ceudev->subdev_list, list)
+ v4l2_async_unregister_subdev(ceu_sd->v4l2_subdev);
+
v4l2_device_unregister(&ceudev->v4l2_dev);
+
error_pm_disable:
pm_runtime_disable(dev);
@@ -1482,10 +1626,13 @@ static int ceu_probe(struct platform_device *pdev)
static int ceu_remove(struct platform_device *pdev)
{
struct ceu_device *ceudev = platform_get_drvdata(pdev);
+ struct ceu_subdev *ceu_sd;
pm_runtime_disable(ceudev->dev);
- v4l2_async_notifier_unregister(&ceudev->notifier);
+ list_for_each_entry(ceu_sd, &ceudev->subdev_list, list)
+ v4l2_async_unregister_subdev(ceu_sd->v4l2_subdev);
+
v4l2_device_unregister(&ceudev->v4l2_dev);
video_unregister_device(&ceudev->vdev);
Add support for registration of multiple subdevices to renesas CEU driver. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> --- drivers/media/platform/renesas-ceu.c | 323 +++++++++++++++++++++++++---------- 1 file changed, 235 insertions(+), 88 deletions(-) -- 2.7.4