332 lines
11 KiB
Diff
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
|
|
|