From 3ba5e8d7839d86d1f358f917eecc0c603a8fe13f Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Fri, 6 Jan 2023 11:25:09 +0100 Subject: [PATCH 057/391] media: sun6i-csi: Add multicamera support for parallel bus Multiple cameras may be connected to parallel bus eg. in a tablet. Allow to register multiple parallel bus subdevices and switch between them by enabling/disabling media graph links. Signed-off-by: Ondrej Jirman --- .../sunxi/sun6i-csi/sun6i_csi_bridge.c | 51 +++++++++++-------- .../sunxi/sun6i-csi/sun6i_csi_bridge.h | 4 +- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index d3a8b18ff..73c17284f 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -207,12 +207,12 @@ static void sun6i_csi_bridge_disable(struct sun6i_csi_device *csi_dev) static void sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev, - struct v4l2_subdev_state *state) + struct v4l2_subdev_state *state, + struct sun6i_csi_bridge_source *source) { struct device *dev = csi_dev->dev; struct regmap *regmap = csi_dev->regmap; - struct v4l2_fwnode_endpoint *endpoint = - &csi_dev->bridge.source_parallel.endpoint; + struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; unsigned char bus_width = endpoint->bus.parallel.bus_width; unsigned int flags = endpoint->bus.parallel.flags; const struct v4l2_mbus_framefmt *sink_format; @@ -380,10 +380,8 @@ static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev, struct sun6i_csi_bridge_source *source, struct v4l2_subdev_state *state) { - struct sun6i_csi_bridge *bridge = &csi_dev->bridge; - - if (source == &bridge->source_parallel) - sun6i_csi_bridge_configure_parallel(csi_dev, state); + if (source->endpoint.bus_type == V4L2_MBUS_PARALLEL) + sun6i_csi_bridge_configure_parallel(csi_dev, state, source); else sun6i_csi_bridge_configure_mipi_csi2(csi_dev, state); @@ -399,11 +397,11 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) struct media_pad *local_pad = &bridge->pads[SUN6I_CSI_BRIDGE_PAD_SINK]; bool capture_streaming = csi_dev->capture.state.streaming; struct device *dev = csi_dev->dev; - struct sun6i_csi_bridge_source *source; + struct sun6i_csi_bridge_source *source = NULL; struct v4l2_subdev *source_subdev; struct media_pad *remote_pad; struct v4l2_subdev_state *state; - int ret; + int ret, i; /* Source */ @@ -416,10 +414,20 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity); - if (source_subdev == bridge->source_parallel.subdev) - source = &bridge->source_parallel; - else + if (source_subdev == bridge->source_mipi_csi2.subdev) { source = &bridge->source_mipi_csi2; + } else { + for (i = 0; i < SUN6I_CSI_SOURCE_PARALLEL_MAX; i++) { + if (source_subdev == bridge->source_parallel[i].subdev) { + source = &bridge->source_parallel[i]; + break; + } + } + } + if (!source) { + dev_err(dev, "bridge source not found\n"); + return -ENODEV; + } if (!on) { v4l2_subdev_call(source_subdev, video, s_stream, 0); @@ -635,10 +643,10 @@ sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier, switch (source->endpoint.base.port) { case SUN6I_CSI_PORT_PARALLEL: - enabled = true; + enabled = source->endpoint.base.id == 0; break; case SUN6I_CSI_PORT_MIPI_CSI2: - enabled = !bridge->source_parallel.expected; + enabled = !bridge->source_parallel[0].expected; break; default: return -EINVAL; @@ -684,7 +692,7 @@ sun6i_csi_bridge_notifier_ops = { static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev, struct sun6i_csi_bridge_source *source, - u32 port, + u32 port, u32 ep, enum v4l2_mbus_type *bus_types) { struct device *dev = csi_dev->dev; @@ -694,7 +702,7 @@ static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev, struct fwnode_handle *handle; int ret; - handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, ep, 0); if (!handle) return -ENODEV; @@ -753,7 +761,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) V4L2_MBUS_BT656, V4L2_MBUS_INVALID }; - int ret; + int ret, i; /* V4L2 Subdev */ @@ -801,11 +809,12 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) v4l2_async_nf_init(notifier); notifier->ops = &sun6i_csi_bridge_notifier_ops; - sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel, - SUN6I_CSI_PORT_PARALLEL, - parallel_mbus_types); + for (i = 0; i < SUN6I_CSI_SOURCE_PARALLEL_MAX; i++) + sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel[i], + SUN6I_CSI_PORT_PARALLEL, i, + parallel_mbus_types); sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2, - SUN6I_CSI_PORT_MIPI_CSI2, NULL); + SUN6I_CSI_PORT_MIPI_CSI2, 0, NULL); if (csi_dev->isp_available) ret = v4l2_async_subdev_nf_register(subdev, notifier); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h index 7754eaaab..94f080568 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h @@ -38,12 +38,14 @@ struct sun6i_csi_bridge_async_subdev { struct sun6i_csi_bridge_source *source; }; +#define SUN6I_CSI_SOURCE_PARALLEL_MAX 2 + struct sun6i_csi_bridge { struct v4l2_subdev subdev; struct v4l2_async_notifier notifier; struct media_pad pads[2]; - struct sun6i_csi_bridge_source source_parallel; + struct sun6i_csi_bridge_source source_parallel[SUN6I_CSI_SOURCE_PARALLEL_MAX]; struct sun6i_csi_bridge_source source_mipi_csi2; }; -- 2.35.3