From 70eaeee95f7b4813737b9e529b4b33b3e9928336 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Tue, 3 Mar 2026 14:54:06 +0530 Subject: media: staging: Drop starfive-camss from staging The starfive-camss driver is no longer being worked upon for destaging, as confirmed by the maintainer, so drop it. Link: https://lore.kernel.org/all/ZQ0PR01MB13024A92926C415C187D2C18F29F2@ZQ0PR01MB1302.CHNPR01.prod.partner.outlook.cn/ Acked-by: Changhuang Liang Reviewed-by: Laurent Pinchart Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/starfive/Kconfig | 5 - drivers/staging/media/starfive/Makefile | 2 - drivers/staging/media/starfive/camss/Kconfig | 18 - drivers/staging/media/starfive/camss/Makefile | 13 - drivers/staging/media/starfive/camss/TODO.txt | 4 - drivers/staging/media/starfive/camss/stf-camss.c | 438 --------------- drivers/staging/media/starfive/camss/stf-camss.h | 134 ----- drivers/staging/media/starfive/camss/stf-capture.c | 605 --------------------- drivers/staging/media/starfive/camss/stf-capture.h | 86 --- .../staging/media/starfive/camss/stf-isp-hw-ops.c | 445 --------------- drivers/staging/media/starfive/camss/stf-isp.c | 379 ------------- drivers/staging/media/starfive/camss/stf-isp.h | 428 --------------- drivers/staging/media/starfive/camss/stf-video.c | 570 ------------------- drivers/staging/media/starfive/camss/stf-video.h | 100 ---- 16 files changed, 3230 deletions(-) delete mode 100644 drivers/staging/media/starfive/Kconfig delete mode 100644 drivers/staging/media/starfive/Makefile delete mode 100644 drivers/staging/media/starfive/camss/Kconfig delete mode 100644 drivers/staging/media/starfive/camss/Makefile delete mode 100644 drivers/staging/media/starfive/camss/TODO.txt delete mode 100644 drivers/staging/media/starfive/camss/stf-camss.c delete mode 100644 drivers/staging/media/starfive/camss/stf-camss.h delete mode 100644 drivers/staging/media/starfive/camss/stf-capture.c delete mode 100644 drivers/staging/media/starfive/camss/stf-capture.h delete mode 100644 drivers/staging/media/starfive/camss/stf-isp-hw-ops.c delete mode 100644 drivers/staging/media/starfive/camss/stf-isp.c delete mode 100644 drivers/staging/media/starfive/camss/stf-isp.h delete mode 100644 drivers/staging/media/starfive/camss/stf-video.c delete mode 100644 drivers/staging/media/starfive/camss/stf-video.h (limited to 'drivers/staging') 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/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 - * Author: Changhuang Liang - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -MODULE_AUTHOR("Changhuang Liang "); -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 -#include -#include -#include -#include -#include -#include - -#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 - -#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 - -#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 -#include -#include -#include -#include - -#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 -#include -#include -#include -#include -#include -#include - -#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 */ -- cgit v1.2.3 From 2b7eb2c5dc72f0fc954ac4aa155f9e285e937f7c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Feb 2026 15:10:54 +0200 Subject: staging: media: atomisp: Disallow all private IOCTLs Disallow all private IOCTLs. These aren't quite as safe as one could assume of IOCTL handlers; disable them for now. Instead of removing the code, return in the beginning of the function if cmd is non-zero in order to keep static checkers happy. Reported-by: Soufiane Dani Closes: https://lore.kernel.org/linux-staging/20260210-atomisp-fix-v1-1-024429cbff31@tutanota.com/ Cc: stable@vger.kernel.org Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Fixes: ad85094b293e ("Revert "media: staging: atomisp: Remove driver"") Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/staging') 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; -- cgit v1.2.3 From f6390408a846aacc2171c17d88b062e202d84e86 Mon Sep 17 00:00:00 2001 From: Omer El Idrissi Date: Wed, 28 Jan 2026 17:58:46 +0100 Subject: staging: media: ipu3: fix function argument alignment Fix alignment of function arguments to match kernel coding style as reported by checkpatch.pl Signed-off-by: Omer El Idrissi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') 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]; -- cgit v1.2.3 From 03fd5070c57acc99a21123a960f4820a6693e910 Mon Sep 17 00:00:00 2001 From: Artem Lytkin Date: Sat, 7 Feb 2026 22:05:44 +0000 Subject: staging: media: av7110: replace BUG() with error return in gpioirq Replace BUG() with a return statement in the gpioirq tasklet handler. If saa7146_wait_for_debi_done() times out, crashing the kernel is disproportionate. The pr_err() already logs the failure, and returning early avoids accessing hardware in a potentially broken state. Signed-off-by: Artem Lytkin Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') 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); -- cgit v1.2.3 From dccc0c3ddf8f16071736f98a7d6dd46a2d43e037 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 20 Dec 2025 10:33:26 +0000 Subject: media: rc: fix race between unregister and urb/irq callbacks Some rc device drivers have a race condition between rc_unregister_device() and irq or urb callbacks. This is because rc_unregister_device() does two things, it marks the device as unregistered so no new commands can be issued and then it calls rc_free_device(). This means the driver has no chance to cancel any pending urb callbacks or interrupts after the device has been marked as unregistered. Those callbacks may access struct rc_dev or its members (e.g. struct ir_raw_event_ctrl), which have been freed by rc_free_device(). This change removes the implicit call to rc_free_device() from rc_unregister_device(). This means that device drivers can call rc_unregister_device() in their remove or disconnect function, then cancel all the urbs and interrupts before explicitly calling rc_free_device(). Note this is an alternative fix for an issue found by Haotian Zhang, see the Closes: tags. Reported-by: Haotian Zhang Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/ Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/ Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/ Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/ Reviewed-by: Patrice Chotard Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110_ir.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/staging') 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 , Oliver Endriss "); -- cgit v1.2.3 From 410ff94513353f737058bb0f5ffba8dae14f1a43 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:24 +0200 Subject: staging: media: tegra-video: expand VI and VIP support to Tegra30 Existing VI and VIP implementation for Tegra20 is fully compatible with Tegra30. Signed-off-by: Svyatoslav Ryhel Tested-by: Luca Ceresoli # Tegra20 VIP Reviewed-by: Luca Ceresoli Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/Makefile | 1 + drivers/staging/media/tegra-video/vi.c | 2 +- drivers/staging/media/tegra-video/vi.h | 2 +- drivers/staging/media/tegra-video/video.c | 2 +- drivers/staging/media/tegra-video/vip.c | 2 +- drivers/staging/media/tegra-video/vip.h | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/staging') 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/vi.c b/drivers/staging/media/tegra-video/vi.c index 9c0b38585d63..a7892036f29e 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1959,7 +1959,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..cac0c0d0e225 100644 --- a/drivers/staging/media/tegra-video/vi.h +++ b/drivers/staging/media/tegra-video/vi.h @@ -296,7 +296,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..b009ceefda14 100644 --- a/drivers/staging/media/tegra-video/video.c +++ b/drivers/staging/media/tegra-video/video.c @@ -121,7 +121,7 @@ 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 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 -- cgit v1.2.3 From 8a50111951356d1975f63dcf3dd2f91edacfadf1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:25 +0200 Subject: staging: media: tegra-video: vi: adjust get_selection operation check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During __tegra_channel_try_format, the VI (Video Input) checks if the camera sensor driver provides a get_selection operation. If this operation is unavailable, the crop is set to 0. However, if the operation is available but returns an error, the VI currently fails. While this works for simple cameras with a single pad, it creates a corner case for sensors like the mt9m114. This sensor provides the same operation set for both IFP pads, but returns an error when get_selection is called on an unsupported pad (such as the source pad), causing the aforementioned behavior. To resolve this, if get_selection is implemented but returns an error, try_crop is now set to 0 — treating it as if the operation was not implemented — instead of returning a failure. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vi.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index a7892036f29e..e0961bc0a017 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -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; } -- cgit v1.2.3 From 4c9f5dc5210b0cbcf5582c8e1484659da376807f Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:26 +0200 Subject: staging: media: tegra-video: vi: add flip controls only if no source controls are provided Because the current Tegra video driver is video-centric, it exposes all controls via /dev/video. If both the camera sensor and the VI provide hflip and vflip, the driver will fail because only one control is allowed. To address this, hflip and vflip should be added from the SoC only if the camera sensor doesn't provide those controls. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index e0961bc0a017..19eebfdae221 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -962,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); @@ -987,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 */ -- cgit v1.2.3 From 6926495fa5204fa50f164c92bb63a303e0a829ff Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:27 +0200 Subject: staging: media: tegra-video: csi: move CSI helpers to header Move CSI helpers into the header for easier access from SoC-specific video driver parts. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/csi.c | 11 ----------- drivers/staging/media/tegra-video/csi.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index 7842104ca933..f858c05ce6c9 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -20,17 +20,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 diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index 609c5952e050..2b4d586d3845 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -155,6 +155,16 @@ struct tegra_csi { 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, -- cgit v1.2.3 From c888c4c834c9afc18879fde91ad405be21b7425d Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:28 +0200 Subject: gpu: host1x: convert MIPI to use operation function pointers Convert existing MIPI code to use operation function pointers, a necessary step for supporting Tegra20/Tegra30 SoCs. All common MIPI configuration that is SoC-independent remains in mipi.c, while all SoC-specific code is moved to tegra114-mipi.c (The naming matches the first SoC generation with a dedicated calibration block). Shared structures and function calls are placed into tegra-mipi-cal.h. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Acked-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/csi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index f858c05ce6c9..bcaea20c3025 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -12,6 +12,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 6017e97f50f8c9cbdd448b3569bc19152f57cd87 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:29 +0200 Subject: staging: media: tegra-video: vi: improve logic of source requesting By default tegra_channel_get_remote_csi_subdev returns next device in pipe assuming it is CSI but in case of Tegra20 and Tegra30 it can also be VIP or even HOST. Define tegra_channel_get_remote_csi_subdev within CSI and add check if returned device is actually CSI by comparing subdevice operations. Previous tegra_channel_get_remote_csi_subdev definition in VI rename to tegra_channel_get_remote_bridge_subdev and use it only in VI driver since core VI driver does not care about source and does not call any specific functions. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/csi.c | 16 ++++++++++++++++ drivers/staging/media/tegra-video/vi.c | 14 +++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index bcaea20c3025..92078a1f9a4e 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -445,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, diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 19eebfdae221..7032c733577d 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; @@ -1631,11 +1631,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; } -- cgit v1.2.3 From 3bcd5a240372cacf0587c549b25a43e246d53bdd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:30 +0200 Subject: staging: media: tegra-video: csi: move avdd-dsi-csi-supply from VI to CSI The avdd-dsi-csi-supply is CSI power supply not VI, hence move it to proper place. Signed-off-by: Svyatoslav Ryhel Tested-by: Luca Ceresoli # Tegra20 VIP Reviewed-by: Luca Ceresoli Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/csi.c | 19 ++++++++++++++++++- drivers/staging/media/tegra-video/csi.h | 2 ++ drivers/staging/media/tegra-video/vi.c | 23 ++--------------------- drivers/staging/media/tegra-video/vi.h | 2 -- 4 files changed, 22 insertions(+), 24 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index 92078a1f9a4e..c4d3514f19b3 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -710,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; } @@ -718,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) @@ -802,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); diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index 2b4d586d3845..5344088aa022 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -141,6 +141,7 @@ 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 * @csi_chans: list head for CSI channels @@ -150,6 +151,7 @@ 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 list_head csi_chans; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 7032c733577d..f14cdc7b5211 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1418,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) @@ -1449,8 +1439,6 @@ static int __maybe_unused vi_runtime_suspend(struct device *dev) clk_disable_unprepare(vi->clk); - regulator_disable(vi->vdd); - return 0; } @@ -1895,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); diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h index cac0c0d0e225..bfadde8858d4 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; -- cgit v1.2.3 From 9e539666209de05980a20720f6eae7332690003d Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:31 +0200 Subject: staging: media: tegra-video: tegra20: set correct maximum width and height Maximum width and height for Tegra20 and Tegra30 is determined by respective register field, rounded down to factor of 2, which is 8191U rounded down to 8190U. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index aa9ff7fec4f9..b2e706fa727b 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -23,11 +23,10 @@ #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 /* -------------------------------------------------------------------------- * Registers -- cgit v1.2.3 From 541f95b23c5662ec9a585c468d0abe4572cd8aa6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:32 +0200 Subject: staging: media: tegra-video: tegra20: add support for second output of VI VI in Tegra20/Tegra30 has 2 VI outputs with different set of supported formats. Convert output registers to macros for simpler work with both outputs since apart formats their layout matches. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 83 ++++++++++++++++------------- 1 file changed, 47 insertions(+), 36 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index b2e706fa727b..c8afc6f2adf2 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -28,13 +28,19 @@ #define TEGRA20_MIN_HEIGHT 32U #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_VI_INPUT_CONTROL 0x0088 #define VI_INPUT_FIELD_DETECT BIT(27) @@ -46,6 +52,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 @@ -66,7 +73,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) @@ -80,6 +87,8 @@ #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_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 */ @@ -89,26 +98,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 @@ -136,7 +145,7 @@ #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) @@ -366,8 +375,8 @@ 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; } } @@ -455,6 +464,7 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan) int stride_l = chan->format.bytesperline; int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 || output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0; + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1; int main_output_format; int yuv_output_format; @@ -472,33 +482,33 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan) /* 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, + tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel), (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); /* 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) @@ -587,7 +597,7 @@ 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_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */ .vi_max_clk_hz = 150000000, .has_h_v_flip = true, }; @@ -607,6 +617,7 @@ 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); int width = vi_chan->format.width; int height = vi_chan->format.height; + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1; unsigned int main_input_format; unsigned int yuv_input_format; @@ -637,10 +648,10 @@ 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); -- cgit v1.2.3 From 8e3d2fbc961ccb3ae7bc8b6177a96ab8d20d7284 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:33 +0200 Subject: staging: media: tegra-video: tegra20: adjust format align calculations Expand supported formats structure with data_type and bit_width fields required for CSI support. Adjust tegra20_fmt_align by factoring out common bytesperline and sizeimage calculation logic shared by supported planar and non-planar formats and leaving planar-related correction under a switch. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 39 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index c8afc6f2adf2..4b218b9fbc26 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -280,18 +280,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; } } @@ -576,20 +571,24 @@ 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), + /* YUV420P */ + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 1, YUV420), + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 1, YVU420), }; const struct tegra_vi_soc tegra20_vi_soc = { -- cgit v1.2.3 From d8f8373f1a319830e54791ee0e18aed1e0f2bd56 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:34 +0200 Subject: staging: media: tegra-video: tegra20: set VI HW revision According to TRM Tegra20, Tegra30 and Tegra114 have VI revision 1, Tegra124 has revision 2 and Tegra210 has revision 3. Set correct revision in tegra20_vi_soc like tegra210 does. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index 4b218b9fbc26..7ceefd920cd6 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -596,6 +596,7 @@ 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, + .hw_revision = 1, .vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */ .vi_max_clk_hz = 150000000, .has_h_v_flip = true, -- cgit v1.2.3 From 071fa83ded5e0d369e3dd2af2afb70e09b39336c Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:35 +0200 Subject: staging: media: tegra-video: tegra20: increase maximum VI clock frequency Increase maximum VI clock frequency to 450MHz to allow correct work with high resolution camera sensors. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index 7ceefd920cd6..bf8755698610 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -598,7 +598,7 @@ const struct tegra_vi_soc tegra20_vi_soc = { .ops = &tegra20_vi_ops, .hw_revision = 1, .vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */ - .vi_max_clk_hz = 150000000, + .vi_max_clk_hz = 450000000, .has_h_v_flip = true, }; -- cgit v1.2.3 From 001beb030603f77c7bb46661483f3b9d85d8ce61 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:36 +0200 Subject: staging: media: tegra-video: tegra20: expand format support with RAW8/10 and YUV422/YUV420p 1X16 Add support for Bayer formats (RAW8 and RAW10) and YUV422/420p 1X16 versions of existing YUV422/YUV420p 2X8. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 74 +++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index bf8755698610..36a57078d539 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -187,6 +187,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; } } @@ -221,6 +233,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; } } @@ -308,6 +332,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) @@ -373,6 +407,19 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan, 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; } } @@ -454,12 +501,15 @@ 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_c = (output_fourcc == V4L2_PIX_FMT_YUV420 || output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0; - enum tegra_vi_out output_channel = TEGRA_VI_OUT_1; + 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; int main_output_format; int yuv_output_format; @@ -586,9 +636,25 @@ static const struct tegra_video_format tegra20_video_formats[] = { 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 = { @@ -615,10 +681,12 @@ 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; - enum tegra_vi_out output_channel = TEGRA_VI_OUT_1; - + 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; unsigned int yuv_input_format; -- cgit v1.2.3 From d974a67033f4c4bd8cf6864a6b30c5f997c6841e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:37 +0200 Subject: staging: media: tegra-video: tegra20: adjust luma buffer stride Luma buffer stride is calculated by multiplying height in pixels of image by bytes per line. Adjust that value accordingly. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index 36a57078d539..1b8f2a2213f2 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -504,7 +504,7 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan) 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; enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 || -- cgit v1.2.3 From 0e2c4117c3512cf6b8f54c2c3d37564bfa3ccd67 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 3 Mar 2026 10:42:38 +0200 Subject: staging: media: tegra-video: add CSI support for Tegra20 and Tegra30 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for MIPI CSI device and calibration logic found in Tegra20 and Tegra30 SoC. To get CSI operational, an additional syncpoint was allocated to serve as the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI frame start events. That said, the frame capture function was refactored to reflect the addition of the CSI syncpoint, and the CSI-specific configuration is guarded by the presence of a passed CSI channel structure pointer. The camera capture setup's configuration was reconsidered: the first two writes must be done before tegra_channel_set_stream for MIPI calibration to work properly; the third write was moved to VIP/CSI-specific functions since it must be source-specific; the function was placed after tegra_channel_set_stream so the initial sequence is preserved and expanded. CSI configuration sequences were added based on downstream 3.1 kernel sources and adjusted to the existing video-tegra framework. Although Tegra20 and Tegra30 have the same set of configurations, they differ by the number of clocks used by CSI. Dropped the software syncpoint counters in favor of reading syncpoints directly and passing the incremented value to the polling function. If the syncpoint increase fails, the PP is reset. This change should prevent possible race conditions. MIPI calibration logic was registered in CSI since Tegra20 and Tegra30 have no dedicated hardware block for these operations and use CSI. These calls are used for both CSI and DSI to work properly, which is why MIPI calibration cannot be contained within CSI. The pads passed to the calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and DSI-B (4). Tested-by: Luca Ceresoli # tegra20, parallel camera Co-developed-by: Jonas Schwöbel Signed-off-by: Jonas Schwöbel Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/csi.c | 17 + drivers/staging/media/tegra-video/csi.h | 10 + drivers/staging/media/tegra-video/tegra20.c | 620 ++++++++++++++++++++++++++-- drivers/staging/media/tegra-video/vi.h | 2 - drivers/staging/media/tegra-video/video.c | 6 + 5 files changed, 608 insertions(+), 47 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index c4d3514f19b3..41d57dac61a7 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -827,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); @@ -859,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 5344088aa022..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 @@ -144,6 +152,7 @@ extern const struct tegra_csi_soc tegra210_csi_soc; * @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 { @@ -154,6 +163,7 @@ struct tegra_csi { 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; }; diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index 1b8f2a2213f2..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 + * + * Copyright (c) 2025 Svyatoslav Ryhel + * Copyright (c) 2025 Jonas Schwöbel */ /* @@ -12,10 +15,15 @@ */ #include +#include +#include #include #include +#include #include #include +#include +#include #include #include "vip.h" @@ -42,6 +50,9 @@ enum tegra_vi_out { #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) #define VI_INPUT_BT656 BIT(25) @@ -88,6 +99,8 @@ enum tegra_vi_out { #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 @@ -152,8 +165,106 @@ enum tegra_vi_out { #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) @@ -161,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. @@ -283,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) @@ -424,41 +571,91 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan, } 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. @@ -483,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; @@ -510,28 +707,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan) 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; - 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_OUTPUT_CONTROL(output_channel), - (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); /* Set up frame size */ tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel), @@ -562,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, @@ -668,6 +853,345 @@ const struct tegra_vi_soc tegra20_vi_soc = { .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 */ @@ -687,10 +1211,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan) 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; - unsigned int yuv_input_format; + 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); @@ -723,6 +1248,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan) 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.h b/drivers/staging/media/tegra-video/vi.h index bfadde8858d4..46328e488aa3 100644 --- a/drivers/staging/media/tegra-video/vi.h +++ b/drivers/staging/media/tegra-video/vi.h @@ -125,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 @@ -188,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; diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c index b009ceefda14..2e84385708ba 100644 --- a/drivers/staging/media/tegra-video/video.c +++ b/drivers/staging/media/tegra-video/video.c @@ -125,6 +125,12 @@ static const struct of_device_id host1x_video_subdevs[] = { { .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", }, -- cgit v1.2.3 From 1cdbadefb7f7bbd70a3c0f2a1621a764c6ca3cd0 Mon Sep 17 00:00:00 2001 From: Ayush Kumar Date: Tue, 25 Nov 2025 20:23:31 +0000 Subject: media: staging: imx: Remove unnecessary braces from if statement Adhering to Linux kernel coding style guidelines (Chapter 3: Indentation). Signed-off-by: Ayush Kumar Reviewed-by: Frank Li Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-of.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/staging') 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; -- cgit v1.2.3 From 072a8f60aa50034a724b6ffcd1664ea372eedd7b Mon Sep 17 00:00:00 2001 From: vivek yadav Date: Tue, 2 Dec 2025 21:44:13 +0530 Subject: media: staging: imx: fix code style issues Applied checkpatch.pl recommendations: - corrected whitespace - fixed line length - adjusted indentation Signed-off-by: vivek yadav Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-csi.c | 2 +- drivers/staging/media/imx/imx-media-vdic.c | 2 +- drivers/staging/media/imx/imx-media.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index fd7e37d803e7..725478bc2026 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1509,7 +1509,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-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; -- cgit v1.2.3 From 9df2aaa64890c0b6226057eb6fcb6352bd2df432 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 7 Nov 2025 11:34:33 +0100 Subject: media: staging: imx: request mbus_config in csi_start Request the upstream mbus_config in csi_start, which starts the stream, instead of caching it in link_validate. This allows to get rid of the mbus_cfg field in the struct csi_priv and avoids state in the driver. Fixes: 4a34ec8e470c ("[media] media: imx: Add CSI subdev driver") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Michael Tretter Reviewed-by: Philipp Zabel Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-csi.c | 40 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 725478bc2026..dc594b286f73 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); @@ -745,9 +746,17 @@ static int csi_setup(struct csi_priv *priv) 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]; @@ -758,7 +767,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 +778,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; @@ -1138,7 +1147,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, mutex_lock(&priv->lock); - priv->mbus_cfg = mbus_cfg; is_csi2 = !is_parallel_bus(&mbus_cfg); if (is_csi2) { /* -- cgit v1.2.3 From ebeec2b000a90cd8aae86d1931ff5ef23af8284e Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 7 Nov 2025 11:34:34 +0100 Subject: media: staging: imx: configure src_mux in csi_start After media_pipeline_start() was called, the media graph is assumed to be validated. It won't be validated again if a second stream starts. The imx-media-csi driver, however, changes hardware configuration in the link_validate() callback. This can result in started streams with misconfigured hardware. In the concrete example, the ipu2_csi1 is driven by a parallel video input. After the media pipeline has been started with this configuration, a second stream is configured to use ipu1_csi0 with MIPI-CSI input from imx6-mipi-csi2. This may require the reconfiguration of ipu1_csi0 with ipu_set_csi_src_mux(). Since the media pipeline is already running, link_validate won't be called, and the ipu1_csi0 won't be reconfigured. The resulting video is broken, because the ipu1_csi0 is misconfigured, but no error is reported. Move ipu_set_csi_src_mux from csi_link_validate to csi_start to ensure that input to ipu1_csi0 is configured correctly when starting the stream. This is a local reconfiguration in ipu1_csi0 and is possible while the media pipeline is running. Since csi_start() is called with priv->lock already locked, csi_set_src() must not lock priv->lock again. Thus, the mutex_lock() is dropped. Signed-off-by: Michael Tretter Fixes: 4a34ec8e470c ("[media] media: imx: Add CSI subdev driver") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-csi.c | 44 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index dc594b286f73..ef22a083f8eb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -744,6 +744,28 @@ 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 }; @@ -760,6 +782,8 @@ static int csi_start(struct csi_priv *priv) 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; @@ -1130,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, @@ -1145,25 +1168,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, return ret; } - mutex_lock(&priv->lock); - - 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; } -- cgit v1.2.3 From b7efb57ac9baad60f1ca67e7cceac853c7e9026c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Mar 2026 12:42:53 +0200 Subject: staging: media: ipu7: Update TODO Remove cleanup of the register definitions from the TODO file. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/TODO | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/staging') 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. -- cgit v1.2.3