diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 08:32:10 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 08:32:10 -0700 |
| commit | 00c6649bafef628955569dd39a59e3170e48f7b5 (patch) | |
| tree | 597ea4250d8e340daca614e1cc9b982391d91c2f /drivers/staging | |
| parent | 1f5ffc672165ff851063a5fd044b727ab2517ae3 (diff) | |
| parent | 4fbeef21f5387234111b5d52924e77757626faa5 (diff) | |
Merge tag 'media/v7.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- new CSI tegra support, covering Tegra20 and Tegra30
- new camera sensor drivers: T4ka3 and ov2732
- m88ds3103: add 3103c chip support
- uvcvideo: add support for Intel RealSense D436/D555 and P010 pixel format
- synopsys csi2rx: add i.MX93 support
- imx8-isi: add i.MX95 support
- imx8mq-mipi-csi2: add i.MX8ULP support
- dw100: add V4L2 requests support
- support for DTV devices from Hauppauge got some improvements
- media staging: dropped starfive-camss driver
- media docs: document multi-committers model and improve maint profile
- media core:
- add v4l2_subdev_get_frame_desc_passthrough() helper
- improve error handling in fwnode parsing
- lots of driver fixes, cleanups and improvements
* tag 'media/v7.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (251 commits)
Revert "media: cx231xx: add USB ID 2040:8360 for Hauppauge WinTV-HVR-935"
media: synopsys: csi2rx: add i.MX93 support
media: dt-bindings: add NXP i.MX93 compatible string
media: synopsys: csi2rx: Use enum and u32 array for register offsets
media: synopsys: csi2rx: implement .get_frame_desc() callback
media: synopsys: csi2rx: only check errors from devm_clk_bulk_get_all()
media: synopsys: csi2rx: use devm_reset_control_get_optional_exclusive()
media: i2c: imx283: add support for non-continuous MIPI clock mode
media: i2c: ov08d10: add support for 24 MHz input clock
media: i2c: ov08d10: add support for reset and power management
media: i2c: ov08d10: add support for binding via device tree
dt-bindings: media: i2c: document Omnivision OV08D10 CMOS image sensor
media: i2c: ov08d10: add missing newline to prints
media: i2c: ov08d10: fix some typos in comments
media: i2c: ov08d10: remove duplicate register write
media: i2c: ov08d10: fix image vertical start setting
media: i2c: ov08d10: fix runtime PM handling in probe
staging: media: ipu7: Update TODO
media: Add t4ka3 camera sensor driver
media: i2c: Add ov2732 image sensor driver
...
Diffstat (limited to 'drivers/staging')
34 files changed, 881 insertions, 3446 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index ab250c89cd4d..1aa31bddf970 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -34,8 +34,6 @@ source "drivers/staging/media/max96712/Kconfig" source "drivers/staging/media/meson/vdec/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 4a073938b2b2..6f78b0edde1e 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ -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/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index a7f22de1c889..50366bf10f32 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -1356,6 +1356,10 @@ static int atomisp_s_parm(struct file *file, void *fh, static long atomisp_vidioc_default(struct file *file, void *fh, bool valid_prio, unsigned int cmd, void *arg) { + /* Disable all private IOCTLs for now! */ + if (cmd) + return -EINVAL; + struct video_device *vdev = video_devdata(file); struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; int err; diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 607992100baf..014d0c6f0a8b 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -460,7 +460,7 @@ static void gpioirq(struct tasklet_struct *t) if (saa7146_wait_for_debi_done(av7110->dev, 0)) { pr_err("%s(): saa7146_wait_for_debi_done timed out\n", __func__); - BUG(); /* maybe we should try resetting the debi? */ + return; } spin_lock(&av7110->debilock); diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c index 68b3979ba5f2..fdae467fd7ab 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/av7110/av7110_ir.c @@ -151,6 +151,7 @@ int av7110_ir_init(struct av7110 *av7110) void av7110_ir_exit(struct av7110 *av7110) { rc_unregister_device(av7110->ir.rcdev); + rc_free_device(av7110->ir.rcdev); } //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>"); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index fd7e37d803e7..ef22a083f8eb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -97,9 +97,6 @@ struct csi_priv { /* the mipi virtual channel number at link validate */ int vc_num; - /* media bus config of the upstream subdevice CSI is receiving from */ - struct v4l2_mbus_config mbus_cfg; - spinlock_t irqlock; /* protect eof_irq handler */ struct timer_list eof_timeout_timer; int eof_irq; @@ -403,7 +400,8 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, } /* init the SMFC IDMAC channel */ -static int csi_idmac_setup_channel(struct csi_priv *priv) +static int csi_idmac_setup_channel(struct csi_priv *priv, + struct v4l2_mbus_config *mbus_cfg) { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *incc; @@ -432,7 +430,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) image.phys0 = phys[0]; image.phys1 = phys[1]; - passthrough = requires_passthrough(&priv->mbus_cfg, infmt, incc); + passthrough = requires_passthrough(mbus_cfg, infmt, incc); passthrough_cycles = 1; /* @@ -572,11 +570,12 @@ static void csi_idmac_unsetup(struct csi_priv *priv, csi_idmac_unsetup_vb2_buf(priv, state); } -static int csi_idmac_setup(struct csi_priv *priv) +static int csi_idmac_setup(struct csi_priv *priv, + struct v4l2_mbus_config *mbus_cfg) { int ret; - ret = csi_idmac_setup_channel(priv); + ret = csi_idmac_setup_channel(priv, mbus_cfg); if (ret) return ret; @@ -595,7 +594,8 @@ static int csi_idmac_setup(struct csi_priv *priv) return 0; } -static int csi_idmac_start(struct csi_priv *priv) +static int csi_idmac_start(struct csi_priv *priv, + struct v4l2_mbus_config *mbus_cfg) { struct imx_media_video_dev *vdev = priv->vdev; int ret; @@ -619,7 +619,7 @@ static int csi_idmac_start(struct csi_priv *priv) priv->last_eof = false; priv->nfb4eof = false; - ret = csi_idmac_setup(priv); + ret = csi_idmac_setup(priv, mbus_cfg); if (ret) { v4l2_err(&priv->sd, "csi_idmac_setup failed: %d\n", ret); goto out_free_dma_buf; @@ -701,7 +701,8 @@ static void csi_idmac_stop(struct csi_priv *priv) } /* Update the CSI whole sensor and active windows */ -static int csi_setup(struct csi_priv *priv) +static int csi_setup(struct csi_priv *priv, + struct v4l2_mbus_config *mbus_cfg) { struct v4l2_mbus_framefmt *infmt, *outfmt; const struct imx_media_pixfmt *incc; @@ -719,7 +720,7 @@ static int csi_setup(struct csi_priv *priv) * if cycles is set, we need to handle this over multiple cycles as * generic/bayer data */ - if (is_parallel_bus(&priv->mbus_cfg) && incc->cycles) { + if (is_parallel_bus(mbus_cfg) && incc->cycles) { if_fmt.width *= incc->cycles; crop.width *= incc->cycles; } @@ -730,7 +731,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &priv->mbus_cfg, &if_fmt, outfmt); + ipu_csi_init_interface(priv->csi, mbus_cfg, &if_fmt, outfmt); ipu_csi_set_dest(priv->csi, priv->dest); @@ -743,14 +744,46 @@ static int csi_setup(struct csi_priv *priv) return 0; } +static void csi_set_src(struct csi_priv *priv, + struct v4l2_mbus_config *mbus_cfg) +{ + bool is_csi2; + + is_csi2 = !is_parallel_bus(mbus_cfg); + if (is_csi2) { + /* + * NOTE! It seems the virtual channels from the mipi csi-2 + * receiver are used only for routing by the video mux's, + * or for hard-wired routing to the CSI's. Once the stream + * enters the CSI's however, they are treated internally + * in the IPU as virtual channel 0. + */ + ipu_csi_set_mipi_datatype(priv->csi, 0, + &priv->format_mbus[CSI_SINK_PAD]); + } + + /* select either parallel or MIPI-CSI2 as input to CSI */ + ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2); +} + static int csi_start(struct csi_priv *priv) { + struct v4l2_mbus_config mbus_cfg = { .type = 0 }; struct v4l2_fract *input_fi, *output_fi; int ret; + ret = csi_get_upstream_mbus_config(priv, &mbus_cfg); + if (ret) { + v4l2_err(&priv->sd, + "failed to get upstream media bus configuration\n"); + return ret; + } + input_fi = &priv->frame_interval[CSI_SINK_PAD]; output_fi = &priv->frame_interval[priv->active_output_pad]; + csi_set_src(priv, &mbus_cfg); + /* start upstream */ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; @@ -758,7 +791,7 @@ static int csi_start(struct csi_priv *priv) return ret; /* Skip first few frames from a BT.656 source */ - if (priv->mbus_cfg.type == V4L2_MBUS_BT656) { + if (mbus_cfg.type == V4L2_MBUS_BT656) { u32 delay_usec, bad_frames = 20; delay_usec = DIV_ROUND_UP_ULL((u64)USEC_PER_SEC * @@ -769,12 +802,12 @@ static int csi_start(struct csi_priv *priv) } if (priv->dest == IPU_CSI_DEST_IDMAC) { - ret = csi_idmac_start(priv); + ret = csi_idmac_start(priv, &mbus_cfg); if (ret) goto stop_upstream; } - ret = csi_setup(priv); + ret = csi_setup(priv, &mbus_cfg); if (ret) goto idmac_stop; @@ -1121,7 +1154,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct v4l2_mbus_config mbus_cfg = { .type = 0 }; - bool is_csi2; int ret; ret = v4l2_subdev_link_validate_default(sd, link, @@ -1136,26 +1168,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, return ret; } - mutex_lock(&priv->lock); - - priv->mbus_cfg = mbus_cfg; - is_csi2 = !is_parallel_bus(&mbus_cfg); - if (is_csi2) { - /* - * NOTE! It seems the virtual channels from the mipi csi-2 - * receiver are used only for routing by the video mux's, - * or for hard-wired routing to the CSI's. Once the stream - * enters the CSI's however, they are treated internally - * in the IPU as virtual channel 0. - */ - ipu_csi_set_mipi_datatype(priv->csi, 0, - &priv->format_mbus[CSI_SINK_PAD]); - } - - /* select either parallel or MIPI-CSI2 as input to CSI */ - ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2); - - mutex_unlock(&priv->lock); return ret; } @@ -1509,7 +1521,7 @@ static void csi_try_fmt(struct csi_priv *priv, } imx_media_try_colorimetry(&sdformat->format, - priv->active_output_pad == CSI_SRC_PAD_DIRECT); + priv->active_output_pad == CSI_SRC_PAD_DIRECT); } static int csi_set_fmt(struct v4l2_subdev *sd, diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index bb28daa4d713..7413551052ae 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -57,9 +57,8 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, of_node_put(csi_np); if (ret) { /* unavailable or already added is not an error */ - if (ret == -ENODEV || ret == -EEXIST) { + if (ret == -ENODEV || ret == -EEXIST) continue; - } /* other error, can't continue */ return ret; diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 86f2b30cb06c..58f1112e28e5 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -616,7 +616,7 @@ out: } static int vdic_link_setup(struct media_entity *entity, - const struct media_pad *local, + const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index f095d9134fee..135daca7d55b 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -231,7 +231,7 @@ int imx_media_probe_complete(struct v4l2_async_notifier *notifier); struct imx_media_dev *imx_media_dev_init(struct device *dev, const struct media_device_ops *ops); int imx_media_dev_notifier_register(struct imx_media_dev *imxmd, - const struct v4l2_async_notifier_operations *ops); + const struct v4l2_async_notifier_operations *ops); /* imx-media-fim.c */ struct imx_media_fim; diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index bdf5a457752b..84c4d0bf027d 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -151,7 +151,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) /* May be called from atomic context */ static struct imgu_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, - int queue, unsigned int pipe) + int queue, unsigned int pipe) { unsigned int i; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; diff --git a/drivers/staging/media/ipu7/TODO b/drivers/staging/media/ipu7/TODO index 7fbc37059adf..dc27bb6463da 100644 --- a/drivers/staging/media/ipu7/TODO +++ b/drivers/staging/media/ipu7/TODO @@ -19,10 +19,8 @@ staging directory. IPU7 driver is expected to work with the common IPU module in future. - Register definition cleanup - Cleanup the register definitions - remove some unnecessary definitions - remove 'U' suffix for hexadecimal and decimal values and add IPU7 prefix - for IPU7 specific registers. - Some ISYS IO sub-blocks register definitions are offset values from - specific sub-block base, but it is not clear and well suited for driver - to use, need to update the register definitions to make it more clear - and readable. + Add IPU7 prefix for IPU7 specific registers and related macros. Some + ISYS IO sub-blocks register definitions are offset values from specific + sub-block base, but it is not clear and well suited for driver to use, + need to update the register definitions to make it more clear and + readable. diff --git a/drivers/staging/media/starfive/Kconfig b/drivers/staging/media/starfive/Kconfig deleted file mode 100644 index 34727cf56072..000000000000 --- a/drivers/staging/media/starfive/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -# 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 deleted file mode 100644 index 4639fa1bca32..000000000000 --- a/drivers/staging/media/starfive/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index 9ea5708fe409..000000000000 --- a/drivers/staging/media/starfive/camss/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -# 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 deleted file mode 100644 index 005790202e7b..000000000000 --- a/drivers/staging/media/starfive/camss/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# 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 deleted file mode 100644 index 7d459f4f09d0..000000000000 --- a/drivers/staging/media/starfive/camss/TODO.txt +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 259aaad010d2..000000000000 --- a/drivers/staging/media/starfive/camss/stf-camss.c +++ /dev/null @@ -1,438 +0,0 @@ -// 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) -{ - struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV]; - struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; - - media_entity_remove_links(&isp_dev->subdev.entity); - media_entity_remove_links(&cap_yuv->video.vdev.entity); - - 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 - */ -static void 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); -} - -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 deleted file mode 100644 index e2b0cfb437bd..000000000000 --- a/drivers/staging/media/starfive/camss/stf-camss.h +++ /dev/null @@ -1,134 +0,0 @@ -/* 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 deleted file mode 100644 index e15d2e97eb0b..000000000000 --- a/drivers/staging/media/starfive/camss/stf-capture.c +++ /dev/null @@ -1,605 +0,0 @@ -// 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 = 16, - }, - { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pixelformat = V4L2_PIX_FMT_SGRBG10, - .planes = 1, - .vsub = { 1 }, - .bpp = 16, - }, - { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pixelformat = V4L2_PIX_FMT_SGBRG10, - .planes = 1, - .vsub = { 1 }, - .bpp = 16, - }, - { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pixelformat = V4L2_PIX_FMT_SBGGR10, - .planes = 1, - .vsub = { 1 }, - .bpp = 16, - }, -}; - -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) { - const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; - - 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(pix->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 deleted file mode 100644 index 2f9740b7e500..000000000000 --- a/drivers/staging/media/starfive/camss/stf-capture.h +++ /dev/null @@ -1,86 +0,0 @@ -/* 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 deleted file mode 100644 index c34631ff9422..000000000000 --- a/drivers/staging/media/starfive/camss/stf-isp-hw-ops.c +++ /dev/null @@ -1,445 +0,0 @@ -// 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 deleted file mode 100644 index df7a903fbb1b..000000000000 --- a/drivers/staging/media/starfive/camss/stf-isp.c +++ /dev/null @@ -1,379 +0,0 @@ -// 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" - -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; - } - - formats = &isp_dev->formats[pad]; - - 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[code->pad]; - 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)/%ux%u\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 deleted file mode 100644 index 955cbb048363..000000000000 --- a/drivers/staging/media/starfive/camss/stf-isp.h +++ /dev/null @@ -1,428 +0,0 @@ -/* 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 deleted file mode 100644 index a0420eb6a0aa..000000000000 --- a/drivers/staging/media/starfive/camss/stf-video.c +++ /dev/null @@ -1,570 +0,0 @@ -// 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, - .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 deleted file mode 100644 index 8052b77e3ad8..000000000000 --- a/drivers/staging/media/starfive/camss/stf-video.h +++ /dev/null @@ -1,100 +0,0 @@ -/* 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/tegra-video/Makefile b/drivers/staging/media/tegra-video/Makefile index 6c7552e05109..96380b5dbd8b 100644 --- a/drivers/staging/media/tegra-video/Makefile +++ b/drivers/staging/media/tegra-video/Makefile @@ -6,5 +6,6 @@ tegra-video-objs := \ csi.o tegra-video-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20.o +tegra-video-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra20.o tegra-video-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o obj-$(CONFIG_VIDEO_TEGRA) += tegra-video.o diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index 7842104ca933..41d57dac61a7 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -12,6 +12,7 @@ #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/tegra-mipi-cal.h> #include <media/v4l2-fwnode.h> @@ -20,17 +21,6 @@ #define MHZ 1000000 -static inline struct tegra_csi * -host1x_client_to_csi(struct host1x_client *client) -{ - return container_of(client, struct tegra_csi, client); -} - -static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct tegra_csi_channel, subdev); -} - /* * CSI is a separate subdevice which has 6 source pads to generate * test pattern. CSI subdevice pad ops are used only for TPG and @@ -455,6 +445,22 @@ static const struct v4l2_subdev_ops tegra_csi_ops = { .pad = &tegra_csi_pad_ops, }; +struct v4l2_subdev *tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan) +{ + struct media_pad *pad; + struct v4l2_subdev *subdev; + + pad = media_pad_remote_pad_first(&chan->pad); + if (!pad) + return NULL; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + if (!subdev) + return NULL; + + return subdev->ops == &tegra_csi_ops ? subdev : NULL; +} + static int tegra_csi_channel_alloc(struct tegra_csi *csi, struct device_node *node, unsigned int port_num, unsigned int lanes, @@ -704,6 +710,8 @@ static int __maybe_unused csi_runtime_suspend(struct device *dev) clk_bulk_disable_unprepare(csi->soc->num_clks, csi->clks); + regulator_disable(csi->vdd); + return 0; } @@ -712,13 +720,23 @@ static int __maybe_unused csi_runtime_resume(struct device *dev) struct tegra_csi *csi = dev_get_drvdata(dev); int ret; + ret = regulator_enable(csi->vdd); + if (ret) { + dev_err(dev, "failed to enable VDD supply: %d\n", ret); + return ret; + } + ret = clk_bulk_prepare_enable(csi->soc->num_clks, csi->clks); if (ret < 0) { dev_err(csi->dev, "failed to enable clocks: %d\n", ret); - return ret; + goto disable_vdd; } return 0; + +disable_vdd: + regulator_disable(csi->vdd); + return ret; } static int tegra_csi_init(struct host1x_client *client) @@ -796,6 +814,11 @@ static int tegra_csi_probe(struct platform_device *pdev) return ret; } + csi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi"); + if (IS_ERR(csi->vdd)) + return dev_err_probe(&pdev->dev, PTR_ERR(csi->vdd), + "failed to get VDD supply"); + if (!pdev->dev.pm_domain) { ret = -ENOENT; dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret); @@ -804,6 +827,17 @@ static int tegra_csi_probe(struct platform_device *pdev) csi->dev = &pdev->dev; csi->ops = csi->soc->ops; + + if (csi->soc->mipi_ops) { + ret = devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node, + csi->soc->mipi_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add MIPI calibration operations\n"); + } + + mutex_init(&csi->mipi_lock); + platform_set_drvdata(pdev, csi); pm_runtime_enable(&pdev->dev); @@ -836,6 +870,12 @@ static void tegra_csi_remove(struct platform_device *pdev) } static const struct of_device_id tegra_csi_of_id_table[] = { +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc }, +#endif +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc }, +#endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc }, #endif diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index 609c5952e050..f0277cb42446 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -115,6 +115,7 @@ struct tegra_csi_ops { * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure * * @ops: csi hardware operations + * @mipi_ops: MIPI calibration operations * @csi_max_channels: supported max streaming channels * @clk_names: csi and cil clock names * @num_clks: total clocks count @@ -123,6 +124,7 @@ struct tegra_csi_ops { */ struct tegra_csi_soc { const struct tegra_csi_ops *ops; + const struct tegra_mipi_ops *mipi_ops; unsigned int csi_max_channels; const char * const *clk_names; unsigned int num_clks; @@ -130,6 +132,12 @@ struct tegra_csi_soc { unsigned int tpg_frmrate_table_size; }; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +extern const struct tegra_csi_soc tegra20_csi_soc; +#endif +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) +extern const struct tegra_csi_soc tegra30_csi_soc; +#endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) extern const struct tegra_csi_soc tegra210_csi_soc; #endif @@ -141,8 +149,10 @@ extern const struct tegra_csi_soc tegra210_csi_soc; * @client: host1x_client struct * @iomem: register base * @clks: clock for CSI and CIL + * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi * @soc: pointer to SoC data structure * @ops: csi operations + * @mipi_lock: for MIPI calibration operations * @csi_chans: list head for CSI channels */ struct tegra_csi { @@ -150,11 +160,23 @@ struct tegra_csi { struct host1x_client client; void __iomem *iomem; struct clk_bulk_data *clks; + struct regulator *vdd; const struct tegra_csi_soc *soc; const struct tegra_csi_ops *ops; + struct mutex mipi_lock; /* for register access */ struct list_head csi_chans; }; +static inline struct tegra_csi *host1x_client_to_csi(struct host1x_client *client) +{ + return container_of(client, struct tegra_csi, client); +} + +static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct tegra_csi_channel, subdev); +} + void tegra_csi_error_recover(struct v4l2_subdev *subdev); void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan, u8 csi_port_num, diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index aa9ff7fec4f9..e513e6ccb776 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -4,6 +4,9 @@ * * Copyright (C) 2023 SKIDATA GmbH * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> + * + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de> */ /* @@ -12,10 +15,15 @@ */ #include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clk/tegra.h> #include <linux/delay.h> #include <linux/host1x.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/kthread.h> +#include <linux/pm_runtime.h> +#include <linux/tegra-mipi-cal.h> #include <linux/v4l2-mediabus.h> #include "vip.h" @@ -23,19 +31,27 @@ #define TEGRA_VI_SYNCPT_WAIT_TIMEOUT msecs_to_jiffies(200) -/* This are just good-sense numbers. The actual min/max is not documented. */ #define TEGRA20_MIN_WIDTH 32U +#define TEGRA20_MAX_WIDTH 8190U #define TEGRA20_MIN_HEIGHT 32U -#define TEGRA20_MAX_WIDTH 2048U -#define TEGRA20_MAX_HEIGHT 2048U +#define TEGRA20_MAX_HEIGHT 8190U + +/* Tegra20/Tegra30 has 2 outputs in VI */ +enum tegra_vi_out { + TEGRA_VI_OUT_1 = 0, + TEGRA_VI_OUT_2 = 1, +}; /* -------------------------------------------------------------------------- * Registers */ -#define TEGRA_VI_CONT_SYNCPT_OUT_1 0x0060 -#define VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT BIT(8) -#define VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT 0 +#define TEGRA_VI_CONT_SYNCPT_OUT(n) (0x0060 + (n) * 4) +#define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8) +#define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0 + +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8) +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8) #define TEGRA_VI_VI_INPUT_CONTROL 0x0088 #define VI_INPUT_FIELD_DETECT BIT(27) @@ -47,6 +63,7 @@ #define VI_INPUT_YUV_INPUT_FORMAT_YVYU (3 << VI_INPUT_YUV_INPUT_FORMAT_SFT) #define VI_INPUT_INPUT_FORMAT_SFT 2 /* bits [5:2] */ #define VI_INPUT_INPUT_FORMAT_YUV422 (0 << VI_INPUT_INPUT_FORMAT_SFT) +#define VI_INPUT_INPUT_FORMAT_BAYER (2 << VI_INPUT_INPUT_FORMAT_SFT) #define VI_INPUT_VIP_INPUT_ENABLE BIT(1) #define TEGRA_VI_VI_CORE_CONTROL 0x008c @@ -67,7 +84,7 @@ #define VI_VI_CORE_CONTROL_OUTPUT_TO_EPP_SFT 2 #define VI_VI_CORE_CONTROL_OUTPUT_TO_ISP_SFT 0 -#define TEGRA_VI_VI_FIRST_OUTPUT_CONTROL 0x0090 +#define TEGRA_VI_VI_OUTPUT_CONTROL(n) (0x0090 + (n) * 4) #define VI_OUTPUT_FORMAT_EXT BIT(22) #define VI_OUTPUT_V_DIRECTION BIT(20) #define VI_OUTPUT_H_DIRECTION BIT(19) @@ -81,6 +98,10 @@ #define VI_OUTPUT_OUTPUT_FORMAT_SFT 0 #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT) #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT) +/* TEGRA_VI_OUT_2 supported formats */ +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT) #define TEGRA_VI_VIP_H_ACTIVE 0x00a4 #define VI_VIP_H_ACTIVE_PERIOD_SFT 16 /* active pixels/line, must be even */ @@ -90,26 +111,26 @@ #define VI_VIP_V_ACTIVE_PERIOD_SFT 16 /* active lines */ #define VI_VIP_V_ACTIVE_START_SFT 0 -#define TEGRA_VI_VB0_START_ADDRESS_FIRST 0x00c4 -#define TEGRA_VI_VB0_BASE_ADDRESS_FIRST 0x00c8 +#define TEGRA_VI_VB0_START_ADDRESS(n) (0x00c4 + (n) * 44) +#define TEGRA_VI_VB0_BASE_ADDRESS(n) (0x00c8 + (n) * 44) #define TEGRA_VI_VB0_START_ADDRESS_U 0x00cc #define TEGRA_VI_VB0_BASE_ADDRESS_U 0x00d0 #define TEGRA_VI_VB0_START_ADDRESS_V 0x00d4 #define TEGRA_VI_VB0_BASE_ADDRESS_V 0x00d8 -#define TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE 0x00e0 -#define VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT 16 -#define VI_FIRST_OUTPUT_FRAME_WIDTH_SFT 0 +#define TEGRA_VI_OUTPUT_FRAME_SIZE(n) (0x00e0 + (n) * 24) +#define VI_OUTPUT_FRAME_HEIGHT_SFT 16 +#define VI_OUTPUT_FRAME_WIDTH_SFT 0 -#define TEGRA_VI_VB0_COUNT_FIRST 0x00e4 +#define TEGRA_VI_VB0_COUNT(n) (0x00e4 + (n) * 24) -#define TEGRA_VI_VB0_SIZE_FIRST 0x00e8 -#define VI_VB0_SIZE_FIRST_V_SFT 16 -#define VI_VB0_SIZE_FIRST_H_SFT 0 +#define TEGRA_VI_VB0_SIZE(n) (0x00e8 + (n) * 24) +#define VI_VB0_SIZE_V_SFT 16 +#define VI_VB0_SIZE_H_SFT 0 -#define TEGRA_VI_VB0_BUFFER_STRIDE_FIRST 0x00ec -#define VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT 30 -#define VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT 0 +#define TEGRA_VI_VB0_BUFFER_STRIDE(n) (0x00ec + (n) * 24) +#define VI_VB0_BUFFER_STRIDE_CHROMA_SFT 30 +#define VI_VB0_BUFFER_STRIDE_LUMA_SFT 0 #define TEGRA_VI_H_LPF_CONTROL 0x0108 #define VI_H_LPF_CONTROL_CHROMA_SFT 16 @@ -137,15 +158,113 @@ #define VI_CAMERA_CONTROL_TEST_MODE BIT(1) #define VI_CAMERA_CONTROL_VIP_ENABLE BIT(0) -#define TEGRA_VI_VI_ENABLE 0x01a4 +#define TEGRA_VI_VI_ENABLE(n) (0x01a4 + (n) * 4) #define VI_VI_ENABLE_SW_FLOW_CONTROL_OUT1 BIT(1) #define VI_VI_ENABLE_FIRST_OUTPUT_TO_MEM_DISABLE BIT(0) #define TEGRA_VI_VI_RAISE 0x01ac #define VI_VI_RAISE_ON_EDGE BIT(0) +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8) +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8) +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8) +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8) + +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */ +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000 +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008 +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c) +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16) +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c) +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28) +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28) +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28) +#define CSI_PP_HEADER_EC_ENABLE BIT(27) +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24) +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24) +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24) +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20) +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16) +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16) +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16) +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16) +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14) +#define CSI_PP_DATA_TYPE(n) ((n) << 8) +#define CSI_PP_CRC_CHECK_ENABLE BIT(7) +#define CSI_PP_WORD_COUNT_HEADER BIT(6) +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5) +#define CSI_PP_PACKET_HEADER_SENT BIT(4) +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c) +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c) +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c) +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16) +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff)) +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c) +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12) +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8) +#define CSI_PP_VSYNC_START_MARKER BIT(4) +#define CSI_PP_SINGLE_SHOT BIT(2) +#define CSI_PP_NOP 0 +#define CSI_PP_ENABLE 1 +#define CSI_PP_DISABLE 2 +#define CSI_PP_RESET 3 +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068 +#define CSI_A_PHY_CIL_NOP 0x0 +#define CSI_A_PHY_CIL_ENABLE 0x1 +#define CSI_A_PHY_CIL_DISABLE 0x2 +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3 +#define CSI_B_PHY_CIL_NOP (0x0 << 16) +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16) +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16) +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16) +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4) +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5) +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078 +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c +#define CSI_MIPI_AUTO_CAL_DONE BIT(15) +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080 +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084 +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088 +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090 +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8) +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8) +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4 +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8 +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31) +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30) +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30) +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26) +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24) +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21) +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21) +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16) +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8) +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f)) +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0 +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4 +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8 +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0) +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4) +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5) +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6) +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n))) +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4) +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4) +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16) +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4) +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0) +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0 +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4 +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16) +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8) +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0) +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8 +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc + /* -------------------------------------------------------------------------- - * VI + * Read and Write helpers */ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val) @@ -153,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u writel(val, chan->vi->iomem + addr); } +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr) +{ + return readl(chan->vi->iomem + addr); +} + +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val) +{ + writel(val, csi_chan->csi->iomem + addr); +} + +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr) +{ + return readl(csi_chan->csi->iomem + addr); +} + +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val) +{ + writel(val, csi->iomem + addr); +} + +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr) +{ + return readl(csi->iomem + addr); +} + +/* -------------------------------------------------------------------------- + * VI + */ + /* * Get the main input format (YUV/RGB...) and the YUV variant as values to * be written into registers for the current VI input mbus code. @@ -179,6 +327,18 @@ static void tegra20_vi_get_input_formats(struct tegra_vi_channel *chan, case MEDIA_BUS_FMT_YVYU8_2X8: (*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_YVYU; break; + /* RAW8 */ + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* RAW10 */ + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + (*main_input_format) = VI_INPUT_INPUT_FORMAT_BAYER; + break; } } @@ -213,6 +373,18 @@ static void tegra20_vi_get_output_formats(struct tegra_vi_channel *chan, case V4L2_PIX_FMT_YVU420: (*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR; break; + /* RAW8 */ + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + /* RAW10 */ + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + (*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT; + break; } } @@ -251,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on) static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan) { struct tegra_vi *vi = chan->vi; - struct host1x_syncpt *out_sp; + struct host1x_syncpt *out_sp, *fs_sp; out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED); if (!out_sp) - return -ENOMEM; + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n"); chan->mw_ack_sp[0] = out_sp; + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED); + if (!fs_sp) + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n"); + + chan->frame_start_sp[0] = fs_sp; + return 0; } static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan) { host1x_syncpt_put(chan->mw_ack_sp[0]); + host1x_syncpt_put(chan->frame_start_sp[0]); } static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp) @@ -272,18 +451,13 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp) pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH); pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT); + pix->bytesperline = roundup(pix->width, 8) * bpp; + pix->sizeimage = pix->bytesperline * pix->height; + switch (pix->pixelformat) { - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - pix->bytesperline = roundup(pix->width, 2) * 2; - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height; - break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - pix->bytesperline = roundup(pix->width, 8); - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2; + pix->sizeimage = pix->sizeimage * 3 / 2; break; } } @@ -305,6 +479,16 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan) case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: + /* RAW8 */ + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SBGGR8: + /* RAW10 */ + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SBGGR10: if (chan->vflip) chan->start_offset += stride * (height - 1); if (chan->hflip) @@ -367,48 +551,111 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan, case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: - tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, base); - tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS_FIRST, base + chan->start_offset); + tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_1), base); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_1), base + chan->start_offset); + break; + /* RAW8 */ + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SBGGR8: + /* RAW10 */ + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SBGGR10: + tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_2), base); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_2), base + chan->start_offset); break; } } static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan, - struct tegra_channel_buffer *buf) + struct tegra_channel_buffer *buf, + struct tegra_csi_channel *csi_chan) { + u32 val; int err; - chan->next_out_sp_idx++; - tegra20_channel_vi_buffer_setup(chan, buf); - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE); + if (csi_chan) { + u32 port = csi_chan->csi_port_nums[0] & 1; + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), + CSI_PP_START_MARKER_FRAME_MAX(0xf) | + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE); + + /* + * ERESTARTSYS workaround for syncpoints is used because host1x_syncpt_wait + * is unconditionally interruptible. This is not an issue with single shots + * or low resolution capture, but -ERESTARTSYS occurs quite often with high + * resolution or high framerate captures and if not addressed here will + * cause capture to fail entirely. + * + * TODO: once uninterruptible version of host1x_syncpt_wait is available, + * host1x_syncpt_wait should be swapped and ERESTARTSYS workaround can be + * removed. + */ + + val = host1x_syncpt_read(chan->frame_start_sp[0]); + do { + err = host1x_syncpt_wait(chan->frame_start_sp[0], + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL); + } while (err == -ERESTARTSYS); + + if (err) { + if (err != -ERESTARTSYS) + dev_err_ratelimited(&chan->video.dev, + "frame start syncpt timeout: %d\n", err); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET); + goto exit; + } + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), + CSI_PP_START_MARKER_FRAME_MAX(0xf) | + CSI_PP_DISABLE); + } else { + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE); + } + + val = host1x_syncpt_read(chan->mw_ack_sp[0]); + do { + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1, + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL); + } while (err == -ERESTARTSYS); - /* Wait for syncpt counter to reach frame start event threshold */ - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx, - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL); if (err) { - host1x_syncpt_incr(chan->mw_ack_sp[0]); - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err); - release_buffer(chan, buf, VB2_BUF_STATE_ERROR); - return err; + if (err != -ERESTARTSYS) + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err); + goto exit; } - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE); + if (!csi_chan) + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE); +exit: release_buffer(chan, buf, VB2_BUF_STATE_DONE); - return 0; + return err; } static int tegra20_chan_capture_kthread_start(void *data) { struct tegra_vi_channel *chan = data; struct tegra_channel_buffer *buf; + struct v4l2_subdev *csi_subdev = NULL; + struct tegra_csi_channel *csi_chan = NULL; unsigned int retries = 0; int err = 0; + csi_subdev = tegra_channel_get_remote_csi_subdev(chan); + if (csi_subdev) + csi_chan = to_csi_chan(csi_subdev); + while (1) { /* * Source is not streaming if error is non-zero. @@ -433,7 +680,7 @@ static int tegra20_chan_capture_kthread_start(void *data) list_del_init(&buf->queue); spin_unlock(&chan->start_lock); - err = tegra20_channel_capture_frame(chan, buf); + err = tegra20_channel_capture_frame(chan, buf, csi_chan); if (!err) { retries = 0; continue; @@ -451,55 +698,37 @@ static int tegra20_chan_capture_kthread_start(void *data) static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan) { u32 output_fourcc = chan->format.pixelformat; + u32 data_type = chan->fmtinfo->img_dt; int width = chan->format.width; int height = chan->format.height; - int stride_l = chan->format.bytesperline; + int stride_l = chan->format.bytesperline * height; int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 || output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0; - int main_output_format; - int yuv_output_format; - - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format); - - /* - * Set up low pass filter. Use 0x240 for chromaticity and 0x240 - * for luminance, which is the default and means not to touch - * anything. - */ - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL, - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT | - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT); - - /* Set up raise-on-edge, so we get an interrupt on end of frame. */ - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE); - - tegra20_vi_write(chan, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL, - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) | - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) | - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT | - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT); + enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 || + data_type == TEGRA_IMAGE_DT_RAW10) ? + TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1; /* Set up frame size */ - tegra20_vi_write(chan, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE, - height << VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT | - width << VI_FIRST_OUTPUT_FRAME_WIDTH_SFT); + tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel), + height << VI_OUTPUT_FRAME_HEIGHT_SFT | + width << VI_OUTPUT_FRAME_WIDTH_SFT); /* First output memory enabled */ - tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0); + tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE(output_channel), 0); /* Set the number of frames in the buffer */ - tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT_FIRST, 1); + tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT(output_channel), 1); /* Set up buffer frame size */ - tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE_FIRST, - height << VI_VB0_SIZE_FIRST_V_SFT | - width << VI_VB0_SIZE_FIRST_H_SFT); + tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE(output_channel), + height << VI_VB0_SIZE_V_SFT | + width << VI_VB0_SIZE_H_SFT); - tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST, - stride_l << VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT | - stride_c << VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT); + tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE(output_channel), + stride_l << VI_VB0_BUFFER_STRIDE_LUMA_SFT | + stride_c << VI_VB0_BUFFER_STRIDE_CHROMA_SFT); - tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0); + tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE(output_channel), 0); } static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count) @@ -508,18 +737,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count) struct media_pipeline *pipe = &chan->video.pipe; int err; - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]); - err = video_device_pipeline_start(&chan->video, pipe); if (err) goto error_pipeline_start; - tegra20_camera_capture_setup(chan); + /* + * Set up low pass filter. Use 0x240 for chromaticity and 0x240 + * for luminance, which is the default and means not to touch + * anything. + */ + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL, + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT | + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT); + + /* Set up raise-on-edge, so we get an interrupt on end of frame. */ + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE); err = tegra_channel_set_stream(chan, true); if (err) goto error_set_stream; + tegra20_camera_capture_setup(chan); + chan->sequence = 0; chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start, @@ -567,20 +806,40 @@ static const struct tegra_vi_ops tegra20_vi_ops = { .vi_stop_streaming = tegra20_vi_stop_streaming, }; -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \ -{ \ - .code = MEDIA_BUS_FMT_##MBUS_CODE, \ - .bpp = BPP, \ - .fourcc = V4L2_PIX_FMT_##FOURCC, \ +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \ +{ \ + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \ + .bit_width = BIT_WIDTH, \ + .code = MEDIA_BUS_FMT_##MBUS_CODE, \ + .bpp = BPP, \ + .fourcc = V4L2_PIX_FMT_##FOURCC, \ } static const struct tegra_video_format tegra20_video_formats[] = { - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY), - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY), - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV), - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU), - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420), - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420), + /* YUV422 */ + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 2, UYVY), + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 2, VYUY), + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 2, YUYV), + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 2, YVYU), + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 2, UYVY), + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_1X16, 2, VYUY), + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_1X16, 2, YUYV), + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_1X16, 2, YVYU), + /* YUV420P */ + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 1, YUV420), + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 1, YVU420), + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 1, YUV420), + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 1, YVU420), + /* RAW 8 */ + TEGRA20_VIDEO_FMT(RAW8, 8, SRGGB8_1X8, 2, SRGGB8), + TEGRA20_VIDEO_FMT(RAW8, 8, SGRBG8_1X8, 2, SGRBG8), + TEGRA20_VIDEO_FMT(RAW8, 8, SGBRG8_1X8, 2, SGBRG8), + TEGRA20_VIDEO_FMT(RAW8, 8, SBGGR8_1X8, 2, SBGGR8), + /* RAW 10 */ + TEGRA20_VIDEO_FMT(RAW10, 10, SRGGB10_1X10, 2, SRGGB10), + TEGRA20_VIDEO_FMT(RAW10, 10, SGRBG10_1X10, 2, SGRBG10), + TEGRA20_VIDEO_FMT(RAW10, 10, SGBRG10_1X10, 2, SGBRG10), + TEGRA20_VIDEO_FMT(RAW10, 10, SBGGR10_1X10, 2, SBGGR10), }; const struct tegra_vi_soc tegra20_vi_soc = { @@ -588,12 +847,352 @@ const struct tegra_vi_soc tegra20_vi_soc = { .nformats = ARRAY_SIZE(tegra20_video_formats), .default_video_format = &tegra20_video_formats[0], .ops = &tegra20_vi_ops, - .vi_max_channels = 1, /* parallel input (VIP) */ - .vi_max_clk_hz = 150000000, + .hw_revision = 1, + .vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */ + .vi_max_clk_hz = 450000000, .has_h_v_flip = true, }; /* -------------------------------------------------------------------------- + * MIPI Calibration + */ +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi) +{ + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev); + unsigned int port = mipi->pads; + u32 value; + int ret; + + guard(mutex)(&csi->mipi_lock); + + ret = pm_runtime_resume_and_get(csi->dev); + if (ret < 0) { + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret); + return ret; + } + + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG, + CSI_CIL_MIPI_CAL_HSPDOS(4) | + CSI_CIL_MIPI_CAL_HSPUOS(3) | + CSI_CIL_MIPI_CAL_TERMOS(0)); + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG, + CSI_PAD_DRIV_DN_REF(5) | + CSI_PAD_DRIV_UP_REF(7) | + CSI_PAD_TERM_REF(0)); + + /* CSI B */ + value = CSI_CIL_MIPI_CAL_HSPDOS(0) | + CSI_CIL_MIPI_CAL_HSPUOS(0) | + CSI_CIL_MIPI_CAL_TERMOS(4); + + if (port == PORT_B) + value |= CSI_CIL_MIPI_CAL_SEL_B; + + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value); + + /* CSI A */ + value = CSI_CIL_MIPI_CAL_STARTCAL | + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) | + CSI_CIL_MIPI_CAL_PRESCALE(0x2) | + CSI_CIL_MIPI_CAL_HSPDOS(0) | + CSI_CIL_MIPI_CAL_HSPUOS(0) | + CSI_CIL_MIPI_CAL_TERMOS(4); + + if (port == PORT_A) + value |= CSI_CIL_MIPI_CAL_SEL_A; + + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value); + + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0); + + return 0; +} + +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi) +{ + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev); + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS; + unsigned int port = mipi->pads; + u32 value, pp = 0, cil = 0; + int ret; + + /* This part is only for CSI */ + if (port > PORT_B) { + pm_runtime_put(csi->dev); + + return 0; + } + + guard(mutex)(&csi->mipi_lock); + + ret = readl_relaxed_poll_timeout(cil_status_reg, value, + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000); + if (ret < 0) { + dev_warn(csi->dev, "MIPI calibration timeout!\n"); + goto exit; + } + + /* clear status */ + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value); + ret = readl_relaxed_poll_timeout(cil_status_reg, value, + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000); + if (ret < 0) { + dev_warn(csi->dev, "MIPI calibration status timeout!\n"); + goto exit; + } + + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS); + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS); + if (pp | cil) { + dev_warn(csi->dev, "Calibration status not been cleared!\n"); + ret = -EINVAL; + goto exit; + } + +exit: + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp); + + /* un-select to avoid interference with DSI */ + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, + CSI_CIL_MIPI_CAL_HSPDOS(0) | + CSI_CIL_MIPI_CAL_HSPUOS(0) | + CSI_CIL_MIPI_CAL_TERMOS(4)); + + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) | + CSI_CIL_MIPI_CAL_PRESCALE(0x2) | + CSI_CIL_MIPI_CAL_HSPDOS(0) | + CSI_CIL_MIPI_CAL_HSPUOS(0) | + CSI_CIL_MIPI_CAL_TERMOS(4)); + + pm_runtime_put(csi->dev); + + return ret; +} + +static const struct tegra_mipi_ops tegra20_mipi_ops = { + .start_calibration = tegra20_start_pad_calibration, + .finish_calibration = tegra20_finish_pad_calibration, +}; + +/* -------------------------------------------------------------------------- + * CSI + */ +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan) +{ + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0); + + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0); + + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0); + + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL, + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 | + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 | + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2); +} + +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan, + u8 portno) +{ + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev); + int width = vi_chan->format.width; + int height = vi_chan->format.height; + u32 data_type = vi_chan->fmtinfo->img_dt; + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8; + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1; + + unsigned int main_output_format, yuv_output_format; + unsigned int port = portno & 1; + u32 value; + + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format); + + switch (data_type) { + case TEGRA_IMAGE_DT_RAW8: + case TEGRA_IMAGE_DT_RAW10: + output_channel = TEGRA_VI_OUT_2; + if (port == PORT_A) + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER; + else + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER; + break; + } + + tegra20_csi_capture_clean(csi_chan); + + /* CSI port cleanup */ + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0); + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0); + + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */ + + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */ + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */ + + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16); + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count); + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */ + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), + CSI_PP_EXP_FRAME_HEIGHT(height) | + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */ + CSI_PP_LINE_TIMEOUT_ENABLE); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), + CSI_PP_OUTPUT_FORMAT_PIXEL | + CSI_PP_DATA_TYPE(data_type) | + CSI_PP_CRC_CHECK_ENABLE | + CSI_PP_WORD_COUNT_HEADER | + CSI_PP_DATA_IDENTIFIER_ENABLE | + CSI_PP_PACKET_HEADER_SENT | + port); + + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), + CSI_SKIP_PACKET_THRESHOLD(0x3f) | + (csi_chan->numlanes - 1)); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), + CSI_CONTINUOUS_CLOCK_MODE_ENABLE | + 0x5); /* Clock settle time */ + + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port), + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT | + host1x_syncpt_id(vi_chan->frame_start_sp[0]) + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT); + + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel), + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT | + host1x_syncpt_id(vi_chan->mw_ack_sp[0]) + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT); + + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE : + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE; + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), + CSI_PP_START_MARKER_FRAME_MAX(0xf) | + CSI_PP_DISABLE); + + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel), + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) | + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) | + yuv_output_format | main_output_format); + + return 0; +}; + +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno) +{ + struct tegra_csi *csi = csi_chan->csi; + unsigned int port = portno & 1; + u32 value; + + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS); + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value); + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value); + + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS); + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value); + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value); + + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), + CSI_PP_START_MARKER_FRAME_MAX(0xf) | + CSI_PP_DISABLE); + + if (csi_chan->numlanes == 4) { + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE); + } else { + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP : + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP; + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value); + } +} + +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan) +{ + u8 *portnos = csi_chan->csi_port_nums; + int ret, i; + + for (i = 0; i < csi_chan->numgangports; i++) { + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]); + if (ret) + goto stream_start_fail; + } + + return 0; + +stream_start_fail: + for (i = i - 1; i >= 0; i--) + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]); + + return ret; +} + +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan) +{ + u8 *portnos = csi_chan->csi_port_nums; + int i; + + for (i = 0; i < csi_chan->numgangports; i++) + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]); +} + +static const struct tegra_csi_ops tegra20_csi_ops = { + .csi_start_streaming = tegra20_csi_start_streaming, + .csi_stop_streaming = tegra20_csi_stop_streaming, +}; + +static const char * const tegra20_csi_clks[] = { + NULL, +}; + +const struct tegra_csi_soc tegra20_csi_soc = { + .ops = &tegra20_csi_ops, + .mipi_ops = &tegra20_mipi_ops, + .csi_max_channels = 2, /* CSI-A and CSI-B */ + .clk_names = tegra20_csi_clks, + .num_clks = ARRAY_SIZE(tegra20_csi_clks), +}; + +static const char * const tegra30_csi_clks[] = { + "csi", + "csia-pad", + "csib-pad", +}; + +const struct tegra_csi_soc tegra30_csi_soc = { + .ops = &tegra20_csi_ops, + .mipi_ops = &tegra20_mipi_ops, + .csi_max_channels = 2, /* CSI-A and CSI-B */ + .clk_names = tegra30_csi_clks, + .num_clks = ARRAY_SIZE(tegra30_csi_clks), +}; + +/* -------------------------------------------------------------------------- * VIP */ @@ -606,13 +1205,17 @@ const struct tegra_vi_soc tegra20_vi_soc = { static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan) { struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&vip_chan->subdev); + u32 data_type = vi_chan->fmtinfo->img_dt; int width = vi_chan->format.width; int height = vi_chan->format.height; - - unsigned int main_input_format; - unsigned int yuv_input_format; + enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 || + data_type == TEGRA_IMAGE_DT_RAW10) ? + TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1; + unsigned int main_input_format, yuv_input_format; + unsigned int main_output_format, yuv_output_format; tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format); + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format); tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0); @@ -638,13 +1241,18 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan) GENMASK(9, 2) << VI_DATA_INPUT_SFT); tegra20_vi_write(vi_chan, TEGRA_VI_PIN_INVERSION, 0); - tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT_1, - VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT | + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel), + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT | host1x_syncpt_id(vi_chan->mw_ack_sp[0]) - << VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT); + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT); tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE); + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel), + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) | + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) | + yuv_output_format | main_output_format); + return 0; } diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 9c0b38585d63..f14cdc7b5211 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -160,8 +160,8 @@ static void tegra_channel_buffer_queue(struct vb2_buffer *vb) wake_up_interruptible(&chan->start_wait); } -struct v4l2_subdev * -tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan) +static struct v4l2_subdev * +tegra_channel_get_remote_bridge_subdev(struct tegra_vi_channel *chan) { struct media_pad *pad; @@ -182,7 +182,7 @@ tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan) struct v4l2_subdev *subdev; struct media_entity *entity; - subdev = tegra_channel_get_remote_csi_subdev(chan); + subdev = tegra_channel_get_remote_bridge_subdev(chan); if (!subdev) return NULL; @@ -204,7 +204,7 @@ static int tegra_channel_enable_stream(struct tegra_vi_channel *chan) struct v4l2_subdev *subdev; int ret; - subdev = tegra_channel_get_remote_csi_subdev(chan); + subdev = tegra_channel_get_remote_bridge_subdev(chan); ret = v4l2_subdev_call(subdev, video, s_stream, true); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; @@ -217,7 +217,7 @@ static int tegra_channel_disable_stream(struct tegra_vi_channel *chan) struct v4l2_subdev *subdev; int ret; - subdev = tegra_channel_get_remote_csi_subdev(chan); + subdev = tegra_channel_get_remote_bridge_subdev(chan); ret = v4l2_subdev_call(subdev, video, s_stream, false); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; @@ -476,17 +476,11 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, 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)) { + if (!v4l2_subdev_has_op(subdev, pad, get_selection) || + v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel)) { try_crop->width = 0; try_crop->height = 0; } else { - ret = v4l2_subdev_call(subdev, pad, get_selection, - NULL, &sdsel); - if (ret) { - ret = -EINVAL; - goto out_free; - } - try_crop->width = sdsel.r.width; try_crop->height = sdsel.r.height; } @@ -968,6 +962,7 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan) } #else struct v4l2_subdev *subdev; + struct v4l2_ctrl *hflip, *vflip; /* custom control */ v4l2_ctrl_new_custom(&chan->ctrl_handler, &syncpt_timeout_ctrl, NULL); @@ -993,11 +988,13 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan) return ret; } - if (chan->vi->soc->has_h_v_flip) { + hflip = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_HFLIP); + if (chan->vi->soc->has_h_v_flip && !hflip) v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - } + vflip = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_VFLIP); + if (chan->vi->soc->has_h_v_flip && !vflip) + v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); #endif /* setup the controls */ @@ -1421,29 +1418,19 @@ static int __maybe_unused vi_runtime_resume(struct device *dev) struct tegra_vi *vi = dev_get_drvdata(dev); int ret; - ret = regulator_enable(vi->vdd); - if (ret) { - dev_err(dev, "failed to enable VDD supply: %d\n", ret); - return ret; - } - ret = clk_set_rate(vi->clk, vi->soc->vi_max_clk_hz); if (ret) { dev_err(dev, "failed to set vi clock rate: %d\n", ret); - goto disable_vdd; + return ret; } ret = clk_prepare_enable(vi->clk); if (ret) { dev_err(dev, "failed to enable vi clock: %d\n", ret); - goto disable_vdd; + return ret; } return 0; - -disable_vdd: - regulator_disable(vi->vdd); - return ret; } static int __maybe_unused vi_runtime_suspend(struct device *dev) @@ -1452,8 +1439,6 @@ static int __maybe_unused vi_runtime_suspend(struct device *dev) clk_disable_unprepare(vi->clk); - regulator_disable(vi->vdd); - return 0; } @@ -1634,11 +1619,11 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier) goto unregister_video; } - subdev = tegra_channel_get_remote_csi_subdev(chan); + subdev = tegra_channel_get_remote_bridge_subdev(chan); if (!subdev) { ret = -ENODEV; dev_err(vi->dev, - "failed to get remote csi subdev: %d\n", ret); + "failed to get remote bridge subdev: %d\n", ret); goto unregister_video; } @@ -1898,13 +1883,6 @@ static int tegra_vi_probe(struct platform_device *pdev) return ret; } - vi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi"); - if (IS_ERR(vi->vdd)) { - ret = PTR_ERR(vi->vdd); - dev_err(&pdev->dev, "failed to get VDD supply: %d\n", ret); - return ret; - } - if (!pdev->dev.pm_domain) { ret = -ENOENT; dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret); @@ -1959,7 +1937,7 @@ static void tegra_vi_remove(struct platform_device *pdev) } static const struct of_device_id tegra_vi_of_id_table[] = { -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) { .compatible = "nvidia,tegra20-vi", .data = &tegra20_vi_soc }, #endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h index 1e6a5caa7082..46328e488aa3 100644 --- a/drivers/staging/media/tegra-video/vi.h +++ b/drivers/staging/media/tegra-video/vi.h @@ -94,7 +94,6 @@ struct tegra_vi_soc { * @client: host1x_client struct * @iomem: register base * @clk: main clock for VI block - * @vdd: vdd regulator for VI hardware, normally it is avdd_dsi_csi * @soc: pointer to SoC data structure * @ops: vi operations * @vi_chans: list head for VI channels @@ -104,7 +103,6 @@ struct tegra_vi { struct host1x_client client; void __iomem *iomem; struct clk *clk; - struct regulator *vdd; const struct tegra_vi_soc *soc; const struct tegra_vi_ops *ops; struct list_head vi_chans; @@ -127,7 +125,6 @@ struct tegra_vi { * frame through host1x syncpoint counters (On Tegra20 used for the * OUT_1 syncpt) * @sp_incr_lock: protects cpu syncpoint increment. - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20) * * @kthread_start_capture: kthread to start capture of single frame when * vb buffer is available. This thread programs VI CSI hardware @@ -190,7 +187,6 @@ struct tegra_vi_channel { struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX]; /* protects the cpu syncpoint increment */ spinlock_t sp_incr_lock[GANG_PORTS_MAX]; - u32 next_out_sp_idx; struct task_struct *kthread_start_capture; wait_queue_head_t start_wait; @@ -296,7 +292,7 @@ struct tegra_video_format { u32 fourcc; }; -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) extern const struct tegra_vi_soc tegra20_vi_soc; #endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c index ae1ae03fa9de..2e84385708ba 100644 --- a/drivers/staging/media/tegra-video/video.c +++ b/drivers/staging/media/tegra-video/video.c @@ -121,10 +121,16 @@ static void host1x_video_remove(struct host1x_device *dev) } static const struct of_device_id host1x_video_subdevs[] = { -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) { .compatible = "nvidia,tegra20-vip", }, { .compatible = "nvidia,tegra20-vi", }, #endif +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + { .compatible = "nvidia,tegra20-csi", }, +#endif +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) + { .compatible = "nvidia,tegra30-csi", }, +#endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) { .compatible = "nvidia,tegra210-csi", }, { .compatible = "nvidia,tegra210-vi", }, diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c index 80cd3b113125..9ff1f1750a15 100644 --- a/drivers/staging/media/tegra-video/vip.c +++ b/drivers/staging/media/tegra-video/vip.c @@ -264,7 +264,7 @@ static void tegra_vip_remove(struct platform_device *pdev) } static const struct of_device_id tegra_vip_of_id_table[] = { -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) { .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc }, #endif { } diff --git a/drivers/staging/media/tegra-video/vip.h b/drivers/staging/media/tegra-video/vip.h index fdded00447e4..563059cbf5b5 100644 --- a/drivers/staging/media/tegra-video/vip.h +++ b/drivers/staging/media/tegra-video/vip.h @@ -50,7 +50,7 @@ struct tegra_vip_soc { const struct tegra_vip_ops *ops; }; -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) extern const struct tegra_vip_soc tegra20_vip_soc; #endif |
