From c201ae84a0ddf1d06e5dddf524e0100aae3c7f3c Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sat, 29 Oct 2022 00:01:20 +0200 Subject: [PATCH 034/389] media: sun6i-csi: Add support for multiple endpoints Devices like tablets and phones can have multiple sensors attached to the parallel camera interface bus. Add support for switching between multiple cameras. Signed-off-by: Ondrej Jirman --- .../platform/sunxi/sun6i-csi/sun6i_csi.c | 54 ++++++++++--------- .../platform/sunxi/sun6i-csi/sun6i_csi.h | 19 +++++-- .../platform/sunxi/sun6i-csi/sun6i_video.c | 36 ++++++++++--- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 8bf5049dde91..66d267168c82 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -32,18 +32,17 @@ /* TODO add 10&12 bit YUV, RGB support */ bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev, - u32 pixformat, u32 mbus_code) + u32 pixformat, u32 mbus_code, + struct v4l2_fwnode_endpoint *vep) { - struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; - /* * Some video receivers have the ability to be compatible with * 8bit and 16bit bus width. * Identify the media bus format from device tree. */ - if ((v4l2->v4l2_ep.bus_type == V4L2_MBUS_PARALLEL - || v4l2->v4l2_ep.bus_type == V4L2_MBUS_BT656) - && v4l2->v4l2_ep.bus.parallel.bus_width == 16) { + if ((vep->bus_type == V4L2_MBUS_PARALLEL + || vep->bus_type == V4L2_MBUS_BT656) + && vep->bus.parallel.bus_width == 16) { switch (pixformat) { case V4L2_PIX_FMT_NV12_16L16: case V4L2_PIX_FMT_NV12: @@ -330,9 +329,9 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_device *csi_dev, return CSI_INPUT_SEQ_YUYV; } -static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev) +static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev, + struct v4l2_fwnode_endpoint *endpoint) { - struct v4l2_fwnode_endpoint *endpoint = &csi_dev->v4l2.v4l2_ep; struct sun6i_csi_config *config = &csi_dev->config; unsigned char bus_width; u32 flags; @@ -530,14 +529,15 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev) } int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev, - struct sun6i_csi_config *config) + struct sun6i_csi_config *config, + struct v4l2_fwnode_endpoint *vep) { if (!config) return -EINVAL; memcpy(&csi_dev->config, config, sizeof(csi_dev->config)); - sun6i_csi_setup_bus(csi_dev); + sun6i_csi_setup_bus(csi_dev, vep); sun6i_csi_set_format(csi_dev); sun6i_csi_set_window(csi_dev); @@ -590,7 +590,8 @@ static const struct media_device_ops sun6i_csi_media_ops = { static int sun6i_csi_link_entity(struct sun6i_csi_device *csi_dev, struct media_entity *entity, - struct fwnode_handle *fwnode) + struct fwnode_handle *fwnode, + u32 link_flags) { struct media_entity *sink; struct media_pad *sink_pad; @@ -613,9 +614,7 @@ static int sun6i_csi_link_entity(struct sun6i_csi_device *csi_dev, dev_dbg(csi_dev->dev, "creating %s:%u -> %s:%u link\n", entity->name, src_pad_index, sink->name, sink_pad->index); ret = media_create_pad_link(entity, src_pad_index, sink, - sink_pad->index, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); + sink_pad->index, link_flags); if (ret < 0) { dev_err(csi_dev->dev, "failed to create %s:%u -> %s:%u link\n", entity->name, src_pad_index, @@ -633,18 +632,24 @@ static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier) v4l2.notifier); struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; + u32 link_flags = MEDIA_LNK_FL_ENABLED; struct v4l2_subdev *sd; int ret; dev_dbg(csi_dev->dev, "notify complete, all subdevs registered\n"); - sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list); - if (!sd) + if (list_empty(&v4l2_dev->subdevs)) return -EINVAL; - ret = sun6i_csi_link_entity(csi_dev, &sd->entity, sd->fwnode); - if (ret < 0) - return ret; + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + ret = sun6i_csi_link_entity(csi_dev, &sd->entity, + sd->fwnode, link_flags); + if (ret < 0) + return ret; + + /* only enable the first link */ + link_flags = 0; + } ret = v4l2_device_register_subdev_nodes(v4l2_dev); if (ret < 0) @@ -661,17 +666,18 @@ static int sun6i_csi_fwnode_parse(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev); + struct sun6i_csi_async_subdev *casd = + container_of(asd, struct sun6i_csi_async_subdev, asd); - if (vep->base.port || vep->base.id) { - dev_warn(dev, "Only support a single port with one endpoint\n"); + if (vep->base.port) { + dev_err(dev, "Only remote entities with a single port are supported\n"); return -ENOTCONN; } switch (vep->bus_type) { case V4L2_MBUS_PARALLEL: case V4L2_MBUS_BT656: - csi_dev->v4l2.v4l2_ep = *vep; + casd->vep = *vep; return 0; default: dev_err(dev, "Unsupported media bus type\n"); @@ -727,7 +733,7 @@ static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev) ret = v4l2_async_nf_parse_fwnode_endpoints(dev, notifier, sizeof(struct - v4l2_async_subdev), + sun6i_csi_async_subdev), sun6i_csi_fwnode_parse); if (ret) goto error_video; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index 5559c1b7d6f0..4629f5c29356 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -10,6 +10,7 @@ #include #include +#include #include #include "sun6i_video.h" @@ -44,10 +45,7 @@ struct sun6i_csi_config { struct sun6i_csi_v4l2 { struct v4l2_device v4l2_dev; struct media_device media_dev; - struct v4l2_async_notifier notifier; - /* video port settings */ - struct v4l2_fwnode_endpoint v4l2_ep; }; struct sun6i_csi_device { @@ -69,16 +67,25 @@ struct sun6i_csi_variant { unsigned long clock_mod_rate; }; +struct sun6i_csi_async_subdev { + struct v4l2_async_subdev asd; /* must be first */ + + /* csi side video port settings for this subdev */ + struct v4l2_fwnode_endpoint vep; +}; + /** * sun6i_csi_is_format_supported() - check if the format supported by csi * @csi_dev: pointer to the csi device * @pixformat: v4l2 pixel format (V4L2_PIX_FMT_*) * @mbus_code: media bus format code (MEDIA_BUS_FMT_*) + * @vep: parsed CSI side bus endpoint configuration * * Return: true if format is supported, false otherwise. */ bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev, - u32 pixformat, u32 mbus_code); + u32 pixformat, u32 mbus_code, + struct v4l2_fwnode_endpoint *vep); /** * sun6i_csi_set_power() - power on/off the csi @@ -93,11 +100,13 @@ int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable); * sun6i_csi_update_config() - update the csi register settings * @csi_dev: pointer to the csi device * @config: see struct sun6i_csi_config + * @vep: parsed CSI side bus endpoint configuration * * Return: 0 if successful, error code otherwise. */ int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev, - struct sun6i_csi_config *config); + struct sun6i_csi_config *config, + struct v4l2_fwnode_endpoint *vep); /** * sun6i_csi_update_buf_addr() - update the csi frame buffer address diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index 82425fe1fb31..077875ec2dc4 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -95,7 +95,8 @@ static void sun6i_video_buffer_configure(struct sun6i_csi_device *csi_dev, sun6i_csi_update_buf_addr(csi_dev, csi_buffer->dma_addr); } -static void sun6i_video_configure(struct sun6i_csi_device *csi_dev) +static void sun6i_video_configure(struct sun6i_csi_device *csi_dev, + struct v4l2_fwnode_endpoint *vep) { struct sun6i_video *video = &csi_dev->video; struct sun6i_csi_config config = { 0 }; @@ -106,7 +107,7 @@ static void sun6i_video_configure(struct sun6i_csi_device *csi_dev) config.width = video->format.fmt.pix.width; config.height = video->format.fmt.pix.height; - sun6i_csi_update_config(csi_dev, &config); + sun6i_csi_update_config(csi_dev, &config, vep); } /* Queue */ @@ -175,6 +176,7 @@ static int sun6i_video_start_streaming(struct vb2_queue *queue, struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); struct sun6i_video *video = &csi_dev->video; struct video_device *video_dev = &video->video_dev; + struct sun6i_csi_async_subdev *casd; struct sun6i_csi_buffer *buf; struct sun6i_csi_buffer *next_buf; struct v4l2_subdev *subdev; @@ -198,7 +200,9 @@ static int sun6i_video_start_streaming(struct vb2_queue *queue, goto error_media_pipeline; } - sun6i_video_configure(csi_dev); + casd = container_of(subdev->asd, struct sun6i_csi_async_subdev, asd); + + sun6i_video_configure(csi_dev, &casd->vep); spin_lock_irqsave(&video->dma_queue_lock, flags); @@ -601,11 +605,16 @@ static const struct v4l2_file_operations sun6i_video_fops = { /* Media Entity */ static int sun6i_video_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) + struct v4l2_subdev_format *fmt, + struct v4l2_fwnode_endpoint **vep) { if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); + struct sun6i_csi_async_subdev *casd = + container_of(sd->asd, struct sun6i_csi_async_subdev, asd); + + *vep = &casd->vep; fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; @@ -622,6 +631,7 @@ static int sun6i_video_link_validate(struct media_link *link) struct sun6i_csi_device *csi_dev = video_get_drvdata(vdev); struct sun6i_video *video = &csi_dev->video; struct v4l2_subdev_format source_fmt; + struct v4l2_fwnode_endpoint *vep; int ret; video->mbus_code = 0; @@ -632,13 +642,13 @@ static int sun6i_video_link_validate(struct media_link *link) return -ENOLINK; } - ret = sun6i_video_link_validate_get_format(link->source, &source_fmt); + ret = sun6i_video_link_validate_get_format(link->source, &source_fmt, &vep); if (ret < 0) return ret; if (!sun6i_csi_is_format_supported(csi_dev, video->format.fmt.pix.pixelformat, - source_fmt.format.code)) { + source_fmt.format.code, vep)) { dev_err(csi_dev->dev, "Unsupported pixformat: 0x%x with mbus code: 0x%x!\n", video->format.fmt.pix.pixelformat, @@ -660,8 +670,20 @@ static int sun6i_video_link_validate(struct media_link *link) return 0; } +static int sun6i_video_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + /* Allow to enable one link only. */ + if ((flags & MEDIA_LNK_FL_ENABLED) && media_pad_remote_pad_first(local)) + return -EBUSY; + + return 0; +} + static const struct media_entity_operations sun6i_video_media_ops = { - .link_validate = sun6i_video_link_validate + .link_validate = sun6i_video_link_validate, + .link_setup = sun6i_video_link_setup, }; /* Video */ -- 2.35.3