@@ -26,6 +26,18 @@
/* ISP */
+int sun6i_csi_isp_complete(struct sun6i_csi_device *csi_dev,
+ struct v4l2_device *v4l2_dev)
+{
+ if (csi_dev->v4l2_dev && csi_dev->v4l2_dev != v4l2_dev)
+ return -EINVAL;
+
+ csi_dev->v4l2_dev = v4l2_dev;
+ csi_dev->media_dev = v4l2_dev->mdev;
+
+ return sun6i_csi_capture_setup(csi_dev);
+}
+
static int sun6i_csi_isp_detect(struct sun6i_csi_device *csi_dev)
{
struct device *dev = csi_dev->dev;
@@ -96,6 +108,9 @@ static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev)
goto error_media;
}
+ csi_dev->v4l2_dev = v4l2_dev;
+ csi_dev->media_dev = media_dev;
+
return 0;
error_media:
@@ -324,17 +339,27 @@ static int sun6i_csi_probe(struct platform_device *platform_dev)
if (ret)
goto error_resources;
- ret = sun6i_csi_v4l2_setup(csi_dev);
- if (ret)
- goto error_resources;
+ /*
+ * Register our own v4l2 and media devices when there is no ISP around.
+ * Otherwise the ISP will use async subdev registration with our bridge,
+ * which will provide v4l2 and media devices that are used to register
+ * the video interface.
+ */
+ if (!csi_dev->isp_available) {
+ ret = sun6i_csi_v4l2_setup(csi_dev);
+ if (ret)
+ goto error_resources;
+ }
ret = sun6i_csi_bridge_setup(csi_dev);
if (ret)
goto error_v4l2;
- ret = sun6i_csi_capture_setup(csi_dev);
- if (ret)
- goto error_bridge;
+ if (!csi_dev->isp_available) {
+ ret = sun6i_csi_capture_setup(csi_dev);
+ if (ret)
+ goto error_bridge;
+ }
return 0;
@@ -342,7 +367,8 @@ static int sun6i_csi_probe(struct platform_device *platform_dev)
sun6i_csi_bridge_cleanup(csi_dev);
error_v4l2:
- sun6i_csi_v4l2_cleanup(csi_dev);
+ if (!csi_dev->isp_available)
+ sun6i_csi_v4l2_cleanup(csi_dev);
error_resources:
sun6i_csi_resources_cleanup(csi_dev);
@@ -356,7 +382,10 @@ static int sun6i_csi_remove(struct platform_device *pdev)
sun6i_csi_capture_cleanup(csi_dev);
sun6i_csi_bridge_cleanup(csi_dev);
- sun6i_csi_v4l2_cleanup(csi_dev);
+
+ if (!csi_dev->isp_available)
+ sun6i_csi_v4l2_cleanup(csi_dev);
+
sun6i_csi_resources_cleanup(csi_dev);
return 0;
@@ -36,6 +36,8 @@ struct sun6i_csi_v4l2 {
struct sun6i_csi_device {
struct device *dev;
+ struct v4l2_device *v4l2_dev;
+ struct media_device *media_dev;
struct sun6i_csi_v4l2 v4l2;
struct sun6i_csi_bridge bridge;
@@ -53,4 +55,9 @@ struct sun6i_csi_variant {
unsigned long clock_mod_rate;
};
+/* ISP */
+
+int sun6i_csi_isp_complete(struct sun6i_csi_device *csi_dev,
+ struct v4l2_device *v4l2_dev);
+
#endif
@@ -653,6 +653,7 @@ sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
bool enabled;
+ int ret;
switch (source->endpoint.base.port) {
case SUN6I_CSI_PORT_PARALLEL:
@@ -667,6 +668,16 @@ sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
source->subdev = remote_subdev;
+ if (csi_dev->isp_available) {
+ /*
+ * Hook to the first available remote subdev to get v4l2 and
+ * media devices and register the capture device then.
+ */
+ ret = sun6i_csi_isp_complete(csi_dev, remote_subdev->v4l2_dev);
+ if (ret)
+ return ret;
+ }
+
return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK,
remote_subdev, enabled);
}
@@ -679,6 +690,9 @@ sun6i_csi_bridge_notifier_complete(struct v4l2_async_notifier *notifier)
bridge.notifier);
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ if (csi_dev->isp_available)
+ return 0;
+
return v4l2_device_register_subdev_nodes(v4l2_dev);
}
@@ -752,7 +766,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
{
struct device *dev = csi_dev->dev;
struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
- struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
struct v4l2_subdev *subdev = &bridge->subdev;
struct v4l2_async_notifier *notifier = &bridge->notifier;
struct media_pad *pads = bridge->pads;
@@ -793,7 +807,11 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
/* V4L2 Subdev */
- ret = v4l2_device_register_subdev(v4l2_dev, subdev);
+ if (csi_dev->isp_available)
+ ret = v4l2_async_register_subdev(subdev);
+ else
+ ret = v4l2_device_register_subdev(v4l2_dev, subdev);
+
if (ret) {
dev_err(dev, "failed to register v4l2 subdev: %d\n", ret);
goto error_media_entity;
@@ -810,7 +828,10 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2,
SUN6I_CSI_PORT_MIPI_CSI2, NULL);
- ret = v4l2_async_nf_register(v4l2_dev, notifier);
+ if (csi_dev->isp_available)
+ ret = v4l2_async_subdev_nf_register(subdev, notifier);
+ else
+ ret = v4l2_async_nf_register(v4l2_dev, notifier);
if (ret) {
dev_err(dev, "failed to register v4l2 async notifier: %d\n",
ret);
@@ -822,7 +843,10 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
error_v4l2_async_notifier:
v4l2_async_nf_cleanup(notifier);
- v4l2_device_unregister_subdev(subdev);
+ if (csi_dev->isp_available)
+ v4l2_async_unregister_subdev(subdev);
+ else
+ v4l2_device_unregister_subdev(subdev);
error_media_entity:
media_entity_cleanup(&subdev->entity);
@@ -570,7 +570,7 @@ static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
struct sun6i_csi_capture *capture = &csi_dev->capture;
- struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
unsigned long size = capture->format.fmt.pix.sizeimage;
@@ -889,7 +889,7 @@ static int sun6i_csi_capture_link_validate(struct media_link *link)
struct video_device *video_dev =
media_entity_to_video_device(link->sink->entity);
struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);
- struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
const struct sun6i_csi_capture_format *capture_format;
const struct sun6i_csi_bridge_format *bridge_format;
unsigned int capture_width, capture_height;
@@ -971,7 +971,7 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
{
struct sun6i_csi_capture *capture = &csi_dev->capture;
struct sun6i_csi_capture_state *state = &capture->state;
- struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;
struct video_device *video_dev = &capture->video_dev;
struct vb2_queue *queue = &capture->queue;
@@ -980,6 +980,10 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
struct v4l2_pix_format *pix_format = &format->fmt.pix;
int ret;
+ /* This may happen with multiple bridge notifier bound calls. */
+ if (state->setup)
+ return 0;
+
/* State */
INIT_LIST_HEAD(&state->queue);
@@ -1055,6 +1059,7 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
ret = media_create_pad_link(&bridge_subdev->entity,
SUN6I_CSI_BRIDGE_PAD_SOURCE,
&video_dev->entity, 0,
+ csi_dev->isp_available ? 0 :
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (ret < 0) {
@@ -1065,6 +1070,8 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
goto error_video_device;
}
+ state->setup = true;
+
return 0;
error_video_device:
@@ -1083,7 +1090,13 @@ void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev)
struct sun6i_csi_capture *capture = &csi_dev->capture;
struct video_device *video_dev = &capture->video_dev;
+ /* This may happen if async registration failed to complete. */
+ if (!capture->state.setup)
+ return;
+
vb2_video_unregister_device(video_dev);
media_entity_cleanup(&video_dev->entity);
mutex_destroy(&capture->lock);
+
+ capture->state.setup = false;
}
@@ -45,6 +45,7 @@ struct sun6i_csi_capture_state {
unsigned int sequence;
bool streaming;
+ bool setup;
};
struct sun6i_csi_capture {