/* 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;
goto error_media;
}
+ csi_dev->v4l2_dev = v4l2_dev;
+ csi_dev->media_dev = media_dev;
+
return 0;
error_media:
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;
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);
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;
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;
unsigned long clock_mod_rate;
};
+/* ISP */
+
+int sun6i_csi_isp_complete(struct sun6i_csi_device *csi_dev,
+ struct v4l2_device *v4l2_dev);
+
#endif
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:
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);
}
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);
}
{
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;
/* 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;
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);
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);
{
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;
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;
{
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;
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);
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) {
goto error_video_device;
}
+ state->setup = true;
+
return 0;
error_video_device:
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;
}
unsigned int sequence;
bool streaming;
+ bool setup;
};
struct sun6i_csi_capture {