diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-13 01:29:48 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-13 01:29:48 +0300 |
commit | 61da593f4458f25c59f65cfd9ba1bda570db5db7 (patch) | |
tree | 42196179375dfda07901577e080f979b2897d308 /drivers/staging | |
parent | a3cc31e75185f9b1ad8dc45eac77f8de788dc410 (diff) | |
parent | 60a031b64984ad4a219a13b0fe912746b586bb9b (diff) | |
download | linux-61da593f4458f25c59f65cfd9ba1bda570db5db7.tar.xz |
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')
52 files changed, 3526 insertions, 173 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index bc6c7b248f86..554c2e475ce3 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 1a4c3a062e3d..dcaeeca0ee6d 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 58cddf11c9ab..5bcd634a2a44 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 9fa390fbc5f3..bec4c5615864 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 1c6643c442ef..20f02d18a8de 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 6a72691ed5b7..133e346ae51b 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 759233a7ba50..f44e6412f4e3 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 abf55a86f795..89118438a3b6 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 a8e4779d007f..01b7fa9b56a2 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 471912dea5cd..a87fc74159e2 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 074826a5b706..92e61ee90993 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 8e26663cecb6..305b103153d7 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 ac5fb332088e..2b80d54006b3 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 9b81cfbcd777..17fd980c9d3c 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 4846078315ff..efa7623b5cee 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 dda1ebc34692..785aac881922 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 064dc562bc96..1b5af8945e6b 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 3c2093c520ba..09da4103a8db 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 2640cd34dce2..f095d9134fee 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 b2d8476d83a0..0d8b42061623 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 e530767e80a5..a66f034380c0 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 219185aaa588..de3e0345ab7c 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 d2844414de4f..0e6c5bd81930 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 23f707cb336f..4a4eae290d65 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 5e7f25cd53ac..8fa99532d9d4 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 a5f8f9f1ab16..58e698ef9108 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 e963d60cc6ad..5f3bdd848a2c 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 84a41792cb4b..ac398b5a9736 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 000000000000..34727cf56072 --- /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 000000000000..4639fa1bca32 --- /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 000000000000..9ea5708fe409 --- /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 000000000000..005790202e7b --- /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 000000000000..7d459f4f09d0 --- /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 000000000000..a587f860101a --- /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 000000000000..e2b0cfb437bd --- /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 000000000000..70c24b050a1b --- /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 000000000000..2f9740b7e500 --- /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 000000000000..c34631ff9422 --- /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 000000000000..d50616ef351e --- /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 000000000000..955cbb048363 --- /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 000000000000..989b5e82bae9 --- /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 000000000000..8052b77e3ad8 --- /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 621944f9907a..cb07a343c9c2 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 ec277ece47af..00aa304a7e36 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 dfb401df138a..3e2843ef6cce 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 fc9297232456..52e94c8f2f01 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 1595a9607775..0eea4c2c3627 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 e28be895b486..53d05e8a364b 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 = ¶ms->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 ccbb530aa2e2..46a334b602f1 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 9aa72863c213..255cccd0c5fd 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 94171e62dee9..af6e3a0d8df4 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 e95cc7bb190e..8504b9ea9cea 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, }; |