aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-01-12 14:29:48 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2024-01-12 14:29:48 -0800
commit61da593f4458f25c59f65cfd9ba1bda570db5db7 (patch)
tree42196179375dfda07901577e080f979b2897d308 /drivers/staging
parenta3cc31e75185f9b1ad8dc45eac77f8de788dc410 (diff)
parent60a031b64984ad4a219a13b0fe912746b586bb9b (diff)
downloadlinux-61da593f4458f25c59f65cfd9ba1bda570db5db7.tar.gz
Merge tag 'media/v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - v4l core: subdev frame interval now supports which field - v4l kapi: moves and renames the init_cfg pad op to init_state as an internal op. - new sensor drivers: gc0308, gc2145, Avnet Alvium, ov64a40, tw9900 - new camera driver: STM32 DCMIPP - s5p-mfc has gained MFC v12 support - new ISP driver added to staging: Starfive - new stateful encoder/decoded: Wave5 codec It is found on the J721S2 SoC, JH7100 SoC, ssd202d SoC. Etc. - fwnode gained support for MIPI "DisCo for Imaging" (https://www.mipi.org/specifications/mipi-disco-imaging) - as usual, lots of cleanups, fixups and driver improvements. * tag 'media/v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (309 commits) media: i2c: thp7312: select CONFIG_FW_LOADER media: i2c: mt9m114: use fsleep() in place of udelay() media: videobuf2: core: Rename min_buffers_needed field in vb2_queue media: i2c: thp7312: Store frame interval in subdev state media: docs: uAPI: Fix documentation of 'which' field for routing ioctls media: docs: uAPI: Expand error documentation for invalid 'which' value media: docs: uAPI: Clarify error documentation for invalid 'which' value media: v4l2-subdev: Store frame interval in subdev state media: v4l2-subdev: Add which field to struct v4l2_subdev_frame_interval media: v4l2-subdev: Turn .[gs]_frame_interval into pad operations media: v4l: subdev: Move out subdev state lock macros outside CONFIG_MEDIA_CONTROLLER media: s5p-mfc: DPB Count Independent of VIDIOC_REQBUF media: s5p-mfc: Load firmware for each run in MFCv12. media: s5p-mfc: Set context for valid case before calling try_run media: s5p-mfc: Add support for DMABUF for encoder media: s5p-mfc: Add support for UHD encoding. media: s5p-mfc: Add support for rate controls in MFCv12 media: s5p-mfc: Add YV12 and I420 multiplanar format support media: s5p-mfc: Add initial support for MFCv12 media: s5p-mfc: Rename IS_MFCV10 macro ...
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc0310.c16
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c16
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c16
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2722.c16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2.c3
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.c8
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.c6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_tpg.c2
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-base.c12
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c34
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c34
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c15
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c38
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c10
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c32
-rw-r--r--drivers/staging/media/imx/imx-media.h4
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c4
-rw-r--r--drivers/staging/media/ipu3/ipu3-v4l2.c16
-rw-r--r--drivers/staging/media/meson/vdec/vdec.c19
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c9
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.c11
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.c11
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.c11
-rw-r--r--drivers/staging/media/rkvdec/Kconfig1
-rw-r--r--drivers/staging/media/rkvdec/rkvdec.c3
-rw-r--r--drivers/staging/media/starfive/Kconfig5
-rw-r--r--drivers/staging/media/starfive/Makefile2
-rw-r--r--drivers/staging/media/starfive/camss/Kconfig18
-rw-r--r--drivers/staging/media/starfive/camss/Makefile13
-rw-r--r--drivers/staging/media/starfive/camss/TODO.txt4
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.c436
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.h134
-rw-r--r--drivers/staging/media/starfive/camss/stf-capture.c603
-rw-r--r--drivers/staging/media/starfive/camss/stf-capture.h86
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp-hw-ops.c445
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.c385
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.h428
-rw-r--r--drivers/staging/media/starfive/camss/stf-video.c572
-rw-r--r--drivers/staging/media/starfive/camss/stf-video.h100
-rw-r--r--drivers/staging/media/sunxi/cedrus/Kconfig1
-rw-r--r--drivers/staging/media/sunxi/cedrus/TODO23
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h264.c9
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h265.c9
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c2
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c2
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c18
-rw-r--r--drivers/staging/media/tegra-video/csi.c22
-rw-r--r--drivers/staging/media/tegra-video/vi.c22
-rw-r--r--drivers/staging/media/tegra-video/vip.c6
52 files changed, 3526 insertions, 173 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index bc6c7b248f862..554c2e475ce3b 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -36,6 +36,8 @@ source "drivers/staging/media/omap4iss/Kconfig"
source "drivers/staging/media/rkvdec/Kconfig"
+source "drivers/staging/media/starfive/Kconfig"
+
source "drivers/staging/media/sunxi/Kconfig"
source "drivers/staging/media/tegra-video/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 1a4c3a062e3dc..dcaeeca0ee6d7 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_VIDEO_MAX96712) += max96712/
obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/
+obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
index 58cddf11c9ab6..5bcd634a2a44d 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
@@ -361,7 +361,7 @@ gc0310_get_pad_format(struct gc0310_device *dev,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&dev->sd, state, pad);
+ return v4l2_subdev_state_get_format(state, pad);
return &dev->mode.fmt;
}
@@ -496,9 +496,17 @@ error_power_down:
return ret;
}
-static int gc0310_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
+static int gc0310_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *interval)
{
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
interval->interval.numerator = 1;
interval->interval.denominator = GC0310_FPS;
@@ -545,7 +553,6 @@ static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = {
static const struct v4l2_subdev_video_ops gc0310_video_ops = {
.s_stream = gc0310_s_stream,
- .g_frame_interval = gc0310_g_frame_interval,
};
static const struct v4l2_subdev_pad_ops gc0310_pad_ops = {
@@ -553,6 +560,7 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = {
.enum_frame_size = gc0310_enum_frame_size,
.get_fmt = gc0310_get_fmt,
.set_fmt = gc0310_set_fmt,
+ .get_frame_interval = gc0310_get_frame_interval,
};
static const struct v4l2_subdev_ops gc0310_ops = {
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
index 9fa390fbc5f31..bec4c56158640 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
@@ -561,7 +561,7 @@ static int gc2235_set_fmt(struct v4l2_subdev *sd,
fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- sd_state->pads->try_fmt = *fmt;
+ *v4l2_subdev_state_get_format(sd_state, 0) = *fmt;
mutex_unlock(&dev->input_lock);
return 0;
}
@@ -698,11 +698,19 @@ fail_power_off:
return ret;
}
-static int gc2235_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
+static int gc2235_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *interval)
{
struct gc2235_device *dev = to_gc2235_sensor(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
interval->interval.numerator = 1;
interval->interval.denominator = dev->res->fps;
@@ -754,7 +762,6 @@ static const struct v4l2_subdev_sensor_ops gc2235_sensor_ops = {
static const struct v4l2_subdev_video_ops gc2235_video_ops = {
.s_stream = gc2235_s_stream,
- .g_frame_interval = gc2235_g_frame_interval,
};
static const struct v4l2_subdev_core_ops gc2235_core_ops = {
@@ -767,6 +774,7 @@ static const struct v4l2_subdev_pad_ops gc2235_pad_ops = {
.enum_frame_size = gc2235_enum_frame_size,
.get_fmt = gc2235_get_fmt,
.set_fmt = gc2235_set_fmt,
+ .get_frame_interval = gc2235_get_frame_interval,
};
static const struct v4l2_subdev_ops gc2235_ops = {
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
index 1c6643c442ef3..20f02d18a8de1 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
@@ -666,7 +666,7 @@ static int mt9m114_set_fmt(struct v4l2_subdev *sd,
fmt->height = res->height;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- sd_state->pads->try_fmt = *fmt;
+ *v4l2_subdev_state_get_format(sd_state, 0) = *fmt;
return 0;
}
@@ -1388,11 +1388,19 @@ static int mt9m114_t_vflip(struct v4l2_subdev *sd, int value)
return !!err;
}
-static int mt9m114_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
+static int mt9m114_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *interval)
{
struct mt9m114_device *dev = to_mt9m114_sensor(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
interval->interval.numerator = 1;
interval->interval.denominator = mt9m114_res[dev->res].fps;
@@ -1479,7 +1487,6 @@ static int mt9m114_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
static const struct v4l2_subdev_video_ops mt9m114_video_ops = {
.s_stream = mt9m114_s_stream,
- .g_frame_interval = mt9m114_g_frame_interval,
};
static const struct v4l2_subdev_sensor_ops mt9m114_sensor_ops = {
@@ -1498,6 +1505,7 @@ static const struct v4l2_subdev_pad_ops mt9m114_pad_ops = {
.get_fmt = mt9m114_get_fmt,
.set_fmt = mt9m114_set_fmt,
.set_selection = mt9m114_s_exposure_selection,
+ .get_frame_interval = mt9m114_get_frame_interval,
};
static const struct v4l2_subdev_ops mt9m114_ops = {
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
index 6a72691ed5b74..133e346ae51b0 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
@@ -671,7 +671,7 @@ static int ov2722_set_fmt(struct v4l2_subdev *sd,
fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- sd_state->pads->try_fmt = *fmt;
+ *v4l2_subdev_state_get_format(sd_state, 0) = *fmt;
return 0;
}
@@ -845,11 +845,19 @@ fail_power_off:
return ret;
}
-static int ov2722_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
+static int ov2722_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *interval)
{
struct ov2722_device *dev = to_ov2722_sensor(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
interval->interval.numerator = 1;
interval->interval.denominator = dev->res->fps;
@@ -901,7 +909,6 @@ static const struct v4l2_subdev_sensor_ops ov2722_sensor_ops = {
static const struct v4l2_subdev_video_ops ov2722_video_ops = {
.s_stream = ov2722_s_stream,
- .g_frame_interval = ov2722_g_frame_interval,
};
static const struct v4l2_subdev_core_ops ov2722_core_ops = {
@@ -914,6 +921,7 @@ static const struct v4l2_subdev_pad_ops ov2722_pad_ops = {
.enum_frame_size = ov2722_enum_frame_size,
.get_fmt = ov2722_get_fmt,
.set_fmt = ov2722_set_fmt,
+ .get_frame_interval = ov2722_get_frame_interval,
};
static const struct v4l2_subdev_ops ov2722_ops = {
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 759233a7ba505..f44e6412f4e31 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -105,8 +105,8 @@ static unsigned short atomisp_get_sensor_fps(struct atomisp_sub_device *asd)
unsigned short fps = 0;
int ret;
- ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- video, g_frame_interval, &fi);
+ ret = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].camera,
+ pad, get_frame_interval, &fi);
if (!ret && fi.interval.numerator)
fps = fi.interval.denominator / fi.interval.numerator;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
index abf55a86f7957..89118438a3b67 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
@@ -29,8 +29,7 @@ v4l2_mbus_framefmt *__csi2_get_format(struct atomisp_mipi_csi2_device *csi2,
unsigned int pad)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&csi2->subdev, sd_state,
- pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
else
return &csi2->formats[pad];
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index a8e4779d007f4..01b7fa9b56a21 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -1028,7 +1028,7 @@ static int atomisp_qbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer
struct atomisp_device *isp = video_get_drvdata(vdev);
struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
- if (buf->index >= vdev->queue->num_buffers)
+ if (buf->index >= vb2_get_num_buffers(vdev->queue))
return -EINVAL;
if (buf->reserved2 & ATOMISP_BUFFER_HAS_PER_FRAME_SETTING) {
@@ -1059,7 +1059,7 @@ static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer
if (ret)
return ret;
- vb = pipe->vb_queue.bufs[buf->index];
+ vb = vb2_get_buffer(&pipe->vb_queue, buf->index);
frame = vb_to_frame(vb);
buf->reserved = asd->frame_status[buf->index];
@@ -1739,8 +1739,8 @@ static int atomisp_s_parm(struct file *file, void *fh,
fi.interval = parm->parm.capture.timeperframe;
- rval = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- video, s_frame_interval, &fi);
+ rval = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].camera,
+ pad, set_frame_interval, &fi);
if (!rval)
parm->parm.capture.timeperframe = fi.interval;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
index 471912dea5cd8..a87fc74159e20 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
@@ -240,9 +240,9 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd,
if (which == V4L2_SUBDEV_FORMAT_TRY) {
switch (target) {
case V4L2_SEL_TGT_CROP:
- return v4l2_subdev_get_try_crop(sd, sd_state, pad);
+ return v4l2_subdev_state_get_crop(sd_state, pad);
case V4L2_SEL_TGT_COMPOSE:
- return v4l2_subdev_get_try_compose(sd, sd_state, pad);
+ return v4l2_subdev_state_get_compose(sd_state, pad);
}
}
@@ -264,7 +264,7 @@ struct v4l2_mbus_framefmt
struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(sd, sd_state, pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
return &isp_sd->fmt[pad].fmt;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp_tpg.c
index 074826a5b7064..92e61ee909934 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_tpg.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_tpg.c
@@ -47,7 +47,7 @@ static int tpg_set_fmt(struct v4l2_subdev *sd,
/* only raw8 grbg is supported by TPG */
fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- sd_state->pads->try_fmt = *fmt;
+ *v4l2_subdev_state_get_format(sd_state, 0) = *fmt;
return 0;
}
return 0;
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
index 8e26663cecb63..305b103153d7e 100644
--- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
+++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
@@ -820,6 +820,8 @@ static int isc_try_configure_pipeline(struct isc_device *isc)
static void isc_try_fse(struct isc_device *isc,
struct v4l2_subdev_state *sd_state)
{
+ struct v4l2_rect *try_crop =
+ v4l2_subdev_state_get_crop(sd_state, 0);
struct v4l2_subdev_frame_size_enum fse = {
.which = V4L2_SUBDEV_FORMAT_TRY,
};
@@ -841,11 +843,11 @@ static void isc_try_fse(struct isc_device *isc,
* just use the maximum ISC can receive.
*/
if (ret) {
- sd_state->pads->try_crop.width = isc->max_width;
- sd_state->pads->try_crop.height = isc->max_height;
+ try_crop->width = isc->max_width;
+ try_crop->height = isc->max_height;
} else {
- sd_state->pads->try_crop.width = fse.max_width;
- sd_state->pads->try_crop.height = fse.max_height;
+ try_crop->width = fse.max_width;
+ try_crop->height = fse.max_height;
}
}
@@ -1869,7 +1871,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &isc->lock;
- q->min_buffers_needed = 1;
+ q->min_queued_buffers = 1;
q->dev = isc->dev;
ret = vb2_queue_init(q);
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
index ac5fb332088ea..2b80d54006b39 100644
--- a/drivers/staging/media/imx/imx-ic-prp.c
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -82,10 +82,8 @@ static struct v4l2_mbus_framefmt *
__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
- struct imx_ic_priv *ic_priv = priv->ic_priv;
-
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
else
return &priv->format_mbus;
}
@@ -395,11 +393,19 @@ out:
return ret;
}
-static int prp_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int prp_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct prp_priv *priv = sd_to_priv(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
if (fi->pad >= PRP_NUM_PADS)
return -EINVAL;
@@ -410,11 +416,19 @@ static int prp_g_frame_interval(struct v4l2_subdev *sd,
return 0;
}
-static int prp_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int prp_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct prp_priv *priv = sd_to_priv(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
if (fi->pad >= PRP_NUM_PADS)
return -EINVAL;
@@ -450,16 +464,15 @@ static int prp_registered(struct v4l2_subdev *sd)
}
static const struct v4l2_subdev_pad_ops prp_pad_ops = {
- .init_cfg = imx_media_init_cfg,
.enum_mbus_code = prp_enum_mbus_code,
.get_fmt = prp_get_fmt,
.set_fmt = prp_set_fmt,
+ .get_frame_interval = prp_get_frame_interval,
+ .set_frame_interval = prp_set_frame_interval,
.link_validate = prp_link_validate,
};
static const struct v4l2_subdev_video_ops prp_video_ops = {
- .g_frame_interval = prp_g_frame_interval,
- .s_frame_interval = prp_s_frame_interval,
.s_stream = prp_s_stream,
};
@@ -474,6 +487,7 @@ static const struct v4l2_subdev_ops prp_subdev_ops = {
};
static const struct v4l2_subdev_internal_ops prp_internal_ops = {
+ .init_state = imx_media_init_state,
.registered = prp_registered,
};
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
index 9b81cfbcd7779..17fd980c9d3c3 100644
--- a/drivers/staging/media/imx/imx-ic-prpencvf.c
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -790,10 +790,8 @@ static struct v4l2_mbus_framefmt *
__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
- struct imx_ic_priv *ic_priv = priv->ic_priv;
-
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
else
return &priv->format_mbus[pad];
}
@@ -1205,11 +1203,19 @@ out:
return ret;
}
-static int prp_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int prp_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct prp_priv *priv = sd_to_priv(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
if (fi->pad >= PRPENCVF_NUM_PADS)
return -EINVAL;
@@ -1220,11 +1226,19 @@ static int prp_g_frame_interval(struct v4l2_subdev *sd,
return 0;
}
-static int prp_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int prp_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct prp_priv *priv = sd_to_priv(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
if (fi->pad >= PRPENCVF_NUM_PADS)
return -EINVAL;
@@ -1298,16 +1312,15 @@ static void prp_unregistered(struct v4l2_subdev *sd)
}
static const struct v4l2_subdev_pad_ops prp_pad_ops = {
- .init_cfg = imx_media_init_cfg,
.enum_mbus_code = prp_enum_mbus_code,
.enum_frame_size = prp_enum_frame_size,
.get_fmt = prp_get_fmt,
.set_fmt = prp_set_fmt,
+ .get_frame_interval = prp_get_frame_interval,
+ .set_frame_interval = prp_set_frame_interval,
};
static const struct v4l2_subdev_video_ops prp_video_ops = {
- .g_frame_interval = prp_g_frame_interval,
- .s_frame_interval = prp_s_frame_interval,
.s_stream = prp_s_stream,
};
@@ -1322,6 +1335,7 @@ static const struct v4l2_subdev_ops prp_subdev_ops = {
};
static const struct v4l2_subdev_internal_ops prp_internal_ops = {
+ .init_state = imx_media_init_state,
.registered = prp_registered,
.unregistered = prp_unregistered,
};
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index 4846078315ff3..efa7623b5cee1 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -511,7 +511,8 @@ static int capture_legacy_g_parm(struct file *file, void *fh,
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- ret = v4l2_subdev_call(priv->src_sd, video, g_frame_interval, &fi);
+ ret = v4l2_subdev_call_state_active(priv->src_sd, pad,
+ get_frame_interval, &fi);
if (ret < 0)
return ret;
@@ -534,7 +535,8 @@ static int capture_legacy_s_parm(struct file *file, void *fh,
return -EINVAL;
fi.interval = a->parm.capture.timeperframe;
- ret = v4l2_subdev_call(priv->src_sd, video, s_frame_interval, &fi);
+ ret = v4l2_subdev_call_state_active(priv->src_sd, pad,
+ set_frame_interval, &fi);
if (ret < 0)
return ret;
@@ -605,6 +607,7 @@ static int capture_queue_setup(struct vb2_queue *vq,
{
struct capture_priv *priv = vb2_get_drv_priv(vq);
struct v4l2_pix_format *pix = &priv->vdev.fmt;
+ unsigned int q_num_bufs = vb2_get_num_buffers(vq);
unsigned int count = *nbuffers;
if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -613,14 +616,14 @@ static int capture_queue_setup(struct vb2_queue *vq,
if (*nplanes) {
if (*nplanes != 1 || sizes[0] < pix->sizeimage)
return -EINVAL;
- count += vq->num_buffers;
+ count += q_num_bufs;
}
count = min_t(__u32, VID_MEM_LIMIT / pix->sizeimage, count);
if (*nplanes)
- *nbuffers = (count < vq->num_buffers) ? 0 :
- count - vq->num_buffers;
+ *nbuffers = (count < q_num_bufs) ? 0 :
+ count - q_num_bufs;
else
*nbuffers = count;
@@ -1021,7 +1024,7 @@ imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd,
vq->mem_ops = &vb2_dma_contig_memops;
vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
vq->lock = &priv->mutex;
- vq->min_buffers_needed = 2;
+ vq->min_queued_buffers = 2;
vq->dev = priv->dev;
ret = vb2_queue_init(vq);
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index dda1ebc34692a..785aac8819221 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -902,11 +902,19 @@ static const struct csi_skip_desc *csi_find_best_skip(struct v4l2_fract *in,
* V4L2 subdev operations.
*/
-static int csi_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int csi_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
if (fi->pad >= CSI_NUM_PADS)
return -EINVAL;
@@ -919,13 +927,21 @@ static int csi_g_frame_interval(struct v4l2_subdev *sd,
return 0;
}
-static int csi_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int csi_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_fract *input_fi;
int ret = 0;
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
mutex_lock(&priv->lock);
input_fi = &priv->frame_interval[CSI_SINK_PAD];
@@ -1148,7 +1164,7 @@ __csi_get_fmt(struct csi_priv *priv, struct v4l2_subdev_state *sd_state,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
else
return &priv->format_mbus[pad];
}
@@ -1158,8 +1174,7 @@ __csi_get_crop(struct csi_priv *priv, struct v4l2_subdev_state *sd_state,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_crop(&priv->sd, sd_state,
- CSI_SINK_PAD);
+ return v4l2_subdev_state_get_crop(sd_state, CSI_SINK_PAD);
else
return &priv->crop;
}
@@ -1169,8 +1184,7 @@ __csi_get_compose(struct csi_priv *priv, struct v4l2_subdev_state *sd_state,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_compose(&priv->sd, sd_state,
- CSI_SINK_PAD);
+ return v4l2_subdev_state_get_compose(sd_state, CSI_SINK_PAD);
else
return &priv->compose;
}
@@ -1862,13 +1876,10 @@ static const struct v4l2_subdev_core_ops csi_core_ops = {
};
static const struct v4l2_subdev_video_ops csi_video_ops = {
- .g_frame_interval = csi_g_frame_interval,
- .s_frame_interval = csi_s_frame_interval,
.s_stream = csi_s_stream,
};
static const struct v4l2_subdev_pad_ops csi_pad_ops = {
- .init_cfg = imx_media_init_cfg,
.enum_mbus_code = csi_enum_mbus_code,
.enum_frame_size = csi_enum_frame_size,
.enum_frame_interval = csi_enum_frame_interval,
@@ -1876,6 +1887,8 @@ static const struct v4l2_subdev_pad_ops csi_pad_ops = {
.set_fmt = csi_set_fmt,
.get_selection = csi_get_selection,
.set_selection = csi_set_selection,
+ .get_frame_interval = csi_get_frame_interval,
+ .set_frame_interval = csi_set_frame_interval,
.link_validate = csi_link_validate,
};
@@ -1886,6 +1899,7 @@ static const struct v4l2_subdev_ops csi_subdev_ops = {
};
static const struct v4l2_subdev_internal_ops csi_internal_ops = {
+ .init_state = imx_media_init_state,
.registered = csi_registered,
.unregistered = csi_unregistered,
};
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index 064dc562bc96a..1b5af8945e6b8 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -426,10 +426,10 @@ EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
/*
* Initializes the TRY format to the ACTIVE format on all pads
- * of a subdev. Can be used as the .init_cfg pad operation.
+ * of a subdev. Can be used as the .init_state internal operation.
*/
-int imx_media_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state)
+int imx_media_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
{
struct v4l2_mbus_framefmt *mf_try;
unsigned int pad;
@@ -445,13 +445,13 @@ int imx_media_init_cfg(struct v4l2_subdev *sd,
if (ret)
continue;
- mf_try = v4l2_subdev_get_try_format(sd, sd_state, pad);
+ mf_try = v4l2_subdev_state_get_format(sd_state, pad);
*mf_try = format.format;
}
return 0;
}
-EXPORT_SYMBOL_GPL(imx_media_init_cfg);
+EXPORT_SYMBOL_GPL(imx_media_init_state);
/*
* Default the colorspace in tryfmt to SRGB if set to an unsupported
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 3c2093c520bab..09da4103a8dbe 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -536,7 +536,7 @@ __vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_state *sd_state,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
else
return &priv->format_mbus[pad];
}
@@ -780,11 +780,19 @@ static int vdic_link_validate(struct v4l2_subdev *sd,
return ret;
}
-static int vdic_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int vdic_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
if (fi->pad >= VDIC_NUM_PADS)
return -EINVAL;
@@ -797,13 +805,21 @@ static int vdic_g_frame_interval(struct v4l2_subdev *sd,
return 0;
}
-static int vdic_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
+static int vdic_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *fi)
{
struct vdic_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_fract *input_fi, *output_fi;
int ret = 0;
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
mutex_lock(&priv->lock);
input_fi = &priv->frame_interval[priv->active_input_pad];
@@ -882,16 +898,15 @@ static void vdic_unregistered(struct v4l2_subdev *sd)
}
static const struct v4l2_subdev_pad_ops vdic_pad_ops = {
- .init_cfg = imx_media_init_cfg,
.enum_mbus_code = vdic_enum_mbus_code,
.get_fmt = vdic_get_fmt,
.set_fmt = vdic_set_fmt,
+ .get_frame_interval = vdic_get_frame_interval,
+ .set_frame_interval = vdic_set_frame_interval,
.link_validate = vdic_link_validate,
};
static const struct v4l2_subdev_video_ops vdic_video_ops = {
- .g_frame_interval = vdic_g_frame_interval,
- .s_frame_interval = vdic_s_frame_interval,
.s_stream = vdic_s_stream,
};
@@ -906,6 +921,7 @@ static const struct v4l2_subdev_ops vdic_subdev_ops = {
};
static const struct v4l2_subdev_internal_ops vdic_internal_ops = {
+ .init_state = imx_media_init_state,
.registered = vdic_registered,
.unregistered = vdic_unregistered,
};
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index 2640cd34dce26..f095d9134fee4 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -192,8 +192,8 @@ static inline int imx_media_enum_ipu_formats(u32 *code, u32 index,
int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
u32 width, u32 height, u32 code, u32 field,
const struct imx_media_pixfmt **cc);
-int imx_media_init_cfg(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state);
+int imx_media_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state);
void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt,
bool ic_route);
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
index b2d8476d83a0a..0d8b420616235 100644
--- a/drivers/staging/media/imx/imx6-mipi-csi2.c
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -501,7 +501,7 @@ __csi2_get_fmt(struct csi2_dev *csi2, struct v4l2_subdev_state *sd_state,
unsigned int pad, enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&csi2->sd, sd_state, pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
else
return &csi2->format_mbus;
}
@@ -619,7 +619,6 @@ static const struct v4l2_subdev_video_ops csi2_video_ops = {
};
static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
- .init_cfg = imx_media_init_cfg,
.get_fmt = csi2_get_fmt,
.set_fmt = csi2_set_fmt,
};
@@ -631,6 +630,7 @@ static const struct v4l2_subdev_ops csi2_subdev_ops = {
};
static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .init_state = imx_media_init_state,
.registered = csi2_registered,
};
diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c
index e530767e80a5d..a66f034380c05 100644
--- a/drivers/staging/media/ipu3/ipu3-v4l2.c
+++ b/drivers/staging/media/ipu3/ipu3-v4l2.c
@@ -36,7 +36,7 @@ static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
/* Initialize try_fmt */
for (i = 0; i < IMGU_NODE_NUM; i++) {
struct v4l2_mbus_framefmt *try_fmt =
- v4l2_subdev_get_try_format(sd, fh->state, i);
+ v4l2_subdev_state_get_format(fh->state, i);
try_fmt->width = try_crop.width;
try_fmt->height = try_crop.height;
@@ -44,8 +44,8 @@ static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
try_fmt->field = V4L2_FIELD_NONE;
}
- *v4l2_subdev_get_try_crop(sd, fh->state, IMGU_NODE_IN) = try_crop;
- *v4l2_subdev_get_try_compose(sd, fh->state, IMGU_NODE_IN) = try_crop;
+ *v4l2_subdev_state_get_crop(fh->state, IMGU_NODE_IN) = try_crop;
+ *v4l2_subdev_state_get_compose(fh->state, IMGU_NODE_IN) = try_crop;
return 0;
}
@@ -136,7 +136,7 @@ static int imgu_subdev_get_fmt(struct v4l2_subdev *sd,
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
fmt->format = imgu_pipe->nodes[pad].pad_fmt;
} else {
- mf = v4l2_subdev_get_try_format(sd, sd_state, pad);
+ mf = v4l2_subdev_state_get_format(sd_state, pad);
fmt->format = *mf;
}
@@ -161,7 +161,7 @@ static int imgu_subdev_set_fmt(struct v4l2_subdev *sd,
imgu_pipe = &imgu->imgu_pipe[pipe];
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
- mf = v4l2_subdev_get_try_format(sd, sd_state, pad);
+ mf = v4l2_subdev_state_get_format(sd_state, pad);
else
mf = &imgu_pipe->nodes[pad].pad_fmt;
@@ -194,7 +194,7 @@ imgu_subdev_get_crop(struct imgu_v4l2_subdev *sd,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_crop(&sd->subdev, sd_state, pad);
+ return v4l2_subdev_state_get_crop(sd_state, pad);
else
return &sd->rect.eff;
}
@@ -205,7 +205,7 @@ imgu_subdev_get_compose(struct imgu_v4l2_subdev *sd,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_compose(&sd->subdev, sd_state, pad);
+ return v4l2_subdev_state_get_compose(sd_state, pad);
else
return &sd->rect.bds;
}
@@ -1198,7 +1198,7 @@ static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
vbq->buf_struct_size = imgu->buf_struct_size;
vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
/* can streamon w/o buffers */
- vbq->min_buffers_needed = 0;
+ vbq->min_queued_buffers = 0;
vbq->drv_priv = imgu;
vbq->lock = &node->lock;
r = vb2_queue_init(vbq);
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 219185aaa588a..de3e0345ab7c6 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -167,23 +167,24 @@ static void process_num_buffers(struct vb2_queue *q,
bool is_reqbufs)
{
const struct amvdec_format *fmt_out = sess->fmt_out;
- unsigned int buffers_total = q->num_buffers + *num_buffers;
+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
+ unsigned int buffers_total = q_num_bufs + *num_buffers;
u32 min_buf_capture = v4l2_ctrl_g_ctrl(sess->ctrl_min_buf_capture);
- if (q->num_buffers + *num_buffers < min_buf_capture)
- *num_buffers = min_buf_capture - q->num_buffers;
+ if (q_num_bufs + *num_buffers < min_buf_capture)
+ *num_buffers = min_buf_capture - q_num_bufs;
if (is_reqbufs && buffers_total < fmt_out->min_buffers)
- *num_buffers = fmt_out->min_buffers - q->num_buffers;
+ *num_buffers = fmt_out->min_buffers - q_num_bufs;
if (buffers_total > fmt_out->max_buffers)
- *num_buffers = fmt_out->max_buffers - q->num_buffers;
+ *num_buffers = fmt_out->max_buffers - q_num_bufs;
/* We need to program the complete CAPTURE buffer list
* in registers during start_streaming, and the firmwares
* are free to choose any of them to write frames to. As such,
* we need all of them to be queued into the driver
*/
- sess->num_dst_bufs = q->num_buffers + *num_buffers;
- q->min_buffers_needed = max(fmt_out->min_buffers, sess->num_dst_bufs);
+ sess->num_dst_bufs = q_num_bufs + *num_buffers;
+ q->min_queued_buffers = max(fmt_out->min_buffers, sess->num_dst_bufs);
}
static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
@@ -824,7 +825,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->drv_priv = sess;
src_vq->buf_struct_size = sizeof(struct dummy_buf);
- src_vq->min_buffers_needed = 1;
+ src_vq->min_queued_buffers = 1;
src_vq->dev = sess->core->dev;
src_vq->lock = &sess->lock;
ret = vb2_queue_init(src_vq);
@@ -838,7 +839,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->drv_priv = sess;
dst_vq->buf_struct_size = sizeof(struct dummy_buf);
- dst_vq->min_buffers_needed = 1;
+ dst_vq->min_queued_buffers = 1;
dst_vq->dev = sess->core->dev;
dst_vq->lock = &sess->lock;
return vb2_queue_init(dst_vq);
diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c
index d2844414de4f7..0e6c5bd81930c 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.c
+++ b/drivers/staging/media/omap4iss/iss_csi2.c
@@ -830,8 +830,7 @@ __csi2_get_format(struct iss_csi2_device *csi2,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&csi2->subdev, sd_state,
- pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
return &csi2->formats[pad];
}
@@ -891,7 +890,7 @@ csi2_try_format(struct iss_csi2_device *csi2,
/*
* csi2_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
- * @cfg : V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @code : pointer to v4l2_subdev_mbus_code_enum structure
* return -EINVAL or zero on success
*/
@@ -965,7 +964,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd,
/*
* csi2_get_format - Handle get format by pads subdev method
* @sd : pointer to v4l2 subdev structure
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: pointer to v4l2 subdev format structure
* return -EINVAL or zero on success
*/
@@ -987,7 +986,7 @@ static int csi2_get_format(struct v4l2_subdev *sd,
/*
* csi2_set_format - Handle set format by pads subdev method
* @sd : pointer to v4l2 subdev structure
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: pointer to v4l2 subdev format structure
* return -EINVAL or zero on success
*/
diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c
index 23f707cb336f5..4a4eae290d65a 100644
--- a/drivers/staging/media/omap4iss/iss_ipipe.c
+++ b/drivers/staging/media/omap4iss/iss_ipipe.c
@@ -180,8 +180,7 @@ __ipipe_get_format(struct iss_ipipe_device *ipipe,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&ipipe->subdev, sd_state,
- pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
return &ipipe->formats[pad];
}
@@ -189,7 +188,7 @@ __ipipe_get_format(struct iss_ipipe_device *ipipe,
/*
* ipipe_try_format - Try video format on a pad
* @ipipe: ISS IPIPE device
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @pad: Pad number
* @fmt: Format
*/
@@ -240,7 +239,7 @@ ipipe_try_format(struct iss_ipipe_device *ipipe,
/*
* ipipe_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
- * @cfg : V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @code : pointer to v4l2_subdev_mbus_code_enum structure
* return -EINVAL or zero on success
*/
@@ -304,7 +303,7 @@ static int ipipe_enum_frame_size(struct v4l2_subdev *sd,
/*
* ipipe_get_format - Retrieve the video format on a pad
* @sd : ISP IPIPE V4L2 subdevice
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: Format
*
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
@@ -328,7 +327,7 @@ static int ipipe_get_format(struct v4l2_subdev *sd,
/*
* ipipe_set_format - Set the video format on a pad
* @sd : ISP IPIPE V4L2 subdevice
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: Format
*
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c
index 5e7f25cd53acf..8fa99532d9d4a 100644
--- a/drivers/staging/media/omap4iss/iss_ipipeif.c
+++ b/drivers/staging/media/omap4iss/iss_ipipeif.c
@@ -361,15 +361,14 @@ __ipipeif_get_format(struct iss_ipipeif_device *ipipeif,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&ipipeif->subdev, sd_state,
- pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
return &ipipeif->formats[pad];
}
/*
* ipipeif_try_format - Try video format on a pad
* @ipipeif: ISS IPIPEIF device
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @pad: Pad number
* @fmt: Format
*/
@@ -440,7 +439,7 @@ ipipeif_try_format(struct iss_ipipeif_device *ipipeif,
/*
* ipipeif_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
- * @cfg : V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @code : pointer to v4l2_subdev_mbus_code_enum structure
* return -EINVAL or zero on success
*/
@@ -512,7 +511,7 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd,
/*
* ipipeif_get_format - Retrieve the video format on a pad
* @sd : ISP IPIPEIF V4L2 subdevice
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: Format
*
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
@@ -536,7 +535,7 @@ static int ipipeif_get_format(struct v4l2_subdev *sd,
/*
* ipipeif_set_format - Set the video format on a pad
* @sd : ISP IPIPEIF V4L2 subdevice
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: Format
*
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c
index a5f8f9f1ab16d..58e698ef91085 100644
--- a/drivers/staging/media/omap4iss/iss_resizer.c
+++ b/drivers/staging/media/omap4iss/iss_resizer.c
@@ -420,15 +420,14 @@ __resizer_get_format(struct iss_resizer_device *resizer,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(&resizer->subdev, sd_state,
- pad);
+ return v4l2_subdev_state_get_format(sd_state, pad);
return &resizer->formats[pad];
}
/*
* resizer_try_format - Try video format on a pad
* @resizer: ISS RESIZER device
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @pad: Pad number
* @fmt: Format
*/
@@ -489,7 +488,7 @@ resizer_try_format(struct iss_resizer_device *resizer,
/*
* resizer_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @code : pointer to v4l2_subdev_mbus_code_enum structure
* return -EINVAL or zero on success
*/
@@ -572,7 +571,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd,
/*
* resizer_get_format - Retrieve the video format on a pad
* @sd : ISP RESIZER V4L2 subdevice
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: Format
*
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
@@ -596,7 +595,7 @@ static int resizer_get_format(struct v4l2_subdev *sd,
/*
* resizer_set_format - Set the video format on a pad
* @sd : ISP RESIZER V4L2 subdevice
- * @cfg: V4L2 subdev pad config
+ * @sd_state: V4L2 subdev state
* @fmt: Format
*
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
diff --git a/drivers/staging/media/rkvdec/Kconfig b/drivers/staging/media/rkvdec/Kconfig
index e963d60cc6ad5..5f3bdd848a2cf 100644
--- a/drivers/staging/media/rkvdec/Kconfig
+++ b/drivers/staging/media/rkvdec/Kconfig
@@ -4,7 +4,6 @@ config VIDEO_ROCKCHIP_VDEC
depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on VIDEO_DEV
select MEDIA_CONTROLLER
- select MEDIA_CONTROLLER_REQUEST_API
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 84a41792cb4b8..ac398b5a97360 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -461,6 +461,9 @@ static const struct v4l2_ioctl_ops rkvdec_ioctl_ops = {
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd,
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
};
static int rkvdec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
diff --git a/drivers/staging/media/starfive/Kconfig b/drivers/staging/media/starfive/Kconfig
new file mode 100644
index 0000000000000..34727cf560722
--- /dev/null
+++ b/drivers/staging/media/starfive/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "StarFive media platform drivers"
+
+source "drivers/staging/media/starfive/camss/Kconfig"
diff --git a/drivers/staging/media/starfive/Makefile b/drivers/staging/media/starfive/Makefile
new file mode 100644
index 0000000000000..4639fa1bca325
--- /dev/null
+++ b/drivers/staging/media/starfive/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += camss/
diff --git a/drivers/staging/media/starfive/camss/Kconfig b/drivers/staging/media/starfive/camss/Kconfig
new file mode 100644
index 0000000000000..9ea5708fe4095
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_STARFIVE_CAMSS
+ tristate "Starfive Camera Subsystem driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on HAS_DMA
+ depends on PM
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ help
+ Enable this to support for the Starfive Camera subsystem
+ found on Starfive JH7110 SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called starfive-camss.
diff --git a/drivers/staging/media/starfive/camss/Makefile b/drivers/staging/media/starfive/camss/Makefile
new file mode 100644
index 0000000000000..005790202e7b8
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for StarFive Camera Subsystem driver
+#
+
+starfive-camss-objs += \
+ stf-camss.o \
+ stf-capture.o \
+ stf-isp.o \
+ stf-isp-hw-ops.o \
+ stf-video.o
+
+obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o
diff --git a/drivers/staging/media/starfive/camss/TODO.txt b/drivers/staging/media/starfive/camss/TODO.txt
new file mode 100644
index 0000000000000..7d459f4f09d00
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/TODO.txt
@@ -0,0 +1,4 @@
+Unstaging requirements:
+- Add userspace support which demonstrates the ability to receive statistics and
+ adapt hardware modules configuration accordingly;
+- Add documentation for description of the statistics data structures;
diff --git a/drivers/staging/media/starfive/camss/stf-camss.c b/drivers/staging/media/starfive/camss/stf-camss.c
new file mode 100644
index 0000000000000..a587f860101ae
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-camss.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * stf_camss.c
+ *
+ * Starfive Camera Subsystem driver
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ *
+ * Author: Jack Zhu <jack.zhu@starfivetech.com>
+ * Author: Changhuang Liang <changhuang.liang@starfivetech.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+
+#include "stf-camss.h"
+
+static const char * const stfcamss_clocks[] = {
+ "wrapper_clk_c",
+ "ispcore_2x",
+ "isp_axi",
+};
+
+static const char * const stfcamss_resets[] = {
+ "wrapper_p",
+ "wrapper_c",
+ "axiwr",
+ "isp_top_n",
+ "isp_top_axi",
+};
+
+static const struct stf_isr_data stf_isrs[] = {
+ {"wr_irq", stf_wr_irq_handler},
+ {"isp_irq", stf_isp_irq_handler},
+ {"line_irq", stf_line_irq_handler},
+};
+
+static int stfcamss_get_mem_res(struct stfcamss *stfcamss)
+{
+ struct platform_device *pdev = to_platform_device(stfcamss->dev);
+
+ stfcamss->syscon_base =
+ devm_platform_ioremap_resource_byname(pdev, "syscon");
+ if (IS_ERR(stfcamss->syscon_base))
+ return PTR_ERR(stfcamss->syscon_base);
+
+ stfcamss->isp_base = devm_platform_ioremap_resource_byname(pdev, "isp");
+ if (IS_ERR(stfcamss->isp_base))
+ return PTR_ERR(stfcamss->isp_base);
+
+ return 0;
+}
+
+/*
+ * stfcamss_of_parse_endpoint_node - Parse port endpoint node
+ * @dev: Device
+ * @node: Device node to be parsed
+ * @csd: Parsed data from port endpoint node
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int stfcamss_of_parse_endpoint_node(struct stfcamss *stfcamss,
+ struct device_node *node,
+ struct stfcamss_async_subdev *csd)
+{
+ struct v4l2_fwnode_endpoint vep = { { 0 } };
+ int ret;
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
+ if (ret) {
+ dev_err(stfcamss->dev, "endpoint not defined at %pOF\n", node);
+ return ret;
+ }
+
+ csd->port = vep.base.port;
+
+ return 0;
+}
+
+/*
+ * stfcamss_of_parse_ports - Parse ports node
+ * @stfcamss: STFCAMSS device
+ *
+ * Return number of "port" nodes found in "ports" node
+ */
+static int stfcamss_of_parse_ports(struct stfcamss *stfcamss)
+{
+ struct device_node *node = NULL;
+ int ret, num_subdevs = 0;
+
+ for_each_endpoint_of_node(stfcamss->dev->of_node, node) {
+ struct stfcamss_async_subdev *csd;
+
+ if (!of_device_is_available(node))
+ continue;
+
+ csd = v4l2_async_nf_add_fwnode_remote(&stfcamss->notifier,
+ of_fwnode_handle(node),
+ struct stfcamss_async_subdev);
+ if (IS_ERR(csd)) {
+ ret = PTR_ERR(csd);
+ dev_err(stfcamss->dev, "failed to add async notifier\n");
+ goto err_cleanup;
+ }
+
+ ret = stfcamss_of_parse_endpoint_node(stfcamss, node, csd);
+ if (ret)
+ goto err_cleanup;
+
+ num_subdevs++;
+ }
+
+ return num_subdevs;
+
+err_cleanup:
+ of_node_put(node);
+ return ret;
+}
+
+static int stfcamss_register_devs(struct stfcamss *stfcamss)
+{
+ struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV];
+ struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
+ int ret;
+
+ ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(stfcamss->dev,
+ "failed to register stf isp%d entity: %d\n", 0, ret);
+ return ret;
+ }
+
+ ret = stf_capture_register(stfcamss, &stfcamss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(stfcamss->dev,
+ "failed to register capture: %d\n", ret);
+ goto err_isp_unregister;
+ }
+
+ ret = media_create_pad_link(&isp_dev->subdev.entity, STF_ISP_PAD_SRC,
+ &cap_yuv->video.vdev.entity, 0, 0);
+ if (ret)
+ goto err_cap_unregister;
+
+ cap_yuv->video.source_subdev = &isp_dev->subdev;
+
+ return ret;
+
+err_cap_unregister:
+ stf_capture_unregister(stfcamss);
+err_isp_unregister:
+ stf_isp_unregister(&stfcamss->isp_dev);
+
+ return ret;
+}
+
+static void stfcamss_unregister_devs(struct stfcamss *stfcamss)
+{
+ stf_isp_unregister(&stfcamss->isp_dev);
+ stf_capture_unregister(stfcamss);
+}
+
+static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asc)
+{
+ struct stfcamss *stfcamss =
+ container_of(async, struct stfcamss, notifier);
+ struct stfcamss_async_subdev *csd =
+ container_of(asc, struct stfcamss_async_subdev, asd);
+ enum stf_port_num port = csd->port;
+ struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
+ struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW];
+ struct media_pad *pad;
+ int ret;
+
+ if (port == STF_PORT_CSI2RX) {
+ pad = &isp_dev->pads[STF_ISP_PAD_SINK];
+ } else {
+ dev_err(stfcamss->dev, "not support port %d\n", port);
+ return -EPERM;
+ }
+
+ ret = v4l2_create_fwnode_links_to_pad(subdev, pad, 0);
+ if (ret)
+ return ret;
+
+ ret = media_create_pad_link(&subdev->entity, 1,
+ &cap_raw->video.vdev.entity, 0, 0);
+ if (ret)
+ return ret;
+
+ isp_dev->source_subdev = subdev;
+ cap_raw->video.source_subdev = subdev;
+
+ return 0;
+}
+
+static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf)
+{
+ struct stfcamss *stfcamss =
+ container_of(ntf, struct stfcamss, notifier);
+
+ return v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations
+stfcamss_subdev_notifier_ops = {
+ .bound = stfcamss_subdev_notifier_bound,
+ .complete = stfcamss_subdev_notifier_complete,
+};
+
+static void stfcamss_mc_init(struct platform_device *pdev,
+ struct stfcamss *stfcamss)
+{
+ stfcamss->media_dev.dev = stfcamss->dev;
+ strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem",
+ sizeof(stfcamss->media_dev.model));
+ media_device_init(&stfcamss->media_dev);
+
+ stfcamss->v4l2_dev.mdev = &stfcamss->media_dev;
+}
+
+/*
+ * stfcamss_probe - Probe STFCAMSS platform device
+ * @pdev: Pointer to STFCAMSS platform device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int stfcamss_probe(struct platform_device *pdev)
+{
+ struct stfcamss *stfcamss;
+ struct device *dev = &pdev->dev;
+ int ret, num_subdevs;
+ unsigned int i;
+
+ stfcamss = devm_kzalloc(dev, sizeof(*stfcamss), GFP_KERNEL);
+ if (!stfcamss)
+ return -ENOMEM;
+
+ stfcamss->dev = dev;
+
+ for (i = 0; i < ARRAY_SIZE(stf_isrs); ++i) {
+ int irq;
+
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(stfcamss->dev, irq, stf_isrs[i].isr, 0,
+ stf_isrs[i].name, stfcamss);
+ if (ret) {
+ dev_err(dev, "request irq failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ stfcamss->nclks = ARRAY_SIZE(stfcamss->sys_clk);
+ for (i = 0; i < stfcamss->nclks; ++i)
+ stfcamss->sys_clk[i].id = stfcamss_clocks[i];
+ ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk);
+ if (ret) {
+ dev_err(dev, "Failed to get clk controls\n");
+ return ret;
+ }
+
+ stfcamss->nrsts = ARRAY_SIZE(stfcamss->sys_rst);
+ for (i = 0; i < stfcamss->nrsts; ++i)
+ stfcamss->sys_rst[i].id = stfcamss_resets[i];
+ ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts,
+ stfcamss->sys_rst);
+ if (ret) {
+ dev_err(dev, "Failed to get reset controls\n");
+ return ret;
+ }
+
+ ret = stfcamss_get_mem_res(stfcamss);
+ if (ret) {
+ dev_err(dev, "Could not map registers\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, stfcamss);
+
+ v4l2_async_nf_init(&stfcamss->notifier, &stfcamss->v4l2_dev);
+
+ num_subdevs = stfcamss_of_parse_ports(stfcamss);
+ if (num_subdevs < 0) {
+ ret = -ENODEV;
+ dev_err(dev, "Failed to get sub devices: %d\n", ret);
+ goto err_cleanup_notifier;
+ }
+
+ ret = stf_isp_init(stfcamss);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init isp: %d\n", ret);
+ goto err_cleanup_notifier;
+ }
+
+ stfcamss_mc_init(pdev, stfcamss);
+
+ ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register V4L2 device: %d\n", ret);
+ goto err_cleanup_media_device;
+ }
+
+ ret = media_device_register(&stfcamss->media_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register media device: %d\n", ret);
+ goto err_unregister_device;
+ }
+
+ ret = stfcamss_register_devs(stfcamss);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register subdevice: %d\n", ret);
+ goto err_unregister_media_dev;
+ }
+
+ pm_runtime_enable(dev);
+
+ stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops;
+ ret = v4l2_async_nf_register(&stfcamss->notifier);
+ if (ret) {
+ dev_err(dev, "Failed to register async subdev nodes: %d\n",
+ ret);
+ pm_runtime_disable(dev);
+ goto err_unregister_subdevs;
+ }
+
+ return 0;
+
+err_unregister_subdevs:
+ stfcamss_unregister_devs(stfcamss);
+err_unregister_media_dev:
+ media_device_unregister(&stfcamss->media_dev);
+err_unregister_device:
+ v4l2_device_unregister(&stfcamss->v4l2_dev);
+err_cleanup_media_device:
+ media_device_cleanup(&stfcamss->media_dev);
+err_cleanup_notifier:
+ v4l2_async_nf_cleanup(&stfcamss->notifier);
+ return ret;
+}
+
+/*
+ * stfcamss_remove - Remove STFCAMSS platform device
+ * @pdev: Pointer to STFCAMSS platform device
+ *
+ * Always returns 0.
+ */
+static int stfcamss_remove(struct platform_device *pdev)
+{
+ struct stfcamss *stfcamss = platform_get_drvdata(pdev);
+
+ stfcamss_unregister_devs(stfcamss);
+ v4l2_device_unregister(&stfcamss->v4l2_dev);
+ media_device_cleanup(&stfcamss->media_dev);
+ v4l2_async_nf_cleanup(&stfcamss->notifier);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id stfcamss_of_match[] = {
+ { .compatible = "starfive,jh7110-camss" },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, stfcamss_of_match);
+
+static int __maybe_unused stfcamss_runtime_suspend(struct device *dev)
+{
+ struct stfcamss *stfcamss = dev_get_drvdata(dev);
+ int ret;
+
+ ret = reset_control_bulk_assert(stfcamss->nrsts, stfcamss->sys_rst);
+ if (ret) {
+ dev_err(dev, "reset assert failed\n");
+ return ret;
+ }
+
+ clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk);
+
+ return 0;
+}
+
+static int __maybe_unused stfcamss_runtime_resume(struct device *dev)
+{
+ struct stfcamss *stfcamss = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(stfcamss->nclks, stfcamss->sys_clk);
+ if (ret) {
+ dev_err(dev, "clock prepare enable failed\n");
+ return ret;
+ }
+
+ ret = reset_control_bulk_deassert(stfcamss->nrsts, stfcamss->sys_rst);
+ if (ret < 0) {
+ dev_err(dev, "cannot deassert resets\n");
+ clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops stfcamss_pm_ops = {
+ SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend,
+ stfcamss_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver stfcamss_driver = {
+ .probe = stfcamss_probe,
+ .remove = stfcamss_remove,
+ .driver = {
+ .name = "starfive-camss",
+ .pm = &stfcamss_pm_ops,
+ .of_match_table = stfcamss_of_match,
+ },
+};
+
+module_platform_driver(stfcamss_driver);
+
+MODULE_AUTHOR("Jack Zhu <jack.zhu@starfivetech.com>");
+MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
+MODULE_DESCRIPTION("StarFive Camera Subsystem driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/starfive/camss/stf-camss.h b/drivers/staging/media/starfive/camss/stf-camss.h
new file mode 100644
index 0000000000000..e2b0cfb437bdb
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-camss.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * stf_camss.h
+ *
+ * Starfive Camera Subsystem driver
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef STF_CAMSS_H
+#define STF_CAMSS_H
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+
+#include "stf-isp.h"
+#include "stf-capture.h"
+
+enum stf_port_num {
+ STF_PORT_DVP = 0,
+ STF_PORT_CSI2RX
+};
+
+enum stf_clk {
+ STF_CLK_WRAPPER_CLK_C = 0,
+ STF_CLK_ISPCORE_2X,
+ STF_CLK_ISP_AXI,
+ STF_CLK_NUM
+};
+
+enum stf_rst {
+ STF_RST_WRAPPER_P = 0,
+ STF_RST_WRAPPER_C,
+ STF_RST_AXIWR,
+ STF_RST_ISP_TOP_N,
+ STF_RST_ISP_TOP_AXI,
+ STF_RST_NUM
+};
+
+struct stf_isr_data {
+ const char *name;
+ irqreturn_t (*isr)(int irq, void *priv);
+};
+
+struct stfcamss {
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct media_pipeline pipe;
+ struct device *dev;
+ struct stf_isp_dev isp_dev;
+ struct stf_capture captures[STF_CAPTURE_NUM];
+ struct v4l2_async_notifier notifier;
+ void __iomem *syscon_base;
+ void __iomem *isp_base;
+ struct clk_bulk_data sys_clk[STF_CLK_NUM];
+ int nclks;
+ struct reset_control_bulk_data sys_rst[STF_RST_NUM];
+ int nrsts;
+};
+
+struct stfcamss_async_subdev {
+ struct v4l2_async_connection asd; /* must be first */
+ enum stf_port_num port;
+};
+
+static inline u32 stf_isp_reg_read(struct stfcamss *stfcamss, u32 reg)
+{
+ return ioread32(stfcamss->isp_base + reg);
+}
+
+static inline void stf_isp_reg_write(struct stfcamss *stfcamss,
+ u32 reg, u32 val)
+{
+ iowrite32(val, stfcamss->isp_base + reg);
+}
+
+static inline void stf_isp_reg_write_delay(struct stfcamss *stfcamss,
+ u32 reg, u32 val, u32 delay)
+{
+ iowrite32(val, stfcamss->isp_base + reg);
+ usleep_range(1000 * delay, 1000 * delay + 100);
+}
+
+static inline void stf_isp_reg_set_bit(struct stfcamss *stfcamss,
+ u32 reg, u32 mask, u32 val)
+{
+ u32 value;
+
+ value = ioread32(stfcamss->isp_base + reg) & ~mask;
+ val &= mask;
+ val |= value;
+ iowrite32(val, stfcamss->isp_base + reg);
+}
+
+static inline void stf_isp_reg_set(struct stfcamss *stfcamss, u32 reg, u32 mask)
+{
+ iowrite32(ioread32(stfcamss->isp_base + reg) | mask,
+ stfcamss->isp_base + reg);
+}
+
+static inline u32 stf_syscon_reg_read(struct stfcamss *stfcamss, u32 reg)
+{
+ return ioread32(stfcamss->syscon_base + reg);
+}
+
+static inline void stf_syscon_reg_write(struct stfcamss *stfcamss,
+ u32 reg, u32 val)
+{
+ iowrite32(val, stfcamss->syscon_base + reg);
+}
+
+static inline void stf_syscon_reg_set_bit(struct stfcamss *stfcamss,
+ u32 reg, u32 bit_mask)
+{
+ u32 value;
+
+ value = ioread32(stfcamss->syscon_base + reg);
+ iowrite32(value | bit_mask, stfcamss->syscon_base + reg);
+}
+
+static inline void stf_syscon_reg_clear_bit(struct stfcamss *stfcamss,
+ u32 reg, u32 bit_mask)
+{
+ u32 value;
+
+ value = ioread32(stfcamss->syscon_base + reg);
+ iowrite32(value & ~bit_mask, stfcamss->syscon_base + reg);
+}
+#endif /* STF_CAMSS_H */
diff --git a/drivers/staging/media/starfive/camss/stf-capture.c b/drivers/staging/media/starfive/camss/stf-capture.c
new file mode 100644
index 0000000000000..70c24b050a1b5
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-capture.c
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * stf_capture.c
+ *
+ * StarFive Camera Subsystem - capture device
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+
+#include "stf-camss.h"
+
+static const char * const stf_cap_names[] = {
+ "capture_raw",
+ "capture_yuv",
+};
+
+static const struct stfcamss_format_info stf_wr_fmts[] = {
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SRGGB10,
+ .planes = 1,
+ .vsub = { 1 },
+ .bpp = 10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SGRBG10,
+ .planes = 1,
+ .vsub = { 1 },
+ .bpp = 10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SGBRG10,
+ .planes = 1,
+ .vsub = { 1 },
+ .bpp = 10,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SBGGR10,
+ .planes = 1,
+ .vsub = { 1 },
+ .bpp = 10,
+ },
+};
+
+static const struct stfcamss_format_info stf_isp_fmts[] = {
+ {
+ .code = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .planes = 2,
+ .vsub = { 1, 2 },
+ .bpp = 8,
+ },
+};
+
+static inline struct stf_capture *to_stf_capture(struct stfcamss_video *video)
+{
+ return container_of(video, struct stf_capture, video);
+}
+
+static void stf_set_raw_addr(struct stfcamss *stfcamss, dma_addr_t addr)
+{
+ stf_syscon_reg_write(stfcamss, VIN_START_ADDR_O, (long)addr);
+ stf_syscon_reg_write(stfcamss, VIN_START_ADDR_N, (long)addr);
+}
+
+static void stf_set_yuv_addr(struct stfcamss *stfcamss,
+ dma_addr_t y_addr, dma_addr_t uv_addr)
+{
+ stf_isp_reg_write(stfcamss, ISP_REG_Y_PLANE_START_ADDR, y_addr);
+ stf_isp_reg_write(stfcamss, ISP_REG_UV_PLANE_START_ADDR, uv_addr);
+}
+
+static void stf_init_addrs(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stf_v_buf *output = &cap->buffers;
+ dma_addr_t addr0, addr1;
+
+ output->active_buf = 0;
+
+ if (!output->buf[0])
+ return;
+
+ addr0 = output->buf[0]->addr[0];
+ addr1 = output->buf[0]->addr[1];
+
+ if (cap->type == STF_CAPTURE_RAW)
+ stf_set_raw_addr(video->stfcamss, addr0);
+ else if (cap->type == STF_CAPTURE_YUV)
+ stf_set_yuv_addr(video->stfcamss, addr0, addr1);
+}
+
+static struct stfcamss_buffer *stf_buf_get_pending(struct stf_v_buf *output)
+{
+ struct stfcamss_buffer *buffer = NULL;
+
+ if (!list_empty(&output->pending_bufs)) {
+ buffer = list_first_entry(&output->pending_bufs,
+ struct stfcamss_buffer,
+ queue);
+ list_del(&buffer->queue);
+ }
+
+ return buffer;
+}
+
+static void stf_cap_s_cfg(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stf_v_buf *output = &cap->buffers;
+ unsigned long flags;
+
+ spin_lock_irqsave(&output->lock, flags);
+
+ output->state = STF_OUTPUT_IDLE;
+ output->buf[0] = stf_buf_get_pending(output);
+
+ if (!output->buf[0] && output->buf[1]) {
+ output->buf[0] = output->buf[1];
+ output->buf[1] = NULL;
+ }
+
+ if (output->buf[0])
+ output->state = STF_OUTPUT_SINGLE;
+
+ output->sequence = 0;
+ stf_init_addrs(video);
+
+ spin_unlock_irqrestore(&output->lock, flags);
+}
+
+static int stf_cap_s_cleanup(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stf_v_buf *output = &cap->buffers;
+ unsigned long flags;
+
+ spin_lock_irqsave(&output->lock, flags);
+
+ output->state = STF_OUTPUT_OFF;
+
+ spin_unlock_irqrestore(&output->lock, flags);
+
+ return 0;
+}
+
+static void stf_wr_data_en(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stfcamss *stfcamss = cap->video.stfcamss;
+
+ stf_syscon_reg_set_bit(stfcamss, VIN_CHANNEL_SEL_EN, U0_VIN_AXIWR0_EN);
+}
+
+static void stf_wr_irq_enable(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stfcamss *stfcamss = cap->video.stfcamss;
+
+ stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_M);
+}
+
+static void stf_wr_irq_disable(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stfcamss *stfcamss = cap->video.stfcamss;
+
+ stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
+ stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
+ stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_M);
+}
+
+static void stf_channel_set(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stfcamss *stfcamss = cap->video.stfcamss;
+ u32 val;
+
+ if (cap->type == STF_CAPTURE_RAW) {
+ val = stf_syscon_reg_read(stfcamss, VIN_CHANNEL_SEL_EN);
+ val &= ~U0_VIN_CHANNEL_SEL_MASK;
+ val |= CHANNEL(0);
+ stf_syscon_reg_write(stfcamss, VIN_CHANNEL_SEL_EN, val);
+
+ val = stf_syscon_reg_read(stfcamss, VIN_INRT_PIX_CFG);
+ val &= ~U0_VIN_PIX_CT_MASK;
+ val |= PIX_CT(1);
+
+ val &= ~U0_VIN_PIXEL_HEIGH_BIT_SEL_MAKS;
+ val |= PIXEL_HEIGH_BIT_SEL(0);
+
+ val &= ~U0_VIN_PIX_CNT_END_MASK;
+ val |= PIX_CNT_END(IMAGE_MAX_WIDTH / 4 - 1);
+
+ stf_syscon_reg_write(stfcamss, VIN_INRT_PIX_CFG, val);
+ } else if (cap->type == STF_CAPTURE_YUV) {
+ val = stf_syscon_reg_read(stfcamss, VIN_CFG_REG);
+ val &= ~U0_VIN_MIPI_BYTE_EN_ISP0_MASK;
+ val |= U0_VIN_MIPI_BYTE_EN_ISP0(0);
+
+ val &= ~U0_VIN_MIPI_CHANNEL_SEL0_MASK;
+ val |= U0_VIN_MIPI_CHANNEL_SEL0(0);
+
+ val &= ~U0_VIN_PIX_NUM_MASK;
+ val |= U0_VIN_PIX_NUM(0);
+
+ val &= ~U0_VIN_P_I_MIPI_HAEDER_EN0_MASK;
+ val |= U0_VIN_P_I_MIPI_HAEDER_EN0(1);
+
+ stf_syscon_reg_write(stfcamss, VIN_CFG_REG, val);
+ }
+}
+
+static void stf_capture_start(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+
+ stf_channel_set(video);
+ if (cap->type == STF_CAPTURE_RAW) {
+ stf_wr_irq_enable(video);
+ stf_wr_data_en(video);
+ }
+
+ stf_cap_s_cfg(video);
+}
+
+static void stf_capture_stop(struct stfcamss_video *video)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+
+ if (cap->type == STF_CAPTURE_RAW)
+ stf_wr_irq_disable(video);
+
+ stf_cap_s_cleanup(video);
+}
+
+static void stf_capture_init(struct stfcamss *stfcamss, struct stf_capture *cap)
+{
+ cap->buffers.state = STF_OUTPUT_OFF;
+ cap->buffers.buf[0] = NULL;
+ cap->buffers.buf[1] = NULL;
+ cap->buffers.active_buf = 0;
+ atomic_set(&cap->buffers.frame_skip, 4);
+ INIT_LIST_HEAD(&cap->buffers.pending_bufs);
+ INIT_LIST_HEAD(&cap->buffers.ready_bufs);
+ spin_lock_init(&cap->buffers.lock);
+
+ cap->video.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cap->video.stfcamss = stfcamss;
+ cap->video.bpl_alignment = 16 * 8;
+
+ if (cap->type == STF_CAPTURE_RAW) {
+ cap->video.formats = stf_wr_fmts;
+ cap->video.nformats = ARRAY_SIZE(stf_wr_fmts);
+ cap->video.bpl_alignment = 8;
+ } else if (cap->type == STF_CAPTURE_YUV) {
+ cap->video.formats = stf_isp_fmts;
+ cap->video.nformats = ARRAY_SIZE(stf_isp_fmts);
+ cap->video.bpl_alignment = 1;
+ }
+}
+
+static void stf_buf_add_ready(struct stf_v_buf *output,
+ struct stfcamss_buffer *buffer)
+{
+ INIT_LIST_HEAD(&buffer->queue);
+ list_add_tail(&buffer->queue, &output->ready_bufs);
+}
+
+static struct stfcamss_buffer *stf_buf_get_ready(struct stf_v_buf *output)
+{
+ struct stfcamss_buffer *buffer = NULL;
+
+ if (!list_empty(&output->ready_bufs)) {
+ buffer = list_first_entry(&output->ready_bufs,
+ struct stfcamss_buffer,
+ queue);
+ list_del(&buffer->queue);
+ }
+
+ return buffer;
+}
+
+static void stf_buf_add_pending(struct stf_v_buf *output,
+ struct stfcamss_buffer *buffer)
+{
+ INIT_LIST_HEAD(&buffer->queue);
+ list_add_tail(&buffer->queue, &output->pending_bufs);
+}
+
+static void stf_buf_update_on_last(struct stf_v_buf *output)
+{
+ switch (output->state) {
+ case STF_OUTPUT_CONTINUOUS:
+ output->state = STF_OUTPUT_SINGLE;
+ output->active_buf = !output->active_buf;
+ break;
+ case STF_OUTPUT_SINGLE:
+ output->state = STF_OUTPUT_STOPPING;
+ break;
+ default:
+ break;
+ }
+}
+
+static void stf_buf_update_on_next(struct stf_v_buf *output)
+{
+ switch (output->state) {
+ case STF_OUTPUT_CONTINUOUS:
+ output->active_buf = !output->active_buf;
+ break;
+ case STF_OUTPUT_SINGLE:
+ default:
+ break;
+ }
+}
+
+static void stf_buf_update_on_new(struct stfcamss_video *video,
+ struct stfcamss_buffer *new_buf)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stf_v_buf *output = &cap->buffers;
+
+ switch (output->state) {
+ case STF_OUTPUT_SINGLE:
+ stf_buf_add_pending(output, new_buf);
+ break;
+ case STF_OUTPUT_IDLE:
+ if (!output->buf[0]) {
+ output->buf[0] = new_buf;
+ stf_init_addrs(video);
+ output->state = STF_OUTPUT_SINGLE;
+ } else {
+ stf_buf_add_pending(output, new_buf);
+ }
+ break;
+ case STF_OUTPUT_STOPPING:
+ if (output->last_buffer) {
+ output->buf[output->active_buf] = output->last_buffer;
+ output->last_buffer = NULL;
+ }
+
+ output->state = STF_OUTPUT_SINGLE;
+ stf_buf_add_pending(output, new_buf);
+ break;
+ case STF_OUTPUT_CONTINUOUS:
+ default:
+ stf_buf_add_pending(output, new_buf);
+ break;
+ }
+}
+
+static void stf_buf_flush(struct stf_v_buf *output, enum vb2_buffer_state state)
+{
+ struct stfcamss_buffer *buf;
+ struct stfcamss_buffer *t;
+
+ list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->queue);
+ }
+ list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->queue);
+ }
+}
+
+static void stf_buf_done(struct stf_v_buf *output)
+{
+ struct stfcamss_buffer *ready_buf;
+ u64 ts = ktime_get_ns();
+ unsigned long flags;
+
+ if (output->state == STF_OUTPUT_OFF ||
+ output->state == STF_OUTPUT_RESERVED)
+ return;
+
+ spin_lock_irqsave(&output->lock, flags);
+
+ while ((ready_buf = stf_buf_get_ready(output))) {
+ ready_buf->vb.vb2_buf.timestamp = ts;
+ ready_buf->vb.sequence = output->sequence++;
+
+ vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ spin_unlock_irqrestore(&output->lock, flags);
+}
+
+static void stf_change_buffer(struct stf_v_buf *output)
+{
+ struct stf_capture *cap = container_of(output, struct stf_capture,
+ buffers);
+ struct stfcamss *stfcamss = cap->video.stfcamss;
+ struct stfcamss_buffer *ready_buf;
+ dma_addr_t *new_addr;
+ unsigned long flags;
+ u32 active_index;
+
+ if (output->state == STF_OUTPUT_OFF ||
+ output->state == STF_OUTPUT_STOPPING ||
+ output->state == STF_OUTPUT_RESERVED ||
+ output->state == STF_OUTPUT_IDLE)
+ return;
+
+ spin_lock_irqsave(&output->lock, flags);
+
+ active_index = output->active_buf;
+
+ ready_buf = output->buf[active_index];
+ if (!ready_buf) {
+ dev_dbg(stfcamss->dev, "missing ready buf %d %d.\n",
+ active_index, output->state);
+ active_index = !active_index;
+ ready_buf = output->buf[active_index];
+ if (!ready_buf) {
+ dev_dbg(stfcamss->dev,
+ "missing ready buf2 %d %d.\n",
+ active_index, output->state);
+ goto out_unlock;
+ }
+ }
+
+ /* Get next buffer */
+ output->buf[active_index] = stf_buf_get_pending(output);
+ if (!output->buf[active_index]) {
+ new_addr = ready_buf->addr;
+ stf_buf_update_on_last(output);
+ } else {
+ new_addr = output->buf[active_index]->addr;
+ stf_buf_update_on_next(output);
+ }
+
+ if (output->state == STF_OUTPUT_STOPPING) {
+ output->last_buffer = ready_buf;
+ } else {
+ if (cap->type == STF_CAPTURE_RAW)
+ stf_set_raw_addr(stfcamss, new_addr[0]);
+ else if (cap->type == STF_CAPTURE_YUV)
+ stf_set_yuv_addr(stfcamss, new_addr[0], new_addr[1]);
+
+ stf_buf_add_ready(output, ready_buf);
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&output->lock, flags);
+}
+
+irqreturn_t stf_wr_irq_handler(int irq, void *priv)
+{
+ struct stfcamss *stfcamss = priv;
+ struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_RAW];
+
+ if (atomic_dec_if_positive(&cap->buffers.frame_skip) < 0) {
+ stf_change_buffer(&cap->buffers);
+ stf_buf_done(&cap->buffers);
+ }
+
+ stf_syscon_reg_set_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
+ stf_syscon_reg_clear_bit(stfcamss, VIN_INRT_PIX_CFG, U0_VIN_INTR_CLEAN);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t stf_isp_irq_handler(int irq, void *priv)
+{
+ struct stfcamss *stfcamss = priv;
+ struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_YUV];
+ u32 status;
+
+ status = stf_isp_reg_read(stfcamss, ISP_REG_ISP_CTRL_0);
+ if (status & ISPC_ISP) {
+ if (status & ISPC_ENUO)
+ stf_buf_done(&cap->buffers);
+
+ stf_isp_reg_write(stfcamss, ISP_REG_ISP_CTRL_0,
+ (status & ~ISPC_INT_ALL_MASK) |
+ ISPC_ISP | ISPC_CSI | ISPC_SC);
+ }
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t stf_line_irq_handler(int irq, void *priv)
+{
+ struct stfcamss *stfcamss = priv;
+ struct stf_capture *cap = &stfcamss->captures[STF_CAPTURE_YUV];
+ u32 status;
+
+ status = stf_isp_reg_read(stfcamss, ISP_REG_ISP_CTRL_0);
+ if (status & ISPC_LINE) {
+ if (atomic_dec_if_positive(&cap->buffers.frame_skip) < 0) {
+ if ((status & ISPC_ENUO))
+ stf_change_buffer(&cap->buffers);
+ }
+
+ stf_isp_reg_set_bit(stfcamss, ISP_REG_CSIINTS,
+ CSI_INTS_MASK, CSI_INTS(0x3));
+ stf_isp_reg_set_bit(stfcamss, ISP_REG_IESHD,
+ SHAD_UP_M | SHAD_UP_EN, 0x3);
+
+ stf_isp_reg_write(stfcamss, ISP_REG_ISP_CTRL_0,
+ (status & ~ISPC_INT_ALL_MASK) | ISPC_LINE);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int stf_queue_buffer(struct stfcamss_video *video,
+ struct stfcamss_buffer *buf)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stf_v_buf *v_bufs = &cap->buffers;
+ unsigned long flags;
+
+ spin_lock_irqsave(&v_bufs->lock, flags);
+ stf_buf_update_on_new(video, buf);
+ spin_unlock_irqrestore(&v_bufs->lock, flags);
+
+ return 0;
+}
+
+static int stf_flush_buffers(struct stfcamss_video *video,
+ enum vb2_buffer_state state)
+{
+ struct stf_capture *cap = to_stf_capture(video);
+ struct stf_v_buf *v_bufs = &cap->buffers;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&v_bufs->lock, flags);
+
+ stf_buf_flush(v_bufs, state);
+
+ for (i = 0; i < ARRAY_SIZE(v_bufs->buf); i++) {
+ if (v_bufs->buf[i])
+ vb2_buffer_done(&v_bufs->buf[i]->vb.vb2_buf, state);
+
+ v_bufs->buf[i] = NULL;
+ }
+
+ if (v_bufs->last_buffer) {
+ vb2_buffer_done(&v_bufs->last_buffer->vb.vb2_buf, state);
+ v_bufs->last_buffer = NULL;
+ }
+
+ spin_unlock_irqrestore(&v_bufs->lock, flags);
+ return 0;
+}
+
+static const struct stfcamss_video_ops stf_capture_ops = {
+ .queue_buffer = stf_queue_buffer,
+ .flush_buffers = stf_flush_buffers,
+ .start_streaming = stf_capture_start,
+ .stop_streaming = stf_capture_stop,
+};
+
+static void stf_capture_unregister_one(struct stf_capture *cap)
+{
+ if (!video_is_registered(&cap->video.vdev))
+ return;
+
+ media_entity_cleanup(&cap->video.vdev.entity);
+ vb2_video_unregister_device(&cap->video.vdev);
+}
+
+void stf_capture_unregister(struct stfcamss *stfcamss)
+{
+ struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW];
+ struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV];
+
+ stf_capture_unregister_one(cap_raw);
+ stf_capture_unregister_one(cap_yuv);
+}
+
+int stf_capture_register(struct stfcamss *stfcamss,
+ struct v4l2_device *v4l2_dev)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(stfcamss->captures); i++) {
+ struct stf_capture *capture = &stfcamss->captures[i];
+
+ capture->type = i;
+ capture->video.ops = &stf_capture_ops;
+ stf_capture_init(stfcamss, capture);
+
+ ret = stf_video_register(&capture->video, v4l2_dev,
+ stf_cap_names[i]);
+ if (ret < 0) {
+ dev_err(stfcamss->dev,
+ "Failed to register video node: %d\n", ret);
+ stf_capture_unregister(stfcamss);
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/media/starfive/camss/stf-capture.h b/drivers/staging/media/starfive/camss/stf-capture.h
new file mode 100644
index 0000000000000..2f9740b7e500a
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-capture.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * stf_capture.h
+ *
+ * Starfive Camera Subsystem driver
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef STF_CAPTURE_H
+#define STF_CAPTURE_H
+
+#include "stf-video.h"
+
+#define VIN_CHANNEL_SEL_EN 0x14
+#define VIN_START_ADDR_N 0x18
+#define VIN_INRT_PIX_CFG 0x1c
+#define VIN_START_ADDR_O 0x20
+#define VIN_CFG_REG 0x24
+
+#define U0_VIN_CNFG_AXI_DVP_EN BIT(2)
+
+#define U0_VIN_CHANNEL_SEL_MASK GENMASK(3, 0)
+#define U0_VIN_AXIWR0_EN BIT(4)
+#define CHANNEL(x) ((x) << 0)
+
+#define U0_VIN_INTR_CLEAN BIT(0)
+#define U0_VIN_INTR_M BIT(1)
+#define U0_VIN_PIX_CNT_END_MASK GENMASK(12, 2)
+#define U0_VIN_PIX_CT_MASK GENMASK(14, 13)
+#define U0_VIN_PIXEL_HEIGH_BIT_SEL_MAKS GENMASK(16, 15)
+
+#define PIX_CNT_END(x) ((x) << 2)
+#define PIX_CT(x) ((x) << 13)
+#define PIXEL_HEIGH_BIT_SEL(x) ((x) << 15)
+
+#define U0_VIN_CNFG_DVP_HS_POS BIT(1)
+#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2)
+#define U0_VIN_CNFG_DVP_VS_POS BIT(3)
+#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4)
+#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5)
+#define U0_VIN_MIPI_BYTE_EN_ISP0(n) ((n) << 6)
+#define U0_VIN_MIPI_CHANNEL_SEL0(n) ((n) << 8)
+#define U0_VIN_P_I_MIPI_HAEDER_EN0(n) ((n) << 12)
+#define U0_VIN_PIX_NUM(n) ((n) << 13)
+#define U0_VIN_MIPI_BYTE_EN_ISP0_MASK GENMASK(7, 6)
+#define U0_VIN_MIPI_CHANNEL_SEL0_MASK GENMASK(11, 8)
+#define U0_VIN_P_I_MIPI_HAEDER_EN0_MASK BIT(12)
+#define U0_VIN_PIX_NUM_MASK GENMASK(16, 13)
+
+enum stf_v_state {
+ STF_OUTPUT_OFF,
+ STF_OUTPUT_RESERVED,
+ STF_OUTPUT_SINGLE,
+ STF_OUTPUT_CONTINUOUS,
+ STF_OUTPUT_IDLE,
+ STF_OUTPUT_STOPPING
+};
+
+struct stf_v_buf {
+ int active_buf;
+ struct stfcamss_buffer *buf[2];
+ struct stfcamss_buffer *last_buffer;
+ struct list_head pending_bufs;
+ struct list_head ready_bufs;
+ enum stf_v_state state;
+ unsigned int sequence;
+ /* protects the above member variables */
+ spinlock_t lock;
+ atomic_t frame_skip;
+};
+
+struct stf_capture {
+ struct stfcamss_video video;
+ struct stf_v_buf buffers;
+ enum stf_capture_type type;
+};
+
+irqreturn_t stf_wr_irq_handler(int irq, void *priv);
+irqreturn_t stf_isp_irq_handler(int irq, void *priv);
+irqreturn_t stf_line_irq_handler(int irq, void *priv);
+int stf_capture_register(struct stfcamss *stfcamss,
+ struct v4l2_device *v4l2_dev);
+void stf_capture_unregister(struct stfcamss *stfcamss);
+
+#endif
diff --git a/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c b/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c
new file mode 100644
index 0000000000000..c34631ff94227
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * stf_isp_hw_ops.c
+ *
+ * Register interface file for StarFive ISP driver
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ *
+ */
+
+#include "stf-camss.h"
+
+static void stf_isp_config_obc(struct stfcamss *stfcamss)
+{
+ u32 reg_val, reg_add;
+
+ stf_isp_reg_write(stfcamss, ISP_REG_OBC_CFG, OBC_W_H(11) | OBC_W_W(11));
+
+ reg_val = GAIN_D_POINT(0x40) | GAIN_C_POINT(0x40) |
+ GAIN_B_POINT(0x40) | GAIN_A_POINT(0x40);
+ for (reg_add = ISP_REG_OBCG_CFG_0; reg_add <= ISP_REG_OBCG_CFG_3;) {
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ }
+
+ reg_val = OFFSET_D_POINT(0) | OFFSET_C_POINT(0) |
+ OFFSET_B_POINT(0) | OFFSET_A_POINT(0);
+ for (reg_add = ISP_REG_OBCO_CFG_0; reg_add <= ISP_REG_OBCO_CFG_3;) {
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ }
+}
+
+static void stf_isp_config_oecf(struct stfcamss *stfcamss)
+{
+ u32 reg_add, par_val;
+ u16 par_h, par_l;
+
+ par_h = 0x10; par_l = 0;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG0; reg_add <= ISP_REG_OECF_Y3_CFG0;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x40; par_l = 0x20;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG1; reg_add <= ISP_REG_OECF_Y3_CFG1;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x80; par_l = 0x60;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG2; reg_add <= ISP_REG_OECF_Y3_CFG2;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0xc0; par_l = 0xa0;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG3; reg_add <= ISP_REG_OECF_Y3_CFG3;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x100; par_l = 0xe0;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG4; reg_add <= ISP_REG_OECF_Y3_CFG4;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x200; par_l = 0x180;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG5; reg_add <= ISP_REG_OECF_Y3_CFG5;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x300; par_l = 0x280;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG6; reg_add <= ISP_REG_OECF_Y3_CFG6;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x3fe; par_l = 0x380;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_X0_CFG7; reg_add <= ISP_REG_OECF_Y3_CFG7;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 0x20;
+ }
+
+ par_h = 0x80; par_l = 0x80;
+ par_val = OCEF_PAR_H(par_h) | OCEF_PAR_L(par_l);
+ for (reg_add = ISP_REG_OECF_S0_CFG0; reg_add <= ISP_REG_OECF_S3_CFG7;) {
+ stf_isp_reg_write(stfcamss, reg_add, par_val);
+ reg_add += 4;
+ }
+}
+
+static void stf_isp_config_lccf(struct stfcamss *stfcamss)
+{
+ u32 reg_add;
+
+ stf_isp_reg_write(stfcamss, ISP_REG_LCCF_CFG_0,
+ Y_DISTANCE(0x21C) | X_DISTANCE(0x3C0));
+ stf_isp_reg_write(stfcamss, ISP_REG_LCCF_CFG_1, LCCF_MAX_DIS(0xb));
+
+ for (reg_add = ISP_REG_LCCF_CFG_2; reg_add <= ISP_REG_LCCF_CFG_5;) {
+ stf_isp_reg_write(stfcamss, reg_add,
+ LCCF_F2_PAR(0x0) | LCCF_F1_PAR(0x0));
+ reg_add += 4;
+ }
+}
+
+static void stf_isp_config_awb(struct stfcamss *stfcamss)
+{
+ u32 reg_val, reg_add;
+ u16 symbol_h, symbol_l;
+
+ symbol_h = 0x0; symbol_l = 0x0;
+ reg_val = AWB_X_SYMBOL_H(symbol_h) | AWB_X_SYMBOL_L(symbol_l);
+
+ for (reg_add = ISP_REG_AWB_X0_CFG_0; reg_add <= ISP_REG_AWB_X3_CFG_1;) {
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ }
+
+ symbol_h = 0x0, symbol_l = 0x0;
+ reg_val = AWB_Y_SYMBOL_H(symbol_h) | AWB_Y_SYMBOL_L(symbol_l);
+
+ for (reg_add = ISP_REG_AWB_Y0_CFG_0; reg_add <= ISP_REG_AWB_Y3_CFG_1;) {
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ }
+
+ symbol_h = 0x80, symbol_l = 0x80;
+ reg_val = AWB_S_SYMBOL_H(symbol_h) | AWB_S_SYMBOL_L(symbol_l);
+
+ for (reg_add = ISP_REG_AWB_S0_CFG_0; reg_add <= ISP_REG_AWB_S3_CFG_1;) {
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ }
+}
+
+static void stf_isp_config_grgb(struct stfcamss *stfcamss)
+{
+ stf_isp_reg_write(stfcamss, ISP_REG_ICTC,
+ GF_MODE(1) | MAXGT(0x140) | MINGT(0x40));
+ stf_isp_reg_write(stfcamss, ISP_REG_IDBC, BADGT(0x200) | BADXT(0x200));
+}
+
+static void stf_isp_config_cfa(struct stfcamss *stfcamss)
+{
+ stf_isp_reg_write(stfcamss, ISP_REG_RAW_FORMAT_CFG,
+ SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) | SMY3(2) |
+ SMY2(3) | SMY1(2) | SMY0(3));
+ stf_isp_reg_write(stfcamss, ISP_REG_ICFAM, CROSS_COV(3) | HV_W(2));
+}
+
+static void stf_isp_config_ccm(struct stfcamss *stfcamss)
+{
+ u32 reg_add;
+
+ stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_0, DNRM_F(6) | CCM_M_DAT(0));
+
+ for (reg_add = ISP_REG_ICAMD_12; reg_add <= ISP_REG_ICAMD_20;) {
+ stf_isp_reg_write(stfcamss, reg_add, CCM_M_DAT(0x80));
+ reg_add += 0x10;
+ }
+
+ stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_24, CCM_M_DAT(0x700));
+ stf_isp_reg_write(stfcamss, ISP_REG_ICAMD_25, CCM_M_DAT(0x200));
+}
+
+static void stf_isp_config_gamma(struct stfcamss *stfcamss)
+{
+ u32 reg_val, reg_add;
+ u16 gamma_slope_v, gamma_v;
+
+ gamma_slope_v = 0x2400; gamma_v = 0x0;
+ reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
+ stf_isp_reg_write(stfcamss, ISP_REG_GAMMA_VAL0, reg_val);
+
+ gamma_slope_v = 0x800; gamma_v = 0x20;
+ for (reg_add = ISP_REG_GAMMA_VAL1; reg_add <= ISP_REG_GAMMA_VAL7;) {
+ reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ gamma_v += 0x20;
+ }
+
+ gamma_v = 0x100;
+ for (reg_add = ISP_REG_GAMMA_VAL8; reg_add <= ISP_REG_GAMMA_VAL13;) {
+ reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
+ stf_isp_reg_write(stfcamss, reg_add, reg_val);
+ reg_add += 4;
+ gamma_v += 0x80;
+ }
+
+ gamma_v = 0x3fe;
+ reg_val = GAMMA_S_VAL(gamma_slope_v) | GAMMA_VAL(gamma_v);
+ stf_isp_reg_write(stfcamss, ISP_REG_GAMMA_VAL14, reg_val);
+}
+
+static void stf_isp_config_r2y(struct stfcamss *stfcamss)
+{
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_0, 0x4C);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_1, 0x97);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_2, 0x1d);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_3, 0x1d5);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_4, 0x1ac);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_5, 0x80);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_6, 0x80);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_7, 0x194);
+ stf_isp_reg_write(stfcamss, ISP_REG_R2Y_8, 0x1ec);
+}
+
+static void stf_isp_config_y_curve(struct stfcamss *stfcamss)
+{
+ u32 reg_add;
+ u16 y_curve;
+
+ y_curve = 0x0;
+ for (reg_add = ISP_REG_YCURVE_0; reg_add <= ISP_REG_YCURVE_63;) {
+ stf_isp_reg_write(stfcamss, reg_add, y_curve);
+ reg_add += 4;
+ y_curve += 0x10;
+ }
+}
+
+static void stf_isp_config_sharpen(struct stfcamss *sc)
+{
+ u32 reg_add;
+
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN0, S_DELTA(0x7) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN1, S_DELTA(0x18) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN2, S_DELTA(0x80) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN3, S_DELTA(0x100) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN4, S_DELTA(0x10) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN5, S_DELTA(0x60) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN6, S_DELTA(0x100) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN7, S_DELTA(0x190) | S_WEIGHT(0xf));
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN8, S_DELTA(0x0) | S_WEIGHT(0xf));
+
+ for (reg_add = ISP_REG_SHARPEN9; reg_add <= ISP_REG_SHARPEN14;) {
+ stf_isp_reg_write(sc, reg_add, S_WEIGHT(0xf));
+ reg_add += 4;
+ }
+
+ for (reg_add = ISP_REG_SHARPEN_FS0; reg_add <= ISP_REG_SHARPEN_FS5;) {
+ stf_isp_reg_write(sc, reg_add, S_FACTOR(0x10) | S_SLOPE(0x0));
+ reg_add += 4;
+ }
+
+ stf_isp_reg_write(sc, ISP_REG_SHARPEN_WN,
+ PDIRF(0x8) | NDIRF(0x8) | WSUM(0xd7c));
+ stf_isp_reg_write(sc, ISP_REG_IUVS1, UVDIFF2(0xC0) | UVDIFF1(0x40));
+ stf_isp_reg_write(sc, ISP_REG_IUVS2, UVF(0xff) | UVSLOPE(0x0));
+ stf_isp_reg_write(sc, ISP_REG_IUVCKS1,
+ UVCKDIFF2(0xa0) | UVCKDIFF1(0x40));
+}
+
+static void stf_isp_config_dnyuv(struct stfcamss *stfcamss)
+{
+ u32 reg_val;
+
+ reg_val = YUVSW5(7) | YUVSW4(7) | YUVSW3(7) | YUVSW2(7) |
+ YUVSW1(7) | YUVSW0(7);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YSWR0, reg_val);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CSWR0, reg_val);
+
+ reg_val = YUVSW3(7) | YUVSW2(7) | YUVSW1(7) | YUVSW0(7);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YSWR1, reg_val);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CSWR1, reg_val);
+
+ reg_val = CURVE_D_H(0x60) | CURVE_D_L(0x40);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR0, reg_val);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR0, reg_val);
+
+ reg_val = CURVE_D_H(0xd8) | CURVE_D_L(0x90);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR1, reg_val);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR1, reg_val);
+
+ reg_val = CURVE_D_H(0x1e6) | CURVE_D_L(0x144);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_YDR2, reg_val);
+ stf_isp_reg_write(stfcamss, ISP_REG_DNYUV_CDR2, reg_val);
+}
+
+static void stf_isp_config_sat(struct stfcamss *stfcamss)
+{
+ stf_isp_reg_write(stfcamss, ISP_REG_CS_GAIN, CMAD(0x0) | CMAB(0x100));
+ stf_isp_reg_write(stfcamss, ISP_REG_CS_THRESHOLD, CMD(0x1f) | CMB(0x1));
+ stf_isp_reg_write(stfcamss, ISP_REG_CS_OFFSET, VOFF(0x0) | UOFF(0x0));
+ stf_isp_reg_write(stfcamss, ISP_REG_CS_HUE_F, SIN(0x0) | COS(0x100));
+ stf_isp_reg_write(stfcamss, ISP_REG_CS_SCALE, 0x8);
+ stf_isp_reg_write(stfcamss, ISP_REG_YADJ0, YOIR(0x401) | YIMIN(0x1));
+ stf_isp_reg_write(stfcamss, ISP_REG_YADJ1, YOMAX(0x3ff) | YOMIN(0x1));
+}
+
+int stf_isp_reset(struct stf_isp_dev *isp_dev)
+{
+ stf_isp_reg_set_bit(isp_dev->stfcamss, ISP_REG_ISP_CTRL_0,
+ ISPC_RST_MASK, ISPC_RST);
+ stf_isp_reg_set_bit(isp_dev->stfcamss, ISP_REG_ISP_CTRL_0,
+ ISPC_RST_MASK, 0);
+
+ return 0;
+}
+
+void stf_isp_init_cfg(struct stf_isp_dev *isp_dev)
+{
+ stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_DC_CFG_1, DC_AXI_ID(0x0));
+ stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_DEC_CFG,
+ DEC_V_KEEP(0x0) |
+ DEC_V_PERIOD(0x0) |
+ DEC_H_KEEP(0x0) |
+ DEC_H_PERIOD(0x0));
+
+ stf_isp_config_obc(isp_dev->stfcamss);
+ stf_isp_config_oecf(isp_dev->stfcamss);
+ stf_isp_config_lccf(isp_dev->stfcamss);
+ stf_isp_config_awb(isp_dev->stfcamss);
+ stf_isp_config_grgb(isp_dev->stfcamss);
+ stf_isp_config_cfa(isp_dev->stfcamss);
+ stf_isp_config_ccm(isp_dev->stfcamss);
+ stf_isp_config_gamma(isp_dev->stfcamss);
+ stf_isp_config_r2y(isp_dev->stfcamss);
+ stf_isp_config_y_curve(isp_dev->stfcamss);
+ stf_isp_config_sharpen(isp_dev->stfcamss);
+ stf_isp_config_dnyuv(isp_dev->stfcamss);
+ stf_isp_config_sat(isp_dev->stfcamss);
+
+ stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_CSI_MODULE_CFG,
+ CSI_DUMP_EN | CSI_SC_EN | CSI_AWB_EN |
+ CSI_LCCF_EN | CSI_OECF_EN | CSI_OBC_EN | CSI_DEC_EN);
+ stf_isp_reg_write(isp_dev->stfcamss, ISP_REG_ISP_CTRL_1,
+ CTRL_SAT(1) | CTRL_DBC | CTRL_CTC | CTRL_YHIST |
+ CTRL_YCURVE | CTRL_BIYUV | CTRL_SCE | CTRL_EE |
+ CTRL_CCE | CTRL_RGE | CTRL_CME | CTRL_AE | CTRL_CE);
+}
+
+static void stf_isp_config_crop(struct stfcamss *stfcamss,
+ struct v4l2_rect *crop)
+{
+ u32 bpp = stfcamss->isp_dev.current_fmt->bpp;
+ u32 val;
+
+ val = VSTART_CAP(crop->top) | HSTART_CAP(crop->left);
+ stf_isp_reg_write(stfcamss, ISP_REG_PIC_CAPTURE_START_CFG, val);
+
+ val = VEND_CAP(crop->height + crop->top - 1) |
+ HEND_CAP(crop->width + crop->left - 1);
+ stf_isp_reg_write(stfcamss, ISP_REG_PIC_CAPTURE_END_CFG, val);
+
+ val = H_ACT_CAP(crop->height) | W_ACT_CAP(crop->width);
+ stf_isp_reg_write(stfcamss, ISP_REG_PIPELINE_XY_SIZE, val);
+
+ val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
+ stf_isp_reg_write(stfcamss, ISP_REG_STRIDE, val);
+}
+
+static void stf_isp_config_raw_fmt(struct stfcamss *stfcamss, u32 mcode)
+{
+ u32 val, val1;
+
+ switch (mcode) {
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ /* 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R */
+ val = SMY13(3) | SMY12(2) | SMY11(3) | SMY10(2) |
+ SMY3(1) | SMY2(0) | SMY1(1) | SMY0(0);
+ val1 = CTRL_SAT(0x0);
+ break;
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ /* 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr */
+ val = SMY13(2) | SMY12(3) | SMY11(2) | SMY10(3) |
+ SMY3(0) | SMY2(1) | SMY1(0) | SMY0(1);
+ val1 = CTRL_SAT(0x2);
+ break;
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ /* 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb */
+ val = SMY13(1) | SMY12(0) | SMY11(1) | SMY10(0) |
+ SMY3(3) | SMY2(2) | SMY1(3) | SMY0(2);
+ val1 = CTRL_SAT(0x3);
+ break;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ /* 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B */
+ val = SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) |
+ SMY3(2) | SMY2(3) | SMY1(2) | SMY0(3);
+ val1 = CTRL_SAT(0x1);
+ break;
+ default:
+ val = SMY13(0) | SMY12(1) | SMY11(0) | SMY10(1) |
+ SMY3(2) | SMY2(3) | SMY1(2) | SMY0(3);
+ val1 = CTRL_SAT(0x1);
+ break;
+ }
+ stf_isp_reg_write(stfcamss, ISP_REG_RAW_FORMAT_CFG, val);
+ stf_isp_reg_set_bit(stfcamss, ISP_REG_ISP_CTRL_1, CTRL_SAT_MASK, val1);
+}
+
+void stf_isp_settings(struct stf_isp_dev *isp_dev,
+ struct v4l2_rect *crop, u32 mcode)
+{
+ struct stfcamss *stfcamss = isp_dev->stfcamss;
+
+ stf_isp_config_crop(stfcamss, crop);
+ stf_isp_config_raw_fmt(stfcamss, mcode);
+
+ stf_isp_reg_set_bit(stfcamss, ISP_REG_DUMP_CFG_1,
+ DUMP_BURST_LEN_MASK | DUMP_SD_MASK,
+ DUMP_BURST_LEN(3));
+
+ stf_isp_reg_write(stfcamss, ISP_REG_ITIIWSR,
+ ITI_HSIZE(IMAGE_MAX_HEIGH) |
+ ITI_WSIZE(IMAGE_MAX_WIDTH));
+ stf_isp_reg_write(stfcamss, ISP_REG_ITIDWLSR, 0x960);
+ stf_isp_reg_write(stfcamss, ISP_REG_ITIDRLSR, 0x960);
+ stf_isp_reg_write(stfcamss, ISP_REG_SENSOR, IMAGER_SEL(1));
+}
+
+void stf_isp_stream_set(struct stf_isp_dev *isp_dev)
+{
+ struct stfcamss *stfcamss = isp_dev->stfcamss;
+
+ stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0,
+ ISPC_ENUO | ISPC_ENLS | ISPC_RST, 10);
+ stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0,
+ ISPC_ENUO | ISPC_ENLS, 10);
+ stf_isp_reg_write(stfcamss, ISP_REG_IESHD, SHAD_UP_M);
+ stf_isp_reg_write_delay(stfcamss, ISP_REG_ISP_CTRL_0,
+ ISPC_ENUO | ISPC_ENLS | ISPC_EN, 10);
+ stf_isp_reg_write_delay(stfcamss, ISP_REG_CSIINTS,
+ CSI_INTS(1) | CSI_SHA_M(4), 10);
+ stf_isp_reg_write_delay(stfcamss, ISP_REG_CSIINTS,
+ CSI_INTS(2) | CSI_SHA_M(4), 10);
+ stf_isp_reg_write_delay(stfcamss, ISP_REG_CSI_INPUT_EN_AND_STATUS,
+ CSI_EN_S, 10);
+}
diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c
new file mode 100644
index 0000000000000..d50616ef351ed
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-isp.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * stf_isp.c
+ *
+ * StarFive Camera Subsystem - ISP Module
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+#include <media/v4l2-rect.h>
+
+#include "stf-camss.h"
+
+#define SINK_FORMATS_INDEX 0
+#define SOURCE_FORMATS_INDEX 1
+
+static int isp_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel);
+
+static const struct stf_isp_format isp_formats_sink[] = {
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+};
+
+static const struct stf_isp_format isp_formats_source[] = {
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, 8 },
+};
+
+static const struct stf_isp_format_table isp_formats_st7110[] = {
+ { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) },
+ { isp_formats_source, ARRAY_SIZE(isp_formats_source) },
+};
+
+static const struct stf_isp_format *
+stf_g_fmt_by_mcode(const struct stf_isp_format_table *fmt_table, u32 mcode)
+{
+ unsigned int i;
+
+ for (i = 0; i < fmt_table->nfmts; i++) {
+ if (fmt_table->fmts[i].code == mcode)
+ return &fmt_table->fmts[i];
+ }
+
+ return NULL;
+}
+
+int stf_isp_init(struct stfcamss *stfcamss)
+{
+ struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
+
+ isp_dev->stfcamss = stfcamss;
+ isp_dev->formats = isp_formats_st7110;
+ isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110);
+ isp_dev->current_fmt = &isp_formats_source[0];
+
+ return 0;
+}
+
+static int isp_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_state *sd_state;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+
+ sd_state = v4l2_subdev_lock_and_get_active_state(sd);
+ fmt = v4l2_subdev_state_get_format(sd_state, STF_ISP_PAD_SINK);
+ crop = v4l2_subdev_state_get_crop(sd_state, STF_ISP_PAD_SRC);
+
+ if (enable) {
+ stf_isp_reset(isp_dev);
+ stf_isp_init_cfg(isp_dev);
+ stf_isp_settings(isp_dev, crop, fmt->code);
+ stf_isp_stream_set(isp_dev);
+ }
+
+ v4l2_subdev_call(isp_dev->source_subdev, video, s_stream, enable);
+
+ v4l2_subdev_unlock_state(sd_state);
+ return 0;
+}
+
+static void isp_try_format(struct stf_isp_dev *isp_dev,
+ struct v4l2_subdev_state *state,
+ unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ const struct stf_isp_format_table *formats;
+
+ if (pad >= STF_ISP_PAD_MAX) {
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ return;
+ }
+
+ if (pad == STF_ISP_PAD_SINK)
+ formats = &isp_dev->formats[SINK_FORMATS_INDEX];
+ else if (pad == STF_ISP_PAD_SRC)
+ formats = &isp_dev->formats[SOURCE_FORMATS_INDEX];
+
+ fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
+ STFCAMSS_FRAME_MAX_WIDTH);
+ fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
+ STFCAMSS_FRAME_MAX_HEIGHT);
+ fmt->height &= ~0x1;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->flags = 0;
+
+ if (!stf_g_fmt_by_mcode(formats, fmt->code))
+ fmt->code = formats->fmts[0].code;
+}
+
+static int isp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+ const struct stf_isp_format_table *formats;
+
+ if (code->pad == STF_ISP_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(isp_formats_sink))
+ return -EINVAL;
+
+ formats = &isp_dev->formats[SINK_FORMATS_INDEX];
+ code->code = formats->fmts[code->index].code;
+ } else {
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ if (code->index >= ARRAY_SIZE(isp_formats_source))
+ return -EINVAL;
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ STF_ISP_PAD_SRC);
+
+ code->code = sink_fmt->code;
+ if (!code->code)
+ return -EINVAL;
+ }
+ code->flags = 0;
+
+ return 0;
+}
+
+static int isp_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
+ if (!format)
+ return -EINVAL;
+
+ isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
+ *format = fmt->format;
+
+ isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
+ fmt->format.code);
+
+ /* Propagate to in crop */
+ if (fmt->pad == STF_ISP_PAD_SINK) {
+ struct v4l2_subdev_selection sel = { 0 };
+
+ /* Reset sink pad compose selection */
+ sel.which = fmt->which;
+ sel.pad = STF_ISP_PAD_SINK;
+ sel.target = V4L2_SEL_TGT_CROP;
+ sel.r.width = fmt->format.width;
+ sel.r.height = fmt->format.height;
+ isp_set_selection(sd, state, &sel);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_rect stf_frame_min_crop = {
+ .width = STFCAMSS_FRAME_MIN_WIDTH,
+ .height = STFCAMSS_FRAME_MIN_HEIGHT,
+ .top = 0,
+ .left = 0,
+};
+
+static void isp_try_crop(struct stf_isp_dev *isp_dev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_rect *crop)
+{
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_state_get_format(state, STF_ISP_PAD_SINK);
+
+ const struct v4l2_rect bounds = {
+ .width = fmt->width,
+ .height = fmt->height,
+ .left = 0,
+ .top = 0,
+ };
+
+ v4l2_rect_set_min_size(crop, &stf_frame_min_crop);
+ v4l2_rect_map_inside(crop, &bounds);
+}
+
+static int isp_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_subdev_format fmt = { 0 };
+ struct v4l2_rect *rect;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (sel->pad == STF_ISP_PAD_SINK) {
+ fmt.format = *v4l2_subdev_state_get_format(state,
+ sel->pad);
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = fmt.format.width;
+ sel->r.height = fmt.format.height;
+ } else if (sel->pad == STF_ISP_PAD_SRC) {
+ rect = v4l2_subdev_state_get_crop(state, sel->pad);
+ sel->r = *rect;
+ }
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ rect = v4l2_subdev_state_get_crop(state, sel->pad);
+ if (!rect)
+ return -EINVAL;
+
+ sel->r = *rect;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int isp_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *rect;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ if (sel->target == V4L2_SEL_TGT_CROP &&
+ sel->pad == STF_ISP_PAD_SINK) {
+ struct v4l2_subdev_selection crop = { 0 };
+
+ rect = v4l2_subdev_state_get_crop(state, sel->pad);
+ if (!rect)
+ return -EINVAL;
+
+ isp_try_crop(isp_dev, state, &sel->r);
+ *rect = sel->r;
+
+ /* Reset source crop selection */
+ crop.which = sel->which;
+ crop.pad = STF_ISP_PAD_SRC;
+ crop.target = V4L2_SEL_TGT_CROP;
+ crop.r = *rect;
+ isp_set_selection(sd, state, &crop);
+ } else if (sel->target == V4L2_SEL_TGT_CROP &&
+ sel->pad == STF_ISP_PAD_SRC) {
+ struct v4l2_subdev_format fmt = { 0 };
+
+ rect = v4l2_subdev_state_get_crop(state, sel->pad);
+ if (!rect)
+ return -EINVAL;
+
+ isp_try_crop(isp_dev, state, &sel->r);
+ *rect = sel->r;
+
+ /* Reset source pad format width and height */
+ fmt.which = sel->which;
+ fmt.pad = STF_ISP_PAD_SRC;
+ fmt.format.width = rect->width;
+ fmt.format.height = rect->height;
+ isp_set_format(sd, state, &fmt);
+ }
+
+ dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%dx%d\n",
+ sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+
+ return 0;
+}
+
+static int isp_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_format format = {
+ .pad = STF_ISP_PAD_SINK,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .format = {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .width = 1920,
+ .height = 1080
+ }
+ };
+
+ return isp_set_format(sd, sd_state, &format);
+}
+
+static const struct v4l2_subdev_video_ops isp_video_ops = {
+ .s_stream = isp_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops isp_pad_ops = {
+ .enum_mbus_code = isp_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = isp_set_format,
+ .get_selection = isp_get_selection,
+ .set_selection = isp_set_selection,
+};
+
+static const struct v4l2_subdev_ops isp_v4l2_ops = {
+ .video = &isp_video_ops,
+ .pad = &isp_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops isp_internal_ops = {
+ .init_state = isp_init_formats,
+};
+
+static const struct media_entity_operations isp_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd = &isp_dev->subdev;
+ struct media_pad *pads = isp_dev->pads;
+ int ret;
+
+ v4l2_subdev_init(sd, &isp_v4l2_ops);
+ sd->internal_ops = &isp_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, ARRAY_SIZE(sd->name), "stf_isp");
+ v4l2_set_subdevdata(sd, isp_dev);
+
+ pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
+ sd->entity.ops = &isp_media_ops;
+ ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads);
+ if (ret) {
+ dev_err(isp_dev->stfcamss->dev,
+ "Failed to init media entity: %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret) {
+ dev_err(isp_dev->stfcamss->dev,
+ "Failed to register subdev: %d\n", ret);
+ goto err_subdev_cleanup;
+ }
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+int stf_isp_unregister(struct stf_isp_dev *isp_dev)
+{
+ v4l2_device_unregister_subdev(&isp_dev->subdev);
+ v4l2_subdev_cleanup(&isp_dev->subdev);
+ media_entity_cleanup(&isp_dev->subdev.entity);
+
+ return 0;
+}
diff --git a/drivers/staging/media/starfive/camss/stf-isp.h b/drivers/staging/media/starfive/camss/stf-isp.h
new file mode 100644
index 0000000000000..955cbb0483637
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-isp.h
@@ -0,0 +1,428 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * stf_isp.h
+ *
+ * StarFive Camera Subsystem - ISP Module
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef STF_ISP_H
+#define STF_ISP_H
+
+#include <media/v4l2-subdev.h>
+
+#include "stf-video.h"
+
+#define ISP_RAW_DATA_BITS 12
+#define SCALER_RATIO_MAX 1
+#define STF_ISP_REG_OFFSET_MAX 0x0fff
+#define STF_ISP_REG_DELAY_MAX 100
+
+/* isp registers */
+#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x000
+#define CSI_SCD_ERR BIT(6)
+#define CSI_ITU656_ERR BIT(4)
+#define CSI_ITU656_F BIT(3)
+#define CSI_SCD_DONE BIT(2)
+#define CSI_BUSY_S BIT(1)
+#define CSI_EN_S BIT(0)
+
+#define ISP_REG_CSIINTS 0x008
+#define CSI_INTS(n) ((n) << 16)
+#define CSI_SHA_M(n) ((n) << 0)
+#define CSI_INTS_MASK GENMASK(17, 16)
+
+#define ISP_REG_CSI_MODULE_CFG 0x010
+#define CSI_DUMP_EN BIT(19)
+#define CSI_VS_EN BIT(18)
+#define CSI_SC_EN BIT(17)
+#define CSI_OBA_EN BIT(16)
+#define CSI_AWB_EN BIT(7)
+#define CSI_LCCF_EN BIT(6)
+#define CSI_OECFHM_EN BIT(5)
+#define CSI_OECF_EN BIT(4)
+#define CSI_LCBQ_EN BIT(3)
+#define CSI_OBC_EN BIT(2)
+#define CSI_DEC_EN BIT(1)
+#define CSI_DC_EN BIT(0)
+
+#define ISP_REG_SENSOR 0x014
+#define DVP_SYNC_POL(n) ((n) << 2)
+#define ITU656_EN(n) ((n) << 1)
+#define IMAGER_SEL(n) ((n) << 0)
+
+#define ISP_REG_RAW_FORMAT_CFG 0x018
+#define SMY13(n) ((n) << 14)
+#define SMY12(n) ((n) << 12)
+#define SMY11(n) ((n) << 10)
+#define SMY10(n) ((n) << 8)
+#define SMY3(n) ((n) << 6)
+#define SMY2(n) ((n) << 4)
+#define SMY1(n) ((n) << 2)
+#define SMY0(n) ((n) << 0)
+
+#define ISP_REG_PIC_CAPTURE_START_CFG 0x01c
+#define VSTART_CAP(n) ((n) << 16)
+#define HSTART_CAP(n) ((n) << 0)
+
+#define ISP_REG_PIC_CAPTURE_END_CFG 0x020
+#define VEND_CAP(n) ((n) << 16)
+#define HEND_CAP(n) ((n) << 0)
+
+#define ISP_REG_DUMP_CFG_0 0x024
+#define ISP_REG_DUMP_CFG_1 0x028
+#define DUMP_ID(n) ((n) << 24)
+#define DUMP_SHT(n) ((n) << 20)
+#define DUMP_BURST_LEN(n) ((n) << 16)
+#define DUMP_SD(n) ((n) << 0)
+#define DUMP_BURST_LEN_MASK GENMASK(17, 16)
+#define DUMP_SD_MASK GENMASK(15, 0)
+
+#define ISP_REG_DEC_CFG 0x030
+#define DEC_V_KEEP(n) ((n) << 24)
+#define DEC_V_PERIOD(n) ((n) << 16)
+#define DEC_H_KEEP(n) ((n) << 8)
+#define DEC_H_PERIOD(n) ((n) << 0)
+
+#define ISP_REG_OBC_CFG 0x034
+#define OBC_W_H(y) ((y) << 4)
+#define OBC_W_W(x) ((x) << 0)
+
+#define ISP_REG_DC_CFG_1 0x044
+#define DC_AXI_ID(n) ((n) << 0)
+
+#define ISP_REG_LCCF_CFG_0 0x050
+#define Y_DISTANCE(y) ((y) << 16)
+#define X_DISTANCE(x) ((x) << 0)
+
+#define ISP_REG_LCCF_CFG_1 0x058
+#define LCCF_MAX_DIS(n) ((n) << 0)
+
+#define ISP_REG_LCBQ_CFG_0 0x074
+#define H_LCBQ(y) ((y) << 12)
+#define W_LCBQ(x) ((x) << 8)
+
+#define ISP_REG_LCBQ_CFG_1 0x07c
+#define Y_COOR(y) ((y) << 16)
+#define X_COOR(x) ((x) << 0)
+
+#define ISP_REG_LCCF_CFG_2 0x0e0
+#define ISP_REG_LCCF_CFG_3 0x0e4
+#define ISP_REG_LCCF_CFG_4 0x0e8
+#define ISP_REG_LCCF_CFG_5 0x0ec
+#define LCCF_F2_PAR(n) ((n) << 16)
+#define LCCF_F1_PAR(n) ((n) << 0)
+
+#define ISP_REG_OECF_X0_CFG0 0x100
+#define ISP_REG_OECF_X0_CFG1 0x104
+#define ISP_REG_OECF_X0_CFG2 0x108
+#define ISP_REG_OECF_X0_CFG3 0x10c
+#define ISP_REG_OECF_X0_CFG4 0x110
+#define ISP_REG_OECF_X0_CFG5 0x114
+#define ISP_REG_OECF_X0_CFG6 0x118
+#define ISP_REG_OECF_X0_CFG7 0x11c
+
+#define ISP_REG_OECF_Y3_CFG0 0x1e0
+#define ISP_REG_OECF_Y3_CFG1 0x1e4
+#define ISP_REG_OECF_Y3_CFG2 0x1e8
+#define ISP_REG_OECF_Y3_CFG3 0x1ec
+#define ISP_REG_OECF_Y3_CFG4 0x1f0
+#define ISP_REG_OECF_Y3_CFG5 0x1f4
+#define ISP_REG_OECF_Y3_CFG6 0x1f8
+#define ISP_REG_OECF_Y3_CFG7 0x1fc
+
+#define ISP_REG_OECF_S0_CFG0 0x200
+#define ISP_REG_OECF_S3_CFG7 0x27c
+#define OCEF_PAR_H(n) ((n) << 16)
+#define OCEF_PAR_L(n) ((n) << 0)
+
+#define ISP_REG_AWB_X0_CFG_0 0x280
+#define ISP_REG_AWB_X0_CFG_1 0x284
+#define ISP_REG_AWB_X1_CFG_0 0x288
+#define ISP_REG_AWB_X1_CFG_1 0x28c
+#define ISP_REG_AWB_X2_CFG_0 0x290
+#define ISP_REG_AWB_X2_CFG_1 0x294
+#define ISP_REG_AWB_X3_CFG_0 0x298
+#define ISP_REG_AWB_X3_CFG_1 0x29c
+#define AWB_X_SYMBOL_H(n) ((n) << 16)
+#define AWB_X_SYMBOL_L(n) ((n) << 0)
+
+#define ISP_REG_AWB_Y0_CFG_0 0x2a0
+#define ISP_REG_AWB_Y0_CFG_1 0x2a4
+#define ISP_REG_AWB_Y1_CFG_0 0x2a8
+#define ISP_REG_AWB_Y1_CFG_1 0x2ac
+#define ISP_REG_AWB_Y2_CFG_0 0x2b0
+#define ISP_REG_AWB_Y2_CFG_1 0x2b4
+#define ISP_REG_AWB_Y3_CFG_0 0x2b8
+#define ISP_REG_AWB_Y3_CFG_1 0x2bc
+#define AWB_Y_SYMBOL_H(n) ((n) << 16)
+#define AWB_Y_SYMBOL_L(n) ((n) << 0)
+
+#define ISP_REG_AWB_S0_CFG_0 0x2c0
+#define ISP_REG_AWB_S0_CFG_1 0x2c4
+#define ISP_REG_AWB_S1_CFG_0 0x2c8
+#define ISP_REG_AWB_S1_CFG_1 0x2cc
+#define ISP_REG_AWB_S2_CFG_0 0x2d0
+#define ISP_REG_AWB_S2_CFG_1 0x2d4
+#define ISP_REG_AWB_S3_CFG_0 0x2d8
+#define ISP_REG_AWB_S3_CFG_1 0x2dc
+#define AWB_S_SYMBOL_H(n) ((n) << 16)
+#define AWB_S_SYMBOL_L(n) ((n) << 0)
+
+#define ISP_REG_OBCG_CFG_0 0x2e0
+#define ISP_REG_OBCG_CFG_1 0x2e4
+#define ISP_REG_OBCG_CFG_2 0x2e8
+#define ISP_REG_OBCG_CFG_3 0x2ec
+#define ISP_REG_OBCO_CFG_0 0x2f0
+#define ISP_REG_OBCO_CFG_1 0x2f4
+#define ISP_REG_OBCO_CFG_2 0x2f8
+#define ISP_REG_OBCO_CFG_3 0x2fc
+#define GAIN_D_POINT(x) ((x) << 24)
+#define GAIN_C_POINT(x) ((x) << 16)
+#define GAIN_B_POINT(x) ((x) << 8)
+#define GAIN_A_POINT(x) ((x) << 0)
+#define OFFSET_D_POINT(x) ((x) << 24)
+#define OFFSET_C_POINT(x) ((x) << 16)
+#define OFFSET_B_POINT(x) ((x) << 8)
+#define OFFSET_A_POINT(x) ((x) << 0)
+
+#define ISP_REG_ISP_CTRL_0 0xa00
+#define ISPC_LINE BIT(27)
+#define ISPC_SC BIT(26)
+#define ISPC_CSI BIT(25)
+#define ISPC_ISP BIT(24)
+#define ISPC_ENUO BIT(20)
+#define ISPC_ENLS BIT(17)
+#define ISPC_ENSS1 BIT(12)
+#define ISPC_ENSS0 BIT(11)
+#define ISPC_RST BIT(1)
+#define ISPC_EN BIT(0)
+#define ISPC_RST_MASK BIT(1)
+#define ISPC_INT_ALL_MASK GENMASK(27, 24)
+
+#define ISP_REG_ISP_CTRL_1 0xa08
+#define CTRL_SAT(n) ((n) << 28)
+#define CTRL_DBC BIT(22)
+#define CTRL_CTC BIT(21)
+#define CTRL_YHIST BIT(20)
+#define CTRL_YCURVE BIT(19)
+#define CTRL_CTM BIT(18)
+#define CTRL_BIYUV BIT(17)
+#define CTRL_SCE BIT(8)
+#define CTRL_EE BIT(7)
+#define CTRL_CCE BIT(5)
+#define CTRL_RGE BIT(4)
+#define CTRL_CME BIT(3)
+#define CTRL_AE BIT(2)
+#define CTRL_CE BIT(1)
+#define CTRL_SAT_MASK GENMASK(31, 28)
+
+#define ISP_REG_PIPELINE_XY_SIZE 0xa0c
+#define H_ACT_CAP(n) ((n) << 16)
+#define W_ACT_CAP(n) ((n) << 0)
+
+#define ISP_REG_ICTC 0xa10
+#define GF_MODE(n) ((n) << 30)
+#define MAXGT(n) ((n) << 16)
+#define MINGT(n) ((n) << 0)
+
+#define ISP_REG_IDBC 0xa14
+#define BADGT(n) ((n) << 16)
+#define BADXT(n) ((n) << 0)
+
+#define ISP_REG_ICFAM 0xa1c
+#define CROSS_COV(n) ((n) << 4)
+#define HV_W(n) ((n) << 0)
+
+#define ISP_REG_CS_GAIN 0xa30
+#define CMAD(n) ((n) << 16)
+#define CMAB(n) ((n) << 0)
+
+#define ISP_REG_CS_THRESHOLD 0xa34
+#define CMD(n) ((n) << 16)
+#define CMB(n) ((n) << 0)
+
+#define ISP_REG_CS_OFFSET 0xa38
+#define VOFF(n) ((n) << 16)
+#define UOFF(n) ((n) << 0)
+
+#define ISP_REG_CS_HUE_F 0xa3c
+#define SIN(n) ((n) << 16)
+#define COS(n) ((n) << 0)
+
+#define ISP_REG_CS_SCALE 0xa40
+
+#define ISP_REG_IESHD 0xa50
+#define SHAD_UP_M BIT(1)
+#define SHAD_UP_EN BIT(0)
+
+#define ISP_REG_YADJ0 0xa54
+#define YOIR(n) ((n) << 16)
+#define YIMIN(n) ((n) << 0)
+
+#define ISP_REG_YADJ1 0xa58
+#define YOMAX(n) ((n) << 16)
+#define YOMIN(n) ((n) << 0)
+
+#define ISP_REG_Y_PLANE_START_ADDR 0xa80
+#define ISP_REG_UV_PLANE_START_ADDR 0xa84
+#define ISP_REG_STRIDE 0xa88
+
+#define ISP_REG_ITIIWSR 0xb20
+#define ITI_HSIZE(n) ((n) << 16)
+#define ITI_WSIZE(n) ((n) << 0)
+
+#define ISP_REG_ITIDWLSR 0xb24
+#define ISP_REG_ITIPDFR 0xb38
+#define ISP_REG_ITIDRLSR 0xb3C
+
+#define ISP_REG_DNYUV_YSWR0 0xc00
+#define ISP_REG_DNYUV_YSWR1 0xc04
+#define ISP_REG_DNYUV_CSWR0 0xc08
+#define ISP_REG_DNYUV_CSWR1 0xc0c
+#define YUVSW5(n) ((n) << 20)
+#define YUVSW4(n) ((n) << 16)
+#define YUVSW3(n) ((n) << 12)
+#define YUVSW2(n) ((n) << 8)
+#define YUVSW1(n) ((n) << 4)
+#define YUVSW0(n) ((n) << 0)
+
+#define ISP_REG_DNYUV_YDR0 0xc10
+#define ISP_REG_DNYUV_YDR1 0xc14
+#define ISP_REG_DNYUV_YDR2 0xc18
+#define ISP_REG_DNYUV_CDR0 0xc1c
+#define ISP_REG_DNYUV_CDR1 0xc20
+#define ISP_REG_DNYUV_CDR2 0xc24
+#define CURVE_D_H(n) ((n) << 16)
+#define CURVE_D_L(n) ((n) << 0)
+
+#define ISP_REG_ICAMD_0 0xc40
+#define ISP_REG_ICAMD_12 0xc70
+#define ISP_REG_ICAMD_20 0xc90
+#define ISP_REG_ICAMD_24 0xca0
+#define ISP_REG_ICAMD_25 0xca4
+#define DNRM_F(n) ((n) << 16)
+#define CCM_M_DAT(n) ((n) << 0)
+
+#define ISP_REG_GAMMA_VAL0 0xe00
+#define ISP_REG_GAMMA_VAL1 0xe04
+#define ISP_REG_GAMMA_VAL2 0xe08
+#define ISP_REG_GAMMA_VAL3 0xe0c
+#define ISP_REG_GAMMA_VAL4 0xe10
+#define ISP_REG_GAMMA_VAL5 0xe14
+#define ISP_REG_GAMMA_VAL6 0xe18
+#define ISP_REG_GAMMA_VAL7 0xe1c
+#define ISP_REG_GAMMA_VAL8 0xe20
+#define ISP_REG_GAMMA_VAL9 0xe24
+#define ISP_REG_GAMMA_VAL10 0xe28
+#define ISP_REG_GAMMA_VAL11 0xe2c
+#define ISP_REG_GAMMA_VAL12 0xe30
+#define ISP_REG_GAMMA_VAL13 0xe34
+#define ISP_REG_GAMMA_VAL14 0xe38
+#define GAMMA_S_VAL(n) ((n) << 16)
+#define GAMMA_VAL(n) ((n) << 0)
+
+#define ISP_REG_R2Y_0 0xe40
+#define ISP_REG_R2Y_1 0xe44
+#define ISP_REG_R2Y_2 0xe48
+#define ISP_REG_R2Y_3 0xe4c
+#define ISP_REG_R2Y_4 0xe50
+#define ISP_REG_R2Y_5 0xe54
+#define ISP_REG_R2Y_6 0xe58
+#define ISP_REG_R2Y_7 0xe5c
+#define ISP_REG_R2Y_8 0xe60
+
+#define ISP_REG_SHARPEN0 0xe80
+#define ISP_REG_SHARPEN1 0xe84
+#define ISP_REG_SHARPEN2 0xe88
+#define ISP_REG_SHARPEN3 0xe8c
+#define ISP_REG_SHARPEN4 0xe90
+#define ISP_REG_SHARPEN5 0xe94
+#define ISP_REG_SHARPEN6 0xe98
+#define ISP_REG_SHARPEN7 0xe9c
+#define ISP_REG_SHARPEN8 0xea0
+#define ISP_REG_SHARPEN9 0xea4
+#define ISP_REG_SHARPEN10 0xea8
+#define ISP_REG_SHARPEN11 0xeac
+#define ISP_REG_SHARPEN12 0xeb0
+#define ISP_REG_SHARPEN13 0xeb4
+#define ISP_REG_SHARPEN14 0xeb8
+#define S_DELTA(n) ((n) << 16)
+#define S_WEIGHT(n) ((n) << 8)
+
+#define ISP_REG_SHARPEN_FS0 0xebc
+#define ISP_REG_SHARPEN_FS1 0xec0
+#define ISP_REG_SHARPEN_FS2 0xec4
+#define ISP_REG_SHARPEN_FS3 0xec8
+#define ISP_REG_SHARPEN_FS4 0xecc
+#define ISP_REG_SHARPEN_FS5 0xed0
+#define S_FACTOR(n) ((n) << 24)
+#define S_SLOPE(n) ((n) << 0)
+
+#define ISP_REG_SHARPEN_WN 0xed4
+#define PDIRF(n) ((n) << 28)
+#define NDIRF(n) ((n) << 24)
+#define WSUM(n) ((n) << 0)
+
+#define ISP_REG_IUVS1 0xed8
+#define UVDIFF2(n) ((n) << 16)
+#define UVDIFF1(n) ((n) << 0)
+
+#define ISP_REG_IUVS2 0xedc
+#define UVF(n) ((n) << 24)
+#define UVSLOPE(n) ((n) << 0)
+
+#define ISP_REG_IUVCKS1 0xee0
+#define UVCKDIFF2(n) ((n) << 16)
+#define UVCKDIFF1(n) ((n) << 0)
+
+#define ISP_REG_IUVCKS2 0xee4
+
+#define ISP_REG_ISHRPET 0xee8
+#define TH(n) ((n) << 8)
+#define EN(n) ((n) << 0)
+
+#define ISP_REG_YCURVE_0 0xf00
+#define ISP_REG_YCURVE_63 0xffc
+
+#define IMAGE_MAX_WIDTH 1920
+#define IMAGE_MAX_HEIGH 1080
+
+/* pad id for media framework */
+enum stf_isp_pad_id {
+ STF_ISP_PAD_SINK = 0,
+ STF_ISP_PAD_SRC,
+ STF_ISP_PAD_MAX
+};
+
+struct stf_isp_format {
+ u32 code;
+ u8 bpp;
+};
+
+struct stf_isp_format_table {
+ const struct stf_isp_format *fmts;
+ int nfmts;
+};
+
+struct stf_isp_dev {
+ struct stfcamss *stfcamss;
+ struct v4l2_subdev subdev;
+ struct media_pad pads[STF_ISP_PAD_MAX];
+ const struct stf_isp_format_table *formats;
+ unsigned int nformats;
+ struct v4l2_subdev *source_subdev;
+ const struct stf_isp_format *current_fmt;
+};
+
+int stf_isp_reset(struct stf_isp_dev *isp_dev);
+void stf_isp_init_cfg(struct stf_isp_dev *isp_dev);
+void stf_isp_settings(struct stf_isp_dev *isp_dev,
+ struct v4l2_rect *crop, u32 mcode);
+void stf_isp_stream_set(struct stf_isp_dev *isp_dev);
+int stf_isp_init(struct stfcamss *stfcamss);
+int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev);
+int stf_isp_unregister(struct stf_isp_dev *isp_dev);
+
+#endif /* STF_ISP_H */
diff --git a/drivers/staging/media/starfive/camss/stf-video.c b/drivers/staging/media/starfive/camss/stf-video.c
new file mode 100644
index 0000000000000..989b5e82bae98
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-video.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * stf_video.c
+ *
+ * StarFive Camera Subsystem - V4L2 device node
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "stf-camss.h"
+#include "stf-video.h"
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static inline struct stfcamss_buffer *
+to_stfcamss_buffer(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct stfcamss_buffer, vb);
+}
+
+static const struct stfcamss_format_info *
+video_g_fi_by_index(struct stfcamss_video *video, int index)
+{
+ if (index >= video->nformats)
+ return NULL;
+
+ return &video->formats[index];
+}
+
+static const struct stfcamss_format_info *
+video_g_fi_by_mcode(struct stfcamss_video *video, u32 mcode)
+{
+ unsigned int i;
+
+ for (i = 0; i < video->nformats; i++) {
+ if (video->formats[i].code == mcode)
+ return &video->formats[i];
+ }
+
+ return NULL;
+}
+
+static const struct stfcamss_format_info *
+video_g_fi_by_pfmt(struct stfcamss_video *video, u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < video->nformats; i++) {
+ if (video->formats[i].pixelformat == pixelformat)
+ return &video->formats[i];
+ }
+
+ return NULL;
+}
+
+static int __video_try_fmt(struct stfcamss_video *video, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct stfcamss_format_info *fi;
+ u32 width, height;
+ u32 bpl;
+ unsigned int i;
+
+ fi = video_g_fi_by_pfmt(video, pix->pixelformat);
+ if (!fi)
+ fi = &video->formats[0]; /* default format */
+
+ width = pix->width;
+ height = pix->height;
+
+ memset(pix, 0, sizeof(*pix));
+
+ pix->pixelformat = fi->pixelformat;
+ pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH,
+ STFCAMSS_FRAME_MAX_WIDTH);
+ pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT,
+ STFCAMSS_FRAME_MAX_HEIGHT);
+ bpl = pix->width * fi->bpp / 8;
+ bpl = ALIGN(bpl, video->bpl_alignment);
+ pix->bytesperline = bpl;
+
+ for (i = 0; i < fi->planes; ++i)
+ pix->sizeimage += bpl * pix->height / fi->vsub[i];
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ pix->flags = 0;
+ pix->ycbcr_enc =
+ V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
+ pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
+ pix->colorspace,
+ pix->ycbcr_enc);
+ pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
+
+ return 0;
+}
+
+static int stf_video_init_format(struct stfcamss_video *video)
+{
+ int ret;
+ struct v4l2_format format = {
+ .type = video->type,
+ .fmt.pix = {
+ .width = 1920,
+ .height = 1080,
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ },
+ };
+
+ ret = __video_try_fmt(video, &format);
+
+ if (ret < 0)
+ return ret;
+
+ video->active_fmt = format;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static int video_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct stfcamss_video *video = vb2_get_drv_priv(q);
+ const struct v4l2_pix_format *format = &video->active_fmt.fmt.pix;
+
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+
+ if (sizes[0] < format->sizeimage)
+ return -EINVAL;
+ } else {
+ *num_planes = 1;
+ sizes[0] = format->sizeimage;
+ }
+
+ if (!sizes[0]) {
+ dev_dbg(video->stfcamss->dev,
+ "%s: error size is zero.\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(video->stfcamss->dev, "planes = %d, size = %d\n",
+ *num_planes, sizes[0]);
+
+ return 0;
+}
+
+static int video_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf);
+ const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
+ dma_addr_t *paddr;
+
+ paddr = vb2_plane_cookie(vb, 0);
+ buffer->addr[0] = *paddr;
+
+ if (fmt->pixelformat == V4L2_PIX_FMT_NV12)
+ buffer->addr[1] =
+ buffer->addr[0] + fmt->bytesperline * fmt->height;
+
+ return 0;
+}
+
+static int video_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
+
+ if (fmt->sizeimage > vb2_plane_size(vb, 0)) {
+ dev_dbg(video->stfcamss->dev,
+ "sizeimage = %u, plane size = %u\n",
+ fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0));
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, 0, fmt->sizeimage);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static void video_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct stfcamss_buffer *buffer = to_stfcamss_buffer(vbuf);
+
+ video->ops->queue_buffer(video, buffer);
+}
+
+static int video_get_subdev_format(struct stfcamss_video *video,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ struct media_entity *entity;
+ int ret;
+
+ entity = &video->vdev.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_pad_remote_pad_first(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ fmt->pad = pad->index;
+
+ ret = v4l2_subdev_call_state_active(subdev, pad, get_fmt, fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ else if (!ret)
+ break;
+ }
+
+ return 0;
+}
+
+static int stf_video_check_format(struct stfcamss_video *video)
+{
+ struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
+ const struct stfcamss_format_info *fi;
+ int ret;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ ret = video_get_subdev_format(video, &sd_fmt);
+ if (ret < 0)
+ return ret;
+
+ fi = video_g_fi_by_mcode(video, sd_fmt.format.code);
+ if (!fi)
+ return -EINVAL;
+
+ if (pix->pixelformat != fi->pixelformat ||
+ pix->height != sd_fmt.format.height ||
+ pix->width != sd_fmt.format.width ||
+ pix->field != sd_fmt.format.field)
+ return -EPIPE;
+
+ return 0;
+}
+
+static int video_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct stfcamss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ int ret;
+
+ ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe);
+ if (ret < 0) {
+ dev_err(video->stfcamss->dev,
+ "Failed to media_pipeline_start: %d\n", ret);
+ goto err_ret_buffers;
+ }
+
+ ret = pm_runtime_resume_and_get(video->stfcamss->dev);
+ if (ret < 0) {
+ dev_err(video->stfcamss->dev, "power up failed %d\n", ret);
+ goto err_pipeline_stop;
+ }
+
+ video->ops->start_streaming(video);
+
+ ret = v4l2_subdev_call(video->source_subdev, video, s_stream, true);
+ if (ret) {
+ dev_err(video->stfcamss->dev, "stream on failed\n");
+ goto err_pm_put;
+ }
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_put(video->stfcamss->dev);
+err_pipeline_stop:
+ video_device_pipeline_stop(vdev);
+err_ret_buffers:
+ video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void video_stop_streaming(struct vb2_queue *q)
+{
+ struct stfcamss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+
+ video->ops->stop_streaming(video);
+
+ v4l2_subdev_call(video->source_subdev, video, s_stream, false);
+
+ pm_runtime_put(video->stfcamss->dev);
+
+ video_device_pipeline_stop(vdev);
+ video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops stf_video_vb2_q_ops = {
+ .queue_setup = video_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_init = video_buf_init,
+ .buf_prepare = video_buf_prepare,
+ .buf_queue = video_buf_queue,
+ .start_streaming = video_start_streaming,
+ .stop_streaming = video_stop_streaming,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int video_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, "starfive-camss", sizeof(cap->driver));
+ strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card));
+
+ return 0;
+}
+
+static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct stfcamss_video *video = video_drvdata(file);
+ const struct stfcamss_format_info *fi;
+
+ if (f->index >= video->nformats)
+ return -EINVAL;
+
+ if (f->mbus_code) {
+ /* Each entry in formats[] table has unique mbus_code */
+ if (f->index > 0)
+ return -EINVAL;
+
+ fi = video_g_fi_by_mcode(video, f->mbus_code);
+ } else {
+ fi = video_g_fi_by_index(video, f->index);
+ }
+
+ if (!fi)
+ return -EINVAL;
+
+ f->pixelformat = fi->pixelformat;
+
+ return 0;
+}
+
+static int video_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct stfcamss_video *video = video_drvdata(file);
+ unsigned int i;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ for (i = 0; i < video->nformats; i++) {
+ if (video->formats[i].pixelformat == fsize->pixel_format)
+ break;
+ }
+
+ if (i == video->nformats)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH;
+ fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH;
+ fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT;
+ fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct stfcamss_video *video = video_drvdata(file);
+
+ *f = video->active_fmt;
+
+ return 0;
+}
+
+static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct stfcamss_video *video = video_drvdata(file);
+ int ret;
+
+ if (vb2_is_busy(&video->vb2_q))
+ return -EBUSY;
+
+ ret = __video_try_fmt(video, f);
+ if (ret < 0)
+ return ret;
+
+ video->active_fmt = *f;
+
+ return 0;
+}
+
+static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct stfcamss_video *video = video_drvdata(file);
+
+ return __video_try_fmt(video, f);
+}
+
+static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
+ .vidioc_querycap = video_querycap,
+ .vidioc_enum_fmt_vid_cap = video_enum_fmt,
+ .vidioc_enum_framesizes = video_enum_framesizes,
+ .vidioc_g_fmt_vid_cap = video_g_fmt,
+ .vidioc_s_fmt_vid_cap = video_s_fmt,
+ .vidioc_try_fmt_vid_cap = video_try_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static const struct v4l2_file_operations stf_vid_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .read = vb2_fop_read,
+};
+
+/* -----------------------------------------------------------------------------
+ * STFCAMSS video core
+ */
+
+static int stf_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct stfcamss_video *video = video_get_drvdata(vdev);
+ int ret;
+
+ ret = stf_video_check_format(video);
+
+ return ret;
+}
+
+static const struct media_entity_operations stf_media_ops = {
+ .link_validate = stf_link_validate,
+};
+
+static void stf_video_release(struct video_device *vdev)
+{
+ struct stfcamss_video *video = video_get_drvdata(vdev);
+
+ media_entity_cleanup(&vdev->entity);
+
+ mutex_destroy(&video->q_lock);
+ mutex_destroy(&video->lock);
+}
+
+int stf_video_register(struct stfcamss_video *video,
+ struct v4l2_device *v4l2_dev, const char *name)
+{
+ struct video_device *vdev = &video->vdev;
+ struct vb2_queue *q;
+ struct media_pad *pad = &video->pad;
+ int ret;
+
+ mutex_init(&video->q_lock);
+ mutex_init(&video->lock);
+
+ q = &video->vb2_q;
+ q->drv_priv = video;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->ops = &stf_video_vb2_q_ops;
+ q->type = video->type;
+ q->io_modes = VB2_DMABUF | VB2_MMAP;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->buf_struct_size = sizeof(struct stfcamss_buffer);
+ q->dev = video->stfcamss->dev;
+ q->lock = &video->q_lock;
+ q->min_queued_buffers = STFCAMSS_MIN_BUFFERS;
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ dev_err(video->stfcamss->dev,
+ "Failed to init vb2 queue: %d\n", ret);
+ goto err_mutex_destroy;
+ }
+
+ pad->flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, pad);
+ if (ret < 0) {
+ dev_err(video->stfcamss->dev,
+ "Failed to init video entity: %d\n", ret);
+ goto err_mutex_destroy;
+ }
+
+ ret = stf_video_init_format(video);
+ if (ret < 0) {
+ dev_err(video->stfcamss->dev,
+ "Failed to init format: %d\n", ret);
+ goto err_media_cleanup;
+ }
+
+ vdev->fops = &stf_vid_fops;
+ vdev->ioctl_ops = &stf_vid_ioctl_ops;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->entity.ops = &stf_media_ops;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->release = stf_video_release;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->queue = &video->vb2_q;
+ vdev->lock = &video->lock;
+ strscpy(vdev->name, name, sizeof(vdev->name));
+
+ video_set_drvdata(vdev, video);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ dev_err(video->stfcamss->dev,
+ "Failed to register video device: %d\n", ret);
+ goto err_media_cleanup;
+ }
+
+ return 0;
+
+err_media_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_mutex_destroy:
+ mutex_destroy(&video->lock);
+ mutex_destroy(&video->q_lock);
+ return ret;
+}
+
+void stf_video_unregister(struct stfcamss_video *video)
+{
+ vb2_video_unregister_device(&video->vdev);
+}
diff --git a/drivers/staging/media/starfive/camss/stf-video.h b/drivers/staging/media/starfive/camss/stf-video.h
new file mode 100644
index 0000000000000..8052b77e3ad83
--- /dev/null
+++ b/drivers/staging/media/starfive/camss/stf-video.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * stf_video.h
+ *
+ * StarFive Camera Subsystem - V4L2 device node
+ *
+ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
+ */
+
+#ifndef STF_VIDEO_H
+#define STF_VIDEO_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+
+#define STFCAMSS_FRAME_MIN_WIDTH 64
+#define STFCAMSS_FRAME_MAX_WIDTH 1920
+#define STFCAMSS_FRAME_MIN_HEIGHT 64
+#define STFCAMSS_FRAME_MAX_HEIGHT 1080
+#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8
+#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128
+#define STFCAMSS_MIN_BUFFERS 2
+
+#define STFCAMSS_MAX_ENTITY_NAME_LEN 27
+
+enum stf_v_line_id {
+ STF_V_LINE_WR = 0,
+ STF_V_LINE_ISP,
+ STF_V_LINE_MAX,
+};
+
+enum stf_capture_type {
+ STF_CAPTURE_RAW = 0,
+ STF_CAPTURE_YUV,
+ STF_CAPTURE_NUM,
+};
+
+struct stfcamss_buffer {
+ struct vb2_v4l2_buffer vb;
+ dma_addr_t addr[2];
+ struct list_head queue;
+};
+
+struct fract {
+ u8 numerator;
+ u8 denominator;
+};
+
+/*
+ * struct stfcamss_format_info - ISP media bus format information
+ * @code: V4L2 media bus format code
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @planes: Number of planes
+ * @vsub: Vertical subsampling (for each plane)
+ * @bpp: Bits per pixel when stored in memory (for each plane)
+ */
+struct stfcamss_format_info {
+ u32 code;
+ u32 pixelformat;
+ u8 planes;
+ u8 vsub[3];
+ u8 bpp;
+};
+
+struct stfcamss_video {
+ struct stfcamss *stfcamss;
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct media_pad pad;
+ struct v4l2_format active_fmt;
+ enum v4l2_buf_type type;
+ const struct stfcamss_video_ops *ops;
+ struct mutex lock; /* serialize device access */
+ struct mutex q_lock; /* protects the queue */
+ unsigned int bpl_alignment;
+ const struct stfcamss_format_info *formats;
+ unsigned int nformats;
+ struct v4l2_subdev *source_subdev;
+};
+
+struct stfcamss_video_ops {
+ int (*queue_buffer)(struct stfcamss_video *video,
+ struct stfcamss_buffer *buf);
+ int (*flush_buffers)(struct stfcamss_video *video,
+ enum vb2_buffer_state state);
+ void (*start_streaming)(struct stfcamss_video *video);
+ void (*stop_streaming)(struct stfcamss_video *video);
+};
+
+int stf_video_register(struct stfcamss_video *video,
+ struct v4l2_device *v4l2_dev, const char *name);
+
+void stf_video_unregister(struct stfcamss_video *video);
+
+#endif /* STF_VIDEO_H */
diff --git a/drivers/staging/media/sunxi/cedrus/Kconfig b/drivers/staging/media/sunxi/cedrus/Kconfig
index 621944f9907a6..cb07a343c9c27 100644
--- a/drivers/staging/media/sunxi/cedrus/Kconfig
+++ b/drivers/staging/media/sunxi/cedrus/Kconfig
@@ -6,7 +6,6 @@ config VIDEO_SUNXI_CEDRUS
depends on HAS_DMA
depends on OF
select MEDIA_CONTROLLER
- select MEDIA_CONTROLLER_REQUEST_API
select SUNXI_SRAM
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
diff --git a/drivers/staging/media/sunxi/cedrus/TODO b/drivers/staging/media/sunxi/cedrus/TODO
index ec277ece47afd..00aa304a7e369 100644
--- a/drivers/staging/media/sunxi/cedrus/TODO
+++ b/drivers/staging/media/sunxi/cedrus/TODO
@@ -1,7 +1,16 @@
-Before this stateless decoder driver can leave the staging area:
-* The Request API needs to be stabilized;
-* The codec-specific controls need to be thoroughly reviewed to ensure they
- cover all intended uses cases;
-* Userspace support for the Request API needs to be reviewed;
-* Another stateless decoder driver should be submitted;
-* At least one stateless encoder driver should be submitted.
+This driver suffers from a bad initial design that results in various aspects
+being intricated, making it difficult to scale to new codecs and to add encoding
+support in the future.
+
+Before leaving the staging area, it should be reworked to clearly distinguish
+between different aspects:
+- platform, with resources management, interrupt handler, watchdog,
+ v4l2 and m2m devices registration;
+- proc, with video device registration and related operations;
+- context, with m2m context, queue and controls management;
+- engine, with each individual codec job execution and codec-specific
+ operation callbacks;
+
+This will make it possible to register two different procs (decoder and
+encoder) while sharing significant common infrastructure, common v4l2 and m2m
+devices but exposing distinct video devices.
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
index dfb401df138a9..3e2843ef6ccec 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
@@ -653,8 +653,13 @@ static void cedrus_h264_stop(struct cedrus_ctx *ctx)
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- for (i = 0; i < vq->num_buffers; i++) {
- buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i));
+ for (i = 0; i < vb2_get_num_buffers(vq); i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(vq, i);
+
+ if (!vb)
+ continue;
+
+ buf = vb2_to_cedrus_buffer(vb);
if (buf->codec.h264.mv_col_buf_size > 0) {
dma_free_attrs(dev->dev,
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index fc9297232456f..52e94c8f2f01a 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -869,8 +869,13 @@ static void cedrus_h265_stop(struct cedrus_ctx *ctx)
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- for (i = 0; i < vq->num_buffers; i++) {
- buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i));
+ for (i = 0; i < vb2_get_num_buffers(vq); i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(vq, i);
+
+ if (!vb)
+ continue;
+
+ buf = vb2_to_cedrus_buffer(vb);
if (buf->codec.h265.mv_col_buf_size > 0) {
dma_free_attrs(dev->dev,
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c
index 1595a9607775b..0eea4c2c36271 100644
--- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c
@@ -660,7 +660,7 @@ int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev)
queue->buf_struct_size = sizeof(struct sun6i_isp_buffer);
queue->ops = &sun6i_isp_capture_queue_ops;
queue->mem_ops = &vb2_dma_contig_memops;
- queue->min_buffers_needed = 2;
+ queue->min_queued_buffers = 2;
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
queue->lock = &capture->lock;
queue->dev = isp_dev->dev;
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c
index e28be895b4861..53d05e8a364bb 100644
--- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c
@@ -489,7 +489,7 @@ int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev)
queue->buf_struct_size = sizeof(struct sun6i_isp_buffer);
queue->ops = &sun6i_isp_params_queue_ops;
queue->mem_ops = &vb2_vmalloc_memops;
- queue->min_buffers_needed = 1;
+ queue->min_queued_buffers = 1;
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
queue->lock = &params->lock;
queue->dev = isp_dev->dev;
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
index ccbb530aa2e23..46a334b602f19 100644
--- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
@@ -256,13 +256,13 @@ sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
}
-static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *state)
+static int sun6i_isp_proc_init_state(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state)
{
struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev);
unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI;
struct v4l2_mbus_framefmt *mbus_format =
- v4l2_subdev_get_try_format(subdev, state, pad);
+ v4l2_subdev_state_get_format(state, pad);
struct mutex *lock = &isp_dev->proc.lock;
mutex_lock(lock);
@@ -302,8 +302,8 @@ static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev,
mutex_lock(lock);
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *mbus_format = *v4l2_subdev_get_try_format(subdev, state,
- format->pad);
+ *mbus_format = *v4l2_subdev_state_get_format(state,
+ format->pad);
else
*mbus_format = isp_dev->proc.mbus_format;
@@ -325,7 +325,7 @@ static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev,
sun6i_isp_proc_mbus_format_prepare(mbus_format);
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *v4l2_subdev_get_try_format(subdev, state, format->pad) =
+ *v4l2_subdev_state_get_format(state, format->pad) =
*mbus_format;
else
isp_dev->proc.mbus_format = *mbus_format;
@@ -336,7 +336,6 @@ static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev,
}
static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = {
- .init_cfg = sun6i_isp_proc_init_cfg,
.enum_mbus_code = sun6i_isp_proc_enum_mbus_code,
.get_fmt = sun6i_isp_proc_get_fmt,
.set_fmt = sun6i_isp_proc_set_fmt,
@@ -347,6 +346,10 @@ static const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = {
.pad = &sun6i_isp_proc_pad_ops,
};
+static const struct v4l2_subdev_internal_ops sun6i_isp_proc_internal_ops = {
+ .init_state = sun6i_isp_proc_init_state,
+};
+
/* Media Entity */
static const struct media_entity_operations sun6i_isp_proc_entity_ops = {
@@ -501,6 +504,7 @@ int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev)
/* V4L2 Subdev */
v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops);
+ subdev->internal_ops = &sun6i_isp_proc_internal_ops;
strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name));
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
subdev->owner = THIS_MODULE;
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 9aa72863c213e..255cccd0c5fda 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -222,14 +222,22 @@ static int csi_set_format(struct v4l2_subdev *subdev,
/*
* V4L2 Subdevice Video Operations
*/
-static int tegra_csi_g_frame_interval(struct v4l2_subdev *subdev,
- struct v4l2_subdev_frame_interval *vfi)
+static int tegra_csi_get_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *vfi)
{
struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
return -ENOIOCTLCMD;
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (vfi->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
vfi->interval.numerator = 1;
vfi->interval.denominator = csi_chan->framerate;
@@ -430,8 +438,6 @@ static int tegra_csi_s_stream(struct v4l2_subdev *subdev, int enable)
*/
static const struct v4l2_subdev_video_ops tegra_csi_video_ops = {
.s_stream = tegra_csi_s_stream,
- .g_frame_interval = tegra_csi_g_frame_interval,
- .s_frame_interval = tegra_csi_g_frame_interval,
};
static const struct v4l2_subdev_pad_ops tegra_csi_pad_ops = {
@@ -440,6 +446,8 @@ static const struct v4l2_subdev_pad_ops tegra_csi_pad_ops = {
.enum_frame_interval = csi_enum_frameintervals,
.get_fmt = csi_get_format,
.set_fmt = csi_set_format,
+ .get_frame_interval = tegra_csi_get_frame_interval,
+ .set_frame_interval = tegra_csi_get_frame_interval,
};
static const struct v4l2_subdev_ops tegra_csi_ops = {
@@ -818,15 +826,13 @@ rpm_disable:
return ret;
}
-static int tegra_csi_remove(struct platform_device *pdev)
+static void tegra_csi_remove(struct platform_device *pdev)
{
struct tegra_csi *csi = platform_get_drvdata(pdev);
host1x_client_unregister(&csi->client);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
@@ -852,5 +858,5 @@ struct platform_driver tegra_csi_driver = {
.pm = &tegra_csi_pm_ops,
},
.probe = tegra_csi_probe,
- .remove = tegra_csi_remove,
+ .remove_new = tegra_csi_remove,
};
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 94171e62dee9e..af6e3a0d8df4f 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -439,6 +439,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.target = V4L2_SEL_TGT_CROP_BOUNDS,
};
+ struct v4l2_rect *try_crop;
int ret;
subdev = tegra_channel_get_remote_source_subdev(chan);
@@ -473,24 +474,25 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
* Attempt to obtain the format size from subdev.
* If not available, try to get crop boundary from subdev.
*/
+ try_crop = v4l2_subdev_state_get_crop(sd_state, 0);
fse.code = fmtinfo->code;
ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse);
if (ret) {
if (!v4l2_subdev_has_op(subdev, pad, get_selection)) {
- sd_state->pads->try_crop.width = 0;
- sd_state->pads->try_crop.height = 0;
+ try_crop->width = 0;
+ try_crop->height = 0;
} else {
ret = v4l2_subdev_call(subdev, pad, get_selection,
NULL, &sdsel);
if (ret)
return -EINVAL;
- sd_state->pads->try_crop.width = sdsel.r.width;
- sd_state->pads->try_crop.height = sdsel.r.height;
+ try_crop->width = sdsel.r.width;
+ try_crop->height = sdsel.r.height;
}
} else {
- sd_state->pads->try_crop.width = fse.max_width;
- sd_state->pads->try_crop.height = fse.max_height;
+ try_crop->width = fse.max_width;
+ try_crop->height = fse.max_height;
}
ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt);
@@ -1172,7 +1174,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
chan->queue.ops = &tegra_channel_queue_qops;
chan->queue.mem_ops = &vb2_dma_contig_memops;
chan->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- chan->queue.min_buffers_needed = 2;
+ chan->queue.min_queued_buffers = 2;
chan->queue.dev = vi->dev;
ret = vb2_queue_init(&chan->queue);
if (ret < 0) {
@@ -1944,7 +1946,7 @@ rpm_disable:
return ret;
}
-static int tegra_vi_remove(struct platform_device *pdev)
+static void tegra_vi_remove(struct platform_device *pdev)
{
struct tegra_vi *vi = platform_get_drvdata(pdev);
@@ -1953,8 +1955,6 @@ static int tegra_vi_remove(struct platform_device *pdev)
if (vi->ops->vi_enable)
vi->ops->vi_enable(vi, false);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static const struct of_device_id tegra_vi_of_id_table[] = {
@@ -1979,5 +1979,5 @@ struct platform_driver tegra_vi_driver = {
.pm = &tegra_vi_pm_ops,
},
.probe = tegra_vi_probe,
- .remove = tegra_vi_remove,
+ .remove_new = tegra_vi_remove,
};
diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c
index e95cc7bb190e1..8504b9ea9cea2 100644
--- a/drivers/staging/media/tegra-video/vip.c
+++ b/drivers/staging/media/tegra-video/vip.c
@@ -254,15 +254,13 @@ static int tegra_vip_probe(struct platform_device *pdev)
return 0;
}
-static int tegra_vip_remove(struct platform_device *pdev)
+static void tegra_vip_remove(struct platform_device *pdev)
{
struct tegra_vip *vip = platform_get_drvdata(pdev);
host1x_client_unregister(&vip->client);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
@@ -283,5 +281,5 @@ struct platform_driver tegra_vip_driver = {
.of_match_table = tegra_vip_of_id_table,
},
.probe = tegra_vip_probe,
- .remove = tegra_vip_remove,
+ .remove_new = tegra_vip_remove,
};