This part only concerns the rework of the CSI driver to support the MIPI CSI-2
and ISP workflows.
Every patch was reviewed at this point and the whole thing looks good to go.
Since this multi-part series has been going on for a while, it would be great
to see it merged soon!
Changes since v6:
- Removed merged patches;
- Added collected tags;
- Rebased on the latest media tree;
- Followed media/video pipeline helpers changes.
Changes since v5:
- Rebased on latest media tree;
- Switched to using media_pad_remote_pad_first;
- Switched to using media_pad_remote_pad_unique;
- Declared variant const to avoid a warning.
Changes since v4:
- Removed the v4l2 controls handler from the driver;
- Removed the info message about video device registration;
- Fixed "literature" typos;
- Moved patches dependent on the ISP driver to its dedicated series;
- Rebased on the latest media tree;
- Added collected tags;
Changes since v3:
- Updated Kconfig to follow the latest media-wide changes;
- Rebased on latest changes to the driver (JPEG/sRGB colorspaces);
- Added helper to get a single enabled link for an entity's pad, to replace
source selection at link_validate time and select the remote source at
stream on time instead;
- Kept clock-managed regmap mmio;
- Added collected review tags;
- Various cosmetic cleanups;
Changes since all-in-one v2:
- Reworked capture video device registration, which stays in the main path.
- Reworked async subdev handling with a dedicated structure holding the
corresponding source to avoid matching in the driver;
- Added mutex for mbus format serialization;
- Remove useless else in link_validate;
- Reworked commit logs to include missing information;
- Cleaned up Kconfig, added PM dependency;
- Moved platform-specific clock rate to of match data;
- Added collected Reviewed-by tags;
- Updated copyright years;
Paul Kocialkowski (28):
media: sun6i-csi: Add bridge v4l2 subdev with port management
media: sun6i-csi: Rename sun6i_video to sun6i_csi_capture
media: sun6i-csi: Add capture state using vsync for page flip
media: sun6i-csi: Rework register definitions, invert misleading
fields
media: sun6i-csi: Add dimensions and format helpers to capture
media: sun6i-csi: Implement address configuration without indirection
media: sun6i-csi: Split stream sequences and irq code in capture
media: sun6i-csi: Move power management to runtime pm in capture
media: sun6i-csi: Move register configuration to capture
media: sun6i-csi: Rework capture format management with helper
media: sun6i-csi: Remove custom format helper and rework configure
media: sun6i-csi: Add bridge dimensions and format helpers
media: sun6i-csi: Get mbus code from bridge instead of storing it
media: sun6i-csi: Tidy capture configure code
media: sun6i-csi: Introduce bridge format structure, list and helper
media: sun6i-csi: Introduce capture format structure, list and helper
media: sun6i-csi: Configure registers from format tables
media: sun6i-csi: Introduce format match structure, list and helper
media: sun6i-csi: Implement capture link validation with logic
media: sun6i-csi: Get bridge subdev directly in capture stream ops
media: sun6i-csi: Move hardware control to the bridge
media: sun6i-csi: Rename the capture video device to sun6i-csi-capture
media: sun6i-csi: Cleanup headers and includes, update copyright lines
media: sun6i-csi: Add support for MIPI CSI-2 to the bridge code
media: sun6i-csi: Only configure capture when streaming
media: sun6i-csi: Add extra checks to the interrupt routine
media: sun6i-csi: Request a shared interrupt
MAINTAINERS: Add myself as sun6i-csi maintainer and rename/move entry
MAINTAINERS | 17 +-
.../media/platform/sunxi/sun6i-csi/Makefile | 2 +-
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 751 +-----------
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 141 +--
.../sunxi/sun6i-csi/sun6i_csi_bridge.c | 844 +++++++++++++
.../sunxi/sun6i-csi/sun6i_csi_bridge.h | 69 ++
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 1089 +++++++++++++++++
.../sunxi/sun6i-csi/sun6i_csi_capture.h | 88 ++
.../platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 362 +++---
.../platform/sunxi/sun6i-csi/sun6i_video.c | 733 -----------
.../platform/sunxi/sun6i-csi/sun6i_video.h | 35 -
11 files changed, 2338 insertions(+), 1793 deletions(-)
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
delete mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
delete mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
--
2.38.1
Create minimal helpers that split the enable/disable flow, which will
make it easier to move control over to the bridge later on.
Generally speaking the goal is to move register configuration to
the capture code and later split it with the bridge code.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 26 ---------
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 7 ---
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 58 ++++++++++++++++++-
3 files changed, 55 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 5e03069bd4c3..d5318e2b04df 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -535,32 +535,6 @@ int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
return 0;
}
-void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable)
-{
- struct regmap *regmap = csi_dev->regmap;
-
- if (!enable) {
- regmap_update_bits(regmap, SUN6I_CSI_CAP_REG,
- SUN6I_CSI_CAP_VCAP_ON, 0);
- regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0);
- return;
- }
-
- regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG,
- SUN6I_CSI_CH_INT_STA_CLEAR);
- regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG,
- SUN6I_CSI_CH_INT_EN_VS |
- SUN6I_CSI_CH_INT_EN_HB_OF |
- SUN6I_CSI_CH_INT_EN_FIFO2_OF |
- SUN6I_CSI_CH_INT_EN_FIFO1_OF |
- SUN6I_CSI_CH_INT_EN_FIFO0_OF |
- SUN6I_CSI_CH_INT_EN_FD |
- SUN6I_CSI_CH_INT_EN_CD);
-
- regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON,
- SUN6I_CSI_CAP_VCAP_ON);
-}
-
/* Media */
static const struct media_device_ops sun6i_csi_media_ops = {
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 75867cde7985..6fe40a56d6df 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -96,13 +96,6 @@ int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable);
int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
struct sun6i_csi_config *config);
-/**
- * sun6i_csi_set_stream() - start/stop csi streaming
- * @csi_dev: pointer to the csi device
- * @enable: start/stop
- */
-void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable);
-
/* get bpp form v4l2 pixformat */
static inline int sun6i_csi_get_bpp(unsigned int pixformat)
{
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index cb23dcdf250f..e611f8c94d9d 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -107,6 +107,51 @@ static bool sun6i_csi_capture_format_check(u32 format)
/* Capture */
+static void sun6i_csi_capture_irq_enable(struct sun6i_csi_device *csi_dev)
+{
+ struct regmap *regmap = csi_dev->regmap;
+
+ regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG,
+ SUN6I_CSI_CH_INT_EN_VS |
+ SUN6I_CSI_CH_INT_EN_HB_OF |
+ SUN6I_CSI_CH_INT_EN_FIFO2_OF |
+ SUN6I_CSI_CH_INT_EN_FIFO1_OF |
+ SUN6I_CSI_CH_INT_EN_FIFO0_OF |
+ SUN6I_CSI_CH_INT_EN_FD |
+ SUN6I_CSI_CH_INT_EN_CD);
+}
+
+static void sun6i_csi_capture_irq_disable(struct sun6i_csi_device *csi_dev)
+{
+ struct regmap *regmap = csi_dev->regmap;
+
+ regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0);
+}
+
+static void sun6i_csi_capture_irq_clear(struct sun6i_csi_device *csi_dev)
+{
+ struct regmap *regmap = csi_dev->regmap;
+
+ regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0);
+ regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG,
+ SUN6I_CSI_CH_INT_STA_CLEAR);
+}
+
+static void sun6i_csi_capture_enable(struct sun6i_csi_device *csi_dev)
+{
+ struct regmap *regmap = csi_dev->regmap;
+
+ regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON,
+ SUN6I_CSI_CAP_VCAP_ON);
+}
+
+static void sun6i_csi_capture_disable(struct sun6i_csi_device *csi_dev)
+{
+ struct regmap *regmap = csi_dev->regmap;
+
+ regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON, 0);
+}
+
static void
sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,
struct sun6i_csi_buffer *csi_buffer)
@@ -357,6 +402,10 @@ static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
goto error_media_pipeline;
}
+ /* Clear */
+
+ sun6i_csi_capture_irq_clear(csi_dev);
+
/* Configure */
sun6i_csi_capture_configure(csi_dev);
@@ -367,7 +416,8 @@ static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
/* Enable */
- sun6i_csi_set_stream(csi_dev, true);
+ sun6i_csi_capture_irq_enable(csi_dev);
+ sun6i_csi_capture_enable(csi_dev);
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD)
@@ -376,7 +426,8 @@ static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
return 0;
error_stream:
- sun6i_csi_set_stream(csi_dev, false);
+ sun6i_csi_capture_disable(csi_dev);
+ sun6i_csi_capture_irq_disable(csi_dev);
error_media_pipeline:
video_device_pipeline_stop(video_dev);
@@ -397,7 +448,8 @@ static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)
if (subdev)
v4l2_subdev_call(subdev, video, s_stream, 0);
- sun6i_csi_set_stream(csi_dev, false);
+ sun6i_csi_capture_disable(csi_dev);
+ sun6i_csi_capture_irq_disable(csi_dev);
video_device_pipeline_stop(&capture->video_dev);
--
2.38.1
Now that the driver is properly split between bridge and capture,
rename the video device to highlight its role and be in line with
the bridge entity naming.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c | 3 ++-
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index 96a8e11fd55e..ec62442f778d 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -1023,7 +1023,8 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
/* Video Device */
- strscpy(video_dev->name, SUN6I_CSI_NAME, sizeof(video_dev->name));
+ strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME,
+ sizeof(video_dev->name));
video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
video_dev->vfl_dir = VFL_DIR_RX;
video_dev->release = video_device_release_empty;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
index a61db3bc72e5..59c57bcefeec 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
@@ -11,6 +11,8 @@
#include <media/v4l2-dev.h>
#include <media/videobuf2-core.h>
+#define SUN6I_CSI_CAPTURE_NAME "sun6i-csi-capture"
+
#define SUN6I_CSI_CAPTURE_WIDTH_MIN 32
#define SUN6I_CSI_CAPTURE_WIDTH_MAX 4800
#define SUN6I_CSI_CAPTURE_HEIGHT_MIN 32
--
2.38.1
Remove the custom sun6i_csi_get_bpp helper in favor of common v4l2
infrastructure and rework the related window configuration code.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 49 -------------
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 70 +++++++++----------
2 files changed, 35 insertions(+), 84 deletions(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index d828d98ace7c..a4df8f8d2980 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -60,53 +60,4 @@ struct sun6i_csi_variant {
bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev,
u32 pixformat, u32 mbus_code);
-/* get bpp form v4l2 pixformat */
-static inline int sun6i_csi_get_bpp(unsigned int pixformat)
-{
- switch (pixformat) {
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- case V4L2_PIX_FMT_JPEG:
- return 8;
- case V4L2_PIX_FMT_SBGGR10:
- case V4L2_PIX_FMT_SGBRG10:
- case V4L2_PIX_FMT_SGRBG10:
- case V4L2_PIX_FMT_SRGGB10:
- return 10;
- case V4L2_PIX_FMT_SBGGR12:
- case V4L2_PIX_FMT_SGBRG12:
- case V4L2_PIX_FMT_SGRBG12:
- case V4L2_PIX_FMT_SRGGB12:
- case V4L2_PIX_FMT_NV12_16L16:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- return 12;
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- case V4L2_PIX_FMT_YUV422P:
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB565X:
- return 16;
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- return 24;
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_BGR32:
- return 32;
- default:
- WARN(1, "Unsupported pixformat: 0x%x\n", pixformat);
- break;
- }
-
- return 0;
-}
-
#endif /* __SUN6I_CSI_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index f434d86f74de..45b2f4480127 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -483,68 +483,68 @@ static void sun6i_csi_capture_configure_format(struct sun6i_csi_device *csi_dev)
static void sun6i_csi_capture_configure_window(struct sun6i_csi_device *csi_dev)
{
+ struct regmap *regmap = csi_dev->regmap;
+ const struct v4l2_format_info *info;
+ u32 hsize_len, vsize_len;
+ u32 luma_line, chroma_line = 0;
u32 pixelformat, field;
u32 width, height;
- u32 bytesperline_y;
- u32 bytesperline_c;
- u32 hor_len;
sun6i_csi_capture_dimensions(csi_dev, &width, &height);
sun6i_csi_capture_format(csi_dev, &pixelformat, &field);
- hor_len = width;
+ hsize_len = width;
+ vsize_len = height;
switch (pixelformat) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
- dev_dbg(csi_dev->dev,
- "Horizontal length should be 2 times of width for packed YUV formats!\n");
- hor_len = width * 2;
+ /*
+ * Horizontal length should be 2 times of width for packed
+ * YUV formats.
+ */
+ hsize_len *= 2;
break;
default:
break;
}
- regmap_write(csi_dev->regmap, SUN6I_CSI_CH_HSIZE_REG,
- SUN6I_CSI_CH_HSIZE_LEN(hor_len) |
+ regmap_write(regmap, SUN6I_CSI_CH_HSIZE_REG,
+ SUN6I_CSI_CH_HSIZE_LEN(hsize_len) |
SUN6I_CSI_CH_HSIZE_START(0));
- regmap_write(csi_dev->regmap, SUN6I_CSI_CH_VSIZE_REG,
- SUN6I_CSI_CH_VSIZE_LEN(height) |
+
+ regmap_write(regmap, SUN6I_CSI_CH_VSIZE_REG,
+ SUN6I_CSI_CH_VSIZE_LEN(vsize_len) |
SUN6I_CSI_CH_VSIZE_START(0));
switch (pixelformat) {
- case V4L2_PIX_FMT_NV12_16L16:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- bytesperline_y = width;
- bytesperline_c = width;
+ case V4L2_PIX_FMT_RGB565X:
+ luma_line = width * 2;
break;
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- bytesperline_y = width;
- bytesperline_c = width / 2;
+ case V4L2_PIX_FMT_NV12_16L16:
+ luma_line = width;
+ chroma_line = width;
break;
- case V4L2_PIX_FMT_YUV422P:
- bytesperline_y = width;
- bytesperline_c = width / 2;
+ case V4L2_PIX_FMT_JPEG:
+ luma_line = width;
break;
- default: /* raw */
- dev_dbg(csi_dev->dev,
- "Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
- pixelformat);
- bytesperline_y = (sun6i_csi_get_bpp(pixelformat) *
- width) / 8;
- bytesperline_c = 0;
+ default:
+ info = v4l2_format_info(pixelformat);
+ if (WARN_ON(!info))
+ return;
+
+ luma_line = width * info->bpp[0];
+
+ if (info->comp_planes > 1)
+ chroma_line = width * info->bpp[1] / info->hdiv;
break;
}
- regmap_write(csi_dev->regmap, SUN6I_CSI_CH_BUF_LEN_REG,
- SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(bytesperline_c) |
- SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(bytesperline_y));
+ regmap_write(regmap, SUN6I_CSI_CH_BUF_LEN_REG,
+ SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(chroma_line) |
+ SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(luma_line));
}
static void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)
--
2.38.1
Introduce a more informative format list for the bridge, with
information about how to configure the input. This separation will
later be useful when using the bridge standalone (without capture)
for the isp workflow.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../sunxi/sun6i-csi/sun6i_csi_bridge.c | 169 ++++++++++++++----
.../sunxi/sun6i-csi/sun6i_csi_bridge.h | 12 ++
2 files changed, 145 insertions(+), 36 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 b98a1e18ddef..0b45cfbe78b4 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
@@ -11,6 +11,7 @@
#include "sun6i_csi.h"
#include "sun6i_csi_bridge.h"
+#include "sun6i_csi_reg.h"
/* Helpers */
@@ -34,47 +35,143 @@ void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev,
/* Format */
-static const u32 sun6i_csi_bridge_mbus_codes[] = {
+static const struct sun6i_csi_bridge_format sun6i_csi_bridge_formats[] = {
/* Bayer */
- MEDIA_BUS_FMT_SBGGR8_1X8,
- MEDIA_BUS_FMT_SGBRG8_1X8,
- MEDIA_BUS_FMT_SGRBG8_1X8,
- MEDIA_BUS_FMT_SRGGB8_1X8,
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SGBRG10_1X10,
- MEDIA_BUS_FMT_SGRBG10_1X10,
- MEDIA_BUS_FMT_SRGGB10_1X10,
- MEDIA_BUS_FMT_SBGGR12_1X12,
- MEDIA_BUS_FMT_SGBRG12_1X12,
- MEDIA_BUS_FMT_SGRBG12_1X12,
- MEDIA_BUS_FMT_SRGGB12_1X12,
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
/* RGB */
- MEDIA_BUS_FMT_RGB565_2X8_LE,
- MEDIA_BUS_FMT_RGB565_2X8_BE,
+ {
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
/* YUV422 */
- MEDIA_BUS_FMT_YUYV8_2X8,
- MEDIA_BUS_FMT_UYVY8_2X8,
- MEDIA_BUS_FMT_YVYU8_2X8,
- MEDIA_BUS_FMT_UYVY8_2X8,
- MEDIA_BUS_FMT_VYUY8_2X8,
- MEDIA_BUS_FMT_YUYV8_1X16,
- MEDIA_BUS_FMT_UYVY8_1X16,
- MEDIA_BUS_FMT_YVYU8_1X16,
- MEDIA_BUS_FMT_UYVY8_1X16,
- MEDIA_BUS_FMT_VYUY8_1X16,
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YVYU,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YUYV,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .input_format = SUN6I_CSI_INPUT_FMT_YUV422,
+ .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_VYUY,
+ .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_UYVY,
+ },
/* Compressed */
- MEDIA_BUS_FMT_JPEG_1X8,
+ {
+ .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
+ .input_format = SUN6I_CSI_INPUT_FMT_RAW,
+ },
};
-static bool sun6i_csi_bridge_mbus_code_check(u32 mbus_code)
+const struct sun6i_csi_bridge_format *
+sun6i_csi_bridge_format_find(u32 mbus_code)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(sun6i_csi_bridge_mbus_codes); i++)
- if (sun6i_csi_bridge_mbus_codes[i] == mbus_code)
- return true;
+ for (i = 0; i < ARRAY_SIZE(sun6i_csi_bridge_formats); i++)
+ if (sun6i_csi_bridge_formats[i].mbus_code == mbus_code)
+ return &sun6i_csi_bridge_formats[i];
- return false;
+ return NULL;
}
/* V4L2 Subdev */
@@ -124,8 +221,8 @@ static const struct v4l2_subdev_video_ops sun6i_csi_bridge_video_ops = {
static void
sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
{
- if (!sun6i_csi_bridge_mbus_code_check(mbus_format->code))
- mbus_format->code = sun6i_csi_bridge_mbus_codes[0];
+ if (!sun6i_csi_bridge_format_find(mbus_format->code))
+ mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
mbus_format->field = V4L2_FIELD_NONE;
mbus_format->colorspace = V4L2_COLORSPACE_RAW;
@@ -144,7 +241,7 @@ static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev,
mutex_lock(lock);
- mbus_format->code = sun6i_csi_bridge_mbus_codes[0];
+ mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
mbus_format->width = 1280;
mbus_format->height = 720;
@@ -160,10 +257,10 @@ sun6i_csi_bridge_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code_enum)
{
- if (code_enum->index >= ARRAY_SIZE(sun6i_csi_bridge_mbus_codes))
+ if (code_enum->index >= ARRAY_SIZE(sun6i_csi_bridge_formats))
return -EINVAL;
- code_enum->code = sun6i_csi_bridge_mbus_codes[code_enum->index];
+ code_enum->code = sun6i_csi_bridge_formats[code_enum->index].mbus_code;
return 0;
}
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 5e6448aa522f..cb3b27af4607 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
@@ -20,6 +20,13 @@ enum sun6i_csi_bridge_pad {
struct sun6i_csi_device;
+struct sun6i_csi_bridge_format {
+ u32 mbus_code;
+ u8 input_format;
+ u8 input_yuv_seq;
+ u8 input_yuv_seq_invert;
+};
+
struct sun6i_csi_bridge_source {
struct v4l2_subdev *subdev;
struct v4l2_fwnode_endpoint endpoint;
@@ -48,6 +55,11 @@ void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev,
void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev,
u32 *mbus_code, u32 *field);
+/* Format */
+
+const struct sun6i_csi_bridge_format *
+sun6i_csi_bridge_format_find(u32 mbus_code);
+
/* Bridge */
int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev);
--
2.38.1
Check against the enabled bits and make sure capture is running before
serving an interrupt, to add extra safety in the process.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index eaf62cab0b64..46c5f98702e1 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -89,13 +89,17 @@ static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev)
static irqreturn_t sun6i_csi_interrupt(int irq, void *private)
{
struct sun6i_csi_device *csi_dev = private;
+ bool capture_streaming = csi_dev->capture.state.streaming;
struct regmap *regmap = csi_dev->regmap;
- u32 status;
+ u32 status = 0, enable = 0;
regmap_read(regmap, SUN6I_CSI_CH_INT_STA_REG, &status);
+ regmap_read(regmap, SUN6I_CSI_CH_INT_EN_REG, &enable);
- if (!(status & 0xFF))
+ if (!status)
return IRQ_NONE;
+ else if (!(status & enable) || !capture_streaming)
+ goto complete;
if ((status & SUN6I_CSI_CH_INT_STA_FIFO0_OF) ||
(status & SUN6I_CSI_CH_INT_STA_FIFO1_OF) ||
@@ -116,6 +120,7 @@ static irqreturn_t sun6i_csi_interrupt(int irq, void *private)
if (status & SUN6I_CSI_CH_INT_STA_VS)
sun6i_csi_capture_sync(csi_dev);
+complete:
regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status);
return IRQ_HANDLED;
--
2.38.1
Introduce a bridge v4l2 subdev to prepare for separation between the
processing part (bridge) and the dma engine, which is required to
properly support ths isp workflow later on.
Currently the bridge just manages fwnode mapping to media pads,
using an async notifier (which was previously in the main code).
The s_stream video op just forwards to the connected v4l2 subdev
(sensor or MIPI CSI-2 bridge).
The video capture device is now registered after the bridge and
attaches to it with a media link.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../media/platform/sunxi/sun6i-csi/Makefile | 2 +-
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 157 +-----
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 10 +-
.../sunxi/sun6i-csi/sun6i_csi_bridge.c | 450 ++++++++++++++++++
.../sunxi/sun6i-csi/sun6i_csi_bridge.h | 49 ++
.../platform/sunxi/sun6i-csi/sun6i_video.c | 19 +
6 files changed, 548 insertions(+), 139 deletions(-)
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile
index e7e315347804..7a699580a641 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/Makefile
+++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-sun6i-csi-y += sun6i_video.o sun6i_csi.o
+sun6i-csi-y += sun6i_video.o sun6i_csi.o sun6i_csi_bridge.o
obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 8b99c17e8403..49f1218b0b28 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -34,16 +34,17 @@
bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev,
u32 pixformat, u32 mbus_code)
{
- struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
+ struct v4l2_fwnode_endpoint *endpoint =
+ &csi_dev->bridge.source_parallel.endpoint;
/*
* 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 ((endpoint->bus_type == V4L2_MBUS_PARALLEL
+ || endpoint->bus_type == V4L2_MBUS_BT656)
+ && endpoint->bus.parallel.bus_width == 16) {
switch (pixformat) {
case V4L2_PIX_FMT_NV12_16L16:
case V4L2_PIX_FMT_NV12:
@@ -328,7 +329,8 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_device *csi_dev,
static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev)
{
- struct v4l2_fwnode_endpoint *endpoint = &csi_dev->v4l2.v4l2_ep;
+ struct v4l2_fwnode_endpoint *endpoint =
+ &csi_dev->bridge.source_parallel.endpoint;
struct sun6i_csi_config *config = &csi_dev->config;
unsigned char bus_width;
u32 flags;
@@ -583,103 +585,11 @@ static const struct media_device_ops sun6i_csi_media_ops = {
/* V4L2 */
-static int sun6i_csi_link_entity(struct sun6i_csi_device *csi_dev,
- struct media_entity *entity,
- struct fwnode_handle *fwnode)
-{
- struct media_entity *sink;
- struct media_pad *sink_pad;
- int src_pad_index;
- int ret;
-
- ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE);
- if (ret < 0) {
- dev_err(csi_dev->dev,
- "%s: no source pad in external entity %s\n", __func__,
- entity->name);
- return -EINVAL;
- }
-
- src_pad_index = ret;
-
- sink = &csi_dev->video.video_dev.entity;
- sink_pad = &csi_dev->video.pad;
-
- 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);
- if (ret < 0) {
- dev_err(csi_dev->dev, "failed to create %s:%u -> %s:%u link\n",
- entity->name, src_pad_index,
- sink->name, sink_pad->index);
- return ret;
- }
-
- return 0;
-}
-
-static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
-{
- struct sun6i_csi_device *csi_dev =
- container_of(notifier, struct sun6i_csi_device,
- v4l2.notifier);
- struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
- struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev;
- 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)
- return -EINVAL;
-
- ret = sun6i_csi_link_entity(csi_dev, &sd->entity, sd->fwnode);
- if (ret < 0)
- return ret;
-
- ret = v4l2_device_register_subdev_nodes(v4l2_dev);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
- .complete = sun6i_subdev_notify_complete,
-};
-
-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);
-
- if (vep->base.port || vep->base.id) {
- dev_warn(dev, "Only support a single port with one endpoint\n");
- return -ENOTCONN;
- }
-
- switch (vep->bus_type) {
- case V4L2_MBUS_PARALLEL:
- case V4L2_MBUS_BT656:
- csi_dev->v4l2.v4l2_ep = *vep;
- return 0;
- default:
- dev_err(dev, "Unsupported media bus type\n");
- return -ENOTCONN;
- }
-}
-
static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev)
{
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
struct media_device *media_dev = &v4l2->media_dev;
struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev;
- struct v4l2_async_notifier *notifier = &v4l2->notifier;
struct device *dev = csi_dev->dev;
int ret;
@@ -709,42 +619,8 @@ static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev)
goto error_media;
}
- /* Video */
-
- ret = sun6i_video_setup(csi_dev);
- if (ret)
- goto error_v4l2_device;
-
- /* V4L2 Async */
-
- v4l2_async_nf_init(notifier);
- notifier->ops = &sun6i_csi_async_ops;
-
- ret = v4l2_async_nf_parse_fwnode_endpoints(dev, notifier,
- sizeof(struct
- v4l2_async_subdev),
- sun6i_csi_fwnode_parse);
- if (ret)
- goto error_video;
-
- ret = v4l2_async_nf_register(v4l2_dev, notifier);
- if (ret) {
- dev_err(dev, "failed to register v4l2 async notifier: %d\n",
- ret);
- goto error_v4l2_async_notifier;
- }
-
return 0;
-error_v4l2_async_notifier:
- v4l2_async_nf_cleanup(notifier);
-
-error_video:
- sun6i_video_cleanup(csi_dev);
-
-error_v4l2_device:
- v4l2_device_unregister(&v4l2->v4l2_dev);
-
error_media:
media_device_unregister(media_dev);
media_device_cleanup(media_dev);
@@ -757,9 +633,6 @@ static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev)
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
media_device_unregister(&v4l2->media_dev);
- v4l2_async_nf_unregister(&v4l2->notifier);
- v4l2_async_nf_cleanup(&v4l2->notifier);
- sun6i_video_cleanup(csi_dev);
v4l2_device_unregister(&v4l2->v4l2_dev);
media_device_cleanup(&v4l2->media_dev);
}
@@ -964,8 +837,22 @@ static int sun6i_csi_probe(struct platform_device *platform_dev)
if (ret)
goto error_resources;
+ ret = sun6i_csi_bridge_setup(csi_dev);
+ if (ret)
+ goto error_v4l2;
+
+ ret = sun6i_video_setup(csi_dev);
+ if (ret)
+ goto error_bridge;
+
return 0;
+error_bridge:
+ sun6i_csi_bridge_cleanup(csi_dev);
+
+error_v4l2:
+ sun6i_csi_v4l2_cleanup(csi_dev);
+
error_resources:
sun6i_csi_resources_cleanup(csi_dev);
@@ -976,6 +863,8 @@ static int sun6i_csi_remove(struct platform_device *pdev)
{
struct sun6i_csi_device *csi_dev = platform_get_drvdata(pdev);
+ sun6i_video_cleanup(csi_dev);
+ sun6i_csi_bridge_cleanup(csi_dev);
sun6i_csi_v4l2_cleanup(csi_dev);
sun6i_csi_resources_cleanup(csi_dev);
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index bab705678280..1dd76631ec87 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -12,11 +12,16 @@
#include <media/v4l2-fwnode.h>
#include <media/videobuf2-v4l2.h>
+#include "sun6i_csi_bridge.h"
#include "sun6i_video.h"
#define SUN6I_CSI_NAME "sun6i-csi"
#define SUN6I_CSI_DESCRIPTION "Allwinner A31 CSI Device"
+enum sun6i_csi_port {
+ SUN6I_CSI_PORT_PARALLEL = 0,
+};
+
struct sun6i_csi_buffer {
struct vb2_v4l2_buffer v4l2_buffer;
struct list_head list;
@@ -44,10 +49,6 @@ 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 {
@@ -55,6 +56,7 @@ struct sun6i_csi_device {
struct sun6i_csi_config config;
struct sun6i_csi_v4l2 v4l2;
+ struct sun6i_csi_bridge bridge;
struct sun6i_video video;
struct regmap *regmap;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
new file mode 100644
index 000000000000..cac1b150e544
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <[email protected]>
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "sun6i_csi.h"
+#include "sun6i_csi_bridge.h"
+
+/* Format */
+
+static const u32 sun6i_csi_bridge_mbus_codes[] = {
+ /* Bayer */
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ /* RGB */
+ MEDIA_BUS_FMT_RGB565_2X8_LE,
+ MEDIA_BUS_FMT_RGB565_2X8_BE,
+ /* YUV422 */
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YVYU8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_VYUY8_1X16,
+ /* Compressed */
+ MEDIA_BUS_FMT_JPEG_1X8,
+};
+
+static bool sun6i_csi_bridge_mbus_code_check(u32 mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun6i_csi_bridge_mbus_codes); i++)
+ if (sun6i_csi_bridge_mbus_codes[i] == mbus_code)
+ return true;
+
+ return false;
+}
+
+/* V4L2 Subdev */
+
+static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
+{
+ struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
+ struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
+ struct media_pad *local_pad = &bridge->pads[SUN6I_CSI_BRIDGE_PAD_SINK];
+ struct device *dev = csi_dev->dev;
+ struct v4l2_subdev *source_subdev;
+ struct media_pad *remote_pad;
+ /* Initialize to 0 to use both in disable label (ret != 0) and off. */
+ int ret = 0;
+
+ /* Source */
+
+ remote_pad = media_pad_remote_pad_unique(local_pad);
+ if (IS_ERR(remote_pad)) {
+ dev_err(dev,
+ "zero or more than a single source connected to the bridge\n");
+ return PTR_ERR(remote_pad);
+ }
+
+ source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ if (!on) {
+ v4l2_subdev_call(source_subdev, video, s_stream, 0);
+ goto disable;
+ }
+
+ ret = v4l2_subdev_call(source_subdev, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD)
+ goto disable;
+
+ return 0;
+
+disable:
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops sun6i_csi_bridge_video_ops = {
+ .s_stream = sun6i_csi_bridge_s_stream,
+};
+
+static void
+sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
+{
+ if (!sun6i_csi_bridge_mbus_code_check(mbus_format->code))
+ mbus_format->code = sun6i_csi_bridge_mbus_codes[0];
+
+ mbus_format->field = V4L2_FIELD_NONE;
+ mbus_format->colorspace = V4L2_COLORSPACE_RAW;
+ mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state)
+{
+ struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
+ unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK;
+ struct v4l2_mbus_framefmt *mbus_format =
+ v4l2_subdev_get_try_format(subdev, state, pad);
+ struct mutex *lock = &csi_dev->bridge.lock;
+
+ mutex_lock(lock);
+
+ mbus_format->code = sun6i_csi_bridge_mbus_codes[0];
+ mbus_format->width = 1280;
+ mbus_format->height = 720;
+
+ sun6i_csi_bridge_mbus_format_prepare(mbus_format);
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static int
+sun6i_csi_bridge_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code_enum)
+{
+ if (code_enum->index >= ARRAY_SIZE(sun6i_csi_bridge_mbus_codes))
+ return -EINVAL;
+
+ code_enum->code = sun6i_csi_bridge_mbus_codes[code_enum->index];
+
+ return 0;
+}
+
+static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
+ struct v4l2_mbus_framefmt *mbus_format = &format->format;
+ struct mutex *lock = &csi_dev->bridge.lock;
+
+ mutex_lock(lock);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ *mbus_format = *v4l2_subdev_get_try_format(subdev, state,
+ format->pad);
+ else
+ *mbus_format = csi_dev->bridge.mbus_format;
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
+ struct v4l2_mbus_framefmt *mbus_format = &format->format;
+ struct mutex *lock = &csi_dev->bridge.lock;
+
+ mutex_lock(lock);
+
+ sun6i_csi_bridge_mbus_format_prepare(mbus_format);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ *v4l2_subdev_get_try_format(subdev, state, format->pad) =
+ *mbus_format;
+ else
+ csi_dev->bridge.mbus_format = *mbus_format;
+
+ mutex_unlock(lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = {
+ .init_cfg = sun6i_csi_bridge_init_cfg,
+ .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code,
+ .get_fmt = sun6i_csi_bridge_get_fmt,
+ .set_fmt = sun6i_csi_bridge_set_fmt,
+};
+
+const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = {
+ .video = &sun6i_csi_bridge_video_ops,
+ .pad = &sun6i_csi_bridge_pad_ops,
+};
+
+/* Media Entity */
+
+static const struct media_entity_operations sun6i_csi_bridge_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* V4L2 Async */
+
+static int sun6i_csi_bridge_link(struct sun6i_csi_device *csi_dev,
+ int sink_pad_index,
+ struct v4l2_subdev *remote_subdev,
+ bool enabled)
+{
+ struct device *dev = csi_dev->dev;
+ struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
+ struct media_entity *sink_entity = &subdev->entity;
+ struct media_entity *source_entity = &remote_subdev->entity;
+ int source_pad_index;
+ int ret;
+
+ /* Get the first remote source pad. */
+ ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (ret < 0) {
+ dev_err(dev, "missing source pad in external entity %s\n",
+ source_entity->name);
+ return -EINVAL;
+ }
+
+ source_pad_index = ret;
+
+ dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name,
+ source_pad_index, sink_entity->name, sink_pad_index);
+
+ ret = media_create_pad_link(source_entity, source_pad_index,
+ sink_entity, sink_pad_index,
+ enabled ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to create %s:%u -> %s:%u link\n",
+ source_entity->name, source_pad_index,
+ sink_entity->name, sink_pad_index);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *remote_subdev,
+ struct v4l2_async_subdev *async_subdev)
+{
+ struct sun6i_csi_device *csi_dev =
+ container_of(notifier, struct sun6i_csi_device,
+ bridge.notifier);
+ struct sun6i_csi_bridge_async_subdev *bridge_async_subdev =
+ container_of(async_subdev, struct sun6i_csi_bridge_async_subdev,
+ async_subdev);
+ struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
+ bool enabled;
+
+ switch (source->endpoint.base.port) {
+ case SUN6I_CSI_PORT_PARALLEL:
+ enabled = true;
+ break;
+ default:
+ break;
+ }
+
+ source->subdev = remote_subdev;
+
+ return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK,
+ remote_subdev, enabled);
+}
+
+static int
+sun6i_csi_bridge_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct sun6i_csi_device *csi_dev =
+ container_of(notifier, struct sun6i_csi_device,
+ bridge.notifier);
+ struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+
+ return v4l2_device_register_subdev_nodes(v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations
+sun6i_csi_bridge_notifier_ops = {
+ .bound = sun6i_csi_bridge_notifier_bound,
+ .complete = sun6i_csi_bridge_notifier_complete,
+};
+
+/* Bridge */
+
+static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev,
+ struct sun6i_csi_bridge_source *source,
+ u32 port,
+ enum v4l2_mbus_type *bus_types)
+{
+ struct device *dev = csi_dev->dev;
+ struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier;
+ struct v4l2_fwnode_endpoint *endpoint = &source->endpoint;
+ struct sun6i_csi_bridge_async_subdev *bridge_async_subdev;
+ struct fwnode_handle *handle;
+ int ret;
+
+ handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0);
+ if (!handle)
+ return -ENODEV;
+
+ ret = v4l2_fwnode_endpoint_parse(handle, endpoint);
+ if (ret)
+ goto complete;
+
+ if (bus_types) {
+ bool valid = false;
+ unsigned int i;
+
+ for (i = 0; bus_types[i] != V4L2_MBUS_INVALID; i++) {
+ if (endpoint->bus_type == bus_types[i]) {
+ valid = true;
+ break;
+ }
+ }
+
+ if (!valid) {
+ dev_err(dev, "unsupported bus type for port %d\n",
+ port);
+ ret = -EINVAL;
+ goto complete;
+ }
+ }
+
+ bridge_async_subdev =
+ v4l2_async_nf_add_fwnode_remote(notifier, handle,
+ struct
+ sun6i_csi_bridge_async_subdev);
+ if (IS_ERR(bridge_async_subdev)) {
+ ret = PTR_ERR(bridge_async_subdev);
+ goto complete;
+ }
+
+ bridge_async_subdev->source = source;
+
+ source->expected = true;
+
+complete:
+ fwnode_handle_put(handle);
+
+ return ret;
+}
+
+int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_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_subdev *subdev = &bridge->subdev;
+ struct v4l2_async_notifier *notifier = &bridge->notifier;
+ struct media_pad *pads = bridge->pads;
+ enum v4l2_mbus_type parallel_mbus_types[] = {
+ V4L2_MBUS_PARALLEL,
+ V4L2_MBUS_BT656,
+ V4L2_MBUS_INVALID
+ };
+ int ret;
+
+ mutex_init(&bridge->lock);
+
+ /* V4L2 Subdev */
+
+ v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops);
+ strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name));
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ subdev->owner = THIS_MODULE;
+ subdev->dev = dev;
+
+ v4l2_set_subdevdata(subdev, csi_dev);
+
+ /* Media Entity */
+
+ subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ subdev->entity.ops = &sun6i_csi_bridge_entity_ops;
+
+ /* Media Pads */
+
+ pads[SUN6I_CSI_BRIDGE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[SUN6I_CSI_BRIDGE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
+ MEDIA_PAD_FL_MUST_CONNECT;
+
+ ret = media_entity_pads_init(&subdev->entity,
+ SUN6I_CSI_BRIDGE_PAD_COUNT, pads);
+ if (ret < 0)
+ return ret;
+
+ /* V4L2 Subdev */
+
+ 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;
+ }
+
+ /* V4L2 Async */
+
+ 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);
+
+ ret = v4l2_async_nf_register(v4l2_dev, notifier);
+ if (ret) {
+ dev_err(dev, "failed to register v4l2 async notifier: %d\n",
+ ret);
+ goto error_v4l2_async_notifier;
+ }
+
+ return 0;
+
+error_v4l2_async_notifier:
+ v4l2_async_nf_cleanup(notifier);
+
+ v4l2_device_unregister_subdev(subdev);
+
+error_media_entity:
+ media_entity_cleanup(&subdev->entity);
+
+ return ret;
+}
+
+void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev)
+{
+ struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;
+ struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier;
+
+ v4l2_async_nf_unregister(notifier);
+ v4l2_async_nf_cleanup(notifier);
+
+ v4l2_device_unregister_subdev(subdev);
+
+ media_entity_cleanup(&subdev->entity);
+}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
new file mode 100644
index 000000000000..f9bf87bf3667
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021-2022 Bootlin
+ * Author: Paul Kocialkowski <[email protected]>
+ */
+
+#ifndef _SUN6I_CSI_BRIDGE_H_
+#define _SUN6I_CSI_BRIDGE_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define SUN6I_CSI_BRIDGE_NAME "sun6i-csi-bridge"
+
+enum sun6i_csi_bridge_pad {
+ SUN6I_CSI_BRIDGE_PAD_SINK = 0,
+ SUN6I_CSI_BRIDGE_PAD_SOURCE = 1,
+ SUN6I_CSI_BRIDGE_PAD_COUNT = 2,
+};
+
+struct sun6i_csi_device;
+
+struct sun6i_csi_bridge_source {
+ struct v4l2_subdev *subdev;
+ struct v4l2_fwnode_endpoint endpoint;
+ bool expected;
+};
+
+struct sun6i_csi_bridge_async_subdev {
+ struct v4l2_async_subdev async_subdev;
+ struct sun6i_csi_bridge_source *source;
+};
+
+struct sun6i_csi_bridge {
+ struct v4l2_subdev subdev;
+ struct v4l2_async_notifier notifier;
+ struct media_pad pads[2];
+ struct v4l2_mbus_framefmt mbus_format;
+ struct mutex lock; /* Mbus format lock. */
+
+ struct sun6i_csi_bridge_source source_parallel;
+};
+
+/* Bridge */
+
+int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev);
+void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev);
+
+#endif
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
index 791583d23a65..fa83211a2c5a 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -632,6 +632,7 @@ int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
{
struct sun6i_video *video = &csi_dev->video;
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
+ struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;
struct video_device *video_dev = &video->video_dev;
struct vb2_queue *queue = &video->queue;
struct media_pad *pad = &video->pad;
@@ -712,8 +713,26 @@ int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
goto error_media_entity;
}
+ /* Media Pad Link */
+
+ ret = media_create_pad_link(&bridge_subdev->entity,
+ SUN6I_CSI_BRIDGE_PAD_SOURCE,
+ &video_dev->entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",
+ bridge_subdev->entity.name,
+ SUN6I_CSI_BRIDGE_PAD_SOURCE,
+ video_dev->entity.name, 0);
+ goto error_video_device;
+ }
+
return 0;
+error_video_device:
+ vb2_video_unregister_device(video_dev);
+
error_media_entity:
media_entity_cleanup(&video_dev->entity);
--
2.38.1
This cleans up the register definitions a bit, adds a prefix, remove masks.
Registers are now fully defined, some additional fields were added when
needed. New format definitions are added for future use.
Some fields are wrongly defined (inverted) in Allwinner literature
(e.g. field vs frame prefixes), which is quite misleading. They are
now corrected to reflect their actual behavior.
This should only be a cosmetic commit. No functional change intended.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 182 ++++++-----
.../platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 296 ++++++++++--------
2 files changed, 266 insertions(+), 212 deletions(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index cc277733d7ec..79d4b00d1fcd 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -155,7 +155,8 @@ int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable)
int ret;
if (!enable) {
- regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
+ regmap_update_bits(regmap, SUN6I_CSI_EN_REG,
+ SUN6I_CSI_EN_CSI_EN, 0);
pm_runtime_put(dev);
return 0;
@@ -165,7 +166,8 @@ int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable)
if (ret < 0)
return ret;
- regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
+ regmap_update_bits(regmap, SUN6I_CSI_EN_REG, SUN6I_CSI_EN_CSI_EN,
+ SUN6I_CSI_EN_CSI_EN);
return 0;
}
@@ -334,7 +336,7 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev)
struct sun6i_csi_config *config = &csi_dev->config;
unsigned char bus_width;
u32 flags;
- u32 cfg;
+ u32 cfg = 0;
bool input_interlaced = false;
if (config->field == V4L2_FIELD_INTERLACED
@@ -344,52 +346,63 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev)
bus_width = endpoint->bus.parallel.bus_width;
- regmap_read(csi_dev->regmap, CSI_IF_CFG_REG, &cfg);
-
- cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
- CSI_IF_CFG_IF_DATA_WIDTH_MASK |
- CSI_IF_CFG_CLK_POL_MASK | CSI_IF_CFG_VREF_POL_MASK |
- CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK |
- CSI_IF_CFG_SRC_TYPE_MASK);
-
if (input_interlaced)
- cfg |= CSI_IF_CFG_SRC_TYPE_INTERLACED;
+ cfg |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED |
+ SUN6I_CSI_IF_CFG_FIELD_DT_PCLK_SHIFT(1) |
+ SUN6I_CSI_IF_CFG_FIELD_DT_FIELD_VSYNC;
else
- cfg |= CSI_IF_CFG_SRC_TYPE_PROGRESSED;
+ cfg |= SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE;
switch (endpoint->bus_type) {
case V4L2_MBUS_PARALLEL:
- cfg |= CSI_IF_CFG_MIPI_IF_CSI;
+ cfg |= SUN6I_CSI_IF_CFG_IF_CSI;
flags = endpoint->bus.parallel.flags;
- cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_YUV422_16BIT :
- CSI_IF_CFG_CSI_IF_YUV422_INTLV;
+ if (bus_width == 16)
+ cfg |= SUN6I_CSI_IF_CFG_IF_CSI_YUV_COMBINED;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_IF_CSI_YUV_RAW;
if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
- cfg |= CSI_IF_CFG_FIELD_POSITIVE;
+ cfg |= SUN6I_CSI_IF_CFG_FIELD_NEGATIVE;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_FIELD_POSITIVE;
if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- cfg |= CSI_IF_CFG_VREF_POL_POSITIVE;
+ cfg |= SUN6I_CSI_IF_CFG_VREF_POL_NEGATIVE;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_VREF_POL_POSITIVE;
+
if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- cfg |= CSI_IF_CFG_HREF_POL_POSITIVE;
+ cfg |= SUN6I_CSI_IF_CFG_HREF_POL_NEGATIVE;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_HREF_POL_POSITIVE;
if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
- cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
+ cfg |= SUN6I_CSI_IF_CFG_CLK_POL_RISING;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_CLK_POL_FALLING;
break;
case V4L2_MBUS_BT656:
- cfg |= CSI_IF_CFG_MIPI_IF_CSI;
+ cfg |= SUN6I_CSI_IF_CFG_IF_CSI;
flags = endpoint->bus.parallel.flags;
- cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_BT1120 :
- CSI_IF_CFG_CSI_IF_BT656;
+ if (bus_width == 16)
+ cfg |= SUN6I_CSI_IF_CFG_IF_CSI_BT1120;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_IF_CSI_BT656;
if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
- cfg |= CSI_IF_CFG_FIELD_POSITIVE;
+ cfg |= SUN6I_CSI_IF_CFG_FIELD_NEGATIVE;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_FIELD_POSITIVE;
if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
+ cfg |= SUN6I_CSI_IF_CFG_CLK_POL_RISING;
+ else
+ cfg |= SUN6I_CSI_IF_CFG_CLK_POL_FALLING;
break;
default:
dev_warn(csi_dev->dev, "Unsupported bus type: %d\n",
@@ -399,13 +412,13 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev)
switch (bus_width) {
case 8:
- cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
+ cfg |= SUN6I_CSI_IF_CFG_DATA_WIDTH_8;
break;
case 10:
- cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
+ cfg |= SUN6I_CSI_IF_CFG_DATA_WIDTH_10;
break;
case 12:
- cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
+ cfg |= SUN6I_CSI_IF_CFG_DATA_WIDTH_12;
break;
case 16: /* No need to configure DATA_WIDTH for 16bit */
break;
@@ -414,42 +427,35 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev)
break;
}
- regmap_write(csi_dev->regmap, CSI_IF_CFG_REG, cfg);
+ regmap_write(csi_dev->regmap, SUN6I_CSI_IF_CFG_REG, cfg);
}
static void sun6i_csi_set_format(struct sun6i_csi_device *csi_dev)
{
struct sun6i_csi_config *config = &csi_dev->config;
- u32 cfg;
+ u32 cfg = 0;
u32 val;
- regmap_read(csi_dev->regmap, CSI_CH_CFG_REG, &cfg);
-
- cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
- CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
- CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
- CSI_CH_CFG_INPUT_SEQ_MASK);
-
val = get_csi_input_format(csi_dev, config->code,
config->pixelformat);
- cfg |= CSI_CH_CFG_INPUT_FMT(val);
+ cfg |= SUN6I_CSI_CH_CFG_INPUT_FMT(val);
val = get_csi_output_format(csi_dev, config->pixelformat,
config->field);
- cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
+ cfg |= SUN6I_CSI_CH_CFG_OUTPUT_FMT(val);
val = get_csi_input_seq(csi_dev, config->code,
config->pixelformat);
- cfg |= CSI_CH_CFG_INPUT_SEQ(val);
+ cfg |= SUN6I_CSI_CH_CFG_INPUT_YUV_SEQ(val);
if (config->field == V4L2_FIELD_TOP)
- cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
+ cfg |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD0;
else if (config->field == V4L2_FIELD_BOTTOM)
- cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
+ cfg |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD1;
else
- cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
+ cfg |= SUN6I_CSI_CH_CFG_FIELD_SEL_EITHER;
- regmap_write(csi_dev->regmap, CSI_CH_CFG_REG, cfg);
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_CFG_REG, cfg);
}
static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
@@ -475,12 +481,12 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
break;
}
- regmap_write(csi_dev->regmap, CSI_CH_HSIZE_REG,
- CSI_CH_HSIZE_HOR_LEN(hor_len) |
- CSI_CH_HSIZE_HOR_START(0));
- regmap_write(csi_dev->regmap, CSI_CH_VSIZE_REG,
- CSI_CH_VSIZE_VER_LEN(height) |
- CSI_CH_VSIZE_VER_START(0));
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_HSIZE_REG,
+ SUN6I_CSI_CH_HSIZE_LEN(hor_len) |
+ SUN6I_CSI_CH_HSIZE_START(0));
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_VSIZE_REG,
+ SUN6I_CSI_CH_VSIZE_LEN(height) |
+ SUN6I_CSI_CH_VSIZE_START(0));
planar_offset[0] = 0;
switch (config->pixelformat) {
@@ -521,9 +527,9 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
break;
}
- regmap_write(csi_dev->regmap, CSI_CH_BUF_LEN_REG,
- CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
- CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_BUF_LEN_REG,
+ SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(bytesperline_c) |
+ SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(bytesperline_y));
}
int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
@@ -544,14 +550,16 @@ int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev,
dma_addr_t addr)
{
- regmap_write(csi_dev->regmap, CSI_CH_F0_BUFA_REG,
- (addr + csi_dev->planar_offset[0]) >> 2);
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,
+ SUN6I_CSI_ADDR_VALUE(addr + csi_dev->planar_offset[0]));
if (csi_dev->planar_offset[1] != -1)
- regmap_write(csi_dev->regmap, CSI_CH_F1_BUFA_REG,
- (addr + csi_dev->planar_offset[1]) >> 2);
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,
+ SUN6I_CSI_ADDR_VALUE(addr +
+ csi_dev->planar_offset[1]));
if (csi_dev->planar_offset[2] != -1)
- regmap_write(csi_dev->regmap, CSI_CH_F2_BUFA_REG,
- (addr + csi_dev->planar_offset[2]) >> 2);
+ regmap_write(csi_dev->regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,
+ SUN6I_CSI_ADDR_VALUE(addr +
+ csi_dev->planar_offset[2]));
}
void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable)
@@ -559,23 +567,25 @@ void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable)
struct regmap *regmap = csi_dev->regmap;
if (!enable) {
- regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
- regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
+ regmap_update_bits(regmap, SUN6I_CSI_CAP_REG,
+ SUN6I_CSI_CAP_VCAP_ON, 0);
+ regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0);
return;
}
- regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF);
- regmap_write(regmap, CSI_CH_INT_EN_REG,
- CSI_CH_INT_EN_VS_INT_EN |
- CSI_CH_INT_EN_HB_OF_INT_EN |
- CSI_CH_INT_EN_FIFO2_OF_INT_EN |
- CSI_CH_INT_EN_FIFO1_OF_INT_EN |
- CSI_CH_INT_EN_FIFO0_OF_INT_EN |
- CSI_CH_INT_EN_FD_INT_EN |
- CSI_CH_INT_EN_CD_INT_EN);
-
- regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
- CSI_CAP_CH0_VCAP_ON);
+ regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG,
+ SUN6I_CSI_CH_INT_STA_CLEAR);
+ regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG,
+ SUN6I_CSI_CH_INT_EN_VS |
+ SUN6I_CSI_CH_INT_EN_HB_OF |
+ SUN6I_CSI_CH_INT_EN_FIFO2_OF |
+ SUN6I_CSI_CH_INT_EN_FIFO1_OF |
+ SUN6I_CSI_CH_INT_EN_FIFO0_OF |
+ SUN6I_CSI_CH_INT_EN_FD |
+ SUN6I_CSI_CH_INT_EN_CD);
+
+ regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON,
+ SUN6I_CSI_CAP_VCAP_ON);
}
/* Media */
@@ -646,29 +656,31 @@ static irqreturn_t sun6i_csi_interrupt(int irq, void *private)
struct regmap *regmap = csi_dev->regmap;
u32 status;
- regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
+ regmap_read(regmap, SUN6I_CSI_CH_INT_STA_REG, &status);
if (!(status & 0xFF))
return IRQ_NONE;
- if ((status & CSI_CH_INT_STA_FIFO0_OF_PD) ||
- (status & CSI_CH_INT_STA_FIFO1_OF_PD) ||
- (status & CSI_CH_INT_STA_FIFO2_OF_PD) ||
- (status & CSI_CH_INT_STA_HB_OF_PD)) {
- regmap_write(regmap, CSI_CH_INT_STA_REG, status);
- regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
- regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN,
- CSI_EN_CSI_EN);
+ if ((status & SUN6I_CSI_CH_INT_STA_FIFO0_OF) ||
+ (status & SUN6I_CSI_CH_INT_STA_FIFO1_OF) ||
+ (status & SUN6I_CSI_CH_INT_STA_FIFO2_OF) ||
+ (status & SUN6I_CSI_CH_INT_STA_HB_OF)) {
+ regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status);
+
+ regmap_update_bits(regmap, SUN6I_CSI_EN_REG,
+ SUN6I_CSI_EN_CSI_EN, 0);
+ regmap_update_bits(regmap, SUN6I_CSI_EN_REG,
+ SUN6I_CSI_EN_CSI_EN, SUN6I_CSI_EN_CSI_EN);
return IRQ_HANDLED;
}
- if (status & CSI_CH_INT_STA_FD_PD)
+ if (status & SUN6I_CSI_CH_INT_STA_FD)
sun6i_csi_capture_frame_done(csi_dev);
- if (status & CSI_CH_INT_STA_VS_PD)
+ if (status & SUN6I_CSI_CH_INT_STA_VS)
sun6i_csi_capture_sync(csi_dev);
- regmap_write(regmap, CSI_CH_INT_STA_REG, status);
+ regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status);
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
index 703fa14bb313..9b0326d6ba3c 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
@@ -10,133 +10,175 @@
#include <linux/kernel.h>
-#define CSI_EN_REG 0x0
-#define CSI_EN_VER_EN BIT(30)
-#define CSI_EN_CSI_EN BIT(0)
-
-#define CSI_IF_CFG_REG 0x4
-#define CSI_IF_CFG_SRC_TYPE_MASK BIT(21)
-#define CSI_IF_CFG_SRC_TYPE_PROGRESSED ((0 << 21) & CSI_IF_CFG_SRC_TYPE_MASK)
-#define CSI_IF_CFG_SRC_TYPE_INTERLACED ((1 << 21) & CSI_IF_CFG_SRC_TYPE_MASK)
-#define CSI_IF_CFG_FPS_DS_EN BIT(20)
-#define CSI_IF_CFG_FIELD_MASK BIT(19)
-#define CSI_IF_CFG_FIELD_NEGATIVE ((0 << 19) & CSI_IF_CFG_FIELD_MASK)
-#define CSI_IF_CFG_FIELD_POSITIVE ((1 << 19) & CSI_IF_CFG_FIELD_MASK)
-#define CSI_IF_CFG_VREF_POL_MASK BIT(18)
-#define CSI_IF_CFG_VREF_POL_NEGATIVE ((0 << 18) & CSI_IF_CFG_VREF_POL_MASK)
-#define CSI_IF_CFG_VREF_POL_POSITIVE ((1 << 18) & CSI_IF_CFG_VREF_POL_MASK)
-#define CSI_IF_CFG_HREF_POL_MASK BIT(17)
-#define CSI_IF_CFG_HREF_POL_NEGATIVE ((0 << 17) & CSI_IF_CFG_HREF_POL_MASK)
-#define CSI_IF_CFG_HREF_POL_POSITIVE ((1 << 17) & CSI_IF_CFG_HREF_POL_MASK)
-#define CSI_IF_CFG_CLK_POL_MASK BIT(16)
-#define CSI_IF_CFG_CLK_POL_RISING_EDGE ((0 << 16) & CSI_IF_CFG_CLK_POL_MASK)
-#define CSI_IF_CFG_CLK_POL_FALLING_EDGE ((1 << 16) & CSI_IF_CFG_CLK_POL_MASK)
-#define CSI_IF_CFG_IF_DATA_WIDTH_MASK GENMASK(10, 8)
-#define CSI_IF_CFG_IF_DATA_WIDTH_8BIT ((0 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
-#define CSI_IF_CFG_IF_DATA_WIDTH_10BIT ((1 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
-#define CSI_IF_CFG_IF_DATA_WIDTH_12BIT ((2 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
-#define CSI_IF_CFG_MIPI_IF_MASK BIT(7)
-#define CSI_IF_CFG_MIPI_IF_CSI (0 << 7)
-#define CSI_IF_CFG_MIPI_IF_MIPI BIT(7)
-#define CSI_IF_CFG_CSI_IF_MASK GENMASK(4, 0)
-#define CSI_IF_CFG_CSI_IF_YUV422_INTLV ((0 << 0) & CSI_IF_CFG_CSI_IF_MASK)
-#define CSI_IF_CFG_CSI_IF_YUV422_16BIT ((1 << 0) & CSI_IF_CFG_CSI_IF_MASK)
-#define CSI_IF_CFG_CSI_IF_BT656 ((4 << 0) & CSI_IF_CFG_CSI_IF_MASK)
-#define CSI_IF_CFG_CSI_IF_BT1120 ((5 << 0) & CSI_IF_CFG_CSI_IF_MASK)
-
-#define CSI_CAP_REG 0x8
-#define CSI_CAP_CH0_CAP_MASK_MASK GENMASK(5, 2)
-#define CSI_CAP_CH0_CAP_MASK(count) (((count) << 2) & CSI_CAP_CH0_CAP_MASK_MASK)
-#define CSI_CAP_CH0_VCAP_ON BIT(1)
-#define CSI_CAP_CH0_SCAP_ON BIT(0)
-
-#define CSI_SYNC_CNT_REG 0xc
-#define CSI_FIFO_THRS_REG 0x10
-#define CSI_BT656_HEAD_CFG_REG 0x14
-#define CSI_PTN_LEN_REG 0x30
-#define CSI_PTN_ADDR_REG 0x34
-#define CSI_VER_REG 0x3c
-
-#define CSI_CH_CFG_REG 0x44
-#define CSI_CH_CFG_INPUT_FMT_MASK GENMASK(23, 20)
-#define CSI_CH_CFG_INPUT_FMT(fmt) (((fmt) << 20) & CSI_CH_CFG_INPUT_FMT_MASK)
-#define CSI_CH_CFG_OUTPUT_FMT_MASK GENMASK(19, 16)
-#define CSI_CH_CFG_OUTPUT_FMT(fmt) (((fmt) << 16) & CSI_CH_CFG_OUTPUT_FMT_MASK)
-#define CSI_CH_CFG_VFLIP_EN BIT(13)
-#define CSI_CH_CFG_HFLIP_EN BIT(12)
-#define CSI_CH_CFG_FIELD_SEL_MASK GENMASK(11, 10)
-#define CSI_CH_CFG_FIELD_SEL_FIELD0 ((0 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
-#define CSI_CH_CFG_FIELD_SEL_FIELD1 ((1 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
-#define CSI_CH_CFG_FIELD_SEL_BOTH ((2 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
-#define CSI_CH_CFG_INPUT_SEQ_MASK GENMASK(9, 8)
-#define CSI_CH_CFG_INPUT_SEQ(seq) (((seq) << 8) & CSI_CH_CFG_INPUT_SEQ_MASK)
-
-#define CSI_CH_SCALE_REG 0x4c
-#define CSI_CH_SCALE_QUART_EN BIT(0)
-
-#define CSI_CH_F0_BUFA_REG 0x50
-
-#define CSI_CH_F1_BUFA_REG 0x58
-
-#define CSI_CH_F2_BUFA_REG 0x60
-
-#define CSI_CH_STA_REG 0x6c
-#define CSI_CH_STA_FIELD_STA_MASK BIT(2)
-#define CSI_CH_STA_FIELD_STA_FIELD0 ((0 << 2) & CSI_CH_STA_FIELD_STA_MASK)
-#define CSI_CH_STA_FIELD_STA_FIELD1 ((1 << 2) & CSI_CH_STA_FIELD_STA_MASK)
-#define CSI_CH_STA_VCAP_STA BIT(1)
-#define CSI_CH_STA_SCAP_STA BIT(0)
-
-#define CSI_CH_INT_EN_REG 0x70
-#define CSI_CH_INT_EN_VS_INT_EN BIT(7)
-#define CSI_CH_INT_EN_HB_OF_INT_EN BIT(6)
-#define CSI_CH_INT_EN_MUL_ERR_INT_EN BIT(5)
-#define CSI_CH_INT_EN_FIFO2_OF_INT_EN BIT(4)
-#define CSI_CH_INT_EN_FIFO1_OF_INT_EN BIT(3)
-#define CSI_CH_INT_EN_FIFO0_OF_INT_EN BIT(2)
-#define CSI_CH_INT_EN_FD_INT_EN BIT(1)
-#define CSI_CH_INT_EN_CD_INT_EN BIT(0)
-
-#define CSI_CH_INT_STA_REG 0x74
-#define CSI_CH_INT_STA_VS_PD BIT(7)
-#define CSI_CH_INT_STA_HB_OF_PD BIT(6)
-#define CSI_CH_INT_STA_MUL_ERR_PD BIT(5)
-#define CSI_CH_INT_STA_FIFO2_OF_PD BIT(4)
-#define CSI_CH_INT_STA_FIFO1_OF_PD BIT(3)
-#define CSI_CH_INT_STA_FIFO0_OF_PD BIT(2)
-#define CSI_CH_INT_STA_FD_PD BIT(1)
-#define CSI_CH_INT_STA_CD_PD BIT(0)
-
-#define CSI_CH_FLD1_VSIZE_REG 0x78
-
-#define CSI_CH_HSIZE_REG 0x80
-#define CSI_CH_HSIZE_HOR_LEN_MASK GENMASK(28, 16)
-#define CSI_CH_HSIZE_HOR_LEN(len) (((len) << 16) & CSI_CH_HSIZE_HOR_LEN_MASK)
-#define CSI_CH_HSIZE_HOR_START_MASK GENMASK(12, 0)
-#define CSI_CH_HSIZE_HOR_START(start) (((start) << 0) & CSI_CH_HSIZE_HOR_START_MASK)
-
-#define CSI_CH_VSIZE_REG 0x84
-#define CSI_CH_VSIZE_VER_LEN_MASK GENMASK(28, 16)
-#define CSI_CH_VSIZE_VER_LEN(len) (((len) << 16) & CSI_CH_VSIZE_VER_LEN_MASK)
-#define CSI_CH_VSIZE_VER_START_MASK GENMASK(12, 0)
-#define CSI_CH_VSIZE_VER_START(start) (((start) << 0) & CSI_CH_VSIZE_VER_START_MASK)
-
-#define CSI_CH_BUF_LEN_REG 0x88
-#define CSI_CH_BUF_LEN_BUF_LEN_C_MASK GENMASK(29, 16)
-#define CSI_CH_BUF_LEN_BUF_LEN_C(len) (((len) << 16) & CSI_CH_BUF_LEN_BUF_LEN_C_MASK)
-#define CSI_CH_BUF_LEN_BUF_LEN_Y_MASK GENMASK(13, 0)
-#define CSI_CH_BUF_LEN_BUF_LEN_Y(len) (((len) << 0) & CSI_CH_BUF_LEN_BUF_LEN_Y_MASK)
-
-#define CSI_CH_FLIP_SIZE_REG 0x8c
-#define CSI_CH_FLIP_SIZE_VER_LEN_MASK GENMASK(28, 16)
-#define CSI_CH_FLIP_SIZE_VER_LEN(len) (((len) << 16) & CSI_CH_FLIP_SIZE_VER_LEN_MASK)
-#define CSI_CH_FLIP_SIZE_VALID_LEN_MASK GENMASK(12, 0)
-#define CSI_CH_FLIP_SIZE_VALID_LEN(len) (((len) << 0) & CSI_CH_FLIP_SIZE_VALID_LEN_MASK)
-
-#define CSI_CH_FRM_CLK_CNT_REG 0x90
-#define CSI_CH_ACC_ITNL_CLK_CNT_REG 0x94
-#define CSI_CH_FIFO_STAT_REG 0x98
-#define CSI_CH_PCLK_STAT_REG 0x9c
+#define SUN6I_CSI_ADDR_VALUE(a) ((a) >> 2)
+
+#define SUN6I_CSI_EN_REG 0x0
+#define SUN6I_CSI_EN_VER_EN BIT(30)
+#define SUN6I_CSI_EN_PTN_CYCLE(v) (((v) << 16) & GENMASK(23, 16))
+#define SUN6I_CSI_EN_SRAM_PWDN BIT(8)
+#define SUN6I_CSI_EN_PTN_START BIT(4)
+#define SUN6I_CSI_EN_CLK_CNT_SPL_VSYNC BIT(3)
+#define SUN6I_CSI_EN_CLK_CNT_EN BIT(2)
+#define SUN6I_CSI_EN_PTN_GEN_EN BIT(1)
+#define SUN6I_CSI_EN_CSI_EN BIT(0)
+
+/* Note that Allwinner manuals and code invert positive/negative definitions. */
+
+#define SUN6I_CSI_IF_CFG_REG 0x4
+#define SUN6I_CSI_IF_CFG_FIELD_DT_PCLK_SHIFT(v) (((v) << 24) & GENMASK(27, 24))
+#define SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE (0 << 21)
+#define SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED (1 << 21)
+#define SUN6I_CSI_IF_CFG_FPS_DS BIT(20)
+#define SUN6I_CSI_IF_CFG_FIELD_POSITIVE (0 << 19)
+#define SUN6I_CSI_IF_CFG_FIELD_NEGATIVE (1 << 19)
+#define SUN6I_CSI_IF_CFG_VREF_POL_POSITIVE (0 << 18)
+#define SUN6I_CSI_IF_CFG_VREF_POL_NEGATIVE (1 << 18)
+#define SUN6I_CSI_IF_CFG_HREF_POL_POSITIVE (0 << 17)
+#define SUN6I_CSI_IF_CFG_HREF_POL_NEGATIVE (1 << 17)
+#define SUN6I_CSI_IF_CFG_CLK_POL_FALLING (0 << 16)
+#define SUN6I_CSI_IF_CFG_CLK_POL_RISING (1 << 16)
+#define SUN6I_CSI_IF_CFG_FIELD_DT_FIELD_VSYNC (0 << 14)
+#define SUN6I_CSI_IF_CFG_FIELD_DT_FIELD (1 << 14)
+#define SUN6I_CSI_IF_CFG_FIELD_DT_VSYNC (2 << 14)
+#define SUN6I_CSI_IF_CFG_DATA_WIDTH_8 (0 << 8)
+#define SUN6I_CSI_IF_CFG_DATA_WIDTH_10 (1 << 8)
+#define SUN6I_CSI_IF_CFG_DATA_WIDTH_12 (2 << 8)
+#define SUN6I_CSI_IF_CFG_DATA_WIDTH_8_PLUS_2 (3 << 8)
+#define SUN6I_CSI_IF_CFG_DATA_WIDTH_2_TIMES_8 (4 << 8)
+#define SUN6I_CSI_IF_CFG_IF_CSI (0 << 7)
+#define SUN6I_CSI_IF_CFG_IF_MIPI (1 << 7)
+#define SUN6I_CSI_IF_CFG_IF_CSI_YUV_RAW (0 << 0)
+#define SUN6I_CSI_IF_CFG_IF_CSI_YUV_COMBINED (1 << 0)
+#define SUN6I_CSI_IF_CFG_IF_CSI_BT656 (4 << 0)
+#define SUN6I_CSI_IF_CFG_IF_CSI_BT1120 (5 << 0)
+
+#define SUN6I_CSI_CAP_REG 0x8
+#define SUN6I_CSI_CAP_MASK(v) (((v) << 2) & GENMASK(5, 2))
+#define SUN6I_CSI_CAP_VCAP_ON BIT(1)
+#define SUN6I_CSI_CAP_SCAP_ON BIT(0)
+
+#define SUN6I_CSI_SYNC_CNT_REG 0xc
+#define SUN6I_CSI_FIFO_THRS_REG 0x10
+#define SUN6I_CSI_BT656_HEAD_CFG_REG 0x14
+
+#define SUN6I_CSI_PTN_LEN_REG 0x30
+#define SUN6I_CSI_PTN_ADDR_REG 0x34
+#define SUN6I_CSI_VER_REG 0x3c
+
+#define SUN6I_CSI_CH_CFG_REG 0x44
+#define SUN6I_CSI_CH_CFG_PAD_VAL(v) (((v) << 24) & GENMASK(31, 24))
+#define SUN6I_CSI_CH_CFG_INPUT_FMT(v) (((v) << 20) & GENMASK(23, 20))
+#define SUN6I_CSI_CH_CFG_OUTPUT_FMT(v) (((v) << 16) & GENMASK(19, 16))
+#define SUN6I_CSI_CH_CFG_VFLIP_EN BIT(13)
+#define SUN6I_CSI_CH_CFG_HFLIP_EN BIT(12)
+#define SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD0 (0 << 10)
+#define SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD1 (1 << 10)
+#define SUN6I_CSI_CH_CFG_FIELD_SEL_EITHER (2 << 10)
+#define SUN6I_CSI_CH_CFG_INPUT_YUV_SEQ(v) (((v) << 8) & GENMASK(9, 8))
+
+#define SUN6I_CSI_INPUT_FMT_RAW 0
+#define SUN6I_CSI_INPUT_FMT_YUV422 3
+#define SUN6I_CSI_INPUT_FMT_YUV420 4
+
+/* Note that Allwinner manuals and code invert frame/field definitions. */
+
+/* RAW */
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8 0
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10 1
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12 2
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565 4
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_RGB888 5
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_PRGB888 6
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8 8
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10 9
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12 10
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565 12
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_RGB888 13
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_PRGB888 14
+
+/* YUV */
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P 0
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P 1
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P 2
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P 3
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP 4
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP 5
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP 6
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP 7
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422MB 8
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB 9
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB 10
+#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422MB 11
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP_10 12
+#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP_10 13
+
+/* YUV Planar */
+#define SUN6I_CSI_INPUT_YUV_SEQ_YUYV 0
+#define SUN6I_CSI_INPUT_YUV_SEQ_YVYU 1
+#define SUN6I_CSI_INPUT_YUV_SEQ_UYVY 2
+#define SUN6I_CSI_INPUT_YUV_SEQ_VYUY 3
+
+/* YUV Semi-planar */
+#define SUN6I_CSI_INPUT_YUV_SEQ_UV 0
+#define SUN6I_CSI_INPUT_YUV_SEQ_VU 1
+
+#define SUN6I_CSI_CH_SCALE_REG 0x4c
+#define SUN6I_CSI_CH_SCALE_QUART_EN BIT(0)
+
+#define SUN6I_CSI_CH_FIFO0_ADDR_REG 0x50
+#define SUN6I_CSI_CH_FIFO1_ADDR_REG 0x58
+#define SUN6I_CSI_CH_FIFO2_ADDR_REG 0x60
+
+#define SUN6I_CSI_CH_STA_REG 0x6c
+#define SUN6I_CSI_CH_STA_FIELD BIT(2)
+#define SUN6I_CSI_CH_STA_VCAP BIT(1)
+#define SUN6I_CSI_CH_STA_SCAP BIT(0)
+
+#define SUN6I_CSI_CH_INT_EN_REG 0x70
+#define SUN6I_CSI_CH_INT_EN_VS BIT(7)
+#define SUN6I_CSI_CH_INT_EN_HB_OF BIT(6)
+#define SUN6I_CSI_CH_INT_EN_MUL_ERR BIT(5)
+#define SUN6I_CSI_CH_INT_EN_FIFO2_OF BIT(4)
+#define SUN6I_CSI_CH_INT_EN_FIFO1_OF BIT(3)
+#define SUN6I_CSI_CH_INT_EN_FIFO0_OF BIT(2)
+#define SUN6I_CSI_CH_INT_EN_FD BIT(1)
+#define SUN6I_CSI_CH_INT_EN_CD BIT(0)
+
+#define SUN6I_CSI_CH_INT_STA_REG 0x74
+#define SUN6I_CSI_CH_INT_STA_CLEAR 0xff
+#define SUN6I_CSI_CH_INT_STA_VS BIT(7)
+#define SUN6I_CSI_CH_INT_STA_HB_OF BIT(6)
+#define SUN6I_CSI_CH_INT_STA_MUL_ERR BIT(5)
+#define SUN6I_CSI_CH_INT_STA_FIFO2_OF BIT(4)
+#define SUN6I_CSI_CH_INT_STA_FIFO1_OF BIT(3)
+#define SUN6I_CSI_CH_INT_STA_FIFO0_OF BIT(2)
+#define SUN6I_CSI_CH_INT_STA_FD BIT(1)
+#define SUN6I_CSI_CH_INT_STA_CD BIT(0)
+
+#define SUN6I_CSI_CH_FLD1_VSIZE_REG 0x78
+#define SUN6I_CSI_CH_FLD1_VSIZE_VER_LEN(v) (((v) << 16) & GENMASK(28, 16))
+#define SUN6I_CSI_CH_FLD1_VSIZE_VER_START(v) ((v) & GENMASK(12, 0))
+
+#define SUN6I_CSI_CH_HSIZE_REG 0x80
+#define SUN6I_CSI_CH_HSIZE_LEN(v) (((v) << 16) & GENMASK(28, 16))
+#define SUN6I_CSI_CH_HSIZE_START(v) ((v) & GENMASK(12, 0))
+
+#define SUN6I_CSI_CH_VSIZE_REG 0x84
+#define SUN6I_CSI_CH_VSIZE_LEN(v) (((v) << 16) & GENMASK(28, 16))
+#define SUN6I_CSI_CH_VSIZE_START(v) ((v) & GENMASK(12, 0))
+
+#define SUN6I_CSI_CH_BUF_LEN_REG 0x88
+#define SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(v) (((v) << 16) & GENMASK(29, 16))
+#define SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(v) ((v) & GENMASK(13, 0))
+
+#define SUN6I_CSI_CH_FLIP_SIZE_REG 0x8c
+#define SUN6I_CSI_CH_FLIP_SIZE_VER_LEN(v) (((v) << 16) & GENMASK(28, 16))
+#define SUN6I_CSI_CH_FLIP_SIZE_VALID_LEN(v) ((v) & GENMASK(12, 0))
+
+#define SUN6I_CSI_CH_FRM_CLK_CNT_REG 0x90
+#define SUN6I_CSI_CH_ACC_ITNL_CLK_CNT_REG 0x94
+#define SUN6I_CSI_CH_FIFO_STAT_REG 0x98
+#define SUN6I_CSI_CH_PCLK_STAT_REG 0x9c
/*
* csi input data format
--
2.38.1
Define and export useful helpers to access dimensions and pixel format.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 19 +++++++++++++++++++
.../sunxi/sun6i-csi/sun6i_csi_capture.h | 5 +++++
2 files changed, 24 insertions(+)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index bdc5465b68a2..a3c3bbf6a506 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -25,6 +25,25 @@
/* Helpers */
+void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev,
+ unsigned int *width, unsigned int *height)
+{
+ if (width)
+ *width = csi_dev->capture.format.fmt.pix.width;
+ if (height)
+ *height = csi_dev->capture.format.fmt.pix.height;
+}
+
+void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev,
+ u32 *pixelformat, u32 *field)
+{
+ if (pixelformat)
+ *pixelformat = csi_dev->capture.format.fmt.pix.pixelformat;
+
+ if (field)
+ *field = csi_dev->capture.format.fmt.pix.field;
+}
+
static struct v4l2_subdev *
sun6i_csi_capture_remote_subdev(struct sun6i_csi_capture *capture, u32 *pad)
{
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
index 7fa66a2af5ec..935f35b7049a 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
@@ -37,6 +37,11 @@ struct sun6i_csi_capture {
u32 mbus_code;
};
+void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev,
+ unsigned int *width, unsigned int *height);
+void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev,
+ u32 *pixelformat, u32 *field);
+
void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev);
void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev);
--
2.38.1
In an effort to distinguish between the core csi engine (to be
represented as the bridge) and the dma engine (the capture video
device), rename the video component to capture, with the appropriate
prefix. No functional change intended.
Signed-off-by: Paul Kocialkowski <[email protected]>
Reviewed-by: Maxime Ripard <[email protected]>
---
.../media/platform/sunxi/sun6i-csi/Makefile | 2 +-
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 6 +-
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 4 +-
.../{sun6i_video.c => sun6i_csi_capture.c} | 342 +++++++++---------
.../{sun6i_video.h => sun6i_csi_capture.h} | 14 +-
5 files changed, 186 insertions(+), 182 deletions(-)
rename drivers/media/platform/sunxi/sun6i-csi/{sun6i_video.c => sun6i_csi_capture.c} (58%)
rename drivers/media/platform/sunxi/sun6i-csi/{sun6i_video.h => sun6i_csi_capture.h} (64%)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile
index 7a699580a641..87e7a715140a 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/Makefile
+++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-sun6i-csi-y += sun6i_video.o sun6i_csi.o sun6i_csi_bridge.o
+sun6i-csi-y += sun6i_csi.o sun6i_csi_bridge.o sun6i_csi_capture.o
obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 49f1218b0b28..cffd664cbc0b 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -662,7 +662,7 @@ static irqreturn_t sun6i_csi_interrupt(int irq, void *private)
}
if (status & CSI_CH_INT_STA_FD_PD)
- sun6i_video_frame_done(csi_dev);
+ sun6i_csi_capture_frame_done(csi_dev);
regmap_write(regmap, CSI_CH_INT_STA_REG, status);
@@ -841,7 +841,7 @@ static int sun6i_csi_probe(struct platform_device *platform_dev)
if (ret)
goto error_v4l2;
- ret = sun6i_video_setup(csi_dev);
+ ret = sun6i_csi_capture_setup(csi_dev);
if (ret)
goto error_bridge;
@@ -863,7 +863,7 @@ static int sun6i_csi_remove(struct platform_device *pdev)
{
struct sun6i_csi_device *csi_dev = platform_get_drvdata(pdev);
- sun6i_video_cleanup(csi_dev);
+ sun6i_csi_capture_cleanup(csi_dev);
sun6i_csi_bridge_cleanup(csi_dev);
sun6i_csi_v4l2_cleanup(csi_dev);
sun6i_csi_resources_cleanup(csi_dev);
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 1dd76631ec87..48acf8078f15 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -13,7 +13,7 @@
#include <media/videobuf2-v4l2.h>
#include "sun6i_csi_bridge.h"
-#include "sun6i_video.h"
+#include "sun6i_csi_capture.h"
#define SUN6I_CSI_NAME "sun6i-csi"
#define SUN6I_CSI_DESCRIPTION "Allwinner A31 CSI Device"
@@ -57,7 +57,7 @@ struct sun6i_csi_device {
struct sun6i_csi_config config;
struct sun6i_csi_v4l2 v4l2;
struct sun6i_csi_bridge bridge;
- struct sun6i_video video;
+ struct sun6i_csi_capture capture;
struct regmap *regmap;
struct clk *clock_mod;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
similarity index 58%
rename from drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
rename to drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index fa83211a2c5a..9838a5a43c2d 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -15,7 +15,7 @@
#include <media/videobuf2-v4l2.h>
#include "sun6i_csi.h"
-#include "sun6i_video.h"
+#include "sun6i_csi_capture.h"
/* This is got from BSP sources. */
#define MIN_WIDTH (32)
@@ -26,7 +26,7 @@
/* Helpers */
static struct v4l2_subdev *
-sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
+sun6i_csi_capture_remote_subdev(struct sun6i_csi_capture *capture, u32 *pad)
{
struct media_pad *remote;
@@ -43,7 +43,7 @@ sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
/* Format */
-static const u32 sun6i_video_formats[] = {
+static const u32 sun6i_csi_capture_formats[] = {
V4L2_PIX_FMT_SBGGR8,
V4L2_PIX_FMT_SGBRG8,
V4L2_PIX_FMT_SGRBG8,
@@ -73,51 +73,52 @@ static const u32 sun6i_video_formats[] = {
V4L2_PIX_FMT_JPEG,
};
-static bool sun6i_video_format_check(u32 format)
+static bool sun6i_csi_capture_format_check(u32 format)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(sun6i_video_formats); i++)
- if (sun6i_video_formats[i] == format)
+ for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++)
+ if (sun6i_csi_capture_formats[i] == format)
return true;
return false;
}
-/* Video */
+/* Capture */
-static void sun6i_video_buffer_configure(struct sun6i_csi_device *csi_dev,
- struct sun6i_csi_buffer *csi_buffer)
+static void
+sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,
+ struct sun6i_csi_buffer *csi_buffer)
{
csi_buffer->queued_to_csi = true;
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_csi_capture_configure(struct sun6i_csi_device *csi_dev)
{
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct sun6i_csi_config config = { 0 };
- config.pixelformat = video->format.fmt.pix.pixelformat;
- config.code = video->mbus_code;
- config.field = video->format.fmt.pix.field;
- config.width = video->format.fmt.pix.width;
- config.height = video->format.fmt.pix.height;
+ config.pixelformat = capture->format.fmt.pix.pixelformat;
+ config.code = capture->mbus_code;
+ config.field = capture->format.fmt.pix.field;
+ config.width = capture->format.fmt.pix.width;
+ config.height = capture->format.fmt.pix.height;
sun6i_csi_update_config(csi_dev, &config);
}
/* Queue */
-static int sun6i_video_queue_setup(struct vb2_queue *queue,
- unsigned int *buffers_count,
- unsigned int *planes_count,
- unsigned int sizes[],
- struct device *alloc_devs[])
+static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,
+ unsigned int *buffers_count,
+ unsigned int *planes_count,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
- struct sun6i_video *video = &csi_dev->video;
- unsigned int size = video->format.fmt.pix.sizeimage;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
+ unsigned int size = capture->format.fmt.pix.sizeimage;
if (*planes_count)
return sizes[0] < size ? -EINVAL : 0;
@@ -128,15 +129,15 @@ static int sun6i_video_queue_setup(struct vb2_queue *queue,
return 0;
}
-static int sun6i_video_buffer_prepare(struct vb2_buffer *buffer)
+static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
struct sun6i_csi_buffer *csi_buffer =
container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
- unsigned long size = video->format.fmt.pix.sizeimage;
+ unsigned long size = capture->format.fmt.pix.sizeimage;
if (vb2_plane_size(buffer, 0) < size) {
v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",
@@ -147,62 +148,62 @@ static int sun6i_video_buffer_prepare(struct vb2_buffer *buffer)
vb2_set_plane_payload(buffer, 0, size);
csi_buffer->dma_addr = vb2_dma_contig_plane_dma_addr(buffer, 0);
- v4l2_buffer->field = video->format.fmt.pix.field;
+ v4l2_buffer->field = capture->format.fmt.pix.field;
return 0;
}
-static void sun6i_video_buffer_queue(struct vb2_buffer *buffer)
+static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer)
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
struct sun6i_csi_buffer *csi_buffer =
container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
unsigned long flags;
- spin_lock_irqsave(&video->dma_queue_lock, flags);
+ spin_lock_irqsave(&capture->dma_queue_lock, flags);
csi_buffer->queued_to_csi = false;
- list_add_tail(&csi_buffer->list, &video->dma_queue);
- spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+ list_add_tail(&csi_buffer->list, &capture->dma_queue);
+ spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
}
-static int sun6i_video_start_streaming(struct vb2_queue *queue,
- unsigned int count)
+static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
{
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_capture *capture = &csi_dev->capture;
+ struct video_device *video_dev = &capture->video_dev;
struct sun6i_csi_buffer *buf;
struct sun6i_csi_buffer *next_buf;
struct v4l2_subdev *subdev;
unsigned long flags;
int ret;
- video->sequence = 0;
+ capture->sequence = 0;
ret = video_device_pipeline_alloc_start(video_dev);
if (ret < 0)
goto error_dma_queue_flush;
- if (video->mbus_code == 0) {
+ if (capture->mbus_code == 0) {
ret = -EINVAL;
goto error_media_pipeline;
}
- subdev = sun6i_video_remote_subdev(video, NULL);
+ subdev = sun6i_csi_capture_remote_subdev(capture, NULL);
if (!subdev) {
ret = -EINVAL;
goto error_media_pipeline;
}
- sun6i_video_configure(csi_dev);
+ sun6i_csi_capture_configure(csi_dev);
- spin_lock_irqsave(&video->dma_queue_lock, flags);
+ spin_lock_irqsave(&capture->dma_queue_lock, flags);
- buf = list_first_entry(&video->dma_queue,
+ buf = list_first_entry(&capture->dma_queue,
struct sun6i_csi_buffer, list);
- sun6i_video_buffer_configure(csi_dev, buf);
+ sun6i_csi_capture_buffer_configure(csi_dev, buf);
sun6i_csi_set_stream(csi_dev, true);
@@ -224,9 +225,9 @@ static int sun6i_video_start_streaming(struct vb2_queue *queue,
* would also drop frame when lacking of queued buffer.
*/
next_buf = list_next_entry(buf, list);
- sun6i_video_buffer_configure(csi_dev, next_buf);
+ sun6i_csi_capture_buffer_configure(csi_dev, next_buf);
- spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+ spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD)
@@ -241,52 +242,52 @@ static int sun6i_video_start_streaming(struct vb2_queue *queue,
video_device_pipeline_stop(video_dev);
error_dma_queue_flush:
- spin_lock_irqsave(&video->dma_queue_lock, flags);
- list_for_each_entry(buf, &video->dma_queue, list)
+ spin_lock_irqsave(&capture->dma_queue_lock, flags);
+ list_for_each_entry(buf, &capture->dma_queue, list)
vb2_buffer_done(&buf->v4l2_buffer.vb2_buf,
VB2_BUF_STATE_QUEUED);
- INIT_LIST_HEAD(&video->dma_queue);
- spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+ INIT_LIST_HEAD(&capture->dma_queue);
+ spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
return ret;
}
-static void sun6i_video_stop_streaming(struct vb2_queue *queue)
+static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct v4l2_subdev *subdev;
unsigned long flags;
struct sun6i_csi_buffer *buf;
- subdev = sun6i_video_remote_subdev(video, NULL);
+ subdev = sun6i_csi_capture_remote_subdev(capture, NULL);
if (subdev)
v4l2_subdev_call(subdev, video, s_stream, 0);
sun6i_csi_set_stream(csi_dev, false);
- video_device_pipeline_stop(&video->video_dev);
+ video_device_pipeline_stop(&capture->video_dev);
/* Release all active buffers */
- spin_lock_irqsave(&video->dma_queue_lock, flags);
- list_for_each_entry(buf, &video->dma_queue, list)
+ spin_lock_irqsave(&capture->dma_queue_lock, flags);
+ list_for_each_entry(buf, &capture->dma_queue, list)
vb2_buffer_done(&buf->v4l2_buffer.vb2_buf, VB2_BUF_STATE_ERROR);
- INIT_LIST_HEAD(&video->dma_queue);
- spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+ INIT_LIST_HEAD(&capture->dma_queue);
+ spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
}
-void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev)
+void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)
{
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct sun6i_csi_buffer *buf;
struct sun6i_csi_buffer *next_buf;
struct vb2_v4l2_buffer *v4l2_buffer;
- spin_lock(&video->dma_queue_lock);
+ spin_lock(&capture->dma_queue_lock);
- buf = list_first_entry(&video->dma_queue,
+ buf = list_first_entry(&capture->dma_queue,
struct sun6i_csi_buffer, list);
- if (list_is_last(&buf->list, &video->dma_queue)) {
+ if (list_is_last(&buf->list, &capture->dma_queue)) {
dev_dbg(csi_dev->dev, "Frame dropped!\n");
goto complete;
}
@@ -298,7 +299,7 @@ void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev)
* for next ISR call.
*/
if (!next_buf->queued_to_csi) {
- sun6i_video_buffer_configure(csi_dev, next_buf);
+ sun6i_csi_capture_buffer_configure(csi_dev, next_buf);
dev_dbg(csi_dev->dev, "Frame dropped!\n");
goto complete;
}
@@ -306,39 +307,39 @@ void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev)
list_del(&buf->list);
v4l2_buffer = &buf->v4l2_buffer;
v4l2_buffer->vb2_buf.timestamp = ktime_get_ns();
- v4l2_buffer->sequence = video->sequence;
+ v4l2_buffer->sequence = capture->sequence;
vb2_buffer_done(&v4l2_buffer->vb2_buf, VB2_BUF_STATE_DONE);
/* Prepare buffer for next frame but one. */
- if (!list_is_last(&next_buf->list, &video->dma_queue)) {
+ if (!list_is_last(&next_buf->list, &capture->dma_queue)) {
next_buf = list_next_entry(next_buf, list);
- sun6i_video_buffer_configure(csi_dev, next_buf);
+ sun6i_csi_capture_buffer_configure(csi_dev, next_buf);
} else {
dev_dbg(csi_dev->dev, "Next frame will be dropped!\n");
}
complete:
- video->sequence++;
- spin_unlock(&video->dma_queue_lock);
+ capture->sequence++;
+ spin_unlock(&capture->dma_queue_lock);
}
-static const struct vb2_ops sun6i_video_queue_ops = {
- .queue_setup = sun6i_video_queue_setup,
- .buf_prepare = sun6i_video_buffer_prepare,
- .buf_queue = sun6i_video_buffer_queue,
- .start_streaming = sun6i_video_start_streaming,
- .stop_streaming = sun6i_video_stop_streaming,
+static const struct vb2_ops sun6i_csi_capture_queue_ops = {
+ .queue_setup = sun6i_csi_capture_queue_setup,
+ .buf_prepare = sun6i_csi_capture_buffer_prepare,
+ .buf_queue = sun6i_csi_capture_buffer_queue,
+ .start_streaming = sun6i_csi_capture_start_streaming,
+ .stop_streaming = sun6i_csi_capture_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
/* V4L2 Device */
-static int sun6i_video_querycap(struct file *file, void *private,
- struct v4l2_capability *capability)
+static int sun6i_csi_capture_querycap(struct file *file, void *private,
+ struct v4l2_capability *capability)
{
struct sun6i_csi_device *csi_dev = video_drvdata(file);
- struct video_device *video_dev = &csi_dev->video.video_dev;
+ struct video_device *video_dev = &csi_dev->capture.video_dev;
strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver));
strscpy(capability->card, video_dev->name, sizeof(capability->card));
@@ -348,38 +349,38 @@ static int sun6i_video_querycap(struct file *file, void *private,
return 0;
}
-static int sun6i_video_enum_fmt(struct file *file, void *private,
- struct v4l2_fmtdesc *fmtdesc)
+static int sun6i_csi_capture_enum_fmt(struct file *file, void *private,
+ struct v4l2_fmtdesc *fmtdesc)
{
u32 index = fmtdesc->index;
- if (index >= ARRAY_SIZE(sun6i_video_formats))
+ if (index >= ARRAY_SIZE(sun6i_csi_capture_formats))
return -EINVAL;
- fmtdesc->pixelformat = sun6i_video_formats[index];
+ fmtdesc->pixelformat = sun6i_csi_capture_formats[index];
return 0;
}
-static int sun6i_video_g_fmt(struct file *file, void *private,
- struct v4l2_format *format)
+static int sun6i_csi_capture_g_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
{
struct sun6i_csi_device *csi_dev = video_drvdata(file);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
- *format = video->format;
+ *format = capture->format;
return 0;
}
-static int sun6i_video_format_try(struct sun6i_video *video,
- struct v4l2_format *format)
+static int sun6i_csi_capture_format_try(struct sun6i_csi_capture *capture,
+ struct v4l2_format *format)
{
struct v4l2_pix_format *pix_format = &format->fmt.pix;
int bpp;
- if (!sun6i_video_format_check(pix_format->pixelformat))
- pix_format->pixelformat = sun6i_video_formats[0];
+ if (!sun6i_csi_capture_format_check(pix_format->pixelformat))
+ pix_format->pixelformat = sun6i_csi_capture_formats[0];
v4l_bound_align_image(&pix_format->width, MIN_WIDTH, MAX_WIDTH, 1,
&pix_format->height, MIN_HEIGHT, MAX_WIDTH, 1, 1);
@@ -403,43 +404,43 @@ static int sun6i_video_format_try(struct sun6i_video *video,
return 0;
}
-static int sun6i_video_format_set(struct sun6i_video *video,
- struct v4l2_format *format)
+static int sun6i_csi_capture_format_set(struct sun6i_csi_capture *capture,
+ struct v4l2_format *format)
{
int ret;
- ret = sun6i_video_format_try(video, format);
+ ret = sun6i_csi_capture_format_try(capture, format);
if (ret)
return ret;
- video->format = *format;
+ capture->format = *format;
return 0;
}
-static int sun6i_video_s_fmt(struct file *file, void *private,
- struct v4l2_format *format)
+static int sun6i_csi_capture_s_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
{
struct sun6i_csi_device *csi_dev = video_drvdata(file);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
- if (vb2_is_busy(&video->queue))
+ if (vb2_is_busy(&capture->queue))
return -EBUSY;
- return sun6i_video_format_set(video, format);
+ return sun6i_csi_capture_format_set(capture, format);
}
-static int sun6i_video_try_fmt(struct file *file, void *private,
- struct v4l2_format *format)
+static int sun6i_csi_capture_try_fmt(struct file *file, void *private,
+ struct v4l2_format *format)
{
struct sun6i_csi_device *csi_dev = video_drvdata(file);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
- return sun6i_video_format_try(video, format);
+ return sun6i_csi_capture_format_try(capture, format);
}
-static int sun6i_video_enum_input(struct file *file, void *private,
- struct v4l2_input *input)
+static int sun6i_csi_capture_enum_input(struct file *file, void *private,
+ struct v4l2_input *input)
{
if (input->index != 0)
return -EINVAL;
@@ -450,16 +451,16 @@ static int sun6i_video_enum_input(struct file *file, void *private,
return 0;
}
-static int sun6i_video_g_input(struct file *file, void *private,
- unsigned int *index)
+static int sun6i_csi_capture_g_input(struct file *file, void *private,
+ unsigned int *index)
{
*index = 0;
return 0;
}
-static int sun6i_video_s_input(struct file *file, void *private,
- unsigned int index)
+static int sun6i_csi_capture_s_input(struct file *file, void *private,
+ unsigned int index)
{
if (index != 0)
return -EINVAL;
@@ -467,17 +468,17 @@ static int sun6i_video_s_input(struct file *file, void *private,
return 0;
}
-static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
- .vidioc_querycap = sun6i_video_querycap,
+static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = {
+ .vidioc_querycap = sun6i_csi_capture_querycap,
- .vidioc_enum_fmt_vid_cap = sun6i_video_enum_fmt,
- .vidioc_g_fmt_vid_cap = sun6i_video_g_fmt,
- .vidioc_s_fmt_vid_cap = sun6i_video_s_fmt,
- .vidioc_try_fmt_vid_cap = sun6i_video_try_fmt,
+ .vidioc_enum_fmt_vid_cap = sun6i_csi_capture_enum_fmt,
+ .vidioc_g_fmt_vid_cap = sun6i_csi_capture_g_fmt,
+ .vidioc_s_fmt_vid_cap = sun6i_csi_capture_s_fmt,
+ .vidioc_try_fmt_vid_cap = sun6i_csi_capture_try_fmt,
- .vidioc_enum_input = sun6i_video_enum_input,
- .vidioc_g_input = sun6i_video_g_input,
- .vidioc_s_input = sun6i_video_s_input,
+ .vidioc_enum_input = sun6i_csi_capture_enum_input,
+ .vidioc_g_input = sun6i_csi_capture_g_input,
+ .vidioc_s_input = sun6i_csi_capture_s_input,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
@@ -492,20 +493,20 @@ static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
/* V4L2 File */
-static int sun6i_video_open(struct file *file)
+static int sun6i_csi_capture_open(struct file *file)
{
struct sun6i_csi_device *csi_dev = video_drvdata(file);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
int ret = 0;
- if (mutex_lock_interruptible(&video->lock))
+ if (mutex_lock_interruptible(&capture->lock))
return -ERESTARTSYS;
ret = v4l2_fh_open(file);
if (ret < 0)
goto error_lock;
- ret = v4l2_pipeline_pm_get(&video->video_dev.entity);
+ ret = v4l2_pipeline_pm_get(&capture->video_dev.entity);
if (ret < 0)
goto error_v4l2_fh;
@@ -516,7 +517,7 @@ static int sun6i_video_open(struct file *file)
goto error_v4l2_fh;
}
- mutex_unlock(&video->lock);
+ mutex_unlock(&capture->lock);
return 0;
@@ -524,37 +525,37 @@ static int sun6i_video_open(struct file *file)
v4l2_fh_release(file);
error_lock:
- mutex_unlock(&video->lock);
+ mutex_unlock(&capture->lock);
return ret;
}
-static int sun6i_video_close(struct file *file)
+static int sun6i_csi_capture_close(struct file *file)
{
struct sun6i_csi_device *csi_dev = video_drvdata(file);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
bool last_close;
- mutex_lock(&video->lock);
+ mutex_lock(&capture->lock);
last_close = v4l2_fh_is_singular_file(file);
_vb2_fop_release(file, NULL);
- v4l2_pipeline_pm_put(&video->video_dev.entity);
+ v4l2_pipeline_pm_put(&capture->video_dev.entity);
/* Power off at last close. */
if (last_close)
sun6i_csi_set_power(csi_dev, false);
- mutex_unlock(&video->lock);
+ mutex_unlock(&capture->lock);
return 0;
}
-static const struct v4l2_file_operations sun6i_video_fops = {
+static const struct v4l2_file_operations sun6i_csi_capture_fops = {
.owner = THIS_MODULE,
- .open = sun6i_video_open,
- .release = sun6i_video_close,
+ .open = sun6i_csi_capture_open,
+ .release = sun6i_csi_capture_close,
.unlocked_ioctl = video_ioctl2,
.mmap = vb2_fop_mmap,
.poll = vb2_fop_poll
@@ -562,8 +563,9 @@ 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)
+static int
+sun6i_csi_capture_link_validate_get_format(struct media_pad *pad,
+ struct v4l2_subdev_format *fmt)
{
if (is_media_entity_v4l2_subdev(pad->entity)) {
struct v4l2_subdev *sd =
@@ -577,72 +579,74 @@ static int sun6i_video_link_validate_get_format(struct media_pad *pad,
return -EINVAL;
}
-static int sun6i_video_link_validate(struct media_link *link)
+static int sun6i_csi_capture_link_validate(struct media_link *link)
{
struct video_device *vdev = container_of(link->sink->entity,
struct video_device, entity);
struct sun6i_csi_device *csi_dev = video_get_drvdata(vdev);
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct v4l2_subdev_format source_fmt;
int ret;
- video->mbus_code = 0;
+ capture->mbus_code = 0;
if (!media_pad_remote_pad_first(link->sink->entity->pads)) {
- dev_info(csi_dev->dev, "video node %s pad not connected\n",
+ dev_info(csi_dev->dev, "capture node %s pad not connected\n",
vdev->name);
return -ENOLINK;
}
- ret = sun6i_video_link_validate_get_format(link->source, &source_fmt);
+ ret = sun6i_csi_capture_link_validate_get_format(link->source,
+ &source_fmt);
if (ret < 0)
return ret;
if (!sun6i_csi_is_format_supported(csi_dev,
- video->format.fmt.pix.pixelformat,
+ capture->format.fmt.pix.pixelformat,
source_fmt.format.code)) {
dev_err(csi_dev->dev,
"Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
- video->format.fmt.pix.pixelformat,
+ capture->format.fmt.pix.pixelformat,
source_fmt.format.code);
return -EPIPE;
}
- if (source_fmt.format.width != video->format.fmt.pix.width ||
- source_fmt.format.height != video->format.fmt.pix.height) {
+ if (source_fmt.format.width != capture->format.fmt.pix.width ||
+ source_fmt.format.height != capture->format.fmt.pix.height) {
dev_err(csi_dev->dev,
"Wrong width or height %ux%u (%ux%u expected)\n",
- video->format.fmt.pix.width, video->format.fmt.pix.height,
+ capture->format.fmt.pix.width,
+ capture->format.fmt.pix.height,
source_fmt.format.width, source_fmt.format.height);
return -EPIPE;
}
- video->mbus_code = source_fmt.format.code;
+ capture->mbus_code = source_fmt.format.code;
return 0;
}
-static const struct media_entity_operations sun6i_video_media_ops = {
- .link_validate = sun6i_video_link_validate
+static const struct media_entity_operations sun6i_csi_capture_media_ops = {
+ .link_validate = sun6i_csi_capture_link_validate
};
-/* Video */
+/* Capture */
-int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
+int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
{
- struct sun6i_video *video = &csi_dev->video;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;
- struct video_device *video_dev = &video->video_dev;
- struct vb2_queue *queue = &video->queue;
- struct media_pad *pad = &video->pad;
+ struct video_device *video_dev = &capture->video_dev;
+ struct vb2_queue *queue = &capture->queue;
+ struct media_pad *pad = &capture->pad;
struct v4l2_format format = { 0 };
struct v4l2_pix_format *pix_format = &format.fmt.pix;
int ret;
/* Media Entity */
- video_dev->entity.ops = &sun6i_video_media_ops;
+ video_dev->entity.ops = &sun6i_csi_capture_media_ops;
/* Media Pad */
@@ -654,22 +658,22 @@ int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
/* DMA queue */
- INIT_LIST_HEAD(&video->dma_queue);
- spin_lock_init(&video->dma_queue_lock);
+ INIT_LIST_HEAD(&capture->dma_queue);
+ spin_lock_init(&capture->dma_queue_lock);
- video->sequence = 0;
+ capture->sequence = 0;
/* Queue */
- mutex_init(&video->lock);
+ mutex_init(&capture->lock);
queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
queue->io_modes = VB2_MMAP | VB2_DMABUF;
queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);
- queue->ops = &sun6i_video_queue_ops;
+ queue->ops = &sun6i_csi_capture_queue_ops;
queue->mem_ops = &vb2_dma_contig_memops;
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- queue->lock = &video->lock;
+ queue->lock = &capture->lock;
queue->dev = csi_dev->dev;
queue->drv_priv = csi_dev;
@@ -685,12 +689,12 @@ int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
/* V4L2 Format */
format.type = queue->type;
- pix_format->pixelformat = sun6i_video_formats[0];
+ pix_format->pixelformat = sun6i_csi_capture_formats[0];
pix_format->width = 1280;
pix_format->height = 720;
pix_format->field = V4L2_FIELD_NONE;
- sun6i_video_format_set(video, &format);
+ sun6i_csi_capture_format_set(capture, &format);
/* Video Device */
@@ -698,11 +702,11 @@ int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
video_dev->vfl_dir = VFL_DIR_RX;
video_dev->release = video_device_release_empty;
- video_dev->fops = &sun6i_video_fops;
- video_dev->ioctl_ops = &sun6i_video_ioctl_ops;
+ video_dev->fops = &sun6i_csi_capture_fops;
+ video_dev->ioctl_ops = &sun6i_csi_capture_ioctl_ops;
video_dev->v4l2_dev = v4l2_dev;
video_dev->queue = queue;
- video_dev->lock = &video->lock;
+ video_dev->lock = &capture->lock;
video_set_drvdata(video_dev, csi_dev);
@@ -736,17 +740,17 @@ int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
error_media_entity:
media_entity_cleanup(&video_dev->entity);
- mutex_destroy(&video->lock);
+ mutex_destroy(&capture->lock);
return ret;
}
-void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev)
+void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev)
{
- struct sun6i_video *video = &csi_dev->video;
- struct video_device *video_dev = &video->video_dev;
+ struct sun6i_csi_capture *capture = &csi_dev->capture;
+ struct video_device *video_dev = &capture->video_dev;
vb2_video_unregister_device(video_dev);
media_entity_cleanup(&video_dev->entity);
- mutex_destroy(&video->lock);
+ mutex_destroy(&capture->lock);
}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
similarity index 64%
rename from drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
rename to drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
index a917d2da6deb..36bba31fcb48 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
@@ -5,15 +5,15 @@
* Author: Yong Deng <[email protected]>
*/
-#ifndef __SUN6I_VIDEO_H__
-#define __SUN6I_VIDEO_H__
+#ifndef __SUN6I_CAPTURE_H__
+#define __SUN6I_CAPTURE_H__
#include <media/v4l2-dev.h>
#include <media/videobuf2-core.h>
struct sun6i_csi_device;
-struct sun6i_video {
+struct sun6i_csi_capture {
struct video_device video_dev;
struct vb2_queue queue;
struct mutex lock; /* Queue lock. */
@@ -27,9 +27,9 @@ struct sun6i_video {
unsigned int sequence;
};
-int sun6i_video_setup(struct sun6i_csi_device *csi_dev);
-void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev);
+int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev);
+void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev);
-void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev);
+void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev);
-#endif /* __SUN6I_VIDEO_H__ */
+#endif /* __SUN6I_CAPTURE_H__ */
--
2.38.1
Rework the capture link validate implementation with actual logic that
reflects the possibilities of the device instead of the combinatory
helper functions, using the match list when needed.
Remove the previous dedicated helper.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 120 ------------------
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 11 --
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 106 +++++++++-------
3 files changed, 62 insertions(+), 175 deletions(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 47ea3d68a4b5..75d9f7edf828 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -28,126 +28,6 @@
#include "sun6i_csi.h"
#include "sun6i_csi_reg.h"
-/* Helpers */
-
-/* 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)
-{
- struct v4l2_fwnode_endpoint *endpoint =
- &csi_dev->bridge.source_parallel.endpoint;
-
- /*
- * Some video receivers have the ability to be compatible with
- * 8bit and 16bit bus width.
- * Identify the media bus format from device tree.
- */
- if ((endpoint->bus_type == V4L2_MBUS_PARALLEL
- || endpoint->bus_type == V4L2_MBUS_BT656)
- && endpoint->bus.parallel.bus_width == 16) {
- switch (pixformat) {
- case V4L2_PIX_FMT_NV12_16L16:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_YUV422P:
- switch (mbus_code) {
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_VYUY8_1X16:
- case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_YVYU8_1X16:
- return true;
- default:
- dev_dbg(csi_dev->dev,
- "Unsupported mbus code: 0x%x\n",
- mbus_code);
- break;
- }
- break;
- default:
- dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n",
- pixformat);
- break;
- }
- return false;
- }
-
- switch (pixformat) {
- case V4L2_PIX_FMT_SBGGR8:
- return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8);
- case V4L2_PIX_FMT_SGBRG8:
- return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8);
- case V4L2_PIX_FMT_SGRBG8:
- return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8);
- case V4L2_PIX_FMT_SRGGB8:
- return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8);
- case V4L2_PIX_FMT_SBGGR10:
- return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10);
- case V4L2_PIX_FMT_SGBRG10:
- return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10);
- case V4L2_PIX_FMT_SGRBG10:
- return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10);
- case V4L2_PIX_FMT_SRGGB10:
- return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10);
- case V4L2_PIX_FMT_SBGGR12:
- return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12);
- case V4L2_PIX_FMT_SGBRG12:
- return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12);
- case V4L2_PIX_FMT_SGRBG12:
- return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12);
- case V4L2_PIX_FMT_SRGGB12:
- return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12);
-
- case V4L2_PIX_FMT_YUYV:
- return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8);
- case V4L2_PIX_FMT_YVYU:
- return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8);
- case V4L2_PIX_FMT_UYVY:
- return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8);
- case V4L2_PIX_FMT_VYUY:
- return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8);
-
- case V4L2_PIX_FMT_NV12_16L16:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_YUV422P:
- switch (mbus_code) {
- case MEDIA_BUS_FMT_UYVY8_2X8:
- case MEDIA_BUS_FMT_VYUY8_2X8:
- case MEDIA_BUS_FMT_YUYV8_2X8:
- case MEDIA_BUS_FMT_YVYU8_2X8:
- return true;
- default:
- dev_dbg(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
- mbus_code);
- break;
- }
- break;
-
- case V4L2_PIX_FMT_RGB565:
- return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE);
- case V4L2_PIX_FMT_RGB565X:
- return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE);
-
- case V4L2_PIX_FMT_JPEG:
- return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
-
- default:
- dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n",
- pixformat);
- break;
- }
-
- return false;
-}
-
/* Media */
static const struct media_device_ops sun6i_csi_media_ops = {
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index a4df8f8d2980..e3f9c49bb829 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -49,15 +49,4 @@ struct sun6i_csi_variant {
unsigned long clock_mod_rate;
};
-/**
- * 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_*)
- *
- * 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);
-
#endif /* __SUN6I_CSI_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index 7958419d3c6e..35a30fcbad98 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -1136,63 +1136,81 @@ static const struct v4l2_file_operations sun6i_csi_capture_fops = {
/* Media Entity */
-static int
-sun6i_csi_capture_link_validate_get_format(struct media_pad *pad,
- struct v4l2_subdev_format *fmt)
+static int sun6i_csi_capture_link_validate(struct media_link *link)
{
- if (is_media_entity_v4l2_subdev(pad->entity)) {
- struct v4l2_subdev *sd =
- media_entity_to_v4l2_subdev(pad->entity);
+ 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;
+ const struct sun6i_csi_capture_format *capture_format;
+ const struct sun6i_csi_bridge_format *bridge_format;
+ unsigned int capture_width, capture_height;
+ unsigned int bridge_width, bridge_height;
+ const struct v4l2_format_info *format_info;
+ u32 pixelformat, capture_field;
+ u32 mbus_code, bridge_field;
+ bool match;
- fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt->pad = pad->index;
- return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
- }
+ sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);
- return -EINVAL;
-}
+ sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field);
+ capture_format = sun6i_csi_capture_format_find(pixelformat);
+ if (WARN_ON(!capture_format))
+ return -EINVAL;
-static int sun6i_csi_capture_link_validate(struct media_link *link)
-{
- struct video_device *vdev = container_of(link->sink->entity,
- struct video_device, entity);
- struct sun6i_csi_device *csi_dev = video_get_drvdata(vdev);
- struct sun6i_csi_capture *capture = &csi_dev->capture;
- struct v4l2_subdev_format source_fmt;
- int ret;
+ sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);
- if (!media_pad_remote_pad_first(link->sink->entity->pads)) {
- dev_info(csi_dev->dev, "capture node %s pad not connected\n",
- vdev->name);
- return -ENOLINK;
+ sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);
+ bridge_format = sun6i_csi_bridge_format_find(mbus_code);
+ if (WARN_ON(!bridge_format))
+ return -EINVAL;
+
+ /* No cropping/scaling is supported. */
+ if (capture_width != bridge_width || capture_height != bridge_height) {
+ v4l2_err(v4l2_dev,
+ "invalid input/output dimensions: %ux%u/%ux%u\n",
+ bridge_width, bridge_height, capture_width,
+ capture_height);
+ return -EINVAL;
}
- ret = sun6i_csi_capture_link_validate_get_format(link->source,
- &source_fmt);
- if (ret < 0)
- return ret;
+ format_info = v4l2_format_info(pixelformat);
+ /* Some formats are not listed. */
+ if (!format_info)
+ return 0;
+
+ if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
+ bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
+ goto invalid;
+
+ if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB &&
+ bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)
+ goto invalid;
- if (!sun6i_csi_is_format_supported(csi_dev,
- capture->format.fmt.pix.pixelformat,
- source_fmt.format.code)) {
- dev_err(csi_dev->dev,
- "Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
- capture->format.fmt.pix.pixelformat,
- source_fmt.format.code);
- return -EPIPE;
+ if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
+ if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 &&
+ bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422)
+ goto invalid;
+
+ /* YUV420 input can't produce YUV422 output. */
+ if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 &&
+ format_info->vdiv == 1)
+ goto invalid;
}
- if (source_fmt.format.width != capture->format.fmt.pix.width ||
- source_fmt.format.height != capture->format.fmt.pix.height) {
- dev_err(csi_dev->dev,
- "Wrong width or height %ux%u (%ux%u expected)\n",
- capture->format.fmt.pix.width,
- capture->format.fmt.pix.height,
- source_fmt.format.width, source_fmt.format.height);
- return -EPIPE;
+ /* With raw input mode, we need a 1:1 match between input and output. */
+ if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||
+ capture_format->input_format_raw) {
+ match = sun6i_csi_capture_format_match(pixelformat, mbus_code);
+ if (!match)
+ goto invalid;
}
return 0;
+
+invalid:
+ v4l2_err(v4l2_dev, "invalid input/output format combination\n");
+ return -EINVAL;
}
static const struct media_entity_operations sun6i_csi_capture_media_ops = {
--
2.38.1
The current implementation requires up to 3 buffers to properly
implement page flipping without losing frames: one is configured
before the video stream is started, one just after that and page
flipping is synchronized to the frame done interrupt. The comment in
the code mentions that "CSI will lookup the next dma buffer for next
frame before the current frame done IRQ triggered".
Based on observations of the CSI unit behavior, it seems that the
buffer DMA address is sampled when the frame scan begins (in addition
to starting the stream), which corresponds to the vblank interrupt
that hits just before the frame-done interrupt of the previous frame.
As a result, the address configured at the frame done interrupt is not
actually used for the next frame but for the one after that.
This proposal changes the page flipping sync point to the vsync
interrupt, which allows the DMA address to be sampled for the next
frame instead and allows operating with only two buffers.
In addition to the change in the sync point, the code is refactored
to introduce a notion of state that clarifies tracking of the buffers
with tidy functions.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 4 +
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 3 -
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 259 ++++++++++--------
.../sunxi/sun6i-csi/sun6i_csi_capture.h | 23 +-
4 files changed, 165 insertions(+), 124 deletions(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index cffd664cbc0b..cc277733d7ec 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -566,6 +566,7 @@ void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable)
regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF);
regmap_write(regmap, CSI_CH_INT_EN_REG,
+ CSI_CH_INT_EN_VS_INT_EN |
CSI_CH_INT_EN_HB_OF_INT_EN |
CSI_CH_INT_EN_FIFO2_OF_INT_EN |
CSI_CH_INT_EN_FIFO1_OF_INT_EN |
@@ -664,6 +665,9 @@ static irqreturn_t sun6i_csi_interrupt(int irq, void *private)
if (status & CSI_CH_INT_STA_FD_PD)
sun6i_csi_capture_frame_done(csi_dev);
+ if (status & CSI_CH_INT_STA_VS_PD)
+ sun6i_csi_capture_sync(csi_dev);
+
regmap_write(regmap, CSI_CH_INT_STA_REG, status);
return IRQ_HANDLED;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 48acf8078f15..6c0110e4b3ab 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -25,9 +25,6 @@ enum sun6i_csi_port {
struct sun6i_csi_buffer {
struct vb2_v4l2_buffer v4l2_buffer;
struct list_head list;
-
- dma_addr_t dma_addr;
- bool queued_to_csi;
};
/**
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index 9838a5a43c2d..bdc5465b68a2 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -90,8 +90,13 @@ static void
sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,
struct sun6i_csi_buffer *csi_buffer)
{
- csi_buffer->queued_to_csi = true;
- sun6i_csi_update_buf_addr(csi_dev, csi_buffer->dma_addr);
+ struct vb2_buffer *vb2_buffer;
+ dma_addr_t address;
+
+ vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
+ address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);
+
+ sun6i_csi_update_buf_addr(csi_dev, address);
}
static void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)
@@ -108,6 +113,119 @@ static void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)
sun6i_csi_update_config(csi_dev, &config);
}
+/* State */
+
+static void sun6i_csi_capture_state_cleanup(struct sun6i_csi_device *csi_dev,
+ bool error)
+{
+ struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
+ struct sun6i_csi_buffer **csi_buffer_states[] = {
+ &state->pending, &state->current, &state->complete,
+ };
+ struct sun6i_csi_buffer *csi_buffer;
+ struct vb2_buffer *vb2_buffer;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ for (i = 0; i < ARRAY_SIZE(csi_buffer_states); i++) {
+ csi_buffer = *csi_buffer_states[i];
+ if (!csi_buffer)
+ continue;
+
+ vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
+ vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+
+ *csi_buffer_states[i] = NULL;
+ }
+
+ list_for_each_entry(csi_buffer, &state->queue, list) {
+ vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
+ vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+ }
+
+ INIT_LIST_HEAD(&state->queue);
+
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev)
+{
+ struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
+ struct sun6i_csi_buffer *csi_buffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (list_empty(&state->queue))
+ goto complete;
+
+ if (state->pending)
+ goto complete;
+
+ csi_buffer = list_first_entry(&state->queue, struct sun6i_csi_buffer,
+ list);
+
+ sun6i_csi_capture_buffer_configure(csi_dev, csi_buffer);
+
+ list_del(&csi_buffer->list);
+
+ state->pending = csi_buffer;
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static void sun6i_csi_capture_state_complete(struct sun6i_csi_device *csi_dev)
+{
+ struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (!state->pending)
+ goto complete;
+
+ state->complete = state->current;
+ state->current = state->pending;
+ state->pending = NULL;
+
+ if (state->complete) {
+ struct sun6i_csi_buffer *csi_buffer = state->complete;
+ struct vb2_buffer *vb2_buffer =
+ &csi_buffer->v4l2_buffer.vb2_buf;
+
+ vb2_buffer->timestamp = ktime_get_ns();
+ csi_buffer->v4l2_buffer.sequence = state->sequence;
+
+ vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);
+
+ state->complete = NULL;
+ }
+
+complete:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)
+{
+ struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+ state->sequence++;
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev)
+{
+ sun6i_csi_capture_state_complete(csi_dev);
+ sun6i_csi_capture_state_update(csi_dev);
+}
+
/* Queue */
static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,
@@ -117,8 +235,7 @@ static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,
struct device *alloc_devs[])
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
- struct sun6i_csi_capture *capture = &csi_dev->capture;
- unsigned int size = capture->format.fmt.pix.sizeimage;
+ unsigned int size = csi_dev->capture.format.fmt.pix.sizeimage;
if (*planes_count)
return sizes[0] < size ? -EINVAL : 0;
@@ -135,8 +252,6 @@ static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
struct sun6i_csi_capture *capture = &csi_dev->capture;
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
- struct sun6i_csi_buffer *csi_buffer =
- container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
unsigned long size = capture->format.fmt.pix.sizeimage;
if (vb2_plane_size(buffer, 0) < size) {
@@ -147,7 +262,6 @@ static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
vb2_set_plane_payload(buffer, 0, size);
- csi_buffer->dma_addr = vb2_dma_contig_plane_dma_addr(buffer, 0);
v4l2_buffer->field = capture->format.fmt.pix.field;
return 0;
@@ -156,16 +270,15 @@ static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)
static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer)
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
- struct sun6i_csi_capture *capture = &csi_dev->capture;
+ struct sun6i_csi_capture_state *state = &csi_dev->capture.state;
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
struct sun6i_csi_buffer *csi_buffer =
container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
unsigned long flags;
- spin_lock_irqsave(&capture->dma_queue_lock, flags);
- csi_buffer->queued_to_csi = false;
- list_add_tail(&csi_buffer->list, &capture->dma_queue);
- spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
+ spin_lock_irqsave(&state->lock, flags);
+ list_add_tail(&csi_buffer->list, &state->queue);
+ spin_unlock_irqrestore(&state->lock, flags);
}
static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
@@ -173,18 +286,16 @@ static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
{
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
struct sun6i_csi_capture *capture = &csi_dev->capture;
+ struct sun6i_csi_capture_state *state = &capture->state;
struct video_device *video_dev = &capture->video_dev;
- struct sun6i_csi_buffer *buf;
- struct sun6i_csi_buffer *next_buf;
struct v4l2_subdev *subdev;
- unsigned long flags;
int ret;
- capture->sequence = 0;
+ state->sequence = 0;
ret = video_device_pipeline_alloc_start(video_dev);
if (ret < 0)
- goto error_dma_queue_flush;
+ goto error_state;
if (capture->mbus_code == 0) {
ret = -EINVAL;
@@ -197,37 +308,17 @@ static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
goto error_media_pipeline;
}
+ /* Configure */
+
sun6i_csi_capture_configure(csi_dev);
- spin_lock_irqsave(&capture->dma_queue_lock, flags);
+ /* State Update */
- buf = list_first_entry(&capture->dma_queue,
- struct sun6i_csi_buffer, list);
- sun6i_csi_capture_buffer_configure(csi_dev, buf);
+ sun6i_csi_capture_state_update(csi_dev);
- sun6i_csi_set_stream(csi_dev, true);
+ /* Enable */
- /*
- * CSI will lookup the next dma buffer for next frame before the
- * current frame done IRQ triggered. This is not documented
- * but reported by Ondřej Jirman.
- * The BSP code has workaround for this too. It skip to mark the
- * first buffer as frame done for VB2 and pass the second buffer
- * to CSI in the first frame done ISR call. Then in second frame
- * done ISR call, it mark the first buffer as frame done for VB2
- * and pass the third buffer to CSI. And so on. The bad thing is
- * that the first buffer will be written twice and the first frame
- * is dropped even the queued buffer is sufficient.
- * So, I make some improvement here. Pass the next buffer to CSI
- * just follow starting the CSI. In this case, the first frame
- * will be stored in first buffer, second frame in second buffer.
- * This method is used to avoid dropping the first frame, it
- * would also drop frame when lacking of queued buffer.
- */
- next_buf = list_next_entry(buf, list);
- sun6i_csi_capture_buffer_configure(csi_dev, next_buf);
-
- spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
+ sun6i_csi_set_stream(csi_dev, true);
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD)
@@ -241,13 +332,8 @@ static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,
error_media_pipeline:
video_device_pipeline_stop(video_dev);
-error_dma_queue_flush:
- spin_lock_irqsave(&capture->dma_queue_lock, flags);
- list_for_each_entry(buf, &capture->dma_queue, list)
- vb2_buffer_done(&buf->v4l2_buffer.vb2_buf,
- VB2_BUF_STATE_QUEUED);
- INIT_LIST_HEAD(&capture->dma_queue);
- spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
+error_state:
+ sun6i_csi_capture_state_cleanup(csi_dev, false);
return ret;
}
@@ -257,8 +343,6 @@ static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
struct sun6i_csi_capture *capture = &csi_dev->capture;
struct v4l2_subdev *subdev;
- unsigned long flags;
- struct sun6i_csi_buffer *buf;
subdev = sun6i_csi_capture_remote_subdev(capture, NULL);
if (subdev)
@@ -268,59 +352,7 @@ static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)
video_device_pipeline_stop(&capture->video_dev);
- /* Release all active buffers */
- spin_lock_irqsave(&capture->dma_queue_lock, flags);
- list_for_each_entry(buf, &capture->dma_queue, list)
- vb2_buffer_done(&buf->v4l2_buffer.vb2_buf, VB2_BUF_STATE_ERROR);
- INIT_LIST_HEAD(&capture->dma_queue);
- spin_unlock_irqrestore(&capture->dma_queue_lock, flags);
-}
-
-void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)
-{
- struct sun6i_csi_capture *capture = &csi_dev->capture;
- struct sun6i_csi_buffer *buf;
- struct sun6i_csi_buffer *next_buf;
- struct vb2_v4l2_buffer *v4l2_buffer;
-
- spin_lock(&capture->dma_queue_lock);
-
- buf = list_first_entry(&capture->dma_queue,
- struct sun6i_csi_buffer, list);
- if (list_is_last(&buf->list, &capture->dma_queue)) {
- dev_dbg(csi_dev->dev, "Frame dropped!\n");
- goto complete;
- }
-
- next_buf = list_next_entry(buf, list);
- /* If a new buffer (#next_buf) had not been queued to CSI, the old
- * buffer (#buf) is still holding by CSI for storing the next
- * frame. So, we queue a new buffer (#next_buf) to CSI then wait
- * for next ISR call.
- */
- if (!next_buf->queued_to_csi) {
- sun6i_csi_capture_buffer_configure(csi_dev, next_buf);
- dev_dbg(csi_dev->dev, "Frame dropped!\n");
- goto complete;
- }
-
- list_del(&buf->list);
- v4l2_buffer = &buf->v4l2_buffer;
- v4l2_buffer->vb2_buf.timestamp = ktime_get_ns();
- v4l2_buffer->sequence = capture->sequence;
- vb2_buffer_done(&v4l2_buffer->vb2_buf, VB2_BUF_STATE_DONE);
-
- /* Prepare buffer for next frame but one. */
- if (!list_is_last(&next_buf->list, &capture->dma_queue)) {
- next_buf = list_next_entry(next_buf, list);
- sun6i_csi_capture_buffer_configure(csi_dev, next_buf);
- } else {
- dev_dbg(csi_dev->dev, "Next frame will be dropped!\n");
- }
-
-complete:
- capture->sequence++;
- spin_unlock(&capture->dma_queue_lock);
+ sun6i_csi_capture_state_cleanup(csi_dev, true);
}
static const struct vb2_ops sun6i_csi_capture_queue_ops = {
@@ -635,6 +667,7 @@ static const struct media_entity_operations sun6i_csi_capture_media_ops = {
int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
{
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_subdev *bridge_subdev = &csi_dev->bridge.subdev;
struct video_device *video_dev = &capture->video_dev;
@@ -644,6 +677,11 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
struct v4l2_pix_format *pix_format = &format.fmt.pix;
int ret;
+ /* State */
+
+ INIT_LIST_HEAD(&state->queue);
+ spin_lock_init(&state->lock);
+
/* Media Entity */
video_dev->entity.ops = &sun6i_csi_capture_media_ops;
@@ -656,13 +694,6 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
if (ret < 0)
return ret;
- /* DMA queue */
-
- INIT_LIST_HEAD(&capture->dma_queue);
- spin_lock_init(&capture->dma_queue_lock);
-
- capture->sequence = 0;
-
/* Queue */
mutex_init(&capture->lock);
@@ -672,14 +703,12 @@ int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)
queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);
queue->ops = &sun6i_csi_capture_queue_ops;
queue->mem_ops = &vb2_dma_contig_memops;
+ queue->min_buffers_needed = 2;
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
queue->lock = &capture->lock;
queue->dev = csi_dev->dev;
queue->drv_priv = csi_dev;
- /* Make sure non-dropped frame. */
- queue->min_buffers_needed = 3;
-
ret = vb2_queue_init(queue);
if (ret) {
v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
index 36bba31fcb48..7fa66a2af5ec 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h
@@ -13,23 +13,34 @@
struct sun6i_csi_device;
+#undef current
+struct sun6i_csi_capture_state {
+ struct list_head queue;
+ spinlock_t lock; /* Queue and buffers lock. */
+
+ struct sun6i_csi_buffer *pending;
+ struct sun6i_csi_buffer *current;
+ struct sun6i_csi_buffer *complete;
+
+ unsigned int sequence;
+};
+
struct sun6i_csi_capture {
+ struct sun6i_csi_capture_state state;
+
struct video_device video_dev;
struct vb2_queue queue;
struct mutex lock; /* Queue lock. */
struct media_pad pad;
- struct list_head dma_queue;
- spinlock_t dma_queue_lock; /* DMA queue lock. */
-
struct v4l2_format format;
u32 mbus_code;
- unsigned int sequence;
};
+void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev);
+void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev);
+
int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev);
void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev);
-void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev);
-
#endif /* __SUN6I_CAPTURE_H__ */
--
2.38.1
Instead of calculating the planar_offset at one point and using it
later in a dedicated function, reimplement address configuration
with v4l2 format info in the buffer_configure function.
Signed-off-by: Paul Kocialkowski <[email protected]>
Acked-by: Jernej Skrabec <[email protected]>
---
.../platform/sunxi/sun6i-csi/sun6i_csi.c | 27 ----------------
.../platform/sunxi/sun6i-csi/sun6i_csi.h | 10 ------
.../sunxi/sun6i-csi/sun6i_csi_capture.c | 32 ++++++++++++++++++-
3 files changed, 31 insertions(+), 38 deletions(-)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 79d4b00d1fcd..5e03069bd4c3 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -463,7 +463,6 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
struct sun6i_csi_config *config = &csi_dev->config;
u32 bytesperline_y;
u32 bytesperline_c;
- int *planar_offset = csi_dev->planar_offset;
u32 width = config->width;
u32 height = config->height;
u32 hor_len = width;
@@ -488,7 +487,6 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
SUN6I_CSI_CH_VSIZE_LEN(height) |
SUN6I_CSI_CH_VSIZE_START(0));
- planar_offset[0] = 0;
switch (config->pixelformat) {
case V4L2_PIX_FMT_NV12_16L16:
case V4L2_PIX_FMT_NV12:
@@ -497,23 +495,15 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
case V4L2_PIX_FMT_NV61:
bytesperline_y = width;
bytesperline_c = width;
- planar_offset[1] = bytesperline_y * height;
- planar_offset[2] = -1;
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
bytesperline_y = width;
bytesperline_c = width / 2;
- planar_offset[1] = bytesperline_y * height;
- planar_offset[2] = planar_offset[1] +
- bytesperline_c * height / 2;
break;
case V4L2_PIX_FMT_YUV422P:
bytesperline_y = width;
bytesperline_c = width / 2;
- planar_offset[1] = bytesperline_y * height;
- planar_offset[2] = planar_offset[1] +
- bytesperline_c * height;
break;
default: /* raw */
dev_dbg(csi_dev->dev,
@@ -522,8 +512,6 @@ static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
config->width) / 8;
bytesperline_c = 0;
- planar_offset[1] = -1;
- planar_offset[2] = -1;
break;
}
@@ -547,21 +535,6 @@ int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
return 0;
}
-void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev,
- dma_addr_t addr)
-{
- regmap_write(csi_dev->regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,
- SUN6I_CSI_ADDR_VALUE(addr + csi_dev->planar_offset[0]));
- if (csi_dev->planar_offset[1] != -1)
- regmap_write(csi_dev->regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,
- SUN6I_CSI_ADDR_VALUE(addr +
- csi_dev->planar_offset[1]));
- if (csi_dev->planar_offset[2] != -1)
- regmap_write(csi_dev->regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,
- SUN6I_CSI_ADDR_VALUE(addr +
- csi_dev->planar_offset[2]));
-}
-
void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable)
{
struct regmap *regmap = csi_dev->regmap;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 6c0110e4b3ab..75867cde7985 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -60,8 +60,6 @@ struct sun6i_csi_device {
struct clk *clock_mod;
struct clk *clock_ram;
struct reset_control *reset;
-
- int planar_offset[3];
};
struct sun6i_csi_variant {
@@ -98,14 +96,6 @@ int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable);
int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
struct sun6i_csi_config *config);
-/**
- * sun6i_csi_update_buf_addr() - update the csi frame buffer address
- * @csi_dev: pointer to the csi device
- * @addr: frame buffer's physical address
- */
-void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev,
- dma_addr_t addr);
-
/**
* sun6i_csi_set_stream() - start/stop csi streaming
* @csi_dev: pointer to the csi device
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index a3c3bbf6a506..cb23dcdf250f 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -6,6 +6,7 @@
*/
#include <linux/of.h>
+#include <linux/regmap.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -16,6 +17,7 @@
#include "sun6i_csi.h"
#include "sun6i_csi_capture.h"
+#include "sun6i_csi_reg.h"
/* This is got from BSP sources. */
#define MIN_WIDTH (32)
@@ -109,13 +111,41 @@ static void
sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,
struct sun6i_csi_buffer *csi_buffer)
{
+ struct regmap *regmap = csi_dev->regmap;
+ const struct v4l2_format_info *info;
struct vb2_buffer *vb2_buffer;
+ unsigned int width, height;
dma_addr_t address;
+ u32 pixelformat;
vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;
address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);
- sun6i_csi_update_buf_addr(csi_dev, address);
+ regmap_write(regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,
+ SUN6I_CSI_ADDR_VALUE(address));
+
+ sun6i_csi_capture_dimensions(csi_dev, &width, &height);
+ sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);
+
+ info = v4l2_format_info(pixelformat);
+ /* Unsupported formats are single-plane, so we can stop here. */
+ if (!info)
+ return;
+
+ if (info->comp_planes > 1) {
+ address += info->bpp[0] * width * height;
+
+ regmap_write(regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,
+ SUN6I_CSI_ADDR_VALUE(address));
+ }
+
+ if (info->comp_planes > 2) {
+ address += info->bpp[1] * DIV_ROUND_UP(width, info->hdiv) *
+ DIV_ROUND_UP(height, info->vdiv);
+
+ regmap_write(regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,
+ SUN6I_CSI_ADDR_VALUE(address));
+ }
}
static void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)
--
2.38.1
Hi Paul,
I love your patch! Perhaps something to improve:
[auto build test WARNING on media-tree/master]
[also build test WARNING on soc/for-next linus/master v6.1-rc4 next-20221109]
[cannot apply to sailus-media-tree/streams]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Paul-Kocialkowski/Allwinner-A31-A83T-MIPI-CSI-2-and-A31-ISP-CSI-Rework/20221104-003518
base: git://linuxtv.org/media_tree.git master
patch link: https://lore.kernel.org/r/20221103163113.245462-2-paul.kocialkowski%40bootlin.com
patch subject: [PATCH v7 01/28] media: sun6i-csi: Add bridge v4l2 subdev with port management
config: arm64-allyesconfig
compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 463da45892e2d2a262277b91b96f5f8c05dc25d0)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# https://github.com/intel-lab-lkp/linux/commit/d26de0b15ad9da72e89a53abb74503a11cc6a1b9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Paul-Kocialkowski/Allwinner-A31-A83T-MIPI-CSI-2-and-A31-ISP-CSI-Rework/20221104-003518
git checkout d26de0b15ad9da72e89a53abb74503a11cc6a1b9
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash drivers/gpio/ drivers/gpu/drm/ drivers/media/platform/sunxi/sun6i-csi/ drivers/thermal/intel/ drivers/usb/gadget/udc/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All warnings (new ones prefixed by >>):
>> drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c:272:2: warning: variable 'enabled' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
default:
^~~~~~~
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c:279:25: note: uninitialized use occurs here
remote_subdev, enabled);
^~~~~~~
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c:266:14: note: initialize the variable 'enabled' to silence this warning
bool enabled;
^
= 0
1 warning generated.
vim +/enabled +272 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
253
254 static int
255 sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
256 struct v4l2_subdev *remote_subdev,
257 struct v4l2_async_subdev *async_subdev)
258 {
259 struct sun6i_csi_device *csi_dev =
260 container_of(notifier, struct sun6i_csi_device,
261 bridge.notifier);
262 struct sun6i_csi_bridge_async_subdev *bridge_async_subdev =
263 container_of(async_subdev, struct sun6i_csi_bridge_async_subdev,
264 async_subdev);
265 struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
266 bool enabled;
267
268 switch (source->endpoint.base.port) {
269 case SUN6I_CSI_PORT_PARALLEL:
270 enabled = true;
271 break;
> 272 default:
273 break;
274 }
275
276 source->subdev = remote_subdev;
277
278 return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK,
279 remote_subdev, enabled);
280 }
281
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Paul,
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Paul-Kocialkowski/Allwinner-A31-A83T-MIPI-CSI-2-and-A31-ISP-CSI-Rework/20221104-003518
base: git://linuxtv.org/media_tree.git master
patch link: https://lore.kernel.org/r/20221103163113.245462-2-paul.kocialkowski%40bootlin.com
patch subject: [PATCH v7 01/28] media: sun6i-csi: Add bridge v4l2 subdev with port management
config: arc-randconfig-m041-20221107
compiler: arc-elf-gcc (GCC) 12.1.0
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Reported-by: Dan Carpenter <[email protected]>
smatch warnings:
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c:86 sun6i_csi_bridge_s_stream() warn: missing error code 'ret'
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c:279 sun6i_csi_bridge_notifier_bound() error: uninitialized symbol 'enabled'.
vim +/ret +86 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 62 static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 63 {
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 64 struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 65 struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 66 struct media_pad *local_pad = &bridge->pads[SUN6I_CSI_BRIDGE_PAD_SINK];
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 67 struct device *dev = csi_dev->dev;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 68 struct v4l2_subdev *source_subdev;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 69 struct media_pad *remote_pad;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 70 /* Initialize to 0 to use both in disable label (ret != 0) and off. */
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 71 int ret = 0;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 72
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 73 /* Source */
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 74
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 75 remote_pad = media_pad_remote_pad_unique(local_pad);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 76 if (IS_ERR(remote_pad)) {
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 77 dev_err(dev,
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 78 "zero or more than a single source connected to the bridge\n");
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 79 return PTR_ERR(remote_pad);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 80 }
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 81
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 82 source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 83
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 84 if (!on) {
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 85 v4l2_subdev_call(source_subdev, video, s_stream, 0);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 @86 goto disable;
This is intentional, but it should just return 0;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 87 }
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 88
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 89 ret = v4l2_subdev_call(source_subdev, video, s_stream, 1);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 90 if (ret && ret != -ENOIOCTLCMD)
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 91 goto disable;
Do we really need to clean anything up? Why not just:
source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity);
return v4l2_subdev_call(source_subdev, video, s_stream, !!on);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 92
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 93 return 0;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 94
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 95 disable:
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 96
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 97 return ret;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 98 }
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 99
[ snip ]
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 254 static int
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 255 sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier,
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 256 struct v4l2_subdev *remote_subdev,
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 257 struct v4l2_async_subdev *async_subdev)
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 258 {
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 259 struct sun6i_csi_device *csi_dev =
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 260 container_of(notifier, struct sun6i_csi_device,
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 261 bridge.notifier);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 262 struct sun6i_csi_bridge_async_subdev *bridge_async_subdev =
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 263 container_of(async_subdev, struct sun6i_csi_bridge_async_subdev,
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 264 async_subdev);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 265 struct sun6i_csi_bridge_source *source = bridge_async_subdev->source;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 266 bool enabled;
This neesds to be "bool enabled = false;"
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 267
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 268 switch (source->endpoint.base.port) {
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 269 case SUN6I_CSI_PORT_PARALLEL:
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 270 enabled = true;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 271 break;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 272 default:
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 273 break;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 274 }
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 275
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 276 source->subdev = remote_subdev;
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 277
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 278 return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK,
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 @279 remote_subdev, enabled);
d26de0b15ad9da7 Paul Kocialkowski 2022-11-03 280 }
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Paul,
On Thu, Nov 03, 2022 at 05:30:47PM +0100, Paul Kocialkowski wrote:
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
> similarity index 58%
> rename from drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
> rename to drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
> index fa83211a2c5a..9838a5a43c2d 100644
> --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
> @@ -15,7 +15,7 @@
> #include <media/videobuf2-v4l2.h>
>
> #include "sun6i_csi.h"
> -#include "sun6i_video.h"
> +#include "sun6i_csi_capture.h"
>
> /* This is got from BSP sources. */
> #define MIN_WIDTH (32)
> @@ -26,7 +26,7 @@
> /* Helpers */
>
> static struct v4l2_subdev *
> -sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
> +sun6i_csi_capture_remote_subdev(struct sun6i_csi_capture *capture, u32 *pad)
> {
> struct media_pad *remote;
>
"video" was still being used a few lines below this, fixed that while
applying. The PR has been sent already. Just FYI.
--
Kind regards,
Sakari Ailus