build/patch/kernel/archive/sunxi-6.1/patches.megous/media-sun6i-csi-Add-support-for-multiple-endpoints.patch

332 lines
11 KiB
Diff

From c201ae84a0ddf1d06e5dddf524e0100aae3c7f3c Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
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 <megi@xff.cz>
---
.../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 <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
#include <media/videobuf2-v4l2.h>
#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